@lannguyensi/harness 0.41.0 → 0.42.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/CHANGELOG.md +31 -0
- package/README.md +9 -0
- package/dist/cli/adopt/derive.d.ts +15 -0
- package/dist/cli/adopt/derive.js +34 -7
- package/dist/cli/adopt/derive.js.map +1 -1
- package/dist/cli/adopt/index.d.ts +30 -0
- package/dist/cli/adopt/index.js +23 -3
- package/dist/cli/adopt/index.js.map +1 -1
- package/dist/cli/apply/generate-settings.d.ts +14 -1
- package/dist/cli/apply/generate-settings.js +7 -3
- package/dist/cli/apply/generate-settings.js.map +1 -1
- package/dist/cli/approve/stdin-report.js +2 -0
- package/dist/cli/approve/stdin-report.js.map +1 -1
- package/dist/cli/doctor/claude-mcp.d.ts +50 -0
- package/dist/cli/doctor/claude-mcp.js +170 -0
- package/dist/cli/doctor/claude-mcp.js.map +1 -0
- package/dist/cli/doctor/format.js +37 -0
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.d.ts +10 -0
- package/dist/cli/doctor/index.js +24 -0
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/npm-bin-path.js +42 -0
- package/dist/cli/doctor/npm-bin-path.js.map +1 -1
- package/dist/cli/doctor/types.d.ts +11 -0
- package/dist/cli/doctor/types.js.map +1 -1
- package/dist/cli/index.js +253 -5
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/agent-tasks-auth.js +32 -0
- package/dist/cli/init/agent-tasks-auth.js.map +1 -1
- package/dist/cli/init/composer.js +3 -9
- package/dist/cli/init/composer.js.map +1 -1
- package/dist/cli/init/dependencies.js +21 -0
- package/dist/cli/init/dependencies.js.map +1 -1
- package/dist/cli/init/detect.d.ts +20 -3
- package/dist/cli/init/detect.js +34 -36
- package/dist/cli/init/detect.js.map +1 -1
- package/dist/cli/init/interactive.d.ts +33 -0
- package/dist/cli/init/interactive.js +399 -5
- package/dist/cli/init/interactive.js.map +1 -1
- package/dist/cli/init/profiles.d.ts +1 -1
- package/dist/cli/init/profiles.js +1 -1
- package/dist/cli/init/templates.d.ts +1 -1
- package/dist/cli/init/templates.js +64 -5
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/main.js +10 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/pack/hook-post-merge-gate-record.d.ts +30 -0
- package/dist/cli/pack/hook-post-merge-gate-record.js +153 -0
- package/dist/cli/pack/hook-post-merge-gate-record.js.map +1 -0
- package/dist/cli/pack/hook-post-merge-gate.d.ts +28 -0
- package/dist/cli/pack/hook-post-merge-gate.js +244 -0
- package/dist/cli/pack/hook-post-merge-gate.js.map +1 -0
- package/dist/cli/pack/hook-solution-acceptance-writeguard.js +148 -2
- package/dist/cli/pack/hook-solution-acceptance-writeguard.js.map +1 -1
- package/dist/cli/pack/understanding-report-schema-hint.js +1 -1
- package/dist/cli/pack/understanding-report-schema-hint.js.map +1 -1
- package/dist/cli/record/index.d.ts +64 -0
- package/dist/cli/record/index.js +211 -0
- package/dist/cli/record/index.js.map +1 -0
- package/dist/cli/session-start/index.d.ts +7 -0
- package/dist/cli/session-start/index.js +133 -38
- package/dist/cli/session-start/index.js.map +1 -1
- package/dist/cli/session-start/toolchain-parity.d.ts +149 -0
- package/dist/cli/session-start/toolchain-parity.js +618 -0
- package/dist/cli/session-start/toolchain-parity.js.map +1 -0
- package/dist/cli/uninstall/index.d.ts +73 -1
- package/dist/cli/uninstall/index.js +134 -2
- package/dist/cli/uninstall/index.js.map +1 -1
- package/dist/io/claude-mcp.d.ts +258 -0
- package/dist/io/claude-mcp.js +544 -0
- package/dist/io/claude-mcp.js.map +1 -0
- package/dist/policies/ledger-client.d.ts +34 -1
- package/dist/policies/ledger-client.js +21 -16
- package/dist/policies/ledger-client.js.map +1 -1
- package/dist/policy-packs/builtin/post-merge-gate-runtime.d.ts +195 -0
- package/dist/policy-packs/builtin/post-merge-gate-runtime.js +290 -0
- package/dist/policy-packs/builtin/post-merge-gate-runtime.js.map +1 -0
- package/dist/policy-packs/builtin/post-merge-gate.d.ts +56 -0
- package/dist/policy-packs/builtin/post-merge-gate.js +291 -0
- package/dist/policy-packs/builtin/post-merge-gate.js.map +1 -0
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js +28 -13
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js.map +1 -1
- package/dist/policy-packs/registry.d.ts +1 -1
- package/dist/policy-packs/registry.js +12 -0
- package/dist/policy-packs/registry.js.map +1 -1
- package/dist/runtime/git-context.d.ts +50 -0
- package/dist/runtime/git-context.js +108 -1
- package/dist/runtime/git-context.js.map +1 -1
- package/dist/runtime/hermetic-spawn-guard.d.ts +25 -0
- package/dist/runtime/hermetic-spawn-guard.js +136 -0
- package/dist/runtime/hermetic-spawn-guard.js.map +1 -0
- package/dist/runtime/ledger-writer.d.ts +27 -0
- package/dist/runtime/ledger-writer.js +46 -0
- package/dist/runtime/ledger-writer.js.map +1 -0
- package/dist/runtime/read-only-bash.js +118 -0
- package/dist/runtime/read-only-bash.js.map +1 -1
- package/dist/schema/index.d.ts +41 -0
- package/dist/schema/index.js +5 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/toolchain-parity.d.ts +18 -0
- package/dist/schema/toolchain-parity.js +35 -0
- package/dist/schema/toolchain-parity.js.map +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
export declare const DEFAULT_TIMEOUT_MS = 30000;
|
|
2
|
+
export interface ClaudeMcpExecResult {
|
|
3
|
+
code: number;
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
/** True iff the failure was a spawn-time ENOENT (the `claude` binary is not resolvable on PATH). */
|
|
7
|
+
enoent: boolean;
|
|
8
|
+
/** True iff the call was killed after exceeding the timeout. */
|
|
9
|
+
timedOut: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface ClaudeMcpExec {
|
|
12
|
+
(args: string[], timeoutMs: number): Promise<ClaudeMcpExecResult>;
|
|
13
|
+
}
|
|
14
|
+
export interface ClaudeMcpCallOptions {
|
|
15
|
+
/** Override the `claude` spawn. Tests inject a fake; default is the real CLI. */
|
|
16
|
+
exec?: ClaudeMcpExec;
|
|
17
|
+
/** Per-call timeout in ms. Default {@link DEFAULT_TIMEOUT_MS}. */
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
}
|
|
20
|
+
/** Claude Code's `mcpServers` entry shape: command + optional args/env (matches SettingsMcpServer in generate-settings.ts). */
|
|
21
|
+
export interface ClaudeMcpServerSpec {
|
|
22
|
+
command: string;
|
|
23
|
+
args?: string[];
|
|
24
|
+
env?: Record<string, string>;
|
|
25
|
+
}
|
|
26
|
+
export type AddJsonStatus = "added" | "already-exists" | "invalid-config" | "cli-missing" | "timeout" | "error";
|
|
27
|
+
export interface AddJsonResult {
|
|
28
|
+
status: AddJsonStatus;
|
|
29
|
+
message: string;
|
|
30
|
+
code: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Wrap a value as a single POSIX shell single-quoted token, escaping any
|
|
34
|
+
* embedded single quote as `'\''`. Used to build the copy-pasteable
|
|
35
|
+
* `claude mcp add-json ... <json>` fallback commands harness prints when the
|
|
36
|
+
* CLI is missing or a server is unregistered, so the operator can paste them
|
|
37
|
+
* verbatim even when a value (e.g. a home path containing an apostrophe, as
|
|
38
|
+
* projected into grounding-mcp's EVIDENCE_LEDGER_DB) has a single quote.
|
|
39
|
+
* Harness never executes these strings itself.
|
|
40
|
+
*/
|
|
41
|
+
export declare function posixSingleQuote(value: string): string;
|
|
42
|
+
export declare function addJsonMcpServer(name: string, spec: ClaudeMcpServerSpec, opts?: ClaudeMcpCallOptions): Promise<AddJsonResult>;
|
|
43
|
+
export type RemoveStatus = "removed" | "not-found" | "cli-missing" | "timeout" | "error";
|
|
44
|
+
export interface RemoveResult {
|
|
45
|
+
status: RemoveStatus;
|
|
46
|
+
message: string;
|
|
47
|
+
code: number;
|
|
48
|
+
}
|
|
49
|
+
export declare function removeMcpServer(name: string, opts?: ClaudeMcpCallOptions): Promise<RemoveResult>;
|
|
50
|
+
/**
|
|
51
|
+
* One `claude mcp remove --scope user <name>` line per name, sorted.
|
|
52
|
+
* Sibling of `manualAddJsonLines` in `cli/init/interactive.ts` — the
|
|
53
|
+
* copy-pasteable fallback GC (task 363a6de0) prints when it can't reach
|
|
54
|
+
* the `claude` CLI (or hits some other non-"removed"/"not-found" outcome)
|
|
55
|
+
* to deregister a stale, harness-owned server itself. Uses
|
|
56
|
+
* {@link posixSingleQuote} on the name for the same reason
|
|
57
|
+
* `manualAddJsonLines` quotes its JSON payload: a server name is an
|
|
58
|
+
* ordinary token today, but quoting it costs nothing and keeps the line
|
|
59
|
+
* safe to paste verbatim regardless. Harness never executes these strings
|
|
60
|
+
* itself.
|
|
61
|
+
*/
|
|
62
|
+
export declare function manualRemoveLines(names: readonly string[]): string[];
|
|
63
|
+
export type GetStatus = "found" | "not-found" | "cli-missing" | "timeout";
|
|
64
|
+
export interface GetResult {
|
|
65
|
+
status: GetStatus;
|
|
66
|
+
raw: string;
|
|
67
|
+
code: number;
|
|
68
|
+
}
|
|
69
|
+
export declare function getMcpServer(name: string, opts?: ClaudeMcpCallOptions): Promise<GetResult>;
|
|
70
|
+
export type ClaudeMcpConnectionStatus = "connected" | "failed" | "needs-authentication" | "unknown";
|
|
71
|
+
export interface ClaudeMcpListEntry {
|
|
72
|
+
name: string;
|
|
73
|
+
command: string;
|
|
74
|
+
args: string[];
|
|
75
|
+
status: ClaudeMcpConnectionStatus;
|
|
76
|
+
statusText: string;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Parse `claude mcp list` stdout. Lines that don't match the expected
|
|
80
|
+
* shape (blank lines, any future banner/preamble) are skipped rather
|
|
81
|
+
* than throwing — the exit code alone tells us the command succeeded.
|
|
82
|
+
*
|
|
83
|
+
* Caveat (same one `buildMcpServers` in generate-settings.ts documents
|
|
84
|
+
* for the inverse direction): splitting the command+args segment on
|
|
85
|
+
* whitespace mis-splits a command path with embedded spaces. Foreign
|
|
86
|
+
* entries whose "command" is a URL (e.g. `claude.ai Gmail: https://... -
|
|
87
|
+
* ! Needs authentication`) still parse correctly since URLs have no
|
|
88
|
+
* embedded whitespace.
|
|
89
|
+
*/
|
|
90
|
+
export declare function parseClaudeMcpListOutput(stdout: string): ClaudeMcpListEntry[];
|
|
91
|
+
export type ListStatus = "ok" | "cli-missing" | "timeout" | "error";
|
|
92
|
+
export interface ListResult {
|
|
93
|
+
status: ListStatus;
|
|
94
|
+
servers: ClaudeMcpListEntry[];
|
|
95
|
+
message?: string;
|
|
96
|
+
}
|
|
97
|
+
export declare function listMcpServers(opts?: ClaudeMcpCallOptions): Promise<ListResult>;
|
|
98
|
+
export interface ResolveRegistryPathOptions {
|
|
99
|
+
/** Override for Claude Code's own config home. Defaults to `path.join(os.homedir(), ".claude")`. */
|
|
100
|
+
homeDir?: string;
|
|
101
|
+
/** Override for process.env (CLAUDE_CONFIG_DIR lookup). Defaults to `process.env`. */
|
|
102
|
+
env?: NodeJS.ProcessEnv;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Resolve the path to the user-scope registry file the `claude` CLI
|
|
106
|
+
* itself reads/writes — the same precedence the CLI applies: an explicit
|
|
107
|
+
* `CLAUDE_CONFIG_DIR` wins (`$CLAUDE_CONFIG_DIR/.claude.json`); otherwise
|
|
108
|
+
* it's `~/.claude.json`, derived here as `path.join(path.dirname(homeDir),
|
|
109
|
+
* ".claude.json")` to match the existing resolution in
|
|
110
|
+
* `src/cli/uninstall/index.ts` (`probeProjectLocalClaudeJson`).
|
|
111
|
+
*/
|
|
112
|
+
export declare function resolveClaudeUserRegistryPath(opts?: ResolveRegistryPathOptions): string;
|
|
113
|
+
export interface RegistryReadResult {
|
|
114
|
+
servers: Record<string, unknown>;
|
|
115
|
+
error: string | null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Read strictly the top-level `mcpServers` key of the registry file. Never
|
|
119
|
+
* reads/interprets `projects.<path>.mcpServers` — that's project-local
|
|
120
|
+
* Claude Code state, out of scope here (see `probeProjectLocalClaudeJson`
|
|
121
|
+
* in uninstall for the existing, separate, read-only handling of it).
|
|
122
|
+
*
|
|
123
|
+
* Exported (task 83d8d03a) as the single allowed primitive for reading the
|
|
124
|
+
* effective Claude Code user-scope MCP registration from outside this
|
|
125
|
+
* module — `adopt` (MCP-drift comparison) and `detect` (team/full
|
|
126
|
+
* recognition) both call this directly instead of duplicating a registry
|
|
127
|
+
* reader or spawning `claude mcp list`. Read-only; never writes.
|
|
128
|
+
*/
|
|
129
|
+
export declare function readTopLevelMcpServers(registryPath: string): RegistryReadResult;
|
|
130
|
+
export type EnsureAction = "noop" | "add" | "replace" | "skipped";
|
|
131
|
+
export interface EnsureServerResult {
|
|
132
|
+
name: string;
|
|
133
|
+
action: EnsureAction;
|
|
134
|
+
/** Present when action === "replace". */
|
|
135
|
+
remove?: RemoveResult;
|
|
136
|
+
/** Present when action === "add" or a "replace" whose remove step succeeded. */
|
|
137
|
+
add?: AddJsonResult;
|
|
138
|
+
/** Present when action === "skipped": the registry file could not be read safely, so drift cannot be determined. */
|
|
139
|
+
reason?: string;
|
|
140
|
+
}
|
|
141
|
+
export interface EnsureMcpServersGcOptions {
|
|
142
|
+
/**
|
|
143
|
+
* Names this harness install is allowed to garbage-collect from the
|
|
144
|
+
* registry (task 363a6de0, D-107 ownership union: the current
|
|
145
|
+
* manifest's `tools.mcp[]` names, union the `.last-apply` manifest
|
|
146
|
+
* snapshot's `tools.mcp[]` names — the snapshot as of BEFORE the
|
|
147
|
+
* caller's own apply ran, so a combined edit that both drops an entry
|
|
148
|
+
* and re-stamps `.last-apply` doesn't erase the provenance). Building
|
|
149
|
+
* this set is entirely the caller's responsibility — see `wireClaudeMcp`
|
|
150
|
+
* in `cli/init/interactive.ts` for the reference construction.
|
|
151
|
+
* Deliberately NOT included: the uninstall module's
|
|
152
|
+
* `DEFAULT_OWNED_MCP_SERVERS` (D-107 supersedes the original D-103,
|
|
153
|
+
* reviewer HIGH finding) — a server sharing one of those default names
|
|
154
|
+
* that the operator registered themselves, outside/before any harness
|
|
155
|
+
* manifest, must never be GC'd just because it's absent from `desired`.
|
|
156
|
+
* A registry entry outside this set is NEVER touched by GC, no matter
|
|
157
|
+
* how it drifts from `desired`.
|
|
158
|
+
*/
|
|
159
|
+
ownedNames: string[];
|
|
160
|
+
}
|
|
161
|
+
export type GcAction = "removed" | "skipped";
|
|
162
|
+
export interface GcServerResult {
|
|
163
|
+
name: string;
|
|
164
|
+
action: GcAction;
|
|
165
|
+
/** The underlying `claude mcp remove` outcome. */
|
|
166
|
+
remove: RemoveResult;
|
|
167
|
+
/**
|
|
168
|
+
* Present when action === "skipped": `remove.status` was something
|
|
169
|
+
* other than "removed"/"not-found" (cli-missing, timeout, or a genuine
|
|
170
|
+
* error) — restated here as a human-readable reason so callers don't
|
|
171
|
+
* have to re-derive it from `remove`.
|
|
172
|
+
*/
|
|
173
|
+
reason?: string;
|
|
174
|
+
}
|
|
175
|
+
export interface GcResult {
|
|
176
|
+
/**
|
|
177
|
+
* One entry per GC candidate: an owned name (per `opts.gc.ownedNames`)
|
|
178
|
+
* that is present in the registry and absent from `desired`. Empty when
|
|
179
|
+
* there were no candidates, or when `registryReadError` is set.
|
|
180
|
+
*/
|
|
181
|
+
results: GcServerResult[];
|
|
182
|
+
/**
|
|
183
|
+
* Present when the registry could not be read safely — the same
|
|
184
|
+
* condition the main loop reports per-name via `skipped`/`reason`. No
|
|
185
|
+
* GC candidate could be determined, so no removal was attempted for any
|
|
186
|
+
* owned name; `results` stays empty rather than guessing.
|
|
187
|
+
*/
|
|
188
|
+
registryReadError?: string;
|
|
189
|
+
}
|
|
190
|
+
export interface EnsureMcpServersOptions {
|
|
191
|
+
/** Desired state: server name -> spec. */
|
|
192
|
+
desired: Record<string, ClaudeMcpServerSpec>;
|
|
193
|
+
exec?: ClaudeMcpExec;
|
|
194
|
+
timeoutMs?: number;
|
|
195
|
+
/** Explicit override for the registry file read for drift comparison. Takes precedence over homeDir/env. */
|
|
196
|
+
registryPath?: string;
|
|
197
|
+
/** Base for the default registry path when `registryPath` is not given. */
|
|
198
|
+
homeDir?: string;
|
|
199
|
+
/** Override for process.env (CLAUDE_CONFIG_DIR lookup) when `registryPath` is not given. */
|
|
200
|
+
env?: NodeJS.ProcessEnv;
|
|
201
|
+
/**
|
|
202
|
+
* Opt-in garbage collection (task 363a6de0): after reconciling
|
|
203
|
+
* `desired`, additionally `claude mcp remove` any name in
|
|
204
|
+
* `gc.ownedNames` that's registered but no longer in `desired` (i.e.
|
|
205
|
+
* removed or disabled in the manifest since it was last registered).
|
|
206
|
+
* Omitted entirely: behavior is exactly the pre-GC contract — every
|
|
207
|
+
* registry entry outside `desired` is left untouched, and
|
|
208
|
+
* `EnsureMcpServersResult.gc` stays undefined.
|
|
209
|
+
*/
|
|
210
|
+
gc?: EnsureMcpServersGcOptions;
|
|
211
|
+
}
|
|
212
|
+
export interface EnsureMcpServersResult {
|
|
213
|
+
registryPath: string;
|
|
214
|
+
results: EnsureServerResult[];
|
|
215
|
+
/** Present iff `opts.gc` was given. */
|
|
216
|
+
gc?: GcResult;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Reconcile `desired` against the live user-scope registry:
|
|
220
|
+
* - name absent from the registry → `add-json`
|
|
221
|
+
* - name present, spec identical → no-op, NO exec call at all
|
|
222
|
+
* - name present, spec differs (drift) → `remove` then `add-json`
|
|
223
|
+
* The registry is only ever read (for the comparison); every write goes
|
|
224
|
+
* through the CLI wrapper. If the registry file can't be read safely
|
|
225
|
+
* (malformed JSON, `mcpServers` not an object, non-ENOENT read error),
|
|
226
|
+
* every desired server is reported `skipped` with a reason instead of
|
|
227
|
+
* guessing — a missing file (ENOENT, i.e. no registry yet) is NOT an
|
|
228
|
+
* error and is treated as an empty registry.
|
|
229
|
+
*
|
|
230
|
+
* `opts.gc` (task 363a6de0) opts into a second pass over the SAME
|
|
231
|
+
* registry snapshot: every name in `opts.gc.ownedNames` that is currently
|
|
232
|
+
* registered but absent from `desired` gets `claude mcp remove`d — this is
|
|
233
|
+
* how a manifest edit that deletes or disables a `tools.mcp[]` entry
|
|
234
|
+
* propagates into a deregistration on the next ensure run. A name that's
|
|
235
|
+
* owned but ALSO still in `desired` (e.g. an operator hand-registered a
|
|
236
|
+
* drifted copy of a still-active entry) is never a GC candidate — the
|
|
237
|
+
* `desired` membership check excludes it before ownership is even
|
|
238
|
+
* considered, so it's protected automatically, not via special-cased
|
|
239
|
+
* content comparison. Without `opts.gc`, behavior is exactly the pre-GC
|
|
240
|
+
* contract: every registry entry outside `desired` is left alone. If the
|
|
241
|
+
* registry couldn't be read safely, GC is skipped too, mirroring the main
|
|
242
|
+
* loop (`gc.registryReadError` set, `gc.results` empty) — same "never
|
|
243
|
+
* guess" rule.
|
|
244
|
+
*/
|
|
245
|
+
export declare function ensureMcpServers(opts: EnsureMcpServersOptions): Promise<EnsureMcpServersResult>;
|
|
246
|
+
export interface StripOwnedMcpServersResult {
|
|
247
|
+
settings: Record<string, unknown>;
|
|
248
|
+
/** Names actually removed (subset of ownedNames that were present), sorted. */
|
|
249
|
+
removedNames: string[];
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Remove exactly the given owned names from `settings.mcpServers`,
|
|
253
|
+
* preserve every foreign entry, drop the `mcpServers` key entirely when
|
|
254
|
+
* it becomes empty, and leave every other top-level key byte-identical
|
|
255
|
+
* (same values, same insertion order). Pure — no file I/O; callers own
|
|
256
|
+
* reading/writing settings.json.
|
|
257
|
+
*/
|
|
258
|
+
export declare function stripOwnedMcpServers(settings: Record<string, unknown>, ownedNames: readonly string[]): StripOwnedMcpServersResult;
|