@cardor/agent-harness-kit 0.18.0 → 1.1.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
@@ -62,8 +62,10 @@ Everything is stored locally in a SQLite database (`.harness/harness.db`). No cl
62
62
  - **Health gate** — agents must run `health.sh` and get a green exit before starting or closing any task. You define what "healthy" means.
63
63
  - **Markdown fallback** — `current.md` is always regenerated so agents can understand the session state even without the MCP server.
64
64
  - **Docs search** — agents can call `docs.search(query)` to find relevant content in your project's docs folder before writing code.
65
- - **Zero native dependencies** — uses `node:sqlite` (Node ≥ 22) or `bun:sqlite` (Bun) built-in. No native compilation, no `node-gyp`.
65
+ - **Multi-database support** — SQLite by default (zero native deps, uses `node:sqlite` 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.
66
66
  - **Incremental scaffold** — `ahk init` and `ahk build` never overwrite files you've customized. Agent definitions you've edited are preserved.
67
+ - **Global installation** — `ahk init` can scaffold the harness into your home directory (`~/.claude` or `~/.config/opencode`) to share it across all projects.
68
+ - **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.
67
69
 
68
70
  ---
69
71
 
@@ -98,7 +100,7 @@ ahk init
98
100
 
99
101
  ### `ahk init`
100
102
 
101
- Interactive scaffold. Asks for your project name, AI provider, docs path, task adapter, and an optional first task. Creates all harness files in the current directory.
103
+ Interactive scaffold. Asks for your project name, description, AI provider, whether to install globally, docs path, task adapter, and an optional first task. Creates all harness files in the current directory (or home directory if global).
102
104
 
103
105
  ```bash
104
106
  ahk init
@@ -109,6 +111,8 @@ ahk init --name "my-app" --provider claude-code --docs ./docs --tasks local
109
111
 
110
112
  Run this once per project. Safe to re-run — it will not overwrite files you've customized.
111
113
 
114
+ **Global installation** — if you answer yes to "Install globally?", files go to `~/.claude` (Claude Code) or `~/.config/opencode` (OpenCode). This lets you share one harness config across all your projects.
115
+
112
116
  ---
113
117
 
114
118
  ### `ahk build`
@@ -230,6 +234,26 @@ ahk task done add-auth-flow
230
234
 
231
235
  ---
232
236
 
237
+ ### `ahk reset`
238
+
239
+ Clears harness data interactively. Only SQLite databases are managed by this command — remote Postgres/MySQL databases are intentionally skipped.
240
+
241
+ ```bash
242
+ ahk reset # interactive — asks before deleting each item
243
+ ahk reset --force # skip all confirmation prompts
244
+ ahk reset --provider claude-code # also delete agent .md files for this provider
245
+ ahk reset --provider opencode
246
+ ```
247
+
248
+ What it can reset:
249
+ - The SQLite `.db` file (plus WAL and SHM files if present)
250
+ - `.harness/feature_list.json`
251
+ - Agent `.md` files in `.claude/agents/` or `.opencode/agents/`
252
+
253
+ After a reset, run `ahk init` to scaffold a fresh harness.
254
+
255
+ ---
256
+
233
257
  ### `ahk migrate`
234
258
 
235
259
  Migrates provider-specific files from one AI provider to another. Useful when switching from Claude Code to OpenCode or vice versa.
@@ -315,10 +339,19 @@ export default defineHarness({
315
339
  custom: [], // define extra agents here
316
340
  },
317
341
 
342
+ // ── Database ──────────────────────────────────────────────────────────────
343
+ // SQLite (default — zero native deps, Node 22+ or Bun)
344
+ database: { type: 'sqlite', path: '.harness/harness.db' },
345
+
346
+ // PostgreSQL — uncomment to use instead:
347
+ // database: { type: 'postgres', connectionString: process.env.DATABASE_URL },
348
+
349
+ // MySQL — uncomment to use instead:
350
+ // database: { type: 'mysql', connectionString: process.env.DATABASE_URL },
351
+
318
352
  storage: {
319
- dir: '.harness',
320
- dbPath: '.harness/harness.db',
321
- tasks: { adapter: 'local' },
353
+ dir: '.harness',
354
+ tasks: { adapter: 'local' }, // 'local' | 'jira' | 'linear' | 'mcp'
322
355
  sections: {
323
356
  toolsUsed: true, // log which tools agents used
324
357
  filesModified: true, // log which files were touched
@@ -335,7 +368,7 @@ export default defineHarness({
335
368
  },
336
369
 
337
370
  tools: {
338
- mcp: { enabled: true, port: 3456 },
371
+ mcp: { enabled: true, port: 3742 },
339
372
  scripts: { enabled: true, outputDir: './.harness/scripts' },
340
373
  },
341
374
  })
@@ -418,10 +451,14 @@ The harness exposes these tools via MCP. Agents use them instead of reading file
418
451
  | `tasks.get` | `status?` | List tasks, optionally filtered by `pending \| in_progress \| done \| blocked` |
419
452
  | `tasks.claim` | `id, agent` | Atomically claim a pending task. Returns `task_already_claimed` if another agent got it first |
420
453
  | `tasks.update` | `id, status` | Change task status |
454
+ | `tasks.add` | `title, slug?, description?, acceptance?` | Create a new task directly from MCP (agents can queue work on the fly) |
455
+ | `tasks.acceptance.update` | `criterionId` | Mark an acceptance criterion as met. Criterion IDs come from `tasks.get` |
421
456
  | `actions.start` | `taskId, agent` | Start a new action, returns `actionId` |
422
- | `actions.write` | `actionId, sectionType, content` | Record a section: `result \| tools_used \| files_modified \| blockers \| next_steps` |
457
+ | `actions.write` | `actionId, sectionType, content` | Record a text section: `result \| tools_used \| blockers \| next_steps`. Does **not** populate the Files dashboard — use `actions.record_file` for that |
423
458
  | `actions.complete` | `actionId, summary` | Close an action with a one-line summary |
424
459
  | `actions.get` | `taskId` | Full action history for a task (all agents, all sections) |
460
+ | `actions.record_file` | `actionId, filePath, operation, notes?` | Register a file touch. The **only** way to populate the Files dashboard. `operation`: `read \| created \| modified \| deleted` |
461
+ | `actions.record_tool` | `actionId, toolName, argsJson?, resultSummary?` | Register a tool call. The **only** way to populate the Tools dashboard |
425
462
  | `docs.search` | `query` | Search the `docsPath` folder for content matching the query |
426
463
 
427
464
  ---
@@ -456,11 +493,18 @@ The rule: commit inputs (config, task definitions, agent instructions). Ignore o
456
493
 
457
494
  ## Runtime compatibility
458
495
 
459
- | Runtime | Support |
460
- |---------|---------|
461
- | Node.js ≥ 22 | ✅ uses `node:sqlite` built-in |
462
- | Bun (any recent) | ✅ uses `bun:sqlite` built-in |
463
- | Node.js < 22 | ❌ `node:sqlite` not available |
496
+ | Runtime | SQLite | PostgreSQL | MySQL |
497
+ |---------|--------|-----------|-------|
498
+ | Node.js ≥ 22 | ✅ uses `node:sqlite` built-in | ✅ via `postgres` package | ✅ via `mysql2` package |
499
+ | Bun (any recent) | ✅ uses `bun:sqlite` built-in | ✅ via `postgres` package | ✅ via `mysql2` package |
500
+ | Node.js < 22 | ❌ `node:sqlite` not available | ✅ | ✅ |
501
+
502
+ SQLite requires no additional packages. For PostgreSQL install `postgres`, for MySQL install `mysql2`:
503
+
504
+ ```bash
505
+ npm install postgres # for PostgreSQL
506
+ npm install mysql2 # for MySQL
507
+ ```
464
508
 
465
509
  ---
466
510
 
@@ -475,9 +519,39 @@ npm run build:ui # build the dashboard SPA (dashboard/ → src/dashboard-dist
475
519
  npm run build # build:ui + tsc + copy-assets
476
520
  npm run dev # watch mode (CLI TypeScript only)
477
521
  npm test # run tests
478
- npm link # register ahk globally from local source
479
522
  ```
480
523
 
524
+ ### Testing the local build in another project
525
+
526
+ Use the helper script to build the package and link it into any local project in one step:
527
+
528
+ ```bash
529
+ # Build + link into a specific project
530
+ ./scripts/link-local.sh /path/to/your-other-project
531
+
532
+ # Build + register globally only (then link manually wherever you need)
533
+ ./scripts/link-local.sh
534
+ ```
535
+
536
+ What the script does:
537
+
538
+ 1. Runs `npm run build` (full build including dashboard assets)
539
+ 2. Runs `npm link` to register the package globally on your machine
540
+ 3. Runs `npm link @cardor/agent-harness-kit` inside the target project
541
+ 4. Smoke-tests the `ahk` binary with `--version`
542
+
543
+ After linking, `npx ahk` inside the target project will use your local build. To unlink when you're done:
544
+
545
+ ```bash
546
+ # Inside the target project
547
+ npm unlink @cardor/agent-harness-kit
548
+
549
+ # Optionally remove the global registration
550
+ npm uninstall -g @cardor/agent-harness-kit
551
+ ```
552
+
553
+ > **Tip:** If you're iterating quickly, run `npm run build` in this repo after each change — the link picks up the new `dist/` immediately without re-running the script.
554
+
481
555
  To work on the dashboard UI with hot reload:
482
556
 
483
557
  ```bash
@@ -502,9 +576,16 @@ Types: `feat fix chore refactor docs test perf style build ci revert`
502
576
 
503
577
  ## Roadmap
504
578
 
505
- - ✅ **`ahk dashboard`** — local web UI with real-time WebSocket updates. Shows tasks, action timelines, file activity, tool usage, and per-agent breakdowns. Run `ahk dashboard` in any initialized project.
506
- - **Open Telemetry integration**emit OpenTelemetry spans for all agent actions, file operations, and tool calls. Enables distributed tracing and integration with any OT-compatible observability platform.
507
- - **Jira task adapter** — pull tasks directly from Jira instead of maintaining `feature_list.json` manually. The `tasks.adapter` config key is already wired for this.
579
+ - ✅ **`ahk dashboard`** — local web UI with real-time WebSocket updates. Shows tasks, action timelines, file activity, tool usage, and per-agent breakdowns.
580
+ - **`ahk reset`**interactively clear the SQLite DB, feature list, and agent files to start a project fresh.
581
+ - **PostgreSQL + MySQL drivers** — remote database support via `postgres` and `mysql2` packages. Configure with `database: { type: 'postgres', connectionString: '...' }`.
582
+ - ✅ **`actions.record_file` + `actions.record_tool`** — dedicated MCP tools for populating the Files and Tools dashboard views.
583
+ - ✅ **`tasks.add` via MCP** — agents can create new tasks on the fly without leaving the conversation.
584
+ - ✅ **Global installation** — `ahk init` can install the harness to your home directory, shared across projects.
585
+ - ✅ **Input validation** — all CLI prompts validate and retry on bad values.
586
+ - **Graphify integration** — connect the harness to Graphify to visualize agent workflows, task dependencies, and action timelines as interactive graphs.
587
+ - **Open Telemetry integration** — emit OpenTelemetry spans for all agent actions, file operations, and tool calls.
588
+ - **Jira task adapter** — pull tasks directly from Jira instead of maintaining `feature_list.json` manually.
508
589
  - **Linear task adapter** — same as Jira, for Linear.
509
590
  - **GitHub Issues adapter** — same, for GitHub Issues.
510
591
  - **Remote MCP adapter** — connect to a hosted MCP server instead of a local SQLite file. Enables shared task state across machines and team members without syncing a DB file.
@@ -66,7 +66,24 @@ tasks.get('pending') → pick the task with the lowest id
66
66
 
67
67
  If `.harness/current.md` is available and MCP is unreachable, read it as fallback.
68
68
 
69
- ### 2. Claim the task
69
+ ### 2. Find or create a task
70
+
71
+ **If pending tasks exist:** pick the one with the lowest id.
72
+
73
+ **If no pending tasks exist:** ask the user what they want to work on. From their reply, infer:
74
+ - `title` — short, action-oriented phrase
75
+ - `description` — goal and context
76
+ - `acceptance` — list of measurable criteria
77
+
78
+ If any of the above are unclear or missing, ask before proceeding. Then create the task:
79
+
80
+ ```
81
+ tasks.add(title, slug?, description?, acceptance[])
82
+ ```
83
+
84
+ The returned task id is what you pass to `tasks.claim` below.
85
+
86
+ ### 3. Claim the task
70
87
 
71
88
  ```
72
89
  tasks.claim(id)
@@ -74,13 +91,13 @@ tasks.claim(id)
74
91
 
75
92
  If response is `task_already_claimed` → pick the next pending task. Never steal a claimed task.
76
93
 
77
- ### 3. Register your action
94
+ ### 4. Register your action
78
95
 
79
96
  ```
80
97
  actions.start(taskId, 'lead') → save the returned actionId
81
98
  ```
82
99
 
83
- ### 4. Write a decomposition plan
100
+ ### 5. Write a decomposition plan
84
101
 
85
102
  Think through:
86
103
  - What does the explorer need to map?
@@ -95,13 +112,13 @@ actions.write(actionId, 'result', '<your structured plan>')
95
112
 
96
113
  Format your plan clearly — the other agents will read it via `actions.get(taskId)`.
97
114
 
98
- ### 5. Complete your action
115
+ ### 6. Complete your action
99
116
 
100
117
  ```
101
118
  actions.complete(actionId, 'Plan defined — delegating to explorer')
102
119
  ```
103
120
 
104
- ### 6. Delegate in order
121
+ ### 7. Delegate in order
105
122
 
106
123
  Invoke: **Explorer** → **Builder** → **Reviewer**
107
124
 
@@ -110,7 +127,7 @@ After each agent completes, read their output:
110
127
  actions.get(taskId) → read the latest completed action and its sections
111
128
  ```
112
129
 
113
- ### 7. Handle a Reviewer block
130
+ ### 8. Handle a Reviewer block
114
131
 
115
132
  If the reviewer blocks the task:
116
133
  1. Read the `blockers` section from the reviewer's action
@@ -118,7 +135,7 @@ If the reviewer blocks the task:
118
135
  3. After the builder completes the fix, re-invoke the reviewer
119
136
  4. Do NOT mark the task done until the reviewer explicitly approves
120
137
 
121
- ### 8. Close the session
138
+ ### 9. Close the session
122
139
 
123
140
  Once the reviewer approves:
124
141
  ```
@@ -48,9 +48,9 @@ function applyDefaults(config) {
48
48
  custom: [],
49
49
  ...c.agents
50
50
  },
51
+ database: c.database ?? { type: "sqlite", path: ".harness/harness.db" },
51
52
  storage: {
52
53
  dir: ".harness",
53
- dbPath: ".harness/harness.db",
54
54
  tasks: { adapter: "local" },
55
55
  sections: {
56
56
  toolsUsed: true,
@@ -79,4 +79,4 @@ export {
79
79
  loadConfig,
80
80
  defineHarness
81
81
  };
82
- //# sourceMappingURL=chunk-LQ7SDMK6.js.map
82
+ //# sourceMappingURL=chunk-X7FOJOZB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/config.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { createJiti } from 'jiti'\n\nimport type { HarnessConfig } from '@/types'\n\nconst CONFIG_NAMES = [\n 'agent-harness-kit.config.ts',\n 'agent-harness-kit.config',\n 'agent-harness-kit.config.mjs',\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 const jiti = createJiti(import.meta.url)\n const mod = await jiti.import(configPath) as { default?: HarnessConfig } | HarnessConfig\n const config = (mod as { default?: HarnessConfig }).default ?? (mod as HarnessConfig)\n\n if (!config || typeof config !== 'object') {\n throw new Error(`agent-harness-kit.config must export a default HarnessConfig object.`)\n }\n\n return applyDefaults(config as HarnessConfig)\n}\n\nexport function defineHarness(config: HarnessConfig): HarnessConfig {\n return config\n}\n\nfunction applyDefaults(config: HarnessConfig): HarnessConfig {\n const c = config as Partial<HarnessConfig>\n return {\n ...config,\n provider: c.provider ?? 'claude-code',\n project: {\n docsPath: './docs',\n agentsMd: './AGENTS.md',\n ...c.project,\n } as HarnessConfig['project'],\n agents: {\n lead: { instructionsPath: null },\n explorer: { instructionsPath: null },\n builder: { instructionsPath: null },\n reviewer: { instructionsPath: null },\n custom: [],\n ...c.agents,\n } as HarnessConfig['agents'],\n database: c.database ?? { type: 'sqlite' as const, path: '.harness/harness.db' },\n storage: {\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 markdownFallback: { enabled: true, path: '.harness/current.md' },\n ...c.storage,\n } as HarnessConfig['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":";AAAA,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAI3B,IAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,eAAe,KAA4B;AACzD,aAAW,QAAQ,cAAc;AAC/B,UAAM,YAAY,KAAK,KAAK,IAAI;AAChC,QAAI,WAAW,SAAS,EAAG,QAAO;AAAA,EACpC;AACA,SAAO;AACT;AAEA,eAAsB,WAAW,KAAqC;AACpE,QAAM,aAAa,eAAe,GAAG;AACrC,MAAI,CAAC,YAAY;AACf,UAAM,IAAI,MAAM,kDAAkD;AAAA,EACpE;AAEA,QAAM,OAAO,WAAW,YAAY,GAAG;AACvC,QAAM,MAAM,MAAM,KAAK,OAAO,UAAU;AACxC,QAAM,SAAU,IAAoC,WAAY;AAEhE,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,UAAM,IAAI,MAAM,sEAAsE;AAAA,EACxF;AAEA,SAAO,cAAc,MAAuB;AAC9C;AAEO,SAAS,cAAc,QAAsC;AAClE,SAAO;AACT;AAEA,SAAS,cAAc,QAAsC;AAC3D,QAAM,IAAI;AACV,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU,EAAE,YAAY;AAAA,IACxB,SAAS;AAAA,MACP,UAAU;AAAA,MACV,UAAU;AAAA,MACV,GAAG,EAAE;AAAA,IACP;AAAA,IACA,QAAQ;AAAA,MACN,MAAM,EAAE,kBAAkB,KAAK;AAAA,MAC/B,UAAU,EAAE,kBAAkB,KAAK;AAAA,MACnC,SAAS,EAAE,kBAAkB,KAAK;AAAA,MAClC,UAAU,EAAE,kBAAkB,KAAK;AAAA,MACnC,QAAQ,CAAC;AAAA,MACT,GAAG,EAAE;AAAA,IACP;AAAA,IACA,UAAU,EAAE,YAAY,EAAE,MAAM,UAAmB,MAAM,sBAAsB;AAAA,IAC/E,SAAS;AAAA,MACP,KAAK;AAAA,MACL,OAAO,EAAE,SAAS,QAAiB;AAAA,MACnC,UAAU;AAAA,QACR,WAAW;AAAA,QACX,eAAe;AAAA,QACf,QAAQ;AAAA,QACR,UAAU;AAAA,QACV,WAAW;AAAA,MACb;AAAA,MACA,kBAAkB,EAAE,SAAS,MAAM,MAAM,sBAAsB;AAAA,MAC/D,GAAG,EAAE;AAAA,IACP;AAAA,IACA,QAAQ;AAAA,MACN,YAAY;AAAA,MACZ,UAAU;AAAA,MACV,GAAG,EAAE;AAAA,IACP;AAAA,IACA,OAAO;AAAA,MACL,KAAK,EAAE,SAAS,MAAM,MAAM,KAAK;AAAA,MACjC,SAAS,EAAE,SAAS,MAAM,WAAW,qBAAqB;AAAA,MAC1D,GAAG,EAAE;AAAA,IACP;AAAA,EACF;AACF;","names":[]}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }