@lannguyensi/harness 0.9.1 → 0.10.1
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 +92 -0
- package/README.md +1 -1
- package/dist/cli/apply/generate-settings.js +36 -4
- package/dist/cli/apply/generate-settings.js.map +1 -1
- package/dist/cli/apply/next-steps.d.ts +7 -0
- package/dist/cli/apply/next-steps.js +21 -4
- package/dist/cli/apply/next-steps.js.map +1 -1
- package/dist/cli/approve/understanding.js +19 -1
- package/dist/cli/approve/understanding.js.map +1 -1
- package/dist/cli/doctor/format.js +4 -1
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/index.js +6 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/dependencies.d.ts +72 -0
- package/dist/cli/init/dependencies.js +182 -0
- package/dist/cli/init/dependencies.js.map +1 -0
- package/dist/cli/init/interactive.d.ts +14 -1
- package/dist/cli/init/interactive.js +109 -14
- package/dist/cli/init/interactive.js.map +1 -1
- package/dist/cli/init/profiles.d.ts +2 -2
- package/dist/cli/init/profiles.js +23 -8
- package/dist/cli/init/profiles.js.map +1 -1
- package/dist/cli/init/templates.d.ts +1 -1
- package/dist/cli/init/templates.js +65 -43
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.d.ts +7 -0
- package/dist/cli/pack/hook-pre-tool-use.js +54 -0
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/probes/memory.d.ts +13 -0
- package/dist/probes/memory.js +45 -8
- package/dist/probes/memory.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const MINIMAL_TEMPLATE = "# ~/.claude/harness.yaml\n#\n# Bootstrapped by `harness init --template minimal`.\n#\n# This is the empty-but-valid manifest. Run `harness validate` to confirm it\n# parses, then add entries under the five top-level keys:\n#\n# grounding: evidence-ledger + claim-gate config (see docs/ARCHITECTURE.md \u00A72)\n# tools: mcp / cli / skills / builtin inventory (\u00A73)\n# memory: directories, retention, scopes (\u00A74)\n# hooks: event-bound shell commands (\u00A75)\n# policies: named rules that bind hooks to triggers (\u00A76)\n#\n# Phase 2 verbs to add entries safely: `harness add mcp <name> ...`,\n# `harness add cli`, `harness add hook`, `harness add skill`.\n# Per-machine overrides live at ~/.claude/machines/<discriminator>.harness.overrides.yaml\n# (ARCHITECTURE.md \u00A78) for paths that vary per host.\n#\n# Docs: https://github.com/LanNguyenSi/harness\n\nversion: 1\n";
|
|
2
|
-
export declare const FULL_TEMPLATE = "# ~/.claude/harness.yaml\n#\n# Bootstrapped by `harness init --template full`.
|
|
2
|
+
export declare const FULL_TEMPLATE = "# ~/.claude/harness.yaml\n#\n# Bootstrapped by `harness init --template full`. The reference manifest:\n# all 5 example policies wired through the generic `harness policy intercept`\n# engine, so no external shell scripts under ~/.claude/hooks/ are required.\n#\n# What you still need on PATH (the wizard offers to `npm i -g` these on\n# init): agent-tasks-mcp-bridge, grounding-mcp, memory-router-*,\n# understanding-gate-claude-*. Optional add-on: a local codebase-oracle\n# MCP server (see comment under tools.mcp below).\n\nversion: 1\n\ngrounding:\n session:\n auto_start: true\n id_format: \"gs-{repo}-{rand:8}\"\n evidence_ledger:\n path: ~/.evidence-ledger/ledger.db\n retention_days: 90\n policies_source: ~/.claude/harness.d/policies/claim-gate.yaml\n\ntools:\n mcp:\n # codebase-oracle (the Pandora RAG MCP server) is intentionally NOT\n # in this default. The npm name `codebase-oracle` is already taken\n # by an unrelated CLI, and the Pandora variant is not yet published\n # under a non-colliding scope. Operators who run from a local\n # checkout can add it back with (note: `harness add` splits the\n # command on commas, not whitespace):\n # harness add mcp codebase-oracle \\\n # --command 'npx,tsx,~/git/pandora/codebase-oracle/src/mcp-server.ts'\n - name: agent-tasks\n # Zero-setup entry: `@agent-tasks/mcp-bridge` exposes the\n # `agent-tasks-mcp-bridge` binary on PATH. The bridge owns token\n # storage and defaults to the hosted backend; override with\n # `AGENT_TASKS_BASE_URL` / `AGENT_TASKS_TOKEN` for self-hosted.\n command: [agent-tasks-mcp-bridge]\n health:\n verb: projects_list\n timeout_ms: 5000\n enabled: true\n - name: grounding-mcp\n # Published bin from `@lannguyensi/grounding-mcp`. No env is set:\n # the bundled default resolves to `~/.evidence-ledger/ledger.db`\n # via os.homedir() at startup. Passing a literal tilde in env\n # bypasses shell expansion and creates rogue cwd-relative DB files\n # (see agent-tasks/42d224a6 incident).\n command: [grounding-mcp]\n health:\n verb: ledger_status\n timeout_ms: 5000\n enabled: true\n\n cli:\n - name: gh\n binary: gh\n required: true\n\n skills:\n enabled:\n - simplify\n - init\n - review\n - security-review\n source_dirs:\n - ~/.claude/skills\n\n builtin:\n known: [Read, Edit, Write, Bash, Agent, Skill, TaskCreate, Glob, Grep]\n\nmemory:\n directories:\n - path: ~/.claude/projects/{project}/memory\n scope: project\n router:\n # Published bin from `@lannguyensi/memory-router`.\n command: [memory-router-user-prompt-submit]\n enabled: true\n retention:\n staleness_days: 180\n broken_refs: warn\n scopes:\n default: project\n allowed: [project, user]\n\n# All PreToolUse hooks share the generic `harness policy intercept` CLI\n# entrypoint. The engine reads the tool event on stdin, evaluates whichever\n# policy below has a matching trigger (`match` + optional `bash_match`),\n# and emits Claude Code's deny envelope when the required ledger tag is\n# absent. No external shell scripts are required.\n#\n# Operators who want a SessionStart producer that writes `preflight:${REPO}`\n# (so the `preflight-before-investigation` policy unblocks) need an\n# agent-preflight-style runner; the bundled `harness session-start preflight`\n# builtin is on the roadmap (agent-tasks follow-up). Until then, supply your\n# own `~/.claude/hooks/git-preflight.sh` and add an entry here.\nhooks:\n - name: require-review-evidence\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_merge\"\n command: harness policy intercept\n blocking: hard\n budget_ms: 2000\n\n - name: require-dogfood-evidence\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*(npm publish\\b|git( -C \\S+)* tag v)'\n command: harness policy intercept\n blocking: hard\n budget_ms: 2000\n\n - name: require-preflight-evidence\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* (status|log|diff|branch)\\b'\n command: harness policy intercept\n blocking: hard\n budget_ms: 1000\n\n - name: require-review-subagent-evidence\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_create\"\n command: harness policy intercept\n blocking: hard\n budget_ms: 2000\n\n - name: require-preflight-push-evidence\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* push\\b'\n command: harness policy intercept\n blocking: hard\n budget_ms: 1000\n\npolicies:\n - name: review-before-merge\n description: Block PR merges unless a ledger entry tagged review:<pr-number> exists for this session.\n trigger:\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_merge\"\n extract:\n PR_NUMBER: \"toolArgs.prNumber\"\n requires:\n ledger_tag: \"review:${PR_NUMBER}\"\n hook: require-review-evidence\n enforcement: block\n\n - name: dogfood-before-release\n description: Block npm publish / git tag v* without a recent dogfood ledger entry.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*(npm publish\\b|git( -C \\S+)* tag v)'\n requires:\n ledger_tag: \"dogfood:${SESSION_ID}\"\n within: 24h\n hook: require-dogfood-evidence\n enforcement: block\n\n - name: preflight-before-investigation\n description: Block investigative git reads (status/log/diff/branch) when agent-preflight has not run recently with ready:true for the current repo.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* (status|log|diff|branch)\\b'\n requires:\n ledger_tag: \"preflight:${REPO}\"\n within: 1h\n hook: require-preflight-evidence\n enforcement: block\n\n - name: review-subagent-before-pr-create\n description: Block agent-tasks PR creation unless a review-subagent ledger entry tagged for this task already exists. Forces the rigorous review BEFORE the PR opens, not after.\n trigger:\n event: PreToolUse\n match: \"mcp__agent-tasks__pull_requests_create\"\n extract:\n TASK_ID: \"toolArgs.taskId\"\n requires:\n ledger_tag: \"review-subagent:${TASK_ID}\"\n hook: require-review-subagent-evidence\n enforcement: block\n\n - name: preflight-before-push\n description: Block git push unless a fresh preflight ledger entry exists for the current branch. Catches the stale-checkout class of incident at the last reversible step.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* push\\b'\n requires:\n ledger_tag: \"preflight:${BRANCH}\"\n within: 10m\n hook: require-preflight-push-evidence\n enforcement: block\n\n# Full inherits the Solo/Team understanding-gate stack: the Stop hook\n# persists each Understanding Report and the PreToolUse pre-tool-use\n# blocker refuses Edit/Write/Bash until the report is approved. Drop\n# this block if you want the reference policies above without the\n# baseline gate.\npolicy_packs:\n - name: understanding-before-execution\n source: builtin\n enabled: true\n description: Force agents to expose their task interpretation and wait for explicit human approval before any write-capable tool fires.\n config:\n mode: grill_me\n";
|
|
3
3
|
export type TemplateName = "minimal" | "full" | "solo" | "team";
|
|
4
4
|
export declare function getTemplate(name: TemplateName): string;
|
|
@@ -22,11 +22,14 @@ version: 1
|
|
|
22
22
|
`;
|
|
23
23
|
export const FULL_TEMPLATE = `# ~/.claude/harness.yaml
|
|
24
24
|
#
|
|
25
|
-
# Bootstrapped by \`harness init --template full\`.
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
25
|
+
# Bootstrapped by \`harness init --template full\`. The reference manifest:
|
|
26
|
+
# all 5 example policies wired through the generic \`harness policy intercept\`
|
|
27
|
+
# engine, so no external shell scripts under ~/.claude/hooks/ are required.
|
|
28
|
+
#
|
|
29
|
+
# What you still need on PATH (the wizard offers to \`npm i -g\` these on
|
|
30
|
+
# init): agent-tasks-mcp-bridge, grounding-mcp, memory-router-*,
|
|
31
|
+
# understanding-gate-claude-*. Optional add-on: a local codebase-oracle
|
|
32
|
+
# MCP server (see comment under tools.mcp below).
|
|
30
33
|
|
|
31
34
|
version: 1
|
|
32
35
|
|
|
@@ -41,40 +44,40 @@ grounding:
|
|
|
41
44
|
|
|
42
45
|
tools:
|
|
43
46
|
mcp:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
# codebase-oracle (the Pandora RAG MCP server) is intentionally NOT
|
|
48
|
+
# in this default. The npm name \`codebase-oracle\` is already taken
|
|
49
|
+
# by an unrelated CLI, and the Pandora variant is not yet published
|
|
50
|
+
# under a non-colliding scope. Operators who run from a local
|
|
51
|
+
# checkout can add it back with (note: \`harness add\` splits the
|
|
52
|
+
# command on commas, not whitespace):
|
|
53
|
+
# harness add mcp codebase-oracle \\
|
|
54
|
+
# --command 'npx,tsx,~/git/pandora/codebase-oracle/src/mcp-server.ts'
|
|
50
55
|
- name: agent-tasks
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
# Zero-setup entry: \`@agent-tasks/mcp-bridge\` exposes the
|
|
57
|
+
# \`agent-tasks-mcp-bridge\` binary on PATH. The bridge owns token
|
|
58
|
+
# storage and defaults to the hosted backend; override with
|
|
59
|
+
# \`AGENT_TASKS_BASE_URL\` / \`AGENT_TASKS_TOKEN\` for self-hosted.
|
|
60
|
+
command: [agent-tasks-mcp-bridge]
|
|
54
61
|
health:
|
|
55
62
|
verb: projects_list
|
|
56
63
|
timeout_ms: 5000
|
|
57
64
|
enabled: true
|
|
58
65
|
- name: grounding-mcp
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
66
|
+
# Published bin from \`@lannguyensi/grounding-mcp\`. No env is set:
|
|
67
|
+
# the bundled default resolves to \`~/.evidence-ledger/ledger.db\`
|
|
68
|
+
# via os.homedir() at startup. Passing a literal tilde in env
|
|
69
|
+
# bypasses shell expansion and creates rogue cwd-relative DB files
|
|
70
|
+
# (see agent-tasks/42d224a6 incident).
|
|
71
|
+
command: [grounding-mcp]
|
|
62
72
|
health:
|
|
63
73
|
verb: ledger_status
|
|
64
74
|
timeout_ms: 5000
|
|
65
75
|
enabled: true
|
|
66
76
|
|
|
67
77
|
cli:
|
|
68
|
-
- name: git-batch
|
|
69
|
-
binary: git-batch
|
|
70
|
-
min_version: "0.2.0"
|
|
71
|
-
required: true
|
|
72
78
|
- name: gh
|
|
73
79
|
binary: gh
|
|
74
80
|
required: true
|
|
75
|
-
- name: ledger
|
|
76
|
-
binary: ledger
|
|
77
|
-
required: false
|
|
78
81
|
|
|
79
82
|
skills:
|
|
80
83
|
enabled:
|
|
@@ -86,14 +89,15 @@ tools:
|
|
|
86
89
|
- ~/.claude/skills
|
|
87
90
|
|
|
88
91
|
builtin:
|
|
89
|
-
known: [Read, Edit, Write, Bash, Agent, Skill, TaskCreate]
|
|
92
|
+
known: [Read, Edit, Write, Bash, Agent, Skill, TaskCreate, Glob, Grep]
|
|
90
93
|
|
|
91
94
|
memory:
|
|
92
95
|
directories:
|
|
93
96
|
- path: ~/.claude/projects/{project}/memory
|
|
94
97
|
scope: project
|
|
95
98
|
router:
|
|
96
|
-
|
|
99
|
+
# Published bin from \`@lannguyensi/memory-router\`.
|
|
100
|
+
command: [memory-router-user-prompt-submit]
|
|
97
101
|
enabled: true
|
|
98
102
|
retention:
|
|
99
103
|
staleness_days: 180
|
|
@@ -102,48 +106,53 @@ memory:
|
|
|
102
106
|
default: project
|
|
103
107
|
allowed: [project, user]
|
|
104
108
|
|
|
109
|
+
# All PreToolUse hooks share the generic \`harness policy intercept\` CLI
|
|
110
|
+
# entrypoint. The engine reads the tool event on stdin, evaluates whichever
|
|
111
|
+
# policy below has a matching trigger (\`match\` + optional \`bash_match\`),
|
|
112
|
+
# and emits Claude Code's deny envelope when the required ledger tag is
|
|
113
|
+
# absent. No external shell scripts are required.
|
|
114
|
+
#
|
|
115
|
+
# Operators who want a SessionStart producer that writes \`preflight:\${REPO}\`
|
|
116
|
+
# (so the \`preflight-before-investigation\` policy unblocks) need an
|
|
117
|
+
# agent-preflight-style runner; the bundled \`harness session-start preflight\`
|
|
118
|
+
# builtin is on the roadmap (agent-tasks follow-up). Until then, supply your
|
|
119
|
+
# own \`~/.claude/hooks/git-preflight.sh\` and add an entry here.
|
|
105
120
|
hooks:
|
|
106
|
-
- name: git-preflight
|
|
107
|
-
event: SessionStart
|
|
108
|
-
command: ~/.claude/hooks/git-preflight.sh
|
|
109
|
-
blocking: false
|
|
110
|
-
budget_ms: 30000
|
|
111
|
-
description: "Run agent-preflight on session start; record ready + confidence into the ledger as preflight:\${REPO}."
|
|
112
|
-
|
|
113
121
|
- name: require-review-evidence
|
|
114
122
|
event: PreToolUse
|
|
115
123
|
match: "mcp__agent-tasks__pull_requests_merge"
|
|
116
|
-
command:
|
|
124
|
+
command: harness policy intercept
|
|
117
125
|
blocking: hard
|
|
118
126
|
budget_ms: 2000
|
|
119
127
|
|
|
120
128
|
- name: require-dogfood-evidence
|
|
121
129
|
event: PreToolUse
|
|
122
130
|
match: "Bash"
|
|
123
|
-
|
|
131
|
+
bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*(npm publish\\b|git( -C \\S+)* tag v)'
|
|
132
|
+
command: harness policy intercept
|
|
124
133
|
blocking: hard
|
|
125
134
|
budget_ms: 2000
|
|
126
135
|
|
|
127
136
|
- name: require-preflight-evidence
|
|
128
137
|
event: PreToolUse
|
|
129
138
|
match: "Bash"
|
|
130
|
-
bash_match:
|
|
131
|
-
command:
|
|
139
|
+
bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* (status|log|diff|branch)\\b'
|
|
140
|
+
command: harness policy intercept
|
|
132
141
|
blocking: hard
|
|
133
142
|
budget_ms: 1000
|
|
134
143
|
|
|
135
144
|
- name: require-review-subagent-evidence
|
|
136
145
|
event: PreToolUse
|
|
137
146
|
match: "mcp__agent-tasks__pull_requests_create"
|
|
138
|
-
command:
|
|
147
|
+
command: harness policy intercept
|
|
139
148
|
blocking: hard
|
|
140
149
|
budget_ms: 2000
|
|
141
150
|
|
|
142
151
|
- name: require-preflight-push-evidence
|
|
143
152
|
event: PreToolUse
|
|
144
153
|
match: "Bash"
|
|
145
|
-
bash_match:
|
|
146
|
-
command:
|
|
154
|
+
bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* push\\b'
|
|
155
|
+
command: harness policy intercept
|
|
147
156
|
blocking: hard
|
|
148
157
|
budget_ms: 1000
|
|
149
158
|
|
|
@@ -165,7 +174,7 @@ policies:
|
|
|
165
174
|
trigger:
|
|
166
175
|
event: PreToolUse
|
|
167
176
|
match: "Bash"
|
|
168
|
-
bash_match:
|
|
177
|
+
bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*(npm publish\\b|git( -C \\S+)* tag v)'
|
|
169
178
|
requires:
|
|
170
179
|
ledger_tag: "dogfood:\${SESSION_ID}"
|
|
171
180
|
within: 24h
|
|
@@ -177,7 +186,7 @@ policies:
|
|
|
177
186
|
trigger:
|
|
178
187
|
event: PreToolUse
|
|
179
188
|
match: "Bash"
|
|
180
|
-
bash_match:
|
|
189
|
+
bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* (status|log|diff|branch)\\b'
|
|
181
190
|
requires:
|
|
182
191
|
ledger_tag: "preflight:\${REPO}"
|
|
183
192
|
within: 1h
|
|
@@ -201,12 +210,25 @@ policies:
|
|
|
201
210
|
trigger:
|
|
202
211
|
event: PreToolUse
|
|
203
212
|
match: "Bash"
|
|
204
|
-
bash_match:
|
|
213
|
+
bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*git( -C \\S+)* push\\b'
|
|
205
214
|
requires:
|
|
206
215
|
ledger_tag: "preflight:\${BRANCH}"
|
|
207
216
|
within: 10m
|
|
208
217
|
hook: require-preflight-push-evidence
|
|
209
218
|
enforcement: block
|
|
219
|
+
|
|
220
|
+
# Full inherits the Solo/Team understanding-gate stack: the Stop hook
|
|
221
|
+
# persists each Understanding Report and the PreToolUse pre-tool-use
|
|
222
|
+
# blocker refuses Edit/Write/Bash until the report is approved. Drop
|
|
223
|
+
# this block if you want the reference policies above without the
|
|
224
|
+
# baseline gate.
|
|
225
|
+
policy_packs:
|
|
226
|
+
- name: understanding-before-execution
|
|
227
|
+
source: builtin
|
|
228
|
+
enabled: true
|
|
229
|
+
description: Force agents to expose their task interpretation and wait for explicit human approval before any write-capable tool fires.
|
|
230
|
+
config:
|
|
231
|
+
mode: grill_me
|
|
210
232
|
`;
|
|
211
233
|
import { SOLO_TEMPLATE, TEAM_TEMPLATE } from "./profiles.js";
|
|
212
234
|
export function getTemplate(name) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../src/cli/init/templates.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;CAqB/B,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../../src/cli/init/templates.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,gBAAgB,GAAG;;;;;;;;;;;;;;;;;;;;;CAqB/B,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiN5B,CAAC;AAEF,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAI7D,MAAM,UAAU,WAAW,CAAC,IAAkB;IAC5C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,MAAM;YACT,OAAO,aAAa,CAAC;QACvB,KAAK,MAAM;YACT,OAAO,aAAa,CAAC;QACvB,KAAK,MAAM;YACT,OAAO,aAAa,CAAC;QACvB,KAAK,SAAS;YACZ,OAAO,gBAAgB,CAAC;IAC5B,CAAC;AACH,CAAC"}
|
|
@@ -25,6 +25,13 @@ export interface PackHookPreToolUseOptions extends LoaderOptions {
|
|
|
25
25
|
export interface PackHookPreToolUseResult {
|
|
26
26
|
exitCode: number;
|
|
27
27
|
blocked: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* True when the hook deferred to the operator's interactive permission
|
|
30
|
+
* prompt (`permissionDecision: "ask"`) instead of hard-denying. Used for
|
|
31
|
+
* the `harness approve` / `harness gate` escape commands so the operator's
|
|
32
|
+
* go on the prompt IS the approval. Mutually exclusive with `blocked`.
|
|
33
|
+
*/
|
|
34
|
+
asked?: boolean;
|
|
28
35
|
approvalCheck: ApprovalCheckResult;
|
|
29
36
|
/** Diagnostic line emitted to stderr (always; even on allow). */
|
|
30
37
|
diagnostic: string;
|
|
@@ -57,6 +57,38 @@ function blockJson(toolName, reason) {
|
|
|
57
57
|
},
|
|
58
58
|
});
|
|
59
59
|
}
|
|
60
|
+
function isEscapeCommand(command) {
|
|
61
|
+
// The operator-approval command `harness approve ...`. The Understanding
|
|
62
|
+
// Gate must not hard-deny it: a `deny` gives no interactive prompt, so
|
|
63
|
+
// denying the very command that records the operator's approval makes the
|
|
64
|
+
// gate un-recoverable from inside the session. Deliberately strict: the
|
|
65
|
+
// command must BE a `harness approve` invocation, with no shell chaining,
|
|
66
|
+
// substitution, or redirection, so the allowlist cannot be used to smuggle
|
|
67
|
+
// other work past the gate.
|
|
68
|
+
const trimmed = command.trim();
|
|
69
|
+
if (/[;&|\n<>]/.test(trimmed))
|
|
70
|
+
return false;
|
|
71
|
+
if (trimmed.includes("`") || trimmed.includes("$("))
|
|
72
|
+
return false;
|
|
73
|
+
return /^harness\s+approve\b/.test(trimmed);
|
|
74
|
+
}
|
|
75
|
+
// The Claude Code PreToolUse "ask" envelope: surface the normal interactive
|
|
76
|
+
// permission prompt. Per the hooks contract `permissionDecision: "ask"` is
|
|
77
|
+
// PreToolUse-only, and the legacy top-level `decision` field is omitted on
|
|
78
|
+
// purpose: a `decision: "block"` would hard-block legacy 2.0.x CLIs and
|
|
79
|
+
// defeat the ask.
|
|
80
|
+
function askJson() {
|
|
81
|
+
const reason = "Understanding Gate: no approved Understanding Report yet. This is a " +
|
|
82
|
+
"`harness approve` command (the operator-approval path). Approve this " +
|
|
83
|
+
"prompt to record your go.";
|
|
84
|
+
return JSON.stringify({
|
|
85
|
+
hookSpecificOutput: {
|
|
86
|
+
hookEventName: "PreToolUse",
|
|
87
|
+
permissionDecision: "ask",
|
|
88
|
+
permissionDecisionReason: reason,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
60
92
|
async function checkLedger(manifest, sessionId, opts) {
|
|
61
93
|
if (opts.ledgerQuery) {
|
|
62
94
|
const result = await opts.ledgerQuery(sessionId);
|
|
@@ -104,6 +136,10 @@ export async function runPackHookPreToolUseCli(opts = {}) {
|
|
|
104
136
|
process.env.CLAUDE_SESSION_ID ??
|
|
105
137
|
"";
|
|
106
138
|
const toolName = typeof event.tool_name === "string" ? event.tool_name : "(unknown)";
|
|
139
|
+
const rawCommand = event.tool_input && typeof event.tool_input === "object"
|
|
140
|
+
? event.tool_input.command
|
|
141
|
+
: undefined;
|
|
142
|
+
const commandStr = typeof rawCommand === "string" ? rawCommand : "";
|
|
107
143
|
// Load manifest (or use injection). Bail to allow on any failure so a
|
|
108
144
|
// missing harness install never bricks the session.
|
|
109
145
|
let manifest;
|
|
@@ -181,6 +217,24 @@ export async function runPackHookPreToolUseCli(opts = {}) {
|
|
|
181
217
|
}
|
|
182
218
|
// Neither source approved.
|
|
183
219
|
const reason = `${ledger.detail}; ${report.detail}`;
|
|
220
|
+
// Exception: the operator-approval command itself. Hard-denying
|
|
221
|
+
// `harness approve understanding` is a catch-22: it is the very command
|
|
222
|
+
// that records the operator's go, and a Bash `deny` gives no prompt to
|
|
223
|
+
// approve. Defer it to the interactive permission prompt instead, so the
|
|
224
|
+
// operator's go on that prompt IS the approval, and `harness approve
|
|
225
|
+
// understanding` then writes the ledger tag that unblocks the session.
|
|
226
|
+
if (toolName === "Bash" && isEscapeCommand(commandStr)) {
|
|
227
|
+
const diagnostic = `harness pack hook: ASK: operator-approval command, deferring to the interactive permission prompt`;
|
|
228
|
+
stderr.write(`${diagnostic}\n`);
|
|
229
|
+
stdout.write(`${askJson()}\n`);
|
|
230
|
+
return {
|
|
231
|
+
exitCode: 0,
|
|
232
|
+
blocked: false,
|
|
233
|
+
asked: true,
|
|
234
|
+
approvalCheck: { approved: false, source: "none", detail: reason },
|
|
235
|
+
diagnostic,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
184
238
|
const diagnostic = `harness pack hook: BLOCK — ${reason}`;
|
|
185
239
|
stderr.write(`${diagnostic}\n`);
|
|
186
240
|
stdout.write(`${blockJson(toolName, "no approved Understanding Report for this session")}\n`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-pre-tool-use.js","sourceRoot":"","sources":["../../../src/cli/pack/hook-pre-tool-use.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,EAAE;AACF,yDAAyD;AACzD,wEAAwE;AACxE,qEAAqE;AACrE,kEAAkE;AAClE,yEAAyE;AACzE,2CAA2C;AAC3C,EAAE;AACF,kEAAkE;AAClE,sEAAsE;AACtE,oEAAoE;AACpE,sEAAsE;AACtE,sEAAsE;AACtE,sEAAsE;AACtE,EAAE;AACF,iEAAiE;AACjE,wEAAwE;AACxE,qEAAqE;AACrE,oEAAoE;AACpE,qEAAqE;AACrE,iEAAiE;AAEjE,OAAO,EACL,gBAAgB,GAEjB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,oBAAoB,EACpB,iBAAiB,EACjB,kBAAkB,GAEnB,MAAM,sEAAsE,CAAC;AAE9E,OAAO,EAAE,YAAY,EAAsB,MAAM,cAAc,CAAC;AAEhE,MAAM,SAAS,GAAG,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"hook-pre-tool-use.js","sourceRoot":"","sources":["../../../src/cli/pack/hook-pre-tool-use.ts"],"names":[],"mappings":"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"}
|
package/dist/probes/memory.d.ts
CHANGED
|
@@ -5,10 +5,17 @@ export interface StaleMemory {
|
|
|
5
5
|
ageDays: number;
|
|
6
6
|
}
|
|
7
7
|
export interface MemoryReport {
|
|
8
|
+
/**
|
|
9
|
+
* `unresolved: true` marks an entry that still contains a placeholder
|
|
10
|
+
* (e.g. `{project}`) because no project context was provided to the
|
|
11
|
+
* probe. The doctor renders these as informational notes instead of
|
|
12
|
+
* "missing", since the real directory only exists per-project.
|
|
13
|
+
*/
|
|
8
14
|
directories: Array<{
|
|
9
15
|
path: string;
|
|
10
16
|
scope: string;
|
|
11
17
|
exists: boolean;
|
|
18
|
+
unresolved?: boolean;
|
|
12
19
|
}>;
|
|
13
20
|
routerExecutable: {
|
|
14
21
|
path: string;
|
|
@@ -20,5 +27,11 @@ export interface MemoryOptions {
|
|
|
20
27
|
homeDir?: string;
|
|
21
28
|
project?: string;
|
|
22
29
|
now?: Date;
|
|
30
|
+
/**
|
|
31
|
+
* Override `process.env.PATH` for the router-executable lookup. Tests
|
|
32
|
+
* use this to assert the bare-name-on-PATH branch without leaking the
|
|
33
|
+
* host's real PATH into the assertion surface.
|
|
34
|
+
*/
|
|
35
|
+
pathEnv?: string;
|
|
23
36
|
}
|
|
24
37
|
export declare function inspectMemory(manifest: Manifest, opts?: MemoryOptions): MemoryReport;
|
package/dist/probes/memory.js
CHANGED
|
@@ -43,23 +43,60 @@ export function inspectMemory(manifest, opts = {}) {
|
|
|
43
43
|
const stalenessDays = manifest.memory.retention.staleness_days;
|
|
44
44
|
const cutoffMs = now.getTime() - stalenessDays * 86400000;
|
|
45
45
|
const directories = manifest.memory.directories.map((d) => {
|
|
46
|
-
const
|
|
46
|
+
const substituted = substituteProject(d.path, opts.project);
|
|
47
|
+
const expanded = expandHome(substituted, home);
|
|
48
|
+
const unresolved = expanded.includes("{project}");
|
|
47
49
|
return {
|
|
48
50
|
path: expanded,
|
|
49
51
|
scope: d.scope,
|
|
50
|
-
|
|
52
|
+
// An entry with an unresolved placeholder is a pattern, not a
|
|
53
|
+
// concrete path; existence is not meaningful and the doctor
|
|
54
|
+
// should not flag it as missing.
|
|
55
|
+
exists: unresolved ? true : fs.existsSync(expanded),
|
|
56
|
+
...(unresolved ? { unresolved: true } : {}),
|
|
51
57
|
};
|
|
52
58
|
});
|
|
53
59
|
let routerExecutable = null;
|
|
54
60
|
if (manifest.memory.router) {
|
|
55
61
|
const cmd = manifest.memory.router.command;
|
|
56
|
-
|
|
62
|
+
// For `[node, /abs/script.js]` shapes the original cmd[0]="node" is
|
|
63
|
+
// useless: the meaningful executable is the script path. Prefer the
|
|
64
|
+
// first absolute / tilde-prefixed argument, then fall back to the
|
|
65
|
+
// first arg that is not an interpreter wrapper. Without this guard
|
|
66
|
+
// the PATH walk below would happily resolve "node" / "npx" / "bun"
|
|
67
|
+
// and report the router as installed even when the actual script is
|
|
68
|
+
// missing.
|
|
69
|
+
const isWrapper = (s) => s === "node" || s === "npx" || s === "bun" || s === "deno" || s === "ts-node" || s === "tsx";
|
|
70
|
+
const scriptPath = cmd.find((arg) => path.isAbsolute(arg) || arg.startsWith("~/")) ??
|
|
71
|
+
cmd.find((arg) => !isWrapper(arg)) ??
|
|
72
|
+
cmd[0];
|
|
57
73
|
if (scriptPath) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
74
|
+
// Two shapes are supported in the manifest: an absolute or
|
|
75
|
+
// tilde-prefixed file path (legacy, `node /abs/router.js`) and a
|
|
76
|
+
// bare bin name on PATH (current, `memory-router-user-prompt-submit`).
|
|
77
|
+
// For the bare-name case we replicate the PATH walk that the
|
|
78
|
+
// doctor uses for `tools.cli` entries so a published bin counts
|
|
79
|
+
// as "found".
|
|
80
|
+
if (path.isAbsolute(scriptPath) || scriptPath.startsWith("~/")) {
|
|
81
|
+
const candidate = expandHome(scriptPath, home);
|
|
82
|
+
routerExecutable = { path: candidate, exists: fs.existsSync(candidate) };
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const pathEnv = opts.pathEnv ?? process.env.PATH ?? "";
|
|
86
|
+
let resolved = null;
|
|
87
|
+
for (const seg of pathEnv.split(path.delimiter)) {
|
|
88
|
+
if (!seg)
|
|
89
|
+
continue;
|
|
90
|
+
const candidate = path.join(seg, scriptPath);
|
|
91
|
+
if (fs.existsSync(candidate)) {
|
|
92
|
+
resolved = candidate;
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
routerExecutable = resolved
|
|
97
|
+
? { path: resolved, exists: true }
|
|
98
|
+
: { path: scriptPath, exists: false };
|
|
99
|
+
}
|
|
63
100
|
}
|
|
64
101
|
}
|
|
65
102
|
const staleMemories = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/probes/memory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/probes/memory.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lannguyensi/harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Declarative control plane for agent harnesses — one YAML for grounding, tools, memory, and hooks.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/LanNguyenSi/harness",
|