@lannguyensi/harness 0.23.1 → 0.24.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.
Files changed (60) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/cli/add/index.js +2 -2
  3. package/dist/cli/add/index.js.map +1 -1
  4. package/dist/cli/adopt/index.js +2 -2
  5. package/dist/cli/adopt/index.js.map +1 -1
  6. package/dist/cli/apply/apply.d.ts +19 -0
  7. package/dist/cli/apply/apply.js +65 -6
  8. package/dist/cli/apply/apply.js.map +1 -1
  9. package/dist/cli/apply/generate-codex-config.js +43 -36
  10. package/dist/cli/apply/generate-codex-config.js.map +1 -1
  11. package/dist/cli/apply/index.d.ts +2 -1
  12. package/dist/cli/apply/index.js +1 -0
  13. package/dist/cli/apply/index.js.map +1 -1
  14. package/dist/cli/apply/install-codex-config.d.ts +28 -0
  15. package/dist/cli/apply/install-codex-config.js +171 -0
  16. package/dist/cli/apply/install-codex-config.js.map +1 -0
  17. package/dist/cli/approve/understanding.d.ts +1 -1
  18. package/dist/cli/approve/understanding.js +55 -30
  19. package/dist/cli/approve/understanding.js.map +1 -1
  20. package/dist/cli/diff/since-apply.js +2 -2
  21. package/dist/cli/diff/since-apply.js.map +1 -1
  22. package/dist/cli/index.js +69 -36
  23. package/dist/cli/index.js.map +1 -1
  24. package/dist/cli/init/composer.js +4 -4
  25. package/dist/cli/init/composer.js.map +1 -1
  26. package/dist/cli/init/index.js +2 -2
  27. package/dist/cli/init/index.js.map +1 -1
  28. package/dist/cli/init/interactive.js +15 -5
  29. package/dist/cli/init/interactive.js.map +1 -1
  30. package/dist/cli/init/profiles.d.ts +2 -2
  31. package/dist/cli/init/profiles.js +6 -6
  32. package/dist/cli/init/profiles.js.map +1 -1
  33. package/dist/cli/init/templates.d.ts +2 -2
  34. package/dist/cli/init/templates.js +16 -16
  35. package/dist/cli/loader.js +12 -8
  36. package/dist/cli/loader.js.map +1 -1
  37. package/dist/cli/main.js +4 -2
  38. package/dist/cli/main.js.map +1 -1
  39. package/dist/cli/migrate-home/index.d.ts +39 -0
  40. package/dist/cli/migrate-home/index.js +194 -0
  41. package/dist/cli/migrate-home/index.js.map +1 -0
  42. package/dist/cli/pack/add.js +2 -2
  43. package/dist/cli/pack/add.js.map +1 -1
  44. package/dist/cli/pack/hook-codex-pre-tool-use.js +16 -1
  45. package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
  46. package/dist/cli/pack/remove.js +2 -2
  47. package/dist/cli/pack/remove.js.map +1 -1
  48. package/dist/cli/remove/index.js +2 -2
  49. package/dist/cli/remove/index.js.map +1 -1
  50. package/dist/policy-packs/builtin/understanding-before-execution.js +23 -13
  51. package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
  52. package/dist/runtime/home-dir.d.ts +39 -0
  53. package/dist/runtime/home-dir.js +105 -0
  54. package/dist/runtime/home-dir.js.map +1 -0
  55. package/dist/runtime/intercept.js +10 -6
  56. package/dist/runtime/intercept.js.map +1 -1
  57. package/dist/runtime/tool-name-aliases.d.ts +7 -0
  58. package/dist/runtime/tool-name-aliases.js +74 -0
  59. package/dist/runtime/tool-name-aliases.js.map +1 -0
  60. package/package.json +1 -1
@@ -16,8 +16,8 @@
16
16
  // Both manifests are validate-clean against the v1 schema. They use the
17
17
  // same path layouts as the existing FULL template (`~/git/pandora/...`,
18
18
  // `~/.claude/...`); operators on a different layout should override via
19
- // `~/.claude/machines/<host>.harness.overrides.yaml` (ARCHITECTURE §8).
20
- export const SOLO_TEMPLATE = `# ~/.claude/harness.yaml
19
+ // `~/.harness/machines/<host>.harness.overrides.yaml` (ARCHITECTURE §8).
20
+ export const SOLO_TEMPLATE = `# ~/.harness/harness.yaml (legacy: ~/.claude/harness.yaml)
21
21
  #
22
22
  # Bootstrapped by \`harness init --template solo\`.
23
23
  #
@@ -28,7 +28,7 @@ export const SOLO_TEMPLATE = `# ~/.claude/harness.yaml
28
28
  # review-gating).
29
29
  #
30
30
  # Adapt the paths under \`command:\` to your install layout, or move
31
- # host-specific paths to ~/.claude/machines/<host>.harness.overrides.yaml.
31
+ # host-specific paths to ~/.harness/machines/<host>.harness.overrides.yaml.
32
32
 
33
33
  version: 1
34
34
 
@@ -94,7 +94,7 @@ policy_packs:
94
94
  - '^git push origin (master|main)\\b'
95
95
  max_age: 1h
96
96
  `;
97
- export const TEAM_TEMPLATE = `# ~/.claude/harness.yaml
97
+ export const TEAM_TEMPLATE = `# ~/.harness/harness.yaml (legacy: ~/.claude/harness.yaml)
98
98
  #
99
99
  # Bootstrapped by \`harness init --template team\`.
100
100
  #
