@descent-vtt/spec-harness 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +229 -0
- package/README.md +19 -4
- package/dist/cli.d.ts +15 -1
- package/dist/cli.js +112 -45
- package/dist/cli.js.map +1 -1
- package/dist/commands.js +12 -2
- package/dist/commands.js.map +1 -1
- package/dist/configure.d.ts +243 -11
- package/dist/configure.js +468 -62
- package/dist/configure.js.map +1 -1
- package/dist/fs.d.ts +10 -0
- package/dist/fs.js +14 -0
- package/dist/fs.js.map +1 -1
- package/dist/git.d.ts +31 -0
- package/dist/git.js +38 -0
- package/dist/git.js.map +1 -1
- package/dist/hooks.d.ts +17 -1
- package/dist/hooks.js +17 -1
- package/dist/hooks.js.map +1 -1
- package/dist/round.d.ts +43 -7
- package/dist/round.js +159 -45
- package/dist/round.js.map +1 -1
- package/dist/setup.d.ts +7 -0
- package/dist/setup.js +96 -17
- package/dist/setup.js.map +1 -1
- package/dist/usage.d.ts +59 -0
- package/dist/usage.js +53 -0
- package/dist/usage.js.map +1 -1
- package/dist/workspace.d.ts +9 -0
- package/dist/workspace.js +40 -8
- package/dist/workspace.js.map +1 -1
- package/package.json +1 -1
package/dist/setup.d.ts
CHANGED
|
@@ -26,6 +26,13 @@ export interface Step {
|
|
|
26
26
|
* doctor both read it. `path` is `null` when git cannot say where its hooks
|
|
27
27
|
* are. `note` is what there is to say of how a hook that runs the harness
|
|
28
28
|
* runs it, or `null`.
|
|
29
|
+
*
|
|
30
|
+
* Only a file is read. A directory under the hook's name, which a
|
|
31
|
+
* repository whose `core.hooksPath` is in its work tree can hold, is not a
|
|
32
|
+
* hook and is one to git all the same: it tries to run what it can execute
|
|
33
|
+
* there, fails, and stops the commit (ADR-0005 has what was measured). Read
|
|
34
|
+
* as a file, it ended this on Node's EISDIR and a stack, and a named pipe
|
|
35
|
+
* would have kept it from ever coming back.
|
|
29
36
|
*/
|
|
30
37
|
export declare function gitHook(root: string): Promise<{
|
|
31
38
|
readonly hook: GitHookState;
|
package/dist/setup.js
CHANGED
|
@@ -11,18 +11,18 @@
|
|
|
11
11
|
* that exist and never replaces what a person wrote; a git hook the
|
|
12
12
|
* repository already has is left alone, with the line to add printed instead.
|
|
13
13
|
*/
|
|
14
|
-
import { existsSync, statSync } from 'node:fs';
|
|
14
|
+
import { accessSync, constants, existsSync, statSync } from 'node:fs';
|
|
15
15
|
import { chmod, mkdir, readFile } from 'node:fs/promises';
|
|
16
16
|
import { dirname, isAbsolute, join, relative, sep } from 'node:path';
|
|
17
17
|
import { CONFIG_FILE } from './config.js';
|
|
18
|
-
import { chooseBase, describeGitHook, describeGraph, describeSkipped, GIT_HOOK_LINE, gitHookNote, graphReadsBriefs, GUARD_HOOK, isStarted, loadsPlugin, measuresArchive, mergeClaudeSettings, mergeMcp, mergeSpecBrief, mergeSpecGraph, missingGitHook, PLUGIN, PRE_COMMIT, PROTECT_RULE_FILES, registersServer, SPEC_BRIEF_CONFIGS, SPEC_GRAPH_CONFIGS, } from './configure.js';
|
|
18
|
+
import { arrayMatcher, chooseBase, commasUnder, describeAbsent, describeArrayMatcher, describeGitHook, describeGraph, describeHooksOff, describePresence, describeSkipped, GIT_HOOK_LINE, gitHookNote, graphReadsBriefs, GUARD_HOOK, guardSwitchedOff, isStarted, loadsPlugin, measuresArchive, mergeClaudeSettings, mergeMcp, mergeSpecBrief, mergeSpecGraph, missingGitHook, ONE_STRING, patternReader, PLUGIN, PRE_COMMIT, PROJECT_SETTINGS, PROTECT_RULE_FILES, registersServer, SPEC_BRIEF_CONFIGS, SPEC_GRAPH_CONFIGS, } from './configure.js';
|
|
19
19
|
import { readJsonObject as readJson, writeAtomic } from './fs.js';
|
|
20
20
|
import { hookPath, localBranches, remoteDefault } from './git.js';
|
|
21
21
|
import { claudeCodeWiring, pluginFile } from './round.js';
|
|
22
22
|
import { runSibling } from './siblings.js';
|
|
23
|
-
import { EXIT_ERROR, EXIT_OK, json, openWorkspace } from './workspace.js';
|
|
23
|
+
import { answerOf, EXIT_ERROR, EXIT_OK, json, openWorkspace } from './workspace.js';
|
|
24
24
|
/** The project's settings file for Claude Code, as a person finds it and as doctor names it. */
|
|
25
|
-
const SETTINGS =
|
|
25
|
+
const SETTINGS = PROJECT_SETTINGS;
|
|
26
26
|
function stringify(value) {
|
|
27
27
|
return `${JSON.stringify(value, null, 2)}\n`;
|
|
28
28
|
}
|
|
@@ -39,27 +39,62 @@ function shown(root, path) {
|
|
|
39
39
|
const inside = relative(root, path);
|
|
40
40
|
return inside.startsWith('..') || isAbsolute(inside) ? path : inside.split(sep).join('/');
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Whether git takes what stands at a hook's path for a hook to run. git
|
|
44
|
+
* asks the system whether the path can be executed, and skips one that
|
|
45
|
+
* cannot, with no more than a hint: asked here the same way. On Windows the
|
|
46
|
+
* question is only whether the path is there (Node's fs page, File access
|
|
47
|
+
* constants, read 2026-10-14), and git there runs whatever is.
|
|
48
|
+
*/
|
|
49
|
+
function executable(path) {
|
|
50
|
+
let may = true;
|
|
51
|
+
try {
|
|
52
|
+
accessSync(path, constants.X_OK);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
may = false;
|
|
56
|
+
}
|
|
57
|
+
return may;
|
|
58
|
+
}
|
|
59
|
+
/** What stands at a path, links followed, or `null` where nothing does: a link to nothing is nothing, to git as well. */
|
|
60
|
+
function standing(path) {
|
|
61
|
+
try {
|
|
62
|
+
return statSync(path);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
42
68
|
/**
|
|
43
69
|
* git's pre-commit hook, where git runs it from, and what is there: init and
|
|
44
70
|
* doctor both read it. `path` is `null` when git cannot say where its hooks
|
|
45
71
|
* are. `note` is what there is to say of how a hook that runs the harness
|
|
46
72
|
* runs it, or `null`.
|
|
73
|
+
*
|
|
74
|
+
* Only a file is read. A directory under the hook's name, which a
|
|
75
|
+
* repository whose `core.hooksPath` is in its work tree can hold, is not a
|
|
76
|
+
* hook and is one to git all the same: it tries to run what it can execute
|
|
77
|
+
* there, fails, and stops the commit (ADR-0005 has what was measured). Read
|
|
78
|
+
* as a file, it ended this on Node's EISDIR and a stack, and a named pipe
|
|
79
|
+
* would have kept it from ever coming back.
|
|
47
80
|
*/
|
|
48
81
|
export async function gitHook(root) {
|
|
49
82
|
const path = await hookPath('pre-commit', root);
|
|
50
83
|
if (path === null)
|
|
51
84
|
return { hook: { state: 'unknown', reason: 'git does not say where they are; spec-harness needs git 2.31 or later' }, path, note: null };
|
|
52
85
|
const file = shown(root, path);
|
|
53
|
-
|
|
86
|
+
const there = standing(path);
|
|
87
|
+
if (there === null)
|
|
54
88
|
return { hook: { state: 'absent', file }, path, note: null };
|
|
89
|
+
const runs = executable(path);
|
|
90
|
+
if (!there.isFile()) {
|
|
91
|
+
const what = there.isDirectory() ? 'a directory' : 'neither a file nor a directory';
|
|
92
|
+
return { hook: { state: runs ? 'stops' : 'ignored', file, what }, path, note: null };
|
|
93
|
+
}
|
|
55
94
|
const text = await readFile(path, 'utf8');
|
|
56
95
|
if (!text.includes('spec-harness'))
|
|
57
96
|
return { hook: { state: 'other', file }, path, note: null };
|
|
58
|
-
|
|
59
|
-
// has no executable bit, and runs it.
|
|
60
|
-
// The Windows job holds the platform check; the sweep runs on Linux, where it is always true.
|
|
61
|
-
const inert = process.platform !== 'win32' && (statSync(path).mode & 0o111) === 0;
|
|
62
|
-
return { hook: { state: inert ? 'inert' : 'runs', file }, path, note: gitHookNote(text) };
|
|
97
|
+
return { hook: { state: runs ? 'runs' : 'inert', file }, path, note: gitHookNote(text) };
|
|
63
98
|
}
|
|
64
99
|
export async function plan(workspace, options, io) {
|
|
65
100
|
const { root } = workspace;
|
|
@@ -72,7 +107,21 @@ export async function plan(workspace, options, io) {
|
|
|
72
107
|
const harnessFile = join(root, CONFIG_FILE);
|
|
73
108
|
const harness = (await readJson(harnessFile));
|
|
74
109
|
const namedBase = harness !== null && typeof harness['base'] === 'string' ? harness['base'] : null;
|
|
75
|
-
|
|
110
|
+
// What the plan asks of git and of Claude Code's settings waits on the
|
|
111
|
+
// root and on nothing of the rest, so it is asked at once: three of git's
|
|
112
|
+
// processes side by side where each waited on the one before. Each is
|
|
113
|
+
// read below where it was asked before, so a plan that fails still fails
|
|
114
|
+
// on the first of them in that order. The matchers in the settings are
|
|
115
|
+
// read with one reader, for the wiring and for what to write. Claude
|
|
116
|
+
// Code's release is asked only where what is read of a line turns on it.
|
|
117
|
+
const pattern = patternReader();
|
|
118
|
+
const [factsAsked, wiringAsked, hookAsked] = await Promise.allSettled([
|
|
119
|
+
namedBase === null ? Promise.all([remoteDefault(root), localBranches(root)]) : null,
|
|
120
|
+
claudeCodeWiring(root, io, pattern, false),
|
|
121
|
+
gitHook(root),
|
|
122
|
+
]);
|
|
123
|
+
const facts = answerOf(factsAsked);
|
|
124
|
+
const choice = facts === null ? null : chooseBase({ remoteDefault: facts[0], branch: workspace.branch, branches: facts[1] });
|
|
76
125
|
const base = choice === null ? namedBase : choice.base;
|
|
77
126
|
// spec-brief first: every other setting is derived from its directories.
|
|
78
127
|
const briefConfig = SPEC_BRIEF_CONFIGS.find((name) => existsSync(join(root, name)));
|
|
@@ -190,10 +239,17 @@ export async function plan(workspace, options, io) {
|
|
|
190
239
|
// guard every write twice (ADR-0012). The hook's lines are known as doctor
|
|
191
240
|
// knows them, by the hook's own reading and by the writes Claude Code
|
|
192
241
|
// starts each for, so that the two say the same of every file (ADR-0005).
|
|
193
|
-
const wiring =
|
|
242
|
+
const wiring = answerOf(wiringAsked);
|
|
194
243
|
const { plugin } = wiring;
|
|
244
|
+
// A plugin that is not on this machine brings neither. init writes none
|
|
245
|
+
// on that reading alone, which a plugin that is there after all would
|
|
246
|
+
// make a second guard: it says what doctor says, and how to have the
|
|
247
|
+
// entries here instead.
|
|
248
|
+
const absent = wiring.presence?.state === 'absent' ? wiring.presence.detail : null;
|
|
195
249
|
const settingsFile = join(root, '.claude', 'settings.json');
|
|
196
250
|
const settings = await readJson(settingsFile);
|
|
251
|
+
// An array for a matcher, under which Claude Code loads no hook of the file.
|
|
252
|
+
const array = arrayMatcher(settings);
|
|
197
253
|
const lines = wiring.lines.filter((line) => line.file === SETTINGS);
|
|
198
254
|
// What doctor says of a line this file holds is said here too, with why
|
|
199
255
|
// init leaves the line: one a person changed is theirs, and a second line
|
|
@@ -206,15 +262,28 @@ export async function plan(workspace, options, io) {
|
|
|
206
262
|
// This file's own entry is said first, so the others are named only
|
|
207
263
|
// where it holds none.
|
|
208
264
|
const present = wiring.hooks.includes(SETTINGS);
|
|
209
|
-
|
|
265
|
+
if (absent !== null)
|
|
266
|
+
steps.push({ file: SETTINGS, action: 'advise', detail: describeAbsent(plugin, absent, 'hooks', present) });
|
|
267
|
+
else {
|
|
268
|
+
// A plugin that cannot be told to be on this machine is no reason to
|
|
269
|
+
// write either: said beside the reason, as doctor says it.
|
|
270
|
+
const untold = describePresence(wiring);
|
|
271
|
+
const detail = describeSkipped(plugin, 'hooks', present, wiring.hooks);
|
|
272
|
+
steps.push({ file: SETTINGS, action: wiring.hooks.length > 0 ? 'advise' : 'skip', detail: untold === null ? detail : `${detail}. ${untold}` });
|
|
273
|
+
}
|
|
210
274
|
}
|
|
211
275
|
else if (settings === 'unreadable') {
|
|
212
276
|
steps.push({ file: SETTINGS, action: 'advise', detail: `cannot be read as JSON; add the guard as a PreToolUse and a PostToolUse hook by hand: ${JSON.stringify(GUARD_HOOK)}` });
|
|
213
277
|
}
|
|
278
|
+
else if (array !== null) {
|
|
279
|
+
// A hook written into such a file would be one more that is not loaded:
|
|
280
|
+
// the matcher is the person's to mend, and init is run again after.
|
|
281
|
+
steps.push({ file: SETTINGS, action: 'advise', detail: `${describeArrayMatcher(SETTINGS, array)}, those init writes among them, so it writes none: ${ONE_STRING}, and run init again` });
|
|
282
|
+
}
|
|
214
283
|
else {
|
|
215
284
|
// An entry is a line of the hook where doctor reports its words as one in this file.
|
|
216
285
|
const reported = new Set(lines.map((line) => JSON.stringify(line.line)));
|
|
217
|
-
const merged = mergeClaudeSettings(settings ?? {}, (words) => reported.has(JSON.stringify(words)));
|
|
286
|
+
const merged = mergeClaudeSettings(settings ?? {}, (words) => reported.has(JSON.stringify(words)), pattern, commasUnder(wiring.release));
|
|
218
287
|
// A line that turns the guard off or holds every write is one doctor
|
|
219
288
|
// fails, and lines of which none is waited on before a write are no
|
|
220
289
|
// guard between them: left as they are, they are for the person to mend.
|
|
@@ -229,11 +298,19 @@ export async function plan(workspace, options, io) {
|
|
|
229
298
|
apply: () => writeAtomic(settingsFile, stringify(merged)),
|
|
230
299
|
});
|
|
231
300
|
}
|
|
301
|
+
// disableAllHooks turns off whatever hooks there are, and those init
|
|
302
|
+
// writes with them: said of the file that holds the key, which is the
|
|
303
|
+
// person's to change. init writes what it would have written.
|
|
304
|
+
if (wiring.hooksOff !== null)
|
|
305
|
+
steps.push({ file: wiring.hooksOff, action: 'advise', detail: describeHooksOff(wiring.hooksOff, guardSwitchedOff(wiring)) });
|
|
232
306
|
const mcpFile = join(root, '.mcp.json');
|
|
233
307
|
const mcp = await readJson(mcpFile);
|
|
234
308
|
if (plugin !== null) {
|
|
235
309
|
const present = registersServer(mcp);
|
|
236
|
-
|
|
310
|
+
if (absent !== null)
|
|
311
|
+
steps.push({ file: '.mcp.json', action: 'advise', detail: describeAbsent(plugin, absent, 'server', present) });
|
|
312
|
+
else
|
|
313
|
+
steps.push({ file: '.mcp.json', action: present ? 'advise' : 'skip', detail: describeSkipped(plugin, 'server', present) });
|
|
237
314
|
}
|
|
238
315
|
else if (mcp === 'unreadable') {
|
|
239
316
|
steps.push({ file: '.mcp.json', action: 'advise', detail: 'cannot be read as JSON; add the spec-harness server by hand' });
|
|
@@ -254,7 +331,7 @@ export async function plan(workspace, options, io) {
|
|
|
254
331
|
// a linked worktree's shared hooks. It is the guard for a write the agent's
|
|
255
332
|
// hooks never see, one through a shell, so it is advised when not asked
|
|
256
333
|
// for; it is configuration outside the tree, so it is written only then.
|
|
257
|
-
const { hook, path: hookFile, note: hookNote } =
|
|
334
|
+
const { hook, path: hookFile, note: hookNote } = answerOf(hookAsked);
|
|
258
335
|
// Only a git older than 2.31, which the README states as the harness's
|
|
259
336
|
// floor, cannot name its hooks, and init runs only in a work tree, where
|
|
260
337
|
// any later git names them: no test reaches this advice, by that decision.
|
|
@@ -266,7 +343,9 @@ export async function plan(workspace, options, io) {
|
|
|
266
343
|
// with the line that runs it as init writes the hook now.
|
|
267
344
|
steps.push({ file: hook.file, action: 'keep', detail: hookNote === null ? 'already runs spec-harness' : `already runs spec-harness; ${hookNote}` });
|
|
268
345
|
}
|
|
269
|
-
else if (hook.state === 'inert') {
|
|
346
|
+
else if (hook.state === 'inert' || hook.state === 'stops' || hook.state === 'ignored') {
|
|
347
|
+
// What is there is the person's to mend or to take away: nothing is
|
|
348
|
+
// written over it, and what doctor says of it is said.
|
|
270
349
|
steps.push({ file: hook.file, action: 'advise', detail: describeGitHook(hook) });
|
|
271
350
|
}
|
|
272
351
|
else if (hook.state === 'other') {
|
package/dist/setup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `spec-harness init`: the family configured to agree, from the first day.\n *\n * The integration faults found when the tools were first run together were all\n * configuration: spec-graph read an archived brief as a retired decision and\n * reported every brief that depended on one; spec-guard executed an archived\n * brief's assertions. Each tool can be told the truth; `init` tells each of\n * them the same truth, derived from spec-brief's own settings.\n *\n * It prints a plan and changes nothing until `--write`. It merges into files\n * that exist and never replaces what a person wrote; a git hook the\n * repository already has is left alone, with the line to add printed instead.\n */\n\nimport { existsSync, statSync } from 'node:fs';\nimport { chmod, mkdir, readFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, sep } from 'node:path';\n\nimport { CONFIG_FILE } from './config.js';\nimport {\n chooseBase,\n describeGitHook,\n describeGraph,\n describeSkipped,\n GIT_HOOK_LINE,\n gitHookNote,\n type GitHookState,\n graphReadsBriefs,\n GUARD_HOOK,\n isStarted,\n loadsPlugin,\n measuresArchive,\n mergeClaudeSettings,\n mergeMcp,\n mergeSpecBrief,\n mergeSpecGraph,\n missingGitHook,\n PLUGIN,\n PRE_COMMIT,\n PROTECT_RULE_FILES,\n registersServer,\n SPEC_BRIEF_CONFIGS,\n SPEC_GRAPH_CONFIGS,\n} from './configure.js';\nimport { readJsonObject as readJson, writeAtomic } from './fs.js';\nimport { hookPath, localBranches, remoteDefault } from './git.js';\nimport { claudeCodeWiring, pluginFile } from './round.js';\nimport { runSibling } from './siblings.js';\nimport { EXIT_ERROR, EXIT_OK, json, openWorkspace, type CliIO, type Options, type Workspace } from './workspace.js';\n\n/** `skip`: a file init would write, left alone because Claude Code's plugin brings what init would add. */\nexport type Action = 'create' | 'update' | 'keep' | 'skip' | 'run' | 'advise';\n\nexport interface Step {\n readonly file: string;\n readonly action: Action;\n readonly detail: string;\n apply?: () => Promise<void>;\n}\n\ntype Json = Record<string, unknown>;\n\n/** The project's settings file for Claude Code, as a person finds it and as doctor names it. */\nconst SETTINGS = '.claude/settings.json';\n\nfunction stringify(value: unknown): string {\n return `${JSON.stringify(value, null, 2)}\\n`;\n}\n\nconst PLUGIN_DETAIL = `load spec-harness's plugin, \"${PLUGIN}\": spec-brief's archive asks it whether a signed ruling allows a protected file, and refuses the file without it`;\n\n/** What init adds to spec-brief's configuration, said once for the plan. */\nfunction specBriefDetail(plugin: boolean, base: string | null): string {\n const parts = plugin ? [PLUGIN_DETAIL] : [];\n if (base !== null) parts.push(`measure the archive from ${base}, as spec-harness does: without a base, spec-brief's archive warns that the scope went unmeasured and checks no protected file`);\n return parts.join('; ');\n}\n\n/** A path as a person finds it: from the root when it is inside it, POSIX-separated, and as it is otherwise. */\nfunction shown(root: string, path: string): string {\n const inside = relative(root, path);\n return inside.startsWith('..') || isAbsolute(inside) ? path : inside.split(sep).join('/');\n}\n\n/**\n * git's pre-commit hook, where git runs it from, and what is there: init and\n * doctor both read it. `path` is `null` when git cannot say where its hooks\n * are. `note` is what there is to say of how a hook that runs the harness\n * runs it, or `null`.\n */\nexport async function gitHook(root: string): Promise<{ readonly hook: GitHookState; readonly path: string | null; readonly note: string | null }> {\n const path = await hookPath('pre-commit', root);\n if (path === null) return { hook: { state: 'unknown', reason: 'git does not say where they are; spec-harness needs git 2.31 or later' }, path, note: null };\n const file = shown(root, path);\n if (!existsSync(path)) return { hook: { state: 'absent', file }, path, note: null };\n const text = await readFile(path, 'utf8');\n if (!text.includes('spec-harness')) return { hook: { state: 'other', file }, path, note: null };\n // git skips a hook it cannot execute, and says so only in a hint; Windows\n // has no executable bit, and runs it.\n // The Windows job holds the platform check; the sweep runs on Linux, where it is always true.\n const inert = process.platform !== 'win32' && (statSync(path).mode & 0o111) === 0;\n return { hook: { state: inert ? 'inert' : 'runs', file }, path, note: gitHookNote(text) };\n}\n\nexport async function plan(workspace: Workspace, options: Options, io: CliIO): Promise<Step[]> {\n const { root } = workspace;\n const steps: Step[] = [];\n\n // The base rounds merge into, decided once: spec-harness verifies rulings\n // against the allowed signers on it, and spec-brief's archive measures the\n // round from it. A base a person wrote, in either file, is kept.\n // openWorkspace loaded this file and refused one that is not a JSON object,\n // so here it is one, or there is none.\n const harnessFile = join(root, CONFIG_FILE);\n const harness = (await readJson(harnessFile)) as Json | null;\n const namedBase = harness !== null && typeof harness['base'] === 'string' ? harness['base'] : null;\n const choice = namedBase === null ? chooseBase({ remoteDefault: await remoteDefault(root), branch: workspace.branch, branches: await localBranches(root) }) : null;\n const base = choice === null ? namedBase : choice.base;\n\n // spec-brief first: every other setting is derived from its directories.\n const briefConfig = SPEC_BRIEF_CONFIGS.find((name) => existsSync(join(root, name)));\n const brief = workspace.siblings.locate('spec-brief');\n let briefs = 'briefs';\n let archive = 'briefs/archive';\n if (briefConfig === undefined) {\n if (brief.kind === 'found') {\n const command = brief.command;\n const created = join(root, '.spec-brief.json');\n steps.push({\n file: '.spec-brief.json',\n action: 'run',\n detail: 'spec-brief init: the brief and archive directories, every default spelled out',\n apply: async () => {\n const run = await runSibling(command, ['init'], root);\n if (run.code !== 0) throw new Error(`spec-brief init failed: ${run.stderr.trim()}`);\n },\n });\n steps.push({\n file: '.spec-brief.json',\n action: 'update',\n detail: specBriefDetail(true, base),\n apply: async () => {\n const written = await readJson(created);\n if (written === null || written === 'unreadable') throw new Error('spec-brief init wrote no configuration JSON can read');\n const merged = mergeSpecBrief(written, base, pluginFile(root));\n if (merged !== null) await writeAtomic(created, stringify(merged));\n },\n });\n } else {\n steps.push({ file: '.spec-brief.json', action: 'advise', detail: brief.reason });\n }\n } else {\n const file = join(root, briefConfig);\n const config = await readJson(file);\n // The file was found a moment ago, so null is one removed in between; and\n // a string has neither key, so these checks are there for the type.\n if (config !== null && config !== 'unreadable') {\n if (typeof config['briefs'] === 'string') briefs = config['briefs'];\n archive = typeof config['archive'] === 'string' ? config['archive'] : `${briefs}/archive`;\n }\n steps.push({ file: briefConfig, action: 'keep', detail: `briefs in ${briefs}/, the archive in ${archive}/` });\n // spec-brief's archive learns that a signed ruling allows a protected file\n // only from this package's plugin (ADR-0006).\n if (config === 'unreadable' || config === null) {\n const measure = base === null ? '' : `, and \"archiving\": { \"base\": \"${base}\" }`;\n steps.push({ file: briefConfig, action: 'advise', detail: `cannot be read as JSON; add \"plugins\": [\"${PLUGIN}\"]${measure} by hand` });\n } else {\n const isPluginFile = pluginFile(root);\n const merged = mergeSpecBrief(config, base, isPluginFile);\n const measured = measuresArchive(config);\n if (merged === null) {\n const archiving = config['archiving'];\n const from = measured ? `, and the archive is measured from ${String((archiving as Json)['base'])}` : '';\n steps.push({ file: briefConfig, action: 'keep', detail: `spec-harness's plugin is loaded${from}` });\n } else {\n const detail = specBriefDetail(!loadsPlugin(config, isPluginFile), measured ? null : base);\n steps.push({ file: briefConfig, action: 'update', detail, apply: () => writeAtomic(file, stringify(merged)) });\n }\n }\n }\n\n // spec-graph: an archived brief is a record, not a retired decision. Its\n // configuration is the first of its files, then a key in package.json; a\n // file written beside that key would shadow it.\n const graph = workspace.siblings.locate('spec-graph');\n if (graph.kind === 'outdated') steps.push({ file: '.spec-graph.json', action: 'advise', detail: graph.reason });\n if (graph.kind === 'found') {\n const archiveGlob = `${archive}/**`;\n const graphName = SPEC_GRAPH_CONFIGS.find((name) => existsSync(join(root, name)));\n const manifest = graphName === undefined ? await readJson(join(root, 'package.json')) : null;\n if (manifest !== null && manifest !== 'unreadable' && 'spec-graph' in manifest) {\n steps.push({ file: 'package.json', action: 'advise', detail: `spec-graph reads its configuration from the \"spec-graph\" key here, which init does not edit: add \"${archiveGlob}\" to its \"historyPatterns\" by hand` });\n } else {\n const file = graphName ?? '.spec-graph.json';\n const current = await readJson(join(root, file));\n if (current === 'unreadable') {\n steps.push({ file, action: 'advise', detail: `cannot be read as JSON; add \"historyPatterns\": [\"${archiveGlob}\"] by hand` });\n } else {\n const merged = mergeSpecGraph(current ?? {}, archiveGlob);\n const detail = describeGraph(archiveGlob, briefs, graphReadsBriefs(current ?? {}, briefs), merged !== null);\n if (merged === null) steps.push({ file, action: 'keep', detail });\n else steps.push({ file, action: current === null ? 'create' : 'update', detail, apply: () => writeAtomic(join(root, file), stringify(merged)) });\n }\n }\n }\n\n // spec-harness's own file, naming the base the rounds merge into: every\n // ruling is verified against the allowed signers on it. No choice was made\n // only where the file names a base.\n if (choice === null) {\n steps.push({ file: CONFIG_FILE, action: 'keep', detail: `rounds are measured from ${namedBase}` });\n } else if (choice.base === null) {\n steps.push({ file: CONFIG_FILE, action: 'advise', detail: choice.detail });\n } else {\n const content: Json = { ...harness, base: choice.base };\n steps.push({ file: CONFIG_FILE, action: harness === null ? 'create' : 'update', detail: choice.detail, apply: () => writeAtomic(harnessFile, stringify(content)) });\n }\n\n // The agent's hooks and server, unless the plugin brings them: Claude Code\n // runs a plugin's hook beside the same hook in settings, so both would\n // guard every write twice (ADR-0012). The hook's lines are known as doctor\n // knows them, by the hook's own reading and by the writes Claude Code\n // starts each for, so that the two say the same of every file (ADR-0005).\n const wiring = await claudeCodeWiring(root, io);\n const { plugin } = wiring;\n const settingsFile = join(root, '.claude', 'settings.json');\n const settings = await readJson(settingsFile);\n const lines = wiring.lines.filter((line) => line.file === SETTINGS);\n // What doctor says of a line this file holds is said here too, with why\n // init leaves the line: one a person changed is theirs, and a second line\n // beside one that is started would run for every write as well.\n const said = lines.flatMap((line) => [line.detail, line.cover]).filter((note): note is string => note !== null);\n const left = said.length === 0 ? '' : `${said.join('; ')}; init rewrites no line of the hook it did not write, and writes none beside one Claude Code starts for a write`;\n if (plugin !== null) {\n // Doubled in this file or in one of the person's own, it is the double\n // install doctor fails: said here too, though init writes neither file.\n // This file's own entry is said first, so the others are named only\n // where it holds none.\n const present = wiring.hooks.includes(SETTINGS);\n steps.push({ file: SETTINGS, action: wiring.hooks.length > 0 ? 'advise' : 'skip', detail: describeSkipped(plugin, 'hooks', present, wiring.hooks) });\n } else if (settings === 'unreadable') {\n steps.push({ file: SETTINGS, action: 'advise', detail: `cannot be read as JSON; add the guard as a PreToolUse and a PostToolUse hook by hand: ${JSON.stringify(GUARD_HOOK)}` });\n } else {\n // An entry is a line of the hook where doctor reports its words as one in this file.\n const reported = new Set(lines.map((line) => JSON.stringify(line.line)));\n const merged = mergeClaudeSettings(settings ?? {}, (words) => reported.has(JSON.stringify(words)));\n // A line that turns the guard off or holds every write is one doctor\n // fails, and lines of which none is waited on before a write are no\n // guard between them: left as they are, they are for the person to mend.\n const broken = lines.some((line) => line.state === 'off' || line.state === 'held') || !lines.some((line) => isStarted(line.before));\n if (merged === null) steps.push({ file: SETTINGS, action: broken ? 'advise' : 'keep', detail: left === '' ? 'the guard hooks are installed' : left });\n else\n steps.push({\n file: SETTINGS,\n action: settings === null ? 'create' : 'update',\n detail: `the guard hooks, run with node from the project's install: a PreToolUse hook refuses writes to protected files; a PostToolUse hook warns about writes outside the scope${left === '' ? '' : `; ${left}`}`,\n apply: () => writeAtomic(settingsFile, stringify(merged)),\n });\n }\n const mcpFile = join(root, '.mcp.json');\n const mcp = await readJson(mcpFile);\n if (plugin !== null) {\n const present = registersServer(mcp);\n steps.push({ file: '.mcp.json', action: present ? 'advise' : 'skip', detail: describeSkipped(plugin, 'server', present) });\n } else if (mcp === 'unreadable') {\n steps.push({ file: '.mcp.json', action: 'advise', detail: 'cannot be read as JSON; add the spec-harness server by hand' });\n } else {\n const merged = mergeMcp(mcp ?? {});\n if (merged === null) steps.push({ file: '.mcp.json', action: 'keep', detail: 'the spec-harness server is registered' });\n else\n steps.push({\n file: '.mcp.json',\n action: mcp === null ? 'create' : 'update',\n detail: 'the spec-harness MCP server, run with node from the project\\'s install: start_round, check_path, request_escalation, audit_round, list_rounds',\n apply: () => writeAtomic(mcpFile, stringify(merged)),\n });\n }\n\n // git's hook, where git runs it from: core.hooksPath as git reads it, and\n // a linked worktree's shared hooks. It is the guard for a write the agent's\n // hooks never see, one through a shell, so it is advised when not asked\n // for; it is configuration outside the tree, so it is written only then.\n const { hook, path: hookFile, note: hookNote } = await gitHook(root);\n // Only a git older than 2.31, which the README states as the harness's\n // floor, cannot name its hooks, and init runs only in a work tree, where\n // any later git names them: no test reaches this advice, by that decision.\n if (hook.state === 'unknown' || hookFile === null) {\n steps.push({ file: 'pre-commit', action: 'advise', detail: describeGitHook(hook) });\n } else if (hook.state === 'runs') {\n // A hook that runs it through npx is kept, as any hook that exists is,\n // with the line that runs it as init writes the hook now.\n steps.push({ file: hook.file, action: 'keep', detail: hookNote === null ? 'already runs spec-harness' : `already runs spec-harness; ${hookNote}` });\n } else if (hook.state === 'inert') {\n steps.push({ file: hook.file, action: 'advise', detail: describeGitHook(hook) });\n } else if (hook.state === 'other') {\n steps.push({ file: hook.file, action: 'advise', detail: `a pre-commit hook exists; add the line \"${GIT_HOOK_LINE}\" to it` });\n } else if (options.gitHook) {\n steps.push({\n file: hook.file,\n action: 'create',\n detail: 'refuse a commit that changes what the active brief protects',\n apply: async () => {\n await mkdir(dirname(hookFile), { recursive: true });\n await writeAtomic(hookFile, PRE_COMMIT);\n await chmod(hookFile, 0o755);\n },\n });\n } else {\n steps.push({ file: hook.file, action: 'advise', detail: missingGitHook(null) });\n }\n\n // The file the configuration names, which a ruling's signature is checked\n // against on the base branch (ADR-0006).\n steps.push({\n file: workspace.config.rulings.allowedSigners,\n action: 'advise',\n detail: `rulings count only when signed by a key listed here on the base branch: one line per person, <email> namespaces=\"git\" <public key>. Where an agent runs as the person, make it a FIDO2 key, ssh-keygen -t ed25519-sk, whose signature needs a touch no process can supply. ${PROTECT_RULE_FILES}`,\n });\n return steps;\n}\n\nexport async function initCommand(options: Options, io: CliIO): Promise<number> {\n const workspace = await openWorkspace(options, io);\n const steps = await plan(workspace, options, io);\n if (options.write) {\n for (const step of steps) {\n if (step.apply !== undefined) {\n try {\n await step.apply();\n } catch (error) {\n io.stderr.write(`spec-harness: ${step.file}: ${(error as Error).message}\\n`);\n return EXIT_ERROR;\n }\n }\n }\n }\n if (options.format === 'json') {\n io.stdout.write(json('init', { written: options.write, steps: steps.map(({ file, action, detail }) => ({ file, action, detail })) }));\n } else {\n for (const step of steps) io.stdout.write(`${step.action.padEnd(6)} ${step.file}\\n ${step.detail}\\n`);\n if (!options.write && steps.some((step) => step.apply !== undefined)) io.stdout.write('\\nNothing was changed. Run again with --write to apply the plan.\\n');\n }\n return EXIT_OK;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"setup.js","sourceRoot":"","sources":["../src/setup.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `spec-harness init`: the family configured to agree, from the first day.\n *\n * The integration faults found when the tools were first run together were all\n * configuration: spec-graph read an archived brief as a retired decision and\n * reported every brief that depended on one; spec-guard executed an archived\n * brief's assertions. Each tool can be told the truth; `init` tells each of\n * them the same truth, derived from spec-brief's own settings.\n *\n * It prints a plan and changes nothing until `--write`. It merges into files\n * that exist and never replaces what a person wrote; a git hook the\n * repository already has is left alone, with the line to add printed instead.\n */\n\nimport { accessSync, constants, existsSync, statSync, type Stats } from 'node:fs';\nimport { chmod, mkdir, readFile } from 'node:fs/promises';\nimport { dirname, isAbsolute, join, relative, sep } from 'node:path';\n\nimport { CONFIG_FILE } from './config.js';\nimport {\n arrayMatcher,\n chooseBase,\n commasUnder,\n describeAbsent,\n describeArrayMatcher,\n describeGitHook,\n describeGraph,\n describeHooksOff,\n describePresence,\n describeSkipped,\n GIT_HOOK_LINE,\n gitHookNote,\n type GitHookState,\n graphReadsBriefs,\n GUARD_HOOK,\n guardSwitchedOff,\n isStarted,\n loadsPlugin,\n measuresArchive,\n mergeClaudeSettings,\n mergeMcp,\n mergeSpecBrief,\n mergeSpecGraph,\n missingGitHook,\n ONE_STRING,\n patternReader,\n PLUGIN,\n PRE_COMMIT,\n PROJECT_SETTINGS,\n PROTECT_RULE_FILES,\n registersServer,\n SPEC_BRIEF_CONFIGS,\n SPEC_GRAPH_CONFIGS,\n} from './configure.js';\nimport { readJsonObject as readJson, writeAtomic } from './fs.js';\nimport { hookPath, localBranches, remoteDefault } from './git.js';\nimport { claudeCodeWiring, pluginFile } from './round.js';\nimport { runSibling } from './siblings.js';\nimport { answerOf, EXIT_ERROR, EXIT_OK, json, openWorkspace, type CliIO, type Options, type Workspace } from './workspace.js';\n\n/** `skip`: a file init would write, left alone because Claude Code's plugin brings what init would add. */\nexport type Action = 'create' | 'update' | 'keep' | 'skip' | 'run' | 'advise';\n\nexport interface Step {\n readonly file: string;\n readonly action: Action;\n readonly detail: string;\n apply?: () => Promise<void>;\n}\n\ntype Json = Record<string, unknown>;\n\n/** The project's settings file for Claude Code, as a person finds it and as doctor names it. */\nconst SETTINGS = PROJECT_SETTINGS;\n\nfunction stringify(value: unknown): string {\n return `${JSON.stringify(value, null, 2)}\\n`;\n}\n\nconst PLUGIN_DETAIL = `load spec-harness's plugin, \"${PLUGIN}\": spec-brief's archive asks it whether a signed ruling allows a protected file, and refuses the file without it`;\n\n/** What init adds to spec-brief's configuration, said once for the plan. */\nfunction specBriefDetail(plugin: boolean, base: string | null): string {\n const parts = plugin ? [PLUGIN_DETAIL] : [];\n if (base !== null) parts.push(`measure the archive from ${base}, as spec-harness does: without a base, spec-brief's archive warns that the scope went unmeasured and checks no protected file`);\n return parts.join('; ');\n}\n\n/** A path as a person finds it: from the root when it is inside it, POSIX-separated, and as it is otherwise. */\nfunction shown(root: string, path: string): string {\n const inside = relative(root, path);\n return inside.startsWith('..') || isAbsolute(inside) ? path : inside.split(sep).join('/');\n}\n\n/**\n * Whether git takes what stands at a hook's path for a hook to run. git\n * asks the system whether the path can be executed, and skips one that\n * cannot, with no more than a hint: asked here the same way. On Windows the\n * question is only whether the path is there (Node's fs page, File access\n * constants, read 2026-10-14), and git there runs whatever is.\n */\nfunction executable(path: string): boolean {\n let may = true;\n try {\n accessSync(path, constants.X_OK);\n } catch {\n may = false;\n }\n return may;\n}\n\n/** What stands at a path, links followed, or `null` where nothing does: a link to nothing is nothing, to git as well. */\nfunction standing(path: string): Stats | null {\n try {\n return statSync(path);\n } catch {\n return null;\n }\n}\n\n/**\n * git's pre-commit hook, where git runs it from, and what is there: init and\n * doctor both read it. `path` is `null` when git cannot say where its hooks\n * are. `note` is what there is to say of how a hook that runs the harness\n * runs it, or `null`.\n *\n * Only a file is read. A directory under the hook's name, which a\n * repository whose `core.hooksPath` is in its work tree can hold, is not a\n * hook and is one to git all the same: it tries to run what it can execute\n * there, fails, and stops the commit (ADR-0005 has what was measured). Read\n * as a file, it ended this on Node's EISDIR and a stack, and a named pipe\n * would have kept it from ever coming back.\n */\nexport async function gitHook(root: string): Promise<{ readonly hook: GitHookState; readonly path: string | null; readonly note: string | null }> {\n const path = await hookPath('pre-commit', root);\n if (path === null) return { hook: { state: 'unknown', reason: 'git does not say where they are; spec-harness needs git 2.31 or later' }, path, note: null };\n const file = shown(root, path);\n const there = standing(path);\n if (there === null) return { hook: { state: 'absent', file }, path, note: null };\n const runs = executable(path);\n if (!there.isFile()) {\n const what = there.isDirectory() ? 'a directory' : 'neither a file nor a directory';\n return { hook: { state: runs ? 'stops' : 'ignored', file, what }, path, note: null };\n }\n const text = await readFile(path, 'utf8');\n if (!text.includes('spec-harness')) return { hook: { state: 'other', file }, path, note: null };\n return { hook: { state: runs ? 'runs' : 'inert', file }, path, note: gitHookNote(text) };\n}\n\nexport async function plan(workspace: Workspace, options: Options, io: CliIO): Promise<Step[]> {\n const { root } = workspace;\n const steps: Step[] = [];\n\n // The base rounds merge into, decided once: spec-harness verifies rulings\n // against the allowed signers on it, and spec-brief's archive measures the\n // round from it. A base a person wrote, in either file, is kept.\n // openWorkspace loaded this file and refused one that is not a JSON object,\n // so here it is one, or there is none.\n const harnessFile = join(root, CONFIG_FILE);\n const harness = (await readJson(harnessFile)) as Json | null;\n const namedBase = harness !== null && typeof harness['base'] === 'string' ? harness['base'] : null;\n // What the plan asks of git and of Claude Code's settings waits on the\n // root and on nothing of the rest, so it is asked at once: three of git's\n // processes side by side where each waited on the one before. Each is\n // read below where it was asked before, so a plan that fails still fails\n // on the first of them in that order. The matchers in the settings are\n // read with one reader, for the wiring and for what to write. Claude\n // Code's release is asked only where what is read of a line turns on it.\n const pattern = patternReader();\n const [factsAsked, wiringAsked, hookAsked] = await Promise.allSettled([\n namedBase === null ? Promise.all([remoteDefault(root), localBranches(root)]) : null,\n claudeCodeWiring(root, io, pattern, false),\n gitHook(root),\n ]);\n const facts = answerOf(factsAsked);\n const choice = facts === null ? null : chooseBase({ remoteDefault: facts[0], branch: workspace.branch, branches: facts[1] });\n const base = choice === null ? namedBase : choice.base;\n\n // spec-brief first: every other setting is derived from its directories.\n const briefConfig = SPEC_BRIEF_CONFIGS.find((name) => existsSync(join(root, name)));\n const brief = workspace.siblings.locate('spec-brief');\n let briefs = 'briefs';\n let archive = 'briefs/archive';\n if (briefConfig === undefined) {\n if (brief.kind === 'found') {\n const command = brief.command;\n const created = join(root, '.spec-brief.json');\n steps.push({\n file: '.spec-brief.json',\n action: 'run',\n detail: 'spec-brief init: the brief and archive directories, every default spelled out',\n apply: async () => {\n const run = await runSibling(command, ['init'], root);\n if (run.code !== 0) throw new Error(`spec-brief init failed: ${run.stderr.trim()}`);\n },\n });\n steps.push({\n file: '.spec-brief.json',\n action: 'update',\n detail: specBriefDetail(true, base),\n apply: async () => {\n const written = await readJson(created);\n if (written === null || written === 'unreadable') throw new Error('spec-brief init wrote no configuration JSON can read');\n const merged = mergeSpecBrief(written, base, pluginFile(root));\n if (merged !== null) await writeAtomic(created, stringify(merged));\n },\n });\n } else {\n steps.push({ file: '.spec-brief.json', action: 'advise', detail: brief.reason });\n }\n } else {\n const file = join(root, briefConfig);\n const config = await readJson(file);\n // The file was found a moment ago, so null is one removed in between; and\n // a string has neither key, so these checks are there for the type.\n if (config !== null && config !== 'unreadable') {\n if (typeof config['briefs'] === 'string') briefs = config['briefs'];\n archive = typeof config['archive'] === 'string' ? config['archive'] : `${briefs}/archive`;\n }\n steps.push({ file: briefConfig, action: 'keep', detail: `briefs in ${briefs}/, the archive in ${archive}/` });\n // spec-brief's archive learns that a signed ruling allows a protected file\n // only from this package's plugin (ADR-0006).\n if (config === 'unreadable' || config === null) {\n const measure = base === null ? '' : `, and \"archiving\": { \"base\": \"${base}\" }`;\n steps.push({ file: briefConfig, action: 'advise', detail: `cannot be read as JSON; add \"plugins\": [\"${PLUGIN}\"]${measure} by hand` });\n } else {\n const isPluginFile = pluginFile(root);\n const merged = mergeSpecBrief(config, base, isPluginFile);\n const measured = measuresArchive(config);\n if (merged === null) {\n const archiving = config['archiving'];\n const from = measured ? `, and the archive is measured from ${String((archiving as Json)['base'])}` : '';\n steps.push({ file: briefConfig, action: 'keep', detail: `spec-harness's plugin is loaded${from}` });\n } else {\n const detail = specBriefDetail(!loadsPlugin(config, isPluginFile), measured ? null : base);\n steps.push({ file: briefConfig, action: 'update', detail, apply: () => writeAtomic(file, stringify(merged)) });\n }\n }\n }\n\n // spec-graph: an archived brief is a record, not a retired decision. Its\n // configuration is the first of its files, then a key in package.json; a\n // file written beside that key would shadow it.\n const graph = workspace.siblings.locate('spec-graph');\n if (graph.kind === 'outdated') steps.push({ file: '.spec-graph.json', action: 'advise', detail: graph.reason });\n if (graph.kind === 'found') {\n const archiveGlob = `${archive}/**`;\n const graphName = SPEC_GRAPH_CONFIGS.find((name) => existsSync(join(root, name)));\n const manifest = graphName === undefined ? await readJson(join(root, 'package.json')) : null;\n if (manifest !== null && manifest !== 'unreadable' && 'spec-graph' in manifest) {\n steps.push({ file: 'package.json', action: 'advise', detail: `spec-graph reads its configuration from the \"spec-graph\" key here, which init does not edit: add \"${archiveGlob}\" to its \"historyPatterns\" by hand` });\n } else {\n const file = graphName ?? '.spec-graph.json';\n const current = await readJson(join(root, file));\n if (current === 'unreadable') {\n steps.push({ file, action: 'advise', detail: `cannot be read as JSON; add \"historyPatterns\": [\"${archiveGlob}\"] by hand` });\n } else {\n const merged = mergeSpecGraph(current ?? {}, archiveGlob);\n const detail = describeGraph(archiveGlob, briefs, graphReadsBriefs(current ?? {}, briefs), merged !== null);\n if (merged === null) steps.push({ file, action: 'keep', detail });\n else steps.push({ file, action: current === null ? 'create' : 'update', detail, apply: () => writeAtomic(join(root, file), stringify(merged)) });\n }\n }\n }\n\n // spec-harness's own file, naming the base the rounds merge into: every\n // ruling is verified against the allowed signers on it. No choice was made\n // only where the file names a base.\n if (choice === null) {\n steps.push({ file: CONFIG_FILE, action: 'keep', detail: `rounds are measured from ${namedBase}` });\n } else if (choice.base === null) {\n steps.push({ file: CONFIG_FILE, action: 'advise', detail: choice.detail });\n } else {\n const content: Json = { ...harness, base: choice.base };\n steps.push({ file: CONFIG_FILE, action: harness === null ? 'create' : 'update', detail: choice.detail, apply: () => writeAtomic(harnessFile, stringify(content)) });\n }\n\n // The agent's hooks and server, unless the plugin brings them: Claude Code\n // runs a plugin's hook beside the same hook in settings, so both would\n // guard every write twice (ADR-0012). The hook's lines are known as doctor\n // knows them, by the hook's own reading and by the writes Claude Code\n // starts each for, so that the two say the same of every file (ADR-0005).\n const wiring = answerOf(wiringAsked);\n const { plugin } = wiring;\n // A plugin that is not on this machine brings neither. init writes none\n // on that reading alone, which a plugin that is there after all would\n // make a second guard: it says what doctor says, and how to have the\n // entries here instead.\n const absent = wiring.presence?.state === 'absent' ? wiring.presence.detail : null;\n const settingsFile = join(root, '.claude', 'settings.json');\n const settings = await readJson(settingsFile);\n // An array for a matcher, under which Claude Code loads no hook of the file.\n const array = arrayMatcher(settings);\n const lines = wiring.lines.filter((line) => line.file === SETTINGS);\n // What doctor says of a line this file holds is said here too, with why\n // init leaves the line: one a person changed is theirs, and a second line\n // beside one that is started would run for every write as well.\n const said = lines.flatMap((line) => [line.detail, line.cover]).filter((note): note is string => note !== null);\n const left = said.length === 0 ? '' : `${said.join('; ')}; init rewrites no line of the hook it did not write, and writes none beside one Claude Code starts for a write`;\n if (plugin !== null) {\n // Doubled in this file or in one of the person's own, it is the double\n // install doctor fails: said here too, though init writes neither file.\n // This file's own entry is said first, so the others are named only\n // where it holds none.\n const present = wiring.hooks.includes(SETTINGS);\n if (absent !== null) steps.push({ file: SETTINGS, action: 'advise', detail: describeAbsent(plugin, absent, 'hooks', present) });\n else {\n // A plugin that cannot be told to be on this machine is no reason to\n // write either: said beside the reason, as doctor says it.\n const untold = describePresence(wiring);\n const detail = describeSkipped(plugin, 'hooks', present, wiring.hooks);\n steps.push({ file: SETTINGS, action: wiring.hooks.length > 0 ? 'advise' : 'skip', detail: untold === null ? detail : `${detail}. ${untold}` });\n }\n } else if (settings === 'unreadable') {\n steps.push({ file: SETTINGS, action: 'advise', detail: `cannot be read as JSON; add the guard as a PreToolUse and a PostToolUse hook by hand: ${JSON.stringify(GUARD_HOOK)}` });\n } else if (array !== null) {\n // A hook written into such a file would be one more that is not loaded:\n // the matcher is the person's to mend, and init is run again after.\n steps.push({ file: SETTINGS, action: 'advise', detail: `${describeArrayMatcher(SETTINGS, array)}, those init writes among them, so it writes none: ${ONE_STRING}, and run init again` });\n } else {\n // An entry is a line of the hook where doctor reports its words as one in this file.\n const reported = new Set(lines.map((line) => JSON.stringify(line.line)));\n const merged = mergeClaudeSettings(settings ?? {}, (words) => reported.has(JSON.stringify(words)), pattern, commasUnder(wiring.release));\n // A line that turns the guard off or holds every write is one doctor\n // fails, and lines of which none is waited on before a write are no\n // guard between them: left as they are, they are for the person to mend.\n const broken = lines.some((line) => line.state === 'off' || line.state === 'held') || !lines.some((line) => isStarted(line.before));\n if (merged === null) steps.push({ file: SETTINGS, action: broken ? 'advise' : 'keep', detail: left === '' ? 'the guard hooks are installed' : left });\n else\n steps.push({\n file: SETTINGS,\n action: settings === null ? 'create' : 'update',\n detail: `the guard hooks, run with node from the project's install: a PreToolUse hook refuses writes to protected files; a PostToolUse hook warns about writes outside the scope${left === '' ? '' : `; ${left}`}`,\n apply: () => writeAtomic(settingsFile, stringify(merged)),\n });\n }\n // disableAllHooks turns off whatever hooks there are, and those init\n // writes with them: said of the file that holds the key, which is the\n // person's to change. init writes what it would have written.\n if (wiring.hooksOff !== null) steps.push({ file: wiring.hooksOff, action: 'advise', detail: describeHooksOff(wiring.hooksOff, guardSwitchedOff(wiring)) });\n const mcpFile = join(root, '.mcp.json');\n const mcp = await readJson(mcpFile);\n if (plugin !== null) {\n const present = registersServer(mcp);\n if (absent !== null) steps.push({ file: '.mcp.json', action: 'advise', detail: describeAbsent(plugin, absent, 'server', present) });\n else steps.push({ file: '.mcp.json', action: present ? 'advise' : 'skip', detail: describeSkipped(plugin, 'server', present) });\n } else if (mcp === 'unreadable') {\n steps.push({ file: '.mcp.json', action: 'advise', detail: 'cannot be read as JSON; add the spec-harness server by hand' });\n } else {\n const merged = mergeMcp(mcp ?? {});\n if (merged === null) steps.push({ file: '.mcp.json', action: 'keep', detail: 'the spec-harness server is registered' });\n else\n steps.push({\n file: '.mcp.json',\n action: mcp === null ? 'create' : 'update',\n detail: 'the spec-harness MCP server, run with node from the project\\'s install: start_round, check_path, request_escalation, audit_round, list_rounds',\n apply: () => writeAtomic(mcpFile, stringify(merged)),\n });\n }\n\n // git's hook, where git runs it from: core.hooksPath as git reads it, and\n // a linked worktree's shared hooks. It is the guard for a write the agent's\n // hooks never see, one through a shell, so it is advised when not asked\n // for; it is configuration outside the tree, so it is written only then.\n const { hook, path: hookFile, note: hookNote } = answerOf(hookAsked);\n // Only a git older than 2.31, which the README states as the harness's\n // floor, cannot name its hooks, and init runs only in a work tree, where\n // any later git names them: no test reaches this advice, by that decision.\n if (hook.state === 'unknown' || hookFile === null) {\n steps.push({ file: 'pre-commit', action: 'advise', detail: describeGitHook(hook) });\n } else if (hook.state === 'runs') {\n // A hook that runs it through npx is kept, as any hook that exists is,\n // with the line that runs it as init writes the hook now.\n steps.push({ file: hook.file, action: 'keep', detail: hookNote === null ? 'already runs spec-harness' : `already runs spec-harness; ${hookNote}` });\n } else if (hook.state === 'inert' || hook.state === 'stops' || hook.state === 'ignored') {\n // What is there is the person's to mend or to take away: nothing is\n // written over it, and what doctor says of it is said.\n steps.push({ file: hook.file, action: 'advise', detail: describeGitHook(hook) });\n } else if (hook.state === 'other') {\n steps.push({ file: hook.file, action: 'advise', detail: `a pre-commit hook exists; add the line \"${GIT_HOOK_LINE}\" to it` });\n } else if (options.gitHook) {\n steps.push({\n file: hook.file,\n action: 'create',\n detail: 'refuse a commit that changes what the active brief protects',\n apply: async () => {\n await mkdir(dirname(hookFile), { recursive: true });\n await writeAtomic(hookFile, PRE_COMMIT);\n await chmod(hookFile, 0o755);\n },\n });\n } else {\n steps.push({ file: hook.file, action: 'advise', detail: missingGitHook(null) });\n }\n\n // The file the configuration names, which a ruling's signature is checked\n // against on the base branch (ADR-0006).\n steps.push({\n file: workspace.config.rulings.allowedSigners,\n action: 'advise',\n detail: `rulings count only when signed by a key listed here on the base branch: one line per person, <email> namespaces=\"git\" <public key>. Where an agent runs as the person, make it a FIDO2 key, ssh-keygen -t ed25519-sk, whose signature needs a touch no process can supply. ${PROTECT_RULE_FILES}`,\n });\n return steps;\n}\n\nexport async function initCommand(options: Options, io: CliIO): Promise<number> {\n const workspace = await openWorkspace(options, io);\n const steps = await plan(workspace, options, io);\n if (options.write) {\n for (const step of steps) {\n if (step.apply !== undefined) {\n try {\n await step.apply();\n } catch (error) {\n io.stderr.write(`spec-harness: ${step.file}: ${(error as Error).message}\\n`);\n return EXIT_ERROR;\n }\n }\n }\n }\n if (options.format === 'json') {\n io.stdout.write(json('init', { written: options.write, steps: steps.map(({ file, action, detail }) => ({ file, action, detail })) }));\n } else {\n for (const step of steps) io.stdout.write(`${step.action.padEnd(6)} ${step.file}\\n ${step.detail}\\n`);\n if (!options.write && steps.some((step) => step.apply !== undefined)) io.stdout.write('\\nNothing was changed. Run again with --write to apply the plan.\\n');\n }\n return EXIT_OK;\n}\n"]}
|
package/dist/usage.d.ts
CHANGED
|
@@ -247,10 +247,41 @@ export interface Guards {
|
|
|
247
247
|
* line names none.
|
|
248
248
|
*/
|
|
249
249
|
readonly brief?: string;
|
|
250
|
+
/**
|
|
251
|
+
* The work tree the root is in, where that is not the project's own and
|
|
252
|
+
* holds it: the hook opens the work tree its root is in, so a write to a
|
|
253
|
+
* file of the project is checked there, by that work tree's briefs, under
|
|
254
|
+
* the path the file has in it. Only git can tell, so the caller asks it.
|
|
255
|
+
* Absent where the line gives no root, or one in the project's work tree.
|
|
256
|
+
*/
|
|
257
|
+
readonly holder?: OtherTree & {
|
|
258
|
+
readonly path: string;
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
/** A work tree the root on the hook's line is in that is not the project's own. */
|
|
262
|
+
export interface OtherTree {
|
|
263
|
+
/** Its top, as git names it. */
|
|
264
|
+
readonly top: string;
|
|
265
|
+
/** Whether it is a work tree of the project's own repository, linked or main, where it is otherwise another repository's. */
|
|
266
|
+
readonly ours: boolean;
|
|
250
267
|
}
|
|
268
|
+
/** How such a work tree is named: by its top, and whose it is. */
|
|
269
|
+
export declare function namedTree({ top, ours }: OtherTree): string;
|
|
251
270
|
export interface GuardOff {
|
|
252
271
|
readonly state: 'off';
|
|
253
272
|
readonly fault: LineFault;
|
|
273
|
+
/**
|
|
274
|
+
* Set where the hook says nothing of the fault and exits 0, so that no
|
|
275
|
+
* run of it fails: a root outside any work tree, or in a work tree that
|
|
276
|
+
* does not hold the project. Every other fault it says, with exit 1.
|
|
277
|
+
*/
|
|
278
|
+
readonly silent?: true;
|
|
279
|
+
/**
|
|
280
|
+
* What goes unchecked, where that is not every write: under a root in a
|
|
281
|
+
* work tree that does not hold the project, the hook checks a write to a
|
|
282
|
+
* file of that work tree, and none to a file of the project.
|
|
283
|
+
*/
|
|
284
|
+
readonly unchecked?: string;
|
|
254
285
|
}
|
|
255
286
|
export interface WritesHeld {
|
|
256
287
|
readonly state: 'held';
|
|
@@ -280,6 +311,34 @@ export declare function unreadHookLine(said: string): GuardOff;
|
|
|
280
311
|
* someone had configured was off.
|
|
281
312
|
*/
|
|
282
313
|
export declare function readHookLine(root: string | undefined, cwd: string, isDirectory: (path: string) => boolean): Guards | GuardOff;
|
|
314
|
+
/**
|
|
315
|
+
* A line whose root is a directory outside any git work tree. The hook
|
|
316
|
+
* reads that as a session outside one, where it has nothing to guard: it
|
|
317
|
+
* checks no write and says nothing, exit 0 (ADR-0005), and that stays. Only
|
|
318
|
+
* git can tell such a root, so the caller asks it. A line that carries one
|
|
319
|
+
* is a guard someone set up that checks nothing, and `doctor` says it as
|
|
320
|
+
* off.
|
|
321
|
+
*/
|
|
322
|
+
export declare function outsideHookLine(root: string): GuardOff;
|
|
323
|
+
/**
|
|
324
|
+
* A line whose root is in a git work tree that does not hold the project:
|
|
325
|
+
* another repository beside it, or a work tree inside it, a repository of
|
|
326
|
+
* its own, a submodule or a linked worktree. The hook opens the work tree
|
|
327
|
+
* its root is in and decides by that work tree's briefs. A file of the
|
|
328
|
+
* project is outside it, which the hook reads as a file outside the
|
|
329
|
+
* repository: it says nothing, exit 0, as it has in every release, and that
|
|
330
|
+
* stays. So no write to a file of the project is checked under the line,
|
|
331
|
+
* and `doctor` says it as a guard that is off. Only git can tell such a
|
|
332
|
+
* root, so the caller asks it.
|
|
333
|
+
*/
|
|
334
|
+
export declare function elsewhereHookLine(root: string, tree: OtherTree): GuardOff;
|
|
335
|
+
/**
|
|
336
|
+
* A line that guards under a root in a work tree which holds the project
|
|
337
|
+
* and is not its own: a repository the project is nested in, or the main
|
|
338
|
+
* work tree around a linked one. `path` is the project's top as that work
|
|
339
|
+
* tree names it.
|
|
340
|
+
*/
|
|
341
|
+
export declare function holderHookLine(line: Guards, tree: OtherTree, path: string): Guards;
|
|
283
342
|
/**
|
|
284
343
|
* What Claude Code's hook does with the words it is handed, or `null` for
|
|
285
344
|
* words that are not its line: read by the parser the command line reads
|
package/dist/usage.js
CHANGED
|
@@ -118,6 +118,10 @@ export function isClaudeHook(command, argument) {
|
|
|
118
118
|
export function noDirectory(root) {
|
|
119
119
|
return `--root is "${root}", which is not a directory`;
|
|
120
120
|
}
|
|
121
|
+
/** How such a work tree is named: by its top, and whose it is. */
|
|
122
|
+
export function namedTree({ top, ours }) {
|
|
123
|
+
return `${top}, ${ours ? 'another work tree of this repository' : 'the work tree of another repository'}`;
|
|
124
|
+
}
|
|
121
125
|
/**
|
|
122
126
|
* A line under which the round has no brief to be checked against, since
|
|
123
127
|
* what names one names a brief spec-brief does not know, or one it has
|
|
@@ -151,6 +155,55 @@ export function readHookLine(root, cwd, isDirectory) {
|
|
|
151
155
|
}
|
|
152
156
|
return { state: 'guards', root: given };
|
|
153
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* A line whose root is a directory outside any git work tree. The hook
|
|
160
|
+
* reads that as a session outside one, where it has nothing to guard: it
|
|
161
|
+
* checks no write and says nothing, exit 0 (ADR-0005), and that stays. Only
|
|
162
|
+
* git can tell such a root, so the caller asks it. A line that carries one
|
|
163
|
+
* is a guard someone set up that checks nothing, and `doctor` says it as
|
|
164
|
+
* off.
|
|
165
|
+
*/
|
|
166
|
+
export function outsideHookLine(root) {
|
|
167
|
+
return {
|
|
168
|
+
state: 'off',
|
|
169
|
+
silent: true,
|
|
170
|
+
fault: {
|
|
171
|
+
wrong: `--root is "${root}", a directory outside any git work tree, which the hook reads as a session outside one, exit 0 and no word`,
|
|
172
|
+
mend: "give the hook's line a directory in the work tree, or leave --root out for the project Claude Code names",
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* A line whose root is in a git work tree that does not hold the project:
|
|
178
|
+
* another repository beside it, or a work tree inside it, a repository of
|
|
179
|
+
* its own, a submodule or a linked worktree. The hook opens the work tree
|
|
180
|
+
* its root is in and decides by that work tree's briefs. A file of the
|
|
181
|
+
* project is outside it, which the hook reads as a file outside the
|
|
182
|
+
* repository: it says nothing, exit 0, as it has in every release, and that
|
|
183
|
+
* stays. So no write to a file of the project is checked under the line,
|
|
184
|
+
* and `doctor` says it as a guard that is off. Only git can tell such a
|
|
185
|
+
* root, so the caller asks it.
|
|
186
|
+
*/
|
|
187
|
+
export function elsewhereHookLine(root, tree) {
|
|
188
|
+
return {
|
|
189
|
+
state: 'off',
|
|
190
|
+
silent: true,
|
|
191
|
+
unchecked: 'no write to a file of this project is checked',
|
|
192
|
+
fault: {
|
|
193
|
+
wrong: `--root is "${root}", a directory in ${namedTree(tree)}, which does not hold this project: the hook checks a write to a file of that work tree, by its briefs, and passes any other with no word, exit 0`,
|
|
194
|
+
mend: "give the hook's line a directory in this work tree, or leave --root out for the project Claude Code names",
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* A line that guards under a root in a work tree which holds the project
|
|
200
|
+
* and is not its own: a repository the project is nested in, or the main
|
|
201
|
+
* work tree around a linked one. `path` is the project's top as that work
|
|
202
|
+
* tree names it.
|
|
203
|
+
*/
|
|
204
|
+
export function holderHookLine(line, tree, path) {
|
|
205
|
+
return { ...line, holder: { ...tree, path } };
|
|
206
|
+
}
|
|
154
207
|
/**
|
|
155
208
|
* What Claude Code's hook does with the words it is handed, or `null` for
|
|
156
209
|
* words that are not its line: read by the parser the command line reads
|