@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 +142 -45
- package/dist/agent-templates/builder.md +7 -9
- package/dist/agent-templates/consultant.md +0 -3
- package/dist/agent-templates/explorer.md +5 -6
- package/dist/agent-templates/lead.md +4 -4
- package/dist/agent-templates/reviewer.md +0 -3
- package/dist/{chunk-D64KK6UU.js → chunk-U3O77CGE.js} +38 -16
- package/dist/chunk-U3O77CGE.js.map +1 -0
- package/dist/cli.js +500 -571
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -23
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-D64KK6UU.js.map +0 -1
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-
|
|
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`
|
|
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
|
-
> **
|
|
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 --
|
|
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
|
|
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** —
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
630
|
-
description:
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
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
|
-
#
|
|
707
|
+
# Explorer Agent
|
|
640
708
|
|
|
641
|
-
You are the
|
|
709
|
+
You are the explorer agent for MyApp. Follow these rules:
|
|
642
710
|
|
|
643
|
-
-
|
|
644
|
-
- Never modify
|
|
645
|
-
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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`
|
|
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
|
-
##
|
|
22
|
+
## Scope
|
|
28
23
|
|
|
29
|
-
You may
|
|
24
|
+
You may write anywhere inside the project.
|
|
30
25
|
|
|
31
|
-
|
|
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
|
-
- **
|
|
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
|
-
##
|
|
21
|
+
## Scope
|
|
25
22
|
|
|
26
|
-
You may read
|
|
23
|
+
You may read anything inside the project.
|
|
27
24
|
|
|
28
|
-
|
|
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
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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 =
|
|
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-
|
|
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":[]}
|