@@ -105,7 +105,7 @@ export const TEAM_TEMPLATE = `# ~/.claude/harness.yaml
105
105
  # before it can land.
106
106
  #
107
107
  # Adapt the paths under \`command:\` to your install layout, or move
108
- # host-specific paths to ~/.claude/machines/<host>.harness.overrides.yaml.
108
+ # host-specific paths to ~/.harness/machines/<host>.harness.overrides.yaml.
109
109
 
110
110
  version: 1
111
111
 
@@ -196,7 +196,7 @@ policies:
196
196
  required:
197
197
  - "a recorded review of PR #\${PR_NUMBER}"
198
198
  run:
199
- - 'mcp__agent-grounding__ledger_add { type: "fact", content: "review:\${PR_NUMBER} — <verdict + key findings + nits>" }'
199
+ - 'mcp__agent-grounding__ledger_add { sessionId: "\${SESSION_ID}", type: "fact", content: "review:\${PR_NUMBER} — <verdict + key findings + nits>" }'
200
200
 
201
201
  policy_packs:
202
202
  - name: understanding-before-execution
@@ -1 +1 @@
1
- {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../../src/cli/init/profiles.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,uEAAuE;AACvE,yCAAyC;AACzC,EAAE;AACF,sEAAsE;AACtE,kEAAkE;AAClE,kEAAkE;AAClE,kEAAkE;AAClE,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,kEAAkE;AAClE,gDAAgD;AAChD,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,wEAAwE;AACxE,wEAAwE;AAExE,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4E5B,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwI5B,CAAC"}
1
+ {"version":3,"file":"profiles.js","sourceRoot":"","sources":["../../../src/cli/init/profiles.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,EAAE;AACF,uEAAuE;AACvE,yCAAyC;AACzC,EAAE;AACF,sEAAsE;AACtE,kEAAkE;AAClE,kEAAkE;AAClE,kEAAkE;AAClE,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,kEAAkE;AAClE,gDAAgD;AAChD,EAAE;AACF,wEAAwE;AACxE,wEAAwE;AACxE,wEAAwE;AACxE,yEAAyE;AAEzE,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4E5B,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwI5B,CAAC"}
@@ -1,4 +1,4 @@
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`. The reference manifest:\n# every example policy from docs/examples/full-manifest.yaml wired through\n# the generic `harness policy intercept` engine, so no external shell\n# scripts under ~/.claude/hooks/ are required.\n#\n# Canonical source for the policy + policy_packs sections is\n# docs/examples/full-manifest.yaml. A parity vitest\n# (tests/cli/init-full-template-parity.test.ts) fails the build if the\n# two diverge on policy names or load-bearing fields.\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-*.\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 the Full default. It is published as\n # `@lannguyensi/codebase-oracle` and works fine standalone, but it\n # is an opinionated workflow add-on (multi-repo semantic search)\n # rather than infrastructure harness itself assumes. Operators who\n # want it wire it explicitly:\n # npm i -g @lannguyensi/codebase-oracle\n # harness add mcp codebase-oracle --command codebase-oracle,mcp\n # Set ORACLE_SCAN_ROOT (absolute path; tilde is not expanded by the\n # MCP env block) and OPENAI_API_KEY (or switch providers via\n # ORACLE_LLM_PROVIDER) before the first call.\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 # `min_version` floor: 0.6.0 added the `--version` short-circuit\n # the doctor probe needs (PR agent-tasks/240, release-cut PR 241).\n # Bump the floor whenever a fix you depend on lands; loose floors\n # are fine, the point is the drift signal not pinning a specific cut.\n command: [agent-tasks-mcp-bridge]\n min_version: \"0.6.0\"\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). `min_version` floor: 0.2.0\n # added the `--version` short-circuit the doctor probe needs (PR\n # agent-grounding/76, release-cut PR 77).\n command: [grounding-mcp]\n min_version: \"0.2.0\"\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 # `min_version` floor: 0.3.0 added the `--version` short-circuit\n # the doctor probe needs (PR agent-memory/40, release-cut PR 41).\n command: [memory-router-user-prompt-submit]\n min_version: \"0.3.0\"\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# The `git-preflight` SessionStart hook is the producer side of the\n# `preflight-before-*` policies: `harness session-start preflight` runs\n# agent-preflight against the session cwd and, on a ready:true result,\n# records `preflight:${REPO}` to the evidence ledger. It needs the\n# `preflight` binary on PATH (`npm i -g @lannguyensi/agent-preflight`); when\n# that is absent the hook logs to stderr and exits 0, so the session is\n# never broken \u2014 the preflight gates just stay closed until a tag is\n# produced some other way.\nhooks:\n - name: git-preflight\n event: SessionStart\n command: harness session-start preflight\n blocking: false\n # 70s budget gives the wrapped preflight (default 60s) headroom plus\n # ledger-write time. Was 30s through v0.17.4, but a healthy preflight\n # on a medium-size repo takes ~28s and the old 25s wrapper ceiling\n # blew through it. Bumped together with DEFAULT_PREFLIGHT_TIMEOUT_MS\n # (agent-tasks/7265599e).\n budget_ms: 70000\n # Floor at agent-preflight 0.1.1, the release that distinguishes\n # \"tool not installed\" (e.g. an npm script invoking eslint that is\n # not in devDependencies) from real lint/test/typecheck failures.\n # Stale 0.1.0 installs silently emit false-positive blockers that\n # keep the preflight-before-* policies closed forever. version_command\n # points at the source-of-truth preflight binary, not at the\n # `harness session-start preflight` wrapper.\n min_version: \"0.1.1\"\n version_command: [\"preflight\", \"--version\"]\n\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 # Tool-agnostic parallel of require-review-evidence for operators on the\n # gh-cli workflow (`gh pr merge`) instead of agent-tasks MCP. Same generic\n # `harness policy intercept` entrypoint; the matching review-before-merge-bash\n # policy below picks up the trigger. A PolicyTrigger can only AND-match one\n # surface (MCP tool-name OR Bash command), so two parallel definitions are\n # the minimum-scope way to cover both PR surfaces without bumping the schema.\n - name: require-review-evidence-bash\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*gh pr merge\\b'\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 # Bash-surface parallel of require-review-subagent-evidence for operators\n # who open PRs with `gh pr create` instead of agent-tasks MCP. The matching\n # review-subagent-before-pr-create-bash policy below tags by branch\n # (`review-subagent:${BRANCH}`) because no task UUID is in `gh pr create`\n # arguments; the working branch is the closest stable handle for \"the\n # PR-in-progress\" at this point in the cycle.\n - name: require-review-subagent-evidence-bash\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*gh pr create\\b'\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 producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"review:${PR_NUMBER} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: Spawn a review subagent against the PR diff, capture its verdict, then persist a ledger entry tagged with the PR number. The content should be self-contained enough for an auditor to read without re-opening the chat.\n ux:\n cannot: \"You cannot merge PR #${PR_NUMBER} yet.\"\n required:\n - \"a recorded review of PR #${PR_NUMBER}\"\n run:\n - 'mcp__agent-grounding__ledger_add { type: \"fact\", content: \"review:${PR_NUMBER} \u2014 <verdict + key findings + nits>\" }'\n\n # Bash-surface parallel of review-before-merge for operators on the gh-cli\n # workflow. Two scope notes:\n # 1. Tag shape: `review:${BRANCH}` instead of `review:${PR_NUMBER}`. The\n # `gh pr merge` invocation can target the PR by number, by URL, or by\n # the current branch (default), and PR_NUMBER is not extractable from\n # `tool_input.command` with today's JSONPath-only extract DSL. BRANCH\n # is the stable identifier the producer can record at review time.\n # 2. This sits ALONGSIDE review-before-merge \u2014 not as a replacement. An\n # operator using both surfaces (e.g. agent-tasks MCP for most repos\n # + gh-cli for a quick hotfix) will have both gates active, each with\n # its own tag shape, which is semantically honest.\n - name: review-before-merge-bash\n description: Block `gh pr merge` unless a ledger entry tagged review:<branch> exists for this session.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*gh pr merge\\b'\n requires:\n ledger_tag: \"review:${BRANCH}\"\n hook: require-review-evidence-bash\n enforcement: block\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"review:${BRANCH} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: Spawn a review subagent against the branch diff, capture its verdict, then persist a ledger entry tagged with the branch name. Mirror of the review-before-merge producer for the gh-cli surface.\n ux:\n cannot: \"You cannot merge the PR for branch ${BRANCH} via `gh pr merge` yet.\"\n required:\n - \"a recorded review of the PR for branch ${BRANCH}\"\n run:\n - 'mcp__agent-grounding__ledger_add { type: \"fact\", content: \"review:${BRANCH} \u2014 <verdict + key findings + nits>\" }'\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 producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"dogfood:${SESSION_ID} \u2014 <end-to-end smoke summary against the live system>\", source:\"manual smoke test\"}'\n description: Before tagging or publishing, run the release path end-to-end against the live system (not just unit tests) and persist the result as a session-tagged ledger entry. Document what you exercised (install, CLI happy path, MCP handshake, etc.) so a future auditor can tell whether the smoke covered the change.\n ux:\n cannot: \"You cannot publish a release yet.\"\n required:\n - \"an end-to-end dogfood run in this session\"\n run:\n - 'mcp__agent-grounding__ledger_add { type: \"fact\", content: \"dogfood:${SESSION_ID} \u2014 <end-to-end smoke summary>\" }'\n\n - name: two-reviewers-required\n description: At least two distinct reviewer ledger entries must exist for the PR.\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 count:\n min: 2\n hook: require-review-evidence\n enforcement: warn\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"review:${PR_NUMBER} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review (reviewer 2)\"}'\n description: Same shape as review-before-merge but TWO DISTINCT reviewer entries must exist before the gate is satisfied (count.min 2). Distinguish reviewers by source so the count is honest. Warn-level enforcement, so the agent CAN merge with one reviewer but should consider spawning a second for load-bearing changes.\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 producers:\n - kind: bash\n command: harness session-start preflight\n description: Runs agent-preflight against the current cwd; on ready:true, records preflight:${REPO} to the ledger. Standard producer.\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"preflight:${REPO}\", source:\"manual\"}'\n description: Direct ledger write. Use when the Bash hook is locked down (e.g. understanding-gate active) or when the standard producer is unavailable.\n ux:\n cannot: \"You cannot investigate this repository yet.\"\n required:\n - \"verified repository preflight\"\n run:\n - \"harness preflight\"\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 producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"review-subagent:${TASK_ID} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: After running a review subagent against the staged diff, persist its verdict + load-bearing findings as a ledger entry tagged with the task UUID. The content should be self-contained enough to audit later without re-reading the chat.\n ux:\n cannot: \"You cannot open a pull request for task ${TASK_ID} yet.\"\n required:\n - \"a completed review-subagent pass on this task\"\n run:\n - 'mcp__agent-grounding__ledger_add { type: \"fact\", content: \"review-subagent:${TASK_ID} \u2014 <verdict + key findings + nits>\" }'\n\n # Bash-surface parallel of review-subagent-before-pr-create. Tag shape is\n # `review-subagent:${BRANCH}` because TASK_ID is an agent-tasks-only\n # concept; for the gh-cli workflow the working branch is the closest stable\n # handle for \"the PR-in-progress\" at this point. Same rationale as\n # review-before-merge-bash: sits alongside the MCP variant, not as a\n # replacement.\n - name: review-subagent-before-pr-create-bash\n description: Block `gh pr create` unless a review-subagent ledger entry tagged review-subagent:<branch> exists for this session. Forces the rigorous review BEFORE the PR opens.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*gh pr create\\b'\n requires:\n ledger_tag: \"review-subagent:${BRANCH}\"\n hook: require-review-subagent-evidence-bash\n enforcement: block\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"review-subagent:${BRANCH} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: After running a review subagent against the staged diff for the working branch, persist its verdict + load-bearing findings as a ledger entry tagged with the branch name. Mirror of the review-subagent-before-pr-create producer for the gh-cli surface.\n ux:\n cannot: \"You cannot open a pull request for branch ${BRANCH} via `gh pr create` yet.\"\n required:\n - \"a completed review-subagent pass on branch ${BRANCH}\"\n run:\n - 'mcp__agent-grounding__ledger_add { type: \"fact\", content: \"review-subagent:${BRANCH} \u2014 <verdict + key findings + nits>\" }'\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 # at_head:true lets a preflight at the current HEAD satisfy the\n # gate at any age (the standard producer writes head:<sha> into\n # the tag content). The 10m window remains the freshness ceiling\n # for the head-mismatch case (operator switched branch, preflight\n # predates HEAD shift, runtime couldn't resolve a sha).\n at_head: true\n hook: require-preflight-push-evidence\n enforcement: block\n producers:\n - kind: bash\n command: harness session-start preflight\n description: Runs agent-preflight against the current cwd; on ready:true, records preflight:${BRANCH} ready:true confidence:<n> head:<sha> to the ledger. Standard producer.\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{type:\"fact\", content:\"preflight:${BRANCH} head:<full-sha> \u2014 <summary of what is on the branch + smoke results>\", source:\"manual\"}'\n description: Direct ledger write. Include head:<full-sha> if you want the entry to count under at_head; the branch is the WIP review surface and the content should summarise what is staged + the smoke evidence so a reviewer can audit later without re-reading the chat.\n ux:\n cannot: \"You cannot push branch ${BRANCH} yet.\"\n required:\n - \"a preflight for ${BRANCH} at the current HEAD (any age) OR any preflight within the last 10 minutes. Re-run `harness preflight` if you committed since the last preflight AND it has been more than 10 minutes.\"\n run:\n - \"harness preflight\"\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 # Producers (agent-tasks/25bced52): rendered into the gate's deny\n # envelope by the same engine as policy producers. Constraint at\n # this layer: at-least-one `ask`. Post-v0.14.0 the gate signal\n # is a filesystem marker and the mcp ledger_add path no longer\n # satisfies the gate; the canonical unblock surface is the\n # operator-approval prompt.\n producers:\n - kind: ask\n command: harness approve understanding\n description: \"Bare command, no pipes or chaining. The hook recognises it via isEscapeCommand and emits permissionDecision:ask; the operator's go on that prompt IS the gate approval. Golden path.\"\n - kind: bash\n command: harness approve understanding\n description: Same command from any un-hooked terminal (operator only, not reachable from inside the gated session). Writes the canonical marker at harness.generated/.approvals/${SESSION_ID}.\n # ux (agent-tasks/e48e3b45): replaces the legacy engine-vocabulary\n # deny envelope with the plain-language { cannot, required, run }\n # shape. Engine details (the BLOCK reason naming session id /\n # marker / report state) still land in stderr for operator audit;\n # the agent only sees this.\n ux:\n cannot: \"You cannot use write-capable tools yet.\"\n required:\n - \"an approved Understanding Report for this session\"\n run:\n - \"Write an Understanding Report covering: Current Understanding, Intended Outcome, Derived Todos, Acceptance Criteria, Assumptions, Open Questions, Out Of Scope, Risks, Verification Plan\"\n - \"Run `harness approve understanding` and approve the prompt\"\n # approval_lifecycle (agent-tasks/d8ee60ca + harness/f54e0ecb,\n # v0.18.0+): expire the approval marker on task-completion\n # boundaries so a multi-task session re-prompts for an\n # Understanding Report between tasks. Without this the legacy\n # \"one approval per session\" contract lets a stale interpretation\n # drive the next task's edits.\n #\n # Full ships both boundary kinds: the agent-tasks MCP verbs for\n # operators on that workflow, plus a Bash regex list for hybrid\n # operators who also use gh-cli for PR mechanics. `max_age` is\n # the safety net. Operators who prefer the legacy per-session\n # behaviour opt out with `approval_lifecycle: { mode: session }`.\n # Operators on other task systems override the matchers.\n approval_lifecycle:\n expire_on_tool_match:\n - mcp__agent-tasks__task_finish\n - mcp__agent-tasks__task_abandon\n - mcp__agent-tasks__pull_requests_merge\n - mcp__agent-tasks__tasks_transition\n expire_on_bash_match:\n - '^gh pr (merge|close)\\b'\n - '^git push origin (master|main)\\b'\n max_age: 4h\n\n # branch-protection (agent-tasks/2fdc5bbe, default-enabled since v0.17.2):\n # blocks Write/Edit (claude-code) or apply_patch (codex) on protected\n # branches (default: master, main, develop). Complements\n # preflight-before-push, which fires at the LAST reversible step;\n # branch-protection fires at the FIRST source mutation, catching the\n # \"forgot to branch off master\" pattern earlier in the cycle.\n #\n # Two satisfying signals: a fresh `branch:non-protected:<branch>` tag\n # from the SessionStart producer (`harness session-start branch-check`),\n # or a `branch-protection-ack:<reason>` override the operator writes\n # via mcp__agent-grounding__ledger_add for deliberate protected-branch\n # edits (version bumps, CI workflow patches, hotfixes).\n #\n # Fails closed (any load / parse / ledger error refuses). Disable by\n # setting `enabled: false` or removing this entry if your workflow\n # routinely edits master directly. Override the protected list via\n # `config.protected_branches`. Full reference:\n # docs/policy-packs/branch-protection.md.\n - name: branch-protection\n source: builtin\n enabled: true\n description: Block Write/Edit on protected branches (master, main, develop) at the first source mutation.\n config:\n # ux (agent-tasks/9806d4f8): replaces the legacy\n # \"branch-protection: refusing ...\" envelope with the\n # plain-language { cannot, required, run } shape. Engine details\n # (the BLOCK reason naming session id / freshness window) stay\n # on stderr for operator audit.\n ux:\n cannot: \"You cannot edit files on protected branch ${BRANCH} yet.\"\n required:\n - \"a checkout of a non-protected branch (current `${BRANCH}` is protected)\"\n run:\n - \"git checkout -b feat/<your-task>\"\n - \"harness session-start branch-check\"\n";
1
+ export declare const MINIMAL_TEMPLATE = "# ~/.harness/harness.yaml (legacy: ~/.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 ~/.harness/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 = "# ~/.harness/harness.yaml (legacy: ~/.claude/harness.yaml)\n#\n# Bootstrapped by `harness init --template full`. The reference manifest:\n# every example policy from docs/examples/full-manifest.yaml wired through\n# the generic `harness policy intercept` engine, so no external shell\n# scripts under ~/.claude/hooks/ are required.\n#\n# Canonical source for the policy + policy_packs sections is\n# docs/examples/full-manifest.yaml. A parity vitest\n# (tests/cli/init-full-template-parity.test.ts) fails the build if the\n# two diverge on policy names or load-bearing fields.\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-*.\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 the Full default. It is published as\n # `@lannguyensi/codebase-oracle` and works fine standalone, but it\n # is an opinionated workflow add-on (multi-repo semantic search)\n # rather than infrastructure harness itself assumes. Operators who\n # want it wire it explicitly:\n # npm i -g @lannguyensi/codebase-oracle\n # harness add mcp codebase-oracle --command codebase-oracle,mcp\n # Set ORACLE_SCAN_ROOT (absolute path; tilde is not expanded by the\n # MCP env block) and OPENAI_API_KEY (or switch providers via\n # ORACLE_LLM_PROVIDER) before the first call.\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 # `min_version` floor: 0.6.0 added the `--version` short-circuit\n # the doctor probe needs (PR agent-tasks/240, release-cut PR 241).\n # Bump the floor whenever a fix you depend on lands; loose floors\n # are fine, the point is the drift signal not pinning a specific cut.\n command: [agent-tasks-mcp-bridge]\n min_version: \"0.6.0\"\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). `min_version` floor: 0.2.0\n # added the `--version` short-circuit the doctor probe needs (PR\n # agent-grounding/76, release-cut PR 77).\n command: [grounding-mcp]\n min_version: \"0.2.0\"\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 # `min_version` floor: 0.3.0 added the `--version` short-circuit\n # the doctor probe needs (PR agent-memory/40, release-cut PR 41).\n command: [memory-router-user-prompt-submit]\n min_version: \"0.3.0\"\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# The `git-preflight` SessionStart hook is the producer side of the\n# `preflight-before-*` policies: `harness session-start preflight` runs\n# agent-preflight against the session cwd and, on a ready:true result,\n# records `preflight:${REPO}` to the evidence ledger. It needs the\n# `preflight` binary on PATH (`npm i -g @lannguyensi/agent-preflight`); when\n# that is absent the hook logs to stderr and exits 0, so the session is\n# never broken \u2014 the preflight gates just stay closed until a tag is\n# produced some other way.\nhooks:\n - name: git-preflight\n event: SessionStart\n command: harness session-start preflight\n blocking: false\n # 70s budget gives the wrapped preflight (default 60s) headroom plus\n # ledger-write time. Was 30s through v0.17.4, but a healthy preflight\n # on a medium-size repo takes ~28s and the old 25s wrapper ceiling\n # blew through it. Bumped together with DEFAULT_PREFLIGHT_TIMEOUT_MS\n # (agent-tasks/7265599e).\n budget_ms: 70000\n # Floor at agent-preflight 0.1.1, the release that distinguishes\n # \"tool not installed\" (e.g. an npm script invoking eslint that is\n # not in devDependencies) from real lint/test/typecheck failures.\n # Stale 0.1.0 installs silently emit false-positive blockers that\n # keep the preflight-before-* policies closed forever. version_command\n # points at the source-of-truth preflight binary, not at the\n # `harness session-start preflight` wrapper.\n min_version: \"0.1.1\"\n version_command: [\"preflight\", \"--version\"]\n\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 # Tool-agnostic parallel of require-review-evidence for operators on the\n # gh-cli workflow (`gh pr merge`) instead of agent-tasks MCP. Same generic\n # `harness policy intercept` entrypoint; the matching review-before-merge-bash\n # policy below picks up the trigger. A PolicyTrigger can only AND-match one\n # surface (MCP tool-name OR Bash command), so two parallel definitions are\n # the minimum-scope way to cover both PR surfaces without bumping the schema.\n - name: require-review-evidence-bash\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*gh pr merge\\b'\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 # Bash-surface parallel of require-review-subagent-evidence for operators\n # who open PRs with `gh pr create` instead of agent-tasks MCP. The matching\n # review-subagent-before-pr-create-bash policy below tags by branch\n # (`review-subagent:${BRANCH}`) because no task UUID is in `gh pr create`\n # arguments; the working branch is the closest stable handle for \"the\n # PR-in-progress\" at this point in the cycle.\n - name: require-review-subagent-evidence-bash\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*gh pr create\\b'\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 producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"review:${PR_NUMBER} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: Spawn a review subagent against the PR diff, capture its verdict, then persist a ledger entry tagged with the PR number. The content should be self-contained enough for an auditor to read without re-opening the chat.\n ux:\n cannot: \"You cannot merge PR #${PR_NUMBER} yet.\"\n required:\n - \"a recorded review of PR #${PR_NUMBER}\"\n run:\n - 'mcp__agent-grounding__ledger_add { sessionId: \"${SESSION_ID}\", type: \"fact\", content: \"review:${PR_NUMBER} \u2014 <verdict + key findings + nits>\" }'\n\n # Bash-surface parallel of review-before-merge for operators on the gh-cli\n # workflow. Two scope notes:\n # 1. Tag shape: `review:${BRANCH}` instead of `review:${PR_NUMBER}`. The\n # `gh pr merge` invocation can target the PR by number, by URL, or by\n # the current branch (default), and PR_NUMBER is not extractable from\n # `tool_input.command` with today's JSONPath-only extract DSL. BRANCH\n # is the stable identifier the producer can record at review time.\n # 2. This sits ALONGSIDE review-before-merge \u2014 not as a replacement. An\n # operator using both surfaces (e.g. agent-tasks MCP for most repos\n # + gh-cli for a quick hotfix) will have both gates active, each with\n # its own tag shape, which is semantically honest.\n - name: review-before-merge-bash\n description: Block `gh pr merge` unless a ledger entry tagged review:<branch> exists for this session.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*gh pr merge\\b'\n requires:\n ledger_tag: \"review:${BRANCH}\"\n hook: require-review-evidence-bash\n enforcement: block\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"review:${BRANCH} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: Spawn a review subagent against the branch diff, capture its verdict, then persist a ledger entry tagged with the branch name. Mirror of the review-before-merge producer for the gh-cli surface.\n ux:\n cannot: \"You cannot merge the PR for branch ${BRANCH} via `gh pr merge` yet.\"\n required:\n - \"a recorded review of the PR for branch ${BRANCH}\"\n run:\n - 'mcp__agent-grounding__ledger_add { sessionId: \"${SESSION_ID}\", type: \"fact\", content: \"review:${BRANCH} \u2014 <verdict + key findings + nits>\" }'\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 producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"dogfood:${SESSION_ID} \u2014 <end-to-end smoke summary against the live system>\", source:\"manual smoke test\"}'\n description: Before tagging or publishing, run the release path end-to-end against the live system (not just unit tests) and persist the result as a session-tagged ledger entry. Document what you exercised (install, CLI happy path, MCP handshake, etc.) so a future auditor can tell whether the smoke covered the change.\n ux:\n cannot: \"You cannot publish a release yet.\"\n required:\n - \"an end-to-end dogfood run in this session\"\n run:\n - 'mcp__agent-grounding__ledger_add { sessionId: \"${SESSION_ID}\", type: \"fact\", content: \"dogfood:${SESSION_ID} \u2014 <end-to-end smoke summary>\" }'\n\n - name: two-reviewers-required\n description: At least two distinct reviewer ledger entries must exist for the PR.\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 count:\n min: 2\n hook: require-review-evidence\n enforcement: warn\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"review:${PR_NUMBER} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review (reviewer 2)\"}'\n description: Same shape as review-before-merge but TWO DISTINCT reviewer entries must exist before the gate is satisfied (count.min 2). Distinguish reviewers by source so the count is honest. Warn-level enforcement, so the agent CAN merge with one reviewer but should consider spawning a second for load-bearing changes.\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 producers:\n - kind: bash\n command: harness session-start preflight\n description: Runs agent-preflight against the current cwd; on ready:true, records preflight:${REPO} to the ledger. Standard producer.\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"preflight:${REPO}\", source:\"manual\"}'\n description: Direct ledger write. Use when the Bash hook is locked down (e.g. understanding-gate active) or when the standard producer is unavailable.\n ux:\n cannot: \"You cannot investigate this repository yet.\"\n required:\n - \"verified repository preflight\"\n run:\n - \"harness preflight\"\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 producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"review-subagent:${TASK_ID} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: After running a review subagent against the staged diff, persist its verdict + load-bearing findings as a ledger entry tagged with the task UUID. The content should be self-contained enough to audit later without re-reading the chat.\n ux:\n cannot: \"You cannot open a pull request for task ${TASK_ID} yet.\"\n required:\n - \"a completed review-subagent pass on this task\"\n run:\n - 'mcp__agent-grounding__ledger_add { sessionId: \"${SESSION_ID}\", type: \"fact\", content: \"review-subagent:${TASK_ID} \u2014 <verdict + key findings + nits>\" }'\n\n # Bash-surface parallel of review-subagent-before-pr-create. Tag shape is\n # `review-subagent:${BRANCH}` because TASK_ID is an agent-tasks-only\n # concept; for the gh-cli workflow the working branch is the closest stable\n # handle for \"the PR-in-progress\" at this point. Same rationale as\n # review-before-merge-bash: sits alongside the MCP variant, not as a\n # replacement.\n - name: review-subagent-before-pr-create-bash\n description: Block `gh pr create` unless a review-subagent ledger entry tagged review-subagent:<branch> exists for this session. Forces the rigorous review BEFORE the PR opens.\n trigger:\n event: PreToolUse\n match: \"Bash\"\n bash_match: '(^|\\n|;|\\||&&|\\()\\s*(\\w+=\\S+\\s+)*gh pr create\\b'\n requires:\n ledger_tag: \"review-subagent:${BRANCH}\"\n hook: require-review-subagent-evidence-bash\n enforcement: block\n producers:\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"review-subagent:${BRANCH} \u2014 <verdict + key findings + nits>\", source:\"Agent(general-purpose) review\"}'\n description: After running a review subagent against the staged diff for the working branch, persist its verdict + load-bearing findings as a ledger entry tagged with the branch name. Mirror of the review-subagent-before-pr-create producer for the gh-cli surface.\n ux:\n cannot: \"You cannot open a pull request for branch ${BRANCH} via `gh pr create` yet.\"\n required:\n - \"a completed review-subagent pass on branch ${BRANCH}\"\n run:\n - 'mcp__agent-grounding__ledger_add { sessionId: \"${SESSION_ID}\", type: \"fact\", content: \"review-subagent:${BRANCH} \u2014 <verdict + key findings + nits>\" }'\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 # at_head:true lets a preflight at the current HEAD satisfy the\n # gate at any age (the standard producer writes head:<sha> into\n # the tag content). The 10m window remains the freshness ceiling\n # for the head-mismatch case (operator switched branch, preflight\n # predates HEAD shift, runtime couldn't resolve a sha).\n at_head: true\n hook: require-preflight-push-evidence\n enforcement: block\n producers:\n - kind: bash\n command: harness session-start preflight\n description: Runs agent-preflight against the current cwd; on ready:true, records preflight:${BRANCH} ready:true confidence:<n> head:<sha> to the ledger. Standard producer.\n - kind: mcp\n verb: mcp__agent-grounding__ledger_add\n example: '{sessionId:\"${SESSION_ID}\", type:\"fact\", content:\"preflight:${BRANCH} head:<full-sha> \u2014 <summary of what is on the branch + smoke results>\", source:\"manual\"}'\n description: Direct ledger write. Include head:<full-sha> if you want the entry to count under at_head; the branch is the WIP review surface and the content should summarise what is staged + the smoke evidence so a reviewer can audit later without re-reading the chat.\n ux:\n cannot: \"You cannot push branch ${BRANCH} yet.\"\n required:\n - \"a preflight for ${BRANCH} at the current HEAD (any age) OR any preflight within the last 10 minutes. Re-run `harness preflight` if you committed since the last preflight AND it has been more than 10 minutes.\"\n run:\n - \"harness preflight\"\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 # Producers (agent-tasks/25bced52): rendered into the gate's deny\n # envelope by the same engine as policy producers. Constraint at\n # this layer: at-least-one `ask`. Post-v0.14.0 the gate signal\n # is a filesystem marker and the mcp ledger_add path no longer\n # satisfies the gate; the canonical unblock surface is the\n # operator-approval prompt.\n producers:\n - kind: ask\n command: harness approve understanding\n description: \"Bare command, no pipes or chaining. The hook recognises it via isEscapeCommand and emits permissionDecision:ask; the operator's go on that prompt IS the gate approval. Golden path.\"\n - kind: bash\n command: harness approve understanding\n description: Same command from any un-hooked terminal (operator only, not reachable from inside the gated session). Writes the canonical marker at harness.generated/.approvals/${SESSION_ID}.\n # ux (agent-tasks/e48e3b45): replaces the legacy engine-vocabulary\n # deny envelope with the plain-language { cannot, required, run }\n # shape. Engine details (the BLOCK reason naming session id /\n # marker / report state) still land in stderr for operator audit;\n # the agent only sees this.\n ux:\n cannot: \"You cannot use write-capable tools yet.\"\n required:\n - \"an approved Understanding Report for this session\"\n run:\n - \"Write an Understanding Report covering: Current Understanding, Intended Outcome, Derived Todos, Acceptance Criteria, Assumptions, Open Questions, Out Of Scope, Risks, Verification Plan\"\n - \"Run `harness approve understanding` and approve the prompt\"\n # approval_lifecycle (agent-tasks/d8ee60ca + harness/f54e0ecb,\n # v0.18.0+): expire the approval marker on task-completion\n # boundaries so a multi-task session re-prompts for an\n # Understanding Report between tasks. Without this the legacy\n # \"one approval per session\" contract lets a stale interpretation\n # drive the next task's edits.\n #\n # Full ships both boundary kinds: the agent-tasks MCP verbs for\n # operators on that workflow, plus a Bash regex list for hybrid\n # operators who also use gh-cli for PR mechanics. `max_age` is\n # the safety net. Operators who prefer the legacy per-session\n # behaviour opt out with `approval_lifecycle: { mode: session }`.\n # Operators on other task systems override the matchers.\n approval_lifecycle:\n expire_on_tool_match:\n - mcp__agent-tasks__task_finish\n - mcp__agent-tasks__task_abandon\n - mcp__agent-tasks__pull_requests_merge\n - mcp__agent-tasks__tasks_transition\n expire_on_bash_match:\n - '^gh pr (merge|close)\\b'\n - '^git push origin (master|main)\\b'\n max_age: 4h\n\n # branch-protection (agent-tasks/2fdc5bbe, default-enabled since v0.17.2):\n # blocks Write/Edit (claude-code) or apply_patch (codex) on protected\n # branches (default: master, main, develop). Complements\n # preflight-before-push, which fires at the LAST reversible step;\n # branch-protection fires at the FIRST source mutation, catching the\n # \"forgot to branch off master\" pattern earlier in the cycle.\n #\n # Two satisfying signals: a fresh `branch:non-protected:<branch>` tag\n # from the SessionStart producer (`harness session-start branch-check`),\n # or a `branch-protection-ack:<reason>` override the operator writes\n # via mcp__agent-grounding__ledger_add for deliberate protected-branch\n # edits (version bumps, CI workflow patches, hotfixes).\n #\n # Fails closed (any load / parse / ledger error refuses). Disable by\n # setting `enabled: false` or removing this entry if your workflow\n # routinely edits master directly. Override the protected list via\n # `config.protected_branches`. Full reference:\n # docs/policy-packs/branch-protection.md.\n - name: branch-protection\n source: builtin\n enabled: true\n description: Block Write/Edit on protected branches (master, main, develop) at the first source mutation.\n config:\n # ux (agent-tasks/9806d4f8): replaces the legacy\n # \"branch-protection: refusing ...\" envelope with the\n # plain-language { cannot, required, run } shape. Engine details\n # (the BLOCK reason naming session id / freshness window) stay\n # on stderr for operator audit.\n ux:\n cannot: \"You cannot edit files on protected branch ${BRANCH} yet.\"\n required:\n - \"a checkout of a non-protected branch (current `${BRANCH}` is protected)\"\n run:\n - \"git checkout -b feat/<your-task>\"\n - \"harness session-start branch-check\"\n";
3
3
  export type TemplateName = "minimal" | "full" | "solo" | "team";
