@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.
Files changed (315) hide show
  1. package/README.md +937 -0
  2. package/bin/harness.js +11 -0
  3. package/bin/kairo-runtime.js +11 -0
  4. package/bin/kairo.js +11 -0
  5. package/global-template/components/catalog.json +25 -0
  6. package/global-template/components/orchestrator/orchestrator.md +55 -0
  7. package/global-template/components/sdd-core/handoff.md +38 -0
  8. package/global-template/components/sdd-core/spec-sizing.md +38 -0
  9. package/global-template/components/sdd-core/workflow.md +31 -0
  10. package/global-template/core/orchestrator.md +55 -0
  11. package/package.json +64 -0
  12. package/prompts/HARNESS_ENFORCEMENT_UPGRADE.md +11 -0
  13. package/prompts/HARNESS_ENTERPRISE.md +33 -0
  14. package/prompts/HARNESS_INSTALLER_MASTER.md +853 -0
  15. package/prompts/HARNESS_MINIMAL.md +30 -0
  16. package/prompts/HARNESS_SPEC_SIZING_UPGRADE.md +41 -0
  17. package/prompts/HARNESS_STANDARD.md +35 -0
  18. package/prompts/HARNESS_UNIVERSAL_PARITY.md +56 -0
  19. package/repo-template/.claude/agents/adapter-parity-reviewer.md +17 -0
  20. package/repo-template/.claude/agents/architect.md +17 -0
  21. package/repo-template/.claude/agents/eval-engineer.md +17 -0
  22. package/repo-template/.claude/agents/reviewer.md +17 -0
  23. package/repo-template/.claude/agents/sdd-reviewer.md +17 -0
  24. package/repo-template/.claude/agents/sdd-spec-writer.md +17 -0
  25. package/repo-template/.claude/skills/checkpoint/SKILL.md +13 -0
  26. package/repo-template/.claude/skills/eval-design/SKILL.md +3 -0
  27. package/repo-template/.claude/skills/evals/SKILL.md +13 -0
  28. package/repo-template/.claude/skills/loop-engineering/SKILL.md +16 -0
  29. package/repo-template/.claude/skills/maintainability-review/SKILL.md +3 -0
  30. package/repo-template/.claude/skills/quality-gate-review/SKILL.md +3 -0
  31. package/repo-template/.claude/skills/sdd/SKILL.md +13 -0
  32. package/repo-template/.claude/skills/spec-complexity-classifier/SKILL.md +10 -0
  33. package/repo-template/.claude/skills/spec-escalation-review/SKILL.md +10 -0
  34. package/repo-template/.claude/skills/spec-intake/SKILL.md +10 -0
  35. package/repo-template/.claude/skills/tdd/SKILL.md +13 -0
  36. package/repo-template/.claude/skills/trust-review/SKILL.md +3 -0
  37. package/repo-template/.codex/skills/adapter-parity/SKILL.md +29 -0
  38. package/repo-template/.codex/skills/checkpoint/SKILL.md +36 -0
  39. package/repo-template/.codex/skills/eval-design/SKILL.md +3 -0
  40. package/repo-template/.codex/skills/evals/SKILL.md +28 -0
  41. package/repo-template/.codex/skills/loop-engineering/SKILL.md +24 -0
  42. package/repo-template/.codex/skills/maintainability-review/SKILL.md +3 -0
  43. package/repo-template/.codex/skills/quality-gate-review/SKILL.md +3 -0
  44. package/repo-template/.codex/skills/sdd/SKILL.md +30 -0
  45. package/repo-template/.codex/skills/spec-complexity-classifier/SKILL.md +10 -0
  46. package/repo-template/.codex/skills/spec-escalation-review/SKILL.md +10 -0
  47. package/repo-template/.codex/skills/spec-intake/SKILL.md +10 -0
  48. package/repo-template/.codex/skills/tdd/SKILL.md +27 -0
  49. package/repo-template/.codex/skills/trust-review/SKILL.md +3 -0
  50. package/repo-template/.cursor/agents/adapter-parity-reviewer.md +29 -0
  51. package/repo-template/.cursor/agents/architect.md +27 -0
  52. package/repo-template/.cursor/agents/context-engineer.md +27 -0
  53. package/repo-template/.cursor/agents/debugger.md +27 -0
  54. package/repo-template/.cursor/agents/eval-engineer.md +27 -0
  55. package/repo-template/.cursor/agents/harness-orchestrator.md +27 -0
  56. package/repo-template/.cursor/agents/loop-controller.md +17 -0
  57. package/repo-template/.cursor/agents/loop-debugger.md +16 -0
  58. package/repo-template/.cursor/agents/reviewer.md +27 -0
  59. package/repo-template/.cursor/agents/sdd/sdd-acceptance-reviewer.md +31 -0
  60. package/repo-template/.cursor/agents/sdd/sdd-implementation-guardian.md +31 -0
  61. package/repo-template/.cursor/agents/sdd/sdd-intake-analyst.md +31 -0
  62. package/repo-template/.cursor/agents/sdd/sdd-orchestrator.md +31 -0
  63. package/repo-template/.cursor/agents/sdd/sdd-spec-reviewer.md +31 -0
  64. package/repo-template/.cursor/agents/sdd/sdd-spec-writer.md +31 -0
  65. package/repo-template/.cursor/agents/sdd/sdd-task-breakdown.md +31 -0
  66. package/repo-template/.cursor/agents/sdd/sdd-technical-planner.md +31 -0
  67. package/repo-template/.cursor/agents/sdd/sdd-test-planner.md +31 -0
  68. package/repo-template/.cursor/agents/spec-intake-analyst.md +14 -0
  69. package/repo-template/.cursor/agents/test-engineer.md +27 -0
  70. package/repo-template/.cursor/commands/adapter-parity.md +34 -0
  71. package/repo-template/.cursor/commands/checkpoint.md +30 -0
  72. package/repo-template/.cursor/commands/debug.md +13 -0
  73. package/repo-template/.cursor/commands/init-eval.md +12 -0
  74. package/repo-template/.cursor/commands/init-feature.md +12 -0
  75. package/repo-template/.cursor/commands/init-harness.md +13 -0
  76. package/repo-template/.cursor/commands/loop-debug.md +3 -0
  77. package/repo-template/.cursor/commands/loop-review.md +3 -0
  78. package/repo-template/.cursor/commands/loop-run.md +5 -0
  79. package/repo-template/.cursor/commands/review.md +21 -0
  80. package/repo-template/.cursor/commands/sdd-intake.md +18 -0
  81. package/repo-template/.cursor/commands/sdd-plan.md +18 -0
  82. package/repo-template/.cursor/commands/sdd-spec.md +18 -0
  83. package/repo-template/.cursor/commands/sdd-tasks.md +18 -0
  84. package/repo-template/.cursor/commands/sdd-validate.md +18 -0
  85. package/repo-template/.cursor/commands/spec-escalate.md +9 -0
  86. package/repo-template/.cursor/commands/spec-intake.md +10 -0
  87. package/repo-template/.cursor/commands/spec-size.md +21 -0
  88. package/repo-template/.cursor/rules/adapter-parity.mdc +20 -0
  89. package/repo-template/.cursor/rules/api.mdc +12 -0
  90. package/repo-template/.cursor/rules/core.mdc +21 -0
  91. package/repo-template/.cursor/rules/enforcement.mdc +16 -0
  92. package/repo-template/.cursor/rules/evals.mdc +10 -0
  93. package/repo-template/.cursor/rules/git.mdc +19 -0
  94. package/repo-template/.cursor/rules/harness.mdc +14 -0
  95. package/repo-template/.cursor/rules/loops.mdc +17 -0
  96. package/repo-template/.cursor/rules/testing.mdc +13 -0
  97. package/repo-template/.cursor/rules/ui.mdc +13 -0
  98. package/repo-template/.cursor/skills/api-design/SKILL.md +23 -0
  99. package/repo-template/.cursor/skills/architecture/SKILL.md +23 -0
  100. package/repo-template/.cursor/skills/code-review/SKILL.md +23 -0
  101. package/repo-template/.cursor/skills/context-graph/SKILL.md +23 -0
  102. package/repo-template/.cursor/skills/debugging/SKILL.md +23 -0
  103. package/repo-template/.cursor/skills/evals/SKILL.md +23 -0
  104. package/repo-template/.cursor/skills/git-workflow/SKILL.md +23 -0
  105. package/repo-template/.cursor/skills/testing/SKILL.md +23 -0
  106. package/repo-template/.cursor/skills/ui-design/SKILL.md +23 -0
  107. package/repo-template/.gentle-ai/README.md +37 -0
  108. package/repo-template/.gentle-ai/loops/README.md +27 -0
  109. package/repo-template/.gentle-ai/loops/sdd-tdd-repair.loop.md +27 -0
  110. package/repo-template/.gentle-ai/model-routing.md +25 -0
  111. package/repo-template/.github/copilot-instructions.md +37 -0
  112. package/repo-template/.github/dependabot.yml +11 -0
  113. package/repo-template/.github/instructions/harness.instructions.md +11 -0
  114. package/repo-template/.github/workflows/harness-quality-gate.yml +67 -0
  115. package/repo-template/.github/workflows/harness-security-gate.yml +32 -0
  116. package/repo-template/.harness/spec-sizing.json +33 -0
  117. package/repo-template/.opencode/README.md +71 -0
  118. package/repo-template/.opencode/agents/adapter-parity-reviewer.md +23 -0
  119. package/repo-template/.opencode/agents/architect.md +38 -0
  120. package/repo-template/.opencode/agents/checkpoint-reviewer.md +38 -0
  121. package/repo-template/.opencode/agents/eval-engineer.md +38 -0
  122. package/repo-template/.opencode/agents/loop-controller.md +38 -0
  123. package/repo-template/.opencode/agents/loop-debugger.md +38 -0
  124. package/repo-template/.opencode/agents/model-router.md +38 -0
  125. package/repo-template/.opencode/agents/sdd-orchestrator.md +38 -0
  126. package/repo-template/.opencode/agents/sdd-spec-reviewer.md +38 -0
  127. package/repo-template/.opencode/agents/spec-intake-analyst.md +19 -0
  128. package/repo-template/.opencode/agents/tdd-engineer.md +38 -0
  129. package/repo-template/.opencode/commands/checkpoint.md +3 -0
  130. package/repo-template/.opencode/commands/eval-design.md +3 -0
  131. package/repo-template/.opencode/commands/maintainability-review.md +3 -0
  132. package/repo-template/.opencode/commands/quality-gate.md +3 -0
  133. package/repo-template/.opencode/commands/spec-escalate.md +9 -0
  134. package/repo-template/.opencode/commands/spec-intake.md +10 -0
  135. package/repo-template/.opencode/commands/spec-size.md +21 -0
  136. package/repo-template/.opencode/commands/trust-review.md +3 -0
  137. package/repo-template/.opencode/loops/ai-behavior-improvement.loop.md +15 -0
  138. package/repo-template/.opencode/loops/bug-repair.loop.md +16 -0
  139. package/repo-template/.opencode/loops/documentation-sync.loop.md +13 -0
  140. package/repo-template/.opencode/loops/feature-development.loop.md +25 -0
  141. package/repo-template/.opencode/skills/spec-complexity-classifier/SKILL.md +10 -0
  142. package/repo-template/.opencode/skills/spec-escalation-review/SKILL.md +10 -0
  143. package/repo-template/.opencode/skills/spec-intake/SKILL.md +10 -0
  144. package/repo-template/.pi/README.md +21 -0
  145. package/repo-template/.pi/prompts/checkpoint.md +11 -0
  146. package/repo-template/.pi/prompts/loop-run.md +10 -0
  147. package/repo-template/.pi/prompts/sdd-plan.md +11 -0
  148. package/repo-template/.pi/prompts/sdd-spec.md +13 -0
  149. package/repo-template/.pi/prompts/spec-escalate.md +9 -0
  150. package/repo-template/.pi/prompts/spec-intake.md +10 -0
  151. package/repo-template/.pi/prompts/spec-size.md +21 -0
  152. package/repo-template/.pi/skills/adapter-parity/SKILL.md +7 -0
  153. package/repo-template/.pi/skills/checkpoint/SKILL.md +7 -0
  154. package/repo-template/.pi/skills/eval-design/SKILL.md +3 -0
  155. package/repo-template/.pi/skills/evals/SKILL.md +7 -0
  156. package/repo-template/.pi/skills/loop-engineering/SKILL.md +7 -0
  157. package/repo-template/.pi/skills/maintainability-review/SKILL.md +3 -0
  158. package/repo-template/.pi/skills/quality-gate-review/SKILL.md +3 -0
  159. package/repo-template/.pi/skills/sdd/SKILL.md +7 -0
  160. package/repo-template/.pi/skills/spec-complexity-classifier/SKILL.md +10 -0
  161. package/repo-template/.pi/skills/spec-escalation-review/SKILL.md +10 -0
  162. package/repo-template/.pi/skills/spec-intake/SKILL.md +10 -0
  163. package/repo-template/.pi/skills/tdd/SKILL.md +7 -0
  164. package/repo-template/.pi/skills/trust-review/SKILL.md +3 -0
  165. package/repo-template/AGENT.md +8 -0
  166. package/repo-template/AGENTS.md +165 -0
  167. package/repo-template/CLAUDE.md +9 -0
  168. package/repo-template/GEMINI.md +9 -0
  169. package/repo-template/docs/ai/adapter-parity.md +135 -0
  170. package/repo-template/docs/ai/agent-workflow.md +45 -0
  171. package/repo-template/docs/ai/api.md +28 -0
  172. package/repo-template/docs/ai/architecture.md +53 -0
  173. package/repo-template/docs/ai/context-budget.md +54 -0
  174. package/repo-template/docs/ai/context-graph.md +39 -0
  175. package/repo-template/docs/ai/conventions.md +34 -0
  176. package/repo-template/docs/ai/decision-log.md +5 -0
  177. package/repo-template/docs/ai/enforcement.md +63 -0
  178. package/repo-template/docs/ai/eval-strategy.md +42 -0
  179. package/repo-template/docs/ai/evals.md +44 -0
  180. package/repo-template/docs/ai/git-workflow.md +35 -0
  181. package/repo-template/docs/ai/governance.md +61 -0
  182. package/repo-template/docs/ai/harness.md +49 -0
  183. package/repo-template/docs/ai/installer-cli.md +34 -0
  184. package/repo-template/docs/ai/loop-log.md +5 -0
  185. package/repo-template/docs/ai/loop-observability.md +57 -0
  186. package/repo-template/docs/ai/loop-policy.md +86 -0
  187. package/repo-template/docs/ai/loops.md +95 -0
  188. package/repo-template/docs/ai/maintainability-gates.md +26 -0
  189. package/repo-template/docs/ai/memory.md +71 -0
  190. package/repo-template/docs/ai/model-policy.md +105 -0
  191. package/repo-template/docs/ai/observability-runtime.md +48 -0
  192. package/repo-template/docs/ai/provider-routing.md +138 -0
  193. package/repo-template/docs/ai/quality-gates.md +48 -0
  194. package/repo-template/docs/ai/rollback-runtime.md +25 -0
  195. package/repo-template/docs/ai/security.md +12 -0
  196. package/repo-template/docs/ai/spec-driven-development.md +124 -0
  197. package/repo-template/docs/ai/spec-escalation.md +44 -0
  198. package/repo-template/docs/ai/spec-intake.md +46 -0
  199. package/repo-template/docs/ai/spec-sizing.md +207 -0
  200. package/repo-template/docs/ai/test-driven-development.md +35 -0
  201. package/repo-template/docs/ai/testing.md +28 -0
  202. package/repo-template/docs/ai/tool-adapters.md +186 -0
  203. package/repo-template/docs/ai/trust-allowlist.md +5 -0
  204. package/repo-template/docs/ai/trust-policy.md +41 -0
  205. package/repo-template/docs/ai/ui.md +17 -0
  206. package/repo-template/docs/skills/adapter-parity-review.md +46 -0
  207. package/repo-template/docs/skills/api-design.md +37 -0
  208. package/repo-template/docs/skills/architecture.md +37 -0
  209. package/repo-template/docs/skills/code-review.md +37 -0
  210. package/repo-template/docs/skills/context-graph.md +38 -0
  211. package/repo-template/docs/skills/debugging.md +37 -0
  212. package/repo-template/docs/skills/eval-design.md +12 -0
  213. package/repo-template/docs/skills/evals.md +37 -0
  214. package/repo-template/docs/skills/git-workflow.md +37 -0
  215. package/repo-template/docs/skills/look-trust-review.md +12 -0
  216. package/repo-template/docs/skills/loop-debugging.md +16 -0
  217. package/repo-template/docs/skills/loop-design.md +39 -0
  218. package/repo-template/docs/skills/loop-retrospective.md +15 -0
  219. package/repo-template/docs/skills/loop-review.md +16 -0
  220. package/repo-template/docs/skills/maintainability-review.md +12 -0
  221. package/repo-template/docs/skills/model-selection.md +36 -0
  222. package/repo-template/docs/skills/quality-gate-review.md +12 -0
  223. package/repo-template/docs/skills/spec-complexity-classifier.md +41 -0
  224. package/repo-template/docs/skills/spec-escalation-review.md +34 -0
  225. package/repo-template/docs/skills/spec-intake.md +25 -0
  226. package/repo-template/docs/skills/testing.md +37 -0
  227. package/repo-template/docs/skills/tool-adapter-sync.md +36 -0
  228. package/repo-template/docs/skills/ui-design.md +37 -0
  229. package/repo-template/docs/specs/README.md +9 -0
  230. package/repo-template/docs/specs/templates/basic-spec.md +41 -0
  231. package/repo-template/docs/specs/templates/complex-spec.md +145 -0
  232. package/repo-template/docs/specs/templates/standard-spec.md +58 -0
  233. package/repo-template/evals/README.md +11 -0
  234. package/repo-template/evals/golden/sample-golden.json +12 -0
  235. package/repo-template/evals/loop-regression/README.md +11 -0
  236. package/repo-template/evals/regression/sample-regression.json +12 -0
  237. package/repo-template/evals/schema/sample-schema.json +12 -0
  238. package/repo-template/evals/tool-calls/sample-tool-calls.json +12 -0
  239. package/repo-template/opencode.json.sample +29 -0
  240. package/repo-template/scripts/harness/backup.mjs +7 -0
  241. package/repo-template/scripts/harness/doctor.mjs +20 -0
  242. package/repo-template/scripts/harness/run-evals.mjs +27 -0
  243. package/repo-template/setup-agent-links.sh +54 -0
  244. package/scripts/check-published-release.mjs +127 -0
  245. package/scripts/check-release-commit.mjs +32 -0
  246. package/scripts/install.sh +225 -0
  247. package/scripts/installer-smoke-test.sh +155 -0
  248. package/scripts/lib/attribution-guard.mjs +59 -0
  249. package/scripts/registry-smoke-test.sh +149 -0
  250. package/scripts/smoke-test.sh +197 -0
  251. package/scripts/ux-smoke-test.sh +140 -0
  252. package/src/cli.js +625 -0
  253. package/src/doctor.js +68 -0
  254. package/src/global/adapter-context.js +27 -0
  255. package/src/global/adapter-matrix.js +38 -0
  256. package/src/global/adapters/claude.js +8 -0
  257. package/src/global/adapters/codex.js +8 -0
  258. package/src/global/adapters/cursor.js +8 -0
  259. package/src/global/adapters/opencode.js +8 -0
  260. package/src/global/agents.js +7 -0
  261. package/src/global/apply-confirmation.js +62 -0
  262. package/src/global/backups.js +54 -0
  263. package/src/global/brand/cli.js +52 -0
  264. package/src/global/brand/index.js +85 -0
  265. package/src/global/clack/setup-preview.js +48 -0
  266. package/src/global/clack/setup-wizard-constants.js +6 -0
  267. package/src/global/clack/setup-wizard.js +207 -0
  268. package/src/global/clack/theme.js +145 -0
  269. package/src/global/component-authoring.js +121 -0
  270. package/src/global/component-builders.js +43 -0
  271. package/src/global/component-distribution.js +270 -0
  272. package/src/global/component-installer.js +106 -0
  273. package/src/global/component-paths.js +22 -0
  274. package/src/global/component-registry.js +89 -0
  275. package/src/global/components/orchestrator.js +14 -0
  276. package/src/global/components/sdd-core.js +17 -0
  277. package/src/global/diff.js +233 -0
  278. package/src/global/drift.js +264 -0
  279. package/src/global/explain.js +144 -0
  280. package/src/global/global-cli.js +1145 -0
  281. package/src/global/global-doctor.js +67 -0
  282. package/src/global/global-installer.js +284 -0
  283. package/src/global/history.js +313 -0
  284. package/src/global/ink/run-setup-ink.js +77 -0
  285. package/src/global/ink/setup-app.js +278 -0
  286. package/src/global/ink/setup-routing.js +51 -0
  287. package/src/global/ink/setup-state.js +180 -0
  288. package/src/global/ink/terminal.js +13 -0
  289. package/src/global/json-output.js +22 -0
  290. package/src/global/load-component-catalog.js +41 -0
  291. package/src/global/load-workspace-component-catalog.js +123 -0
  292. package/src/global/managed-body.js +28 -0
  293. package/src/global/managed-config-adapter.js +134 -0
  294. package/src/global/managed-section.js +73 -0
  295. package/src/global/npm-registry.js +25 -0
  296. package/src/global/paths.js +22 -0
  297. package/src/global/policy.js +344 -0
  298. package/src/global/preflight.js +65 -0
  299. package/src/global/registry.js +79 -0
  300. package/src/global/report.js +110 -0
  301. package/src/global/rollback.js +126 -0
  302. package/src/global/setup.js +284 -0
  303. package/src/global/state-migration.js +65 -0
  304. package/src/global/state.js +50 -0
  305. package/src/global/status.js +94 -0
  306. package/src/global/sync.js +129 -0
  307. package/src/global/upgrade.js +145 -0
  308. package/src/harness-files.js +145 -0
  309. package/src/harness-updater.js +120 -0
  310. package/src/hash.js +5 -0
  311. package/src/manifest.js +36 -0
  312. package/src/project-detection.js +105 -0
  313. package/src/template-installer.js +72 -0
  314. package/src/text-template.js +24 -0
  315. package/src/workspace-cli.js +115 -0
