@uzysjung/agent-harness 26.102.0 → 26.103.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 +25 -2
- package/dist/index.js +78 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -71,6 +71,26 @@ Curation, not a list to browse — pick `csr-supabase` and step 3 pre-checks exa
|
|
|
71
71
|
|
|
72
72
|
---
|
|
73
73
|
|
|
74
|
+
## Curation philosophy — lean by default
|
|
75
|
+
|
|
76
|
+
> Maintainer's direction statement (2026-07). The catalog itself is unchanged by this section — it describes how curation decisions are made going forward.
|
|
77
|
+
|
|
78
|
+
Frontier coding agents (Opus 4.8 / GPT-5.6 class) keep absorbing what skill packs used to teach. Our working position: **a skill has to earn its context cost** — every installed skill occupies the agent's attention even when unused, so the default install stays minimal and "more skills" is treated as a cost, not a feature.
|
|
79
|
+
|
|
80
|
+
What we believe still earns its place is the **insight layer**, not the knowledge layer:
|
|
81
|
+
|
|
82
|
+
- **Orchestration know-how** — model/effort role splits (`model-orchestration`), multi-perspective verification (`multi-persona-review`), auditing your own harness (`harness-health-audit`).
|
|
83
|
+
- **Cross-CLI leverage** — use each CLI for what it's best at: natural prose polish via Antigravity (`gemini-consult`), concise structuring + image generation via Codex (`codex-consult`).
|
|
84
|
+
- **Operational facts** — CLI flags, auth flows, deploy steps (`supabase-cli`, `railway-skills`, …) drift with upstream releases no matter how smart models get. Generic pattern guides are the first to become redundant.
|
|
85
|
+
|
|
86
|
+
Structured workflow bundles (superpowers, BMAD, OpenSpec, …) stay **opt-in and never pre-checked**. Our view: with strong models, an agile direction plus a firm git/PR policy beats enforced ceremony for solo/greenfield work — but multi-dev alignment, junior onboarding, and audit trails are human-side problems that no model upgrade solves, so the choice stays yours ([Workflow curation guide](docs/WORKFLOWS.md)).
|
|
87
|
+
|
|
88
|
+
What stays fixed is small and deterministic: git/PR policy, verification gates, honest reporting. Within those rails the agent develops flexibly — including running several agents in parallel in one session (safety conventions for that, like worktree isolation, are roadmap items, not shipped features). Since v26.103.0 the installer shows the **session-start context cost** of your selection (bundled skills measured from frontmatter, external assets honestly marked unmeasured), and our test suite holds the bundled-skill descriptors of the default install under a token budget (external assets stay unmeasured).
|
|
89
|
+
|
|
90
|
+
One honest note: this repository's *own* development harness is deliberately heavier than what it installs — it self-verifies an installer that writes into your project. Shipped defaults ≠ our dev rig.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
74
94
|
## Installing into an existing project
|
|
75
95
|
|
|
76
96
|
`agent-harness` never silently overwrites your config. Before replacing an **editable** file whose contents differ, it writes a timestamped backup next to it — and every backup path is printed in the install summary (`backup` rows). Nothing is deleted.
|
|
@@ -200,12 +220,15 @@ External assets are recommended automatically based on your track selection. Ste
|
|
|
200
220
|
| `asis-tobe-decision` ★ | Present an A-or-B / approval moment as context → recommendation → option table → AS-IS/TO-BE contrast | this project (core) |
|
|
201
221
|
| `compaction-handoff` ★ | Persist durable state + git snapshot + resume anchor before a context `/compact` | this project (core) |
|
|
202
222
|
| `northstar-roadmap` ★ | Measure current state vs the vision doc → ranked feature backlog persisted to docs/plans + memory | this project (core) |
|
|
223
|
+
| `model-orchestration` ★ | Model orchestration policy — role split (orchestrator directs/reviews · strong model authors core/V&V · mid model does repetitive impl/E2E) + effort floors + quota handoff | this project (core) |
|
|
224
|
+
| `harness-health-audit` ★ | Audit your CLAUDE.md/rules/skills/hooks on 4 questions a linter can't answer: TRUE · USED · AFFORDABLE · SAFE | this project (core) |
|
|
203
225
|
|
|
204
|
-
**First-party
|
|
226
|
+
**First-party advisors** (`official`, **opt-in** — `--with gemini-consult` / `--with codex-consult`; repo-bundled, install across all 4 CLIs; each needs its external CLI at runtime — Antigravity [`agy`](https://antigravity.google/cli) or OpenAI `codex`):
|
|
205
227
|
|
|
206
228
|
| Asset | What | Source |
|
|
207
229
|
|---|---|---|
|
|
208
|
-
| `gemini-consult` | Consult Gemini (via `agy`) for natural **Korean** phrasing + **multi-persona** second-opinion review — an idiomatic, independent second model | this project |
|
|
230
|
+
| `gemini-consult` | Consult Gemini (via `agy`) for natural **Korean** phrasing + **multi-persona** second-opinion review + image generation — an idiomatic, independent second model | this project |
|
|
231
|
+
| `codex-consult` | Consult OpenAI Codex (`codex exec`) for **concise / structured** rewriting + **image generation** (real PNG on disk) — division of labor: nuance/persona → gemini, concision/structure/images → codex | this project |
|
|
209
232
|
|
|
210
233
|
### Security & ECC (opt-in)
|
|
211
234
|
|
package/dist/index.js
CHANGED
|
@@ -709,7 +709,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
709
709
|
// package.json
|
|
710
710
|
var package_default = {
|
|
711
711
|
name: "@uzysjung/agent-harness",
|
|
712
|
-
version: "26.
|
|
712
|
+
version: "26.103.0",
|
|
713
713
|
description: "Curate vetted AI-coding skills & plugins by your tech stack \u2014 install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
|
|
714
714
|
type: "module",
|
|
715
715
|
publishConfig: {
|
|
@@ -763,7 +763,8 @@ var package_default = {
|
|
|
763
763
|
|
|
764
764
|
// src/commands/install.ts
|
|
765
765
|
init_esm_shims();
|
|
766
|
-
import { resolve as
|
|
766
|
+
import { resolve as resolve3 } from "path";
|
|
767
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
767
768
|
|
|
768
769
|
// src/cli-targets.ts
|
|
769
770
|
init_esm_shims();
|
|
@@ -2719,6 +2720,54 @@ function listHookFiles(hookDir) {
|
|
|
2719
2720
|
// src/commands/install-render.ts
|
|
2720
2721
|
init_esm_shims();
|
|
2721
2722
|
|
|
2723
|
+
// src/context-cost.ts
|
|
2724
|
+
init_esm_shims();
|
|
2725
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12 } from "fs";
|
|
2726
|
+
import { join as join11, resolve as resolve2 } from "path";
|
|
2727
|
+
import { fileURLToPath } from "url";
|
|
2728
|
+
var CHARS_PER_TOKEN = 4;
|
|
2729
|
+
function estimateTokens(chars) {
|
|
2730
|
+
return Math.ceil(chars / CHARS_PER_TOKEN);
|
|
2731
|
+
}
|
|
2732
|
+
function resolveBundleRoot() {
|
|
2733
|
+
return resolve2(fileURLToPath(new URL(".", import.meta.url)), "..");
|
|
2734
|
+
}
|
|
2735
|
+
function extractFrontmatter(content) {
|
|
2736
|
+
const m = content.match(/^?---\r?\n([\s\S]*?)\r?\n---/);
|
|
2737
|
+
return m?.[1] ?? null;
|
|
2738
|
+
}
|
|
2739
|
+
function assetDescriptorTokens(assetId, root = resolveBundleRoot()) {
|
|
2740
|
+
const asset = EXTERNAL_ASSETS.find((a) => a.id === assetId);
|
|
2741
|
+
if (!asset || asset.method.kind !== "internal") return null;
|
|
2742
|
+
const skillMd = join11(root, "templates", "skills", asset.method.key, "SKILL.md");
|
|
2743
|
+
if (!existsSync12(skillMd)) return null;
|
|
2744
|
+
const fm = extractFrontmatter(readFileSync12(skillMd, "utf8"));
|
|
2745
|
+
return fm === null ? null : estimateTokens(fm.length);
|
|
2746
|
+
}
|
|
2747
|
+
function summarizeContextCost(assetIds, root = resolveBundleRoot()) {
|
|
2748
|
+
let measuredTokens = 0;
|
|
2749
|
+
let measuredCount = 0;
|
|
2750
|
+
let unmeasuredCount = 0;
|
|
2751
|
+
for (const id of assetIds) {
|
|
2752
|
+
const tokens = assetDescriptorTokens(id, root);
|
|
2753
|
+
if (tokens === null) unmeasuredCount += 1;
|
|
2754
|
+
else {
|
|
2755
|
+
measuredTokens += tokens;
|
|
2756
|
+
measuredCount += 1;
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
return { measuredTokens, measuredCount, unmeasuredCount };
|
|
2760
|
+
}
|
|
2761
|
+
function formatContextCostLine(s) {
|
|
2762
|
+
if (s.measuredCount === 0 && s.unmeasuredCount === 0) return null;
|
|
2763
|
+
if (s.measuredCount === 0) {
|
|
2764
|
+
return `session-start context cost: unmeasured (${s.unmeasuredCount} external asset${s.unmeasuredCount === 1 ? "" : "s"})`;
|
|
2765
|
+
}
|
|
2766
|
+
const skills = `${s.measuredCount} bundled skill${s.measuredCount === 1 ? "" : "s"} measured`;
|
|
2767
|
+
const external = s.unmeasuredCount > 0 ? ` \xB7 ${s.unmeasuredCount} external unmeasured` : "";
|
|
2768
|
+
return `session-start context cost: ~${s.measuredTokens} tokens (${skills}${external})`;
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2722
2771
|
// src/preset-recommend.ts
|
|
2723
2772
|
init_esm_shims();
|
|
2724
2773
|
function recommendedExternalAssets(presets) {
|
|
@@ -2783,6 +2832,8 @@ function renderInstallHeader(log, spec, mode) {
|
|
|
2783
2832
|
for (const [cat, ids] of groupAssetsByCategory(finalAssets)) {
|
|
2784
2833
|
log(` ${c.dim(`\xB7 ${cat}:`)} ${ids.join(", ")}`);
|
|
2785
2834
|
}
|
|
2835
|
+
const cost = formatContextCostLine(summarizeContextCost(finalAssets));
|
|
2836
|
+
if (cost) log(` ${c.dim(`\xB7 ${cost}`)}`);
|
|
2786
2837
|
}
|
|
2787
2838
|
log("");
|
|
2788
2839
|
}
|
|
@@ -3248,7 +3299,7 @@ function installAction(options, deps = {}) {
|
|
|
3248
3299
|
withKarpathyHook: options.withKarpathyHook === true
|
|
3249
3300
|
},
|
|
3250
3301
|
cli: validated.cli,
|
|
3251
|
-
projectDir:
|
|
3302
|
+
projectDir: resolve3(options.projectDir ?? process.cwd()),
|
|
3252
3303
|
scope: resolveScopeOption(options.scope, err)
|
|
3253
3304
|
};
|
|
3254
3305
|
executeSpec(spec, {
|
|
@@ -3317,7 +3368,7 @@ function defaultRunPipeline(spec, harnessRoot, mode, callbacks) {
|
|
|
3317
3368
|
return runInstall(ctx);
|
|
3318
3369
|
}
|
|
3319
3370
|
function defaultHarnessRoot() {
|
|
3320
|
-
return
|
|
3371
|
+
return resolve3(fileURLToPath2(new URL(".", import.meta.url)), "..");
|
|
3321
3372
|
}
|
|
3322
3373
|
function registerInstallCommand(cli2) {
|
|
3323
3374
|
cli2.command("install", "Install harness assets into a project").option("--track <name>", "[Track] Track to install (repeatable)", { type: [String] }).option(
|
|
@@ -3352,15 +3403,15 @@ function registerInstallCommand(cli2) {
|
|
|
3352
3403
|
// src/commands/uninstall.ts
|
|
3353
3404
|
init_esm_shims();
|
|
3354
3405
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3355
|
-
import { existsSync as
|
|
3356
|
-
import { join as
|
|
3406
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13, rmSync } from "fs";
|
|
3407
|
+
import { join as join12, resolve as resolve4 } from "path";
|
|
3357
3408
|
function uninstallAction(options, deps = {}) {
|
|
3358
3409
|
const log = deps.log ?? console.log;
|
|
3359
3410
|
const err = deps.err ?? console.error;
|
|
3360
3411
|
const exit = deps.exit ?? ((code) => process.exit(code));
|
|
3361
3412
|
const spawn = deps.spawn ?? defaultSpawn2;
|
|
3362
3413
|
const rm = deps.rm ?? defaultRm;
|
|
3363
|
-
const projectDir =
|
|
3414
|
+
const projectDir = resolve4(options.projectDir ?? process.cwd());
|
|
3364
3415
|
const installLog = readInstallLog(projectDir);
|
|
3365
3416
|
if (!installLog) {
|
|
3366
3417
|
err(status.failure(c.red(`ERROR: install log not found at ${installLogPath(projectDir)}`)));
|
|
@@ -3525,22 +3576,22 @@ function buildGlobalAdvisoryCmd(asset) {
|
|
|
3525
3576
|
}
|
|
3526
3577
|
}
|
|
3527
3578
|
function removeTemplates(log, projectDir, rm) {
|
|
3528
|
-
rm(
|
|
3529
|
-
if (log.templates.codexDir) rm(
|
|
3530
|
-
if (log.templates.opencodeDir) rm(
|
|
3579
|
+
rm(join12(projectDir, log.templates.claudeDir));
|
|
3580
|
+
if (log.templates.codexDir) rm(join12(projectDir, log.templates.codexDir));
|
|
3581
|
+
if (log.templates.opencodeDir) rm(join12(projectDir, log.templates.opencodeDir));
|
|
3531
3582
|
const rootMd = log.templates.rootClaudeMd;
|
|
3532
3583
|
if (rootMd) {
|
|
3533
3584
|
if (rootClaudeMdModified(log, projectDir)) return { rootClaudeMdKept: true };
|
|
3534
|
-
rm(
|
|
3585
|
+
rm(join12(projectDir, rootMd.path));
|
|
3535
3586
|
}
|
|
3536
3587
|
return { rootClaudeMdKept: false };
|
|
3537
3588
|
}
|
|
3538
3589
|
function rootClaudeMdModified(log, projectDir) {
|
|
3539
3590
|
const rootMd = log.templates.rootClaudeMd;
|
|
3540
3591
|
if (!rootMd) return false;
|
|
3541
|
-
const path =
|
|
3542
|
-
if (!
|
|
3543
|
-
return hashContent(
|
|
3592
|
+
const path = join12(projectDir, rootMd.path);
|
|
3593
|
+
if (!existsSync13(path)) return false;
|
|
3594
|
+
return hashContent(readFileSync13(path, "utf8")) !== rootMd.sha256;
|
|
3544
3595
|
}
|
|
3545
3596
|
function formatTemplateList(log) {
|
|
3546
3597
|
const items = [log.templates.claudeDir];
|
|
@@ -3552,7 +3603,7 @@ function defaultSpawn2(cmd, args) {
|
|
|
3552
3603
|
return spawnSync2(cmd, [...args], { encoding: "utf8", stdio: "pipe", timeout: 12e4 });
|
|
3553
3604
|
}
|
|
3554
3605
|
function defaultRm(path) {
|
|
3555
|
-
if (
|
|
3606
|
+
if (existsSync13(path)) {
|
|
3556
3607
|
rmSync(path, { recursive: true, force: true });
|
|
3557
3608
|
}
|
|
3558
3609
|
}
|
|
@@ -4846,8 +4897,8 @@ Proceed?`,
|
|
|
4846
4897
|
|
|
4847
4898
|
// src/state.ts
|
|
4848
4899
|
init_esm_shims();
|
|
4849
|
-
import { existsSync as
|
|
4850
|
-
import { join as
|
|
4900
|
+
import { existsSync as existsSync14, readFileSync as readFileSync14 } from "fs";
|
|
4901
|
+
import { join as join13 } from "path";
|
|
4851
4902
|
var META_FILE = ".claude/.installed-tracks";
|
|
4852
4903
|
var LEGACY_SIGNATURES = [
|
|
4853
4904
|
{ rule: "htmx.md", track: "ssr-htmx" },
|
|
@@ -4857,13 +4908,13 @@ var LEGACY_SIGNATURES = [
|
|
|
4857
4908
|
{ rule: "cli-development.md", track: "tooling" }
|
|
4858
4909
|
];
|
|
4859
4910
|
function detectInstallState(projectDir) {
|
|
4860
|
-
const claudeDir =
|
|
4861
|
-
const hasClaudeDir =
|
|
4911
|
+
const claudeDir = join13(projectDir, ".claude");
|
|
4912
|
+
const hasClaudeDir = existsSync14(claudeDir);
|
|
4862
4913
|
if (!hasClaudeDir) {
|
|
4863
4914
|
return { state: "new", tracks: [], source: "none", hasClaudeDir: false };
|
|
4864
4915
|
}
|
|
4865
|
-
const metaPath =
|
|
4866
|
-
if (
|
|
4916
|
+
const metaPath = join13(projectDir, META_FILE);
|
|
4917
|
+
if (existsSync14(metaPath)) {
|
|
4867
4918
|
const tracks2 = readMetafile(metaPath);
|
|
4868
4919
|
return { state: "existing", tracks: tracks2, source: "metafile", hasClaudeDir: true };
|
|
4869
4920
|
}
|
|
@@ -4871,7 +4922,7 @@ function detectInstallState(projectDir) {
|
|
|
4871
4922
|
return { state: "existing", tracks, source: "legacy", hasClaudeDir: true };
|
|
4872
4923
|
}
|
|
4873
4924
|
function readMetafile(path) {
|
|
4874
|
-
const raw =
|
|
4925
|
+
const raw = readFileSync14(path, "utf8");
|
|
4875
4926
|
const seen = /* @__PURE__ */ new Set();
|
|
4876
4927
|
for (const line of raw.split(/\s+/)) {
|
|
4877
4928
|
const trimmed = line.trim();
|
|
@@ -4882,13 +4933,13 @@ function readMetafile(path) {
|
|
|
4882
4933
|
return [...seen].sort();
|
|
4883
4934
|
}
|
|
4884
4935
|
function inferFromLegacySignatures(projectDir) {
|
|
4885
|
-
const rulesDir =
|
|
4886
|
-
if (!
|
|
4936
|
+
const rulesDir = join13(projectDir, ".claude/rules");
|
|
4937
|
+
if (!existsSync14(rulesDir)) {
|
|
4887
4938
|
return [];
|
|
4888
4939
|
}
|
|
4889
4940
|
const found = /* @__PURE__ */ new Set();
|
|
4890
4941
|
for (const sig of LEGACY_SIGNATURES) {
|
|
4891
|
-
if (
|
|
4942
|
+
if (existsSync14(join13(rulesDir, sig.rule))) {
|
|
4892
4943
|
found.add(sig.track);
|
|
4893
4944
|
}
|
|
4894
4945
|
}
|
|
@@ -5101,6 +5152,8 @@ function formatSummary(spec) {
|
|
|
5101
5152
|
for (const [cat, ids] of groupAssetsByCategory(finalAssets)) {
|
|
5102
5153
|
lines.push(` \xB7 ${cat}: ${ids.join(", ")}`);
|
|
5103
5154
|
}
|
|
5155
|
+
const cost = formatContextCostLine(summarizeContextCost(finalAssets));
|
|
5156
|
+
if (cost) lines.push(` \xB7 ${cost}`);
|
|
5104
5157
|
}
|
|
5105
5158
|
if (spec.userOverride) {
|
|
5106
5159
|
if (spec.userOverride.forceInclude.length > 0) {
|