4
4
  export declare function getTemplate(name: TemplateName): string;
@@ -1,4 +1,4 @@
1
- export const MINIMAL_TEMPLATE = `# ~/.claude/harness.yaml
1
+ export const MINIMAL_TEMPLATE = `# ~/.harness/harness.yaml (legacy: ~/.claude/harness.yaml)
2
2
  #
3
3
  # Bootstrapped by \`harness init --template minimal\`.
4
4
  #
@@ -13,14 +13,14 @@ export const MINIMAL_TEMPLATE = `# ~/.claude/harness.yaml
13
13
  #
14
14
  # Phase 2 verbs to add entries safely: \`harness add mcp <name> ...\`,
15
15
  # \`harness add cli\`, \`harness add hook\`, \`harness add skill\`.
16
- # Per-machine overrides live at ~/.claude/machines/<discriminator>.harness.overrides.yaml
16
+ # Per-machine overrides live at ~/.harness/machines/<discriminator>.harness.overrides.yaml
17
17
  # (ARCHITECTURE.md §8) for paths that vary per host.
18
18
  #
19
19
  # Docs: https://github.com/LanNguyenSi/harness
20
20
 
21
21
  version: 1
22
22
  `;
23
- export const FULL_TEMPLATE = `# ~/.claude/harness.yaml
23
+ export const FULL_TEMPLATE = `# ~/.harness/harness.yaml (legacy: ~/.claude/harness.yaml)
24
24
  #
