@holmes-lab/holmes-kit 0.1.7 → 0.1.9

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 (114) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +11 -7
  3. package/bin/holmes-mcp.js +0 -0
  4. package/dist/.build-id +1 -1
  5. package/dist/holmes/cli/agents.d.ts +50 -0
  6. package/dist/holmes/cli/ci-gate.d.ts +18 -0
  7. package/dist/holmes/cli/doctor.d.ts +44 -0
  8. package/dist/holmes/cli/gitignore-merge.d.ts +18 -0
  9. package/dist/holmes/cli/governed-precondition.d.ts +27 -0
  10. package/dist/holmes/cli/index.d.ts +14 -0
  11. package/dist/holmes/cli/index.js +23 -1
  12. package/dist/holmes/cli/init.d.ts +69 -0
  13. package/dist/holmes/cli/interactive-prompt.d.ts +9 -0
  14. package/dist/holmes/cli/playbook-skills.d.ts +129 -0
  15. package/dist/holmes/cli/roles-readme.d.ts +12 -0
  16. package/dist/holmes/cli/serve.d.ts +14 -0
  17. package/dist/holmes/cli/serve.js +70 -0
  18. package/dist/holmes/cli/settings-merge.d.ts +66 -0
  19. package/dist/holmes/config/config.d.ts +13 -0
  20. package/dist/holmes/context/bundler.d.ts +40 -0
  21. package/dist/holmes/context/render.d.ts +9 -0
  22. package/dist/holmes/context/tiers.d.ts +54 -0
  23. package/dist/holmes/context/tokens.d.ts +2 -0
  24. package/dist/holmes/cpg/ast-mutation.d.ts +31 -0
  25. package/dist/holmes/cpg/ast-mutation.js +126 -0
  26. package/dist/holmes/cpg/cpg-scanner.d.ts +69 -0
  27. package/dist/holmes/cpg/dynamic-cpg.d.ts +18 -0
  28. package/dist/holmes/cpg/dynamic-cpg.js +82 -0
  29. package/dist/holmes/cpg/hash-cache.d.ts +21 -0
  30. package/dist/holmes/cpg/language-parser-walk.d.ts +31 -0
  31. package/dist/holmes/cpg/language-parser-worker.d.ts +1 -0
  32. package/dist/holmes/cpg/language-parser.d.ts +116 -0
  33. package/dist/holmes/cpg/program-slicing.d.ts +21 -0
  34. package/dist/holmes/cpg/program-slicing.js +123 -0
  35. package/dist/holmes/cpg/scan-cache.d.ts +21 -0
  36. package/dist/holmes/cpg/source-path.d.ts +2 -0
  37. package/dist/holmes/cpg/test-files.d.ts +36 -0
  38. package/dist/holmes/governance/constitution-debt.d.ts +3 -0
  39. package/dist/holmes/governance/constitution-report.d.ts +23 -0
  40. package/dist/holmes/governance/constitution.d.ts +61 -0
  41. package/dist/holmes/governance/identity.d.ts +28 -0
  42. package/dist/holmes/governance/ledger-lock.d.ts +55 -0
  43. package/dist/holmes/governance/ledger-store.conformance.d.ts +11 -0
  44. package/dist/holmes/governance/ledger-store.d.ts +114 -0
  45. package/dist/holmes/governance/progress-ledger.d.ts +25 -0
  46. package/dist/holmes/governance/provenance-chain.d.ts +154 -0
  47. package/dist/holmes/governance/provenance-ledger.d.ts +102 -0
  48. package/dist/holmes/governance/provenance-schema.d.ts +21 -0
  49. package/dist/holmes/governance/replica-id.d.ts +20 -0
  50. package/dist/holmes/governance/role-policy.d.ts +52 -0
  51. package/dist/holmes/governance/trust-score.d.ts +32 -0
  52. package/dist/holmes/guardrail/anchors.d.ts +8 -0
  53. package/dist/holmes/guardrail/blind-spots.d.ts +32 -0
  54. package/dist/holmes/guardrail/decision-ledger.d.ts +29 -0
  55. package/dist/holmes/guardrail/executable-artifact.d.ts +1 -0
  56. package/dist/holmes/guardrail/governance-history.d.ts +15 -0
  57. package/dist/holmes/guardrail/phase.d.ts +27 -0
  58. package/dist/holmes/guardrail/risk-classifier.d.ts +52 -0
  59. package/dist/holmes/guardrail/risk-gate.d.ts +77 -0
  60. package/dist/holmes/guardrail/risk-types.d.ts +48 -0
  61. package/dist/holmes/guardrail/tspec-state.d.ts +71 -0
  62. package/dist/holmes/guardrail/write-target.d.ts +77 -0
  63. package/dist/holmes/hooks/adapters/antigravity.d.ts +42 -0
  64. package/dist/holmes/hooks/pre-tool-use.d.ts +144 -0
  65. package/dist/holmes/hooks/stop.d.ts +91 -0
  66. package/dist/holmes/mcp/basis.d.ts +100 -0
  67. package/dist/holmes/mcp/handlers.d.ts +482 -0
  68. package/dist/holmes/mcp/server.d.ts +2 -0
  69. package/dist/holmes/mcp/stdio-client.d.ts +51 -0
  70. package/dist/holmes/mcp/supervisor.d.ts +48 -0
  71. package/dist/holmes/mcp/tool-schemas.d.ts +12 -0
  72. package/dist/holmes/mcp/validate-args.d.ts +27 -0
  73. package/dist/holmes/messages/registry.d.ts +23 -0
  74. package/dist/holmes/project/baseline.d.ts +66 -0
  75. package/dist/holmes/project/change-source.d.ts +89 -0
  76. package/dist/holmes/project/ignore.d.ts +33 -0
  77. package/dist/holmes/project/root.d.ts +34 -0
  78. package/dist/holmes/reverse/anchor.d.ts +47 -0
  79. package/dist/holmes/reverse/cluster.d.ts +45 -0
  80. package/dist/holmes/reverse/draft.d.ts +33 -0
  81. package/dist/holmes/reverse/dynamic-wiring.d.ts +18 -0
  82. package/dist/holmes/reverse/scan.d.ts +103 -0
  83. package/dist/holmes/reverse/surface.d.ts +91 -0
  84. package/dist/holmes/reverse/test-map.d.ts +91 -0
  85. package/dist/holmes/review/coverage.d.ts +21 -0
  86. package/dist/holmes/review/findings.d.ts +49 -0
  87. package/dist/holmes/review/package.d.ts +26 -0
  88. package/dist/holmes/review/review-targets.d.ts +28 -0
  89. package/dist/holmes/review/scope.d.ts +13 -0
  90. package/dist/holmes/review/test-evidence.d.ts +31 -0
  91. package/dist/holmes/review/test-runner.d.ts +141 -0
  92. package/dist/holmes/rtm/dataflow-taint.d.ts +81 -0
  93. package/dist/holmes/rtm/gap-analyzer.d.ts +28 -0
  94. package/dist/holmes/rtm/git-changes.d.ts +33 -0
  95. package/dist/holmes/rtm/heatmap.d.ts +21 -0
  96. package/dist/holmes/rtm/incremental.d.ts +38 -0
  97. package/dist/holmes/rtm/localize.d.ts +36 -0
  98. package/dist/holmes/rtm/rtm-builder.d.ts +101 -0
  99. package/dist/holmes/rtm/rtm-check.d.ts +13 -0
  100. package/dist/holmes/rtm/rtm-graph.d.ts +88 -0
  101. package/dist/holmes/rtm/taint.d.ts +35 -0
  102. package/dist/holmes/rtm/test-scope.d.ts +85 -0
  103. package/dist/holmes/server/dashboard.d.ts +85 -0
  104. package/dist/holmes/server/dashboard.js +2188 -0
  105. package/dist/holmes/spec/approval-blockers.d.ts +62 -0
  106. package/dist/holmes/spec/breaking-change.d.ts +42 -0
  107. package/dist/holmes/spec/legacy-format.d.ts +93 -0
  108. package/dist/holmes/spec/spec-digest.d.ts +29 -0
  109. package/dist/holmes/spec/spec-parser.d.ts +12 -0
  110. package/dist/holmes/spec/spec-store.conformance.d.ts +10 -0
  111. package/dist/holmes/spec/spec-store.d.ts +130 -0
  112. package/dist/holmes/spec/spec-types.d.ts +150 -0
  113. package/dist/holmes/spec/validator.d.ts +65 -0
  114. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
  <!-- @implements A-SPEC-209 -->
