chore: push all design docs, V2 plan specs, and current working state
Includes AirPlan design documents, AircOding-alpha1-plan, AirPlanV2, AirPlan-ParaV2, AirPlan-Para V1 reference docs, and all working code changes across packages. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -2,12 +2,13 @@
|
||||
* Doctor Tools - Diagnostic and repair operations
|
||||
*
|
||||
* Implements T-213: doctor.*
|
||||
* Wraps DoctorService (P8). Stub acceptable in P2.
|
||||
* FR-018: Wired to DoctorService for real diagnostics.
|
||||
*
|
||||
* @module packages/runtime/src/tools/doctor
|
||||
*/
|
||||
|
||||
import type { ToolDefinition, ToolCall, ToolResultEnvelope, ISOTimeString } from '@aircoding/contracts'
|
||||
import { DoctorService } from '../../doctor/DoctorService.js'
|
||||
|
||||
export const doctor_check: ToolDefinition = {
|
||||
name: 'doctor.check',
|
||||
@@ -18,7 +19,7 @@ export const doctor_check: ToolDefinition = {
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
scope: { type: 'string', enum: ['all', 'runtime', 'storage', 'project', 'permissions'], default: 'all' }
|
||||
scope: { type: 'string', enum: ['all', 'self_bootstrap', 'capability', 'project', 'runtime', 'toolchain', 'display', 'network', 'provider'], default: 'all' }
|
||||
}
|
||||
},
|
||||
permissions: { read_paths: { allow: ["*"] } },
|
||||
@@ -28,42 +29,100 @@ export const doctor_check: ToolDefinition = {
|
||||
export const doctor_fix: ToolDefinition = {
|
||||
name: 'doctor.fix',
|
||||
category: 'doctor',
|
||||
description: 'Attempt to fix issues',
|
||||
description: 'Attempt to fix issues (requires permission policy)',
|
||||
version: 1,
|
||||
output_schema: { type: 'object', properties: {}, required: [] },
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
issue_id: { type: 'string', description: 'Issue ID to fix' },
|
||||
dry_run: { type: 'boolean', default: false, description: 'Show what would be done without doing it' }
|
||||
check_name: { type: 'string', description: 'Check name to fix (e.g., bun, display, toolchain.cmake)' },
|
||||
fix: { type: 'boolean', default: true, description: 'Actually perform the fix vs dry-run' }
|
||||
},
|
||||
required: ['issue_id']
|
||||
required: ['check_name']
|
||||
},
|
||||
permissions: { write_paths: { allow: ["*"] } },
|
||||
streaming: false
|
||||
}
|
||||
|
||||
// Stub executor - wraps DoctorService (P8)
|
||||
export function createDoctorExecutor() {
|
||||
/**
|
||||
* FR-018: Real executor that wraps DoctorService
|
||||
*/
|
||||
export function createDoctorExecutor(project_root?: string, capability_registry?: any) {
|
||||
const doctor = new DoctorService(project_root || process.cwd(), capability_registry)
|
||||
|
||||
return {
|
||||
'doctor.check': async (call: ToolCall): Promise<ToolResultEnvelope> => {
|
||||
const { scope = 'all' } = call.arguments as { scope?: string }
|
||||
return create_result(call.call_id, 'doctor.check', 'text', {
|
||||
scope,
|
||||
issues_found: 0,
|
||||
status: 'healthy',
|
||||
message: 'Diagnostic check complete'
|
||||
})
|
||||
const scope = (call.arguments?.scope as any) || 'all'
|
||||
const valid_scopes = ['all', 'self_bootstrap', 'capability', 'project', 'runtime', 'toolchain', 'display', 'network', 'provider']
|
||||
const actual_scope = valid_scopes.includes(scope) ? scope : 'all'
|
||||
|
||||
try {
|
||||
const report = await doctor.run_diagnostics(actual_scope as any)
|
||||
return create_result(call.call_id, 'doctor.check', 'text', {
|
||||
scope: actual_scope,
|
||||
checks: report.checks.map(c => ({
|
||||
name: c.name,
|
||||
category: c.category,
|
||||
passed: c.passed,
|
||||
message: c.message,
|
||||
fixable: c.fixable,
|
||||
fix: c.fix
|
||||
})),
|
||||
all_passed: report.all_passed,
|
||||
bootstrap_passed: report.bootstrap_passed,
|
||||
fixable_count: report.fixable_count,
|
||||
message: report.all_passed ? 'All checks passed' : `${report.fixable_count} fixable issues found`
|
||||
})
|
||||
} catch (e: any) {
|
||||
return create_result(call.call_id, 'doctor.check', 'error', { message: e.message || String(e) })
|
||||
}
|
||||
},
|
||||
|
||||
'doctor.fix': async (call: ToolCall): Promise<ToolResultEnvelope> => {
|
||||
const { issue_id, dry_run = false } = call.arguments as { issue_id: string; dry_run?: boolean }
|
||||
return create_result(call.call_id, 'doctor.fix', 'text', {
|
||||
issue_id,
|
||||
dry_run,
|
||||
action: dry_run ? 'would_fix' : 'fixed',
|
||||
message: `Issue ${issue_id} ${dry_run ? 'would be' : 'was'} fixed`
|
||||
})
|
||||
const { check_name, fix = true } = call.arguments as { check_name: string; fix?: boolean }
|
||||
|
||||
try {
|
||||
// FR-018: Check permission before system modifications
|
||||
const requires_permission = check_name === 'display' || check_name.startsWith('toolchain.') || check_name.startsWith('capability.')
|
||||
const project_root_val = project_root || process.cwd()
|
||||
let has_permission = true
|
||||
|
||||
if (requires_permission) {
|
||||
const { existsSync } = await import('fs')
|
||||
const permission_config_path = `${project_root_val}/.air/shared/permissions.yaml`
|
||||
if (existsSync(permission_config_path)) {
|
||||
// Check if auto-fix is allowed in permissions config
|
||||
has_permission = false // Require explicit consent for system changes
|
||||
}
|
||||
}
|
||||
|
||||
if (requires_permission && !has_permission) {
|
||||
return create_result(call.call_id, 'doctor.fix', 'error', {
|
||||
message: `System modification "${check_name}" requires explicit permission. Use --ask-confirm flag or add to permissions.yaml.`
|
||||
})
|
||||
}
|
||||
|
||||
if (!fix) {
|
||||
// Dry-run: just describe what would be done
|
||||
const result = await doctor.fix(check_name)
|
||||
return create_result(call.call_id, 'doctor.fix', 'text', {
|
||||
check_name,
|
||||
dry_run: true,
|
||||
would_do: result.message,
|
||||
status: 'dry_run'
|
||||
})
|
||||
}
|
||||
|
||||
const result = await doctor.fix(check_name)
|
||||
return create_result(call.call_id, 'doctor.fix', 'text', {
|
||||
check_name,
|
||||
fixed: result.ok,
|
||||
message: result.message,
|
||||
status: result.ok ? 'fixed' : 'failed'
|
||||
})
|
||||
} catch (e: any) {
|
||||
return create_result(call.call_id, 'doctor.fix', 'error', { message: e.message || String(e) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user