25
25
  # Bootstrapped by \`harness init --template full\`. The reference manifest:
26
26
  # every example policy from docs/examples/full-manifest.yaml wired through
@@ -241,14 +241,14 @@ policies:
241
241
  producers:
242
242
  - kind: mcp
243
243
  verb: mcp__agent-grounding__ledger_add
244
- example: '{type:"fact", content:"review:\${PR_NUMBER} — <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
244
+ example: '{sessionId:"\${SESSION_ID}", type:"fact", content:"review:\${PR_NUMBER} — <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
245
245
  description: Spawn a review subagent against the PR diff, capture its verdict, then persist a ledger entry tagged with the PR number. The content should be self-contained enough for an auditor to read without re-opening the chat.
246
246
  ux:
247
247
  cannot: "You cannot merge PR #\${PR_NUMBER} yet."
248
248
  required:
249
249
  - "a recorded review of PR #\${PR_NUMBER}"
250
250
  run:
251
- - 'mcp__agent-grounding__ledger_add { type: "fact", content: "review:\${PR_NUMBER} — <verdict + key findings + nits>" }'
251
+ - 'mcp__agent-grounding__ledger_add { sessionId: "\${SESSION_ID}", type: "fact", content: "review:\${PR_NUMBER} — <verdict + key findings + nits>" }'
252
252
 