8
+ ## [0.1.8] - 2026-08-20
9
+
10
+ ### Added
11
+ - **Hybrid Dynamic CPG Engine (REQ-216)**: Dynamic test coverage trace fusion with static AST graphs to eliminate reflection/dynamic invocation false negatives.
12
+ - **Datalog-based Program Slicing & Noise Pruning (REQ-217)**: Transitive closure reachability slicing (forward/backward/both) to prune irrelevant AST nodes and isolate affected blast radius.
13
+ - **AST Mutation & Fuzzing Harness (REQ-214)**: Multi-operator mutation generator (relational, arithmetic, logical, unary) with mutation score calculation.
14
+ - **TypeScript Declaration Bundling**: Full `.d.ts` declaration generation across all modules and `types` entrypoint in `package.json`.
15
+
8
16
  ## [0.1.7] - 2026-08-19
9
17
 
10
18
  ### Changed
package/README.md CHANGED
@@ -55,7 +55,7 @@ Holmes-Kit prioritizes **CLI-based AI Coding Agents** where OS-level process hoo
55
55
  ```bash
56
56
  npm install -g @holmes-lab/holmes-kit
57
57
  ```
58
- *(Requires Node.js `>= 20.0.0` and C++ build tools for native SQLite/tree-sitter)*
58
+ *(Prerequisites: Node.js `>= 20.0.0`. Prebuilt binaries are provided automatically for macOS, Linux, and Windows. For minimal Alpine Docker containers, install build tools: `apk add --no-cache python3 make g++`)*
59
59
 
