@kal-elsam/kairo-runtime 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +937 -0
- package/bin/harness.js +11 -0
- package/bin/kairo-runtime.js +11 -0
- package/bin/kairo.js +11 -0
- package/global-template/components/catalog.json +25 -0
- package/global-template/components/orchestrator/orchestrator.md +55 -0
- package/global-template/components/sdd-core/handoff.md +38 -0
- package/global-template/components/sdd-core/spec-sizing.md +38 -0
- package/global-template/components/sdd-core/workflow.md +31 -0
- package/global-template/core/orchestrator.md +55 -0
- package/package.json +64 -0
- package/prompts/HARNESS_ENFORCEMENT_UPGRADE.md +11 -0
- package/prompts/HARNESS_ENTERPRISE.md +33 -0
- package/prompts/HARNESS_INSTALLER_MASTER.md +853 -0
- package/prompts/HARNESS_MINIMAL.md +30 -0
- package/prompts/HARNESS_SPEC_SIZING_UPGRADE.md +41 -0
- package/prompts/HARNESS_STANDARD.md +35 -0
- package/prompts/HARNESS_UNIVERSAL_PARITY.md +56 -0
- package/repo-template/.claude/agents/adapter-parity-reviewer.md +17 -0
- package/repo-template/.claude/agents/architect.md +17 -0
- package/repo-template/.claude/agents/eval-engineer.md +17 -0
- package/repo-template/.claude/agents/reviewer.md +17 -0
- package/repo-template/.claude/agents/sdd-reviewer.md +17 -0
- package/repo-template/.claude/agents/sdd-spec-writer.md +17 -0
- package/repo-template/.claude/skills/checkpoint/SKILL.md +13 -0
- package/repo-template/.claude/skills/eval-design/SKILL.md +3 -0
- package/repo-template/.claude/skills/evals/SKILL.md +13 -0
- package/repo-template/.claude/skills/loop-engineering/SKILL.md +16 -0
- package/repo-template/.claude/skills/maintainability-review/SKILL.md +3 -0
- package/repo-template/.claude/skills/quality-gate-review/SKILL.md +3 -0
- package/repo-template/.claude/skills/sdd/SKILL.md +13 -0
- package/repo-template/.claude/skills/spec-complexity-classifier/SKILL.md +10 -0
- package/repo-template/.claude/skills/spec-escalation-review/SKILL.md +10 -0
- package/repo-template/.claude/skills/spec-intake/SKILL.md +10 -0
- package/repo-template/.claude/skills/tdd/SKILL.md +13 -0
- package/repo-template/.claude/skills/trust-review/SKILL.md +3 -0
- package/repo-template/.codex/skills/adapter-parity/SKILL.md +29 -0
- package/repo-template/.codex/skills/checkpoint/SKILL.md +36 -0
- package/repo-template/.codex/skills/eval-design/SKILL.md +3 -0
- package/repo-template/.codex/skills/evals/SKILL.md +28 -0
- package/repo-template/.codex/skills/loop-engineering/SKILL.md +24 -0
- package/repo-template/.codex/skills/maintainability-review/SKILL.md +3 -0
- package/repo-template/.codex/skills/quality-gate-review/SKILL.md +3 -0
- package/repo-template/.codex/skills/sdd/SKILL.md +30 -0
- package/repo-template/.codex/skills/spec-complexity-classifier/SKILL.md +10 -0
- package/repo-template/.codex/skills/spec-escalation-review/SKILL.md +10 -0
- package/repo-template/.codex/skills/spec-intake/SKILL.md +10 -0
- package/repo-template/.codex/skills/tdd/SKILL.md +27 -0
- package/repo-template/.codex/skills/trust-review/SKILL.md +3 -0
- package/repo-template/.cursor/agents/adapter-parity-reviewer.md +29 -0
- package/repo-template/.cursor/agents/architect.md +27 -0
- package/repo-template/.cursor/agents/context-engineer.md +27 -0
- package/repo-template/.cursor/agents/debugger.md +27 -0
- package/repo-template/.cursor/agents/eval-engineer.md +27 -0
- package/repo-template/.cursor/agents/harness-orchestrator.md +27 -0
- package/repo-template/.cursor/agents/loop-controller.md +17 -0
- package/repo-template/.cursor/agents/loop-debugger.md +16 -0
- package/repo-template/.cursor/agents/reviewer.md +27 -0
- package/repo-template/.cursor/agents/sdd/sdd-acceptance-reviewer.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-implementation-guardian.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-intake-analyst.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-orchestrator.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-spec-reviewer.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-spec-writer.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-task-breakdown.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-technical-planner.md +31 -0
- package/repo-template/.cursor/agents/sdd/sdd-test-planner.md +31 -0
- package/repo-template/.cursor/agents/spec-intake-analyst.md +14 -0
- package/repo-template/.cursor/agents/test-engineer.md +27 -0
- package/repo-template/.cursor/commands/adapter-parity.md +34 -0
- package/repo-template/.cursor/commands/checkpoint.md +30 -0
- package/repo-template/.cursor/commands/debug.md +13 -0
- package/repo-template/.cursor/commands/init-eval.md +12 -0
- package/repo-template/.cursor/commands/init-feature.md +12 -0
- package/repo-template/.cursor/commands/init-harness.md +13 -0
- package/repo-template/.cursor/commands/loop-debug.md +3 -0
- package/repo-template/.cursor/commands/loop-review.md +3 -0
- package/repo-template/.cursor/commands/loop-run.md +5 -0
- package/repo-template/.cursor/commands/review.md +21 -0
- package/repo-template/.cursor/commands/sdd-intake.md +18 -0
- package/repo-template/.cursor/commands/sdd-plan.md +18 -0
- package/repo-template/.cursor/commands/sdd-spec.md +18 -0
- package/repo-template/.cursor/commands/sdd-tasks.md +18 -0
- package/repo-template/.cursor/commands/sdd-validate.md +18 -0
- package/repo-template/.cursor/commands/spec-escalate.md +9 -0
- package/repo-template/.cursor/commands/spec-intake.md +10 -0
- package/repo-template/.cursor/commands/spec-size.md +21 -0
- package/repo-template/.cursor/rules/adapter-parity.mdc +20 -0
- package/repo-template/.cursor/rules/api.mdc +12 -0
- package/repo-template/.cursor/rules/core.mdc +21 -0
- package/repo-template/.cursor/rules/enforcement.mdc +16 -0
- package/repo-template/.cursor/rules/evals.mdc +10 -0
- package/repo-template/.cursor/rules/git.mdc +19 -0
- package/repo-template/.cursor/rules/harness.mdc +14 -0
- package/repo-template/.cursor/rules/loops.mdc +17 -0
- package/repo-template/.cursor/rules/testing.mdc +13 -0
- package/repo-template/.cursor/rules/ui.mdc +13 -0
- package/repo-template/.cursor/skills/api-design/SKILL.md +23 -0
- package/repo-template/.cursor/skills/architecture/SKILL.md +23 -0
- package/repo-template/.cursor/skills/code-review/SKILL.md +23 -0
- package/repo-template/.cursor/skills/context-graph/SKILL.md +23 -0
- package/repo-template/.cursor/skills/debugging/SKILL.md +23 -0
- package/repo-template/.cursor/skills/evals/SKILL.md +23 -0
- package/repo-template/.cursor/skills/git-workflow/SKILL.md +23 -0
- package/repo-template/.cursor/skills/testing/SKILL.md +23 -0
- package/repo-template/.cursor/skills/ui-design/SKILL.md +23 -0
- package/repo-template/.gentle-ai/README.md +37 -0
- package/repo-template/.gentle-ai/loops/README.md +27 -0
- package/repo-template/.gentle-ai/loops/sdd-tdd-repair.loop.md +27 -0
- package/repo-template/.gentle-ai/model-routing.md +25 -0
- package/repo-template/.github/copilot-instructions.md +37 -0
- package/repo-template/.github/dependabot.yml +11 -0
- package/repo-template/.github/instructions/harness.instructions.md +11 -0
- package/repo-template/.github/workflows/harness-quality-gate.yml +67 -0
- package/repo-template/.github/workflows/harness-security-gate.yml +32 -0
- package/repo-template/.harness/spec-sizing.json +33 -0
- package/repo-template/.opencode/README.md +71 -0
- package/repo-template/.opencode/agents/adapter-parity-reviewer.md +23 -0
- package/repo-template/.opencode/agents/architect.md +38 -0
- package/repo-template/.opencode/agents/checkpoint-reviewer.md +38 -0
- package/repo-template/.opencode/agents/eval-engineer.md +38 -0
- package/repo-template/.opencode/agents/loop-controller.md +38 -0
- package/repo-template/.opencode/agents/loop-debugger.md +38 -0
- package/repo-template/.opencode/agents/model-router.md +38 -0
- package/repo-template/.opencode/agents/sdd-orchestrator.md +38 -0
- package/repo-template/.opencode/agents/sdd-spec-reviewer.md +38 -0
- package/repo-template/.opencode/agents/spec-intake-analyst.md +19 -0
- package/repo-template/.opencode/agents/tdd-engineer.md +38 -0
- package/repo-template/.opencode/commands/checkpoint.md +3 -0
- package/repo-template/.opencode/commands/eval-design.md +3 -0
- package/repo-template/.opencode/commands/maintainability-review.md +3 -0
- package/repo-template/.opencode/commands/quality-gate.md +3 -0
- package/repo-template/.opencode/commands/spec-escalate.md +9 -0
- package/repo-template/.opencode/commands/spec-intake.md +10 -0
- package/repo-template/.opencode/commands/spec-size.md +21 -0
- package/repo-template/.opencode/commands/trust-review.md +3 -0
- package/repo-template/.opencode/loops/ai-behavior-improvement.loop.md +15 -0
- package/repo-template/.opencode/loops/bug-repair.loop.md +16 -0
- package/repo-template/.opencode/loops/documentation-sync.loop.md +13 -0
- package/repo-template/.opencode/loops/feature-development.loop.md +25 -0
- package/repo-template/.opencode/skills/spec-complexity-classifier/SKILL.md +10 -0
- package/repo-template/.opencode/skills/spec-escalation-review/SKILL.md +10 -0
- package/repo-template/.opencode/skills/spec-intake/SKILL.md +10 -0
- package/repo-template/.pi/README.md +21 -0
- package/repo-template/.pi/prompts/checkpoint.md +11 -0
- package/repo-template/.pi/prompts/loop-run.md +10 -0
- package/repo-template/.pi/prompts/sdd-plan.md +11 -0
- package/repo-template/.pi/prompts/sdd-spec.md +13 -0
- package/repo-template/.pi/prompts/spec-escalate.md +9 -0
- package/repo-template/.pi/prompts/spec-intake.md +10 -0
- package/repo-template/.pi/prompts/spec-size.md +21 -0
- package/repo-template/.pi/skills/adapter-parity/SKILL.md +7 -0
- package/repo-template/.pi/skills/checkpoint/SKILL.md +7 -0
- package/repo-template/.pi/skills/eval-design/SKILL.md +3 -0
- package/repo-template/.pi/skills/evals/SKILL.md +7 -0
- package/repo-template/.pi/skills/loop-engineering/SKILL.md +7 -0
- package/repo-template/.pi/skills/maintainability-review/SKILL.md +3 -0
- package/repo-template/.pi/skills/quality-gate-review/SKILL.md +3 -0
- package/repo-template/.pi/skills/sdd/SKILL.md +7 -0
- package/repo-template/.pi/skills/spec-complexity-classifier/SKILL.md +10 -0
- package/repo-template/.pi/skills/spec-escalation-review/SKILL.md +10 -0
- package/repo-template/.pi/skills/spec-intake/SKILL.md +10 -0
- package/repo-template/.pi/skills/tdd/SKILL.md +7 -0
- package/repo-template/.pi/skills/trust-review/SKILL.md +3 -0
- package/repo-template/AGENT.md +8 -0
- package/repo-template/AGENTS.md +165 -0
- package/repo-template/CLAUDE.md +9 -0
- package/repo-template/GEMINI.md +9 -0
- package/repo-template/docs/ai/adapter-parity.md +135 -0
- package/repo-template/docs/ai/agent-workflow.md +45 -0
- package/repo-template/docs/ai/api.md +28 -0
- package/repo-template/docs/ai/architecture.md +53 -0
- package/repo-template/docs/ai/context-budget.md +54 -0
- package/repo-template/docs/ai/context-graph.md +39 -0
- package/repo-template/docs/ai/conventions.md +34 -0
- package/repo-template/docs/ai/decision-log.md +5 -0
- package/repo-template/docs/ai/enforcement.md +63 -0
- package/repo-template/docs/ai/eval-strategy.md +42 -0
- package/repo-template/docs/ai/evals.md +44 -0
- package/repo-template/docs/ai/git-workflow.md +35 -0
- package/repo-template/docs/ai/governance.md +61 -0
- package/repo-template/docs/ai/harness.md +49 -0
- package/repo-template/docs/ai/installer-cli.md +34 -0
- package/repo-template/docs/ai/loop-log.md +5 -0
- package/repo-template/docs/ai/loop-observability.md +57 -0
- package/repo-template/docs/ai/loop-policy.md +86 -0
- package/repo-template/docs/ai/loops.md +95 -0
- package/repo-template/docs/ai/maintainability-gates.md +26 -0
- package/repo-template/docs/ai/memory.md +71 -0
- package/repo-template/docs/ai/model-policy.md +105 -0
- package/repo-template/docs/ai/observability-runtime.md +48 -0
- package/repo-template/docs/ai/provider-routing.md +138 -0
- package/repo-template/docs/ai/quality-gates.md +48 -0
- package/repo-template/docs/ai/rollback-runtime.md +25 -0
- package/repo-template/docs/ai/security.md +12 -0
- package/repo-template/docs/ai/spec-driven-development.md +124 -0
- package/repo-template/docs/ai/spec-escalation.md +44 -0
- package/repo-template/docs/ai/spec-intake.md +46 -0
- package/repo-template/docs/ai/spec-sizing.md +207 -0
- package/repo-template/docs/ai/test-driven-development.md +35 -0
- package/repo-template/docs/ai/testing.md +28 -0
- package/repo-template/docs/ai/tool-adapters.md +186 -0
- package/repo-template/docs/ai/trust-allowlist.md +5 -0
- package/repo-template/docs/ai/trust-policy.md +41 -0
- package/repo-template/docs/ai/ui.md +17 -0
- package/repo-template/docs/skills/adapter-parity-review.md +46 -0
- package/repo-template/docs/skills/api-design.md +37 -0
- package/repo-template/docs/skills/architecture.md +37 -0
- package/repo-template/docs/skills/code-review.md +37 -0
- package/repo-template/docs/skills/context-graph.md +38 -0
- package/repo-template/docs/skills/debugging.md +37 -0
- package/repo-template/docs/skills/eval-design.md +12 -0
- package/repo-template/docs/skills/evals.md +37 -0
- package/repo-template/docs/skills/git-workflow.md +37 -0
- package/repo-template/docs/skills/look-trust-review.md +12 -0
- package/repo-template/docs/skills/loop-debugging.md +16 -0
- package/repo-template/docs/skills/loop-design.md +39 -0
- package/repo-template/docs/skills/loop-retrospective.md +15 -0
- package/repo-template/docs/skills/loop-review.md +16 -0
- package/repo-template/docs/skills/maintainability-review.md +12 -0
- package/repo-template/docs/skills/model-selection.md +36 -0
- package/repo-template/docs/skills/quality-gate-review.md +12 -0
- package/repo-template/docs/skills/spec-complexity-classifier.md +41 -0
- package/repo-template/docs/skills/spec-escalation-review.md +34 -0
- package/repo-template/docs/skills/spec-intake.md +25 -0
- package/repo-template/docs/skills/testing.md +37 -0
- package/repo-template/docs/skills/tool-adapter-sync.md +36 -0
- package/repo-template/docs/skills/ui-design.md +37 -0
- package/repo-template/docs/specs/README.md +9 -0
- package/repo-template/docs/specs/templates/basic-spec.md +41 -0
- package/repo-template/docs/specs/templates/complex-spec.md +145 -0
- package/repo-template/docs/specs/templates/standard-spec.md +58 -0
- package/repo-template/evals/README.md +11 -0
- package/repo-template/evals/golden/sample-golden.json +12 -0
- package/repo-template/evals/loop-regression/README.md +11 -0
- package/repo-template/evals/regression/sample-regression.json +12 -0
- package/repo-template/evals/schema/sample-schema.json +12 -0
- package/repo-template/evals/tool-calls/sample-tool-calls.json +12 -0
- package/repo-template/opencode.json.sample +29 -0
- package/repo-template/scripts/harness/backup.mjs +7 -0
- package/repo-template/scripts/harness/doctor.mjs +20 -0
- package/repo-template/scripts/harness/run-evals.mjs +27 -0
- package/repo-template/setup-agent-links.sh +54 -0
- package/scripts/check-published-release.mjs +127 -0
- package/scripts/check-release-commit.mjs +32 -0
- package/scripts/install.sh +225 -0
- package/scripts/installer-smoke-test.sh +155 -0
- package/scripts/lib/attribution-guard.mjs +59 -0
- package/scripts/registry-smoke-test.sh +149 -0
- package/scripts/smoke-test.sh +197 -0
- package/scripts/ux-smoke-test.sh +140 -0
- package/src/cli.js +625 -0
- package/src/doctor.js +68 -0
- package/src/global/adapter-context.js +27 -0
- package/src/global/adapter-matrix.js +38 -0
- package/src/global/adapters/claude.js +8 -0
- package/src/global/adapters/codex.js +8 -0
- package/src/global/adapters/cursor.js +8 -0
- package/src/global/adapters/opencode.js +8 -0
- package/src/global/agents.js +7 -0
- package/src/global/apply-confirmation.js +62 -0
- package/src/global/backups.js +54 -0
- package/src/global/brand/cli.js +52 -0
- package/src/global/brand/index.js +85 -0
- package/src/global/clack/setup-preview.js +48 -0
- package/src/global/clack/setup-wizard-constants.js +6 -0
- package/src/global/clack/setup-wizard.js +207 -0
- package/src/global/clack/theme.js +145 -0
- package/src/global/component-authoring.js +121 -0
- package/src/global/component-builders.js +43 -0
- package/src/global/component-distribution.js +270 -0
- package/src/global/component-installer.js +106 -0
- package/src/global/component-paths.js +22 -0
- package/src/global/component-registry.js +89 -0
- package/src/global/components/orchestrator.js +14 -0
- package/src/global/components/sdd-core.js +17 -0
- package/src/global/diff.js +233 -0
- package/src/global/drift.js +264 -0
- package/src/global/explain.js +144 -0
- package/src/global/global-cli.js +1145 -0
- package/src/global/global-doctor.js +67 -0
- package/src/global/global-installer.js +284 -0
- package/src/global/history.js +313 -0
- package/src/global/ink/run-setup-ink.js +77 -0
- package/src/global/ink/setup-app.js +278 -0
- package/src/global/ink/setup-routing.js +51 -0
- package/src/global/ink/setup-state.js +180 -0
- package/src/global/ink/terminal.js +13 -0
- package/src/global/json-output.js +22 -0
- package/src/global/load-component-catalog.js +41 -0
- package/src/global/load-workspace-component-catalog.js +123 -0
- package/src/global/managed-body.js +28 -0
- package/src/global/managed-config-adapter.js +134 -0
- package/src/global/managed-section.js +73 -0
- package/src/global/npm-registry.js +25 -0
- package/src/global/paths.js +22 -0
- package/src/global/policy.js +344 -0
- package/src/global/preflight.js +65 -0
- package/src/global/registry.js +79 -0
- package/src/global/report.js +110 -0
- package/src/global/rollback.js +126 -0
- package/src/global/setup.js +284 -0
- package/src/global/state-migration.js +65 -0
- package/src/global/state.js +50 -0
- package/src/global/status.js +94 -0
- package/src/global/sync.js +129 -0
- package/src/global/upgrade.js +145 -0
- package/src/harness-files.js +145 -0
- package/src/harness-updater.js +120 -0
- package/src/hash.js +5 -0
- package/src/manifest.js +36 -0
- package/src/project-detection.js +105 -0
- package/src/template-installer.js +72 -0
- package/src/text-template.js +24 -0
- package/src/workspace-cli.js +115 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { copyFile, mkdir, readdir } from "node:fs/promises";
|
|
3
|
+
import { dirname, join, relative, sep } from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
backupFileBeforeChange,
|
|
6
|
+
backupTimestamp,
|
|
7
|
+
listBackupSnapshots,
|
|
8
|
+
resolveSnapshotDir
|
|
9
|
+
} from "./backups.js";
|
|
10
|
+
import { harnessHomePaths } from "./paths.js";
|
|
11
|
+
|
|
12
|
+
const BLOCKED_RELATIVE_PATHS = new Set([".harness/state.json"]);
|
|
13
|
+
|
|
14
|
+
export function backupNameToRelativePath(backupName) {
|
|
15
|
+
return backupName.split("__").join("/");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function resolveBackupTarget(backupName, homeDir) {
|
|
19
|
+
if (!backupName || backupName.includes("..")) return null;
|
|
20
|
+
|
|
21
|
+
const relativePath = backupNameToRelativePath(backupName);
|
|
22
|
+
if (relativePath.includes("..")) return null;
|
|
23
|
+
|
|
24
|
+
const normalizedRelative = relativePath.split("/").join(sep);
|
|
25
|
+
if (BLOCKED_RELATIVE_PATHS.has(relativePath.replace(/\\/g, "/"))) return null;
|
|
26
|
+
|
|
27
|
+
const targetPath = join(homeDir, normalizedRelative);
|
|
28
|
+
const withinHome = relative(homeDir, targetPath);
|
|
29
|
+
|
|
30
|
+
if (withinHome.startsWith("..") || withinHome === "") return null;
|
|
31
|
+
|
|
32
|
+
return targetPath;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function formatHomePath(homeDir, filePath) {
|
|
36
|
+
const rel = relative(homeDir, filePath);
|
|
37
|
+
return `~/${rel.split(sep).join("/")}`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function listSnapshotFiles(snapshotDir) {
|
|
41
|
+
if (!existsSync(snapshotDir)) return [];
|
|
42
|
+
|
|
43
|
+
const entries = await readdir(snapshotDir, { withFileTypes: true });
|
|
44
|
+
|
|
45
|
+
return entries
|
|
46
|
+
.filter((entry) => entry.isFile())
|
|
47
|
+
.map((entry) => entry.name)
|
|
48
|
+
.sort();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function describeBackupSnapshots(backupsDir) {
|
|
52
|
+
const names = await listBackupSnapshots(backupsDir);
|
|
53
|
+
const snapshots = [];
|
|
54
|
+
|
|
55
|
+
for (const name of names) {
|
|
56
|
+
const files = await listSnapshotFiles(join(backupsDir, name));
|
|
57
|
+
snapshots.push({ name, fileCount: files.length });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return snapshots;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function buildRestorePlans(snapshotDir, homeDir) {
|
|
64
|
+
return listSnapshotFiles(snapshotDir).then((files) =>
|
|
65
|
+
files
|
|
66
|
+
.map((backupName) => {
|
|
67
|
+
const targetPath = resolveBackupTarget(backupName, homeDir);
|
|
68
|
+
if (!targetPath) return null;
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
backupName,
|
|
72
|
+
targetPath,
|
|
73
|
+
displayPath: formatHomePath(homeDir, targetPath),
|
|
74
|
+
backupPath: join(snapshotDir, backupName)
|
|
75
|
+
};
|
|
76
|
+
})
|
|
77
|
+
.filter(Boolean)
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function previewRollback({ homeDir, snapshot }) {
|
|
82
|
+
const paths = harnessHomePaths(homeDir);
|
|
83
|
+
const snapshotDir = resolveSnapshotDir(paths.backupsDir, snapshot);
|
|
84
|
+
const plans = await buildRestorePlans(snapshotDir, homeDir);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
snapshot,
|
|
88
|
+
plans,
|
|
89
|
+
noop: plans.length === 0
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function applyRollback({ homeDir, snapshot }) {
|
|
94
|
+
const paths = harnessHomePaths(homeDir);
|
|
95
|
+
const snapshotDir = resolveSnapshotDir(paths.backupsDir, snapshot);
|
|
96
|
+
const plans = await buildRestorePlans(snapshotDir, homeDir);
|
|
97
|
+
|
|
98
|
+
if (plans.length === 0) {
|
|
99
|
+
return { snapshot, restored: [], safetyBackup: null, noop: true };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const safetyTimestamp = backupTimestamp();
|
|
103
|
+
let safetyBackup = null;
|
|
104
|
+
|
|
105
|
+
for (const plan of plans) {
|
|
106
|
+
if (existsSync(plan.targetPath)) {
|
|
107
|
+
await backupFileBeforeChange({
|
|
108
|
+
backupsDir: paths.backupsDir,
|
|
109
|
+
homeDir,
|
|
110
|
+
filePath: plan.targetPath,
|
|
111
|
+
timestamp: safetyTimestamp
|
|
112
|
+
});
|
|
113
|
+
safetyBackup ??= safetyTimestamp;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
await mkdir(dirname(plan.targetPath), { recursive: true });
|
|
117
|
+
await copyFile(plan.backupPath, plan.targetPath);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
snapshot,
|
|
122
|
+
restored: plans.map((plan) => plan.displayPath),
|
|
123
|
+
safetyBackup,
|
|
124
|
+
noop: false
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
2
|
+
import { BRAND } from "./brand/index.js";
|
|
3
|
+
import { installGlobalHarness } from "./global-installer.js";
|
|
4
|
+
import {
|
|
5
|
+
assertExplicitApplyConsent,
|
|
6
|
+
createReadlinePrompt,
|
|
7
|
+
promptApplyConfirmation,
|
|
8
|
+
shouldPromptApplyConfirmation
|
|
9
|
+
} from "./apply-confirmation.js";
|
|
10
|
+
import { summarizeInstallPreflight } from "./diff.js";
|
|
11
|
+
import {
|
|
12
|
+
COMPONENT_IDS,
|
|
13
|
+
DEFAULT_COMPONENT_IDS,
|
|
14
|
+
validateComponentIds
|
|
15
|
+
} from "./component-registry.js";
|
|
16
|
+
import {
|
|
17
|
+
GLOBAL_AGENT_IDS,
|
|
18
|
+
detectInstalledAdapters,
|
|
19
|
+
isAllAgentsSelection,
|
|
20
|
+
resolveAgentIds,
|
|
21
|
+
validateAdapterIds
|
|
22
|
+
} from "./registry.js";
|
|
23
|
+
import { printManagedPreflight, shouldShowPreflight } from "./preflight.js";
|
|
24
|
+
import { loadConsentAudit } from "./policy.js";
|
|
25
|
+
import {
|
|
26
|
+
renderSetupWizardResult,
|
|
27
|
+
runSetupWizard as defaultRunSetupWizard,
|
|
28
|
+
SetupWizardCancelledError,
|
|
29
|
+
shouldUseSetupWizard as evaluateClackWizard
|
|
30
|
+
} from "./clack/setup-wizard.js";
|
|
31
|
+
import {
|
|
32
|
+
renderSetupInkResult,
|
|
33
|
+
runSetupInk as defaultRunSetupInk,
|
|
34
|
+
SetupWizardCancelledError as SetupInkCancelledError
|
|
35
|
+
} from "./ink/run-setup-ink.js";
|
|
36
|
+
import { canUseSetupInk } from "./ink/terminal.js";
|
|
37
|
+
import { shouldUseSetupInk as evaluateSetupInk } from "./ink/setup-routing.js";
|
|
38
|
+
|
|
39
|
+
export { shouldUseSetupInk } from "./ink/setup-routing.js";
|
|
40
|
+
export { shouldUseSetupWizard } from "./clack/setup-wizard.js";
|
|
41
|
+
/** @deprecated Use shouldUseSetupWizard */
|
|
42
|
+
export { shouldUseSetupTui } from "./clack/setup-wizard.js";
|
|
43
|
+
|
|
44
|
+
export async function runHarnessSetup({
|
|
45
|
+
packageRoot,
|
|
46
|
+
packageName,
|
|
47
|
+
cliVersion,
|
|
48
|
+
homeDir,
|
|
49
|
+
workspaceRoot = null,
|
|
50
|
+
agents = null,
|
|
51
|
+
components = null,
|
|
52
|
+
noDefaultComponents = false,
|
|
53
|
+
dryRun = false,
|
|
54
|
+
yes = false,
|
|
55
|
+
confirm = false,
|
|
56
|
+
preflight = true,
|
|
57
|
+
preflightExplicit = false,
|
|
58
|
+
yesExplicit = false,
|
|
59
|
+
confirmExplicit = false,
|
|
60
|
+
json = false,
|
|
61
|
+
simple = false,
|
|
62
|
+
interactive = Boolean(input.isTTY && output.isTTY),
|
|
63
|
+
createPrompt = createReadlinePrompt,
|
|
64
|
+
runSetupInkImpl = defaultRunSetupInk,
|
|
65
|
+
runSetupWizardImpl = defaultRunSetupWizard,
|
|
66
|
+
inkCapable = canUseSetupInk({ interactive })
|
|
67
|
+
}) {
|
|
68
|
+
const routing = { interactive, simple, inkCapable, dryRun, yes, confirm, json, agents, components, noDefaultComponents };
|
|
69
|
+
const useInk = evaluateSetupInk(routing);
|
|
70
|
+
const useWizard = !useInk && evaluateClackWizard({ ...routing, inkCapable });
|
|
71
|
+
|
|
72
|
+
let selectedAgents = agents;
|
|
73
|
+
let selectedComponents = components;
|
|
74
|
+
let selectedNoDefaults = noDefaultComponents;
|
|
75
|
+
let usedWizard = false;
|
|
76
|
+
let usedInk = false;
|
|
77
|
+
|
|
78
|
+
if (!useInk && !useWizard) {
|
|
79
|
+
printSetupIntro({ homeDir });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const setupUiArgs = {
|
|
83
|
+
homeDir,
|
|
84
|
+
workspaceRoot,
|
|
85
|
+
packageRoot,
|
|
86
|
+
packageName,
|
|
87
|
+
cliVersion,
|
|
88
|
+
dryRun,
|
|
89
|
+
preflight,
|
|
90
|
+
yes,
|
|
91
|
+
confirm,
|
|
92
|
+
preflightExplicit,
|
|
93
|
+
yesExplicit,
|
|
94
|
+
confirmExplicit,
|
|
95
|
+
interactive
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if (useInk) {
|
|
99
|
+
try {
|
|
100
|
+
const inkOutcome = await runSetupInkImpl(setupUiArgs);
|
|
101
|
+
|
|
102
|
+
if (inkOutcome.cancelled) {
|
|
103
|
+
return { cancelled: true, usedWizard: true, usedInk: true };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
selectedAgents = inkOutcome.agents;
|
|
107
|
+
selectedComponents = inkOutcome.components;
|
|
108
|
+
selectedNoDefaults = inkOutcome.noDefaultComponents;
|
|
109
|
+
usedWizard = true;
|
|
110
|
+
usedInk = true;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (error instanceof SetupInkCancelledError) {
|
|
113
|
+
return { cancelled: true, usedWizard: true, usedInk: true };
|
|
114
|
+
}
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
} else if (useWizard) {
|
|
118
|
+
try {
|
|
119
|
+
const wizardOutcome = await runSetupWizardImpl(setupUiArgs);
|
|
120
|
+
|
|
121
|
+
if (wizardOutcome.cancelled) {
|
|
122
|
+
return { cancelled: true, usedWizard: true };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
selectedAgents = wizardOutcome.agents;
|
|
126
|
+
selectedComponents = wizardOutcome.components;
|
|
127
|
+
selectedNoDefaults = wizardOutcome.noDefaultComponents;
|
|
128
|
+
usedWizard = true;
|
|
129
|
+
} catch (error) {
|
|
130
|
+
if (error instanceof SetupWizardCancelledError) {
|
|
131
|
+
return { cancelled: true, usedWizard: true };
|
|
132
|
+
}
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
} else if (
|
|
136
|
+
interactive && !dryRun && !yes && !confirm
|
|
137
|
+
&& agents == null
|
|
138
|
+
&& components == null
|
|
139
|
+
&& !noDefaultComponents
|
|
140
|
+
) {
|
|
141
|
+
const prompt = createPrompt();
|
|
142
|
+
const detected = detectInstalledAdapters({ homeDir });
|
|
143
|
+
const defaultAgentsLabel = detected.length > 0 ? detected.join(",") : GLOBAL_AGENT_IDS.join(",");
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
const agentsAnswer = (await prompt(`Agents to configure [${defaultAgentsLabel}]: `)).trim();
|
|
147
|
+
if (agentsAnswer.length > 0) {
|
|
148
|
+
selectedAgents = parseList(agentsAnswer);
|
|
149
|
+
if (!isAllAgentsSelection(selectedAgents)) {
|
|
150
|
+
validateAdapterIds(selectedAgents);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const componentsAnswer = (await prompt(
|
|
155
|
+
`Components to install [${DEFAULT_COMPONENT_IDS.join(",")}] (or "none"): `
|
|
156
|
+
)).trim();
|
|
157
|
+
|
|
158
|
+
if (componentsAnswer.toLowerCase() === "none") {
|
|
159
|
+
selectedNoDefaults = true;
|
|
160
|
+
selectedComponents = null;
|
|
161
|
+
} else if (componentsAnswer.length > 0) {
|
|
162
|
+
selectedComponents = parseList(componentsAnswer);
|
|
163
|
+
validateComponentIds(selectedComponents, { workspaceRoot });
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
console.log("");
|
|
167
|
+
printSetupPlanPreview({
|
|
168
|
+
agents: resolveAgentIds(selectedAgents, { homeDir }),
|
|
169
|
+
components: selectedNoDefaults
|
|
170
|
+
? []
|
|
171
|
+
: (selectedComponents ?? [...DEFAULT_COMPONENT_IDS])
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const confirmAnswer = (await prompt("Apply this plan? [Y/n]: ")).trim().toLowerCase();
|
|
175
|
+
if (confirmAnswer === "n" || confirmAnswer === "no") {
|
|
176
|
+
console.log("Setup cancelled.");
|
|
177
|
+
return { cancelled: true };
|
|
178
|
+
}
|
|
179
|
+
} finally {
|
|
180
|
+
await prompt.close?.();
|
|
181
|
+
}
|
|
182
|
+
} else if (!useInk && !useWizard) {
|
|
183
|
+
printSetupPlanPreview({
|
|
184
|
+
agents: resolveAgentIds(selectedAgents, { homeDir }),
|
|
185
|
+
components: selectedNoDefaults
|
|
186
|
+
? []
|
|
187
|
+
: (selectedComponents ?? [...DEFAULT_COMPONENT_IDS])
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const installArgs = {
|
|
192
|
+
packageRoot,
|
|
193
|
+
packageName,
|
|
194
|
+
cliVersion,
|
|
195
|
+
homeDir,
|
|
196
|
+
workspaceRoot,
|
|
197
|
+
agents: selectedAgents,
|
|
198
|
+
components: selectedComponents,
|
|
199
|
+
noDefaultComponents: selectedNoDefaults
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const applying = !dryRun;
|
|
203
|
+
|
|
204
|
+
assertExplicitApplyConsent({
|
|
205
|
+
applying,
|
|
206
|
+
dryRun,
|
|
207
|
+
json,
|
|
208
|
+
yes,
|
|
209
|
+
confirm,
|
|
210
|
+
noPreflight: !preflight,
|
|
211
|
+
interactive,
|
|
212
|
+
command: "setup"
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
if (!usedWizard && shouldShowPreflight({ preflight, dryRun, json, applying })) {
|
|
216
|
+
const preview = await installGlobalHarness({ ...installArgs, dryRun: true });
|
|
217
|
+
const summary = await summarizeInstallPreflight(homeDir, preview);
|
|
218
|
+
const consent = await loadConsentAudit(homeDir, {
|
|
219
|
+
yes,
|
|
220
|
+
confirm,
|
|
221
|
+
yesExplicit,
|
|
222
|
+
confirmExplicit,
|
|
223
|
+
preflight,
|
|
224
|
+
preflightExplicit,
|
|
225
|
+
interactive,
|
|
226
|
+
applying,
|
|
227
|
+
dryRun,
|
|
228
|
+
json
|
|
229
|
+
});
|
|
230
|
+
printManagedPreflight({
|
|
231
|
+
command: "setup",
|
|
232
|
+
...summary,
|
|
233
|
+
consentSource: consent.consentSource,
|
|
234
|
+
policyProfile: consent.policyProfile
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (!usedWizard && shouldPromptApplyConfirmation({ applying, dryRun, json, confirm, interactive })) {
|
|
239
|
+
const approved = await promptApplyConfirmation({ command: "setup", createPrompt });
|
|
240
|
+
if (!approved) {
|
|
241
|
+
console.log("Setup cancelled.");
|
|
242
|
+
return { cancelled: true };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const result = await installGlobalHarness({ ...installArgs, dryRun });
|
|
247
|
+
|
|
248
|
+
if (usedInk) {
|
|
249
|
+
renderSetupInkResult(result, { dryRun });
|
|
250
|
+
} else if (usedWizard) {
|
|
251
|
+
renderSetupWizardResult(result, { dryRun });
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return { cancelled: false, result, usedWizard, usedInk };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function printSetupIntro({ homeDir }) {
|
|
258
|
+
const detected = detectInstalledAdapters({ homeDir });
|
|
259
|
+
|
|
260
|
+
console.log(`${BRAND.displayName} setup — local AI ecosystem configurator`);
|
|
261
|
+
console.log("Configures and coordinates local agents. Does not install the AI apps themselves.");
|
|
262
|
+
console.log("");
|
|
263
|
+
console.log(`Detected agents: ${detected.join(", ") || "none"}`);
|
|
264
|
+
console.log(`Supported agents: ${GLOBAL_AGENT_IDS.join(", ")}`);
|
|
265
|
+
console.log(`Default components: ${DEFAULT_COMPONENT_IDS.join(", ")}`);
|
|
266
|
+
console.log("");
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function printSetupPlanPreview({ agents, components }) {
|
|
270
|
+
console.log("Plan:");
|
|
271
|
+
console.log(` Agents: ${agents.join(", ")}`);
|
|
272
|
+
console.log(` Components: ${components.join(", ") || "none (core plumbing only)"}`);
|
|
273
|
+
console.log(` Available components: ${COMPONENT_IDS.join(", ")}`);
|
|
274
|
+
console.log("");
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function parseList(value) {
|
|
278
|
+
return [...new Set(
|
|
279
|
+
value
|
|
280
|
+
.split(",")
|
|
281
|
+
.map((item) => item.trim().toLowerCase())
|
|
282
|
+
.filter(Boolean)
|
|
283
|
+
)];
|
|
284
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { resolveAdapter } from "./registry.js";
|
|
2
|
+
import { resolveComponent } from "./component-registry.js";
|
|
3
|
+
|
|
4
|
+
const LEGACY_ORCHESTRATOR_COMPONENT = {
|
|
5
|
+
id: "orchestrator",
|
|
6
|
+
version: resolveComponent("orchestrator").version,
|
|
7
|
+
managedTargets: []
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export function normalizeGlobalState(state) {
|
|
11
|
+
if (!state) return null;
|
|
12
|
+
|
|
13
|
+
const adapters = state.adapters ?? migrateLegacyAgents(state.agents ?? []);
|
|
14
|
+
const components = state.components ?? migrateLegacyComponents(state, adapters);
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
...state,
|
|
18
|
+
adapters,
|
|
19
|
+
agents: toLegacyAgents(adapters),
|
|
20
|
+
components
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getInstalledAdapterIds(state) {
|
|
25
|
+
const normalized = normalizeGlobalState(state);
|
|
26
|
+
return normalized?.adapters?.map((entry) => entry.id) ?? [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getInstalledComponentIds(state) {
|
|
30
|
+
const normalized = normalizeGlobalState(state);
|
|
31
|
+
return normalized?.components?.map((entry) => entry.id) ?? [];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function migrateLegacyAgents(agents) {
|
|
35
|
+
return agents.map((entry) => {
|
|
36
|
+
const adapter = resolveAdapter(entry.id);
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
id: adapter.id,
|
|
40
|
+
label: adapter.label,
|
|
41
|
+
rootDir: adapter.assets.rootDir,
|
|
42
|
+
configFile: entry.configFile ?? adapter.assets.configFile,
|
|
43
|
+
managedTargets: [...adapter.assets.managedTargets],
|
|
44
|
+
present: entry.present ?? false
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function migrateLegacyComponents(state, adapters) {
|
|
50
|
+
if (Array.isArray(state.components)) return state.components;
|
|
51
|
+
|
|
52
|
+
const managedTargets = adapters.map((adapter) => adapter.configFile);
|
|
53
|
+
const hasLegacyOrchestrator = Boolean(state.coreFiles?.["core/orchestrator.md"]);
|
|
54
|
+
|
|
55
|
+
if (!hasLegacyOrchestrator && managedTargets.length === 0) return [];
|
|
56
|
+
|
|
57
|
+
return [{
|
|
58
|
+
...LEGACY_ORCHESTRATOR_COMPONENT,
|
|
59
|
+
managedTargets
|
|
60
|
+
}];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function toLegacyAgents(adapters) {
|
|
64
|
+
return adapters.map(({ id, configFile, present }) => ({ id, configFile, present }));
|
|
65
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname } from "node:path";
|
|
4
|
+
import { normalizeGlobalState } from "./state-migration.js";
|
|
5
|
+
|
|
6
|
+
export const STATE_VERSION = 3;
|
|
7
|
+
|
|
8
|
+
export async function readGlobalState(statePath) {
|
|
9
|
+
if (!existsSync(statePath)) return null;
|
|
10
|
+
|
|
11
|
+
try {
|
|
12
|
+
const raw = JSON.parse(await readFile(statePath, "utf8"));
|
|
13
|
+
return normalizeGlobalState(raw);
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function writeGlobalState(statePath, state) {
|
|
20
|
+
await mkdir(dirname(statePath), { recursive: true });
|
|
21
|
+
await writeFile(statePath, `${JSON.stringify(state, null, 2)}\n`, "utf8");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createGlobalState({
|
|
25
|
+
packageName,
|
|
26
|
+
cliVersion,
|
|
27
|
+
adapters,
|
|
28
|
+
components = [],
|
|
29
|
+
coreFiles,
|
|
30
|
+
backups,
|
|
31
|
+
installedAt
|
|
32
|
+
}) {
|
|
33
|
+
const now = new Date().toISOString();
|
|
34
|
+
const normalizedAdapters = adapters.map((entry) => ({ ...entry }));
|
|
35
|
+
const normalizedComponents = components.map((entry) => ({ ...entry }));
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
stateVersion: STATE_VERSION,
|
|
39
|
+
packageName,
|
|
40
|
+
cliVersion,
|
|
41
|
+
scope: "agent-global",
|
|
42
|
+
installedAt: installedAt ?? now,
|
|
43
|
+
updatedAt: now,
|
|
44
|
+
adapters: normalizedAdapters,
|
|
45
|
+
agents: normalizedAdapters.map(({ id, configFile, present }) => ({ id, configFile, present })),
|
|
46
|
+
components: normalizedComponents,
|
|
47
|
+
coreFiles,
|
|
48
|
+
backups
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { listBackupSnapshots } from "./backups.js";
|
|
2
|
+
import { runGlobalDoctorChecks } from "./global-doctor.js";
|
|
3
|
+
import { buildEffectivePolicy } from "./policy.js";
|
|
4
|
+
import { harnessHomePaths } from "./paths.js";
|
|
5
|
+
import { GLOBAL_AGENT_IDS, detectInstalledAdapters } from "./registry.js";
|
|
6
|
+
import { readGlobalState } from "./state.js";
|
|
7
|
+
import { formatCliCommand } from "./brand/cli.js";
|
|
8
|
+
|
|
9
|
+
export async function buildStatusReport(homeDir, { packageRoot, workspaceRoot = null } = {}) {
|
|
10
|
+
const paths = harnessHomePaths(homeDir);
|
|
11
|
+
const state = await readGlobalState(paths.statePath);
|
|
12
|
+
const detected = detectInstalledAdapters({ homeDir });
|
|
13
|
+
const doctor = await runGlobalDoctorChecks(homeDir, { packageRoot, workspaceRoot });
|
|
14
|
+
const backups = await listBackupSnapshots(paths.backupsDir);
|
|
15
|
+
|
|
16
|
+
const installedAgentIds = new Set((state?.adapters ?? state?.agents ?? []).map((entry) => entry.id));
|
|
17
|
+
const agents = GLOBAL_AGENT_IDS.map((id) => ({
|
|
18
|
+
id,
|
|
19
|
+
detected: detected.includes(id),
|
|
20
|
+
managed: installedAgentIds.has(id)
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
const components = (state?.components ?? []).map((entry) => {
|
|
24
|
+
const related = doctor.checks.filter((check) => check.componentId === entry.id);
|
|
25
|
+
const status = summarizeCheckStatuses(related);
|
|
26
|
+
return {
|
|
27
|
+
id: entry.id,
|
|
28
|
+
version: entry.version,
|
|
29
|
+
source: entry.source ?? "bundled",
|
|
30
|
+
status
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const counts = {
|
|
35
|
+
ok: doctor.checks.filter((check) => check.status === "ok").length,
|
|
36
|
+
stale: doctor.checks.filter((check) => check.status === "stale").length,
|
|
37
|
+
missing: doctor.checks.filter((check) => check.status === "missing").length,
|
|
38
|
+
warning: doctor.checks.filter((check) => check.status === "warning").length
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const overall = resolveOverallStatus({ state, doctor });
|
|
42
|
+
const nextAction = resolveNextAction(overall, { backups: backups.length });
|
|
43
|
+
const policy = await buildEffectivePolicy(homeDir);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
homeDir,
|
|
47
|
+
stateRoot: paths.root,
|
|
48
|
+
state,
|
|
49
|
+
agents,
|
|
50
|
+
components,
|
|
51
|
+
checks: doctor.checks,
|
|
52
|
+
backups: backups.length,
|
|
53
|
+
counts,
|
|
54
|
+
overall,
|
|
55
|
+
nextAction,
|
|
56
|
+
policy,
|
|
57
|
+
ok: overall === "ok"
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function summarizeCheckStatuses(checks) {
|
|
62
|
+
if (checks.length === 0) return "ok";
|
|
63
|
+
if (checks.some((check) => check.status === "missing")) return "missing";
|
|
64
|
+
if (checks.some((check) => check.status === "stale")) return "stale";
|
|
65
|
+
return "ok";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function resolveOverallStatus({ state, doctor }) {
|
|
69
|
+
if (!state) return "missing";
|
|
70
|
+
if (doctor.hasDrift) return "drift";
|
|
71
|
+
if (!doctor.ok) return "failed";
|
|
72
|
+
return "ok";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveNextAction(overall, { backups = 0 } = {}) {
|
|
76
|
+
switch (overall) {
|
|
77
|
+
case "missing":
|
|
78
|
+
return `Run "${formatCliCommand("setup")}" (or "${formatCliCommand("install")}") to configure the local ecosystem.`;
|
|
79
|
+
case "drift":
|
|
80
|
+
return backups > 0
|
|
81
|
+
? `Run "${formatCliCommand("sync")}" to repair managed content. Use "${formatCliCommand("rollback")}" to restore a prior snapshot.`
|
|
82
|
+
: `Run "${formatCliCommand("sync")}" to repair managed content.`;
|
|
83
|
+
case "failed":
|
|
84
|
+
return `Run "${formatCliCommand("doctor")}" for details, then "${formatCliCommand("sync")}" or "${formatCliCommand("setup")}".`;
|
|
85
|
+
case "ok":
|
|
86
|
+
return backups > 0
|
|
87
|
+
? `Ecosystem healthy. Use "${formatCliCommand("rollback")}" if you need a prior snapshot.`
|
|
88
|
+
: "Ecosystem healthy. No action required.";
|
|
89
|
+
default: {
|
|
90
|
+
const _exhaustive = overall;
|
|
91
|
+
return _exhaustive;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|