fix(regression): repair 5 regressions from second round, close B10/B12/B15/B16
Round 2 regression fixes: - B10 (INV-2 outbox, CRITICAL): wiring.ts — switch durable events from eventBus.publish (live-only) to eventIngestor.ingest (persistent) for debug.record.created and memory.promoted. Add required RuntimeEvent fields (id, source, route). - B12 (Scheduler events, CRITICAL): Scheduler.ts — replace all 4 eventBus.publish calls with eventIngestor.ingest + registered event types (task.started/task.failed/agent.lost/agent.cancelled). Remove unregistered task.status.changed references. - B15 (duplicate ProjectionClient): remove orphan tui/src/ProjectionClient.ts (zero references, superseded by runtime/src/projection/ProjectionClient.ts re-exported via @aircoding/runtime barrel). - RuntimeApp: wire Scheduler→WorkerManager in constructor; document start() bootstrap→recover→hydrate→ready sequence (DD §22.2). - createRuntime: read project_id from .air/shared/project.json (DD §6.1 stable UUID), fallback to Date.now() only if not initialized. - B16 (api_key strict): ProviderManager.get_or_create_adapter now calls ModelConfigLoader.validate() before passing raw api_key to adapter. Also fix from R1 regression: - ArchitectureDesigner: replace broken additive-heuristic risk scoring (single runtime file→replan, large refactor→confirmation only) with change-scope classification (contracts→confirmation, breaking→escalate, large→replan, safe→silent_continue). Remove dead evaluate_risk(). - MainAgent test: update confirmation test from old state name AWAITING_CONFIRMATION to canonical CONFIRMING (B13 state machine fix). Test: 148/148 pass (regression + e2e + llm + toolchain-cpp). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,21 +1,39 @@
|
||||
/**
|
||||
* InitCommand - First-run project initialization wizard
|
||||
* DD §17.
|
||||
* DD §17. Routes filesystem writes through ToolRegistry (INV-3).
|
||||
*
|
||||
* @module packages/cli/src/commands/init
|
||||
*/
|
||||
|
||||
import { mkdirSync, writeFileSync, existsSync } from 'fs'
|
||||
import { existsSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { randomUUID } from 'crypto'
|
||||
import { loadConfig } from '../bootstrap/loadConfig.js'
|
||||
import { ToolRegistry, createToolRegistry, register_builtin_tools } from '@aircoding/runtime'
|
||||
import type { ToolExecutionContext } from '@aircoding/runtime'
|
||||
|
||||
export async function initCommand(project_path?: string): Promise<void> {
|
||||
// TODO(P8): Route filesystem writes through RuntimeApp→ToolRegistry→PermissionEngine (INV-3).
|
||||
export async function initCommand(project_path?: string, toolRegistry?: ToolRegistry): Promise<void> {
|
||||
const project_root = project_path || process.cwd()
|
||||
console.log(`Initializing AirCoding project at ${project_root}`)
|
||||
|
||||
// Create .air directory structure
|
||||
// Create minimal ToolRegistry if not provided (INV-3 compliance)
|
||||
let registry = toolRegistry
|
||||
if (!registry) {
|
||||
registry = createToolRegistry(project_root)
|
||||
register_builtin_tools(registry)
|
||||
}
|
||||
|
||||
const context: ToolExecutionContext = {
|
||||
session_id: 'init',
|
||||
project_id: `proj_${randomUUID()}`,
|
||||
project_root,
|
||||
agent_id: 'cli-init',
|
||||
agent_type: 'executor',
|
||||
task_scope: { allowed_paths: [project_root], denied_paths: [] },
|
||||
permission_profile: 'executor'
|
||||
}
|
||||
|
||||
// Create .air directory structure via fs.write tool (INV-3)
|
||||
const dirs = [
|
||||
join(project_root, '.air', 'shared'),
|
||||
join(project_root, '.air', 'local'),
|
||||
@@ -26,7 +44,8 @@ export async function initCommand(project_path?: string): Promise<void> {
|
||||
|
||||
for (const dir of dirs) {
|
||||
if (!existsSync(dir)) {
|
||||
mkdirSync(dir, { recursive: true })
|
||||
// Use fs.write with empty content to create directory
|
||||
await registry.call({ name: 'fs.write', arguments: { path: join(dir, '.gitkeep'), content: '', create_dirs: true } }, context)
|
||||
console.log(` Created ${dir}`)
|
||||
}
|
||||
}
|
||||
@@ -34,7 +53,7 @@ export async function initCommand(project_path?: string): Promise<void> {
|
||||
// Generate project_id
|
||||
const project_id = `proj_${randomUUID()}`
|
||||
|
||||
// Write project.json
|
||||
// Write project.json via fs.write (INV-3)
|
||||
const project_json = {
|
||||
project_id,
|
||||
name: project_root.split('/').pop() || 'aircoding-project',
|
||||
@@ -42,17 +61,25 @@ export async function initCommand(project_path?: string): Promise<void> {
|
||||
version: '1.0.0-alpha'
|
||||
}
|
||||
|
||||
writeFileSync(
|
||||
join(project_root, '.air', 'shared', 'project.json'),
|
||||
JSON.stringify(project_json, null, 2)
|
||||
)
|
||||
await registry.call({
|
||||
name: 'fs.write',
|
||||
arguments: {
|
||||
path: join(project_root, '.air', 'shared', 'project.json'),
|
||||
content: JSON.stringify(project_json, null, 2),
|
||||
create_dirs: true
|
||||
}
|
||||
}, context)
|
||||
console.log(` Created .air/shared/project.json (project_id: ${project_id})`)
|
||||
|
||||
// Write default rules
|
||||
writeFileSync(
|
||||
join(project_root, '.air', 'shared', 'rules.md'),
|
||||
'# Project Rules\n\nAdd your project-specific rules here.\n'
|
||||
)
|
||||
// Write default rules via fs.write (INV-3)
|
||||
await registry.call({
|
||||
name: 'fs.write',
|
||||
arguments: {
|
||||
path: join(project_root, '.air', 'shared', 'rules.md'),
|
||||
content: '# Project Rules\n\nAdd your project-specific rules here.\n',
|
||||
create_dirs: true
|
||||
}
|
||||
}, context)
|
||||
|
||||
console.log('\nProject initialized successfully!')
|
||||
console.log(`Run 'air run' to start a session.`)
|
||||
|
||||
Reference in New Issue
Block a user