@cardor/agent-harness-kit 1.10.5 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -50,7 +50,7 @@ npx ahk init
50
50
  - [What each file does](#what-each-file-does)
51
51
  - [Tasks schema](#tasks-schema)
52
52
  - [What you can customize](#what-you-can-customize)
53
- - [`agent-harness-kit.config.ts`](#agent-harness-kitconfigts)
53
+ - [`agent-harness-kit.config.{json|ts|mjs|cjs}`](#agent-harness-kitconfigjsontsmjscjs)
54
54
  - [`health.sh`](#healthsh)
55
55
  - [Agent definition files](#agent-definition-files)
56
56
  - [`.harness/feature_list.json`](#harnessfeature_listjson)
@@ -117,7 +117,7 @@ Everything is stored locally in a SQLite database (`.harness/harness.db`). No cl
117
117
  - **Markdown fallback** — `current.md` is always regenerated so agents can understand the session state even without the MCP server.
118
118
  - **Docs search** — agents can call `docs.search(query)` to find relevant content in your project's docs folder before writing code.
119
119
  - **Multi-database support** — SQLite by default (uses `better-sqlite3` on Node ≥ 22 or `bun:sqlite` on Bun). Switch to PostgreSQL or MySQL with a single config line — same schema, same MCP tools, same workflow.
120
- - **Incremental scaffold** — `ahk init` preserves files you've already customized (agent definitions you've edited are kept). `ahk build` always regenerates agent files from the latest templates so they stay up to date.
120
+ - **Incremental scaffold** — `ahk init` preserves files you've already customized (agent definitions you've edited are kept). `ahk build` does too: it creates missing agent files and never touches existing ones. Use `ahk build --force` to regenerate them from the latest templates, discarding your edits (a backup is written first).
121
121
  - **Global installation** — `ahk init` can scaffold the harness into your home directory (`~/.claude` or `~/.config/opencode`) to share it across all projects.
122
122
  - **Input validation** — CLI prompts validate all inputs (name length, path format, task title, etc.) and retry with the error message instead of silently accepting bad values.
123
123
 
@@ -143,7 +143,18 @@ Then run the interactive setup inside your project:
143
143
  npx ahk init
144
144
  ```
145
145
 
146
- > **Local install recommended.** The generated `agent-harness-kit.config.ts` uses `import type`, which is erased at compile time, so `ahk` no longer needs to resolve the package from your project's `node_modules` at runtime. If it detects a **global-only** install (`npm install -g @cardor/agent-harness-kit`), it prints a non-blocking warning recommending `npm install --save-dev @cardor/agent-harness-kit` (or the `pnpm`/`yarn`/`bun` equivalent) — the command still runs and exits normally either way. This is only about keeping the CLI version pinned and reproducible across your team and CI, not a functional requirement.
146
+ > **The config file format depends on whether the package is installed locally.** `ahk init` checks that first, before anything else:
147
+ >
148
+ > | Local install | Generated config | Why |
149
+ > | --- | --- | --- |
150
+ > | **Not installed** (global-only CLI) | `agent-harness-kit.config.json` | Your project cannot resolve `@cardor/agent-harness-kit`, so a TypeScript config's `import type` would red-underline in your editor and fail `tsc --noEmit` on a package that isn't there. JSON has no imports and no types — nothing to resolve, zero editor errors. |
151
+ > | **Installed** (`npm install --save-dev @cardor/agent-harness-kit`) | `.ts`, `.mjs` or `.cjs` | The package resolves, so you get the full typed config with editor autocompletion. Which of the three is picked is unchanged: `.ts` when a `tsconfig.json` is present, otherwise `.mjs`/`.cjs` based on `package.json` `type`. |
152
+ >
153
+ > The trade-off is autocompletion: a JSON config has no type information behind it, so your editor cannot suggest fields. Installing the package locally and switching to a `.ts` config gets that back. There is no `$schema` key in the generated JSON — no JSON Schema for `HarnessConfig` is published yet, and pointing at a URL that doesn't resolve would only swap a type error for a fetch error.
154
+ >
155
+ > **Existing projects are never converted.** If a config of any extension already exists, it keeps working and keeps its format — installing or removing the package locally will not silently rewrite it. `loadConfig()` reads all five formats, and `ahk init` stops when it finds any of them.
156
+ >
157
+ > **A local install is still recommended** even though it is no longer required: it pins the CLI version so behavior stays reproducible across your team and CI instead of drifting with whatever is installed globally on each machine. On a global-only install `ahk` prints a non-blocking warning suggesting it — the command runs and exits normally either way.
147
158
  >
148
159
  > This check also works with **Yarn Berry (PnP)** projects, which never create a `node_modules` folder — `ahk` detects `.pnp.cjs`/`.pnp.loader.mjs` and falls back to checking that the package is declared in `package.json` instead of requiring a `node_modules` entry.
149
160
 
@@ -208,9 +219,34 @@ Regenerates `AGENTS.md` and provider-specific files from your `agent-harness-kit
208
219
  ```bash
209
220
  ahk build
210
221
  ahk build --watch # watch mode: rebuilds automatically on config changes
211
- ahk build --sync # sync tools: frontmatter in agent files to match current permission constants (claude-code only; no-op for opencode/codex-cli)
222
+ ahk build --force # DESTRUCTIVE: regenerate agent files, discarding your edits
223
+ ahk build --sync # kept for backwards compatibility — now a no-op on every provider
224
+ ```
225
+
226
+ ### Agent files are yours
227
+
228
+ `ahk build` **creates agent files that are missing and never modifies ones that already exist.** Edit `.claude/agents/<role>.md` (or `.opencode/agents/<role>.md`, or `.codex/agents/<role>.toml`) freely — change the role prompt, set a `model:` line, adjust the restriction fields. Rebuilding will not revert your work. `ahk doctor` does not report hand-edited files either; it checks existence only.
229
+
230
+ Everything else `build` writes — `AGENTS.md`, `CLAUDE.md`, MCP config, skills — is derived from your config and **is** regenerated on every run.
231
+
232
+ > **If you use OpenCode or Codex CLI, your agent files may be out of date right now.** Those two providers have always preserved existing agent files on build, which means they have never picked up template improvements shipped in newer versions of this package. Claude Code, by contrast, used to overwrite them on every build — that inconsistency was a bug, and it is now fixed in favour of preserving your edits. To pull in the current templates, run `ahk build --force` (read the warning below first).
233
+
234
+ ### `--force`
235
+
236
+ Because `build` no longer overwrites agent files on any provider, `--force` is the **only** way to regenerate them from the packaged templates. It is destructive:
237
+
238
+ ```bash
239
+ ahk build --force
212
240
  ```
213
241
 
242
+ - **It discards your customizations.** Every agent file is rewritten from the template. Prompt edits, `model:` lines, and restriction tweaks are all lost.
243
+ - **It backs up first.** Before overwriting anything, the current content of every affected file is copied to `.harness/backups/agents-<timestamp>/`. If that backup cannot be written, the command aborts and **no file is modified** — the same fail-safe as [`ahk migrate storage --force`](#storage-migration).
244
+ - **It names what it touched.** The command prints every file it overwrote and the backup location, so you can diff or restore.
245
+
246
+ `--watch` never forces, even if you pass both flags: an automatic rebuild triggered by a file change must not destroy your edits in the background.
247
+
248
+ `--sync` used to rewrite the `tools:` frontmatter of agent files so it matched a canonical allowlist. Agent files no longer declare an allowlist at all — they inherit every tool and declare only restrictions — so there is nothing left to synchronise. Use `ahk build --force` to regenerate agent files.
249
+
214
250
  ---
215
251
 
216
252
  ### `ahk dashboard`
@@ -263,7 +299,7 @@ ahk health
263
299
 
264
300
  ### `ahk doctor`
265
301
 
266
- Checks that the installed lib version, agent files, and harness skills are all in sync.
302
+ Checks the installed lib version, that every agent file is present, and that the harness skills are in sync.
267
303
 
268
304
  ```bash
269
305
  ahk doctor
@@ -272,7 +308,7 @@ ahk doctor
272
308
  Reports three categories:
273
309
 
274
310
  - **lib version** — compares installed version against the latest on npm. Shows `[✓]` if up to date, `[!]` if an update is available, or `[~]` if the registry could not be reached.
275
- - **agent files** — reads each agent file on disk and compares against what `ahk build` would generate. Reports `[!]` with the file name if outdated.
311
+ - **agent files** — checks only that a definition file exists for every role. Reports `[!]` with the file name if one is missing. The contents are never read, so **editing an agent file by hand is a fully supported state and is never reported** — customise the body, the description, or the restrictions freely and `ahk doctor` stays green.
276
312
  - **harness skills** — checks that `ahk-ask`, `ahk-consultant`, `ahk-triage`, and `ahk-review` skills exist and match the bundled source. Reports `[!]` if missing or outdated.
277
313
 
278
314
  Run `ahk build` to fix any reported issues.
@@ -427,7 +463,7 @@ ahk export --sql --output dump.sql # SQL dump to file
427
463
 
428
464
  ```
429
465
  your-project/
430
- ├── agent-harness-kit.config.{ts|mjs|cjs}
466
+ ├── agent-harness-kit.config.{json|ts|mjs|cjs}
431
467
  ├── AGENTS.md
432
468
  ├── CLAUDE.md
433
469
  ├── health.sh
@@ -450,7 +486,7 @@ your-project/
450
486
 
451
487
  ```
452
488
  your-project/
453
- ├── agent-harness-kit.config.{ts|mjs|cjs}
489
+ ├── agent-harness-kit.config.{json|ts|mjs|cjs}
454
490
  ├── AGENTS.md
455
491
  ├── health.sh
456
492
  ├── opencode.json ← MCP server + default_agent + compaction config
@@ -467,7 +503,7 @@ your-project/
467
503
 
468
504
  ```
469
505
  your-project/
470
- ├── agent-harness-kit.config.{ts|mjs|cjs}
506
+ ├── agent-harness-kit.config.{json|ts|mjs|cjs}
471
507
  ├── AGENTS.md
472
508
  ├── health.sh
473
509
  ├── .harness/
@@ -485,19 +521,19 @@ your-project/
485
521
 
486
522
  | File | Purpose | Edit it? |
487
523
  | ----------------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
488
- | `agent-harness-kit.config.ts` | Defines project metadata, provider, storage paths, MCP port | Yes — it's yours |
524
+ | `agent-harness-kit.config.{json\|ts\|mjs\|cjs}` | Defines project metadata, provider, storage paths, MCP port. JSON when the package isn't installed locally, otherwise `.ts`/`.mjs`/`.cjs` | Yes — it's yours |
489
525
  | `AGENTS.md` | Navigation map agents read first. Regenerated by `ahk build` | No — changes will be overwritten |
490
526
  | `health.sh` | Shell script agents run before starting work. Must exit 0 | **Yes — implement your checks here** |
491
527
  | `.harness/feature_list.json` | Task backlog in JSON. Humans edit this, `ahk sync` loads it into SQLite | Yes — add tasks here |
492
528
  | `.harness/harness.db` | SQLite database (local scope only). Source of truth for tasks, actions, sections | No — managed by the harness |
493
529
  | `.harness/current.md` | Auto-generated session snapshot for agents without MCP access (local scope only) | No — regenerated automatically |
494
530
  | `.harness/storage-state.json` | Always project-local. Records the REAL current storage state (`scope`, `projectId`, `dbType`, `migratedAt`) — used by migration tooling | No — managed by the harness |
495
- | `.claude/agents/*.md` | Agent role definitions (Claude Code). Created once, never overwritten | **Yes — customize agent behavior** |
531
+ | `.claude/agents/*.md` | Agent role definitions (Claude Code). Created once, never overwritten (`ahk build --force` regenerates) | **Yes — customize agent behavior** |
496
532
  | `.claude/mcp.json` | MCP server registration for Claude Code. Merged by `ahk build` | Yes, carefully — don't remove the `agent-harness-kit` entry |
497
533
  | `.claude/settings.json` | Sets `agent: "lead"` so lead runs as the default session agent. Merged by `ahk build` | Yes, carefully |
498
- | `.opencode/agents/*.md` | Agent role definitions (OpenCode). Created once, never overwritten | **Yes — customize agent behavior** |
534
+ | `.opencode/agents/*.md` | Agent role definitions (OpenCode). Created once, never overwritten (`ahk build --force` regenerates) | **Yes — customize agent behavior** |
499
535
  | `opencode.json` | MCP server + `default_agent` + compaction config for OpenCode. Merged by `ahk build` | Yes, carefully |
500
- | `.codex/agents/*.toml` | Agent role definitions (Codex CLI). Created once, never overwritten | **Yes — customize agent behavior** |
536
+ | `.codex/agents/*.toml` | Agent role definitions (Codex CLI). Created once, never overwritten (`ahk build --force` regenerates) | **Yes — customize agent behavior** |
501
537
  | `.codex/config.toml` | MCP server registration for Codex CLI. Merged by `ahk build` | Yes, carefully |
502
538
 
503
539
  ---
@@ -510,9 +546,9 @@ The `tasks` table includes an `updated_at` timestamp column, set on creation and
510
546
 
511
547
  ## What you can customize
512
548
 
513
- ### `agent-harness-kit.config.ts`
549
+ ### `agent-harness-kit.config.{json|ts|mjs|cjs}`
514
550
 
515
- Everything in the config file is yours to change:
551
+ Everything in the config file is yours to change. The example below is the TypeScript form, generated when the package is installed locally in your project:
516
552
 
517
553
  ```ts
518
554
  import type { HarnessConfig } from '@cardor/agent-harness-kit'
@@ -526,14 +562,8 @@ const config: HarnessConfig = {
526
562
 
527
563
  provider: 'claude-code', // 'claude-code' | 'opencode' | 'codex-cli'
528
564
 
529
- agents: {
530
- lead: { instructionsPath: null, model: 'sonnet' }, // optional per-agent model override
531
- explorer: { instructionsPath: null, allowedPaths: ['./docs', './src'], model: 'haiku' },
532
- builder: { instructionsPath: null, writablePaths: ['./src', './tests'] },
533
- reviewer: { instructionsPath: null },
534
- consultant: { instructionsPath: null, model: 'haiku' },
535
- custom: [], // define extra agents here
536
- },
565
+ // There is no `agents` key. Per-agent settings live in the generated agent
566
+ // file itself, which is yours to edit see "Agent files are yours" below.
537
567
 
538
568
  // ── Database ──────────────────────────────────────────────────────────────
539
569
  // SQLite (default — zero native deps, Node 22+ or Bun). Note: `database`
@@ -581,6 +611,41 @@ const config: HarnessConfig = {
581
611
  export default config
582
612
  ```
583
613
 
614
+ **The JSON form** (`agent-harness-kit.config.json`, generated when the package is *not* installed locally) holds exactly the same values, minus the comments and the type annotation:
615
+
616
+ ```json
617
+ {
618
+ "project": {
619
+ "name": "My App",
620
+ "description": "What this project does",
621
+ "docsPath": "./docs"
622
+ },
623
+ "provider": "claude-code",
624
+ "database": { "type": "sqlite" },
625
+ "storage": {
626
+ "dir": ".harness",
627
+ "tasks": { "adapter": "local" },
628
+ "sections": {
629
+ "toolsUsed": true,
630
+ "filesModified": true,
631
+ "result": true,
632
+ "blockers": true,
633
+ "nextSteps": false
634
+ },
635
+ "markdownFallback": { "enabled": true, "path": ".harness/current.md" },
636
+ "scope": "local",
637
+ "projectId": "5f2c..."
638
+ },
639
+ "health": { "scriptPath": "./health.sh", "required": true },
640
+ "tools": {
641
+ "mcp": { "enabled": true, "port": 3742 },
642
+ "scripts": { "enabled": true, "outputDir": "./.harness/scripts" }
643
+ }
644
+ }
645
+ ```
646
+
647
+ Every option documented below applies to both forms — the same keys, the same defaults, the same runtime normalization. The only difference is that the JSON form has no type checking or autocompletion behind it, since there is no package to resolve them from. To switch a JSON config to TypeScript, install the package locally (`npm install --save-dev @cardor/agent-harness-kit`) and rename the file to `agent-harness-kit.config.ts`, wrapping the object as shown above. `ahk` will not convert it for you — an existing config always keeps its format.
648
+
584
649
  **`scope: 'global'`** — DB and current.md live under `~/.harness/dbs/<projectId>/`, outside the project tree. Under this scope, `sqlitePath` and `markdownFallback.path` don't exist on the type at all (a type error, not just a no-op) — there's nothing local to declare a path for:
585
650
 
586
651
  ```ts
@@ -620,32 +685,56 @@ echo "All checks passed."
620
685
 
621
686
  ### Agent definition files
622
687
 
623
- Created by `ahk init` (which preserves existing files) and **regenerated by `ahk build`** from the latest templates. If you customise an agent file, re-running `ahk build` will overwrite your editskeep customisations in source control.
688
+ **These files belong to you.** `ahk init` and `ahk build` both create them when missing and **never modify them once they exist**. Customise them freely: rewrite the role prompt, add a `model:` line, adjust the restriction fields. Nothing in the normal workflow will revert your edits, and `ahk doctor` never reports a hand-edited file as drift it checks existence only.
689
+
690
+ The trade-off is that you do not automatically receive template improvements from new versions of this package. `ahk build --force` is the only way to pull them in, and it **discards your customisations** (writing a backup to `.harness/backups/agents-<timestamp>/` first). Keep customisations in source control so you can diff against a forced regeneration.
691
+
692
+ There is no `agents` key in `agent-harness-kit.config.ts`. Per-agent settings live here, in the file itself — set the model on the `model:` frontmatter line (`model = "..."` for Codex CLI) and write role instructions in the body. When no model line is present, the provider applies its own default.
624
693
 
625
- **Claude Code** (`.claude/agents/*.md`) and **OpenCode** (`.opencode/agents/*.md`) use Markdown with YAML frontmatter:
694
+ Agent files do not declare a tool allowlist. Each agent inherits the full tool set of the session — including `Task` and every MCP tool and the file declares only what the role is *not* allowed to do. Each provider expresses that restriction in its own syntax.
695
+
696
+ **Claude Code** (`.claude/agents/*.md`) uses a `disallowedTools` YAML block sequence:
626
697
 
627
698
  ```markdown
628
699
  ---
629
- name: builder
630
- description: Builder agent — implements the plan produced by explorer and lead
631
- tools:
632
- read: true
633
- write: true
634
- edit: true
635
- bash: true
636
- permissionMode: acceptEdits
700
+ name: explorer
701
+ description: Explorer agent — reads and maps the codebase, never writes
702
+ disallowedTools:
703
+ - Write
704
+ - Edit
637
705
  ---
638
706
 
639
- # Builder Agent
707
+ # Explorer Agent
640
708
 
641
- You are the builder agent for MyApp. Follow these rules:
709
+ You are the explorer agent for MyApp. Follow these rules:
642
710
 
643
- - All API endpoints must be defined in `src/routes/`
644
- - Never modify `src/core/` without lead approval
645
- - Run `npm test` after every change and fix failures before completing
646
- - Use the existing error handling pattern from `src/lib/errors.ts`
711
+ - Map the modules relevant to the task and report where each concern lives
712
+ - Never modify files record every file you read
713
+ - Prefer the existing patterns in `src/lib/` when describing conventions
647
714
  ```
648
715
 
716
+ **OpenCode** (`.opencode/agents/*.md`) uses a `permission` mapping instead. OpenCode has no separate `write` permission — its `edit` key is defined as "file modifications including write/patch", so a single `edit: deny` covers Write, Edit, and patch:
717
+
718
+ ```markdown
719
+ ---
720
+ name: explorer
721
+ description: Explorer agent — reads and maps the codebase, never writes
722
+ permission:
723
+ edit: deny
724
+ ---
725
+
726
+ # Explorer Agent
727
+
728
+ You are the explorer agent for MyApp. Follow these rules:
729
+
730
+ - Map the modules relevant to the task and report where each concern lives
731
+ - Never modify files — record every file you read
732
+ ```
733
+
734
+ > The legacy OpenCode `tools: { write: false }` dict is deprecated upstream in favour of `permission` and is no longer emitted.
735
+
736
+ For the **builder**, which has no restrictions, the key is omitted entirely — no `disallowedTools` under Claude Code, no `permission` under OpenCode.
737
+
649
738
  **Codex CLI** (`.codex/agents/*.toml`) uses TOML format:
650
739
 
651
740
  ```toml
@@ -667,7 +756,9 @@ You are the builder agent for MyApp. Follow these rules:
667
756
  """
668
757
  ```
669
758
 
670
- The `sandbox_mode` field controls Codex's filesystem permissions per agent: `"read-only"` for lead, explorer, and reviewer; `"workspace-write"` for builder. The `permissionMode` field in Claude Code agent files enforces the same constraints at the session level (`plan` for read-only roles, `acceptEdits` for builder).
759
+ Codex CLI has no per-agent tool denylist, so `sandbox_mode` is the only real mechanism: `"read-only"` for lead, explorer, consultant, and reviewer; `"workspace-write"` for builder. Because Codex keeps the write tools *visible* to the model even under a read-only sandbox, the restriction is additionally restated in prose inside `developer_instructions` without it the model burns turns on calls the sandbox will reject.
760
+
761
+ The equivalent constraint under Claude Code is expressed as `disallowedTools: [Write, Edit]`, and under OpenCode as `permission: { edit: deny }`.
671
762
 
672
763
  ### `.harness/feature_list.json`
673
764
 
@@ -714,7 +805,7 @@ The harness exposes these tools via MCP. Agents use them instead of reading file
714
805
  | `tasks.acceptance_get` | `taskId` | Returns all acceptance criteria for a task with their `id`, `task_id`, `criterion` text, and `met` status. Use the returned `id` values with `tasks.acceptance.update` |
715
806
  | `deps.snapshot` | _(none)_ | Snapshot current `package.json` dependencies to `.harness/deps-lock.json` |
716
807
  | `deps.check` | _(none)_ | Compare current `package.json` against `.harness/deps-lock.json`. Returns `{ significant, added, removed, majorBumps, advisory }` |
717
- | `ahk.doctor` | _(none)_ | Check lib version, agent files, and harness skills sync status. Returns `{ lib: { current, latest, outdated }, agents: { missing, outdated, ok }, skills: { missing, outdated, ok } }`. The `lib` version lookup (npm registry check) is cached in-memory with a 5-minute TTL — repeated calls within that window do not hit the network again. |
808
+ | `ahk.doctor` | _(none)_ | Check lib version, agent file presence, and harness skills sync status. Returns `{ lib: { current, latest, outdated }, agents: { missing, ok }, skills: { missing, outdated, ok } }`. Agents are existence-checked only, so there is no `outdated` bucket for them; `skills` still has all three. The `lib` version lookup (npm registry check) is cached in-memory with a 5-minute TTL — repeated calls within that window do not hit the network again. |
718
809
 
719
810
  ---
720
811
 
@@ -725,12 +816,18 @@ The harness exposes these tools via MCP. Agents use them instead of reading file
725
816
  | **lead** | Decomposes the task into a plan, assigns sub-agents. Does not write code or read source files. |
726
817
  | **explorer** | Reads and maps the codebase. Never writes files. Records every file read. |
727
818
  | **consultant** | Provides structured technical advisory after explorer. Runs conditionally. Never writes code. Writes advisory to harness via actions.write. |
728
- | **builder** | Implements the plan. Only writes to `writablePaths`. Records every file modified. |
819
+ | **builder** | Implements the plan. The only role that writes its write tools are enabled where every other role's are disabled. Records every file modified. |
729
820
  | **reviewer** | Verifies all acceptance criteria are met. Approves or blocks. Runs health check before approving. |
730
821
 
822
+ > **Scope note.** What a role may not do is enforced **per tool, not per path**. There is no per-agent path scoping and it is not configurable: the `allowedPaths` / `writablePaths` fields were removed because they were only interpolated into prompt text and no provider ever enforced them — they looked like a security control without being one. The real restriction lives in `src/core/materializer/agent-restrictions.ts`, which each provider translates natively: `disallowedTools` in Claude Code, `permission.edit` in OpenCode, `sandbox_mode` in Codex CLI. If a config still declares the removed fields they are stripped at load time with a warning.
823
+ >
824
+ > **The entire `agents` config key has since been removed too**, for the same underlying reason: everything left in it was either dead or better expressed elsewhere. `instructionsPath`, `context` and `custom` were written by the generator and never read by anything; `model` was the only field with an effect, and it now belongs in the agent file's frontmatter alongside the role prompt, since that file is user-owned. A config that still declares `agents` loads normally — the key is ignored, with one aggregated warning pointing at the agent file.
825
+ >
826
+ > **Breaking change for library consumers (compile time).** The `AgentConfig`, `AgentsConfig` and `CustomAgentConfig` types are no longer exported from the package, and `HarnessConfig` no longer has an `agents` property. If you import those types, remove the import; if you construct a `HarnessConfig` in TypeScript, drop the `agents` property. This is separate from the runtime tolerance above: existing config *files* keep loading, but code that references the removed types will not compile. The `AgentName` type is unrelated and unaffected.
827
+
731
828
  ### MCP tool permissions by role
732
829
 
733
- Each agent role has a scoped set of MCP tools enforced through the agent definition files.
830
+ > **Scope note.** This table describes the *intended* division of labour between roles, not a restriction enforced by the agent files. Agent definitions no longer declare a tool allowlist, so every role inherits **all** MCP tools. The per-role `MCP_CLAUDE_PERMISSIONS_*` arrays still exist, but they are only unioned together to populate the allow list in `.claude/settings.local.json` — they are not applied per agent. Treat the table as the convention each role's prompt asks it to follow.
734
831
 
735
832
  | Tool | lead | explorer | consultant | builder | reviewer |
736
833
  | ----------------------------- | :--: | :------: | :--------: | :-----: | :------: |
@@ -754,7 +851,7 @@ Each agent role has a scoped set of MCP tools enforced through the agent definit
754
851
  **lead** and **builder** have identical access, both excluding `tasks.acceptance.update`.
755
852
  **consultant** is advisory-only — reads code, writes to harness, and can call deps tools. Never modifies the codebase.
756
853
 
757
- `permissions.check` compares each `.claude/agents/*.md` tool list against the canonical constants in the package. Returns `{ in_sync: bool, agents: { lead, explorer, consultant, builder, reviewer } }` with per-agent `missing` and `extra` arrays. Run `ahk build --sync` to fix any drift.
854
+ `permissions.check` verifies only that a `.claude/agents/*.md` definition file **exists** for every role. Returns `{ in_sync: bool, agents: { lead, explorer, consultant, builder, reviewer } }` where each agent is `{ ok: true }` or `{ ok: false, reason: 'missing_file' }`. Agent file contents are never inspected — they are meant to be customised freely — so this never reports drift, only absence. Run `ahk build` to restore a missing file.
758
855
 
759
856
  ---
760
857
 
@@ -762,7 +859,7 @@ Each agent role has a scoped set of MCP tools enforced through the agent definit
762
859
 
763
860
  | File | Commit? |
764
861
  | ----------------------------- | ------------------- |
765
- | `agent-harness-kit.config.ts` | Yes |
862
+ | `agent-harness-kit.config.{json\|ts\|mjs\|cjs}` | Yes |
766
863
  | `AGENTS.md` | Yes |
767
864
  | `CLAUDE.md` | Yes |
768
865
  | `health.sh` | Yes |
@@ -5,11 +5,6 @@ description: >
5
5
  and analyzed by explorer. The builder writes, edits, and creates files based on the plan
6
6
  and the explorer's analysis. Invoke only after the explorer has completed its action.
7
7
  Never invoke without a lead plan and explorer analysis available in actions.get(taskId).
8
- tools:
9
- - Read
10
- - Write
11
- - Edit
12
- - Bash
13
8
  ---
14
9
 
15
10
  # Builder Agent — {{projectName}}
@@ -24,11 +19,14 @@ You are the **builder agent** for `{{projectName}}`. Your job is to implement
24
19
  - Run tests after implementing to catch regressions early
25
20
  - Surface blockers clearly rather than guessing through them
26
21
 
27
- ## Writable paths
22
+ ## Scope
28
23
 
29
- You may only write to: `{{writablePaths}}`
24
+ You may write anywhere inside the project.
30
25
 
31
- Do not modify files outside these paths. If the task requires it, record a blocker and stop.
26
+ You are the only role that writes. Stay inside the project root never edit files
27
+ outside it. Breadth of access is not licence to widen scope: implement what the plan
28
+ asks and nothing more. If a change genuinely belongs outside the project root, record
29
+ a blocker and stop.
32
30
 
33
31
  ---
34
32
 
@@ -168,7 +166,7 @@ Before writing a commit message, detect whether the repo already enforces a comm
168
166
  ## Hard rules
169
167
 
170
168
  - **Read the plan and analysis first.** Never implement cold.
171
- - **Only write to `{{writablePaths}}`.** No exceptions.
169
+ - **Stay inside the project.** Never write outside the project root.
172
170
  - **Log every file you touch.** Call `actions.record_file(actionId, path, operation, notes)` after each Edit/Write.
173
171
  - **Log every tool call.** Call `actions.record_tool(actionId, toolName, args, summary)` after each Read, Edit, Write, Bash invocation.
174
172
  - **Leave tests green.** If tests fail after your changes, fix them before completing.
@@ -4,9 +4,6 @@ description: >
4
4
  Technical advisor agent for {{projectName}}. Runs after the explorer and before the builder.
5
5
  Provides structured advisory — patterns, best practices, warnings, and risks — written
6
6
  directly to the harness so the builder can read it via actions.get. Never writes code.
7
- tools:
8
- - Read
9
- - Bash
10
7
  ---
11
8
 
12
9
  # Consultant Agent — {{projectName}}
@@ -5,9 +5,6 @@ description: >
5
5
  relevant files, understands existing patterns, and produces a structured analysis for the
6
6
  builder to use. Invoke after the lead has defined a plan and before the builder starts.
7
7
  Never invoke for tasks that require writing or modifying files.
8
- tools:
9
- - Read
10
- - Bash
11
8
  ---
12
9
 
13
10
  # Explorer Agent — {{projectName}}
@@ -21,11 +18,13 @@ You are the **explorer agent** for `{{projectName}}`. Your job is to read and un
21
18
  - Search project docs for relevant guidance
22
19
  - Produce a structured analysis the builder can act on directly
23
20
 
24
- ## Allowed paths
21
+ ## Scope
25
22
 
26
- You may read files under: `{{allowedPaths}}`
23
+ You may read anything inside the project.
27
24
 
28
- If you need to read outside these paths, record that as a blocker do not proceed.
25
+ You never write. Your write tools are disabled, so do not plan changes that require
26
+ editing files — describe them for the builder instead. If a task genuinely requires
27
+ reading outside the project root, record that as a blocker — do not proceed.
29
28
 
30
29
  ---
31
30
 
@@ -5,9 +5,6 @@ description: >
5
5
  delegate to explorer, builder, and reviewer in sequence, and close the session correctly.
6
6
  Invoke when starting a new work session, picking up a pending task, or when another agent
7
7
  reports a blocker that requires re-coordination.
8
- tools:
9
- - Read
10
- - Bash
11
8
  ---
12
9
 
13
10
  # Lead Agent — {{projectName}}
@@ -73,7 +70,10 @@ When in lightweight mode:
73
70
 
74
71
  ### File creation in lightweight mode
75
72
 
76
- You may only create files in lightweight mode if the user **explicitly** asks to save the output (e.g., "write the triage report to TRIAGE.md"). Even then, do not use the full harness pipeline — just write the file directly.
73
+ Your Write and Edit tools are disabled, so you cannot save output yourself not even in
74
+ lightweight mode. If the user **explicitly** asks to persist the result (e.g., "write the
75
+ triage report to TRIAGE.md"), delegate that single write to the builder. Do not spin up the
76
+ full harness pipeline for it; hand the builder the exact content and target path.
77
77
 
78
78
  > **If in lightweight mode: skip Step 1 (Orient) entirely.** No health.sh, no MCP calls.
79
79
 
@@ -5,9 +5,6 @@ description: >
5
5
  for the current task. The reviewer reads the full action history, checks the builder's
6
6
  changes against each criterion, runs the health check, and either approves or blocks
7
7
  with specific, actionable feedback. Invoke only after the builder has completed its action.
8
- tools:
9
- - Read
10
- - Bash
11
8
  ---
12
9
 
13
10
  # Reviewer Agent — {{projectName}}
@@ -1,13 +1,14 @@
1
1
  // src/core/config.ts
2
2
  import { randomUUID } from "crypto";
3
- import { existsSync } from "fs";
3
+ import { existsSync, readFileSync } from "fs";
4
4
  import { join } from "path";
5
5
  import { createJiti } from "jiti";
6
6
  var CONFIG_NAMES = [
7
7
  "agent-harness-kit.config.ts",
8
8
  "agent-harness-kit.config",
9
9
  "agent-harness-kit.config.mjs",
10
- "agent-harness-kit.config.cjs"
10
+ "agent-harness-kit.config.cjs",
11
+ "agent-harness-kit.config.json"
11
12
  ];
12
13
  function findConfigFile(cwd) {
13
14
  for (const name of CONFIG_NAMES) {
@@ -21,10 +22,27 @@ async function loadConfig(cwd) {
21
22
  if (!configPath) {
22
23
  throw new Error("No agent-harness-kit.config found. Run: ahk init");
23
24
  }
24
- const jiti = createJiti(import.meta.url);
25
- const mod = await jiti.import(configPath);
26
- const config = mod.default ?? mod;
27
- if (!config || typeof config !== "object") {
25
+ let config;
26
+ if (configPath.endsWith(".json")) {
27
+ let raw;
28
+ try {
29
+ raw = readFileSync(configPath, "utf8");
30
+ } catch (err) {
31
+ throw new Error(`Could not read ${configPath}: ${err instanceof Error ? err.message : String(err)}`);
32
+ }
33
+ try {
34
+ config = JSON.parse(raw);
35
+ } catch (err) {
36
+ throw new Error(
37
+ `${configPath} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`
38
+ );
39
+ }
40
+ } else {
41
+ const jiti = createJiti(import.meta.url);
42
+ const mod = await jiti.import(configPath);
43
+ config = mod.default ?? mod;
44
+ }
45
+ if (!config || typeof config !== "object" || Array.isArray(config)) {
28
46
  throw new Error(`agent-harness-kit.config must export a default HarnessConfig object.`);
29
47
  }
30
48
  return applyDefaults(config);
@@ -59,8 +77,20 @@ function normalizeLegacyStorageShape(raw) {
59
77
  );
60
78
  return { ...raw, storage: normalizedStorage, database: normalizedDatabase ?? database };
61
79
  }
80
+ function normalizeLegacyAgentsKey(raw) {
81
+ if (!("agents" in raw)) return raw;
82
+ const agents = raw.agents;
83
+ const roles = agents && typeof agents === "object" && !Array.isArray(agents) ? Object.keys(agents) : [];
84
+ const normalized = Object.fromEntries(Object.entries(raw).filter(([k]) => k !== "agents"));
85
+ console.warn(
86
+ `[agent-harness-kit] The 'agents' key is set in agent-harness-kit.config.ts${roles.length > 0 ? ` (${roles.map((r) => `agents.${r}`).join(", ")})` : ""} but no longer has any effect \u2014 it has been removed and is ignored. Per-agent settings now live in the generated agent file itself, which is yours to edit: set the model on the 'model:' frontmatter line and write role instructions in the body of .claude/agents/<role>.md (Claude Code), .opencode/agents/<role>.md (OpenCode) or .codex/agents/<role>.toml (Codex CLI). 'ahk build' creates those files when missing and never overwrites them; use 'ahk build --force' to regenerate them from the packaged templates. Remove the 'agents' key from your config. See docs/architecture.md#agent-restrictions.`
87
+ );
88
+ return normalized;
89
+ }
62
90
  function applyDefaults(config) {
63
- const normalized = normalizeLegacyStorageShape(config);
91
+ const normalized = normalizeLegacyAgentsKey(
92
+ normalizeLegacyStorageShape(config)
93
+ );
64
94
  const c = normalized;
65
95
  const scope = c.storage?.scope === "global" ? "global" : "local";
66
96
  const projectId = c.storage?.projectId ?? randomUUID();
@@ -97,14 +127,6 @@ function applyDefaults(config) {
97
127
  agentsMd: "./AGENTS.md",
98
128
  ...c.project
99
129
  },
100
- agents: {
101
- lead: { instructionsPath: null },
102
- explorer: { instructionsPath: null },
103
- builder: { instructionsPath: null },
104
- reviewer: { instructionsPath: null },
105
- custom: [],
106
- ...c.agents
107
- },
108
130
  database: c.database ?? { type: "sqlite" },
109
131
  storage,
110
132
  health: {
@@ -125,4 +147,4 @@ export {
125
147
  loadConfig,
126
148
  defineHarness
127
149
  };
128
- //# sourceMappingURL=chunk-D64KK6UU.js.map
150
+ //# sourceMappingURL=chunk-U3O77CGE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/config.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto'\nimport { existsSync, readFileSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { createJiti } from 'jiti'\n\nimport type { HarnessConfig } from '@/types'\n\n/** Order is precedence: the first file that exists wins. `.json` is appended\n * last so adding it cannot change which config an existing project resolves\n * to — a project that already has a .ts/.mjs/.cjs config keeps loading it. */\nconst CONFIG_NAMES = [\n 'agent-harness-kit.config.ts',\n 'agent-harness-kit.config',\n 'agent-harness-kit.config.mjs',\n 'agent-harness-kit.config.cjs',\n 'agent-harness-kit.config.json',\n]\n\nexport function findConfigFile(cwd: string): string | null {\n for (const name of CONFIG_NAMES) {\n const candidate = join(cwd, name)\n if (existsSync(candidate)) return candidate\n }\n return null\n}\n\nexport async function loadConfig(cwd: string): Promise<HarnessConfig> {\n const configPath = findConfigFile(cwd)\n if (!configPath) {\n throw new Error('No agent-harness-kit.config found. Run: ahk init')\n }\n\n let config: HarnessConfig\n\n if (configPath.endsWith('.json')) {\n // Read and parse directly rather than going through jiti: a JSON config is\n // pure data with no module semantics to interpret, and parsing it here lets\n // a syntax error name the file and the reason instead of surfacing as an\n // opaque module-resolution failure.\n let raw: string\n try {\n raw = readFileSync(configPath, 'utf8')\n } catch (err) {\n throw new Error(`Could not read ${configPath}: ${err instanceof Error ? err.message : String(err)}`)\n }\n try {\n config = JSON.parse(raw) as HarnessConfig\n } catch (err) {\n throw new Error(\n `${configPath} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,\n )\n }\n } else {\n const jiti = createJiti(import.meta.url)\n const mod = await jiti.import(configPath) as { default?: HarnessConfig } | HarnessConfig\n config = (mod as { default?: HarnessConfig }).default ?? (mod as HarnessConfig)\n }\n\n if (!config || typeof config !== 'object' || Array.isArray(config)) {\n throw new Error(`agent-harness-kit.config must export a default HarnessConfig object.`)\n }\n\n // applyDefaults() runs the same normalizers (normalizeLegacyStorageShape,\n // normalizeLegacyAgentsKey) for every format — a JSON config carrying a\n // legacy `agents` key or a contradictory global-scope path is normalized\n // and warned about exactly like its .ts/.mjs/.cjs counterpart.\n return applyDefaults(config as HarnessConfig)\n}\n\nexport function defineHarness(config: HarnessConfig): HarnessConfig {\n return config\n}\n\n/** Detects and normalizes the legacy contradictory config shape: `scope:\n * 'global'` declared alongside now-meaningless local-only path fields\n * (`database.path` / `storage.sqlitePath`, `storage.markdownFallback.path`).\n *\n * This is necessary IN ADDITION to the type-level redesign (not instead of\n * it) because `loadConfig()` loads `agent-harness-kit.config.ts` via\n * `jiti.import()` at runtime, which transpiles TS to JS and strips types\n * entirely before the module is ever evaluated — a hard type error on\n * `GlobalStorageConfig` protects authors who type-check their config file\n * (IDE, `tsc --noEmit`), but gives ZERO protection against an existing\n * config on disk that already has both fields set. Operates on the RAW\n * untyped input (may not conform to the new types at all) and returns a\n * normalized (stripped) plain object — never crashes, only warns. */\nfunction normalizeLegacyStorageShape(raw: Record<string, unknown>): Record<string, unknown> {\n const storage = raw.storage as Record<string, unknown> | undefined\n const database = raw.database as Record<string, unknown> | undefined\n if (!storage || storage.scope !== 'global') return raw\n\n const offenders: string[] = []\n let normalizedStorage = storage\n let normalizedDatabase = database\n\n const omit = (obj: Record<string, unknown>, key: string): Record<string, unknown> =>\n Object.fromEntries(Object.entries(obj).filter(([k]) => k !== key))\n\n if (database && typeof database.path === 'string' && database.path) {\n offenders.push('database.path')\n normalizedDatabase = omit(database, 'path')\n }\n if (typeof storage.sqlitePath === 'string' && storage.sqlitePath) {\n offenders.push('storage.sqlitePath')\n normalizedStorage = omit(normalizedStorage, 'sqlitePath')\n }\n const markdownFallback = normalizedStorage.markdownFallback as Record<string, unknown> | undefined\n if (markdownFallback && typeof markdownFallback.path === 'string' && markdownFallback.path) {\n offenders.push('storage.markdownFallback.path')\n normalizedStorage = { ...normalizedStorage, markdownFallback: omit(markdownFallback, 'path') }\n }\n\n if (offenders.length === 0) return raw\n\n console.warn(\n `[agent-harness-kit] storage.scope is 'global' but ${offenders.join(', ')} ${offenders.length > 1 ? 'are' : 'is'} set in ` +\n `agent-harness-kit.config.ts — ${offenders.length > 1 ? 'these are' : 'this is'} ignored under global scope and will be ` +\n `removed by a future major version. See docs/architecture.md#storage-scope.`,\n )\n\n return { ...raw, storage: normalizedStorage, database: normalizedDatabase ?? database }\n}\n\n/** Detects and strips the removed `agents` config key entirely.\n *\n * Supersedes the narrower normalizer that only stripped `allowedPaths` /\n * `writablePaths` from `agents.*`: with the whole key gone, per-field\n * stripping is subsumed — a config declaring `agents.explorer.allowedPaths`\n * loses it because it loses `agents` altogether.\n *\n * Same rationale as `normalizeLegacyStorageShape` above: deleting the key from\n * `HarnessConfig` protects authors who type-check their config file, but\n * `loadConfig()` imports the config via `jiti.import()`, which strips types\n * before evaluation — so an existing config on disk that still declares\n * `agents` reaches us untouched. It must not crash; the key is simply dropped.\n *\n * Operates on the RAW untyped input and returns a normalized plain object —\n * never crashes, only warns, exactly once, no matter how many roles or fields\n * the old config declared. */\nfunction normalizeLegacyAgentsKey(raw: Record<string, unknown>): Record<string, unknown> {\n if (!('agents' in raw)) return raw\n\n const agents = raw.agents\n const roles =\n agents && typeof agents === 'object' && !Array.isArray(agents)\n ? Object.keys(agents as Record<string, unknown>)\n : []\n\n const normalized = Object.fromEntries(Object.entries(raw).filter(([k]) => k !== 'agents'))\n\n console.warn(\n `[agent-harness-kit] The 'agents' key is set in agent-harness-kit.config.ts` +\n `${roles.length > 0 ? ` (${roles.map((r) => `agents.${r}`).join(', ')})` : ''} ` +\n `but no longer has any effect — it has been removed and is ignored. ` +\n `Per-agent settings now live in the generated agent file itself, which is yours to edit: ` +\n `set the model on the 'model:' frontmatter line and write role instructions in the body of ` +\n `.claude/agents/<role>.md (Claude Code), .opencode/agents/<role>.md (OpenCode) or ` +\n `.codex/agents/<role>.toml (Codex CLI). 'ahk build' creates those files when missing and never ` +\n `overwrites them; use 'ahk build --force' to regenerate them from the packaged templates. ` +\n `Remove the 'agents' key from your config. See docs/architecture.md#agent-restrictions.`,\n )\n\n return normalized\n}\n\nfunction applyDefaults(config: HarnessConfig): HarnessConfig {\n const normalized = normalizeLegacyAgentsKey(\n normalizeLegacyStorageShape(config as unknown as Record<string, unknown>),\n )\n const c = normalized as Partial<HarnessConfig>\n\n const scope: 'local' | 'global' = c.storage?.scope === 'global' ? 'global' : 'local'\n const projectId = c.storage?.projectId ?? randomUUID()\n const baseStorage = {\n dir: '.harness',\n tasks: { adapter: 'local' as const },\n sections: {\n toolsUsed: true,\n filesModified: true,\n result: true,\n blockers: true,\n nextSteps: false,\n },\n }\n\n const storageOverrides = (c.storage ?? {}) as Record<string, unknown>\n\n const storage: HarnessConfig['storage'] =\n scope === 'global'\n ? ({\n ...baseStorage,\n markdownFallback: { enabled: true },\n ...storageOverrides,\n scope: 'global',\n projectId,\n } as HarnessConfig['storage'])\n : ({\n ...baseStorage,\n markdownFallback: { enabled: true, path: '.harness/current.md' },\n ...storageOverrides,\n scope: 'local',\n projectId,\n } as HarnessConfig['storage'])\n\n return {\n ...(normalized as unknown as HarnessConfig),\n provider: c.provider ?? 'claude-code',\n project: {\n docsPath: './docs',\n agentsMd: './AGENTS.md',\n ...c.project,\n } as HarnessConfig['project'],\n database: c.database ?? { type: 'sqlite' as const },\n storage,\n health: {\n scriptPath: './health.sh',\n required: true,\n ...c.health,\n },\n tools: {\n mcp: { enabled: true, port: 3742 },\n scripts: { enabled: true, outputDir: './.harness/scripts' },\n ...c.tools,\n } as HarnessConfig['tools'],\n }\n}\n"],"mappings":"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","names":[]}