@@ -0,0 +1,129 @@
1
+ import { updateGlobalHarness } from "./global-installer.js";
2
+ import {
3
+ assertExplicitApplyConsent,
4
+ createReadlinePrompt,
5
+ promptApplyConfirmation,
6
+ shouldPromptApplyConfirmation
7
+ } from "./apply-confirmation.js";
8
+ import { buildDiffReport } from "./diff.js";
9
+ import { harnessHomePaths } from "./paths.js";
10
+ import { printManagedPreflight, shouldShowPreflight, summarizeDiffPreflight } from "./preflight.js";
11
+ import { loadConsentAudit } from "./policy.js";
12
+ import { readGlobalState } from "./state.js";
13
+ import { buildStatusReport } from "./status.js";
14
+
15
+ export async function runHarnessSync({
16
+ packageRoot,
17
+ packageName,
18
+ cliVersion,
19
+ homeDir,
20
+ workspaceRoot = null,
21
+ dryRun = false,
22
+ yes = false,
23
+ confirm = false,
24
+ preflight = true,
25
+ preflightExplicit = false,
26
+ yesExplicit = false,
27
+ confirmExplicit = false,
28
+ json = false,
29
+ interactive = null,
30
+ createPrompt = createReadlinePrompt
31
+ }) {
32
+ const paths = harnessHomePaths(homeDir);
33
+ const state = await readGlobalState(paths.statePath);
34
+
35
+ if (!state) {
36
+ return {
37
+ action: "setup-required",
38
+ wrote: false,
39
+ result: null,
40
+ report: await buildStatusReport(homeDir, { packageRoot, workspaceRoot })
41
+ };
42
+ }
43
+
44
+ const preReport = await buildStatusReport(homeDir, { packageRoot, workspaceRoot });
45
+ const needsRepair = preReport.overall === "drift"
46
+ || preReport.counts.missing > 0
47
+ || preReport.counts.stale > 0;
48
+
49
+ if (!needsRepair) {
50
+ return {
51
+ action: "noop",
52
+ wrote: false,
53
+ result: null,
54
+ report: preReport
55
+ };
56
+ }
57
+
58
+ const applying = !dryRun;
59
+
60
+ assertExplicitApplyConsent({
61
+ applying,
62
+ dryRun,
63
+ json,
64
+ yes,
65
+ confirm,
66
+ noPreflight: !preflight,
67
+ interactive,
68
+ command: "sync"
69
+ });
70
+
71
+ if (shouldShowPreflight({ preflight, dryRun, json, applying })) {
72
+ const diffReport = await buildDiffReport(homeDir, {
73
+ packageRoot,
74
+ packageName,
75
+ cliVersion,
76
+ workspaceRoot
77
+ });
78
+ const consent = await loadConsentAudit(homeDir, {
79
+ yes,
80
+ confirm,
81
+ yesExplicit,
82
+ confirmExplicit,
83
+ preflight,
84
+ preflightExplicit,
85
+ interactive,
86
+ applying,
87
+ dryRun,
88
+ json
89
+ });
90
+ printManagedPreflight({
91
+ command: "sync",
92
+ ...summarizeDiffPreflight(diffReport),
93
+ consentSource: consent.consentSource,
94
+ policyProfile: consent.policyProfile
95
+ });
96
+ }
97
+
98
+ if (shouldPromptApplyConfirmation({ applying, dryRun, json, confirm, interactive })) {
99
+ const approved = await promptApplyConfirmation({ command: "sync", createPrompt });
100
+ if (!approved) {
101
+ return {
102
+ action: "cancelled",
103
+ wrote: false,
104
+ result: null,
105
+ report: preReport
106
+ };
107
+ }
108
+ }
109
+
110
+ const result = await updateGlobalHarness({
111
+ packageRoot,
112
+ packageName,
113
+ cliVersion,
114
+ homeDir,
115
+ workspaceRoot,
116
+ dryRun
117
+ });
118
+
119
+ const report = dryRun
120
+ ? preReport
121
+ : await buildStatusReport(homeDir, { packageRoot, workspaceRoot });
122
+
123
+ return {
124
+ action: dryRun ? "plan" : "repaired",
125
+ wrote: !dryRun,
126
+ result,
127
+ report
128
+ };
129
+ }
@@ -0,0 +1,145 @@
1
+ import { updateGlobalHarness } from "./global-installer.js";
2
+ import {
3
+ assertExplicitApplyConsent,
4
+ createReadlinePrompt,
5
+ promptApplyConfirmation,
6
+ shouldPromptApplyConfirmation
7
+ } from "./apply-confirmation.js";
8
+ import { buildDiffReport } from "./diff.js";
9
+ import { fetchPublishedVersion } from "./npm-registry.js";
10
+ import { harnessHomePaths } from "./paths.js";
11
+ import { printManagedPreflight, shouldShowPreflight, summarizeDiffPreflight } from "./preflight.js";
12
+ import { loadConsentAudit } from "./policy.js";
13
+ import { readGlobalState } from "./state.js";
14
+ import { formatCliCommand } from "./brand/cli.js";
15
+ import { runHarnessSetup } from "./setup.js";
16
+
17
+ export async function runHarnessUpgrade({
18
+ packageRoot,
19
+ packageName,
20
+ cliVersion,
21
+ homeDir,
22
+ workspaceRoot = null,
23
+ dryRun = false,
24
+ yes = false,
25
+ confirm = false,
26
+ preflight = true,
27
+ preflightExplicit = false,
28
+ yesExplicit = false,
29
+ confirmExplicit = false,
30
+ json = false,
31
+ interactive = null,
32
+ createPrompt = createReadlinePrompt,
33
+ fetchVersion = fetchPublishedVersion
34
+ }) {
35
+ if (yes && dryRun) {
36
+ throw new Error("Use either --dry-run or --yes, not both.");
37
+ }
38
+
39
+ const preview = !yes;
40
+
41
+ const latestVersion = await fetchVersion(packageName);
42
+ const paths = harnessHomePaths(homeDir);
43
+ const state = await readGlobalState(paths.statePath);
44
+ const latestCommand = `npx ${packageName}@latest setup --yes`;
45
+ const previewCommand = `npx ${packageName}@latest setup --dry-run`;
46
+
47
+ let result = null;
48
+ const applying = yes && !dryRun;
49
+
50
+ assertExplicitApplyConsent({
51
+ applying,
52
+ dryRun,
53
+ json,
54
+ yes,
55
+ confirm,
56
+ noPreflight: !preflight,
57
+ interactive,
58
+ command: "upgrade"
59
+ });
60
+
61
+ if (yes && shouldShowPreflight({ preflight, dryRun: false, json, applying: true }) && state) {
62
+ const diffReport = await buildDiffReport(homeDir, {
63
+ packageRoot,
64
+ packageName,
65
+ cliVersion,
66
+ workspaceRoot
67
+ });
68
+ const consent = await loadConsentAudit(homeDir, {
69
+ yes,
70
+ confirm,
71
+ yesExplicit,
72
+ confirmExplicit,
73
+ preflight,
74
+ preflightExplicit,
75
+ interactive,
76
+ applying: true,
77
+ dryRun: false,
78
+ json
79
+ });
80
+ printManagedPreflight({
81
+ command: "upgrade",
82
+ ...summarizeDiffPreflight(diffReport),
83
+ consentSource: consent.consentSource,
84
+ policyProfile: consent.policyProfile
85
+ });
86
+ }
87
+
88
+ if (yes && shouldPromptApplyConfirmation({ applying, dryRun, json, confirm, interactive })) {
89
+ const approved = await promptApplyConfirmation({ command: "upgrade", createPrompt });
90
+ if (!approved) {
91
+ return {
92
+ cancelled: true,
93
+ dryRun: true,
94
+ wrote: false,
95
+ installedVersion: cliVersion,
96
+ latestVersion,
97
+ latestCommand,
98
+ previewCommand,
99
+ result: null,
100
+ statePresent: Boolean(state)
101
+ };
102
+ }
103
+ }
104
+
105
+ if (state) {
106
+ result = await updateGlobalHarness({
107
+ packageRoot,
108
+ packageName,
109
+ cliVersion,
110
+ homeDir,
111
+ workspaceRoot,
112
+ dryRun: preview
113
+ });
114
+ } else if (preview) {
115
+ const outcome = await runHarnessSetup({
116
+ packageRoot,
117
+ packageName,
118
+ cliVersion,
119
+ homeDir,
120
+ workspaceRoot,
121
+ dryRun: true,
122
+ yes: false,
123
+ interactive: false
124
+ });
125
+
126
+ if (outcome.cancelled) {
127
+ throw new Error("Upgrade preview cancelled.");
128
+ }
129
+
130
+ result = outcome.result;
131
+ } else {
132
+ throw new Error(`No managed state found. Run "${formatCliCommand("setup --yes")}" before upgrading.`);
133
+ }
134
+
135
+ return {
136
+ dryRun: preview,
137
+ wrote: !preview,
138
+ installedVersion: cliVersion,
139
+ latestVersion,
140
+ latestCommand,
141
+ previewCommand,
142
+ result,
143
+ statePresent: Boolean(state)
144
+ };
145
+ }
@@ -0,0 +1,145 @@
1
+ import { constants } from "node:fs";
2
+ import { access, readFile, readdir } from "node:fs/promises";
3
+ import { relative, resolve, sep } from "node:path";
4
+ import { renderTemplate } from "./text-template.js";
5
+
6
+ export const MODES = new Set(["minimal", "standard", "enterprise"]);
7
+ export const ADAPTERS = new Set(["codex", "cursor", "claude", "gemini", "copilot", "opencode", "pi"]);
8
+
9
+ export const ADAPTER_PATHS = {
10
+ codex: [".codex/"],
11
+ cursor: [".cursor/"],
12
+ claude: [".claude/", "CLAUDE.md"],
13
+ gemini: ["GEMINI.md"],
14
+ copilot: [".github/copilot-instructions.md"],
15
+ opencode: [".opencode/", "opencode.json.sample"],
16
+ pi: [".pi/"]
17
+ };
18
+
19
+ const TEXT_EXTENSIONS = new Set([".md", ".mdc", ".json", ".yml", ".yaml", ".sample", ".sh", ".mjs"]);
20
+
21
+ const MINIMAL_WHITELIST = new Set([
22
+ "AGENTS.md",
23
+ "CLAUDE.md",
24
+ "GEMINI.md",
25
+ "AGENT.md",
26
+ "docs/ai/architecture.md",
27
+ "docs/ai/harness.md",
28
+ "docs/ai/testing.md",
29
+ "docs/ai/git-workflow.md",
30
+ "docs/ai/memory.md",
31
+ "docs/ai/context-graph.md",
32
+ ".cursor/rules/core.mdc",
33
+ ".cursor/rules/testing.mdc",
34
+ ".cursor/rules/git.mdc",
35
+ ".github/copilot-instructions.md",
36
+ "setup-agent-links.sh"
37
+ ]);
38
+
39
+ const STANDARD_EXCLUDED_PREFIXES = [
40
+ "evals/",
41
+ ".github/workflows/",
42
+ ".opencode/loops/",
43
+ ".gentle-ai/loops/",
44
+ "scripts/harness/"
45
+ ];
46
+
47
+ export const COMPATIBILITY_LINKS = [
48
+ ["CLAUDE.md", "AGENTS.md"],
49
+ ["GEMINI.md", "AGENTS.md"],
50
+ [".claude/CLAUDE.md", "../AGENTS.md"],
51
+ [".agent/AGENTS.md", "../AGENTS.md"],
52
+ [".windsurfrules", "AGENTS.md"]
53
+ ];
54
+
55
+ export async function listTemplateFiles(packageRoot, mode, options = {}) {
56
+ if (!MODES.has(mode)) {
57
+ throw new Error(`Invalid mode "${mode}". Use minimal, standard, or enterprise.`);
58
+ }
59
+
60
+ const templateRoot = resolve(packageRoot, "repo-template");
61
+ const absolutePaths = await listFilesRecursive(templateRoot);
62
+ const selectedAdapters = normalizeAdapters(options.adapters);
63
+
64
+ return absolutePaths
65
+ .map((sourcePath) => ({ sourcePath, relativePath: normalizePath(relative(templateRoot, sourcePath)) }))
66
+ .filter(({ relativePath }) => shouldInstall(relativePath, mode, selectedAdapters));
67
+ }
68
+
69
+ async function listFilesRecursive(root) {
70
+ const entries = await readdir(root, { withFileTypes: true });
71
+ const files = [];
72
+
73
+ for (const entry of entries) {
74
+ const fullPath = resolve(root, entry.name);
75
+ if (entry.isDirectory()) files.push(...await listFilesRecursive(fullPath));
76
+ else if (entry.isFile()) files.push(fullPath);
77
+ }
78
+
79
+ return files;
80
+ }
81
+
82
+ function shouldInstall(relativePath, mode, selectedAdapters) {
83
+ const includedByMode = mode === "enterprise"
84
+ ? true
85
+ : mode === "minimal"
86
+ ? MINIMAL_WHITELIST.has(relativePath)
87
+ : !STANDARD_EXCLUDED_PREFIXES.some((prefix) => relativePath.startsWith(prefix));
88
+
89
+ if (!includedByMode) return false;
90
+ if (!selectedAdapters) return true;
91
+
92
+ const adapter = adapterForPath(relativePath);
93
+ if (!adapter) return true;
94
+
95
+ return selectedAdapters.has(adapter);
96
+ }
97
+
98
+ function normalizeAdapters(adapters) {
99
+ if (adapters == null) return null;
100
+
101
+ const normalized = new Set();
102
+ for (const adapter of adapters) {
103
+ if (!ADAPTERS.has(adapter)) {
104
+ throw new Error(`Invalid adapter "${adapter}". Use ${[...ADAPTERS].join(", ")}.`);
105
+ }
106
+ normalized.add(adapter);
107
+ }
108
+ return normalized;
109
+ }
110
+
111
+ export function adapterForPath(relativePath) {
112
+ for (const [adapter, patterns] of Object.entries(ADAPTER_PATHS)) {
113
+ if (patterns.some((pattern) => relativePath === pattern || relativePath.startsWith(pattern))) {
114
+ return adapter;
115
+ }
116
+ }
117
+
118
+ return null;
119
+ }
120
+
121
+ export function normalizePath(filePath) {
122
+ return sep === "/" ? filePath : filePath.split(sep).join("/");
123
+ }
124
+
125
+ export function isTextFile(filePath) {
126
+ return TEXT_EXTENSIONS.has(filePath.slice(filePath.lastIndexOf(".")));
127
+ }
128
+
129
+ export async function pathExists(filePath) {
130
+ try {
131
+ await access(filePath, constants.F_OK);
132
+ return true;
133
+ } catch {
134
+ return false;
135
+ }
136
+ }
137
+
138
+ export async function renderFileContent(sourcePath, project) {
139
+ if (isTextFile(sourcePath)) {
140
+ const text = await readFile(sourcePath, "utf8");
141
+ return Buffer.from(renderTemplate(text, project), "utf8");
142
+ }
143
+
144
+ return readFile(sourcePath);
145
+ }
@@ -0,0 +1,120 @@
1
+ import { mkdir, readFile, symlink, writeFile } from "node:fs/promises";
2
+ import { dirname, resolve } from "node:path";
3
+ import { COMPATIBILITY_LINKS, MODES, adapterForPath, listTemplateFiles, pathExists, renderFileContent } from "./harness-files.js";
4
+ import { hashBuffer } from "./hash.js";
5
+ import { createManifest, readManifest, writeManifest } from "./manifest.js";
6
+ import { formatCliCommand } from "./global/brand/cli.js";
7
+
8
+ export async function updateHarness({ project, packageRoot, packageName, cliVersion, mode, adapters, force = false, dryRun = false }) {
9
+ const manifest = await readManifest(project.root);
10
+
11
+ if (!manifest) {
12
+ throw new Error(`No .harness/manifest.json found. Run "${formatCliCommand("init")}" first.`);
13
+ }
14
+
15
+ const effectiveMode = mode ?? manifest.mode;
16
+
17
+ if (!MODES.has(effectiveMode)) {
18
+ throw new Error(`Invalid mode "${effectiveMode}". Use minimal, standard, or enterprise.`);
19
+ }
20
+
21
+ const effectiveAdapters = adapters == null ? manifest.adapters ?? null : [...adapters].sort();
22
+ const templateFiles = await listTemplateFiles(packageRoot, effectiveMode, { adapters: effectiveAdapters });
23
+ const result = {
24
+ mode: effectiveMode,
25
+ adapters: effectiveAdapters,
26
+ created: [],
27
+ updated: [],
28
+ unchanged: [],
29
+ skippedModified: [],
30
+ skippedUntracked: []
31
+ };
32
+ const nextFiles = { ...manifest.files };
33
+
34
+ for (const { relativePath, sourcePath } of templateFiles) {
35
+ await reconcileFile({ project, relativePath, sourcePath, manifest, force, dryRun, result, nextFiles });
36
+ }
37
+
38
+ await createMissingCompatibilityLinks(project.root, { adapters: effectiveAdapters, dryRun, result });
39
+
40
+ if (!dryRun) {
41
+ await writeManifest(project.root, createManifest({
42
+ packageName: manifest.packageName ?? packageName,
43
+ cliVersion,
44
+ mode: effectiveMode,
45
+ adapters: effectiveAdapters,
46
+ files: nextFiles,
47
+ installedAt: manifest.installedAt
48
+ }));
49
+ }
50
+
51
+ return result;
52
+ }
53
+
54
+ async function reconcileFile({ project, relativePath, sourcePath, manifest, force, dryRun, result, nextFiles }) {
55
+ const destinationPath = resolve(project.root, relativePath);
56
+ const newContent = await renderFileContent(sourcePath, project);
57
+ const newHash = hashBuffer(newContent);
58
+ const exists = await pathExists(destinationPath);
59
+
60
+ if (!exists) {
61
+ if (!dryRun) await writeManagedFile(destinationPath, newContent);
62
+ result.created.push(relativePath);
63
+ nextFiles[relativePath] = newHash;
64
+ return;
65
+ }
66
+
67
+ const trackedHash = manifest.files[relativePath];
68
+ const currentHash = hashBuffer(await readFile(destinationPath));
69
+ const isUnmodifiedSinceInstall = Boolean(trackedHash) && trackedHash === currentHash;
70
+
71
+ if (isUnmodifiedSinceInstall) {
72
+ nextFiles[relativePath] = newHash;
73
+
74
+ if (currentHash === newHash) {
75
+ result.unchanged.push(relativePath);
76
+ } else {
77
+ if (!dryRun) await writeManagedFile(destinationPath, newContent);
78
+ result.updated.push(relativePath);
79
+ }
80
+
81
+ return;
82
+ }
83
+
84
+ if (!force) {
85
+ result[trackedHash ? "skippedModified" : "skippedUntracked"].push(relativePath);
86
+ return;
87
+ }
88
+
89
+ if (!dryRun) await writeManagedFile(destinationPath, newContent);
90
+ result.updated.push(relativePath);
91
+ nextFiles[relativePath] = newHash;
92
+ }
93
+
94
+ async function writeManagedFile(destinationPath, content) {
95
+ await mkdir(dirname(destinationPath), { recursive: true });
96
+ await writeFile(destinationPath, content);
97
+ }
98
+
99
+ async function createMissingCompatibilityLinks(root, { adapters, dryRun, result }) {
100
+ for (const [linkPath, target] of COMPATIBILITY_LINKS) {
101
+ const adapter = adapterForPath(linkPath);
102
+ if (adapters && adapter && !adapters.includes(adapter)) continue;
103
+
104
+ const destination = resolve(root, linkPath);
105
+ const exists = await pathExists(destination);
106
+
107
+ if (exists) continue;
108
+
109
+ if (!dryRun) {
110
+ await mkdir(dirname(destination), { recursive: true });
111
+ try {
112
+ await symlink(target, destination);
113
+ } catch {
114
+ await writeFile(destination, `Compatibility pointer. Source of truth: ${target}\n`, "utf8");
115
+ }
116
+ }
117
+
118
+ result.created.push(linkPath);
119
+ }
120
+ }
package/src/hash.js ADDED
@@ -0,0 +1,5 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ export function hashBuffer(buffer) {
4
+ return createHash("sha256").update(buffer).digest("hex");
5
+ }
@@ -0,0 +1,36 @@
1
+ import { existsSync } from "node:fs";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { dirname, resolve } from "node:path";
4
+
5
+ export const MANIFEST_RELATIVE_PATH = ".harness/manifest.json";
6
+
7
+ export async function readManifest(projectRoot) {
8
+ const manifestPath = resolve(projectRoot, MANIFEST_RELATIVE_PATH);
9
+ if (!existsSync(manifestPath)) return null;
10
+
11
+ try {
12
+ return JSON.parse(await readFile(manifestPath, "utf8"));
13
+ } catch {
14
+ return null;
15
+ }
16
+ }
17
+
18
+ export async function writeManifest(projectRoot, manifest) {
19
+ const manifestPath = resolve(projectRoot, MANIFEST_RELATIVE_PATH);
20
+ await mkdir(dirname(manifestPath), { recursive: true });
21
+ await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
22
+ }
23
+
24
+ export function createManifest({ packageName, cliVersion, mode, adapters, files, installedAt }) {
25
+ const now = new Date().toISOString();
26
+
27
+ return {
28
+ packageName,
29
+ cliVersion,
30
+ mode,
31
+ adapters,
32
+ installedAt: installedAt ?? now,
33
+ updatedAt: now,
34
+ files
35
+ };
36
+ }
@@ -0,0 +1,105 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
+ import { basename, resolve } from "node:path";
4
+
5
+ export async function detectProject(cwd) {
6
+ const root = resolve(cwd);
7
+ const packageJsonPath = resolve(root, "package.json");
8
+ const packageJson = existsSync(packageJsonPath)
9
+ ? JSON.parse(await readFile(packageJsonPath, "utf8"))
10
+ : null;
11
+
12
+ const packageManager = detectPackageManager(root, packageJson);
13
+ const scripts = packageJson?.scripts ?? {};
14
+
15
+ return {
16
+ root,
17
+ name: packageJson?.name ?? basename(root),
18
+ purpose: packageJson?.description ?? "AI-assisted software project",
19
+ packageManager,
20
+ stack: detectStack(root, packageJson),
21
+ architecturePattern: detectArchitecture(root),
22
+ detectedAdapters: detectAdapters(root),
23
+ commands: {
24
+ install: installCommand(packageManager),
25
+ dev: scriptCommand(packageManager, scripts, "dev"),
26
+ lint: scriptCommand(packageManager, scripts, "lint"),
27
+ format: scriptCommand(packageManager, scripts, "format"),
28
+ typeCheck: scriptCommand(packageManager, scripts, "typecheck") ?? scriptCommand(packageManager, scripts, "type-check"),
29
+ test: scriptCommand(packageManager, scripts, "test"),
30
+ build: scriptCommand(packageManager, scripts, "build")
31
+ }
32
+ };
33
+ }
34
+
35
+ function detectPackageManager(root, packageJson) {
36
+ if (existsSync(resolve(root, "pnpm-lock.yaml"))) return "pnpm";
37
+ if (existsSync(resolve(root, "yarn.lock"))) return "yarn";
38
+ if (existsSync(resolve(root, "bun.lockb"))) return "bun";
39
+ if (existsSync(resolve(root, "package-lock.json"))) return "npm";
40
+
41
+ const declared = packageJson?.packageManager?.split("@")[0];
42
+ return declared ?? "npm";
43
+ }
44
+
45
+ function detectStack(root, packageJson) {
46
+ const dependencies = {
47
+ ...packageJson?.dependencies,
48
+ ...packageJson?.devDependencies
49
+ };
50
+
51
+ if (dependencies.next || existsSync(resolve(root, "next.config.js")) || existsSync(resolve(root, "next.config.mjs"))) return "Next.js";
52
+ if (dependencies.vite || existsSync(resolve(root, "vite.config.js")) || existsSync(resolve(root, "vite.config.ts"))) return "Vite";
53
+ if (dependencies.react) return "React";
54
+ if (existsSync(resolve(root, "pyproject.toml"))) return "Python";
55
+ if (existsSync(resolve(root, "go.mod"))) return "Go";
56
+ if (existsSync(resolve(root, "Cargo.toml"))) return "Rust";
57
+ if (packageJson) return "Node.js";
58
+ return "Unknown";
59
+ }
60
+
61
+ function detectArchitecture(root) {
62
+ if (existsSync(resolve(root, "src/domain")) || existsSync(resolve(root, "src/application"))) {
63
+ return "Layered or Clean Architecture";
64
+ }
65
+
66
+ if (existsSync(resolve(root, "app")) || existsSync(resolve(root, "pages"))) {
67
+ return "Framework-driven application";
68
+ }
69
+
70
+ return "To be documented";
71
+ }
72
+
73
+ function detectAdapters(root) {
74
+ const markers = [
75
+ ["codex", ".codex"],
76
+ ["cursor", ".cursor"],
77
+ ["claude", ".claude"],
78
+ ["claude", "CLAUDE.md"],
79
+ ["gemini", "GEMINI.md"],
80
+ ["copilot", ".github/copilot-instructions.md"],
81
+ ["opencode", ".opencode"],
82
+ ["opencode", "opencode.json"],
83
+ ["opencode", "opencode.json.sample"],
84
+ ["pi", ".pi"]
85
+ ];
86
+
87
+ return [...new Set(
88
+ markers
89
+ .filter(([, markerPath]) => existsSync(resolve(root, markerPath)))
90
+ .map(([adapter]) => adapter)
91
+ )].sort();
92
+ }
93
+
94
+ function installCommand(packageManager) {
95
+ if (packageManager === "pnpm") return "pnpm install";
96
+ if (packageManager === "yarn") return "yarn install";
97
+ if (packageManager === "bun") return "bun install";
98
+ return "npm install";
99
+ }
100
+
101
+ function scriptCommand(packageManager, scripts, scriptName) {
102
+ if (!scripts[scriptName]) return "Not configured";
103
+ if (packageManager === "npm") return `npm run ${scriptName}`;
104
+ return `${packageManager} ${scriptName}`;
105
+ }