253
253
  # Bash-surface parallel of review-before-merge for operators on the gh-cli
254
254
  # workflow. Two scope notes:
@@ -274,14 +274,14 @@ policies:
274
274
  producers:
275
275
  - kind: mcp
276
276
  verb: mcp__agent-grounding__ledger_add
277
- example: '{type:"fact", content:"review:\${BRANCH} — <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
277
+ example: '{sessionId:"\${SESSION_ID}", type:"fact", content:"review:\${BRANCH} — <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
278
278
  description: Spawn a review subagent against the branch diff, capture its verdict, then persist a ledger entry tagged with the branch name. Mirror of the review-before-merge producer for the gh-cli surface.
279
279
  ux:
280
280
  cannot: "You cannot merge the PR for branch \${BRANCH} via \`gh pr merge\` yet."
281
281
  required:
282
282
  - "a recorded review of the PR for branch \${BRANCH}"
283
283
  run:
284
- - 'mcp__agent-grounding__ledger_add { type: "fact", content: "review:\${BRANCH} — <verdict + key findings + nits>" }'
284
+ - 'mcp__agent-grounding__ledger_add { sessionId: "\${SESSION_ID}", type: "fact", content: "review:\${BRANCH} — <verdict + key findings + nits>" }'
285
285
 
286
286
  - name: dogfood-before-release
