@descent-vtt/spec-harness 0.0.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +16 -0
- package/.claude-plugin/plugin.json +17 -0
- package/.mcp.json +12 -0
- package/CHANGELOG.md +46 -0
- package/README.md +127 -9
- package/bin/spec-harness.js +19 -0
- package/dist/audit.d.ts +69 -0
- package/dist/audit.js +93 -0
- package/dist/audit.js.map +1 -0
- package/dist/branch.d.ts +26 -0
- package/dist/branch.js +100 -0
- package/dist/branch.js.map +1 -0
- package/dist/briefs.d.ts +49 -0
- package/dist/briefs.js +111 -0
- package/dist/briefs.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.js +245 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +18 -0
- package/dist/commands.js +224 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +57 -0
- package/dist/config.js +167 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +56 -0
- package/dist/context.js +125 -0
- package/dist/context.js.map +1 -0
- package/dist/document.d.ts +33 -0
- package/dist/document.js +9 -0
- package/dist/document.js.map +1 -0
- package/dist/fs.d.ts +29 -0
- package/dist/fs.js +85 -0
- package/dist/fs.js.map +1 -0
- package/dist/git.d.ts +57 -0
- package/dist/git.js +129 -0
- package/dist/git.js.map +1 -0
- package/dist/guard.d.ts +43 -0
- package/dist/guard.js +120 -0
- package/dist/guard.js.map +1 -0
- package/dist/hooks.d.ts +50 -0
- package/dist/hooks.js +109 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/junit.d.ts +30 -0
- package/dist/junit.js +166 -0
- package/dist/junit.js.map +1 -0
- package/dist/manifests.d.ts +52 -0
- package/dist/manifests.js +346 -0
- package/dist/manifests.js.map +1 -0
- package/dist/plugin.d.ts +45 -0
- package/dist/plugin.js +79 -0
- package/dist/plugin.js.map +1 -0
- package/dist/premises.d.ts +16 -0
- package/dist/premises.js +71 -0
- package/dist/premises.js.map +1 -0
- package/dist/probe.d.ts +108 -0
- package/dist/probe.js +203 -0
- package/dist/probe.js.map +1 -0
- package/dist/reader.d.ts +8 -0
- package/dist/reader.js +105 -0
- package/dist/reader.js.map +1 -0
- package/dist/round.d.ts +68 -0
- package/dist/round.js +339 -0
- package/dist/round.js.map +1 -0
- package/dist/rulings.d.ts +92 -0
- package/dist/rulings.js +160 -0
- package/dist/rulings.js.map +1 -0
- package/dist/sandbox.d.ts +28 -0
- package/dist/sandbox.js +110 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.js +212 -0
- package/dist/server.js.map +1 -0
- package/dist/setup.d.ts +32 -0
- package/dist/setup.js +218 -0
- package/dist/setup.js.map +1 -0
- package/dist/siblings.d.ts +66 -0
- package/dist/siblings.js +134 -0
- package/dist/siblings.js.map +1 -0
- package/dist/types.d.ts +38 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/vendor/spec-core/jsonrpc/index.d.ts +2 -0
- package/dist/vendor/spec-core/jsonrpc/index.js +2 -0
- package/dist/vendor/spec-core/jsonrpc/index.js.map +1 -0
- package/dist/vendor/spec-core/jsonrpc/mcp.d.ts +157 -0
- package/dist/vendor/spec-core/jsonrpc/mcp.js +353 -0
- package/dist/vendor/spec-core/jsonrpc/mcp.js.map +1 -0
- package/dist/vendor/spec-core/markdown/frontmatter.d.ts +103 -0
- package/dist/vendor/spec-core/markdown/frontmatter.js +459 -0
- package/dist/vendor/spec-core/markdown/frontmatter.js.map +1 -0
- package/dist/vendor/spec-core/markdown/headings.d.ts +45 -0
- package/dist/vendor/spec-core/markdown/headings.js +199 -0
- package/dist/vendor/spec-core/markdown/headings.js.map +1 -0
- package/dist/vendor/spec-core/markdown/index.d.ts +5 -0
- package/dist/vendor/spec-core/markdown/index.js +4 -0
- package/dist/vendor/spec-core/markdown/index.js.map +1 -0
- package/dist/vendor/spec-core/markdown/layout.d.ts +39 -0
- package/dist/vendor/spec-core/markdown/layout.js +28 -0
- package/dist/vendor/spec-core/markdown/layout.js.map +1 -0
- package/dist/vendor/spec-core/markdown/links.d.ts +22 -0
- package/dist/vendor/spec-core/markdown/links.js +364 -0
- package/dist/vendor/spec-core/markdown/links.js.map +1 -0
- package/dist/vendor/spec-core/markdown/lists.d.ts +22 -0
- package/dist/vendor/spec-core/markdown/lists.js +127 -0
- package/dist/vendor/spec-core/markdown/lists.js.map +1 -0
- package/dist/vendor/spec-core/markdown/scan.d.ts +36 -0
- package/dist/vendor/spec-core/markdown/scan.js +439 -0
- package/dist/vendor/spec-core/markdown/scan.js.map +1 -0
- package/dist/vendor/spec-core/markdown/syntax.d.ts +75 -0
- package/dist/vendor/spec-core/markdown/syntax.js +181 -0
- package/dist/vendor/spec-core/markdown/syntax.js.map +1 -0
- package/dist/vendor/spec-core/markdown/tables.d.ts +18 -0
- package/dist/vendor/spec-core/markdown/tables.js +86 -0
- package/dist/vendor/spec-core/markdown/tables.js.map +1 -0
- package/dist/vendor/spec-core/markdown/types.d.ts +287 -0
- package/dist/vendor/spec-core/markdown/types.js +9 -0
- package/dist/vendor/spec-core/markdown/types.js.map +1 -0
- package/dist/vendor/spec-core/path/index.d.ts +1 -0
- package/dist/vendor/spec-core/path/index.js +2 -0
- package/dist/vendor/spec-core/path/index.js.map +1 -0
- package/dist/vendor/spec-core/path/posix.d.ts +83 -0
- package/dist/vendor/spec-core/path/posix.js +150 -0
- package/dist/vendor/spec-core/path/posix.js.map +1 -0
- package/dist/vendor/spec-core/pattern/automaton.d.ts +129 -0
- package/dist/vendor/spec-core/pattern/automaton.js +340 -0
- package/dist/vendor/spec-core/pattern/automaton.js.map +1 -0
- package/dist/vendor/spec-core/pattern/charset.d.ts +58 -0
- package/dist/vendor/spec-core/pattern/charset.js +116 -0
- package/dist/vendor/spec-core/pattern/charset.js.map +1 -0
- package/dist/vendor/spec-core/pattern/glob.d.ts +121 -0
- package/dist/vendor/spec-core/pattern/glob.js +481 -0
- package/dist/vendor/spec-core/pattern/glob.js.map +1 -0
- package/dist/vendor/spec-core/pattern/index.d.ts +6 -0
- package/dist/vendor/spec-core/pattern/index.js +4 -0
- package/dist/vendor/spec-core/pattern/index.js.map +1 -0
- package/dist/vendor/spec-core/pattern/regex.d.ts +67 -0
- package/dist/vendor/spec-core/pattern/regex.js +813 -0
- package/dist/vendor/spec-core/pattern/regex.js.map +1 -0
- package/dist/vendor/spec-core/text/index.d.ts +2 -0
- package/dist/vendor/spec-core/text/index.js +2 -0
- package/dist/vendor/spec-core/text/index.js.map +1 -0
- package/dist/vendor/spec-core/text/lines.d.ts +72 -0
- package/dist/vendor/spec-core/text/lines.js +156 -0
- package/dist/vendor/spec-core/text/lines.js.map +1 -0
- package/dist/versions.d.ts +42 -0
- package/dist/versions.js +57 -0
- package/dist/versions.js.map +1 -0
- package/dist/workspace.d.ts +77 -0
- package/dist/workspace.js +138 -0
- package/dist/workspace.js.map +1 -0
- package/hooks/hooks.json +28 -0
- package/package.json +86 -6
- package/skills/close-round/SKILL.md +38 -0
- package/skills/draft-brief/SKILL.md +64 -0
- package/skills/run-round/SKILL.md +46 -0
- package/skills/split-goal/SKILL.md +42 -0
- package/src/vendor/spec-core/LICENSE +21 -0
- package/src/vendor/spec-core/README.md +8 -0
package/dist/setup.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `spec-harness init`: the family configured to agree, from the first day.
|
|
3
|
+
*
|
|
4
|
+
* The integration faults found when the tools were first run together were all
|
|
5
|
+
* configuration: spec-graph read an archived brief as a retired decision and
|
|
6
|
+
* reported every brief that depended on one; spec-guard executed an archived
|
|
7
|
+
* brief's assertions. Each tool can be told the truth; `init` tells each of
|
|
8
|
+
* them the same truth, derived from spec-brief's own settings.
|
|
9
|
+
*
|
|
10
|
+
* It prints a plan and changes nothing until `--write`. It merges into files
|
|
11
|
+
* that exist and never replaces what a person wrote; a git hook the
|
|
12
|
+
* repository already has is left alone, with the line to add printed instead.
|
|
13
|
+
*/
|
|
14
|
+
import { type CliIO, type Options, type Workspace } from './workspace.js';
|
|
15
|
+
export type Action = 'create' | 'update' | 'keep' | 'run' | 'advise';
|
|
16
|
+
export interface Step {
|
|
17
|
+
readonly file: string;
|
|
18
|
+
readonly action: Action;
|
|
19
|
+
readonly detail: string;
|
|
20
|
+
apply?: () => Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
type Json = Record<string, unknown>;
|
|
23
|
+
export declare const HOOK_COMMAND = "npx --no-install spec-harness hook claude";
|
|
24
|
+
/** `.claude/settings.json` with the guard hooks merged in, or `null` when they are already there. */
|
|
25
|
+
export declare function mergeClaudeSettings(current: Json): Json | null;
|
|
26
|
+
/** `.mcp.json` with the harness's server merged in, or `null` when it is already there. */
|
|
27
|
+
export declare function mergeMcp(current: Json): Json | null;
|
|
28
|
+
/** spec-graph's configuration with the archive read as history, or `null` when it already is. */
|
|
29
|
+
export declare function mergeSpecGraph(current: Json, archiveGlob: string): Json | null;
|
|
30
|
+
export declare function plan(workspace: Workspace, options: Options): Promise<Step[]>;
|
|
31
|
+
export declare function initCommand(options: Options, io: CliIO): Promise<number>;
|
|
32
|
+
export {};
|
package/dist/setup.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `spec-harness init`: the family configured to agree, from the first day.
|
|
3
|
+
*
|
|
4
|
+
* The integration faults found when the tools were first run together were all
|
|
5
|
+
* configuration: spec-graph read an archived brief as a retired decision and
|
|
6
|
+
* reported every brief that depended on one; spec-guard executed an archived
|
|
7
|
+
* brief's assertions. Each tool can be told the truth; `init` tells each of
|
|
8
|
+
* them the same truth, derived from spec-brief's own settings.
|
|
9
|
+
*
|
|
10
|
+
* It prints a plan and changes nothing until `--write`. It merges into files
|
|
11
|
+
* that exist and never replaces what a person wrote; a git hook the
|
|
12
|
+
* repository already has is left alone, with the line to add printed instead.
|
|
13
|
+
*/
|
|
14
|
+
import { existsSync } from 'node:fs';
|
|
15
|
+
import { chmod, mkdir, readFile } from 'node:fs/promises';
|
|
16
|
+
import { dirname, join } from 'node:path';
|
|
17
|
+
import { CONFIG_FILE } from './config.js';
|
|
18
|
+
import { writeAtomic } from './fs.js';
|
|
19
|
+
import { git, remoteDefault } from './git.js';
|
|
20
|
+
import { runSibling } from './siblings.js';
|
|
21
|
+
import { EXIT_ERROR, EXIT_OK, json, openWorkspace } from './workspace.js';
|
|
22
|
+
async function readJson(file) {
|
|
23
|
+
if (!existsSync(file))
|
|
24
|
+
return null;
|
|
25
|
+
try {
|
|
26
|
+
const value = JSON.parse(await readFile(file, 'utf8'));
|
|
27
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value) ? value : 'unreadable';
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return 'unreadable';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function stringify(value) {
|
|
34
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
35
|
+
}
|
|
36
|
+
export const HOOK_COMMAND = 'npx --no-install spec-harness hook claude';
|
|
37
|
+
const HOOK_MATCHER = 'Edit|Write|MultiEdit|NotebookEdit';
|
|
38
|
+
/** `.claude/settings.json` with the guard hooks merged in, or `null` when they are already there. */
|
|
39
|
+
export function mergeClaudeSettings(current) {
|
|
40
|
+
const hooks = (typeof current['hooks'] === 'object' && current['hooks'] !== null ? current['hooks'] : {});
|
|
41
|
+
let changed = false;
|
|
42
|
+
const next = { ...hooks };
|
|
43
|
+
for (const event of ['PreToolUse', 'PostToolUse']) {
|
|
44
|
+
const groups = Array.isArray(hooks[event]) ? [...hooks[event]] : [];
|
|
45
|
+
const present = groups.some((group) => Array.isArray(group['hooks']) && group['hooks'].some((hook) => hook['command'] === HOOK_COMMAND));
|
|
46
|
+
if (present)
|
|
47
|
+
continue;
|
|
48
|
+
groups.push({ matcher: HOOK_MATCHER, hooks: [{ type: 'command', command: HOOK_COMMAND, timeout: 60 }] });
|
|
49
|
+
next[event] = groups;
|
|
50
|
+
changed = true;
|
|
51
|
+
}
|
|
52
|
+
return changed ? { ...current, hooks: next } : null;
|
|
53
|
+
}
|
|
54
|
+
/** `.mcp.json` with the harness's server merged in, or `null` when it is already there. */
|
|
55
|
+
export function mergeMcp(current) {
|
|
56
|
+
const servers = (typeof current['mcpServers'] === 'object' && current['mcpServers'] !== null ? current['mcpServers'] : {});
|
|
57
|
+
if ('spec-harness' in servers)
|
|
58
|
+
return null;
|
|
59
|
+
return { ...current, mcpServers: { ...servers, 'spec-harness': { command: 'npx', args: ['--no-install', 'spec-harness', 'mcp'] } } };
|
|
60
|
+
}
|
|
61
|
+
/** spec-graph's configuration with the archive read as history, or `null` when it already is. */
|
|
62
|
+
export function mergeSpecGraph(current, archiveGlob) {
|
|
63
|
+
const history = Array.isArray(current['historyPatterns']) ? current['historyPatterns'].filter((p) => typeof p === 'string') : [];
|
|
64
|
+
if (history.includes(archiveGlob))
|
|
65
|
+
return null;
|
|
66
|
+
return { ...current, historyPatterns: [...history, archiveGlob] };
|
|
67
|
+
}
|
|
68
|
+
const PRE_COMMIT = `#!/bin/sh
|
|
69
|
+
# spec-harness: refuse a commit that changes what the active brief protects.
|
|
70
|
+
exec npx --no-install spec-harness hook git
|
|
71
|
+
`;
|
|
72
|
+
export async function plan(workspace, options) {
|
|
73
|
+
const { root } = workspace;
|
|
74
|
+
const steps = [];
|
|
75
|
+
// spec-brief first: every other setting is derived from its directories.
|
|
76
|
+
const briefConfigFile = ['.spec-brief.json', 'spec-brief.json'].map((name) => join(root, name)).find((file) => existsSync(file));
|
|
77
|
+
const brief = workspace.siblings.locate('spec-brief');
|
|
78
|
+
let briefs = 'briefs';
|
|
79
|
+
let archive = 'briefs/archive';
|
|
80
|
+
if (briefConfigFile === undefined) {
|
|
81
|
+
if (brief.kind === 'found') {
|
|
82
|
+
const command = brief.command;
|
|
83
|
+
steps.push({
|
|
84
|
+
file: '.spec-brief.json',
|
|
85
|
+
action: 'run',
|
|
86
|
+
detail: 'spec-brief init: the brief and archive directories, every default spelled out',
|
|
87
|
+
apply: async () => {
|
|
88
|
+
const run = await runSibling(command, ['init'], root);
|
|
89
|
+
if (run.code !== 0)
|
|
90
|
+
throw new Error(`spec-brief init failed: ${run.stderr.trim()}`);
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
steps.push({ file: '.spec-brief.json', action: 'advise', detail: brief.reason });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
const config = await readJson(briefConfigFile);
|
|
100
|
+
if (config !== null && config !== 'unreadable') {
|
|
101
|
+
if (typeof config['briefs'] === 'string')
|
|
102
|
+
briefs = config['briefs'];
|
|
103
|
+
archive = typeof config['archive'] === 'string' ? config['archive'] : `${briefs}/archive`;
|
|
104
|
+
}
|
|
105
|
+
steps.push({ file: briefConfigFile.slice(root.length + 1), action: 'keep', detail: `briefs in ${briefs}/, the archive in ${archive}/` });
|
|
106
|
+
}
|
|
107
|
+
// spec-graph: an archived brief is a record, not a retired decision.
|
|
108
|
+
const graphFile = join(root, '.spec-graph.json');
|
|
109
|
+
const graph = workspace.siblings.locate('spec-graph');
|
|
110
|
+
if (graph.kind === 'outdated')
|
|
111
|
+
steps.push({ file: '.spec-graph.json', action: 'advise', detail: graph.reason });
|
|
112
|
+
if (graph.kind === 'found') {
|
|
113
|
+
const current = await readJson(graphFile);
|
|
114
|
+
const archiveGlob = `${archive}/**`;
|
|
115
|
+
if (current === 'unreadable') {
|
|
116
|
+
steps.push({ file: '.spec-graph.json', action: 'advise', detail: 'cannot be read as JSON; add "historyPatterns": ["' + archiveGlob + '"] by hand' });
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
const merged = mergeSpecGraph(current ?? {}, archiveGlob);
|
|
120
|
+
if (merged === null)
|
|
121
|
+
steps.push({ file: '.spec-graph.json', action: 'keep', detail: `${archiveGlob} is already history` });
|
|
122
|
+
else
|
|
123
|
+
steps.push({ file: '.spec-graph.json', action: current === null ? 'create' : 'update', detail: `read ${archiveGlob} as history, so a brief that depends on an archived one is not a stale premise`, apply: () => writeAtomic(graphFile, stringify(merged)) });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// spec-harness's own file, naming the base the rounds merge into.
|
|
127
|
+
const harnessFile = join(root, CONFIG_FILE);
|
|
128
|
+
if (!existsSync(harnessFile)) {
|
|
129
|
+
const base = await remoteDefault(root);
|
|
130
|
+
const content = base === null ? {} : { base };
|
|
131
|
+
steps.push({ file: CONFIG_FILE, action: 'create', detail: base === null ? 'the defaults' : `rounds are measured from ${base}`, apply: () => writeAtomic(harnessFile, stringify(content)) });
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
steps.push({ file: CONFIG_FILE, action: 'keep', detail: 'already configured' });
|
|
135
|
+
}
|
|
136
|
+
// The agent's hook and server.
|
|
137
|
+
const settingsFile = join(root, '.claude', 'settings.json');
|
|
138
|
+
const settings = await readJson(settingsFile);
|
|
139
|
+
if (settings === 'unreadable') {
|
|
140
|
+
steps.push({ file: '.claude/settings.json', action: 'advise', detail: `cannot be read as JSON; add a PreToolUse and a PostToolUse hook running "${HOOK_COMMAND}" by hand` });
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
const merged = mergeClaudeSettings(settings ?? {});
|
|
144
|
+
if (merged === null)
|
|
145
|
+
steps.push({ file: '.claude/settings.json', action: 'keep', detail: 'the guard hooks are installed' });
|
|
146
|
+
else
|
|
147
|
+
steps.push({ file: '.claude/settings.json', action: settings === null ? 'create' : 'update', detail: 'a PreToolUse hook refuses writes to protected files; a PostToolUse hook warns about writes outside the scope', apply: () => writeAtomic(settingsFile, stringify(merged)) });
|
|
148
|
+
}
|
|
149
|
+
const mcpFile = join(root, '.mcp.json');
|
|
150
|
+
const mcp = await readJson(mcpFile);
|
|
151
|
+
if (mcp === 'unreadable') {
|
|
152
|
+
steps.push({ file: '.mcp.json', action: 'advise', detail: 'cannot be read as JSON; add the spec-harness server by hand' });
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
const merged = mergeMcp(mcp ?? {});
|
|
156
|
+
if (merged === null)
|
|
157
|
+
steps.push({ file: '.mcp.json', action: 'keep', detail: 'the spec-harness server is registered' });
|
|
158
|
+
else
|
|
159
|
+
steps.push({ file: '.mcp.json', action: mcp === null ? 'create' : 'update', detail: 'the spec-harness MCP server: start_round, check_path, request_escalation, audit_round, list_rounds', apply: () => writeAtomic(mcpFile, stringify(merged)) });
|
|
160
|
+
}
|
|
161
|
+
// git's hook, only when asked: it is configuration outside the tree.
|
|
162
|
+
if (options.gitHook) {
|
|
163
|
+
const hooksPath = (await git(['config', '--get', 'core.hooksPath'], root)).stdout.trim();
|
|
164
|
+
const hookFile = hooksPath === '' ? join(workspace.commonDir, 'hooks', 'pre-commit') : join(root, hooksPath, 'pre-commit');
|
|
165
|
+
if (existsSync(hookFile)) {
|
|
166
|
+
const text = await readFile(hookFile, 'utf8');
|
|
167
|
+
steps.push(text.includes('spec-harness')
|
|
168
|
+
? { file: hookFile, action: 'keep', detail: 'already runs spec-harness' }
|
|
169
|
+
: { file: hookFile, action: 'advise', detail: 'a pre-commit hook exists; add the line "npx --no-install spec-harness hook git" to it' });
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
steps.push({
|
|
173
|
+
file: hookFile,
|
|
174
|
+
action: 'create',
|
|
175
|
+
detail: 'refuse a commit that changes what the active brief protects',
|
|
176
|
+
apply: async () => {
|
|
177
|
+
await mkdir(dirname(hookFile), { recursive: true });
|
|
178
|
+
await writeAtomic(hookFile, PRE_COMMIT);
|
|
179
|
+
await chmod(hookFile, 0o755);
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
steps.push({
|
|
185
|
+
file: '.github/allowed_signers',
|
|
186
|
+
action: 'advise',
|
|
187
|
+
detail: 'rulings count only when signed by a key listed here on the base branch: one line per person, "<email> namespaces=\\"git\\" <public key>"; protect it, the ADRs and the tool configurations with CODEOWNERS',
|
|
188
|
+
});
|
|
189
|
+
return steps;
|
|
190
|
+
}
|
|
191
|
+
export async function initCommand(options, io) {
|
|
192
|
+
const workspace = await openWorkspace(options, io);
|
|
193
|
+
const steps = await plan(workspace, options);
|
|
194
|
+
if (options.write) {
|
|
195
|
+
for (const step of steps) {
|
|
196
|
+
if (step.apply !== undefined) {
|
|
197
|
+
try {
|
|
198
|
+
await step.apply();
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
io.stderr.write(`spec-harness: ${step.file}: ${error.message}\n`);
|
|
202
|
+
return EXIT_ERROR;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (options.format === 'json') {
|
|
208
|
+
io.stdout.write(json('init', { written: options.write, steps: steps.map(({ file, action, detail }) => ({ file, action, detail })) }));
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
for (const step of steps)
|
|
212
|
+
io.stdout.write(`${step.action.padEnd(6)} ${step.file}\n ${step.detail}\n`);
|
|
213
|
+
if (!options.write && steps.some((step) => step.apply !== undefined))
|
|
214
|
+
io.stdout.write('\nNothing was changed. Run again with --write to apply the plan.\n');
|
|
215
|
+
}
|
|
216
|
+
return EXIT_OK;
|
|
217
|
+
}
|
|
218
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +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 } from 'node:fs';\nimport { chmod, mkdir, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\n\nimport { CONFIG_FILE } from './config.js';\nimport { writeAtomic } from './fs.js';\nimport { git, remoteDefault } from './git.js';\nimport { runSibling } from './siblings.js';\nimport { EXIT_ERROR, EXIT_OK, json, openWorkspace, type CliIO, type Options, type Workspace } from './workspace.js';\n\nexport type Action = 'create' | 'update' | 'keep' | '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\nasync function readJson(file: string): Promise<Json | null | 'unreadable'> {\n if (!existsSync(file)) return null;\n try {\n const value = JSON.parse(await readFile(file, 'utf8')) as unknown;\n return typeof value === 'object' && value !== null && !Array.isArray(value) ? (value as Json) : 'unreadable';\n } catch {\n return 'unreadable';\n }\n}\n\nfunction stringify(value: unknown): string {\n return `${JSON.stringify(value, null, 2)}\\n`;\n}\n\nexport const HOOK_COMMAND = 'npx --no-install spec-harness hook claude';\nconst HOOK_MATCHER = 'Edit|Write|MultiEdit|NotebookEdit';\n\n/** `.claude/settings.json` with the guard hooks merged in, or `null` when they are already there. */\nexport function mergeClaudeSettings(current: Json): Json | null {\n const hooks = (typeof current['hooks'] === 'object' && current['hooks'] !== null ? current['hooks'] : {}) as Json;\n let changed = false;\n const next: Json = { ...hooks };\n for (const event of ['PreToolUse', 'PostToolUse']) {\n const groups = Array.isArray(hooks[event]) ? [...(hooks[event] as Json[])] : [];\n const present = groups.some((group) => Array.isArray(group['hooks']) && (group['hooks'] as Json[]).some((hook) => hook['command'] === HOOK_COMMAND));\n if (present) continue;\n groups.push({ matcher: HOOK_MATCHER, hooks: [{ type: 'command', command: HOOK_COMMAND, timeout: 60 }] });\n next[event] = groups;\n changed = true;\n }\n return changed ? { ...current, hooks: next } : null;\n}\n\n/** `.mcp.json` with the harness's server merged in, or `null` when it is already there. */\nexport function mergeMcp(current: Json): Json | null {\n const servers = (typeof current['mcpServers'] === 'object' && current['mcpServers'] !== null ? current['mcpServers'] : {}) as Json;\n if ('spec-harness' in servers) return null;\n return { ...current, mcpServers: { ...servers, 'spec-harness': { command: 'npx', args: ['--no-install', 'spec-harness', 'mcp'] } } };\n}\n\n/** spec-graph's configuration with the archive read as history, or `null` when it already is. */\nexport function mergeSpecGraph(current: Json, archiveGlob: string): Json | null {\n const history = Array.isArray(current['historyPatterns']) ? (current['historyPatterns'] as unknown[]).filter((p): p is string => typeof p === 'string') : [];\n if (history.includes(archiveGlob)) return null;\n return { ...current, historyPatterns: [...history, archiveGlob] };\n}\n\nconst PRE_COMMIT = `#!/bin/sh\n# spec-harness: refuse a commit that changes what the active brief protects.\nexec npx --no-install spec-harness hook git\n`;\n\nexport async function plan(workspace: Workspace, options: Options): Promise<Step[]> {\n const { root } = workspace;\n const steps: Step[] = [];\n\n // spec-brief first: every other setting is derived from its directories.\n const briefConfigFile = ['.spec-brief.json', 'spec-brief.json'].map((name) => join(root, name)).find((file) => existsSync(file));\n const brief = workspace.siblings.locate('spec-brief');\n let briefs = 'briefs';\n let archive = 'briefs/archive';\n if (briefConfigFile === undefined) {\n if (brief.kind === 'found') {\n const command = brief.command;\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 } else {\n steps.push({ file: '.spec-brief.json', action: 'advise', detail: brief.reason });\n }\n } else {\n const config = await readJson(briefConfigFile);\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: briefConfigFile.slice(root.length + 1), action: 'keep', detail: `briefs in ${briefs}/, the archive in ${archive}/` });\n }\n\n // spec-graph: an archived brief is a record, not a retired decision.\n const graphFile = join(root, '.spec-graph.json');\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 current = await readJson(graphFile);\n const archiveGlob = `${archive}/**`;\n if (current === 'unreadable') {\n steps.push({ file: '.spec-graph.json', action: 'advise', detail: 'cannot be read as JSON; add \"historyPatterns\": [\"' + archiveGlob + '\"] by hand' });\n } else {\n const merged = mergeSpecGraph(current ?? {}, archiveGlob);\n if (merged === null) steps.push({ file: '.spec-graph.json', action: 'keep', detail: `${archiveGlob} is already history` });\n else steps.push({ file: '.spec-graph.json', action: current === null ? 'create' : 'update', detail: `read ${archiveGlob} as history, so a brief that depends on an archived one is not a stale premise`, apply: () => writeAtomic(graphFile, stringify(merged)) });\n }\n }\n\n // spec-harness's own file, naming the base the rounds merge into.\n const harnessFile = join(root, CONFIG_FILE);\n if (!existsSync(harnessFile)) {\n const base = await remoteDefault(root);\n const content: Json = base === null ? {} : { base };\n steps.push({ file: CONFIG_FILE, action: 'create', detail: base === null ? 'the defaults' : `rounds are measured from ${base}`, apply: () => writeAtomic(harnessFile, stringify(content)) });\n } else {\n steps.push({ file: CONFIG_FILE, action: 'keep', detail: 'already configured' });\n }\n\n // The agent's hook and server.\n const settingsFile = join(root, '.claude', 'settings.json');\n const settings = await readJson(settingsFile);\n if (settings === 'unreadable') {\n steps.push({ file: '.claude/settings.json', action: 'advise', detail: `cannot be read as JSON; add a PreToolUse and a PostToolUse hook running \"${HOOK_COMMAND}\" by hand` });\n } else {\n const merged = mergeClaudeSettings(settings ?? {});\n if (merged === null) steps.push({ file: '.claude/settings.json', action: 'keep', detail: 'the guard hooks are installed' });\n else steps.push({ file: '.claude/settings.json', action: settings === null ? 'create' : 'update', detail: 'a PreToolUse hook refuses writes to protected files; a PostToolUse hook warns about writes outside the scope', apply: () => writeAtomic(settingsFile, stringify(merged)) });\n }\n const mcpFile = join(root, '.mcp.json');\n const mcp = await readJson(mcpFile);\n 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 steps.push({ file: '.mcp.json', action: mcp === null ? 'create' : 'update', detail: 'the spec-harness MCP server: start_round, check_path, request_escalation, audit_round, list_rounds', apply: () => writeAtomic(mcpFile, stringify(merged)) });\n }\n\n // git's hook, only when asked: it is configuration outside the tree.\n if (options.gitHook) {\n const hooksPath = (await git(['config', '--get', 'core.hooksPath'], root)).stdout.trim();\n const hookFile = hooksPath === '' ? join(workspace.commonDir, 'hooks', 'pre-commit') : join(root, hooksPath, 'pre-commit');\n if (existsSync(hookFile)) {\n const text = await readFile(hookFile, 'utf8');\n steps.push(\n text.includes('spec-harness')\n ? { file: hookFile, action: 'keep', detail: 'already runs spec-harness' }\n : { file: hookFile, action: 'advise', detail: 'a pre-commit hook exists; add the line \"npx --no-install spec-harness hook git\" to it' },\n );\n } else {\n steps.push({\n file: hookFile,\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 }\n }\n\n steps.push({\n file: '.github/allowed_signers',\n action: 'advise',\n detail:\n 'rulings count only when signed by a key listed here on the base branch: one line per person, \"<email> namespaces=\\\\\"git\\\\\" <public key>\"; protect it, the ADRs and the tool configurations with CODEOWNERS',\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);\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"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The edge where the harness meets spec-brief, spec-graph and spec-guard.
|
|
3
|
+
*
|
|
4
|
+
* Through their command lines and their versioned JSON, never by importing
|
|
5
|
+
* their packages: each is released, pinned and dropped on its own, and none
|
|
6
|
+
* becomes a dependency of this one (spec-core ADR-0005, ADR-0002 here).
|
|
7
|
+
*
|
|
8
|
+
* A sibling is found as the repository installed it -
|
|
9
|
+
* `node_modules/@descent-vtt/<name>/bin/<name>.js`, run with this Node - or
|
|
10
|
+
* as `.spec-harness.json` names it. One that is not there is `absent`, and
|
|
11
|
+
* what needed it says so; nothing it would have checked is reported clean.
|
|
12
|
+
* One installed below this release's minimum is `outdated`, and is reported
|
|
13
|
+
* the same way rather than run (versions.ts).
|
|
14
|
+
*/
|
|
15
|
+
import type { HarnessConfig, SiblingName } from './config.js';
|
|
16
|
+
import type { BriefRow } from './types.js';
|
|
17
|
+
export interface SiblingRun {
|
|
18
|
+
readonly code: number;
|
|
19
|
+
readonly stdout: string;
|
|
20
|
+
readonly stderr: string;
|
|
21
|
+
}
|
|
22
|
+
export type Sibling =
|
|
23
|
+
/** `version` is `null` for a command `.spec-harness.json` names, whose version is not checked. */
|
|
24
|
+
{
|
|
25
|
+
readonly kind: 'found';
|
|
26
|
+
readonly command: readonly string[];
|
|
27
|
+
readonly version: string | null;
|
|
28
|
+
} | {
|
|
29
|
+
readonly kind: 'absent';
|
|
30
|
+
readonly reason: string;
|
|
31
|
+
}
|
|
32
|
+
/** Installed, at a version below the minimum or one that cannot be read, which is `null`. */
|
|
33
|
+
| {
|
|
34
|
+
readonly kind: 'outdated';
|
|
35
|
+
readonly reason: string;
|
|
36
|
+
readonly version: string | null;
|
|
37
|
+
};
|
|
38
|
+
/** Where a sibling is, or why it cannot be run. */
|
|
39
|
+
export declare function locate(name: SiblingName, root: string, config: HarnessConfig): Sibling;
|
|
40
|
+
/**
|
|
41
|
+
* Runs a sibling with arguments, in the repository root.
|
|
42
|
+
*
|
|
43
|
+
* Never through a shell: an argument is a path or a brief id, and a shell
|
|
44
|
+
* would read it as syntax. `node` as a command's first word is this Node, so
|
|
45
|
+
* `["node", "path/to/bin.js"]` works on every host; a Windows `.cmd` shim such
|
|
46
|
+
* as `npx` cannot be started without a shell, and the error says to name the
|
|
47
|
+
* script instead.
|
|
48
|
+
*/
|
|
49
|
+
export declare function runSibling(command: readonly string[], args: readonly string[], root: string, timeoutMs?: number): Promise<SiblingRun>;
|
|
50
|
+
export declare class SiblingError extends Error {
|
|
51
|
+
constructor(message: string);
|
|
52
|
+
}
|
|
53
|
+
export interface Siblings {
|
|
54
|
+
readonly root: string;
|
|
55
|
+
locate(name: SiblingName): Sibling;
|
|
56
|
+
/** Every brief, live and archived. Exit 2 from spec-brief is an error here too. */
|
|
57
|
+
briefs(): Promise<BriefRow[]>;
|
|
58
|
+
/** A sibling's JSON output for a command line, with its exit code; `absent` says why it cannot be run, missing or outdated. */
|
|
59
|
+
json(name: SiblingName, args: readonly string[]): Promise<{
|
|
60
|
+
readonly code: number;
|
|
61
|
+
readonly document: unknown;
|
|
62
|
+
} | {
|
|
63
|
+
readonly absent: string;
|
|
64
|
+
}>;
|
|
65
|
+
}
|
|
66
|
+
export declare function createSiblings(root: string, config: HarnessConfig): Siblings;
|
package/dist/siblings.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The edge where the harness meets spec-brief, spec-graph and spec-guard.
|
|
3
|
+
*
|
|
4
|
+
* Through their command lines and their versioned JSON, never by importing
|
|
5
|
+
* their packages: each is released, pinned and dropped on its own, and none
|
|
6
|
+
* becomes a dependency of this one (spec-core ADR-0005, ADR-0002 here).
|
|
7
|
+
*
|
|
8
|
+
* A sibling is found as the repository installed it -
|
|
9
|
+
* `node_modules/@descent-vtt/<name>/bin/<name>.js`, run with this Node - or
|
|
10
|
+
* as `.spec-harness.json` names it. One that is not there is `absent`, and
|
|
11
|
+
* what needed it says so; nothing it would have checked is reported clean.
|
|
12
|
+
* One installed below this release's minimum is `outdated`, and is reported
|
|
13
|
+
* the same way rather than run (versions.ts).
|
|
14
|
+
*/
|
|
15
|
+
import { execFile } from 'node:child_process';
|
|
16
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { parseBriefList, SiblingOutputError } from './briefs.js';
|
|
19
|
+
import { checkVersion } from './versions.js';
|
|
20
|
+
/** Where a sibling is, or why it cannot be run. */
|
|
21
|
+
export function locate(name, root, config) {
|
|
22
|
+
// A command the configuration names is run as named: which package it runs,
|
|
23
|
+
// and so which version, is the configuration's to say.
|
|
24
|
+
const configured = config.tools[name];
|
|
25
|
+
if (configured !== null)
|
|
26
|
+
return { kind: 'found', command: configured, version: null };
|
|
27
|
+
const directory = join(root, 'node_modules', '@descent-vtt', name);
|
|
28
|
+
const bin = join(directory, 'bin', `${name}.js`);
|
|
29
|
+
if (!existsSync(bin)) {
|
|
30
|
+
return {
|
|
31
|
+
kind: 'absent',
|
|
32
|
+
reason: `${name} is not installed here: npm install --save-dev @descent-vtt/${name}, or name its command under "tools" in .spec-harness.json`,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
const check = checkVersion(name, declaredVersion(join(directory, 'package.json')));
|
|
36
|
+
if (!check.ok)
|
|
37
|
+
return { kind: 'outdated', reason: check.reason, version: check.version };
|
|
38
|
+
return { kind: 'found', command: [process.execPath, bin], version: check.version };
|
|
39
|
+
}
|
|
40
|
+
/** The `version` a package.json declares, or `undefined` when there is no such file or it is not JSON. */
|
|
41
|
+
function declaredVersion(file) {
|
|
42
|
+
try {
|
|
43
|
+
const manifest = JSON.parse(readFileSync(file, 'utf8'));
|
|
44
|
+
return typeof manifest === 'object' && manifest !== null ? manifest.version : undefined;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Runs a sibling with arguments, in the repository root.
|
|
52
|
+
*
|
|
53
|
+
* Never through a shell: an argument is a path or a brief id, and a shell
|
|
54
|
+
* would read it as syntax. `node` as a command's first word is this Node, so
|
|
55
|
+
* `["node", "path/to/bin.js"]` works on every host; a Windows `.cmd` shim such
|
|
56
|
+
* as `npx` cannot be started without a shell, and the error says to name the
|
|
57
|
+
* script instead.
|
|
58
|
+
*/
|
|
59
|
+
export function runSibling(command, args, root, timeoutMs = 600_000) {
|
|
60
|
+
const [first, ...rest] = command;
|
|
61
|
+
const program = first === 'node' ? process.execPath : first;
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
execFile(program, [...rest, ...args], {
|
|
64
|
+
cwd: root,
|
|
65
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
66
|
+
encoding: 'utf8',
|
|
67
|
+
windowsHide: true,
|
|
68
|
+
timeout: timeoutMs,
|
|
69
|
+
shell: false,
|
|
70
|
+
env: { ...process.env, NO_COLOR: '1', FORCE_COLOR: '0' },
|
|
71
|
+
}, (error, stdout, stderr) => {
|
|
72
|
+
const failure = error;
|
|
73
|
+
if (failure !== null && failure.code === 'ENOENT') {
|
|
74
|
+
resolve({
|
|
75
|
+
code: -1,
|
|
76
|
+
stdout: '',
|
|
77
|
+
stderr: `cannot start "${first}": name the tool's script, such as ["node", "node_modules/@descent-vtt/<tool>/bin/<tool>.js"]`,
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const code = failure === null ? 0 : typeof failure.code === 'number' ? failure.code : -1;
|
|
82
|
+
resolve({ code, stdout, stderr });
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
export class SiblingError extends Error {
|
|
87
|
+
constructor(message) {
|
|
88
|
+
super(message);
|
|
89
|
+
this.name = 'SiblingError';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function parseJson(name, run) {
|
|
93
|
+
try {
|
|
94
|
+
return JSON.parse(run.stdout);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
const said = (run.stderr.trim() || run.stdout.trim()).split('\n').slice(0, 3).join(' ');
|
|
98
|
+
throw new SiblingError(`${name} exited ${run.code} without JSON${said ? `: ${said}` : ''}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export function createSiblings(root, config) {
|
|
102
|
+
const found = (name) => {
|
|
103
|
+
const sibling = locate(name, root, config);
|
|
104
|
+
if (sibling.kind !== 'found')
|
|
105
|
+
throw new SiblingError(sibling.reason);
|
|
106
|
+
return sibling.command;
|
|
107
|
+
};
|
|
108
|
+
return {
|
|
109
|
+
root,
|
|
110
|
+
locate: (name) => locate(name, root, config),
|
|
111
|
+
async briefs() {
|
|
112
|
+
const run = await runSibling(found('spec-brief'), ['list', '--archived', '--format', 'json', '--no-color'], root);
|
|
113
|
+
if (run.code === 2) {
|
|
114
|
+
throw new SiblingError(`spec-brief could not list the briefs: ${run.stderr.trim().split('\n')[0] ?? 'exit 2'}`);
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
return parseBriefList(parseJson('spec-brief', run));
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (error instanceof SiblingOutputError)
|
|
121
|
+
throw new SiblingError(error.message);
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
async json(name, args) {
|
|
126
|
+
const sibling = locate(name, root, config);
|
|
127
|
+
if (sibling.kind !== 'found')
|
|
128
|
+
return { absent: sibling.reason };
|
|
129
|
+
const run = await runSibling(sibling.command, args, root);
|
|
130
|
+
return { code: run.code, document: parseJson(name, run) };
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=siblings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"siblings.js","sourceRoot":"","sources":["../src/siblings.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The edge where the harness meets spec-brief, spec-graph and spec-guard.\n *\n * Through their command lines and their versioned JSON, never by importing\n * their packages: each is released, pinned and dropped on its own, and none\n * becomes a dependency of this one (spec-core ADR-0005, ADR-0002 here).\n *\n * A sibling is found as the repository installed it -\n * `node_modules/@descent-vtt/<name>/bin/<name>.js`, run with this Node - or\n * as `.spec-harness.json` names it. One that is not there is `absent`, and\n * what needed it says so; nothing it would have checked is reported clean.\n * One installed below this release's minimum is `outdated`, and is reported\n * the same way rather than run (versions.ts).\n */\n\nimport { execFile } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nimport { parseBriefList, SiblingOutputError } from './briefs.js';\nimport type { HarnessConfig, SiblingName } from './config.js';\nimport type { BriefRow } from './types.js';\nimport { checkVersion } from './versions.js';\n\nexport interface SiblingRun {\n readonly code: number;\n readonly stdout: string;\n readonly stderr: string;\n}\n\nexport type Sibling =\n /** `version` is `null` for a command `.spec-harness.json` names, whose version is not checked. */\n | { readonly kind: 'found'; readonly command: readonly string[]; readonly version: string | null }\n | { readonly kind: 'absent'; readonly reason: string }\n /** Installed, at a version below the minimum or one that cannot be read, which is `null`. */\n | { readonly kind: 'outdated'; readonly reason: string; readonly version: string | null };\n\n/** Where a sibling is, or why it cannot be run. */\nexport function locate(name: SiblingName, root: string, config: HarnessConfig): Sibling {\n // A command the configuration names is run as named: which package it runs,\n // and so which version, is the configuration's to say.\n const configured = config.tools[name];\n if (configured !== null) return { kind: 'found', command: configured, version: null };\n const directory = join(root, 'node_modules', '@descent-vtt', name);\n const bin = join(directory, 'bin', `${name}.js`);\n if (!existsSync(bin)) {\n return {\n kind: 'absent',\n reason: `${name} is not installed here: npm install --save-dev @descent-vtt/${name}, or name its command under \"tools\" in .spec-harness.json`,\n };\n }\n const check = checkVersion(name, declaredVersion(join(directory, 'package.json')));\n if (!check.ok) return { kind: 'outdated', reason: check.reason, version: check.version };\n return { kind: 'found', command: [process.execPath, bin], version: check.version };\n}\n\n/** The `version` a package.json declares, or `undefined` when there is no such file or it is not JSON. */\nfunction declaredVersion(file: string): unknown {\n try {\n const manifest: unknown = JSON.parse(readFileSync(file, 'utf8'));\n return typeof manifest === 'object' && manifest !== null ? (manifest as { version?: unknown }).version : undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Runs a sibling with arguments, in the repository root.\n *\n * Never through a shell: an argument is a path or a brief id, and a shell\n * would read it as syntax. `node` as a command's first word is this Node, so\n * `[\"node\", \"path/to/bin.js\"]` works on every host; a Windows `.cmd` shim such\n * as `npx` cannot be started without a shell, and the error says to name the\n * script instead.\n */\nexport function runSibling(command: readonly string[], args: readonly string[], root: string, timeoutMs = 600_000): Promise<SiblingRun> {\n const [first, ...rest] = command;\n const program = first === 'node' ? process.execPath : (first as string);\n return new Promise((resolve) => {\n execFile(\n program,\n [...rest, ...args],\n {\n cwd: root,\n maxBuffer: 256 * 1024 * 1024,\n encoding: 'utf8',\n windowsHide: true,\n timeout: timeoutMs,\n shell: false,\n env: { ...process.env, NO_COLOR: '1', FORCE_COLOR: '0' },\n },\n (error, stdout, stderr) => {\n const failure = error as (Error & { code?: unknown }) | null;\n if (failure !== null && failure.code === 'ENOENT') {\n resolve({\n code: -1,\n stdout: '',\n stderr: `cannot start \"${first}\": name the tool's script, such as [\"node\", \"node_modules/@descent-vtt/<tool>/bin/<tool>.js\"]`,\n });\n return;\n }\n const code = failure === null ? 0 : typeof failure.code === 'number' ? failure.code : -1;\n resolve({ code, stdout, stderr });\n },\n );\n });\n}\n\nexport class SiblingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'SiblingError';\n }\n}\n\nfunction parseJson(name: string, run: SiblingRun): unknown {\n try {\n return JSON.parse(run.stdout);\n } catch {\n const said = (run.stderr.trim() || run.stdout.trim()).split('\\n').slice(0, 3).join(' ');\n throw new SiblingError(`${name} exited ${run.code} without JSON${said ? `: ${said}` : ''}`);\n }\n}\n\nexport interface Siblings {\n readonly root: string;\n locate(name: SiblingName): Sibling;\n /** Every brief, live and archived. Exit 2 from spec-brief is an error here too. */\n briefs(): Promise<BriefRow[]>;\n /** A sibling's JSON output for a command line, with its exit code; `absent` says why it cannot be run, missing or outdated. */\n json(name: SiblingName, args: readonly string[]): Promise<{ readonly code: number; readonly document: unknown } | { readonly absent: string }>;\n}\n\nexport function createSiblings(root: string, config: HarnessConfig): Siblings {\n const found = (name: SiblingName): readonly string[] => {\n const sibling = locate(name, root, config);\n if (sibling.kind !== 'found') throw new SiblingError(sibling.reason);\n return sibling.command;\n };\n return {\n root,\n locate: (name) => locate(name, root, config),\n async briefs() {\n const run = await runSibling(found('spec-brief'), ['list', '--archived', '--format', 'json', '--no-color'], root);\n if (run.code === 2) {\n throw new SiblingError(`spec-brief could not list the briefs: ${run.stderr.trim().split('\\n')[0] ?? 'exit 2'}`);\n }\n try {\n return parseBriefList(parseJson('spec-brief', run));\n } catch (error) {\n if (error instanceof SiblingOutputError) throw new SiblingError(error.message);\n throw error;\n }\n },\n async json(name, args) {\n const sibling = locate(name, root, config);\n if (sibling.kind !== 'found') return { absent: sibling.reason };\n const run = await runSibling(sibling.command, args, root);\n return { code: run.code, document: parseJson(name, run) };\n },\n };\n}\n"]}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Types shared across modules. Type-only: compiles to nothing. */
|
|
2
|
+
export type Severity = 'error' | 'warning' | 'note';
|
|
3
|
+
/** One thing a command found, where to fix it, and what to do next (spec-core ADR-0005). */
|
|
4
|
+
export interface Finding {
|
|
5
|
+
readonly rule: string;
|
|
6
|
+
readonly severity: Severity;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
readonly hint: string;
|
|
9
|
+
readonly file?: string | undefined;
|
|
10
|
+
readonly line?: number | undefined;
|
|
11
|
+
}
|
|
12
|
+
/** A brief as `spec-brief list --format json` reports it. */
|
|
13
|
+
export interface BriefRow {
|
|
14
|
+
readonly id: string;
|
|
15
|
+
/** Repository-relative POSIX path. */
|
|
16
|
+
readonly file: string;
|
|
17
|
+
readonly title: string | null;
|
|
18
|
+
readonly phase: 'live' | 'archived';
|
|
19
|
+
readonly status: string | null;
|
|
20
|
+
readonly type: string | null;
|
|
21
|
+
readonly wave: number | null;
|
|
22
|
+
readonly dependsOn: readonly string[];
|
|
23
|
+
readonly affectedFiles: readonly string[];
|
|
24
|
+
readonly protectedFiles: readonly string[];
|
|
25
|
+
readonly tasks: {
|
|
26
|
+
readonly total: number;
|
|
27
|
+
readonly checked: number;
|
|
28
|
+
};
|
|
29
|
+
readonly ready: boolean;
|
|
30
|
+
readonly waitingOn: readonly string[];
|
|
31
|
+
}
|
|
32
|
+
/** A file a range of commits changed, as `git diff --name-status` reports it. */
|
|
33
|
+
export interface FileChange {
|
|
34
|
+
readonly status: 'A' | 'M' | 'D' | 'R' | 'C' | 'T';
|
|
35
|
+
readonly path: string;
|
|
36
|
+
/** The path it came from, for a rename or a copy. */
|
|
37
|
+
readonly from?: string | undefined;
|
|
38
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,mEAAmE","sourcesContent":["/** Types shared across modules. Type-only: compiles to nothing. */\n\nexport type Severity = 'error' | 'warning' | 'note';\n\n/** One thing a command found, where to fix it, and what to do next (spec-core ADR-0005). */\nexport interface Finding {\n readonly rule: string;\n readonly severity: Severity;\n readonly message: string;\n readonly hint: string;\n readonly file?: string | undefined;\n readonly line?: number | undefined;\n}\n\n/** A brief as `spec-brief list --format json` reports it. */\nexport interface BriefRow {\n readonly id: string;\n /** Repository-relative POSIX path. */\n readonly file: string;\n readonly title: string | null;\n readonly phase: 'live' | 'archived';\n readonly status: string | null;\n readonly type: string | null;\n readonly wave: number | null;\n readonly dependsOn: readonly string[];\n readonly affectedFiles: readonly string[];\n readonly protectedFiles: readonly string[];\n readonly tasks: { readonly total: number; readonly checked: number };\n readonly ready: boolean;\n readonly waitingOn: readonly string[];\n}\n\n/** A file a range of commits changed, as `git diff --name-status` reports it. */\nexport interface FileChange {\n readonly status: 'A' | 'M' | 'D' | 'R' | 'C' | 'T';\n readonly path: string;\n /** The path it came from, for a rename or a copy. */\n readonly from?: string | undefined;\n}\n"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { classifyRequest, createMcpServer, envelopeIssue, isObject, isRequestId, negotiateLegacyVersion, serveLines, toolError, unknownArguments, ProtocolError, CLIENT_CAPABILITIES_KEY, CLIENT_INFO_KEY, INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, LEGACY_PROTOCOL_VERSIONS, LEGACY_RESOURCE_NOT_FOUND, METHOD_NOT_FOUND, MODERN_PROTOCOL_VERSIONS, PARSE_ERROR, PROTOCOL_VERSION_KEY, SERVER_INFO_KEY, UNSUPPORTED_PROTOCOL_VERSION, } from './mcp.js';
|
|
2
|
+
export type { ByteSource, Era, ErrorObject, JsonObject, McpServerDefinition, OutgoingMessage, PromptDefinition, RequestId, ResourceProvider, ToolDefinition, ToolOutcome, } from './mcp.js';
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { classifyRequest, createMcpServer, envelopeIssue, isObject, isRequestId, negotiateLegacyVersion, serveLines, toolError, unknownArguments, ProtocolError, CLIENT_CAPABILITIES_KEY, CLIENT_INFO_KEY, INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, LEGACY_PROTOCOL_VERSIONS, LEGACY_RESOURCE_NOT_FOUND, METHOD_NOT_FOUND, MODERN_PROTOCOL_VERSIONS, PARSE_ERROR, PROTOCOL_VERSION_KEY, SERVER_INFO_KEY, UNSUPPORTED_PROTOCOL_VERSION, } from './mcp.js';
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/vendor/spec-core/jsonrpc/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,eAAe,EACf,aAAa,EACb,QAAQ,EACR,WAAW,EACX,sBAAsB,EACtB,UAAU,EACV,SAAS,EACT,gBAAgB,EAChB,aAAa,EACb,uBAAuB,EACvB,eAAe,EACf,cAAc,EACd,cAAc,EACd,eAAe,EACf,wBAAwB,EACxB,yBAAyB,EACzB,gBAAgB,EAChB,wBAAwB,EACxB,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,4BAA4B,GAC7B,MAAM,UAAU,CAAC","sourcesContent":["export {\n classifyRequest,\n createMcpServer,\n envelopeIssue,\n isObject,\n isRequestId,\n negotiateLegacyVersion,\n serveLines,\n toolError,\n unknownArguments,\n ProtocolError,\n CLIENT_CAPABILITIES_KEY,\n CLIENT_INFO_KEY,\n INTERNAL_ERROR,\n INVALID_PARAMS,\n INVALID_REQUEST,\n LEGACY_PROTOCOL_VERSIONS,\n LEGACY_RESOURCE_NOT_FOUND,\n METHOD_NOT_FOUND,\n MODERN_PROTOCOL_VERSIONS,\n PARSE_ERROR,\n PROTOCOL_VERSION_KEY,\n SERVER_INFO_KEY,\n UNSUPPORTED_PROTOCOL_VERSION,\n} from './mcp.js';\nexport type {\n ByteSource,\n Era,\n ErrorObject,\n JsonObject,\n McpServerDefinition,\n OutgoingMessage,\n PromptDefinition,\n RequestId,\n ResourceProvider,\n ToolDefinition,\n ToolOutcome,\n} from './mcp.js';\n"]}
|