@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,123 @@
1
+ import { existsSync, lstatSync, readFileSync, realpathSync } from "node:fs";
2
+ import { join, resolve } from "node:path";
3
+ import { createWorkspaceManagedSectionBuilder } from "./component-builders.js";
4
+ import { isPathInside } from "./component-paths.js";
5
+
6
+ export function workspaceCatalogPath(workspaceRoot) {
7
+ return join(resolve(workspaceRoot), ".harness", "components", "catalog.json");
8
+ }
9
+
10
+ export function hasWorkspaceCatalog(workspaceRoot) {
11
+ if (!workspaceRoot) return false;
12
+ return existsSync(workspaceCatalogPath(workspaceRoot));
13
+ }
14
+
15
+ export function readWorkspaceCatalogDocument(workspaceRoot) {
16
+ return JSON.parse(readFileSync(workspaceCatalogPath(workspaceRoot), "utf8"));
17
+ }
18
+
19
+ export function loadWorkspaceComponentCatalog(workspaceRoot, { bundledIds = [] } = {}) {
20
+ if (!workspaceRoot || !hasWorkspaceCatalog(workspaceRoot)) {
21
+ return [];
22
+ }
23
+
24
+ const resolvedRoot = resolve(workspaceRoot);
25
+ const catalog = readWorkspaceCatalogDocument(resolvedRoot);
26
+ const componentsRoot = join(resolvedRoot, ".harness", "components");
27
+ const entries = catalog.components;
28
+
29
+ if (!Array.isArray(entries)) {
30
+ throw new Error("Workspace component catalog must declare a components array.");
31
+ }
32
+
33
+ const seen = new Set();
34
+ const components = [];
35
+
36
+ for (const entry of entries) {
37
+ validateWorkspaceEntry(entry, { componentsRoot, workspaceRoot: resolvedRoot, bundledIds, seen });
38
+ seen.add(entry.id);
39
+
40
+ components.push({
41
+ id: entry.id,
42
+ label: entry.label,
43
+ version: entry.version,
44
+ source: "workspace",
45
+ defaultEnabled: false,
46
+ assetFiles: [...entry.assetFiles],
47
+ adapterHints: { ...(entry.adapterHints ?? {}) },
48
+ instructions: entry.instructions ?? null,
49
+ workspaceRoot: resolvedRoot,
50
+ buildManagedSection: createWorkspaceManagedSectionBuilder(entry)
51
+ });
52
+ }
53
+
54
+ return components;
55
+ }
56
+
57
+ function validateWorkspaceEntry(entry, { componentsRoot, workspaceRoot, bundledIds, seen }) {
58
+ const required = ["id", "label", "version", "assetFiles"];
59
+
60
+ for (const field of required) {
61
+ if (entry[field] === undefined) {
62
+ throw new Error(`Workspace component catalog entry is missing "${field}".`);
63
+ }
64
+ }
65
+
66
+ if (!Array.isArray(entry.assetFiles) || entry.assetFiles.length === 0) {
67
+ throw new Error(`Workspace component "${entry.id}" must declare at least one asset file.`);
68
+ }
69
+
70
+ if (bundledIds.includes(entry.id)) {
71
+ throw new Error(`Workspace component "${entry.id}" conflicts with a bundled component.`);
72
+ }
73
+
74
+ if (seen.has(entry.id)) {
75
+ throw new Error(`Duplicate workspace component id "${entry.id}".`);
76
+ }
77
+
78
+ const componentDir = join(componentsRoot, entry.id);
79
+
80
+ for (const assetFile of entry.assetFiles) {
81
+ validateAssetFile(assetFile, { componentDir, componentId: entry.id, workspaceRoot });
82
+ }
83
+ }
84
+
85
+ function validateAssetFile(assetFile, { componentDir, componentId, workspaceRoot }) {
86
+ if (typeof assetFile !== "string" || assetFile.length === 0) {
87
+ throw new Error(`Workspace component "${componentId}" has an invalid asset path.`);
88
+ }
89
+
90
+ if (assetFile.startsWith("/") || assetFile.includes("\\") || assetFile.includes("..")) {
91
+ throw new Error(`Workspace component "${componentId}" asset "${assetFile}" must be a relative path without "..".`);
92
+ }
93
+
94
+ if (assetFile.endsWith("/")) {
95
+ throw new Error(`Workspace component "${componentId}" asset "${assetFile}" must reference a file.`);
96
+ }
97
+
98
+ const assetPath = join(componentDir, ...assetFile.split("/"));
99
+ const normalized = resolve(assetPath);
100
+
101
+ if (!isPathInside(componentDir, normalized)) {
102
+ throw new Error(`Workspace component "${componentId}" asset "${assetFile}" escapes its component directory.`);
103
+ }
104
+
105
+ if (!existsSync(assetPath)) {
106
+ throw new Error(`Workspace component "${componentId}" is missing asset "${assetFile}".`);
107
+ }
108
+
109
+ if (lstatSync(assetPath).isDirectory()) {
110
+ throw new Error(`Workspace component "${componentId}" asset "${assetFile}" must reference a file.`);
111
+ }
112
+
113
+ assertAssetStaysInWorkspace(assetPath, workspaceRoot, componentId, assetFile);
114
+ }
115
+
116
+ function assertAssetStaysInWorkspace(assetPath, workspaceRoot, componentId, assetFile) {
117
+ const workspaceReal = realpathSync(workspaceRoot);
118
+ const assetReal = realpathSync(assetPath);
119
+
120
+ if (!isPathInside(workspaceReal, assetReal)) {
121
+ throw new Error(`Workspace component "${componentId}" asset "${assetFile}" escapes the workspace via symlink.`);
122
+ }
123
+ }
@@ -0,0 +1,28 @@
1
+ import { formatCliCommand } from "./brand/cli.js";
2
+ import { BRAND } from "./brand/index.js";
3
+
4
+ export function buildManagedBody(context, adapter) {
5
+ const sections = [
6
+ `## ${BRAND.displayName} (managed)`,
7
+ "",
8
+ `Managed by \`${context.packageName}\`. Content inside these markers is refreshed by`,
9
+ `\`${formatCliCommand("sync")}\`. Everything outside the markers is yours and is preserved.`
10
+ ];
11
+
12
+ if (context.components.length === 0) {
13
+ sections.push("", "_No optional components installed. Core plumbing only._");
14
+ return sections.join("\n");
15
+ }
16
+
17
+ for (const component of context.components) {
18
+ sections.push("", component.buildManagedSection(context, adapter));
19
+ }
20
+
21
+ sections.push(
22
+ "",
23
+ `- Run \`${formatCliCommand("doctor")}\` to check ecosystem health.`,
24
+ `- Run \`${formatCliCommand("uninstall")}\` to remove managed sections safely.`
25
+ );
26
+
27
+ return sections.join("\n");
28
+ }
@@ -0,0 +1,134 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
+ import { dirname, join } from "node:path";
4
+ import { backupFileBeforeChange } from "./backups.js";
5
+ import { buildManagedBody } from "./managed-body.js";
6
+ import { hasManagedSection, removeManagedSection, upsertManagedSection } from "./managed-section.js";
7
+ import { formatCliCommand } from "./brand/cli.js";
8
+
9
+ export function createManagedConfigAdapter({ id, label, rootDir, configFile }) {
10
+ const assets = {
11
+ rootDir,
12
+ configFile,
13
+ managedTargets: [configFile]
14
+ };
15
+
16
+ return {
17
+ id,
18
+ label,
19
+ assets,
20
+
21
+ detect(context) {
22
+ return existsSync(join(context.homeDir, rootDir));
23
+ },
24
+
25
+ plan(context) {
26
+ const configPath = join(context.homeDir, configFile);
27
+ const exists = existsSync(configPath);
28
+ const current = exists ? readFileSync(configPath, "utf8") : "";
29
+ const body = buildManagedBody(context, { id, assets });
30
+ const { changed } = upsertManagedSection(current, body);
31
+
32
+ let action = "unchanged";
33
+ if (changed) action = exists ? "update" : "create";
34
+
35
+ return {
36
+ adapterId: id,
37
+ configFile,
38
+ configPath,
39
+ action,
40
+ exists,
41
+ backupNeeded: exists && changed
42
+ };
43
+ },
44
+
45
+ async apply(context, plan) {
46
+ if (plan.action === "unchanged") {
47
+ return { configFile, action: "unchanged", backupPath: null };
48
+ }
49
+
50
+ const current = plan.exists ? await readFile(plan.configPath, "utf8") : "";
51
+ const body = buildManagedBody(context, { id, assets });
52
+ const { content: next } = upsertManagedSection(current, body);
53
+ let backupPath = null;
54
+
55
+ if (plan.backupNeeded) {
56
+ backupPath = await backupFileBeforeChange({
57
+ backupsDir: context.paths.backupsDir,
58
+ homeDir: context.homeDir,
59
+ filePath: plan.configPath,
60
+ timestamp: context.timestamp,
61
+ dryRun: context.dryRun
62
+ });
63
+ }
64
+
65
+ if (!context.dryRun) {
66
+ await mkdir(dirname(plan.configPath), { recursive: true });
67
+ await writeFile(plan.configPath, next, "utf8");
68
+ }
69
+
70
+ return { configFile, action: plan.action, backupPath };
71
+ },
72
+
73
+ async doctor(context, stateEntry) {
74
+ const installed = Boolean(stateEntry);
75
+ const detected = this.detect(context);
76
+ const isRelevant = detected || installed;
77
+
78
+ if (!isRelevant) {
79
+ return {
80
+ name: `agent:${id}`,
81
+ status: "info",
82
+ detail: "Not detected on this machine."
83
+ };
84
+ }
85
+
86
+ const configPath = join(context.homeDir, configFile);
87
+ if (!existsSync(configPath)) {
88
+ return {
89
+ name: `agent:${id}`,
90
+ status: installed ? "missing" : "warning",
91
+ detail: `Config not found: ~/${configFile}`
92
+ };
93
+ }
94
+
95
+ const content = await readFile(configPath, "utf8");
96
+ const managed = hasManagedSection(content);
97
+
98
+ return {
99
+ name: `agent:${id}`,
100
+ status: managed ? "ok" : "warning",
101
+ detail: managed
102
+ ? `Managed section present in ~/${configFile}`
103
+ : `No managed section in ~/${configFile}. Run "${formatCliCommand("install")}".`
104
+ };
105
+ },
106
+
107
+ async uninstall(context, stateEntry) {
108
+ const configPath = join(context.homeDir, configFile);
109
+ if (!existsSync(configPath)) {
110
+ return { configFile, cleaned: false, backupPath: null };
111
+ }
112
+
113
+ const content = await readFile(configPath, "utf8");
114
+ const { content: cleaned, removed } = removeManagedSection(content);
115
+ if (!removed) {
116
+ return { configFile, cleaned: false, backupPath: null };
117
+ }
118
+
119
+ const backupPath = await backupFileBeforeChange({
120
+ backupsDir: context.paths.backupsDir,
121
+ homeDir: context.homeDir,
122
+ filePath: configPath,
123
+ timestamp: context.timestamp,
124
+ dryRun: context.dryRun
125
+ });
126
+
127
+ if (!context.dryRun) {
128
+ await writeFile(configPath, cleaned, "utf8");
129
+ }
130
+
131
+ return { configFile, cleaned: true, backupPath };
132
+ }
133
+ };
134
+ }
@@ -0,0 +1,73 @@
1
+ export const SECTION_START = "<!-- harness:managed:start -->";
2
+ export const SECTION_END = "<!-- harness:managed:end -->";
3
+
4
+ export function hasManagedSection(content) {
5
+ const start = content.indexOf(SECTION_START);
6
+ const end = content.indexOf(SECTION_END);
7
+ return start !== -1 && end !== -1 && end > start;
8
+ }
9
+
10
+ export function extractManagedBody(content) {
11
+ if (!hasManagedSection(content)) return null;
12
+
13
+ const start = content.indexOf(SECTION_START) + SECTION_START.length;
14
+ const end = content.indexOf(SECTION_END);
15
+
16
+ return content.slice(start, end).replace(/^\n/, "").replace(/\n$/, "");
17
+ }
18
+
19
+ export function buildManagedBlock(body) {
20
+ return `${SECTION_START}\n${body.replace(/\s+$/, "")}\n${SECTION_END}\n`;
21
+ }
22
+
23
+ export function upsertManagedSection(content, body) {
24
+ const block = buildManagedBlock(body);
25
+
26
+ if (!hasManagedSection(content)) {
27
+ if (content.trim() === "") {
28
+ return { content: block, changed: block !== content, hadSection: false };
29
+ }
30
+
31
+ const next = `${content.replace(/\s+$/, "")}\n\n${block}`;
32
+ return { content: next, changed: true, hadSection: false };
33
+ }
34
+
35
+ const start = content.indexOf(SECTION_START);
36
+ const end = blockEndIndex(content);
37
+ const next = content.slice(0, start) + block + content.slice(end);
38
+
39
+ return { content: next, changed: next !== content, hadSection: true };
40
+ }
41
+
42
+ export function removeManagedSection(content) {
43
+ if (!hasManagedSection(content)) {
44
+ return { content, removed: false };
45
+ }
46
+
47
+ const start = content.indexOf(SECTION_START);
48
+ const end = blockEndIndex(content);
49
+ const before = content.slice(0, start).replace(/\s+$/, "");
50
+ const after = content.slice(end).replace(/^\s+/, "");
51
+
52
+ if (!before && !after) return { content: "", removed: true };
53
+ if (!before) return { content: after, removed: true };
54
+ if (!after) return { content: `${before}\n`, removed: true };
55
+
56
+ return { content: `${before}\n\n${after}`, removed: true };
57
+ }
58
+
59
+ export function userOwnedContent(content) {
60
+ if (!hasManagedSection(content)) return content;
61
+
62
+ const start = content.indexOf(SECTION_START);
63
+ const end = blockEndIndex(content);
64
+ const before = content.slice(0, start);
65
+ const after = content.slice(end);
66
+
67
+ return `${before}${after}`;
68
+ }
69
+
70
+ function blockEndIndex(content) {
71
+ const end = content.indexOf(SECTION_END) + SECTION_END.length;
72
+ return content[end] === "\n" ? end + 1 : end;
73
+ }
@@ -0,0 +1,25 @@
1
+ export async function fetchPublishedVersion(packageName, fetchJson = defaultFetchJson) {
2
+ const testVersion = process.env.HARNESS_TEST_LATEST_VERSION?.trim();
3
+ if (testVersion) {
4
+ return testVersion;
5
+ }
6
+
7
+ const encodedPackage = encodeURIComponent(packageName);
8
+ const meta = await fetchJson(`https://registry.npmjs.org/${encodedPackage}/latest`);
9
+
10
+ if (!meta?.version) {
11
+ throw new Error(`Could not resolve latest version for ${packageName}.`);
12
+ }
13
+
14
+ return meta.version;
15
+ }
16
+
17
+ async function defaultFetchJson(url) {
18
+ const response = await fetch(url);
19
+
20
+ if (!response.ok) {
21
+ throw new Error(`Failed to fetch ${url}: HTTP ${response.status}`);
22
+ }
23
+
24
+ return response.json();
25
+ }
@@ -0,0 +1,22 @@
1
+ import { homedir } from "node:os";
2
+ import { join } from "node:path";
3
+
4
+ export const HARNESS_DIR_NAME = ".harness";
5
+
6
+ export function resolveHomeDir(env = process.env) {
7
+ return env.HARNESS_HOME ?? homedir();
8
+ }
9
+
10
+ export function harnessHomePaths(homeDir) {
11
+ const root = join(homeDir, HARNESS_DIR_NAME);
12
+
13
+ return {
14
+ homeDir,
15
+ root,
16
+ statePath: join(root, "state.json"),
17
+ policyPath: join(root, "policy.json"),
18
+ historyPath: join(root, "history.jsonl"),
19
+ coreDir: join(root, "core"),
20
+ backupsDir: join(root, "backups")
21
+ };
22
+ }