287
287
  description: Block npm publish / git tag v* without a recent dogfood ledger entry.
@@ -297,14 +297,14 @@ policies:
297
297
  producers:
298
298
  - kind: mcp
299
299
  verb: mcp__agent-grounding__ledger_add
300
- example: '{type:"fact", content:"dogfood:\${SESSION_ID} — <end-to-end smoke summary against the live system>", source:"manual smoke test"}'
300
+ example: '{sessionId:"\${SESSION_ID}", type:"fact", content:"dogfood:\${SESSION_ID} — <end-to-end smoke summary against the live system>", source:"manual smoke test"}'
301
301
  description: Before tagging or publishing, run the release path end-to-end against the live system (not just unit tests) and persist the result as a session-tagged ledger entry. Document what you exercised (install, CLI happy path, MCP handshake, etc.) so a future auditor can tell whether the smoke covered the change.
302
302
  ux:
303
303
  cannot: "You cannot publish a release yet."
304
304
  required:
305
305
  - "an end-to-end dogfood run in this session"
306
306
  run:
307
- - 'mcp__agent-grounding__ledger_add { type: "fact", content: "dogfood:\${SESSION_ID} — <end-to-end smoke summary>" }'
307
+ - 'mcp__agent-grounding__ledger_add { sessionId: "\${SESSION_ID}", type: "fact", content: "dogfood:\${SESSION_ID} — <end-to-end smoke summary>" }'
308
308
 