60
60
  ### 2. Initialize in Your Project
61
61
  ```bash
@@ -104,12 +104,16 @@ flowchart LR
104
104
  | `holmes-kit init --agent all` | Non-interactive instant setup for all supported agents |
105
105
  | `holmes-kit init --dry-run` | Preview files and configuration changes without writing |
106
106
  | `holmes-kit doctor` | Comprehensive health check of specs, hooks, MCP, and anchors |
107
+ | `holmes-kit doctor --fix` | Automatically self-heal and repair broken hooks or missing skills |
108
+ | `holmes-kit ci` | Run non-interactive headless governance gate for GitHub Actions / GitLab CI |
109
+ | `holmes-kit ci --json` | Run CI gate and emit machine-readable JSON results |
107
110
 
108
111
  ---
109
112
 
110
- ## 🤖 Manual MCP Configuration
113
+ ## 🧩 Optional: Manual MCP Integration (Cursor, Windsurf, Claude Desktop)
111
114
 
112
- If you prefer to configure MCP manually or integrate with other IDEs, add the following to your `.mcp.json`:
115
+ > **Note**: If you ran `holmes-kit init`, this configuration is **100% automated for you**.
116
+ > Use the manual configuration below only if you wish to integrate Holmes-Kit into standalone third-party MCP clients like Cursor, Windsurf, Claude Desktop, or VS Code:
113
117
 
114
118
  ```json
