@dombaras/agent-harness 0.1.11 → 0.1.12
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 +15 -8
- package/bin/agent-harness.js +52 -14
- package/package.json +1 -1
- package/templates/.opencode/command/board.md +14 -16
package/README.md
CHANGED
|
@@ -11,8 +11,9 @@ and QA gates — then deploys them into any project.
|
|
|
11
11
|
|
|
12
12
|
## Why
|
|
13
13
|
|
|
14
|
-
- The agent harness is tooling, not application code.
|
|
15
|
-
|
|
14
|
+
- The agent harness is tooling, not application code. Its source lives in the
|
|
15
|
+
npm package — you deploy it with `init`/`update` rather than hand-copying it
|
|
16
|
+
into every repo.
|
|
16
17
|
- One canonical, versioned source of truth for personas/rules/QA tiers, deployed
|
|
17
18
|
on demand and updatable via `npx @dombaras/agent-harness update`.
|
|
18
19
|
|
|
@@ -44,6 +45,7 @@ npx @dombaras/agent-harness init --target . --dry-run
|
|
|
44
45
|
| `scripts/qa/*` | `test:dispatch` / `test:governance` / `test:qa-plan` gates + QA-script wiring check | harness (overwrite) |
|
|
45
46
|
| `.agents/memory/*` | project data (domain-map, stack-versions, handoff, locations, model-routing, history, flow-map, qa-plan) | **project** (create-if-missing) |
|
|
46
47
|
| `.harness.json` | deployed version + project profile + per-file checksums | harness |
|
|
48
|
+
| `.gitignore` | harness-managed section (`.harness-backup/`) merged in, project entries preserved | harness (merged) |
|
|
47
49
|
|
|
48
50
|
### `opencode.json` is merged, not clobbered
|
|
49
51
|
|
|
@@ -109,10 +111,9 @@ warns but does not block; the guarantee only applies to registered flows.
|
|
|
109
111
|
`opencode/deepseek-v4-pro` (gateway), `pickle` → `opencode/big-pickle`, or any
|
|
110
112
|
explicit `provider/model`. Restart opencode after switching (config reads once).
|
|
111
113
|
- **`/board` command** — every deploy ships an opencode command
|
|
112
|
-
(`.opencode/command/board.md`) that
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
delta. Read-only — it never edits the board.
|
|
114
|
+
(`.opencode/command/board.md`) that reads the local `BACKLOG.md` and renders a
|
|
115
|
+
clean task list (Priority · Title · Short description). Read-only, no git — the
|
|
116
|
+
local file is the source of truth.
|
|
116
117
|
|
|
117
118
|
## QA gates
|
|
118
119
|
|
|
@@ -149,11 +150,13 @@ npx @dombaras/agent-harness update --target /path/to/project
|
|
|
149
150
|
- Overwrites harness-owned files, preserves `.agents/memory/*` and `BACKLOG.md`.
|
|
150
151
|
- Auto-wires the harness gate scripts (`test:dispatch`, `test:governance`,
|
|
151
152
|
`test:qa-plan`, `test:backlog`) into the target's `package.json` (merged, add-only).
|
|
152
|
-
- **Auto-commits**
|
|
153
|
+
- **Auto-commits** the harness files it changed (`chore(harness): @dombaras/agent-harness
|
|
153
154
|
<old> -> <new>`) and **pushes** to origin, so the next session never sees unexplained
|
|
154
155
|
modified harness files. Your unrelated uncommitted work is never staged.
|
|
155
156
|
- `--no-commit` to skip commit+push, `--no-push` to commit but not push.
|
|
156
|
-
- `init`
|
|
157
|
+
- `init` auto-commits + pushes by default too: it commits everything it scaffolds
|
|
158
|
+
(including `.agents/memory/*`, `.agents/features/*`, and `BACKLOG.md`) so a fresh
|
|
159
|
+
project starts fully tracked and no harness file is left untracked.
|
|
157
160
|
- Harness-owned paths are **force-added**, so a project that gitignores deployment
|
|
158
161
|
artifacts (an inherited `.agents/` `.opencode/` `AGENTS.md` pattern) still gets the
|
|
159
162
|
harness commit instead of a raw `git add` failure that silently skips it — force-added
|
|
@@ -171,6 +174,10 @@ npx @dombaras/agent-harness update --target /path/to/project
|
|
|
171
174
|
last deploy (tracked by checksum in `.harness.json`), it is backed up to
|
|
172
175
|
`.harness-backup/<timestamp>/` before overwrite.
|
|
173
176
|
- `opencode.json` is merged (project keys preserved).
|
|
177
|
+
- **Managed `.gitignore`**: the harness merges a marked section (`.harness-backup/`, its
|
|
178
|
+
local rollback artifacts) into the project's `.gitignore`, preserving every project entry.
|
|
179
|
+
New harness files are tracked (committed), not ignored — so they don't need a gitignore
|
|
180
|
+
change. If the harness ever adds a new local-only artifact, `update` re-merges the entry.
|
|
174
181
|
- `--dry-run` previews the plan without writing.
|
|
175
182
|
|
|
176
183
|
### Backlog migration (`migrate-backlog`)
|
package/bin/agent-harness.js
CHANGED
|
@@ -217,6 +217,25 @@ function mergePackageJson(target) {
|
|
|
217
217
|
return { text: JSON.stringify(merged, null, 2) + "\n", hadPkg };
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
// Harness-managed .gitignore entries. These are LOCAL-ONLY artifacts (never
|
|
221
|
+
// committed): everything else the harness deploys is tracked by git. Entries
|
|
222
|
+
// live in a marked section so `update` can re-merge them (and pick up new
|
|
223
|
+
// entries) without touching the project's own ignores.
|
|
224
|
+
const GITIGNORE_MARKER = "# agent-harness (managed below)";
|
|
225
|
+
const HARNESS_IGNORE_ENTRIES = [".harness-backup/"];
|
|
226
|
+
|
|
227
|
+
function mergeGitignore(target) {
|
|
228
|
+
const p = path.join(target, ".gitignore");
|
|
229
|
+
const existing = fs.existsSync(p) ? fs.readFileSync(p, "utf8") : "";
|
|
230
|
+
const lines = existing.split(/\r?\n/);
|
|
231
|
+
const idx = lines.findIndex((l) => l === GITIGNORE_MARKER);
|
|
232
|
+
const kept = idx === -1 ? lines : lines.slice(0, idx);
|
|
233
|
+
while (kept.length && kept[kept.length - 1].trim() === "") kept.pop();
|
|
234
|
+
const managed = [GITIGNORE_MARKER, ...HARNESS_IGNORE_ENTRIES, ""];
|
|
235
|
+
const out = kept.length ? [...kept, "", ...managed] : [...managed];
|
|
236
|
+
return out.join("\n");
|
|
237
|
+
}
|
|
238
|
+
|
|
220
239
|
// ---------------------------------------------------------------- git helpers
|
|
221
240
|
|
|
222
241
|
function runGit(target, args) {
|
|
@@ -348,6 +367,23 @@ async function deploy(target, opts) {
|
|
|
348
367
|
}
|
|
349
368
|
}
|
|
350
369
|
|
|
370
|
+
// Merge harness-managed gitignore entries (add-only, marked section).
|
|
371
|
+
{
|
|
372
|
+
const text = mergeGitignore(target);
|
|
373
|
+
const key = ".gitignore";
|
|
374
|
+
const targetAbs = path.join(target, ".gitignore");
|
|
375
|
+
const had = fs.existsSync(targetAbs);
|
|
376
|
+
const current = had ? fs.readFileSync(targetAbs, "utf8") : null;
|
|
377
|
+
if (!had) actions.push({ rel: key, kind: "create" });
|
|
378
|
+
else if (current !== text) actions.push({ rel: key, kind: "merge" });
|
|
379
|
+
else actions.push({ rel: key, kind: "unchanged" });
|
|
380
|
+
manifest[key] = sha256(text);
|
|
381
|
+
if (!dryRun) {
|
|
382
|
+
fs.mkdirSync(path.dirname(targetAbs), { recursive: true });
|
|
383
|
+
fs.writeFileSync(targetAbs, text, "utf8");
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
351
387
|
for (const { abs, rel } of listFiles(TEMPLATES_DIR)) {
|
|
352
388
|
const key = relKey(rel);
|
|
353
389
|
const targetAbs = path.join(target, rel);
|
|
@@ -425,15 +461,15 @@ async function deploy(target, opts) {
|
|
|
425
461
|
fs.writeFileSync(path.join(target, CONFIG_FILE), JSON.stringify(config, null, 2) + "\n", "utf8");
|
|
426
462
|
}
|
|
427
463
|
|
|
428
|
-
// Auto-commit
|
|
464
|
+
// Auto-commit the files this run created/changed so nothing the harness
|
|
465
|
+
// deploys is left untracked. Runs for both `init` and `update`. Excludes only
|
|
466
|
+
// rollback artifacts; freshly-scaffolded project-data files (memory/features/
|
|
467
|
+
// BACKLOG) are committed so they start tracked, while pre-existing project
|
|
468
|
+
// data is never touched (a "preserve" never enters `changedRelsAll`).
|
|
429
469
|
let commitResult = null;
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
!r.startsWith(".agents/memory/") &&
|
|
434
|
-
!r.startsWith(".agents/features/") &&
|
|
435
|
-
!r.startsWith(".harness-backup/")
|
|
436
|
-
);
|
|
470
|
+
const shouldCommit = !dryRun && (!isUpdate || !nothingToUpdate);
|
|
471
|
+
if (shouldCommit) {
|
|
472
|
+
const changedRels = changedRelsAll.filter((r) => !r.startsWith(".harness-backup/"));
|
|
437
473
|
changedRels.push(CONFIG_FILE); // .harness.json reflects the new deployed version
|
|
438
474
|
commitResult = commitHarnessChanges(target, changedRels, {
|
|
439
475
|
commit,
|
|
@@ -527,13 +563,14 @@ function printUsage() {
|
|
|
527
563
|
console.log(
|
|
528
564
|
`agent-harness v${PKG.version}\n\n` +
|
|
529
565
|
`Usage:\n` +
|
|
530
|
-
` agent-harness init [--target <dir>] [--name <project>] [--domain <desc>] [--yes] [--dry-run]\n` +
|
|
566
|
+
` agent-harness init [--target <dir>] [--name <project>] [--domain <desc>] [--yes] [--dry-run] [--no-commit] [--no-push]\n` +
|
|
531
567
|
` agent-harness update [--target <dir>] [--dry-run] [--no-commit] [--no-push]\n` +
|
|
532
568
|
` agent-harness migrate-backlog [--target <dir>]\n` +
|
|
533
569
|
`\n` +
|
|
534
|
-
` update auto-
|
|
535
|
-
`
|
|
536
|
-
` to commit but not push. Your unrelated
|
|
570
|
+
` init and update auto-commit only the harness files they create/change\n` +
|
|
571
|
+
` (chore(harness): ...) and push to origin by default. Use --no-commit to\n` +
|
|
572
|
+
` skip commit+push, or --no-push to commit but not push. Your unrelated\n` +
|
|
573
|
+
` uncommitted work is never staged.\n` +
|
|
537
574
|
`\n` +
|
|
538
575
|
` migrate-backlog consolidates any live \`.agents/features/INDEX.md\` F-rows and\n` +
|
|
539
576
|
` \`.agents/memory/todo.md\` into the canonical BACKLOG.md, then rewrites those\n` +
|
|
@@ -785,10 +822,11 @@ async function init(target, flags) {
|
|
|
785
822
|
yes: !!flags["--yes"],
|
|
786
823
|
dryRun: !!flags["--dry-run"],
|
|
787
824
|
isUpdate: false,
|
|
788
|
-
commit:
|
|
789
|
-
push:
|
|
825
|
+
commit: !flags["--no-commit"],
|
|
826
|
+
push: !flags["--no-commit"] && !flags["--no-push"],
|
|
790
827
|
});
|
|
791
828
|
printSummary(result, false);
|
|
829
|
+
printCommitResult(result);
|
|
792
830
|
if (!result.dryRun) printNextSteps();
|
|
793
831
|
}
|
|
794
832
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dombaras/agent-harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Reusable multi-agent harness for AI-assisted development: personas, skills, operating rules, model routing, and QA gates. Deploy into any project with `npx @dombaras/agent-harness init`.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agent-harness": "bin/agent-harness.js"
|
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: Show the local BACKLOG.md as a clean task list (title, short description, priority). Read-only — no git.
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Read `BACKLOG.md` at the repo root and display a clean, human-readable task list.
|
|
6
|
+
The local file is the source of truth — do NOT run git, do NOT pull, do NOT edit
|
|
7
|
+
anything.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
- `git pull --ff-only`
|
|
10
|
-
- If the pull fails (uncommitted local changes / conflict), do NOT force — report the failure and show the board from the current on-disk state, flagged "possibly stale".
|
|
9
|
+
Render the `Open` rows (and the `Active / next` picks if present) as a table with
|
|
10
|
+
exactly these columns:
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
| Priority | Title | Short description |
|
|
13
|
+
|----------|-------|-------------------|
|
|
14
|
+
| P1 | F-001 | block something |
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
- `Archive` — only the last ~5 shipped rows (date/commit).
|
|
19
|
-
- Latest handoff delta (planned → shipped → deferred) from `.agents/memory/handoff.md`, plus any new `git log` commits from other sessions since the last refresh.
|
|
16
|
+
- **Priority** = the `P` cell.
|
|
17
|
+
- **Title** = the row's ID.
|
|
18
|
+
- **Short description** = the `Task` cell.
|
|
19
|
+
- Omit the `Evidence` column. Do not render the Frozen / Archive sections.
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
- Read-only: never edit `BACKLOG.md`, `.agents/memory/*`, or any code here.
|
|
23
|
-
- This is a status snapshot, not a report. If a field is unknown write `?` — never invent.
|
|
21
|
+
One row per open task. Never invent — show a `?` cell exactly as `?`.
|