309
309
  - name: two-reviewers-required
310
310
  description: At least two distinct reviewer ledger entries must exist for the PR.
@@ -322,7 +322,7 @@ policies:
322
322
  producers:
323
323
  - kind: mcp
324
324
  verb: mcp__agent-grounding__ledger_add
325
- example: '{type:"fact", content:"review:\${PR_NUMBER} — <verdict + key findings + nits>", source:"Agent(general-purpose) review (reviewer 2)"}'
325
+ example: '{sessionId:"\${SESSION_ID}", type:"fact", content:"review:\${PR_NUMBER} — <verdict + key findings + nits>", source:"Agent(general-purpose) review (reviewer 2)"}'
326
326
  description: Same shape as review-before-merge but TWO DISTINCT reviewer entries must exist before the gate is satisfied (count.min 2). Distinguish reviewers by source so the count is honest. Warn-level enforcement, so the agent CAN merge with one reviewer but should consider spawning a second for load-bearing changes.
327
327
 
328
328
  - name: preflight-before-investigation
@@ -342,7 +342,7 @@ policies:
342
342
  description: Runs agent-preflight against the current cwd; on ready:true, records preflight:\${REPO} to the ledger. Standard producer.
343
343
  - kind: mcp
344
344
  verb: mcp__agent-grounding__ledger_add
345
- example: '{type:"fact", content:"preflight:\${REPO}", source:"manual"}'
345
+ example: '{sessionId:"\${SESSION_ID}", type:"fact", content:"preflight:\${REPO}", source:"manual"}'
346
346
  description: Direct ledger write. Use when the Bash hook is locked down (e.g. understanding-gate active) or when the standard producer is unavailable.
347
347
  ux:
348
348
  cannot: "You cannot investigate this repository yet."
@@ -365,14 +365,14 @@ policies:
365
365
  producers:
366
366
  - kind: mcp
367
367
  verb: mcp__agent-grounding__ledger_add
368
- example: '{type:"fact", content:"review-subagent:\${TASK_ID} — <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
368
+ example: '{sessionId:"\${SESSION_ID}", type:"fact", content:"review-subagent:\${TASK_ID} — <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
369
369
  description: After running a review subagent against the staged diff, persist its verdict + load-bearing findings as a ledger entry tagged with the task UUID. The content should be self-contained enough to audit later without re-reading the chat.
370
370
  ux:
371
371
  cannot: "You cannot open a pull request for task \${TASK_ID} yet."
372
372
  required:
373
373
  - "a completed review-subagent pass on this task"
374
374
  run:
375
- - 'mcp__agent-grounding__ledger_add { type: "fact", content: "review-subagent:\${TASK_ID} — <verdict + key findings + nits>" }'
375
+ - 'mcp__agent-grounding__ledger_add { sessionId: "\${SESSION_ID}", type: "fact", content: "review-subagent:\${TASK_ID} — <verdict + key findings + nits>" }'
376
376
 
377
377
  # Bash-surface parallel of review-subagent-before-pr-create. Tag shape is
378
378
  # \`review-subagent:\${BRANCH}\` because TASK_ID is an agent-tasks-only
@@ -393,14 +393,14 @@ policies:
393
393
  producers:
394
394
  - kind: mcp
395
395
  verb: mcp__agent-grounding__ledger_add
396
- example: '{type:"fact", content:"review-subagent:\${BRANCH} — <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
396
+ example: '{sessionId:"\${SESSION_ID}", type:"fact", content:"review-subagent:\${BRANCH} — <verdict + key findings + nits>", source:"Agent(general-purpose) review"}'
397
397
  description: After running a review subagent against the staged diff for the working branch, persist its verdict + load-bearing findings as a ledger entry tagged with the branch name. Mirror of the review-subagent-before-pr-create producer for the gh-cli surface.
398
398
  ux:
399
399
  cannot: "You cannot open a pull request for branch \${BRANCH} via \`gh pr create\` yet."
400
400
  required:
401
401
  - "a completed review-subagent pass on branch \${BRANCH}"
402
402
  run:
403
- - 'mcp__agent-grounding__ledger_add { type: "fact", content: "review-subagent:\${BRANCH} — <verdict + key findings + nits>" }'
403
+ - 'mcp__agent-grounding__ledger_add { sessionId: "\${SESSION_ID}", type: "fact", content: "review-subagent:\${BRANCH} — <verdict + key findings + nits>" }'
404
404
 
405
405
  - name: preflight-before-push
406
406
  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.
@@ -425,7 +425,7 @@ policies:
425
425
  description: Runs agent-preflight against the current cwd; on ready:true, records preflight:\${BRANCH} ready:true confidence:<n> head:<sha> to the ledger. Standard producer.
426
426
  - kind: mcp
427
427
  verb: mcp__agent-grounding__ledger_add