115
119
  {
@@ -154,11 +158,11 @@ Holmes-Kit embeds native AST & Code Property Graph (D-CPG) analyzers to track ca
154
158
 
155
159
  ---
156
160
 
157
- ## 🏛️ Key Capabilities
161
+ ## 🔒 Source Code Availability & Distribution Policy
158
162
 
159
- - **Deterministic Hook Enforcement**: Gating is enforced via OS-level PreToolUse & Stop hooks, not easily bypassed prompts.
160
- - **D-CPG + RTM Graph**: Tree-sitter Code Property Graph + SQLite recursive-CTE for instant blast-radius impact analysis (`rtm_impact`).
161
- - **Cryptographic Provenance Ledger**: Every decision, approval, and gate transition is immutably recorded in `.ax/ledger/`.
163
+ - **Distribution Mode**: Holmes-Kit is currently distributed and executable as an official public npm package ([`@holmes-lab/holmes-kit`](https://www.npmjs.com/package/@holmes-lab/holmes-kit)).
164
+ - **Source Code Status**: The underlying source code repository is currently **private / closed-source**.
165
+ - **Open Source Consideration**: Decisions regarding whether, when, and how to transition to a full open-source codebase will be reviewed and determined in future milestones based on enterprise feedback, security audits, and community governance requirements.
162
166
 
163
167
  ---
164
168
 
package/bin/holmes-mcp.js CHANGED
File without changes
package/dist/.build-id CHANGED
@@ -1 +1 @@
1
- 47dc66a-msytu2tq
1
+ ef6b2ad-mt1pqqxf
@@ -0,0 +1,50 @@
1
+ /**
2
+ * 하네스별 배선물.
3
+ *
4
+ * Holmes-Kit 의 명제("승인된 스펙 없이는 코드가 들어가지 않는다")를 **집행**하는 것은 MCP
5
+ * 도구가 아니라 훅이다. 그래서 하네스마다 물어야 할 첫 질문은 "이 도구에 훅이 있는가"이고,
6
+ * 없다면 **집행한다고 말하지 않는 것**이 이 파일의 일이다. 도구는 주되 규율은 못 세우면서
7
+ * 세운다고 적으면, 그 문장을 믿은 사용자가 지켜지지 않는 게이트 위에서 일한다.
8
+ */
9
+ export declare const AGENTS: readonly ["claude", "antigravity", "codex"];
10
+ export type Agent = (typeof AGENTS)[number];
11
+ /**
12
+ * 그 하네스에서 게이트가 **집행되는가**.
13
+ *
14
+ * - `claude` — PreToolUse/Stop 훅. 이 저장소가 처음부터 배선해 온 하네스.
15
+ * - `antigravity` — PreToolUse/Stop 훅이 있고 `deny`/`continue` 로 막을 수 있다(설치본의
16
+ * `agy-customizations` 규약 문서와 실제 대화 기록에서 실측).
17
+ * - `codex` — 이 기계에 설치되어 있지 않아 훅 규약을 **재지 못했다**. 재지 못한 것을 있다고
18
+ * 적지 않는다. 도구(MCP)와 지침만 준다.
19
+ */
20
+ export declare const HARNESS_ENFORCES: Record<Agent, boolean>;
21
+ export interface AgentWiringOptions {
22
+ target: string;
23
+ packageRoot: string;
24
+ specsDir: string;
25
+ }
26
+ export interface AgentFile {
27
+ path: string;
28
+ content: string;
29
+ }
30
+ /**
31
+ * 이 하네스에 써야 할 파일들. **쓰지는 않는다** — 무엇을 쓸지만 말한다.
32
+ *
33
+ * 계산과 쓰기를 나누는 이유는 dry-run 이 실제 실행과 같은 집합을 예고해야 하기 때문이다
34
+ * (A-SPEC-190 §9). 같은 함수가 두 경로에 답하면 둘이 어긋날 수 없다.
35
+ */
36
+ export declare function agentFiles(agent: Agent, opts: AgentWiringOptions): AgentFile[];
37
+ export interface AgentLink {
38
+ path: string;
39
+ target: string;
40
+ why: string;
41
+ }
42
+ /**
43
+ * 하네스가 스킬을 **볼 수 있게** 하는 링크.
44
+ *
45
+ * 스킬 본문은 한 곳(`.claude/skills`)에만 둔다 — 같은 내용을 두 벌 두면 하나가 갱신될 때
46
+ * 다른 하나가 낡고, doctor 는 한쪽만 본다(그 드리프트가 REQ-190 의 출발점이었다). 그래서
47
+ * Antigravity 쪽은 링크로 같은 파일을 가리킨다. 이 배치는 타깃 안에 머물므로 설치 담장을
48
+ * 지나간다(A-SPEC-193 §7 이 그 담장을 '타깃 밖만'으로 좁힌 이유이기도 하다).
49
+ */
50
+ export declare function agentLinks(agent: Agent, opts: AgentWiringOptions): AgentLink[];
@@ -0,0 +1,18 @@
1
+ export interface CiViolation {
2
+ file?: string;
3
+ specId?: string;
4
+ reason: string;
5
+ }
6
+ export interface CiGateResult {
7
+ ok: boolean;
8
+ violations: CiViolation[];
9
+ summary: string;
10
+ }
11
+ /**
12
+ * Headless CI/CD Governance Gate Runner for GitHub Actions / GitLab CI.
13
+ *
14
+ * @implements A-SPEC-213
15
+ */
16
+ export declare function runCiGate(targetDir: string, options?: {
17
+ specsDir?: string;
18
+ }): CiGateResult;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * `holmes-kit doctor` — install diagnostics.
3
+ *
4
+ * Native-module breakage is the likeliest failure mode of an install on another machine, and the
5
+ * hooks fail OPEN, so a broken install is silent: governance simply never fires. This command makes
6
+ * that loud. Every check reports PASS/WARN/FAIL plus a concrete remediation.
7
+ */
8
+ export type Level = 'PASS' | 'WARN' | 'FAIL';
9
+ export interface Check {
10
+ name: string;
11
+ level: Level;
12
+ detail: string;
13
+ fix?: string;
14
+ remediate?: () => boolean;
15
+ }
16
+ export interface DoctorOptions {
17
+ run?: ProbeRunner;
18
+ autoFix?: boolean;
19
+ }
20
+ /**
21
+ * Environment variables removed before spawning a probe.
22
+ *
23
+ * @implements A-SPEC-143
24
+ * `HOLMES_APPROVAL` grants permission, so a probe that inherits one measures the GRANT rather than
25
+ * the gate. `HOLMES_SPECS` redirects the spec root, so a probe reading another project's specs
26
+ * answers a different question than the one asked. `HOLMES_LEDGER_KEY` is deliberately NOT here: it
27
+ * changes how the ledger is signed, not whether the gate enforces, and stripping it could mask a
28
+ * real key-related failure.
29
+ */
30
+ export declare const STRIPPED_FOR_PROBE: readonly ["HOLMES_APPROVAL", "HOLMES_SPECS"];
31
+ /** The parent environment minus the variables that legitimately change a gate decision. Pure. */
32
+ export declare function probeEnv(parent: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
33
+ /** Seam for the gate probe's child process. Default is a real `spawnSync`; tests inject a fake. */
34
+ export interface ProbeRunner {
35
+ /** `env` is passed EXPLICITLY so a test can observe what the child would actually receive — a
36
+ * runner that never sees it cannot verify the wiring, only restate the helper. */
37
+ (script: string, payload: unknown, env: NodeJS.ProcessEnv): {
38
+ status: number | null;
39
+ stdout?: string;
40
+ stderr?: string;
41
+ };
42
+ }
43
+ export declare function runDoctor(packageRoot: string, target?: string, opts?: DoctorOptions, extraChecks?: Check[]): Promise<Check[]>;
44
+ export declare function formatChecks(checks: Check[]): string;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * PURE .gitignore block merge. holmes-kit writes derived state under `.ax/` in the TARGET project
3
+ * (ledger, cpg_cache, reports) that must never be committed. The block is delimited so it can be
4
+ * added, refreshed, and removed exactly — never touching the user's own lines.
5
+ */
6
+ export declare const BEGIN = "# >>> holmes-kit >>>";
7
+ export declare const END = "# <<< holmes-kit <<<";
8
+ /** Derived/runtime paths holmes-kit creates in a target project. */
9
+ export declare const IGNORE_LINES: string[];
10
+ /**
11
+ * Add (or refresh) the holmes-kit block. Idempotent: a second call reproduces the same text.
12
+ * Preserves everything outside the block, and preserves whether the file ended with a newline.
13
+ */
14
+ export declare function mergeGitignore(existing: string, lines?: string[]): string;
15
+ /** Remove the block (and the blank line that preceded it, if we added one). Inverse of merge. */
16
+ export declare function removeGitignoreBlock(existing: string): string;
17
+ /** True iff the holmes-kit block is present. */
18
+ export declare function hasGitignoreBlock(existing: string): boolean;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * How many directories the walk may visit.
3
+ *
4
+ * The cap only bites on projects with NO code — the walk returns at the first gated file — and those
5
+ * are exactly the ones where exhausting it gives the wrong playbook. Measured 2026-08-12 at roughly
6
+ * 0.05ms per directory, so 2000 costs about 100ms inside a one-shot command that also writes files
7
+ * and installs skills.
8
+ */
9
+ export declare const MAX_DIRS = 2000;
10
+ /**
11
+ * Does this target contain a file the Write/Edit gate would refuse once governed mode is live?
12
+ *
13
+ * The judgement is `classifyAction` — the gate's own function. Re-deriving "is this code" from a
14
+ * second extension list would leave two sources of truth, and the guidance would be wrong the moment
15
+ * they drifted apart (the failure REQ-163 measured for path identity).
16
+ *
17
+ * Content is never read: `classifyAction`'s shebang signal needs the file open, which does not fit
18
+ * the cost ceiling, and missing it errs toward `adopt` — the harmless direction.
19
+ */
20
+ export declare function hasGatedCode(target: string): boolean;
21
+ /**
22
+ * The three lines a forced governed transition owes the user: that the precondition was skipped,
23
+ * what is now denied (and what still works), and the one playbook that fits this target.
24
+ *
25
+ * Naming both playbooks would hand the judgement back to the user, which is the defect itself.
26
+ */
27
+ export declare function governedWarning(target: string): string[];
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `holmes-kit` CLI entry — argv dispatch, hand-rolled (no new dependency).
3
+ *
4
+ * Commands:
5
+ * init [--mode guardrail|governed] [--target dir] [--dry-run] [--remove] [--force] ...
6
+ * doctor [--target dir] [--json]
7
+ */
8
+ /**
9
+ * The installed package root. From `dist/holmes/cli/index.js` that is three levels up. Node resolves
10
+ * a global bin symlink to its real path before setting __dirname, so this is correct even when the
11
+ * command is invoked through the PATH shim.
12
+ */
13
+ export declare function packageRoot(): string;
14
+ export declare function main(argv: string[]): Promise<number>;
@@ -37,6 +37,7 @@ exports.packageRoot = packageRoot;
37
37
  exports.main = main;
38
38
  // @implements A-SPEC-100.2
39
39
  // @implements A-SPEC-213
40
+ // @implements A-SPEC-215
40
41
  const fs = __importStar(require("node:fs"));
41
42
  const path = __importStar(require("node:path"));
42
43
  const init_1 = require("./init");
@@ -71,6 +72,7 @@ const KNOWN_FLAGS = {
71
72
  doctor: ['help', 'target', 'json'],
72
73
  skills: ['help', 'target'],
73
74
  ci: ['help', 'target', 'specs-dir', 'json'],
75
+ serve: ['help', 'target', 'port'],
74
76
  };
75
77
  class UnknownFlagError extends Error {
76
78
  }
@@ -325,6 +327,21 @@ async function main(argv) {
325
327
  }
326
328
  return res.ok ? 0 : 1;
327
329
  }
330
+ // @implements A-SPEC-215
331
+ if (cmd === 'serve') {
332
+ if (typeof flags.target === 'string') {
333
+ const t = path.resolve(flags.target);
334
+ if (!fs.existsSync(t) || !fs.statSync(t).isDirectory()) {
335
+ process.stderr.write(`--target ${t} is not an existing directory\n\n${USAGE}`);
336
+ return 2;
337
+ }
338
+ }
339
+ const target = typeof flags.target === 'string' ? flags.target : process.cwd();
340
+ const port = typeof flags.port === 'string' ? parseInt(flags.port, 10) : undefined;
341
+ const { runServeCommand } = require('./serve');
342
+ await runServeCommand({ root: target, port });
343
+ return 0;
344
+ }
328
345
  if (cmd === 'init') {
329
346
  const mode = (typeof flags.mode === 'string' ? flags.mode : 'guardrail');
330
347
  // 형제 열거 오류와 같은 모양(round 7): --settings·--target 은 USAGE 와 함께 exit 2 인데
@@ -428,5 +445,10 @@ async function main(argv) {
428
445
  return cmd.startsWith('-') ? 2 : 1;
429
446
  }
430
447
  if (require.main === module) {
431
- main(process.argv.slice(2)).then((code) => process.exit(code));
448
+ main(process.argv.slice(2))
449
+ .then((code) => process.exit(code))
450
+ .catch((err) => {
451
+ process.stderr.write(`[holmes-kit error] ${err?.message || err}\n`);
452
+ process.exit(1);
453
+ });
432
454
  }
@@ -0,0 +1,69 @@
1
+ import { HookPlan } from './settings-merge';
2
+ import { Agent } from './agents';
3
+ import { Approval } from '../guardrail/risk-gate';
4
+ /**
5
+ * `holmes-kit init` — wire holmes-kit into a TARGET project (the I/O half; all merge logic is pure
6
+ * and lives in settings-merge.ts / gitignore-merge.ts).
7
+ *
8
+ * The mode choice is the single most consequential decision this tool makes:
9
+ * guardrail (DEFAULT) — PreToolUse matcher 'Bash' + Stop. On a spec-less project this blocks
10
+ * nothing (the shell-code-write gate checks for approved specs first, and the
11
+ * Stop gate early-returns on an empty governed set).
12
+ * governed — matcher 'Bash|Write|Edit'. phaseCheck has NO empty-spec escape, so on a
13
+ * project without an approved A-SPEC this denies EVERY code write — it would
14
+ * brick the target. Hence the precondition check below.
15
+ */
16
+ export declare const SERVER_NAME = "holmes-kit";
17
+ export type InitMode = 'guardrail' | 'governed';
18
+ export interface InitOptions {
19
+ target: string;
20
+ packageRoot: string;
21
+ mode: InitMode;
22
+ specsDir: string;
23
+ settingsFile: 'local' | 'project';
24
+ mcp: boolean;
25
+ matcher?: string;
26
+ dryRun: boolean;
27
+ force: boolean;
28
+ remove: boolean;
29
+ approval?: Approval;
30
+ agents?: Agent[];
31
+ allowAdditive?: boolean;
32
+ }
33
+ export interface FileChange {
34
+ path: string;
35
+ before: string | null;
36
+ after: string | null;
37
+ }
38
+ export interface InitResult {
39
+ ok: boolean;
40
+ exitCode: 0 | 1 | 2;
41
+ messages: string[];
42
+ changes: FileChange[];
43
+ /** Paths a dry-run says it WOULD DELETE — never rendered with the write verb (round-8). */
44
+ removals?: string[];
45
+ }
46
+ /**
47
+ * @implements A-SPEC-163
48
+ * Which tools the PreToolUse hook is registered for.
49
+ *
50
+ * `guardrail` used to be `Bash` alone, so an installed project's `Write` never reached the hook at
51
+ * all — measured 2026-08-08 against a real install. Fixing the gate's own tool predicate would have
52
+ * changed nothing there, which is the third time a capability existed in the repo and stopped at the
53
+ * installer (REQ-142, REQ-153).
54
+ *
55
+ * `.*` rather than a list: the list is what let `MultiEdit` and `NotebookEdit` through, and the next
56
+ * tool would repeat it. The gate itself decides what is a write (`writesFiles`); the matcher only
57
+ * decides what it gets to look at, and it should look at everything.
58
+ */
59
+ /**
60
+ * @implements A-SPEC-178
61
+ * The matcher `init` installs. Exported so `doctor` compares against THIS value rather than a second
62
+ * spelling of it — a copy drifts the moment this changes, and doctor would then warn about correct
63
+ * wiring, which is worse than saying nothing.
64
+ */
65
+ export declare const MATCHERS: Record<InitMode, string>;
66
+ /** Absolute hook/mcp commands, quoted so a package path containing spaces still works. */
67
+ export declare function buildHookPlan(packageRoot: string, matcher: string, specsDir?: string): HookPlan;
68
+ export declare const settingsPathOf: (target: string, which: "local" | "project") => string;
69
+ export declare function runInit(opts: InitOptions): InitResult;
@@ -0,0 +1,9 @@
1
+ import { Agent } from './agents';
2
+ /**
3
+ * Parses user input or CLI flag string into normalized Agent array.
4
+ */
5
+ export declare function parseAgentList(input: string): Agent[];
6
+ /**
7
+ * Renders an interactive TTY checkbox selection menu using standard readline & ANSI codes.
8
+ */
9
+ export declare function promptAgentSelection(availableAgents?: readonly Agent[], currentWired?: Agent[]): Promise<Agent[]>;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Install the recovery playbooks into a project as agent skills.
3
+ *
4
+ * WHY this exists, measured: the gate denies with five deliberately specific strings, and each
5
+ * playbook is a skill whose `description` quotes the exact deny text that should summon it — the
6
+ * layer that turns a refusal into a next step. It did not ship. `npm pack --dry-run` carried ZERO
7
+ * playbook files, because `files: ["bin/","dist/"]` covered neither `playbooks/` nor anything like
8
+ * it, and `init` never wrote one either. Every installed user got the enforcement and none of the
9
+ * recovery, while all 47 conformance assertions passed in this repository and protected nobody.
10
+ *
11
+ * Playbooks are ADVISORY. No gate decision reads them, and a user who deletes every one of them
12
+ * still gets identical enforcement — which is why a missing playbook is a doctor WARN, never a FAIL.
13
+ */
14
+ /**
15
+ * Marks a file as OURS. Ownership is decided by this marker and never by the path, because the path
16
+ * is exactly what a user's own skill of the same name would occupy, and overwriting that would be
17
+ * the same class of error as clobbering their settings.
18
+ */
19
+ export declare const PLAYBOOK_SKILL_MARKER = "<!-- installed by holmes-kit; edits will be replaced on upgrade -->";
20
+ /**
21
+ * @implements A-SPEC-190 (round 7)
22
+ * The bytes an install writes. The marker used to go FIRST, which put it ahead of the playbook's
23
+ * YAML frontmatter — and frontmatter is only frontmatter when it starts at byte 0. Measured on this
24
+ * repository's own installed skills: the agent's skill list rendered
25
+ * `holmes-adopt: <!-- installed by holmes-kit; edits will be replaced on upgrade -->` for all three,
26
+ * i.e. the `description` that quotes the deny text — the entire reason this layer exists, since a
27
+ * refusal is supposed to summon the playbook by that sentence — was destroyed at install time,
28
+ * while doctor certified the layout as `current`. The marker now follows the frontmatter block.
29
+ */
30
+ export declare function installedSkillBytes(body: string): string;
31
+ /**
32
+ * @implements A-SPEC-190 (round 7)
33
+ * Ownership is the marker AT ITS PLACE, not the string anywhere in the file. `includes` made any
34
+ * document that merely QUOTES the marker — documentation about holmes-kit, a review note — read as
35
+ * ours: doctor called it `drifted` and the refresh it advised overwrote the user's file with no
36
+ * backup. A quotation is not a claim of ownership.
37
+ */
38
+ export declare function carriesMarker(content: string): boolean;
39
+ /**
40
+ * @implements A-SPEC-190 (round 8)
41
+ * Where a leading YAML frontmatter block ends, or null. Round-8 measured the shapes the first
42
+ * regex missed — a BOM, an EMPTY block (`---\n---\n`), and a closing fence at end-of-file with no
43
+ * trailing newline — each of which sent the marker back to byte 0, recreating the CRITICAL the
44
+ * round-7 fix existed to remove while doctor certified the result as `current`.
45
+ */
46
+ export declare function frontmatterEnd(content: string): number | null;
47
+ /**
48
+ * @implements A-SPEC-172
49
+ * What the user types to reach a playbook.
50
+ *
51
+ * The install directory is namespaced (`holmes-adopt`), but the internal playbook name is bare
52
+ * (`adopt`), and every message echoed the bare one. Measured 2026-08-12 on an installed tarball: init
53
+ * printed `Installed 3 recovery skill(s): adopt, author-slice, promote-slice` while nothing by those
54
+ * names existed. Anything shown to a user goes through here.
55
+ */
56
+ export declare function invocableSkillName(playbookName: string): string;
57
+ /** `<target>/.claude/skills/holmes-<name>/SKILL.md` */
58
+ export declare function skillPathFor(target: string, playbookName: string): string;
59
+ export interface PlaybookInstallResult {
60
+ /** Playbook names written (new, or refreshed because the destination was ours). */
61
+ written: string[];
62
+ /** Destinations whose previous content was preserved beside them before being rewritten. */
63
+ backedUp: {
64
+ name: string;
65
+ path: string;
66
+ }[];
67
+ /** Names left alone because a FOREIGN file occupies the destination. */
68
+ skipped: string[];
69
+ /** Names whose destination could not be written; reported rather than thrown. */
70
+ failed: {
71
+ name: string;
72
+ reason: string;
73
+ }[];
74
+ }
75
+ export declare function installPlaybookSkills(packageRoot: string, target: string): PlaybookInstallResult;
76
+ export interface PlaybookRemoveResult {
77
+ /** RAW directory names, case intact — round-7: the report re-prefixed a case variant and named a directory that does not exist. */
78
+ removed: string[];
79
+ failed: {
80
+ name: string;
81
+ reason: string;
82
+ }[];
83
+ }
84
+ /** Removes ONLY files carrying the marker; a foreign file, and the directory holding it, are left. */
85
+ export declare function removePlaybookSkills(target: string): PlaybookRemoveResult;
86
+ /**
87
+ * How a failed entry should be NAMED in a report.
88
+ *
89
+ * @implements A-SPEC-190 §21 (round 13) — root-level refusals carry `.claude/skills` as their name,
90
+ * and the callers ran every name through `invocableSkillName`, printing `holmes-.claude/skills` —
91
+ * a skill that does not exist. Round 7 fixed the same prefixing on the remove side. A name that
92
+ * already looks like a path is a path.
93
+ */
94
+ export declare function skillLabel(name: string): string;
95
+ export type PlaybookSkillState = 'current' | 'drifted' | 'foreign' | 'missing' | 'orphaned' | 'source-unreadable' | 'aliased' | 'unresolvable';
96
+ /**
97
+ * @implements A-SPEC-190
98
+ * Per-playbook state of the installed copy, judged against the bytes install would write NOW
99
+ * (`marker + '\n' + shipped body`). An installed skill is a COPY: the kit's conventions move on
100
+ * while the copy stays frozen at install time. Measured 2026-08-15 in this very repository: all
101
+ * three installed skills had drifted (diff 50/38/64 lines) and the author-slice copy still taught
102
+ * the abolished `A-SPEC-125.5 → T-SPEC-1255` id convention — while the count-only doctor check
103
+ * reported "3 of 3 … PASS". Equality with the reconstruction is the single source of truth; a
104
+ * version stamp or hash file would be a second truth that itself goes stale.
105
+ *
106
+ * A shipped body that cannot be read (a race — playbookNames just confirmed it) yields no entry
107
+ * for that name: silence beats a wrong repair hint.
108
+ */
109
+ export declare function playbookSkillStates(packageRoot: string, target: string): {
110
+ name: string;
111
+ state: PlaybookSkillState;
112
+ }[];
113
+ /**
114
+ * @implements A-SPEC-190 (§6)
115
+ * Which extra `.claude/skills` entries are ORPHANED marker-owned installs? Pure — the fs facts
116
+ * (inode identity, marker ownership) come in as functions, so the case-SENSITIVE world is testable
117
+ * on any machine (round-5 HIGH lived exactly in the branch this box's case-folding FS cannot
118
+ * exercise: `Holmes-adopt` sliced to `adopt`, matched shipped, and was swallowed BEFORE the
119
+ * variant/inode logic — doctor printed PASS over stale marker-owned guidance).
120
+ *
121
+ * - exact canonical entries are the shipped loop's business (skip);
122
+ * - a case-variant with the SAME inode is the same physical dir (case-insensitive FS — skip;
123
+ * round-2: double-counting made the orphan remedy delete the current skill);
124
+ * - everything else marker-owned is orphaned; unreadable/markerless stays silent (ownership is
125
+ * the marker's job — user territory).
126
+ */
127
+ export declare function classifyExtraEntries(entries: string[], shipped: string[], sameInode: (entry: string, canonicalDir: string) => boolean | undefined, owned: (entry: string) => boolean): string[];
128
+ /** How many recovery skills are installed — the fact `doctor` reports. */
129
+ export declare function installedPlaybookCount(target: string): number;
@@ -0,0 +1,12 @@
1
+ /** Identifies the file as ours, so `--remove` never reclaims something a user wrote. */
2
+ export declare const ROLES_README_MARKER = "<!-- holmes-kit:roles-guide -->";
3
+ export declare const rolesReadmePath: (root: string) => string;
4
+ /** Installs the guidance. A file the user has edited is left alone. */
5
+ export declare function installRolesReadme(root: string): void;
6
+ /**
7
+ * Reclaims the guidance on `init --remove`. Returns whether it was ours to remove.
8
+ *
9
+ * Only this file is touched — a project's real `policy.yaml` living beside it must survive, since
10
+ * removing the docs is not a request to disable governance.
11
+ */
12
+ export declare function removeRolesReadme(root: string): boolean;
@@ -0,0 +1,14 @@
1
+ export interface ServeOptions {
2
+ root: string;
3
+ port?: number;
4
+ }
5
+ /**
6
+ * Execute holmes-kit serve subcommand. Keeps process alive until SIGINT/SIGTERM in CLI execution.
7
+ *
8
+ * @implements A-SPEC-215
9
+ */
10
+ export declare function runServeCommand(options: ServeOptions): Promise<{
11
+ ok: boolean;
12
+ url: string;
13
+ stop: () => Promise<void>;
14
+ }>;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.runServeCommand = runServeCommand;
37
+ // @implements A-SPEC-215
38
+ const fs = __importStar(require("node:fs"));
39
+ const path = __importStar(require("node:path"));
40
+ const dashboard_1 = require("../server/dashboard");
41
+ /**
42
+ * Execute holmes-kit serve subcommand. Keeps process alive until SIGINT/SIGTERM in CLI execution.
43
+ *
44
+ * @implements A-SPEC-215
45
+ */
46
+ async function runServeCommand(options) {
47
+ const root = path.resolve(options.root);
48
+ if (!fs.existsSync(root) || !fs.statSync(root).isDirectory()) {
49
+ process.stderr.write(`[holmes-kit serve] Target directory does not exist: ${root}\n`);
50
+ process.exit(2);
51
+ }
52
+ const handle = await (0, dashboard_1.startDashboardServer)({ root, port: options.port });
53
+ process.stdout.write(`🚀 Holmes-Kit Dashboard Server running at ${handle.url}\nPress Ctrl+C to stop.\n`);
54
+ if (process.env.NODE_ENV !== 'test') {
55
+ await new Promise((resolve) => {
56
+ const shutdown = async () => {
57
+ process.stdout.write(`\nStopping Holmes-Kit Dashboard Server...\n`);
58
+ await handle.stop();
59
+ resolve();
60
+ };
61
+ process.once('SIGINT', shutdown);
62
+ process.once('SIGTERM', shutdown);
63
+ });
64
+ }
65
+ return {
66
+ ok: true,
67
+ url: handle.url,
68
+ stop: handle.stop,
69
+ };
70
+ }