Files
TORCH/src/cli.mjs
T

2079 lines
106 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { analyzeRepository } from './kernel/analyze.mjs';
import { BacklogService } from './backlog/service.mjs';
import { ArtifactService } from './artifacts/service.mjs';
import { createClaudeAdapter } from './adapters/claude.mjs';
import { createRuntimeAdapterRegistry } from './adapters/registry.mjs';
import { planProviderUpdates, updateProviders } from './adapters/updates.mjs';
import { profileCapabilityIssues, resolveLaunchPolicy } from './adapters/runtime.mjs';
import {
inspectRuntimePluginTrust, planRuntimePluginRevoke, planRuntimePluginTrust,
revokeRuntimePlugin, runtimePluginTrustPath, trustRuntimePlugin,
} from './adapters/plugins.mjs';
import { CheckService } from './checks/service.mjs';
import { startConsole } from './console/server.mjs';
import { classifyRecoverability, createLocalCanonical, planLocalCanonical } from './canonical/local.mjs';
import { openControlPlane } from './control-plane/service.mjs';
import { fetchCanonicalObjects, planCanonicalFetch } from './canonical/fetch.mjs';
import { spawnSync } from 'node:child_process';
import { randomUUID } from 'node:crypto';
import {
mkdirSync, mkdtempSync, renameSync, rmSync, statSync, writeFileSync,
} from 'node:fs';
import { tmpdir } from 'node:os';
import { diagnoseProject } from './kernel/doctor.mjs';
import { loadProjectConfig, validateProjectConfig } from './kernel/config.mjs';
import { asErrorRecord, TorchError } from './kernel/errors.mjs';
import { inspectRepository } from './kernel/git.mjs';
import { resolveInstalledProjectRoot } from './kernel/project-root.mjs';
import { setupProject, restoreProject } from './kernel/setup.mjs';
import { inspectSpecifications } from './kernel/specifications.mjs';
import { installProject, planInstall, uninstallProject } from './kernel/install.mjs';
import { IntegrationService } from './integration/service.mjs';
import { analyzeCombatrigFleet } from './importers/combatrig.mjs';
import { proposeDomains } from './kernel/domains.mjs';
import { readFileSync } from 'node:fs';
import { join, resolve } from 'node:path';
import { fileHash, writeNewFile } from './kernel/files.mjs';
import { createWorktrees, planWorktrees } from './kernel/worktrees.mjs';
import {
captureFleet, createFleetBrief, detachFleet, planAreaUp, planFleetDetach,
planFleetDown, planFleetUp, startFleet, stopFleet,
} from './runtime/lifecycle.mjs';
import { ResourceService } from './resources/service.mjs';
import { CANDIDATE_ACCEPTANCE_SCENARIOS, createVersionService } from './self-host/service.mjs';
import { observeProject } from './observability/snapshot.mjs';
import { OwnerDigestService } from './observability/owner-digest.mjs';
import { ContextTelemetryService } from './telemetry/context.mjs';
import { ScheduleService } from './schedules/service.mjs';
import { assertLauncherDigest, ScheduleLauncherService } from './schedules/launcher.mjs';
import { createFleetDesignBrief } from './design/brief.mjs';
import {
createArchitectRunPlan, loadArchitectArtifact, runSessionArchitect, validateArchitectProposal,
} from './design/architect.mjs';
import { FleetEvolutionService } from './evolution/service.mjs';
import { HierarchyEvolutionService } from './evolution/hierarchy.mjs';
import { ConvergenceService } from './convergence/service.mjs';
import {
attachForge, detachForge, forgeStatus, planForgeAttach, planForgeDetach,
planForgeSync, syncForgeRemote,
} from './forge/service.mjs';
import {
configureDelivery, DeliveryService, planDeliveryConfiguration,
} from './delivery/service.mjs';
const HELP = `TORCH — portable agent fleet
Usage:
torch init [--repo <path>] [--spec <path>] [--json]
torch analyze [--repo <path>] [--spec <path>] [--json]
torch design [--repo <path>] [--spec <path>] [--output <path>] [--json]
torch bootstrap [--repo <path>] [--spec <path>] [--output <path>] [--json]
torch architect plan --brief <path> --provider <claude|codex> [--model <model>] [--max-budget-usd <amount>] [--json]
torch architect validate --brief <path> --response <path> [--json]
torch architect run --brief <path> --provider <claude|codex> --output <path> [--model <model>] [--max-budget-usd <amount>] --yes [--json]
torch domains [--repo <path>] [--spec <path>] [--output <path>] [--json]
torch install --proposal <path> [--runtime <adapter,adapter>] [--default-runtime <adapter>] [--update-runtimes] [--dry-run] [--yes] [--json]
torch worktrees [--parent <path>] [--dry-run] [--yes] [--json]
torch install --restore [--dry-run] [--yes] [--json]
torch setup [--parent <path>] [--dry-run] [--yes] [--json]
torch up [--fresh] [--only <id,id>] [--dry-run] [--yes] [--json]
torch down [--dry-run] [--yes] [--json]
torch capture [--json]
torch detach [--dry-run] [--yes] [--json]
torch list [--json]
torch brief [--area <id>] [--json]
torch identity --area <id> [--json]
torch agents [--json]
torch agent --area <id> [--json]
torch roster [--json]
torch who-owns (--path <path> | --capability <text>) [--json]
torch neighbours --area <id> [--json]
torch message --from <id> --to <id|all> --body <text> [--task <id>] [--path <path>] [--commit <sha>] [--json]
torch inbox --area <id> [--unacknowledged] [--limit <n>] [--json]
torch ack --area <id> --message <id> [--json]
torch status --area <id> --state <state> [--summary <text>] [--runtime <name>] [--session <id>] [--task <id>] [--json]
torch profile show --area <id> [--json]
torch profile set --area <id> [--runtime <adapter>] [--model <model>|--inherit-model] [--reasoning <level|none>] [--launch-policy <field=value> ...] [--reset|--reset-launch-policy] [--dry-run|--yes] [--json]
torch profile defaults --runtime <adapter> [--model <model>] [--reasoning <level|none>] [--launch-policy <field=value> ...] [--reset|--reset-launch-policy] --yes [--json]
torch runtimes list [--json]
torch runtimes update --runtime <codex,claude,pi> [--force] [--dry-run|--yes] [--json]
torch runtimes update-policy --mode <auto|off> [--max-age-hours <1-168>] --yes [--json]
torch runtimes trust --name <id> --module </absolute/path/adapter.cjs> [--yes --sha256 <reviewed-hash>] [--json]
torch runtimes revoke --name <id> [--yes --sha256 <reviewed-hash>] [--json]
torch complete --area <id> --summary <text> [--task <id>] [--evidence <text>] [--commit <sha>] [--json]
torch blocked --area <id> --summary <text> [--task <id>] [--evidence <text>] [--path <path>] [--commit <sha>] [--json]
torch coordinate --from <id> --body <text> [--with <id,id>] [--task <id>] [--path <path>] [--json]
torch handoff --from <id> [--to <id>] (--path <path> | --task <id>) --reason <text> [--json]
torch approvals list [--area <id|owner>] [--status <pending|approved|rejected|cancelled>] [--json]
torch approvals decide --id <approval> --revision <n> --by owner --decision <approved|rejected> [--note <text>] --yes [--json]
torch backlog list [--state <state>] [--owner <id>] [--json]
torch backlog next --area <id> [--json]
torch backlog claim-next --area <id> [--dry-run|--yes] [--json]
torch backlog health [--stale-days <n>] [--stale-observed-commits <n>] [--json]
torch backlog activity [--stale-days <n>] [--max-commits <n>] [--json]
torch backlog close-landed --integration <id> --area session-manager (--dry-run | --yes) [--json]
torch backlog get --task <id> [--json]
torch backlog create --area <id> --title <text> --description <text> --accept <text,...> [--priority <priority>] [--domains <id,...>] [--depends <task,...>] [--observed-at <sha>] [--json]
torch backlog classify --task <id> --area session-manager --revision <n> [--feature <name>] [--milestone <name>] [--clear-feature] [--clear-milestone] --reason <text> [--json]
torch backlog transition --task <id> --area <id> --state <state> --revision <n> [--owner <id>] [--evidence <text,...>] [--commit <sha>] [--integration <id>] [--reason <text>] [--note <text>] [--json]
torch artifacts list [--json]
torch artifacts publish --area <id> --task <id> --title <text> --alt <text> --file <path> --commit <full-sha> [--json]
torch artifacts comment --artifact <id> --body <text> --by owner [--yes] [--json]
torch checks list [--json]
torch checks receipts [--commit <sha>] [--id <check>] [--area <id>] [--json]
torch checks plan --id <check> --area <id> [--json]
torch checks run --id <check> --area <id> --yes [--json]
torch checks prepare --id <check> --area <id> --yes [--json]
torch checks prepared [--area <id>] [--json]
torch checks run-prepared --prepared <id> --area <id> --yes [--json]
torch checks cancel-prepared --prepared <id> --area <id> --yes [--json]
torch checks recovery-plan --prepared <id> [--json]
torch checks recover-prepared --prepared <id> --executors-stopped --evidence <text> --yes [--json]
torch resources list [--json]
torch resources status --id <resource> [--json]
torch resources acquire --id <resource> --area <id> [--json]
torch resources release --id <resource> --area <id> [--json]
torch resources cancel --id <resource> --area <id> [--json]
torch converge plan --area <id> [--json]
torch converge run --area <id> --yes [--json]
torch converge guards --area <id> [--json]
torch converge hold --area <id> --type <measurement|pin> --reason <text> [--json]
torch converge release --area <id> --type <measurement|pin> [--json]
torch integrate list [--state <state>] [--json]
torch integrate request --area <id> [--commit <sha>] [--json]
torch integrate evaluate --request <id> [--json]
torch integrate authorize --request <id> --area <id> [--json]
torch integrate plan --request <id> --area <id> [--json]
torch integrate land --request <id> --area <id> --yes [--json]
torch integrate drain --area <landing-authority> --yes [--limit <n>] [--json]
torch canonical plan [--json]
torch canonical create --yes [--json]
torch recoverability [--commit <sha>] [--json]
torch forge plan --remote <name> [--provider <name>] [--json]
torch forge attach --remote <name> [--provider <name>] --yes [--json]
torch forge status [--json]
torch forge sync plan [--json]
torch forge sync --yes [--json]
torch forge fetch plan [--json]
torch forge fetch status [--json]
torch forge fetch --yes [--attempts 3] [--json]
torch forge detach [--dry-run] --yes [--json]
torch delivery list [--state <state>] [--json]
torch digest build [--json]
torch digest latest [--json]
torch digest publish --by owner --yes [--json]
torch delivery attempts [--id <delivery-id>] [--json]
torch delivery operations [--id <delivery-id>] [--json]
torch delivery recover-not-applied --operation <id> --by owner --runtime-stopped --evidence <refs> --yes [--json]
torch delivery recover-succeeded --operation <id> --by owner (--dry-run | --runtime-stopped --evidence <refs> --yes) [--json]
torch delivery get --delivery <id> [--json]
torch delivery create --area <id> [--commit <sha>] --label <text> --evidence <text,...> [--json]
torch delivery plan --delivery <id> --to <state> --by <id|owner> [--json]
torch delivery transition --delivery <id> --to <state> --by <id|owner> --evidence <text,...> [--yes] [--json]
torch delivery configure [--release-provider <name>] [--deployment-provider <name>] [--dry-run] --yes [--json]
torch candidate plan --source <path> [--version <version>] [--json]
torch candidate build --source <path> [--version <version>] --yes [--json]
torch candidate status [--json]
torch candidate test --version <version> [--json]
torch upgrade --version <version> [--dry-run] --yes [--json]
torch rollback [--dry-run] --yes [--json]
torch import combatrig --source <path> [--output <path>] [--json]
torch console snapshot [--repo <path>] [--json]
torch console serve [--repo <path>] [--host <address>] [--port <n>]
torch context report [--area <id>] [--task <id>] [--commit <sha>] [--measurement <class>] [--json]
torch context record --area <id> --source <text> --measurement <measured|estimated> [usage fields] [--json]
torch schedules list [--actor <id|owner>] [--json]
torch schedules runs [--id <schedule>] [--json]
torch schedules wakes [--manager <id>] [--json]
torch schedules recover-wake --reservation <id> --runtime-stopped --note <evidence> --yes [--json]
torch schedules plan --id <schedule> --actor <id|owner> [--json]
torch schedules run --id <schedule> --actor <id|owner> [--yes] [--json]
torch schedules launcher <plan|plan-reconcile|status|install|reconcile|remove> [--yes] [--json]
torch fleet changes [--state <state>] [--json]
torch fleet get --change <id> [--json]
torch fleet assess [--from session-manager] [--json]
torch fleet propose --from session-manager --proposal <path> [--json]
torch fleet propose-retirement --from session-manager --proposal <path> [--json]
torch fleet propose-merge --from session-manager --proposal <path> [--json]
torch fleet propose-split --from session-manager --proposal <path> [--json]
torch fleet approve --change <id> --by <owner> --yes [--json]
torch fleet reject --change <id> --by <owner> --reason <text> --yes [--json]
torch fleet plan --change <id> [--json]
torch fleet activate --change <id> --by <owner> --yes [--json]
torch fleet start --change <id> [--fresh] [--dry-run] --yes [--json]
torch fleet hierarchy-changes [--state <state>] [--json]
torch fleet hierarchy-assess [--from session-manager] [--json]
torch fleet hierarchy-get --proposal <id> [--json]
torch fleet hierarchy-propose --from session-manager --proposal <path> [--json]
torch fleet hierarchy-decide --proposal <id> --decision <approve-pilot|defer|reject> --by owner [--reason <text>] --yes [--json]
torch fleet hierarchy-plan --proposal <id> [--json]
torch fleet hierarchy-activate --proposal <id> --by owner --yes [--json]
torch fleet hierarchy-review-pilot --proposal <id> --review <json-path> --from session-manager --yes [--json]
torch fleet hierarchy-pilot-reviews --proposal <id> [--json]
torch fleet hierarchy-conclusion-plan --proposal <id> --decision <adopt|reverse> --reason <text> [--json]
torch fleet hierarchy-conclude --proposal <id> --decision <adopt|reverse> --reason <text> --plan-hash <digest> --by owner --yes [--json]
torch doctor [--json]
torch uninstall [--dry-run] [--purge --yes] [--json]
`;
function print(value, { json = false } = {}) {
if (json || typeof value !== 'string') process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
else process.stdout.write(`${value}\n`);
}
function optionValue(argv, name) {
const direct = argv.find((arg) => arg.startsWith(`${name}=`));
if (direct) return direct.slice(name.length + 1);
const index = argv.indexOf(name);
return index >= 0 ? argv[index + 1] : undefined;
}
function optionValues(argv, name) {
const values = [];
for (let index = 0; index < argv.length; index += 1) {
if (argv[index].startsWith(`${name}=`)) values.push(argv[index].slice(name.length + 1));
else if (argv[index] === name && argv[index + 1] !== undefined) values.push(argv[index + 1]);
}
return values;
}
function launchPolicyOptions(argv) {
const result = {};
for (const entry of optionValues(argv, '--launch-policy')) {
const match = /^([a-z][A-Za-z0-9]*)=(.+)$/.exec(entry);
if (!match || Object.hasOwn(result, match[1])) {
throw new TorchError(`Invalid or duplicate --launch-policy value: ${entry}`, {
code: 'RUNTIME_PROFILE_INVALID', details: { expected: 'field=value' },
});
}
result[match[1]] = match[2];
}
return result;
}
function assertEffectiveProfileSupported(config, areaId, registry) {
const profile = effectiveProfile(config, areaId, registry);
const issues = profileCapabilityIssues(registry.get(profile.runtime), profile);
if (issues.length) {
throw new TorchError(`Runtime profile is unsupported by ${profile.runtime}`, {
code: 'RUNTIME_PROFILE_UNSUPPORTED', details: { profile, capabilities: issues },
});
}
return profile;
}
function assertRuntimeDefaultsSupported(config, runtime, registry) {
const adapter = registry.get(runtime);
const settings = config.runtimes[runtime] ?? {};
const profile = {
model: settings.model ?? adapter?.configuration?.model ?? null,
reasoning: settings.reasoning ?? adapter?.configuration?.reasoning ?? null,
launchPolicy: resolveLaunchPolicy({
runtime, adapterDefaults: adapter?.configuration, runtimeConfig: settings,
}),
};
const issues = profileCapabilityIssues(adapter, profile);
if (issues.length) throw new TorchError(`Runtime defaults are unsupported by ${runtime}`, {
code: 'RUNTIME_PROFILE_UNSUPPORTED', details: { profile, capabilities: issues },
});
}
function migrateLegacyRuntimeLaunchPolicy(runtime, settings, adapter) {
if (runtime !== 'codex' || (!Object.hasOwn(settings, 'sandbox') && !Object.hasOwn(settings, 'approval'))) return;
const migrated = resolveLaunchPolicy({ runtime, adapterDefaults: adapter?.configuration, runtimeConfig: settings });
if (settings.sandbox === 'workspace-write' && settings.approval === 'approve-for-me') {
// If the owner changes approval away from approve-for-me, preserve the
// implicit workspace-write boundary as an explicit sandbox field.
migrated.sandbox = 'workspace-write';
}
delete settings.sandbox;
delete settings.approval;
if (Object.keys(migrated).length) settings.launchPolicy = migrated;
else delete settings.launchPolicy;
}
function commaList(value) {
return (value ?? '').split(',').map((item) => item.trim()).filter(Boolean);
}
function loadProposal(cwd, proposalPath) {
if (!proposalPath) {
throw new TorchError('Installation requires an approved domain proposal from torch domains --output.', {
code: 'PROPOSAL_REQUIRED',
});
}
try { return JSON.parse(readFileSync(resolve(cwd, proposalPath), 'utf8')); } catch (error) {
throw new TorchError(`Cannot read domain proposal: ${proposalPath}`, {
code: 'PROPOSAL_INVALID', details: error.message,
});
}
}
function loadJsonFile(cwd, inputPath, { label = 'JSON input', code = 'INPUT_INVALID' } = {}) {
if (!inputPath) throw new TorchError(`${label} path is required`, { code });
try { return JSON.parse(readFileSync(resolve(cwd, inputPath), 'utf8')); } catch (error) {
throw new TorchError(`Cannot read ${label}: ${inputPath}`, { code, details: error.message });
}
}
function effectiveProfile(config, areaId, runtimeRegistry = createRuntimeAdapterRegistry()) {
const identity = areaId === 'session-manager'
? config.session_manager : config.domains.find((area) => area.id === areaId);
if (!identity) throw new TorchError(`Unknown Fleet identity: ${areaId}`, { code: 'FLEET_IDENTITY_MISSING' });
const runtime = identity.runtime ?? config.runtimes.default;
const defaults = config.runtimes[runtime] ?? {};
const adapterDefaults = runtimeRegistry.configuration(runtime) ?? {};
const launchPolicy = resolveLaunchPolicy({ runtime, adapterDefaults, runtimeConfig: defaults, identity });
return {
areaId, runtime,
model: identity.model ?? defaults.model ?? adapterDefaults.model ?? null,
reasoning: identity.reasoning ?? defaults.reasoning ?? adapterDefaults.reasoning ?? null,
launchPolicy,
overrides: {
runtime: identity.runtime !== undefined,
model: identity.model != null,
reasoning: identity.reasoning != null,
launchPolicy: identity.launchPolicy !== undefined,
},
};
}
function persistProfileConfig(repositoryRoot, config) {
validateProjectConfig(config);
const path = resolve(repositoryRoot, '.torch', 'torch.yaml');
const mode = statSync(path).mode & 0o777;
const temporaryPath = `${path}.${process.pid}.${randomUUID()}.tmp`;
writeFileSync(temporaryPath, `${JSON.stringify(config, null, 2)}\n`, { encoding: 'utf8', mode });
renameSync(temporaryPath, path);
return path;
}
function withControlPlane(repository, env, callback) {
const controlPlane = openControlPlane({ repositoryRoot: repository.root, env });
try {
return callback(controlPlane);
} finally {
controlPlane.close();
}
}
function configuredRuntimeNames(repositoryRoot, areaIds = undefined) {
const config = loadProjectConfig(repositoryRoot);
const selected = areaIds === undefined ? null : new Set(areaIds);
const identities = [config.session_manager, ...config.domains];
return [...new Set(identities
.filter((identity) => !selected || selected.has(identity.id))
.map((identity) => identity.runtime ?? config.runtimes.default))];
}
function activeRuntimeNames(controlPlane) {
return controlPlane.listAgents().map((identity) => identity.runtime).filter(Boolean);
}
function proposalRuntimeNames(proposal) {
return [...new Set([
proposal.session_manager?.runtime,
...proposal.domains.map((domain) => domain.runtime),
].filter((runtime) => runtime && runtime !== 'default'))];
}
function runtimeAdapters(env = process.env, pluginNames = undefined) {
return createRuntimeAdapterRegistry({ env, loadPlugins: true, pluginNames }).toMap();
}
function runtimeStoppers(spawn, env = process.env, pluginNames = undefined) {
const registry = createRuntimeAdapterRegistry({ env, loadPlugins: true, pluginNames });
registry.replace(createClaudeAdapter({
runner: (executable, args) => spawn(executable, args, {
encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
}),
}));
return registry.toMap();
}
function stopThrough(adapters, action) {
const adapter = adapters.get(action.runtime);
if (!adapter) throw new TorchError(`No runtime stopper for ${action.runtime}`, {
code: 'RUNTIME_ADAPTER_MISSING', details: { runtime: action.runtime },
});
return adapter.stopSession({
runtimeSessionId: action.runtimeSessionId, worktree: action.worktree?.path,
});
}
function acceptanceExcerpt(value, limit = 12_000) {
const output = value?.toString() ?? '';
return output.length > limit ? `[earlier output truncated]\n${output.slice(-limit)}` : output;
}
export const CANDIDATE_ACCEPTANCE_EVIDENCE = Object.freeze({
'candidate-acceptance-isolation': ['SCN-candidate-acceptance-isolation'],
'init-analyze': ['SCN-init-read-only'],
'spec-aware-design': ['SCN-spec-fleet-design', 'SCN-cli-spec-design'],
'ai-fleet-bootstrap': ['SCN-ai-fleet-bootstrap', 'SCN-bootstrap-specialist-only'],
'ai-fleet-planning': ['SCN-ai-fleet-planning', 'SCN-architect-organization-assessment', 'SCN-bootstrap-primary-ownership', 'SCN-architect-file-ownership'],
'fleet-evolution': ['SCN-fleet-evolution', 'SCN-cli-fleet-evolution'],
'fleet-boundary-evolution': ['SCN-fleet-boundary-evolution'],
'cli-lifecycle-surface': ['SCN-cli-lifecycle-surface'],
'forge-migration': ['SCN-forge-migration'],
'delivery-lifecycle': ['SCN-delivery-lifecycle', 'SCN-delivery-attempts', 'SCN-delivery-retry-boundaries', 'SCN-delivery-succeeded-recovery',
'SCN-owner-digest', 'SCN-owner-digest-schedule', 'SCN-owner-digest-receipts'],
'review-install-roster': ['SCN-cli-domain-review', 'SCN-cli-install-provider-default', 'SCN-install-provider-inheritance', 'SCN-guided-setup'],
'branches-worktrees': ['SCN-worktree-bootstrap'],
'runtime-identities': ['SCN-mixed-runtime'],
'runtime-profiles': ['SCN-runtime-profiles', 'SCN-runtime-cost-ceiling', 'SCN-runtime-profile-cli'],
'provider-updates': ['SCN-provider-updates', 'SCN-provider-updates-recovery', 'SCN-provider-update-safety', 'SCN-provider-update-cli'],
'console-runtime-profile': ['SCN-console-runtime-profile'],
'console-schedule-launcher': ['SCN-console-schedule-launcher'],
'runtime-adapters': [
'SCN-pi-adapter', 'SCN-pi-lifecycle-plan', 'SCN-pi-mcp-bridge',
'SCN-claude-adapter', 'SCN-codex-adapter',
],
'runtime-plugin-trust': ['SCN-runtime-plugin-trust', 'SCN-runtime-plugin-lifecycle'],
'organization-graph': ['SCN-organization-graph'],
'manager-check-ins': [
'SCN-manager-check-in', 'SCN-manager-check-in-refresh', 'SCN-manager-check-in-specialist', 'SCN-manager-check-in-schedule',
'SCN-manager-check-in-budgeted-wake', 'SCN-manager-check-in-cost-ceiling', 'SCN-manager-check-in-active-runtime',
'SCN-manager-check-in-count-mode', 'SCN-manager-wake-reservation',
'SCN-manager-wake-policy', 'SCN-manager-wake-recovery',
'SCN-manager-schedule-coverage', 'SCN-manager-schedule-growth',
'SCN-default-manager-checkin-config', 'SCN-manager-check-in-cadence-floor',
],
'manager-check-in-console': ['SCN-console-manager-check-ins'],
'structured-approvals': ['SCN-approval-request'],
'console-owner-approval': ['SCN-console-owner-approval'],
'hierarchy-evolution': [
'SCN-hierarchy-proposal', 'SCN-hierarchy-pressure-assessment',
'SCN-hierarchy-reconsideration-after-new-evidence',
'SCN-console-organization-review', 'SCN-hierarchy-proposal-mcp',
'SCN-hierarchy-activation', 'SCN-hierarchy-activation-rollback',
'SCN-hierarchy-pilot-review', 'SCN-hierarchy-pilot-reversal', 'SCN-console-hierarchy-conclusion', 'SCN-console-hierarchy-adoption',
],
'hierarchy-cli-review': [
'SCN-hierarchy-cli', 'SCN-hierarchy-cli-activation', 'SCN-console-hierarchy-activation',
],
'durable-messaging': ['SCN-durable-message'],
'ownership-query': ['SCN-ownership-handoff'],
'unsafe-worktree-detection': ['SCN-worktree-purge-safety'],
'backlog-integration': [
'SCN-backlog-lifecycle', 'SCN-native-integration',
'SCN-backlog-commit-activity', 'SCN-backlog-activity-coverage', 'SCN-commit-closure-intent',
'SCN-landed-task-closure', 'SCN-landed-closure-recovery',
'SCN-backlog-self-claim', 'SCN-backlog-self-claim-readiness',
'SCN-backlog-self-claim-race', 'SCN-cli-backlog-self-claim',
'SCN-backlog-blocked-resume-serialization',
'SCN-integration-fifo-drain', 'SCN-cli-integration-drain',
],
'forge-sync': ['SCN-forge-sync', 'SCN-canonical-fetch', 'SCN-canonical-fetch-boundaries'],
'resource-lifecycle': [
'SCN-resource-fifo', 'SCN-frozen-check-inputs', 'SCN-frozen-check-rejection',
'SCN-frozen-resource-check', 'SCN-frozen-check-mutating-executor',
'SCN-check-conditions-contract', 'SCN-check-conditions-fail-closed',
'SCN-check-condition-drift', 'SCN-condition-receipts',
],
'package-distribution': ['SCN-package-distribution'],
'capture-stop-resume': [
'SCN-fleet-fresh-resume', 'SCN-hierarchy-aware-start-stop', 'SCN-hierarchy-startup-cycle',
],
'detach-uninstall': ['SCN-install-doctor-purge', 'SCN-install-restore', 'SCN-worktree-canonical-brief'],
'combatrig-compatibility': ['SCN-combatrig-import', 'SCN-cli-combatrig-import'],
'product-surface': ['SCN-product-site', 'SCN-dashboard-demo', 'SCN-console-readonly', 'SCN-console-lifecycle-blockers',
'SCN-console-owner-digest', 'SCN-demo-owner-digest', 'SCN-console-check-evidence', 'SCN-console-task-activity', 'SCN-console-operation-outcomes', 'SCN-console-live-refresh'],
'artifact-review': ['SCN-artifact-review', 'SCN-artifact-boundaries'],
'operations-observability': [
'SCN-context-locality', 'SCN-schedule-boundaries', 'SCN-cli-schedules',
'SCN-scheduled-integration-drain',
],
});
export function runCandidateAcceptance(candidateRoot, spawn) {
const acceptanceRoot = mkdtempSync(join(tmpdir(), 'torch-candidate-acceptance-'));
const home = join(acceptanceRoot, 'home');
const env = {
...process.env,
HOME: home,
TMPDIR: join(acceptanceRoot, 'tmp'),
XDG_CACHE_HOME: join(home, '.cache'),
XDG_CONFIG_HOME: join(home, '.config'),
XDG_DATA_HOME: join(home, '.local', 'share'),
XDG_STATE_HOME: join(home, '.local', 'state'),
XDG_RUNTIME_DIR: join(acceptanceRoot, 'runtime'),
};
let checks;
try {
for (const path of [home, env.TMPDIR, env.XDG_CACHE_HOME, env.XDG_CONFIG_HOME,
env.XDG_DATA_HOME, env.XDG_STATE_HOME, env.XDG_RUNTIME_DIR]) {
mkdirSync(path, { recursive: true, mode: 0o700 });
}
checks = [
{ id: 'test', args: ['test'] },
{ id: 'lint', args: ['run', 'lint'] },
{ id: 'syntax', args: ['run', 'check'] },
].map((check) => {
const result = spawn('npm', check.args, {
cwd: candidateRoot, env, encoding: 'utf8',
stdio: ['ignore', 'pipe', 'pipe'], timeout: 300_000,
});
return {
id: check.id, status: result.status ?? (result.error ? 1 : 0),
signal: result.signal ?? null, error: result.error?.message ?? null,
stdout: result.stdout ?? '', stderr: result.stderr ?? '',
};
});
} finally {
rmSync(acceptanceRoot, { recursive: true, force: true });
}
const testOutput = checks.find((check) => check.id === 'test')?.stdout ?? '';
const observedMarkers = new Set(testOutput.match(/\bSCN-[A-Za-z0-9_-]+\b/g) ?? []);
const evidenceMappingComplete = CANDIDATE_ACCEPTANCE_SCENARIOS.every((scenario) =>
Array.isArray(CANDIDATE_ACCEPTANCE_EVIDENCE[scenario])
&& CANDIDATE_ACCEPTANCE_EVIDENCE[scenario].length > 0);
const scenarios = CANDIDATE_ACCEPTANCE_SCENARIOS.filter((scenario) =>
CANDIDATE_ACCEPTANCE_EVIDENCE[scenario]?.every((marker) => observedMarkers.has(marker)) === true);
const passed = checks.every((check) => check.status === 0 && !check.signal && !check.error)
&& evidenceMappingComplete && scenarios.length === CANDIDATE_ACCEPTANCE_SCENARIOS.length;
return {
passed,
scenarios,
evidence: checks.map(({ stdout, stderr, ...check }) => {
const failed = check.status !== 0 || check.signal || check.error;
return {
...check,
outputBytes: Buffer.byteLength(stdout) + Buffer.byteLength(stderr),
...(failed ? {
stdoutExcerpt: acceptanceExcerpt(stdout), stderrExcerpt: acceptanceExcerpt(stderr),
} : {}),
};
}),
};
}
export async function runCli(argv = process.argv.slice(2), {
cwd = process.cwd(), env = process.env, spawn = spawnSync,
} = {}) {
const command = argv.find((arg) => !arg.startsWith('-')) ?? 'help';
const json = argv.includes('--json');
try {
if (command === 'help' || argv.includes('--help') || argv.includes('-h')) {
process.stdout.write(HELP);
return 0;
}
if (command === 'runtimes') {
const operation = argv[1] ?? 'list';
if (operation === 'update') {
const names = commaList(optionValue(argv, '--runtime'));
if (!names.length) throw new TorchError('Select --runtime codex,claude,pi', { code: 'RUNTIME_UPDATE_INVALID' });
const plan = planProviderUpdates(names, { env });
if (argv.includes('--dry-run') || !argv.includes('--yes')) {
print({ ...plan, requiresApproval: true }, { json }); return 0;
}
print(updateProviders(names, { env, runner: spawn, authorized: true, force: argv.includes('--force') }), { json });
return 0;
}
if (operation === 'update-policy') {
const repository = inspectRepository(optionValue(argv, '--repo') ?? cwd);
const config = loadProjectConfig(repository.root);
const mode = optionValue(argv, '--mode');
const maxAge = Number(optionValue(argv, '--max-age-hours') ?? 24);
const policy = { mode, max_age_hours: maxAge };
const names = optionValue(argv, '--runtime') === undefined
? Object.keys(config.runtimes).filter(name => name !== 'default')
: commaList(optionValue(argv, '--runtime'));
planProviderUpdates(names, { env });
for (const name of names) {
if (!Object.hasOwn(config.runtimes, name)) throw new TorchError('Runtime is not configured', { code: 'RUNTIME_UPDATE_INVALID', details: { name } });
config.runtimes[name].updatePolicy = policy;
}
validateProjectConfig(config);
if (argv.includes('--dry-run') || !argv.includes('--yes')) {
print({ policy, runtimes: names, requiresApproval: true, mutationPerformed: false }, { json }); return 0;
}
print({ path: persistProfileConfig(repository.root, config), policy, runtimes: names,
sessionsStarted: false, mutationPerformed: true }, { json });
return 0;
}
if (operation === 'list') {
const builtIns = createRuntimeAdapterRegistry({ env, loadPlugins: false }).names();
let trust;
try { trust = inspectRuntimePluginTrust({ env }); } catch (error) {
trust = {
path: runtimePluginTrustPath(env), status: 'invalid',
error: { code: error.code ?? 'RUNTIME_PLUGIN_STORE_INVALID', message: error.message },
};
}
print({
schema: 'torch.dev/runtime-catalog/v1alpha1', builtIns,
trust, mutationPerformed: false,
executesPluginCode: false,
}, { json });
return 0;
}
if (operation === 'trust') {
const name = optionValue(argv, '--name');
const modulePath = optionValue(argv, '--module');
const plan = planRuntimePluginTrust({ name, modulePath, env });
if (!argv.includes('--yes')) {
print({ ...plan, requiresApproval: true }, { json });
return 0;
}
print(trustRuntimePlugin({
name, modulePath, env, approved: true, expectedSha256: optionValue(argv, '--sha256'),
}), { json });
return 0;
}
if (operation === 'revoke') {
const name = optionValue(argv, '--name');
const plan = planRuntimePluginRevoke({ name, env });
if (!argv.includes('--yes') || plan.sha256 === null) {
print({ ...plan, requiresApproval: plan.sha256 !== null }, { json });
return 0;
}
print(revokeRuntimePlugin({
name, env, approved: true, expectedSha256: optionValue(argv, '--sha256'),
}), { json });
return 0;
}
throw new TorchError(`Unknown runtimes operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
}
if (command === 'candidate') {
const operation = argv[1] ?? 'status';
const versions = createVersionService({ env });
if (operation === 'status') {
print(versions.status(), { json });
return 0;
}
if (operation === 'test') {
const version = optionValue(argv, '--version');
const status = versions.status();
const validationPath = resolve(status.root, 'versions', version ?? '', '.torch-validation.json');
if (!version || !status.installed.includes(version)) {
throw new TorchError('Candidate test receipt requires an installed version', {
code: 'VERSION_NOT_INSTALLED', details: { version: version ?? null },
});
}
print(JSON.parse(readFileSync(validationPath, 'utf8')), { json });
return 0;
}
const source = optionValue(argv, '--source');
const version = optionValue(argv, '--version');
if (operation === 'plan') {
print(versions.planCandidate({ source: resolve(cwd, source ?? ''), version }), { json });
return 0;
}
if (operation === 'build') {
if (!argv.includes('--yes')) {
throw new TorchError('Candidate build copies code and runs its acceptance suite. Review candidate plan, then re-run with --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(versions.installCandidate({
source: resolve(cwd, source ?? ''), version,
validate: ({ candidateRoot }) => runCandidateAcceptance(candidateRoot, spawn),
}), { json });
return 0;
}
throw new TorchError(`Unknown candidate operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
}
if (command === 'upgrade') {
const versions = createVersionService({ env });
const version = optionValue(argv, '--version');
const status = versions.status();
const plan = {
action: 'upgrade', from: status.activeVersion, to: version ?? null,
canProceed: Boolean(version && status.installed.includes(version)), mutationPerformed: false,
};
if (argv.includes('--dry-run')) {
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (!argv.includes('--yes')) {
throw new TorchError('Upgrade atomically changes the active TORCH version. Review --dry-run, then re-run with --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(versions.activate(version), { json });
return 0;
}
if (command === 'rollback') {
const versions = createVersionService({ env });
const status = versions.status();
const plan = {
action: 'rollback', from: status.activeVersion, to: status.previousVersion,
canProceed: Boolean(status.previousVersion), mutationPerformed: false,
};
if (argv.includes('--dry-run')) {
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (!argv.includes('--yes')) {
throw new TorchError('Rollback atomically restores the previous TORCH version. Review --dry-run, then re-run with --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(versions.rollback(), { json });
return 0;
}
if (command === 'import') {
const format = argv[1];
if (format !== 'combatrig') {
throw new TorchError(`Unsupported fleet import format: ${format ?? '<missing>'}`, {
code: 'IMPORT_FORMAT_UNSUPPORTED',
});
}
const source = optionValue(argv, '--source');
if (!source) throw new TorchError('COMBATRIG import requires --source', { code: 'IMPORT_SOURCE_REQUIRED' });
const sourceRepository = inspectRepository(resolve(cwd, source));
const proposal = analyzeCombatrigFleet({ repository: sourceRepository });
const output = optionValue(argv, '--output');
if (output) {
const path = resolve(cwd, output);
writeNewFile(path, `${JSON.stringify(proposal, null, 2)}\n`);
print({ proposal, output: path, mutationPerformed: true }, { json });
} else print({ proposal, mutationPerformed: false }, { json });
return proposal.compatibility.readyForApproval ? 0 : 1;
}
if (command === 'console') {
const operation = argv[1] ?? 'serve';
const root = resolve(cwd, optionValue(argv, '--repo') ?? '.');
if (operation === 'snapshot') {
print(observeProject({ repositoryRoot: root, env }), { json });
return 0;
}
if (operation === 'serve') {
const host = optionValue(argv, '--host') ?? '127.0.0.1';
const port = Number(optionValue(argv, '--port') ?? 4317);
if (!Number.isInteger(port) || port < 0 || port > 65535) {
throw new TorchError('Console port must be an integer from 0 through 65535', { code: 'CONSOLE_PORT_INVALID' });
}
startConsole({ repositoryRoot: root, env, host, port });
return 0;
}
throw new TorchError(`Unknown console operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
}
if (command === 'architect') {
const operation = argv[1] ?? 'plan';
const briefPath = resolve(cwd, optionValue(argv, '--brief') ?? '');
const brief = loadArchitectArtifact(briefPath, 'Fleet design brief');
if (operation === 'validate') {
const responsePath = resolve(cwd, optionValue(argv, '--response') ?? '');
const proposal = loadArchitectArtifact(responsePath, 'Session Architect proposal');
const validation = validateArchitectProposal({ brief, proposal });
print({ proposal, validation, mutationPerformed: false }, { json });
return validation.valid ? 0 : 1;
}
const provider = optionValue(argv, '--provider');
const model = optionValue(argv, '--model');
const budgetValue = optionValue(argv, '--max-budget-usd');
const maxBudgetUsd = budgetValue === undefined ? undefined : Number(budgetValue);
if (budgetValue !== undefined && (!Number.isFinite(maxBudgetUsd) || maxBudgetUsd <= 0)) {
throw new TorchError('--max-budget-usd must be a positive number', { code: 'ARCHITECT_BUDGET_INVALID' });
}
if (operation === 'plan') {
print(createArchitectRunPlan({ brief, provider, model, maxBudgetUsd }), { json });
return 0;
}
if (operation === 'run') {
if (!argv.includes('--yes')) {
throw new TorchError('Session Architect execution consumes provider quota. Review architect plan, then use --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
if (provider === 'claude' && maxBudgetUsd === undefined) {
throw new TorchError('Claude architect execution requires an explicit --max-budget-usd limit.', {
code: 'ARCHITECT_BUDGET_REQUIRED',
});
}
const output = optionValue(argv, '--output');
if (!output) throw new TorchError('Architect run requires --output for the validated pending proposal.', { code: 'OUTPUT_REQUIRED' });
const result = await runSessionArchitect({
brief, provider, model, maxBudgetUsd, authorized: true,
executor: (plan) => spawn(plan.command, plan.args, {
cwd: plan.cwd, input: plan.input, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 300_000,
}),
});
const path = resolve(cwd, output);
writeNewFile(path, `${JSON.stringify(result.proposal, null, 2)}\n`);
print({ ...result, output: path, mutationPerformed: true }, { json });
return 0;
}
throw new TorchError(`Unknown architect operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
}
const designCommand = ['init', 'analyze', 'domains', 'design', 'bootstrap'].includes(command);
const repositoryRoot = designCommand ? resolve(cwd, optionValue(argv, '--repo') ?? '.') : cwd;
const inspectedRepository = inspectRepository(repositoryRoot);
const repository = designCommand ? inspectedRepository
: inspectRepository(resolveInstalledProjectRoot(inspectedRepository.root, env));
if (command === 'profile') {
const operation = argv[1] ?? 'show';
const areaId = optionValue(argv, '--area');
const config = loadProjectConfig(repository.root);
if (operation === 'defaults') {
const runtime = optionValue(argv, '--runtime');
if (!argv.includes('--yes')) {
throw new TorchError('Changing project runtime defaults affects future launch plans. Review profile show, then re-run with --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
const registry = createRuntimeAdapterRegistry({ env, loadPlugins: true, pluginNames: [runtime] });
if (!runtime || runtime === 'default' || !registry.has(runtime)) {
throw new TorchError(`Runtime adapter is not configured: ${runtime ?? '<missing>'}`, {
code: 'RUNTIME_ADAPTER_MISSING', details: { runtime: runtime ?? null },
});
}
const model = optionValue(argv, '--model');
const reasoning = optionValue(argv, '--reasoning');
const policyOptions = launchPolicyOptions(argv);
const reset = argv.includes('--reset');
const resetLaunchPolicy = argv.includes('--reset-launch-policy');
const beforeSettings = structuredClone(config.runtimes[runtime] ?? registry.configuration(runtime) ?? {});
if (reset && (model !== undefined || reasoning !== undefined || Object.keys(policyOptions).length || resetLaunchPolicy)) {
throw new TorchError('--reset cannot be combined with profile values.', { code: 'RUNTIME_PROFILE_INVALID' });
}
if (resetLaunchPolicy && (Object.keys(policyOptions).length || reset)) {
throw new TorchError('--reset-launch-policy cannot be combined with launch-policy values or --reset.', { code: 'RUNTIME_PROFILE_INVALID' });
}
if (!reset && model === undefined && reasoning === undefined && !Object.keys(policyOptions).length && !resetLaunchPolicy) {
throw new TorchError('Set --model, --reasoning, --launch-policy, or use a reset option.', { code: 'RUNTIME_PROFILE_INVALID' });
}
if (!Object.hasOwn(config.runtimes, runtime)) config.runtimes[runtime] = registry.configuration(runtime) ?? {};
if (!reset && (Object.keys(policyOptions).length || resetLaunchPolicy)) {
migrateLegacyRuntimeLaunchPolicy(runtime, config.runtimes[runtime], registry.get(runtime));
}
if (reset) {
delete config.runtimes[runtime].model;
delete config.runtimes[runtime].reasoning;
delete config.runtimes[runtime].launchPolicy;
} else {
if (model !== undefined) config.runtimes[runtime].model = model;
if (reasoning === 'none') delete config.runtimes[runtime].reasoning;
else if (reasoning !== undefined) config.runtimes[runtime].reasoning = reasoning;
if (resetLaunchPolicy) delete config.runtimes[runtime].launchPolicy;
else if (Object.keys(policyOptions).length) {
config.runtimes[runtime].launchPolicy = {
...(config.runtimes[runtime].launchPolicy ?? {}), ...policyOptions,
};
}
}
assertRuntimeDefaultsSupported(config, runtime, registry);
const path = persistProfileConfig(repository.root, config);
const profile = {
model: config.runtimes[runtime].model ?? registry.configuration(runtime)?.model ?? null,
reasoning: config.runtimes[runtime].reasoning ?? registry.configuration(runtime)?.reasoning ?? null,
launchPolicy: resolveLaunchPolicy({
runtime, adapterDefaults: registry.configuration(runtime), runtimeConfig: config.runtimes[runtime],
}),
};
withControlPlane(repository, env, (controlPlane) => controlPlane.auditOwnerAction({
actorId: 'owner', operation: 'profile.change', entityType: 'runtime-default', entityId: runtime,
details: { before: beforeSettings, after: config.runtimes[runtime], effectiveProfile: profile, currentHead: repository.head },
}));
print({
runtime, profile,
path, mutationPerformed: true,
sessionsStarted: false, existingSessionChanged: false,
}, { json });
return 0;
}
if (!areaId) throw new TorchError('Runtime profile commands require --area <id>.', { code: 'FLEET_IDENTITY_REQUIRED' });
const identity = areaId === 'session-manager'
? config.session_manager : config.domains.find((area) => area.id === areaId);
if (!identity) throw new TorchError(`Unknown Fleet identity: ${areaId}`, { code: 'FLEET_IDENTITY_MISSING' });
if (operation === 'show') {
print(effectiveProfile(config, areaId, createRuntimeAdapterRegistry({ env })), { json });
return 0;
}
if (operation !== 'set') throw new TorchError(`Unknown profile operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
const dryRun = argv.includes('--dry-run');
if (!dryRun && !argv.includes('--yes')) {
throw new TorchError('Changing a project runtime profile affects future launch plans. Review profile show, then re-run with --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
const requestedRuntime = optionValue(argv, '--runtime');
const requestedModel = optionValue(argv, '--model');
const inheritModel = argv.includes('--inherit-model');
const requestedReasoning = optionValue(argv, '--reasoning');
const policyOptions = launchPolicyOptions(argv);
const reset = argv.includes('--reset');
const resetLaunchPolicy = argv.includes('--reset-launch-policy');
const registry = createRuntimeAdapterRegistry({
env, loadPlugins: true,
pluginNames: [...new Set([identity.runtime ?? config.runtimes.default, requestedRuntime].filter(Boolean))],
});
if (inheritModel && requestedModel !== undefined) {
throw new TorchError('--inherit-model cannot be combined with --model.', { code: 'RUNTIME_PROFILE_INVALID' });
}
if (reset && (requestedRuntime !== undefined || requestedModel !== undefined || inheritModel || requestedReasoning !== undefined
|| Object.keys(policyOptions).length || resetLaunchPolicy)) {
throw new TorchError('--reset cannot be combined with profile values.', { code: 'RUNTIME_PROFILE_INVALID' });
}
if (resetLaunchPolicy && (Object.keys(policyOptions).length || reset)) {
throw new TorchError('--reset-launch-policy cannot be combined with launch-policy values or --reset.', { code: 'RUNTIME_PROFILE_INVALID' });
}
if (!reset && requestedRuntime === undefined && requestedModel === undefined && !inheritModel && requestedReasoning === undefined
&& !Object.keys(policyOptions).length && !resetLaunchPolicy) {
throw new TorchError('Set at least one profile value or use a reset option.', {
code: 'RUNTIME_PROFILE_INVALID',
});
}
const beforeProfile = effectiveProfile(config, areaId, registry);
if (requestedRuntime !== undefined) {
if (!registry.has(requestedRuntime) || requestedRuntime === 'default') {
throw new TorchError(`Runtime adapter is not configured: ${requestedRuntime}`, {
code: 'RUNTIME_ADAPTER_MISSING', details: { runtime: requestedRuntime },
});
}
if (!Object.hasOwn(config.runtimes, requestedRuntime)) {
config.runtimes[requestedRuntime] = registry.configuration(requestedRuntime) ?? {};
}
identity.runtime = requestedRuntime;
}
if (reset) {
delete identity.model;
delete identity.reasoning;
delete identity.launchPolicy;
} else {
if (requestedModel !== undefined) identity.model = requestedModel;
else if (inheritModel) delete identity.model;
if (requestedReasoning === 'none') delete identity.reasoning;
else if (requestedReasoning !== undefined) identity.reasoning = requestedReasoning;
if (resetLaunchPolicy) delete identity.launchPolicy;
else if (Object.keys(policyOptions).length) identity.launchPolicy = {
...(identity.launchPolicy ?? {}), ...policyOptions,
};
}
assertEffectiveProfileSupported(config, areaId, registry);
const profile = effectiveProfile(config, areaId, registry);
const configPath = resolve(repository.root, '.torch', 'torch.yaml');
const configSha256 = fileHash(configPath);
const expectedConfigHash = optionValue(argv, '--expect-config-sha256');
if (expectedConfigHash && expectedConfigHash !== configSha256) {
throw new TorchError('Runtime configuration changed after review; inspect a fresh profile plan.', {
code: 'RUNTIME_PROFILE_REVIEW_STALE',
});
}
if (dryRun) {
print({
action: 'change-runtime-profile', beforeProfile, profile,
configSha256,
effect: 'Updates this identity’s profile for future launch plans only. It does not change or start a running session.',
mutationPerformed: false, sessionsStarted: false, existingSessionChanged: false,
}, { json });
return 0;
}
const path = persistProfileConfig(repository.root, config);
withControlPlane(repository, env, (controlPlane) => controlPlane.auditOwnerAction({
actorId: 'owner', operation: 'profile.change', entityType: 'runtime-profile', entityId: areaId,
details: { before: beforeProfile, after: profile, currentHead: repository.head },
}));
print({
profile, path, mutationPerformed: true,
sessionsStarted: false, existingSessionChanged: false,
}, { json });
return 0;
}
if (command === 'init' || command === 'analyze') {
const specifications = inspectSpecifications(optionValues(argv, '--spec'), {
cwd, repositoryRoot: repository.root,
});
const analysis = analyzeRepository(repository, { specifications });
print({
command, repository, analysis,
next: 'Run torch bootstrap with the same inputs for an AI Session Architect brief, or torch design for the deterministic baseline. Review the resulting proposal before install.',
}, { json });
return 0;
}
if (command === 'domains' || command === 'design' || command === 'bootstrap') {
const specifications = inspectSpecifications(optionValues(argv, '--spec'), {
cwd, repositoryRoot: repository.root,
});
const analysis = analyzeRepository(repository, { specifications });
const proposal = proposeDomains({ repository, analysis });
const artifact = command === 'bootstrap'
? createFleetDesignBrief({ repository, analysis, baseline: proposal })
: proposal;
const output = optionValue(argv, '--output');
if (output) {
const path = resolve(cwd, output);
writeNewFile(path, `${JSON.stringify(artifact, null, 2)}\n`);
print({ [command === 'bootstrap' ? 'brief' : 'proposal']: artifact, output: path, mutationPerformed: true }, { json });
} else {
print({ [command === 'bootstrap' ? 'brief' : 'proposal']: artifact, mutationPerformed: false }, { json });
}
return 0;
}
if (command === 'install') {
if (argv.includes('--restore')) {
const result = restoreProject({ repository, env, dryRun: argv.includes('--dry-run'), authorized: argv.includes('--yes') });
print(result, { json });
return 0;
}
const proposalPath = optionValue(argv, '--proposal');
const runtimeOption = optionValue(argv, '--runtime');
const defaultRuntime = optionValue(argv, '--default-runtime');
const runtimes = runtimeOption === undefined ? undefined : commaList(runtimeOption);
if (argv.includes('--dry-run')) {
const proposal = loadProposal(cwd, proposalPath);
const plan = planInstall({
repository, proposal, env, runtimes, defaultRuntime,
runtimeRegistry: createRuntimeAdapterRegistry({
env, loadPlugins: true, pluginNames: [...(runtimes ?? proposalRuntimeNames(proposal)), ...[defaultRuntime].filter(Boolean)],
}),
});
print({ ...plan, proposal: resolve(cwd, proposalPath),
...(argv.includes('--update-runtimes') ? { providerUpdates: planProviderUpdates(plan.runtimes, { env }) } : {}),
}, { json });
return 0;
}
if (!argv.includes('--yes')) {
throw new TorchError('Installation requires reviewed approval. Re-run with --dry-run, then --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
const proposal = loadProposal(cwd, proposalPath);
if (argv.includes('--update-runtimes')) {
const installPlan = planInstall({ repository, proposal, env, runtimes, defaultRuntime });
if (!installPlan.canProceed) throw new TorchError('Install plan has blockers', { code: 'INSTALL_BLOCKED', details: installPlan.blockers });
updateProviders(installPlan.runtimes, { env, runner: spawn, authorized: true });
}
const installed = installProject({
repository, proposal, env, runtimes, defaultRuntime,
runtimeRegistry: createRuntimeAdapterRegistry({
env, loadPlugins: true, pluginNames: [...(runtimes ?? proposalRuntimeNames(proposal)), ...[defaultRuntime].filter(Boolean)],
}),
});
if (argv.includes('--update-runtimes')) {
const config = loadProjectConfig(repository.root);
for (const name of Object.keys(config.runtimes).filter(name => name !== 'default')) {
config.runtimes[name].updatePolicy = { mode: 'auto', max_age_hours: 24 };
}
persistProfileConfig(repository.root, config);
}
print(argv.includes('--setup') ? { ...installed, setup: setupProject({ repository: inspectRepository(repository.root), env, authorized: true, parentOverride: optionValue(argv, '--parent') }) } : installed, { json });
return 0;
}
if (command === 'setup') {
print(setupProject({ repository, env, authorized: argv.includes('--yes'), dryRun: argv.includes('--dry-run'), parentOverride: optionValue(argv, '--parent') }), { json });
return 0;
}
if (command === 'doctor') {
const diagnosis = diagnoseProject({ repository, env });
print(diagnosis, { json });
return diagnosis.healthy ? 0 : 1;
}
if (command === 'worktrees') {
const parentOverride = optionValue(argv, '--parent');
if (argv.includes('--dry-run')) {
const plan = planWorktrees({ repository, parentOverride });
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (!argv.includes('--yes')) {
throw new TorchError('Worktree creation requires a reviewed dry run. Re-run with --dry-run, then --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(createWorktrees({ repository, parentOverride }), { json });
return 0;
}
if (command === 'up') {
const updateConfig = loadProjectConfig(repository.root);
if (argv.includes('--yes') && !argv.includes('--dry-run')) {
const selected = optionValue(argv, '--only') === undefined ? undefined : commaList(optionValue(argv, '--only'));
for (const name of configuredRuntimeNames(repository.root, selected)) {
const policy = updateConfig.runtimes[name]?.updatePolicy;
if (policy?.mode === 'auto') updateProviders([name], {
env, runner: spawn, authorized: true, maxAgeHours: policy.max_age_hours,
});
}
}
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const only = optionValue(argv, '--only') === undefined ? undefined : commaList(optionValue(argv, '--only'));
const adapters = runtimeAdapters(env, configuredRuntimeNames(repository.root, only));
const plan = planFleetUp({
repositoryRoot: repository.root, controlPlane: control, adapters, fresh: argv.includes('--fresh'),
only,
});
if (argv.includes('--dry-run')) {
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (!argv.includes('--yes')) {
throw new TorchError('Fleet startup launches configured AI runtimes. Review --dry-run, then re-run with --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(startFleet({
plan, controlPlane: control, adapters,
executor: (launch) => spawn(launch.command, launch.args, {
cwd: launch.cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
}),
}), { json });
return 0;
} finally {
control.close();
}
}
if (command === 'down') {
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const pluginNames = [...new Set([
...configuredRuntimeNames(repository.root), ...activeRuntimeNames(control),
])];
const adapters = runtimeAdapters(env, pluginNames);
const plan = planFleetDown({ repositoryRoot: repository.root, controlPlane: control, adapters });
if (argv.includes('--dry-run')) {
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (!argv.includes('--yes')) {
throw new TorchError('Fleet wind-down stops configured runtime sessions. Review --dry-run, then re-run with --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
const stoppers = runtimeStoppers(spawn, env, pluginNames);
print(stopFleet({
plan, controlPlane: control,
stopRuntime: (action) => stopThrough(stoppers, action),
}), { json });
return 0;
} finally {
control.close();
}
}
if (command === 'capture') {
print(withControlPlane(repository, env, (control) => captureFleet({
repositoryRoot: repository.root, controlPlane: control,
})), { json });
return 0;
}
if (command === 'detach') {
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const stoppers = runtimeStoppers(spawn, env, activeRuntimeNames(control));
const plan = planFleetDetach({
repositoryRoot: repository.root, controlPlane: control, adapters: stoppers,
});
if (argv.includes('--dry-run')) {
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (!argv.includes('--yes')) {
throw new TorchError('Fleet detach stops runtime sessions while preserving organization and worktrees. Review --dry-run, then use --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(detachFleet({
plan, controlPlane: control, stopRuntime: (action) => stopThrough(stoppers, action),
}), { json });
return 0;
} finally {
control.close();
}
}
if (command === 'identity') {
print(withControlPlane(repository, env, (control) => control.identity(optionValue(argv, '--area'))), { json });
return 0;
}
if (command === 'agents' || command === 'list') {
print(withControlPlane(repository, env, (control) => ({ agents: control.listAgents() })), { json });
return 0;
}
if (command === 'brief') {
print(withControlPlane(repository, env, (control) => createFleetBrief({
repositoryRoot: repository.root, controlPlane: control, areaId: optionValue(argv, '--area'),
})), { json });
return 0;
}
if (command === 'agent') {
print(withControlPlane(repository, env, (control) => control.getAgent(optionValue(argv, '--area'))), { json });
return 0;
}
if (command === 'roster') {
print(withControlPlane(repository, env, (control) => control.getRoster()), { json });
return 0;
}
if (command === 'who-owns') {
print(withControlPlane(repository, env, (control) => control.whoOwns({
path: optionValue(argv, '--path'), capability: optionValue(argv, '--capability'),
})), { json });
return 0;
}
if (command === 'neighbours') {
const areaId = optionValue(argv, '--area');
print(withControlPlane(repository, env, (control) => ({ areaId, neighbours: control.neighbours(areaId) })), { json });
return 0;
}
if (command === 'message') {
print(withControlPlane(repository, env, (control) => control.sendMessage({
sender: optionValue(argv, '--from'), recipient: optionValue(argv, '--to'),
body: optionValue(argv, '--body'), references: {
task: optionValue(argv, '--task'), path: optionValue(argv, '--path'),
commit: optionValue(argv, '--commit'), handoff: optionValue(argv, '--handoff'),
},
})), { json });
return 0;
}
if (command === 'inbox') {
print(withControlPlane(repository, env, (control) => ({
recipient: optionValue(argv, '--area'),
messages: control.readMessages({
recipient: optionValue(argv, '--area'), unacknowledgedOnly: argv.includes('--unacknowledged'),
limit: Number(optionValue(argv, '--limit') ?? 100),
}),
})), { json });
return 0;
}
if (command === 'ack') {
print(withControlPlane(repository, env, (control) => control.ackMessage({
recipient: optionValue(argv, '--area'), messageId: optionValue(argv, '--message'),
})), { json });
return 0;
}
if (command === 'status') {
print(withControlPlane(repository, env, (control) => control.reportStatus({
areaId: optionValue(argv, '--area'), state: optionValue(argv, '--state'),
summary: optionValue(argv, '--summary'), runtime: optionValue(argv, '--runtime'),
runtimeSessionId: optionValue(argv, '--session'), task: optionValue(argv, '--task'),
})), { json });
return 0;
}
if (command === 'complete' || command === 'blocked') {
const input = {
areaId: optionValue(argv, '--area'), summary: optionValue(argv, '--summary'),
task: optionValue(argv, '--task'), evidence: optionValue(argv, '--evidence'),
path: optionValue(argv, '--path'), commit: optionValue(argv, '--commit'),
};
print(withControlPlane(repository, env, (control) =>
(command === 'complete' ? control.reportComplete(input) : control.reportBlocked(input))), { json });
return 0;
}
if (command === 'coordinate') {
const participants = (optionValue(argv, '--with') ?? '').split(',').filter(Boolean);
print(withControlPlane(repository, env, (control) => control.requestCoordination({
sender: optionValue(argv, '--from'), participants, body: optionValue(argv, '--body'),
task: optionValue(argv, '--task'), path: optionValue(argv, '--path'),
})), { json });
return 0;
}
if (command === 'handoff') {
print(withControlPlane(repository, env, (control) => control.requestHandoff({
sender: optionValue(argv, '--from'), recipient: optionValue(argv, '--to') ?? 'session-manager',
path: optionValue(argv, '--path'), task: optionValue(argv, '--task'), reason: optionValue(argv, '--reason'),
})), { json });
return 0;
}
if (command === 'approvals') {
const operation = argv[1] ?? 'list';
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
if (operation === 'list') {
const actorId = optionValue(argv, '--area') ?? 'owner';
print({ approvals: control.listApprovals({ actorId, status: optionValue(argv, '--status') }) }, { json });
return 0;
}
if (operation === 'decide') {
const decidedBy = optionValue(argv, '--by');
if (decidedBy !== 'owner') {
throw new TorchError('AI approvals must be decided through the bound TORCH MCP identity.', {
code: 'APPROVER_AUTHORITY_REQUIRED',
});
}
if (!argv.includes('--yes')) {
throw new TorchError('Owner approval decisions require explicit --yes after reviewing the request.', {
code: 'APPROVAL_REQUIRED',
});
}
const result = control.decideApproval({
approvalId: optionValue(argv, '--id'), decidedBy,
decision: optionValue(argv, '--decision'), note: optionValue(argv, '--note'),
expectedRevision: Number(optionValue(argv, '--revision')) || undefined,
});
print(result, { json });
return 0;
}
throw new TorchError(`Unknown approvals operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
} finally {
control.close();
}
}
if (command === 'fleet') {
const operation = argv[1] ?? 'changes';
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const evolution = new FleetEvolutionService({ repositoryRoot: repository.root, controlPlane: control });
const hierarchy = new HierarchyEvolutionService({ repositoryRoot: repository.root, controlPlane: control });
const changeId = optionValue(argv, '--change');
const hierarchyProposalId = optionValue(argv, '--proposal');
if (operation === 'hierarchy-assess') print(hierarchy.assess({
assessor: optionValue(argv, '--from') ?? 'session-manager',
}), { json });
else if (operation === 'hierarchy-changes') print({ proposals: hierarchy.list({ state: optionValue(argv, '--state') }) }, { json });
else if (operation === 'hierarchy-get') print(hierarchy.get(hierarchyProposalId), { json });
else if (operation === 'hierarchy-propose') {
const input = loadJsonFile(cwd, optionValue(argv, '--proposal'), {
label: 'Hierarchy change proposal', code: 'HIERARCHY_PROPOSAL_INVALID',
});
print(hierarchy.propose({
proposer: optionValue(argv, '--from') ?? input.proposer,
proposal: input.proposal ?? input,
}), { json });
} else if (operation === 'hierarchy-decide') {
if (!argv.includes('--yes')) {
throw new TorchError('Owner review of a hierarchy proposal requires explicit --yes.', { code: 'APPROVAL_REQUIRED' });
}
print(hierarchy.decide({
proposalId: hierarchyProposalId, decision: optionValue(argv, '--decision'),
decidedBy: optionValue(argv, '--by'), reason: optionValue(argv, '--reason'),
}), { json });
} else if (operation === 'hierarchy-plan') print(hierarchy.planPilot(hierarchyProposalId), { json });
else if (operation === 'hierarchy-conclusion-plan') print(hierarchy.planConclusion({
proposalId: hierarchyProposalId, decision: optionValue(argv, '--decision'), reason: optionValue(argv, '--reason'),
}), { json });
else if (operation === 'hierarchy-conclude') print(hierarchy.concludePilot({
proposalId: hierarchyProposalId, decision: optionValue(argv, '--decision'), reason: optionValue(argv, '--reason'),
planHash: optionValue(argv, '--plan-hash'), decidedBy: optionValue(argv, '--by'), approved: argv.includes('--yes'),
}), { json });
else if (operation === 'hierarchy-pilot-reviews') print({ reviews: hierarchy.pilotReviews(hierarchyProposalId) }, { json });
else if (operation === 'hierarchy-review-pilot') {
if (!argv.includes('--yes')) throw new TorchError('Recording pilot review evidence requires explicit --yes.', { code: 'APPROVAL_REQUIRED' });
print(hierarchy.recordPilotReview({ proposalId: hierarchyProposalId,
reviewer: optionValue(argv, '--from') ?? 'session-manager',
review: loadJsonFile(cwd, optionValue(argv, '--review'), {
label: 'Hierarchy pilot review', code: 'HIERARCHY_REVIEW_INVALID',
}),
}), { json });
}
else if (operation === 'hierarchy-activate') {
if (!argv.includes('--yes')) {
throw new TorchError('Hierarchy activation writes and commits tracked organization state. Review hierarchy-plan, then use --yes; no runtime is started.', {
code: 'APPROVAL_REQUIRED',
});
}
print(hierarchy.activatePilot({
proposalId: hierarchyProposalId, activatedBy: optionValue(argv, '--by'),
}), { json });
}
else if (operation === 'changes') print({ changes: evolution.list({ state: optionValue(argv, '--state') }) }, { json });
else if (operation === 'get') print(evolution.get(changeId), { json });
else if (operation === 'assess') print(evolution.assessDomainNeeds({
assessor: optionValue(argv, '--from') ?? 'session-manager',
}), { json });
else if (operation === 'propose') {
const input = loadJsonFile(cwd, optionValue(argv, '--proposal'), {
label: 'Fleet domain proposal', code: 'FLEET_PROPOSAL_INVALID',
});
print(evolution.proposeDomain({
...input, proposer: optionValue(argv, '--from') ?? input.proposer,
}), { json });
} else if (operation === 'propose-retirement') {
const input = loadJsonFile(cwd, optionValue(argv, '--proposal'), {
label: 'Fleet domain retirement proposal', code: 'FLEET_PROPOSAL_INVALID',
});
print(evolution.proposeRetirement({
...input, areaId: input.area_id ?? input.areaId,
proposer: optionValue(argv, '--from') ?? input.proposer,
}), { json });
} else if (operation === 'propose-merge' || operation === 'propose-split') {
const input = loadJsonFile(cwd, optionValue(argv, '--proposal'), {
label: 'Fleet boundary proposal', code: 'FLEET_PROPOSAL_INVALID',
});
const request = {
...input,
proposer: optionValue(argv, '--from') ?? input.proposer,
sourceDomains: input.sourceDomains ?? input.source_domains,
resultDomains: input.resultDomains ?? input.result_domains,
ownershipAssignments: input.ownershipAssignments ?? input.ownership_assignments,
expectedBenefit: input.expectedBenefit ?? (input.expected_benefit ? {
summary: input.expected_benefit.summary,
recurringWork: input.expected_benefit.recurring_work,
contextLocality: input.expected_benefit.context_locality,
coordinationCost: input.expected_benefit.coordination_cost,
} : undefined),
};
print(operation === 'propose-merge'
? evolution.proposeMerge(request) : evolution.proposeSplit(request), { json });
} else if (operation === 'approve') {
if (!argv.includes('--yes')) {
throw new TorchError('Owner approval of a Fleet change requires explicit --yes.', { code: 'APPROVAL_REQUIRED' });
}
print(evolution.approve({ changeId, approvedBy: optionValue(argv, '--by') }), { json });
} else if (operation === 'reject') {
if (!argv.includes('--yes')) {
throw new TorchError('Owner rejection of a Fleet change requires explicit --yes.', { code: 'APPROVAL_REQUIRED' });
}
print(evolution.reject({
changeId, rejectedBy: optionValue(argv, '--by'), reason: optionValue(argv, '--reason'),
}), { json });
} else if (operation === 'plan') print(evolution.planActivation(changeId), { json });
else if (operation === 'activate') {
if (!argv.includes('--yes')) {
throw new TorchError('Fleet activation changes tracked organization state and may create or remove a worktree. Review fleet plan, then use --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(evolution.activate({ changeId, approvedBy: optionValue(argv, '--by') }), { json });
} else if (operation === 'start') {
const change = evolution.get(changeId);
if (change.type !== 'add-domain' || change.state !== 'active') {
throw new TorchError('Only an active Fleet domain can start a runtime session', {
code: 'FLEET_CHANGE_STATE_CONFLICT', details: { state: change.state },
});
}
const adapters = runtimeAdapters(env, [
...configuredRuntimeNames(repository.root), change.domain.runtime,
]);
const plan = planAreaUp({
repositoryRoot: repository.root, controlPlane: control, areaId: change.domain.id,
adapters, fresh: argv.includes('--fresh'),
});
if (argv.includes('--dry-run')) {
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (!argv.includes('--yes')) {
throw new TorchError('Starting the new domain invokes its configured AI runtime. Review --dry-run, then use --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(startFleet({
plan, controlPlane: control, adapters,
executor: (launch) => spawn(launch.command, launch.args, {
cwd: launch.cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
}),
}), { json });
} else throw new TorchError(`Unknown fleet operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
return 0;
} finally {
control.close();
}
}
if (command === 'backlog') {
const operation = argv[1] ?? 'list';
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const resources = new ResourceService({ repositoryRoot: repository.root, controlPlane: control });
const checks = new CheckService({
repositoryRoot: repository.root, controlPlane: control, resourceService: resources,
});
const integration = new IntegrationService({
repositoryRoot: repository.root, controlPlane: control, checkService: checks,
});
const backlog = new BacklogService({
repositoryRoot: repository.root, controlPlane: control, checkService: checks,
integrationLookup: (requestId) => integration.get(requestId),
});
if (operation === 'list') print({ tasks: backlog.list({
state: optionValue(argv, '--state'), owner: optionValue(argv, '--owner'),
}) }, { json });
else if (operation === 'next') print(backlog.next({
areaId: optionValue(argv, '--area'),
}), { json });
else if (operation === 'claim-next') {
const areaId = optionValue(argv, '--area');
if (argv.includes('--dry-run')) print(backlog.planClaim({ areaId }), { json });
else {
if (!argv.includes('--yes')) throw new TorchError('Self-claim requires explicit --yes and approved project policy.', {
code: 'APPROVAL_REQUIRED',
});
print(backlog.claimNext({ areaId }), { json });
}
}
else if (operation === 'health') print(backlog.health({
staleAfterDays: optionValue(argv, '--stale-days') === undefined
? 7 : Number(optionValue(argv, '--stale-days')),
staleObservedCommits: optionValue(argv, '--stale-observed-commits') === undefined
? 50 : Number(optionValue(argv, '--stale-observed-commits')),
}), { json });
else if (operation === 'activity') print(backlog.activity({
staleDays: optionValue(argv, '--stale-days') === undefined ? undefined : Number(optionValue(argv, '--stale-days')),
maxCommits: optionValue(argv, '--max-commits') === undefined ? undefined : Number(optionValue(argv, '--max-commits')),
}), { json });
else if (operation === 'close-landed') {
const input = { integrationRequest: optionValue(argv, '--integration'), actorId: optionValue(argv, '--area') };
if (argv.includes('--dry-run')) print(backlog.planLandedClosure(input), { json });
else {
if (!argv.includes('--yes')) throw new TorchError('Landed closure requires explicit --yes.', { code: 'APPROVAL_REQUIRED' });
print(backlog.reconcileLanded({ ...input, approved: true }), { json });
}
}
else if (operation === 'get') print(backlog.get(optionValue(argv, '--task')), { json });
else if (operation === 'create') print(backlog.create({
actorId: optionValue(argv, '--area'), title: optionValue(argv, '--title'),
description: optionValue(argv, '--description'), priority: optionValue(argv, '--priority') ?? 'normal',
acceptanceCriteria: commaList(optionValue(argv, '--accept')),
affectedDomains: commaList(optionValue(argv, '--domains')),
dependencies: commaList(optionValue(argv, '--depends')),
observedAt: optionValue(argv, '--observed-at'),
feature: optionValue(argv, '--feature'), milestone: optionValue(argv, '--milestone'),
}), { json });
else if (operation === 'classify') print(backlog.classify({
taskId: optionValue(argv, '--task'), actorId: optionValue(argv, '--area'),
expectedRevision: Number(optionValue(argv, '--revision')),
feature: optionValue(argv, '--feature'), milestone: optionValue(argv, '--milestone'),
clearFeature: argv.includes('--clear-feature'), clearMilestone: argv.includes('--clear-milestone'),
reason: optionValue(argv, '--reason'),
}), { json });
else if (operation === 'transition') print(backlog.transition({
taskId: optionValue(argv, '--task'), actorId: optionValue(argv, '--area'),
to: optionValue(argv, '--state'), expectedRevision: Number(optionValue(argv, '--revision')),
owner: optionValue(argv, '--owner'), evidence: commaList(optionValue(argv, '--evidence')),
commit: optionValue(argv, '--commit'), integrationRequest: optionValue(argv, '--integration'),
blockedReason: optionValue(argv, '--reason'), note: optionValue(argv, '--note'),
}), { json });
else throw new TorchError(`Unknown backlog operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
return 0;
} finally {
control.close();
}
}
if (command === 'context') {
const operation = argv[1] ?? 'report';
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const telemetry = new ContextTelemetryService({ controlPlane: control });
if (operation === 'report') print(telemetry.report({
areaId: optionValue(argv, '--area'), task: optionValue(argv, '--task'),
commit: optionValue(argv, '--commit'), measurement: optionValue(argv, '--measurement'),
}), { json });
else if (operation === 'record') print(telemetry.record({
areaId: optionValue(argv, '--area'), source: optionValue(argv, '--source'),
measurement: optionValue(argv, '--measurement'), runtime: optionValue(argv, '--runtime'),
runtimeSessionId: optionValue(argv, '--session'), task: optionValue(argv, '--task'),
commit: optionValue(argv, '--commit'), cachedInput: optionValue(argv, '--cached-input'),
uncachedInput: optionValue(argv, '--uncached-input'), cacheCreation: optionValue(argv, '--cache-creation'),
cacheRead: optionValue(argv, '--cache-read'), compactions: optionValue(argv, '--compactions'),
resumedPromptBytes: optionValue(argv, '--resumed-prompt-bytes'), costMicrousd: optionValue(argv, '--cost-microusd'),
verifiedItems: optionValue(argv, '--verified-items'),
}), { json });
else throw new TorchError(`Unknown context operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
return 0;
} finally {
control.close();
}
}
if (command === 'schedules') {
const operation = argv[1] ?? 'list';
if (operation === 'launcher') {
const launcherOperation = argv[2] ?? 'status';
const launcher = new ScheduleLauncherService({
repositoryRoot: repository.root, env,
command: [process.execPath, new URL('../bin/torch.mjs', import.meta.url).pathname],
});
if (launcherOperation === 'plan') {
const plan = launcher.plan();
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (launcherOperation === 'plan-reconcile') {
const plan = launcher.planReconcile();
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (launcherOperation === 'status') {
print(launcher.status(), { json });
return 0;
}
if (!['install', 'reconcile', 'remove'].includes(launcherOperation)) {
throw new TorchError(`Unknown schedule launcher operation: ${launcherOperation}`, { code: 'UNKNOWN_COMMAND' });
}
if (!argv.includes('--yes')) {
throw new TorchError('Persistent schedule launcher changes user systemd state. Review the plan, then re-run with --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
const result = launcherOperation === 'install' ? launcher.install()
: launcherOperation === 'reconcile' ? launcher.reconcile() : launcher.remove();
print(result, { json });
return 0;
}
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const schedules = new ScheduleService({
repositoryRoot: repository.root, controlPlane: control,
wakeManager: {
plan: ({ managerId, maxUsd }) => {
const adapters = runtimeAdapters(env, configuredRuntimeNames(repository.root, [managerId]));
const plan = planAreaUp({
repositoryRoot: repository.root, controlPlane: control, areaId: managerId,
adapters, maxCostUsd: maxUsd,
});
return { plan, adapters };
},
invoke: ({ managerId, maxUsd, prepared }) => {
const { plan, adapters } = prepared;
const plannedAction = plan.actions?.find((action) => action.areaId === managerId);
if (!plan.canProceed || !plannedAction || plan.actions.length !== 1
|| (maxUsd !== undefined && (plannedAction.costCeiling?.enforced !== true
|| plannedAction.costCeiling.maxUsd !== maxUsd))) {
throw new TorchError(`Manager runtime wake is blocked for ${managerId}`, {
code: 'MANAGER_WAKE_BLOCKED', details: plan.blockers,
});
}
return startFleet({
plan, controlPlane: control, adapters,
executor: (launch) => spawn(launch.command, launch.args, {
cwd: launch.cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
timeout: 300_000, maxBuffer: 8 * 1024 * 1024, killSignal: 'SIGTERM',
}),
});
},
},
});
const scheduleId = optionValue(argv, '--id');
const actorId = optionValue(argv, '--actor') ?? 'owner';
if (operation === 'dispatch-system') {
if (!argv.includes('--yes')) throw new TorchError('System schedule dispatch requires installed-launcher approval.', { code: 'APPROVAL_REQUIRED' });
assertLauncherDigest(repository.root, optionValue(argv, '--launcher-digest'));
const dispatched = schedules.dispatchSystem({ actorId: 'owner', approved: true });
print(dispatched, { json });
return dispatched.runs.every((run) => run.result === 'succeeded') ? 0 : 1;
}
if (operation === 'list') print({ schedules: schedules.list({ actorId }) }, { json });
else if (operation === 'runs') print({ runs: schedules.runs({ scheduleId }) }, { json });
else if (operation === 'wakes') print({ reservations: schedules.wakeReservations({
managerId: optionValue(argv, '--manager'),
}) }, { json });
else if (operation === 'recover-wake') print(schedules.recoverWake({
reservationId: optionValue(argv, '--reservation'), actorId,
approved: argv.includes('--yes'), runtimeStopped: argv.includes('--runtime-stopped'),
note: optionValue(argv, '--note'),
}), { json });
else if (operation === 'plan') print(schedules.plan({ scheduleId, actorId }), { json });
else if (operation === 'run') {
const result = schedules.run({ scheduleId, actorId, approved: argv.includes('--yes') });
print(result, { json });
return result.result === 'succeeded' ? 0 : 1;
} else throw new TorchError(`Unknown schedules operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
return 0;
} finally {
control.close();
}
}
if (command === 'checks') {
const operation = argv[1] ?? 'list';
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const resources = new ResourceService({ repositoryRoot: repository.root, controlPlane: control });
const checks = new CheckService({
repositoryRoot: repository.root, controlPlane: control, resourceService: resources,
});
if (operation === 'list') print({ checks: checks.listChecks() }, { json });
else if (operation === 'receipts') print({ receipts: checks.receipts({
commit: optionValue(argv, '--commit'), checkId: optionValue(argv, '--id'),
areaId: optionValue(argv, '--area'),
}) }, { json });
else if (operation === 'plan') print(checks.plan({
checkId: optionValue(argv, '--id'), areaId: optionValue(argv, '--area'),
}), { json });
else if (operation === 'prepared') print({ prepared: checks.prepared({
areaId: optionValue(argv, '--area'),
}) }, { json });
else if (operation === 'recovery-plan') print(checks.planPreparedRecovery({
preparedId: optionValue(argv, '--prepared'), actorId: 'owner',
}), { json });
else if (operation === 'recover-prepared') print(checks.recoverPrepared({
preparedId: optionValue(argv, '--prepared'), actorId: 'owner', approved: argv.includes('--yes'),
executorsStopped: argv.includes('--executors-stopped'), evidence: optionValue(argv, '--evidence'),
}), { json });
else if (['run', 'prepare', 'run-prepared', 'cancel-prepared'].includes(operation)) {
if (!argv.includes('--yes')) {
throw new TorchError('Configured check execution requires an explicit --yes.', { code: 'APPROVAL_REQUIRED' });
}
const input = { checkId: optionValue(argv, '--id'), areaId: optionValue(argv, '--area'),
preparedId: optionValue(argv, '--prepared') };
const action = { run: 'run', prepare: 'prepare', 'run-prepared': 'runPrepared', 'cancel-prepared': 'cancelPrepared' }[operation];
print(checks[action](input), { json });
} else throw new TorchError(`Unknown checks operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
return 0;
} finally {
control.close();
}
}
if (command === 'converge') {
const operation = argv[1] ?? 'plan';
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const convergence = new ConvergenceService({ repositoryRoot: repository.root, controlPlane: control });
const areaId = optionValue(argv, '--area');
if (operation === 'plan') print(convergence.plan({ areaId }), { json });
else if (operation === 'guards') print({ guards: convergence.guards(areaId) }, { json });
else if (operation === 'hold') {
const type = optionValue(argv, '--type');
if (!['measurement', 'pin'].includes(type)) {
throw new TorchError('CLI guards may only hold measurement or pin; check guards are automatic.', {
code: 'WORKTREE_GUARD_INVALID', details: { type: type ?? null },
});
}
print(convergence.hold({ areaId, type, reason: optionValue(argv, '--reason') }), { json });
} else if (operation === 'release') {
const type = optionValue(argv, '--type');
if (!['measurement', 'pin'].includes(type)) {
throw new TorchError('CLI guards may only release measurement or pin; check guards are automatic.', {
code: 'WORKTREE_GUARD_INVALID', details: { type: type ?? null },
});
}
print(convergence.release({ areaId, type }), { json });
} else if (operation === 'run') {
if (!argv.includes('--yes')) {
throw new TorchError('Convergence merges canonical state into a domain worktree. Review converge plan, then re-run with --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(convergence.converge({ areaId }), { json });
} else throw new TorchError(`Unknown converge operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
return 0;
} finally {
control.close();
}
}
if (command === 'resources') {
const operation = argv[1] ?? 'list';
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const resources = new ResourceService({ repositoryRoot: repository.root, controlPlane: control });
const resourceId = optionValue(argv, '--id');
const areaId = optionValue(argv, '--area');
if (operation === 'list') print({ resources: resources.list() }, { json });
else if (operation === 'status') print(resources.status(resourceId), { json });
else if (operation === 'acquire') print(resources.acquire({ resourceId, areaId }), { json });
else if (operation === 'release') print(resources.release({ resourceId, areaId }), { json });
else if (operation === 'cancel') print(resources.cancel({ resourceId, areaId }), { json });
else throw new TorchError(`Unknown resources operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
return 0;
} finally {
control.close();
}
}
if (command === 'integrate') {
const operation = argv[1] ?? 'list';
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const resources = new ResourceService({ repositoryRoot: repository.root, controlPlane: control });
const checks = new CheckService({
repositoryRoot: repository.root, controlPlane: control, resourceService: resources,
});
const integration = new IntegrationService({
repositoryRoot: repository.root, controlPlane: control, checkService: checks,
});
const requestId = optionValue(argv, '--request');
const actorId = optionValue(argv, '--area');
if (operation === 'list') print({ requests: integration.list({ state: optionValue(argv, '--state') }) }, { json });
else if (operation === 'request') print(integration.request({
areaId: actorId, commit: optionValue(argv, '--commit'),
}), { json });
else if (operation === 'evaluate') print(integration.evaluate(requestId), { json });
else if (operation === 'authorize') print(integration.authorize({ requestId, actorId }), { json });
else if (operation === 'plan') print(integration.planLanding({ requestId, actorId }), { json });
else if (operation === 'land') {
if (!argv.includes('--yes')) {
throw new TorchError('Landing changes canonical main. Review integrate plan, then re-run with --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(integration.land({ requestId, actorId }), { json });
} else if (operation === 'drain') {
if (!argv.includes('--yes')) {
throw new TorchError('Draining the authorized FIFO integration queue changes canonical main. Review integrate list, then re-run with --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
const limit = optionValue(argv, '--limit') === undefined ? 50 : Number(optionValue(argv, '--limit'));
print(integration.drain({ actorId, limit }), { json });
} else throw new TorchError(`Unknown integrate operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
return 0;
} finally {
control.close();
}
}
if (command === 'canonical') {
const operation = argv[1] ?? 'plan';
if (operation === 'plan') {
const plan = planLocalCanonical({ repositoryRoot: repository.root });
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (operation === 'create') {
if (!argv.includes('--yes')) {
throw new TorchError('Local canonical creation changes Git configuration. Review canonical plan, then use --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(createLocalCanonical({ repositoryRoot: repository.root }), { json });
return 0;
}
throw new TorchError(`Unknown canonical operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
}
if (command === 'recoverability') {
print(classifyRecoverability({
repositoryRoot: repository.root, commit: optionValue(argv, '--commit') ?? 'HEAD',
}), { json });
return 0;
}
if (command === 'artifacts') {
const operation = argv[1] ?? 'list';
if (operation === 'list') {
const snapshot = observeProject({ repositoryRoot: repository.root, env });
print(snapshot.artifacts ?? { available: false, items: [] }, { json });
return 0;
}
if (operation === 'comment' && !argv.includes('--yes')) {
const by = optionValue(argv, '--by');
if (by !== 'owner') throw new TorchError('Artifact feedback preview requires --by owner.', {
code: 'OWNER_AUTHORITY_REQUIRED',
});
const artifactId = optionValue(argv, '--artifact');
const artifact = (observeProject({ repositoryRoot: repository.root, env }).artifacts?.items ?? [])
.find((item) => item.id === artifactId);
if (!artifact) throw new TorchError(`Unknown artifact: ${artifactId}`, { code: 'ARTIFACT_NOT_FOUND' });
const body = optionValue(argv, '--body');
if (!body?.trim()) throw new TorchError('Artifact feedback body is required.', { code: 'INVALID_ARTIFACT_INPUT' });
print({
schema: 'torch.dev/artifact-feedback-plan/v1alpha1', mutationPerformed: false,
artifactId, body: body.trim(), by, recipient: artifact.identityId,
taskId: artifact.taskId, commit: artifact.commit,
effect: 'Create one durable owner message for the responsible identity, linked to this artifact and backlog task.',
}, { json });
return 0;
}
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const backlog = new BacklogService({ repositoryRoot: repository.root, controlPlane: control });
const artifacts = new ArtifactService({ repositoryRoot: repository.root, controlPlane: control, backlogService: backlog });
if (operation === 'publish') {
print(artifacts.publish({
areaId: optionValue(argv, '--area'), taskId: optionValue(argv, '--task'),
title: optionValue(argv, '--title'), alt: optionValue(argv, '--alt'),
file: optionValue(argv, '--file'), commit: optionValue(argv, '--commit'),
}), { json });
} else if (operation === 'comment') {
print(artifacts.comment({
artifactId: optionValue(argv, '--artifact'), body: optionValue(argv, '--body'),
by: optionValue(argv, '--by'),
}), { json });
} else throw new TorchError(`Unsupported artifacts operation: ${operation}`, { code: 'CLI_USAGE' });
} finally { control.close(); }
return 0;
}
if (command === 'forge') {
const operation = argv[1] ?? 'status';
if (operation === 'fetch') {
if (argv[2] === 'status') {
print({ fetches: observeProject({ repositoryRoot: repository.root, env }).canonicalFetches ?? [],
evidence: 'Recorded operations; running records do not establish live executor state' }, { json });
return 0;
}
if (!argv.includes('--yes')) {
if (argv[2] !== 'plan') throw new TorchError('Review forge fetch plan, then confirm with --yes.', { code: 'APPROVAL_REQUIRED' });
print(planCanonicalFetch({ repositoryRoot: repository.root }), { json });
return 0;
}
const result = withControlPlane(repository, env, (controlPlane) => fetchCanonicalObjects({
repositoryRoot: repository.root, controlPlane, attempts: Number(optionValue(argv, '--attempts') ?? 3),
}));
print(result, { json });
return result.succeeded ? 0 : 1;
}
if (operation === 'sync') {
if ((argv[2] ?? 'plan') === 'plan') {
const plan = planForgeSync({ repositoryRoot: repository.root });
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (!argv.includes('--yes')) {
throw new TorchError('Forge sync publishes the current canonical commit to the configured remote. Review forge sync plan, then use --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
print(syncForgeRemote({ repositoryRoot: repository.root, controlPlane: control, actorId: 'owner' }), { json });
} finally { control.close(); }
return 0;
}
if (operation === 'status') {
const status = forgeStatus({ repositoryRoot: repository.root });
print(status, { json });
return status.available === false ? 1 : 0;
}
if (operation === 'plan') {
const plan = planForgeAttach({
repositoryRoot: repository.root, remote: optionValue(argv, '--remote'),
provider: optionValue(argv, '--provider') ?? 'generic-git',
});
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (operation === 'attach') {
if (!argv.includes('--yes')) {
throw new TorchError('Forge attach changes tracked canonical-repository policy. Review forge plan, then use --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(attachForge({
repositoryRoot: repository.root, remote: optionValue(argv, '--remote'),
provider: optionValue(argv, '--provider') ?? 'generic-git',
}), { json });
return 0;
}
if (operation === 'detach') {
if (argv.includes('--dry-run')) {
const plan = planForgeDetach({ repositoryRoot: repository.root });
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (!argv.includes('--yes')) {
throw new TorchError('Forge detach changes tracked canonical-repository policy. Review the current status, then use --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(detachForge({ repositoryRoot: repository.root }), { json });
return 0;
}
throw new TorchError(`Unknown forge operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
}
if (command === 'digest') {
const operation = argv[1] ?? 'build';
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const service = new OwnerDigestService({ repositoryRoot: repository.root, controlPlane: control });
if (operation === 'build') {
const report = service.build();
if (json) print(report, { json }); else process.stdout.write(report.markdown);
} else if (operation === 'latest') print(service.latest(), { json });
else if (operation === 'publish') print(service.publish({ actorId: optionValue(argv, '--by'), approved: argv.includes('--yes') }), { json });
else throw new TorchError('Unknown digest operation', { code: 'UNKNOWN_COMMAND' });
return 0;
} finally { control.close(); }
}
if (command === 'delivery') {
const operation = argv[1] ?? 'list';
if (operation === 'configure') {
const input = {
repositoryRoot: repository.root,
releaseProvider: optionValue(argv, '--release-provider') ?? 'none',
deploymentProvider: optionValue(argv, '--deployment-provider') ?? 'none',
};
if (argv.includes('--dry-run')) {
const plan = planDeliveryConfiguration(input);
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (!argv.includes('--yes')) {
throw new TorchError('Delivery adapter configuration changes tracked project policy. Review --dry-run, then use --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
print(configureDelivery(input), { json });
return 0;
}
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const resources = new ResourceService({ repositoryRoot: repository.root, controlPlane: control });
const checks = new CheckService({
repositoryRoot: repository.root, controlPlane: control, resourceService: resources,
});
const delivery = new DeliveryService({
repositoryRoot: repository.root, controlPlane: control, checkService: checks,
});
const deliveryId = optionValue(argv, '--delivery');
if (operation === 'list') print({ deliveries: delivery.list({ state: optionValue(argv, '--state') }) }, { json });
else if (operation === 'attempts') print({ attempts: delivery.attempts({ deliveryId }) }, { json });
else if (operation === 'operations') print({ operations: delivery.operations({ deliveryId }) }, { json });
else if (operation === 'recover-not-applied') print(delivery.recoverNotApplied({
operationId: optionValue(argv, '--operation'), actor: optionValue(argv, '--by'),
approved: argv.includes('--yes'), runtimeStopped: argv.includes('--runtime-stopped'),
evidence: commaList(optionValue(argv, '--evidence')),
}), { json });
else if (operation === 'recover-succeeded') {
const input = { operationId: optionValue(argv, '--operation'), actor: optionValue(argv, '--by') };
if (argv.includes('--dry-run')) print(delivery.planSucceededRecovery(input), { json });
else print(delivery.reconcileSucceeded({ ...input, approved: argv.includes('--yes'),
runtimeStopped: argv.includes('--runtime-stopped'), evidence: commaList(optionValue(argv, '--evidence')),
}), { json });
}
else if (operation === 'get') print(delivery.get(deliveryId), { json });
else if (operation === 'create') print(delivery.create({
sourceArea: optionValue(argv, '--area'), commit: optionValue(argv, '--commit') ?? 'HEAD',
label: optionValue(argv, '--label'), evidence: commaList(optionValue(argv, '--evidence')),
}), { json });
else if (operation === 'plan') {
const plan = delivery.planTransition({
deliveryId, targetState: optionValue(argv, '--to'), actor: optionValue(argv, '--by'),
});
print(plan, { json });
return plan.canProceed ? 0 : 1;
} else if (operation === 'transition') {
print(delivery.transition({
deliveryId, targetState: optionValue(argv, '--to'), actor: optionValue(argv, '--by'),
evidence: commaList(optionValue(argv, '--evidence')), approved: argv.includes('--yes'),
}), { json });
} else throw new TorchError(`Unknown delivery operation: ${operation}`, { code: 'UNKNOWN_COMMAND' });
return 0;
} finally {
control.close();
}
}
if (command === 'uninstall') {
const purge = argv.includes('--purge');
const dryRun = argv.includes('--dry-run');
if (!dryRun && !argv.includes('--yes')) {
throw new TorchError(purge
? 'Purging TORCH removes tracked configuration, managed worktrees, and local state. Review --dry-run, then re-run with --purge --yes.'
: 'Uninstall stops the Fleet and removes owned persistent runtime integrations while preserving project state. Review --dry-run, then re-run with --yes.', {
code: 'APPROVAL_REQUIRED',
});
}
if (!purge) {
const control = openControlPlane({ repositoryRoot: repository.root, env });
try {
const stoppers = runtimeStoppers(spawn, env, activeRuntimeNames(control));
const fleet = planFleetDetach({
repositoryRoot: repository.root, controlPlane: control, adapters: stoppers,
});
const launcher = new ScheduleLauncherService({ repositoryRoot: repository.root, env });
const runtimeIntegrations = launcher.planRemoval();
const plan = {
action: 'uninstall', projectId: fleet.projectId, fleet, runtimeIntegrations,
blockers: [...fleet.blockers, ...runtimeIntegrations.blockers],
canProceed: fleet.canProceed && runtimeIntegrations.canProceed,
preserved: ['tracked organization', 'worktrees', 'branches', 'local state'],
mutationPerformed: false,
};
if (dryRun) {
print(plan, { json });
return plan.canProceed ? 0 : 1;
}
if (!plan.canProceed) throw new TorchError('TORCH uninstall stopped because active work or changed runtime integrations remain', {
code: 'UNSAFE_TO_UNINSTALL', details: plan.blockers,
});
const detached = detachFleet({
plan: fleet, controlPlane: control, stopRuntime: (action) => stopThrough(stoppers, action),
});
const removedRuntimeIntegrations = launcher.remove();
print({
...plan, detached, removedRuntimeIntegrations,
mutationPerformed: true,
}, { json });
return 0;
} finally {
control.close();
}
}
const result = uninstallProject({
repository,
purge,
dryRun,
env,
});
print(result, { json });
return result.canProceed === false ? 1 : 0;
}
throw new TorchError(`Unknown command: ${command}`, { code: 'UNKNOWN_COMMAND' });
} catch (error) {
print(asErrorRecord(error), { json: true });
return error instanceof TorchError ? 2 : 1;
}
}