428
- example: '{type:"fact", content:"preflight:\${BRANCH} head:<full-sha> — <summary of what is on the branch + smoke results>", source:"manual"}'
428
+ example: '{sessionId:"\${SESSION_ID}", type:"fact", content:"preflight:\${BRANCH} head:<full-sha> — <summary of what is on the branch + smoke results>", source:"manual"}'
429
429
  description: Direct ledger write. Include head:<full-sha> if you want the entry to count under at_head; the branch is the WIP review surface and the content should summarise what is staged + the smoke evidence so a reviewer can audit later without re-reading the chat.
430
430
  ux:
431
431
  cannot: "You cannot push branch \${BRANCH} yet."
@@ -1,14 +1,17 @@
1
1
  import * as fs from "node:fs";
2
- import * as os from "node:os";
3
2
  import * as path from "node:path";
4
3
  import { parse as parseYaml } from "yaml";
5
4
  import { applyLayers } from "../overrides/merge.js";
6
5
  import { machineOverrideCandidates, resolveMachineDiscriminators, } from "../overrides/machines.js";
7
6
  import { ManifestParseError, parseManifest } from "../schema/index.js";
7
+ import { resolveHomeDir } from "../runtime/home-dir.js";
8
8
  import { EX_NOINPUT, HarnessExitError } from "./exit-codes.js";
9
9
  const DEFAULT_BASENAME = "harness.yaml";
10
10
  function defaultHome(opts) {
11
- return opts.homeDir ?? path.join(os.homedir(), ".claude");
11
+ // resolveHomeDir handles flag > $HARNESS_HOME > ~/.harness/ (new) >
12
+ // ~/.claude/ (legacy fallback with deprecation warning) >
13
+ // ~/.harness/ (create-on-first-use). See runtime/home-dir.ts.
14
+ return resolveHomeDir({ ...(opts.homeDir !== undefined ? { homeDir: opts.homeDir } : {}) }).path;
12
15
  }
13
16
  export function resolvePaths(opts = {}) {
14
17
  if (opts.homeDir === undefined &&
@@ -17,12 +20,13 @@ export function resolvePaths(opts = {}) {
17
20
  // Defense against the recurring test-isolation class (v0.21.1 preflight
18
21
  // stage leak, v0.22.0 approveUnderstanding marker leak, latent post-pause
19
22
  // pause-sentinel read leak): without an explicit `homeDir` or
20
- // `configPath`, this resolver would silently fall back to
21
- // `~/.claude/harness.yaml` and the caller would read/write the
22
- // operator's runtime dir. The harness CLI binary sets the env var
23
- // before `run()`; tests don't, so this throw surfaces leak sites at
24
- // assertion time instead of as silent operator-state mutation.
25
- throw new Error("resolvePaths refused to fall back to ~/.claude/ — set { homeDir } or { configPath } on LoaderOptions, " +
23
+ // `configPath`, this resolver would silently fall back to the
24
+ // operator's real `~/.harness/` (or legacy `~/.claude/`) and the
25
+ // caller would read/write that real state dir. The harness CLI
26
+ // binary sets the env var before `run()`; tests don't, so this
27
+ // throw surfaces leak sites at assertion time instead of as silent
28
+ // operator-state mutation.
29
+ throw new Error("resolvePaths refused to fall back to the real harness home dir, set { homeDir } or { configPath } on LoaderOptions, " +
26
30
  "or (for the real harness binary) set HARNESS_ALLOW_REAL_GENERATED_DIR=1");
27
31
  }
28
32
  const home = defaultHome(opts);
@@ -1 +1 @@
1
- {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/cli/loader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/cli/loader.ts"],"names":[],"mappings":"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"}
package/dist/cli/main.js CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
- // Opt the binary into reading the operator's real `~/.claude/harness.yaml`
3
- // fallback when no --config / --home is supplied. The loader (`resolvePaths`)
2
+ // Opt the binary into reading the operator's real harness home dir
3
+ // (`~/.harness/harness.yaml` preferred, `~/.claude/harness.yaml` as
4
+ // legacy fallback) when no --config / --home is supplied. The loader
5
+ // (`resolvePaths`)
4
6
  // refuses that fallback unless this flag is set, so tests that forget to
5
7
  // inject `homeDir` / `generatedDir` fail loudly instead of silently
6
8
  // reading/writing the operator's runtime dir. Recurring class, two prior
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AACA,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,oEAAoE;AACpE,yEAAyE;AACzE,0EAA0E;AAC1E,wEAAwE;AACxE,mCAAmC;AACnC,EAAE;AACF,sEAAsE;AACtE,0EAA0E;AAC1E,0EAA0E;AAC1E,uEAAuE;AACvE,6DAA6D;AAC7D,sEAAsE;AACtE,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,GAAG,GAAG,CAAC;AAEtD,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAEjC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;IAClB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":";AACA,mEAAmE;AACnE,oEAAoE;AACpE,qEAAqE;AACrE,mBAAmB;AACnB,yEAAyE;AACzE,oEAAoE;AACpE,yEAAyE;AACzE,0EAA0E;AAC1E,wEAAwE;AACxE,mCAAmC;AACnC,EAAE;AACF,sEAAsE;AACtE,0EAA0E;AAC1E,0EAA0E;AAC1E,uEAAuE;AACvE,6DAA6D;AAC7D,sEAAsE;AACtE,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,GAAG,GAAG,CAAC;AAEtD,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAEjC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;IAClB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,39 @@
1
+ export declare const MIGRATION_MARKER_BASENAME = "MOVED_TO_~_DOT_HARNESS.txt";
2
+ export interface MigrateHomeOptions {
3
+ /** If true, perform the move; otherwise dry-run. */
4
+ apply?: boolean;
5
+ /** Test injection for `os.homedir()`. */
6
+ userHome?: string;
7
+ /** Test injection for stdout. */
8
+ stdout?: NodeJS.WritableStream;
9
+ /** Test injection for stderr. */
10
+ stderr?: NodeJS.WritableStream;
11
+ }
12
+ export interface MigrateHomeResult {
13
+ legacyPath: string;
14
+ newPath: string;
15
+ /** Per-item plan: what was found, what would be moved, what was moved. */
16
+ items: MigrateHomeItemResult[];
17
+ /**
18
+ * Outcome of the run:
19
+ * - `no-op`: nothing to migrate (legacy dir empty, or already moved).
20
+ * - `would-apply`: dry-run; the listed items would move.
21
+ * - `applied`: items moved this run.
22
+ * - `partial`: some items moved before an error stopped the run.
23
+ * - `target-conflict`: a corresponding item already exists at the
24
+ * new path; the run refuses to overwrite. Operator must resolve.
25
+ */
26
+ outcome: "no-op" | "would-apply" | "applied" | "partial" | "target-conflict";
27
+ /** Path of the breadcrumb marker, present on `applied`. */
28
+ markerPath?: string;
29
+ }
30
+ export interface MigrateHomeItemResult {
31
+ basename: string;
32
+ legacyExists: boolean;
33
+ newExists: boolean;
34
+ /** What the run did (or would do) with this item. */
35
+ action: "skip" | "would-move" | "moved" | "target-exists";
36
+ /** Error message if `target-exists` or a move failed. */
37
+ detail?: string;
38
+ }
39
+ export declare function migrateHome(opts?: MigrateHomeOptions): MigrateHomeResult;