@uzysjung/agent-harness 26.159.0 → 26.160.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/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
CATEGORY_TITLES,
|
|
6
6
|
CLI_BASES,
|
|
7
7
|
CONTINUOUS_SKILLS,
|
|
8
|
+
CONTINUOUS_SKILLS_HEADING,
|
|
8
9
|
DEFAULT_OPTIONS,
|
|
9
10
|
EXTERNAL_ASSETS,
|
|
10
11
|
HARNESS_ANCHOR_FILE,
|
|
@@ -57,6 +58,7 @@ import {
|
|
|
57
58
|
listFilesRecursive,
|
|
58
59
|
mergeExternalFiles,
|
|
59
60
|
readInstallLog,
|
|
61
|
+
renderContinuousSkillsNote,
|
|
60
62
|
renderFillScaffold,
|
|
61
63
|
residentCost,
|
|
62
64
|
resolveBundleRoot,
|
|
@@ -67,7 +69,7 @@ import {
|
|
|
67
69
|
upsertHarnessImport,
|
|
68
70
|
withContinuousSkillsNote,
|
|
69
71
|
writeInstallLog
|
|
70
|
-
} from "./chunk-
|
|
72
|
+
} from "./chunk-ZXWOY5VO.js";
|
|
71
73
|
|
|
72
74
|
// node_modules/sisteransi/src/index.js
|
|
73
75
|
var require_src = __commonJS({
|
|
@@ -752,7 +754,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
752
754
|
// package.json
|
|
753
755
|
var package_default = {
|
|
754
756
|
name: "@uzysjung/agent-harness",
|
|
755
|
-
version: "26.
|
|
757
|
+
version: "26.160.0",
|
|
756
758
|
description: "Curate vetted AI-coding skills & plugins by your tech stack \u2014 install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
|
|
757
759
|
type: "module",
|
|
758
760
|
publishConfig: {
|
|
@@ -1084,11 +1086,137 @@ import { basename, join as join6 } from "path";
|
|
|
1084
1086
|
|
|
1085
1087
|
// src/codex/agents-md.ts
|
|
1086
1088
|
init_esm_shims();
|
|
1089
|
+
|
|
1090
|
+
// src/agents-md-merge.ts
|
|
1091
|
+
init_esm_shims();
|
|
1092
|
+
var marker = (name) => ({
|
|
1093
|
+
start: `<!-- uzys-harness:${name}:start -->`,
|
|
1094
|
+
end: `<!-- uzys-harness:${name}:end -->`
|
|
1095
|
+
});
|
|
1096
|
+
var ANCHOR_BLOCK = marker("anchor");
|
|
1097
|
+
var SKILLS_BLOCK = marker("skills");
|
|
1098
|
+
var CONTEXT_SECTION = "Project Context";
|
|
1099
|
+
var RULES_SECTION = "Project Rules";
|
|
1100
|
+
function wrapHarnessBlock(m, body) {
|
|
1101
|
+
return `${m.start}
|
|
1102
|
+
${body}
|
|
1103
|
+
${m.end}`;
|
|
1104
|
+
}
|
|
1105
|
+
function withMarkedContinuousSkillsNote(scaffold, selectedInternalSkills) {
|
|
1106
|
+
const note = renderContinuousSkillsNote(selectedInternalSkills);
|
|
1107
|
+
return note ? `${scaffold}
|
|
1108
|
+
|
|
1109
|
+
${wrapHarnessBlock(SKILLS_BLOCK, note)}` : scaffold;
|
|
1110
|
+
}
|
|
1111
|
+
function sectionNames(template) {
|
|
1112
|
+
return new Set([...template.matchAll(/^## (.+)$/gm)].map((m) => (m[1] ?? "").trim()));
|
|
1113
|
+
}
|
|
1114
|
+
function headingName(line, names) {
|
|
1115
|
+
const name = /^## (.+?)\s*$/.exec(line.replace(/\r$/, ""))?.[1];
|
|
1116
|
+
return name !== void 0 && names.has(name) ? name : null;
|
|
1117
|
+
}
|
|
1118
|
+
function sectionBody(text, names, want) {
|
|
1119
|
+
const lines = text.split("\n");
|
|
1120
|
+
let start = -1;
|
|
1121
|
+
for (const [i, line] of lines.entries()) {
|
|
1122
|
+
const name = headingName(line, names);
|
|
1123
|
+
if (name === null) continue;
|
|
1124
|
+
if (start === -1) {
|
|
1125
|
+
if (name === want) start = i;
|
|
1126
|
+
} else {
|
|
1127
|
+
return lines.slice(start + 1, i);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
return start === -1 ? null : lines.slice(start + 1);
|
|
1131
|
+
}
|
|
1132
|
+
function assemble(rendered, names, bodies) {
|
|
1133
|
+
const out = [];
|
|
1134
|
+
let replacing = false;
|
|
1135
|
+
for (const line of rendered.split("\n")) {
|
|
1136
|
+
const name = headingName(line, names);
|
|
1137
|
+
if (name !== null) {
|
|
1138
|
+
out.push(line);
|
|
1139
|
+
const override = bodies.get(name);
|
|
1140
|
+
replacing = override !== void 0;
|
|
1141
|
+
if (override !== void 0) out.push(...override);
|
|
1142
|
+
continue;
|
|
1143
|
+
}
|
|
1144
|
+
if (!replacing) out.push(line);
|
|
1145
|
+
}
|
|
1146
|
+
return out.join("\n");
|
|
1147
|
+
}
|
|
1148
|
+
function blockRange(lines, m) {
|
|
1149
|
+
const start = lines.findIndex((l) => l.trim() === m.start);
|
|
1150
|
+
if (start === -1) return null;
|
|
1151
|
+
const end = lines.findIndex((l, i) => i > start && l.trim() === m.end);
|
|
1152
|
+
return end === -1 ? null : [start, end];
|
|
1153
|
+
}
|
|
1154
|
+
function trimTrailingBlanks(lines) {
|
|
1155
|
+
const out = [...lines];
|
|
1156
|
+
while (out.length > 0 && (out.at(-1) ?? "").trim() === "") out.pop();
|
|
1157
|
+
return out;
|
|
1158
|
+
}
|
|
1159
|
+
function refreshBlock(existing, rendered, m, legacyHeading) {
|
|
1160
|
+
const at2 = blockRange(rendered, m);
|
|
1161
|
+
const fresh = at2 === null ? null : rendered.slice(at2[0], at2[1] + 1);
|
|
1162
|
+
const here = blockRange(existing, m);
|
|
1163
|
+
if (here !== null) {
|
|
1164
|
+
const head2 = existing.slice(0, here[0]);
|
|
1165
|
+
const tail2 = existing.slice(here[1] + 1);
|
|
1166
|
+
if (fresh === null) {
|
|
1167
|
+
const kept = trimTrailingBlanks(head2);
|
|
1168
|
+
return tail2.length > 0 ? [...kept, ...tail2] : [...kept, ""];
|
|
1169
|
+
}
|
|
1170
|
+
return [...head2, ...fresh, ...tail2];
|
|
1171
|
+
}
|
|
1172
|
+
const legacyAt = legacyHeading === void 0 ? -1 : existing.findIndex((l) => l.replace(/\r$/, "").trim() === legacyHeading);
|
|
1173
|
+
if (legacyAt === -1) {
|
|
1174
|
+
const kept = trimTrailingBlanks(existing);
|
|
1175
|
+
return fresh === null ? [...kept, ""] : [...kept, "", ...fresh, ""];
|
|
1176
|
+
}
|
|
1177
|
+
const head = trimTrailingBlanks(existing.slice(0, legacyAt));
|
|
1178
|
+
const tail = existing.slice(legacyNoteEnd(existing, legacyAt));
|
|
1179
|
+
while (tail.length > 0 && (tail[0] ?? "").trim() === "") tail.shift();
|
|
1180
|
+
const body = fresh === null ? [...head] : [...head, "", ...fresh];
|
|
1181
|
+
return tail.length > 0 ? [...body, "", ...tail] : [...body, ""];
|
|
1182
|
+
}
|
|
1183
|
+
function legacyNoteEnd(lines, headingAt) {
|
|
1184
|
+
let i = headingAt + 1;
|
|
1185
|
+
while (i < lines.length) {
|
|
1186
|
+
const l = (lines[i] ?? "").replace(/\r$/, "").trim();
|
|
1187
|
+
const isNote = l === "" || l.startsWith("- `") || l.startsWith("These installed skills apply");
|
|
1188
|
+
if (!isNote) break;
|
|
1189
|
+
i++;
|
|
1190
|
+
}
|
|
1191
|
+
return i;
|
|
1192
|
+
}
|
|
1193
|
+
function mergeAgentsMd(params) {
|
|
1194
|
+
const { rendered, existing, template } = params;
|
|
1195
|
+
if (existing === null) return rendered;
|
|
1196
|
+
const names = sectionNames(template);
|
|
1197
|
+
const bodies = /* @__PURE__ */ new Map();
|
|
1198
|
+
const contextNow = sectionBody(existing, names, CONTEXT_SECTION);
|
|
1199
|
+
const contextNew = sectionBody(rendered, names, CONTEXT_SECTION);
|
|
1200
|
+
if (contextNow !== null && contextNew !== null) {
|
|
1201
|
+
bodies.set(
|
|
1202
|
+
CONTEXT_SECTION,
|
|
1203
|
+
refreshBlock(contextNow, contextNew, SKILLS_BLOCK, CONTINUOUS_SKILLS_HEADING)
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
const rulesNow = sectionBody(existing, names, RULES_SECTION);
|
|
1207
|
+
const rulesNew = sectionBody(rendered, names, RULES_SECTION);
|
|
1208
|
+
if (rulesNow !== null && rulesNew !== null && blockRange(rulesNow, ANCHOR_BLOCK) !== null) {
|
|
1209
|
+
bodies.set(RULES_SECTION, refreshBlock(rulesNow, rulesNew, ANCHOR_BLOCK));
|
|
1210
|
+
}
|
|
1211
|
+
return bodies.size === 0 ? rendered : assemble(rendered, names, bodies);
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
// src/codex/agents-md.ts
|
|
1087
1215
|
function renameSlashes(text) {
|
|
1088
1216
|
return text.replaceAll("/uzys:", "/uzys-");
|
|
1089
1217
|
}
|
|
1090
1218
|
function renderAgentsMd(params) {
|
|
1091
|
-
const body = params.claudeMd.replace(/^#\s+.*\r?\n/, "").trim();
|
|
1219
|
+
const body = wrapHarnessBlock(ANCHOR_BLOCK, params.claudeMd.replace(/^#\s+.*\r?\n/, "").trim());
|
|
1092
1220
|
const replaced = params.template.replaceAll("{PROJECT_NAME}", params.projectName).replaceAll("{PROJECT_RULES}", body).replaceAll("{PROJECT_CONTEXT}", params.projectContext).replaceAll("{HARNESS_RULES}", params.harnessRules ?? "");
|
|
1093
1221
|
return renameSlashes(replaced);
|
|
1094
1222
|
}
|
|
@@ -1312,7 +1440,8 @@ function writeRules(harnessRoot, projectDir, writer, selectedInternalSkills) {
|
|
|
1312
1440
|
selectedInternalSkills
|
|
1313
1441
|
)
|
|
1314
1442
|
});
|
|
1315
|
-
|
|
1443
|
+
const withoutMarkers = rulesOut.split("\n").filter((l) => !/^<!-- uzys-harness:(?:anchor|skills):(?:start|end) -->$/.test(l.trim())).join("\n");
|
|
1444
|
+
return writer.write(target, withoutMarkers) ? target : null;
|
|
1316
1445
|
}
|
|
1317
1446
|
|
|
1318
1447
|
// src/codex/opt-in.ts
|
|
@@ -1457,14 +1586,22 @@ function runCodexTransform(params) {
|
|
|
1457
1586
|
claudeMd,
|
|
1458
1587
|
projectName,
|
|
1459
1588
|
// ADR-085 — 상시 스킬 안내는 앵커가 아니라 여기(프로젝트 맥락)에, 깔린 것만.
|
|
1460
|
-
|
|
1589
|
+
// #503 — 그 조각은 설치자 소유 절 안에 사니 마커로 감싼다.
|
|
1590
|
+
projectContext: withMarkedContinuousSkillsNote(
|
|
1461
1591
|
renderFillScaffold("agents-md"),
|
|
1462
1592
|
selectedInternalSkills
|
|
1463
1593
|
),
|
|
1464
1594
|
// Codex 는 룰 디렉터리가 없다 — 룰이 AGENTS.md 본문에 들어가야 도달한다(§Harness Rules).
|
|
1465
1595
|
harnessRules: renderRulesBlock(portRules(harnessRoot, rules))
|
|
1466
1596
|
});
|
|
1467
|
-
writer.write(
|
|
1597
|
+
writer.write(
|
|
1598
|
+
agentsMdPath,
|
|
1599
|
+
mergeAgentsMd({
|
|
1600
|
+
rendered: agentsMdOut,
|
|
1601
|
+
existing: existsSync8(agentsMdPath) ? readFileSync7(agentsMdPath, "utf8") : null,
|
|
1602
|
+
template: agentsTemplate
|
|
1603
|
+
})
|
|
1604
|
+
);
|
|
1468
1605
|
const configTomlPath = join8(projectDir, ".codex/config.toml");
|
|
1469
1606
|
writer.write(
|
|
1470
1607
|
configTomlPath,
|
|
@@ -1530,7 +1667,7 @@ function renameSlashes2(text) {
|
|
|
1530
1667
|
return text.replaceAll("/uzys:", "/uzys-");
|
|
1531
1668
|
}
|
|
1532
1669
|
function renderAgentsMd2(params) {
|
|
1533
|
-
const body = params.claudeMd.replace(/^#\s+.*\r?\n/, "").trim();
|
|
1670
|
+
const body = wrapHarnessBlock(ANCHOR_BLOCK, params.claudeMd.replace(/^#\s+.*\r?\n/, "").trim());
|
|
1534
1671
|
const replaced = params.template.replaceAll("{PROJECT_NAME}", params.projectName).replaceAll("{PROJECT_RULES}", body).replaceAll("{PROJECT_CONTEXT}", params.projectContext).replaceAll("{HARNESS_RULES}", params.harnessRules ?? "");
|
|
1535
1672
|
return renameSlashes2(replaced);
|
|
1536
1673
|
}
|
|
@@ -1579,7 +1716,8 @@ function runOpencodeTransform(params) {
|
|
|
1579
1716
|
claudeMd,
|
|
1580
1717
|
projectName,
|
|
1581
1718
|
// ADR-085 — 상시 스킬 안내는 앵커가 아니라 여기(프로젝트 맥락)에, 깔린 것만.
|
|
1582
|
-
|
|
1719
|
+
// #503 — 그 조각은 설치자 소유 절 안에 사니 마커로 감싼다 (codex 와 같은 마커).
|
|
1720
|
+
projectContext: withMarkedContinuousSkillsNote(
|
|
1583
1721
|
renderFillScaffold("agents-md"),
|
|
1584
1722
|
selectedInternalSkills
|
|
1585
1723
|
),
|
|
@@ -1588,7 +1726,14 @@ function runOpencodeTransform(params) {
|
|
|
1588
1726
|
// (독립 검증 C-1 실측). 같은 내용을 쓰면 순서가 결과를 바꾸지 않는다.
|
|
1589
1727
|
harnessRules: renderRulesBlock(portRules(harnessRoot, rules))
|
|
1590
1728
|
});
|
|
1591
|
-
writer.write(
|
|
1729
|
+
writer.write(
|
|
1730
|
+
agentsMdPath,
|
|
1731
|
+
mergeAgentsMd({
|
|
1732
|
+
rendered: agentsMdOut,
|
|
1733
|
+
existing: existsSync9(agentsMdPath) ? readFileSync8(agentsMdPath, "utf8") : null,
|
|
1734
|
+
template: agentsTemplate
|
|
1735
|
+
})
|
|
1736
|
+
);
|
|
1592
1737
|
const opencodeJsonPath = join9(projectDir, "opencode.json");
|
|
1593
1738
|
writer.write(opencodeJsonPath, renderOpencodeJson({ template: opencodeTemplate, mcp }));
|
|
1594
1739
|
const skillFiles = writeBundledSkillDirs({
|
|
@@ -2366,11 +2511,13 @@ function installNewSkillDirs(projectDir, templatesDir, tracks) {
|
|
|
2366
2511
|
const foreignOwned = [];
|
|
2367
2512
|
const log = readInstallLog(projectDir);
|
|
2368
2513
|
const excluded = new Set(log?.spec.baselineExclude ?? []);
|
|
2514
|
+
const skillExcluded = new Set(log?.spec.skillExclude ?? []);
|
|
2369
2515
|
if (!(log?.spec.cli ?? ["claude"]).includes("claude")) return { installed, foreignOwned };
|
|
2370
2516
|
const spec = buildAssetSpec({ tracks, options: DEFAULT_OPTIONS });
|
|
2371
2517
|
for (const entry of buildManifest(spec)) {
|
|
2372
2518
|
if (entry.type !== "dir" || !entry.target.startsWith(".claude/skills/")) continue;
|
|
2373
2519
|
if (!entry.applies(spec) || isBaselineExcluded(entry.target, excluded)) continue;
|
|
2520
|
+
if (skillExcluded.has(entry.target.slice(".claude/skills/".length))) continue;
|
|
2374
2521
|
const target = join12(projectDir, entry.target);
|
|
2375
2522
|
const foreign = foreignOwnedTarget(projectDir, entry.target);
|
|
2376
2523
|
if (foreign !== null) {
|
|
@@ -2473,6 +2620,11 @@ function recordAnchorBaseline(projectDir, anchor) {
|
|
|
2473
2620
|
} catch {
|
|
2474
2621
|
}
|
|
2475
2622
|
}
|
|
2623
|
+
function installedBundledSkills(projectDir) {
|
|
2624
|
+
return INTERNAL_BUNDLED_SKILL_IDS.filter(
|
|
2625
|
+
(id) => existsSync13(join12(projectDir, ".claude/skills", id)) || existsSync13(join12(projectDir, ".agents/skills", id))
|
|
2626
|
+
);
|
|
2627
|
+
}
|
|
2476
2628
|
function refreshExternalCli(projectDir, harnessRoot) {
|
|
2477
2629
|
const log = readInstallLog(projectDir);
|
|
2478
2630
|
const baselineExcluded = new Set(log?.spec.baselineExclude ?? []);
|
|
@@ -2480,8 +2632,11 @@ function refreshExternalCli(projectDir, harnessRoot) {
|
|
|
2480
2632
|
harnessRoot,
|
|
2481
2633
|
projectDir,
|
|
2482
2634
|
cli: ALL_CLI_TARGETS,
|
|
2483
|
-
|
|
2484
|
-
//
|
|
2635
|
+
// 스킬은 **디스크에 있는 것만** 넘긴다. 스킬 *파일*은 전체 목록을 넘겨도 refreshOnly 가
|
|
2636
|
+
// 없는 것을 건너뛰지만, `AGENTS.md` 의 상시 스킬 안내(ADR-085)는 이 목록 그대로 렌더돼
|
|
2637
|
+
// `--without` 으로 뺀 스킬(#505)·opt-in 스킬을 "열어라"고 적었다(실측 2026-09-21). 판정은
|
|
2638
|
+
// `upsertRootImport` 와 같다 — `.claude/skills/<id>` 또는 `.agents/skills/<id>` 가 있으면 깔린 것.
|
|
2639
|
+
selectedInternalSkills: installedBundledSkills(projectDir),
|
|
2485
2640
|
rules: ALL_RULES.filter((r) => !isBaselineExcluded(`.claude/rules/${r}.md`, baselineExcluded)),
|
|
2486
2641
|
previousExternal: log?.externalFiles ?? [],
|
|
2487
2642
|
refreshOnly: true
|
|
@@ -4107,10 +4262,10 @@ function formatAssetRows(assets) {
|
|
|
4107
4262
|
const idWidth = Math.max(0, ...assets.map((a) => a.id.length));
|
|
4108
4263
|
const methodWidth = Math.max(0, ...assets.map((a) => a.method.length));
|
|
4109
4264
|
return assets.map((a) => {
|
|
4110
|
-
const
|
|
4265
|
+
const marker2 = a.scope === "global" ? c.yellow("!") : c.green("\u2713");
|
|
4111
4266
|
const version = a.version ? c.dim(` v${a.version}`) : "";
|
|
4112
4267
|
const note = a.scope === "global" ? c.yellow(" (manual removal \u2014 D16)") : "";
|
|
4113
|
-
return ` ${
|
|
4268
|
+
return ` ${marker2} ${padDisplay(a.id, idWidth)} ${c.dim(padDisplay(a.method, methodWidth))} ${c.dim(a.scope)}${version}${note}`;
|
|
4114
4269
|
});
|
|
4115
4270
|
}
|
|
4116
4271
|
function formatTemplateRows(log, projectDir) {
|