@uzysjung/agent-harness 26.148.0 → 26.149.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/{chunk-PTUM2NI3.js → chunk-YSW3OLH4.js} +50 -9
- package/dist/chunk-YSW3OLH4.js.map +1 -0
- package/dist/index.js +228 -109
- package/dist/index.js.map +1 -1
- package/dist/trust-tier-drift.js +1 -1
- package/package.json +2 -1
- package/templates/rules/ship-checklist.md +4 -3
- package/templates/rules/test-policy.md +12 -3
- package/templates/skills/humanize-korean/SKILL.md +228 -0
- package/templates/skills/north-star/NORTH_STAR.template.md +106 -47
- package/templates/skills/north-star/SKILL.md +230 -74
- package/templates/skills/north-star/references/roadmap-method.md +62 -12
- package/templates/skills/self-hosted-github-runner/SKILL.md +328 -0
- package/templates/skills/verification-loop/SKILL.md +23 -0
- package/dist/chunk-PTUM2NI3.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -56,7 +56,7 @@ import {
|
|
|
56
56
|
summarizeContextCost,
|
|
57
57
|
upsertHarnessImport,
|
|
58
58
|
writeInstallLog
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-YSW3OLH4.js";
|
|
60
60
|
|
|
61
61
|
// node_modules/sisteransi/src/index.js
|
|
62
62
|
var require_src = __commonJS({
|
|
@@ -741,7 +741,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
741
741
|
// package.json
|
|
742
742
|
var package_default = {
|
|
743
743
|
name: "@uzysjung/agent-harness",
|
|
744
|
-
version: "26.
|
|
744
|
+
version: "26.149.0",
|
|
745
745
|
description: "Curate vetted AI-coding skills & plugins by your tech stack \u2014 install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
|
|
746
746
|
type: "module",
|
|
747
747
|
publishConfig: {
|
|
@@ -776,6 +776,7 @@ var package_default = {
|
|
|
776
776
|
"cost:report": "npm run build && node scripts/context-cost-report.mjs",
|
|
777
777
|
"cost:baseline": "npm run build && node scripts/context-cost-baseline.mjs",
|
|
778
778
|
"assets:history": "bash scripts/asset-history.sh",
|
|
779
|
+
"release:audit": "node scripts/release-audit.mjs",
|
|
779
780
|
security: "npm audit --omit=dev --audit-level=high && npx --yes ecc-agentshield@1.4.0 scan --baseline security-baseline.json --gate",
|
|
780
781
|
"security:baseline": "npx --yes ecc-agentshield@1.4.0 scan --save-baseline security-baseline.json"
|
|
781
782
|
},
|
|
@@ -981,9 +982,9 @@ import {
|
|
|
981
982
|
chmodSync as chmodSync2,
|
|
982
983
|
existsSync as existsSync13,
|
|
983
984
|
mkdirSync as mkdirSync4,
|
|
984
|
-
readdirSync as
|
|
985
|
+
readdirSync as readdirSync4,
|
|
985
986
|
readFileSync as readFileSync12,
|
|
986
|
-
unlinkSync as
|
|
987
|
+
unlinkSync as unlinkSync3,
|
|
987
988
|
writeFileSync as writeFileSync6
|
|
988
989
|
} from "fs";
|
|
989
990
|
import { basename as basename5, dirname as dirname4, join as join12, resolve } from "path";
|
|
@@ -1230,7 +1231,7 @@ function writeRules(harnessRoot, projectDir, writer) {
|
|
|
1230
1231
|
template,
|
|
1231
1232
|
claudeMd,
|
|
1232
1233
|
projectName: basename(projectDir),
|
|
1233
|
-
projectContext: renderFillScaffold()
|
|
1234
|
+
projectContext: renderFillScaffold("antigravity-rule")
|
|
1234
1235
|
});
|
|
1235
1236
|
return writer.write(target, rulesOut) ? target : null;
|
|
1236
1237
|
}
|
|
@@ -1376,7 +1377,7 @@ function runCodexTransform(params) {
|
|
|
1376
1377
|
template: agentsTemplate,
|
|
1377
1378
|
claudeMd,
|
|
1378
1379
|
projectName,
|
|
1379
|
-
projectContext: renderFillScaffold(),
|
|
1380
|
+
projectContext: renderFillScaffold("agents-md"),
|
|
1380
1381
|
// Codex 는 룰 디렉터리가 없다 — 룰이 AGENTS.md 본문에 들어가야 도달한다(§Harness Rules).
|
|
1381
1382
|
harnessRules: renderRulesBlock(portRules(harnessRoot, rules))
|
|
1382
1383
|
});
|
|
@@ -1441,7 +1442,7 @@ function readOptionalJson(path) {
|
|
|
1441
1442
|
|
|
1442
1443
|
// src/opencode/transform.ts
|
|
1443
1444
|
init_esm_shims();
|
|
1444
|
-
import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
1445
|
+
import { existsSync as existsSync7, readdirSync, readFileSync as readFileSync6, unlinkSync } from "fs";
|
|
1445
1446
|
import { basename as basename3, join as join7 } from "path";
|
|
1446
1447
|
|
|
1447
1448
|
// src/opencode/agents-md.ts
|
|
@@ -1455,72 +1456,6 @@ function renderAgentsMd2(params) {
|
|
|
1455
1456
|
return renameSlashes2(replaced);
|
|
1456
1457
|
}
|
|
1457
1458
|
|
|
1458
|
-
// src/opencode/commands.ts
|
|
1459
|
-
init_esm_shims();
|
|
1460
|
-
function renderCommandFromSkill(source, id, opts) {
|
|
1461
|
-
const { description, body } = parseSkillFrontmatter(source);
|
|
1462
|
-
const finalDescription = description || `${id} (dev-method skill, OpenCode command fallback)`;
|
|
1463
|
-
const escapedDesc = finalDescription.replace(/"/g, '\\"');
|
|
1464
|
-
const renamedBody = renameSlashes2(body).trimEnd();
|
|
1465
|
-
const agent = opts?.shellDependent ? "build" : "plan";
|
|
1466
|
-
return [
|
|
1467
|
-
"---",
|
|
1468
|
-
`description: "${escapedDesc}"`,
|
|
1469
|
-
`agent: ${agent}`,
|
|
1470
|
-
"---",
|
|
1471
|
-
"",
|
|
1472
|
-
renamedBody,
|
|
1473
|
-
""
|
|
1474
|
-
].join("\n");
|
|
1475
|
-
}
|
|
1476
|
-
function parseSkillFrontmatter(source) {
|
|
1477
|
-
const lines = source.split(/\r?\n/);
|
|
1478
|
-
if (lines[0] !== "---") {
|
|
1479
|
-
const firstLine = lines[0] ?? "";
|
|
1480
|
-
return { description: firstLine.trim(), body: lines.slice(1).join("\n") };
|
|
1481
|
-
}
|
|
1482
|
-
let secondDelimAt = -1;
|
|
1483
|
-
let description = "";
|
|
1484
|
-
for (let i = 1; i < lines.length; i++) {
|
|
1485
|
-
const line = lines[i] ?? "";
|
|
1486
|
-
if (line === "---") {
|
|
1487
|
-
secondDelimAt = i;
|
|
1488
|
-
break;
|
|
1489
|
-
}
|
|
1490
|
-
const inline = line.match(/^description:\s*(.+)$/);
|
|
1491
|
-
if (!inline) {
|
|
1492
|
-
continue;
|
|
1493
|
-
}
|
|
1494
|
-
const raw = (inline[1] ?? "").trim();
|
|
1495
|
-
if (raw === ">-" || raw === ">" || raw === "|" || raw === "|-") {
|
|
1496
|
-
const collected = [];
|
|
1497
|
-
for (let j3 = i + 1; j3 < lines.length; j3++) {
|
|
1498
|
-
const next = lines[j3] ?? "";
|
|
1499
|
-
if (next === "---") {
|
|
1500
|
-
break;
|
|
1501
|
-
}
|
|
1502
|
-
if (next.trim() === "" || /^\s/.test(next)) {
|
|
1503
|
-
collected.push(next.trim());
|
|
1504
|
-
} else {
|
|
1505
|
-
break;
|
|
1506
|
-
}
|
|
1507
|
-
}
|
|
1508
|
-
description = collected.join(" ").replace(/\s+/g, " ").trim();
|
|
1509
|
-
} else {
|
|
1510
|
-
description = stripQuotes(raw);
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
const body = secondDelimAt >= 0 ? lines.slice(secondDelimAt + 1).join("\n").replace(/^\n+/, "") : source;
|
|
1514
|
-
return { description, body };
|
|
1515
|
-
}
|
|
1516
|
-
function stripQuotes(raw) {
|
|
1517
|
-
const trimmed = raw.trim();
|
|
1518
|
-
if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
1519
|
-
return trimmed.slice(1, -1);
|
|
1520
|
-
}
|
|
1521
|
-
return trimmed;
|
|
1522
|
-
}
|
|
1523
|
-
|
|
1524
1459
|
// src/opencode/opencode-json.ts
|
|
1525
1460
|
init_esm_shims();
|
|
1526
1461
|
function renderOpencodeJson(params) {
|
|
@@ -1564,7 +1499,7 @@ function runOpencodeTransform(params) {
|
|
|
1564
1499
|
template: agentsTemplate,
|
|
1565
1500
|
claudeMd,
|
|
1566
1501
|
projectName,
|
|
1567
|
-
projectContext: renderFillScaffold(),
|
|
1502
|
+
projectContext: renderFillScaffold("agents-md"),
|
|
1568
1503
|
// codex 와 **같은 파일**(프로젝트 루트 `AGENTS.md`)이다. 두 transform 이 서로 다른 본문을
|
|
1569
1504
|
// 쓰면 나중에 도는 쪽이 앞선 쪽을 덮어써, codex+opencode 조합에서 룰이 통째로 사라진다
|
|
1570
1505
|
// (독립 검증 C-1 실측). 같은 내용을 쓰면 순서가 결과를 바꾸지 않는다.
|
|
@@ -1573,27 +1508,37 @@ function runOpencodeTransform(params) {
|
|
|
1573
1508
|
writer.write(agentsMdPath, agentsMdOut);
|
|
1574
1509
|
const opencodeJsonPath = join7(projectDir, "opencode.json");
|
|
1575
1510
|
writer.write(opencodeJsonPath, renderOpencodeJson({ template: opencodeTemplate, mcp }));
|
|
1576
|
-
const
|
|
1577
|
-
if (!refreshOnly) ensureDir(cmdDir);
|
|
1578
|
-
const commandFiles = [];
|
|
1511
|
+
const skillFiles = [];
|
|
1579
1512
|
for (const id of selectedInternalSkills) {
|
|
1580
1513
|
const src = join7(harnessRoot, "templates/skills", id, "SKILL.md");
|
|
1581
1514
|
if (!existsSync7(src)) {
|
|
1582
1515
|
continue;
|
|
1583
1516
|
}
|
|
1584
|
-
const target = join7(
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1517
|
+
const target = join7(projectDir, ".agents", "skills", id, "SKILL.md");
|
|
1518
|
+
if (!writer.write(target, renderBundledSkill(readFileSync6(src, "utf8")))) continue;
|
|
1519
|
+
skillFiles.push(target);
|
|
1520
|
+
}
|
|
1521
|
+
const retiredCommands = [];
|
|
1522
|
+
const cmdDir = join7(projectDir, ".opencode/commands");
|
|
1523
|
+
if (existsSync7(cmdDir)) {
|
|
1524
|
+
for (const entry of readdirSync(cmdDir)) {
|
|
1525
|
+
if (!entry.endsWith(".md")) continue;
|
|
1526
|
+
const id = entry.slice(0, -3);
|
|
1527
|
+
if (!existsSync7(join7(harnessRoot, "templates/skills", id, "SKILL.md"))) continue;
|
|
1528
|
+
const victim = join7(cmdDir, entry);
|
|
1529
|
+
try {
|
|
1530
|
+
backupFile(victim);
|
|
1531
|
+
unlinkSync(victim);
|
|
1532
|
+
retiredCommands.push(victim);
|
|
1533
|
+
} catch {
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1592
1536
|
}
|
|
1593
1537
|
return {
|
|
1594
1538
|
agentsMdPath,
|
|
1595
1539
|
opencodeJsonPath,
|
|
1596
|
-
|
|
1540
|
+
skillFiles,
|
|
1541
|
+
retiredCommands,
|
|
1597
1542
|
ownership: writer.result()
|
|
1598
1543
|
};
|
|
1599
1544
|
}
|
|
@@ -1754,8 +1699,8 @@ function addGitignoreEnv(projectDir) {
|
|
|
1754
1699
|
if (GITIGNORE_ENV_PATTERN.test(content)) {
|
|
1755
1700
|
return false;
|
|
1756
1701
|
}
|
|
1757
|
-
const
|
|
1758
|
-
appendFileSync(path, `${
|
|
1702
|
+
const sep3 = content.endsWith("\n") ? "" : "\n";
|
|
1703
|
+
appendFileSync(path, `${sep3}
|
|
1759
1704
|
# Secret env (auto-added by agent-harness install)
|
|
1760
1705
|
.env
|
|
1761
1706
|
`);
|
|
@@ -1776,9 +1721,9 @@ function addGitignoreAgentArtifacts(projectDir) {
|
|
|
1776
1721
|
if (missing.length === 0) {
|
|
1777
1722
|
return [];
|
|
1778
1723
|
}
|
|
1779
|
-
const
|
|
1724
|
+
const sep3 = content.endsWith("\n") ? "" : "\n";
|
|
1780
1725
|
const block = [GITIGNORE_AGENT_ARTIFACT_HEADER, ...missing].join("\n");
|
|
1781
|
-
appendFileSync(path, `${
|
|
1726
|
+
appendFileSync(path, `${sep3}
|
|
1782
1727
|
${block}
|
|
1783
1728
|
`);
|
|
1784
1729
|
return [...missing];
|
|
@@ -1787,7 +1732,7 @@ ${block}
|
|
|
1787
1732
|
// src/external-installer.ts
|
|
1788
1733
|
init_esm_shims();
|
|
1789
1734
|
import { spawnSync } from "child_process";
|
|
1790
|
-
import { existsSync as existsSync9, readdirSync, readFileSync as readFileSync8 } from "fs";
|
|
1735
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2, readFileSync as readFileSync8 } from "fs";
|
|
1791
1736
|
import { homedir as homedir2 } from "os";
|
|
1792
1737
|
import { join as join9 } from "path";
|
|
1793
1738
|
var DEFAULT_SPAWN_TIMEOUT_MS = 12e4;
|
|
@@ -1890,6 +1835,45 @@ var SKILLS_CLI_VERSION = "1.5.11";
|
|
|
1890
1835
|
function skillsCliSpec() {
|
|
1891
1836
|
return `skills@${SKILLS_CLI_VERSION}`;
|
|
1892
1837
|
}
|
|
1838
|
+
function refreshExternalSkills(projectDir, deps = {}) {
|
|
1839
|
+
const none = { attempted: 0, refreshed: 0, failed: [], notInCatalog: [] };
|
|
1840
|
+
const log = (deps.readLog ?? readInstallLog)(projectDir);
|
|
1841
|
+
if (!log) {
|
|
1842
|
+
return { ...none, unknown: true };
|
|
1843
|
+
}
|
|
1844
|
+
const installedIds = new Set(log.assets.filter((a) => a.method === "skill").map((a) => a.id));
|
|
1845
|
+
const catalog = deps.assets ?? EXTERNAL_ASSETS;
|
|
1846
|
+
const targets = catalog.filter((a) => a.method.kind === "skill" && installedIds.has(a.id));
|
|
1847
|
+
const inCatalog = new Set(targets.map((a) => a.id));
|
|
1848
|
+
const notInCatalog = [...installedIds].filter((id) => !inCatalog.has(id));
|
|
1849
|
+
if (targets.length === 0) {
|
|
1850
|
+
return { ...none, notInCatalog, unknown: false };
|
|
1851
|
+
}
|
|
1852
|
+
const report = (deps.run ?? runExternalInstall)(
|
|
1853
|
+
{
|
|
1854
|
+
tracks: log.spec.tracks,
|
|
1855
|
+
// `forceInclude` 가 조건 판정을 앞지르므로 옵션 값은 결과에 영향을 주지 않는다.
|
|
1856
|
+
options: DEFAULT_OPTIONS,
|
|
1857
|
+
cli: log.spec.cli,
|
|
1858
|
+
userOverride: { forceInclude: [...installedIds], forceExclude: [] },
|
|
1859
|
+
scope: log.scope,
|
|
1860
|
+
projectDir
|
|
1861
|
+
},
|
|
1862
|
+
{
|
|
1863
|
+
assets: targets,
|
|
1864
|
+
...deps.spawn ? { spawn: deps.spawn } : {},
|
|
1865
|
+
...deps.log ? { log: deps.log } : {},
|
|
1866
|
+
...deps.warn ? { warn: deps.warn } : {}
|
|
1867
|
+
}
|
|
1868
|
+
);
|
|
1869
|
+
return {
|
|
1870
|
+
attempted: report.attempted.length,
|
|
1871
|
+
refreshed: report.succeeded,
|
|
1872
|
+
failed: report.attempted.filter((r) => !r.ok).map((r) => ({ id: r.asset.id, message: r.message ?? "failed" })),
|
|
1873
|
+
notInCatalog,
|
|
1874
|
+
unknown: false
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1893
1877
|
function buildSkillArgs(method, cli2, scope) {
|
|
1894
1878
|
const args = [skillsCliSpec(), "add", method.source];
|
|
1895
1879
|
if (method.skill) {
|
|
@@ -1899,6 +1883,7 @@ function buildSkillArgs(method, cli2, scope) {
|
|
|
1899
1883
|
for (const c3 of cli2) {
|
|
1900
1884
|
args.push("--agent", SKILLS_CLI_AGENT_MAP[c3] ?? c3);
|
|
1901
1885
|
}
|
|
1886
|
+
args.push("--copy");
|
|
1902
1887
|
}
|
|
1903
1888
|
if (scope === "global") {
|
|
1904
1889
|
args.push("-g");
|
|
@@ -1958,7 +1943,7 @@ function detectVersion(method, spawn) {
|
|
|
1958
1943
|
const marketplaceShort = method.pluginId.slice(at2 + 1);
|
|
1959
1944
|
const cacheBase = join9(homedir2(), ".claude/plugins/cache", marketplaceShort, plugin);
|
|
1960
1945
|
if (!existsSync9(cacheBase)) return void 0;
|
|
1961
|
-
const versions =
|
|
1946
|
+
const versions = readdirSync2(cacheBase).filter((v2) => /^\d/.test(v2)).sort();
|
|
1962
1947
|
return versions.at(-1);
|
|
1963
1948
|
}
|
|
1964
1949
|
case "npm": {
|
|
@@ -2107,9 +2092,9 @@ import {
|
|
|
2107
2092
|
copyFileSync,
|
|
2108
2093
|
existsSync as existsSync12,
|
|
2109
2094
|
mkdirSync as mkdirSync3,
|
|
2110
|
-
readdirSync as
|
|
2095
|
+
readdirSync as readdirSync3,
|
|
2111
2096
|
readFileSync as readFileSync11,
|
|
2112
|
-
unlinkSync,
|
|
2097
|
+
unlinkSync as unlinkSync2,
|
|
2113
2098
|
writeFileSync as writeFileSync5
|
|
2114
2099
|
} from "fs";
|
|
2115
2100
|
import { basename as basename4, dirname as dirname3, join as join11 } from "path";
|
|
@@ -2119,10 +2104,11 @@ function buildUpdateSpec(projectDir, tracks) {
|
|
|
2119
2104
|
tracks: [...tracks],
|
|
2120
2105
|
options: DEFAULT_OPTIONS,
|
|
2121
2106
|
cli: ["claude"],
|
|
2122
|
-
projectDir
|
|
2107
|
+
projectDir,
|
|
2108
|
+
scope: readInstallLog(projectDir)?.scope ?? "project"
|
|
2123
2109
|
};
|
|
2124
2110
|
}
|
|
2125
|
-
function runUpdateMode(projectDir, templatesDir, harnessRoot) {
|
|
2111
|
+
function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
2126
2112
|
const claudeDir = join11(projectDir, ".claude");
|
|
2127
2113
|
const report = {
|
|
2128
2114
|
updated: {},
|
|
@@ -2137,6 +2123,10 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot) {
|
|
|
2137
2123
|
policyBackedUp: [],
|
|
2138
2124
|
externalUpdated: 0,
|
|
2139
2125
|
externalBackedUp: [],
|
|
2126
|
+
externalSkillsRefreshed: 0,
|
|
2127
|
+
externalSkillsFailed: [],
|
|
2128
|
+
externalSkillsNotInCatalog: [],
|
|
2129
|
+
externalSkillsUnknown: false,
|
|
2140
2130
|
foreignOwned: [],
|
|
2141
2131
|
installedNew: [],
|
|
2142
2132
|
restored: [],
|
|
@@ -2192,6 +2182,11 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot) {
|
|
|
2192
2182
|
if (!saidBySlotRow.has(f) && !merged.includes(f)) merged.push(f);
|
|
2193
2183
|
}
|
|
2194
2184
|
report.foreignOwned = merged;
|
|
2185
|
+
const skillRefresh = (deps.refreshSkills ?? refreshExternalSkills)(projectDir);
|
|
2186
|
+
report.externalSkillsRefreshed = skillRefresh.refreshed;
|
|
2187
|
+
report.externalSkillsFailed = skillRefresh.failed;
|
|
2188
|
+
report.externalSkillsNotInCatalog = skillRefresh.notInCatalog;
|
|
2189
|
+
report.externalSkillsUnknown = skillRefresh.unknown;
|
|
2195
2190
|
return report;
|
|
2196
2191
|
}
|
|
2197
2192
|
function installNewAssets(projectDir, templatesDir) {
|
|
@@ -2324,7 +2319,7 @@ function updateDir(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
|
|
|
2324
2319
|
if (!existsSync12(target) || !existsSync12(source)) return { updated: 0, backedUp: [] };
|
|
2325
2320
|
let updated = 0;
|
|
2326
2321
|
const backedUp = [];
|
|
2327
|
-
for (const file of
|
|
2322
|
+
for (const file of readdirSync3(target)) {
|
|
2328
2323
|
if (!file.endsWith(ext)) continue;
|
|
2329
2324
|
const targetFile = join11(target, file);
|
|
2330
2325
|
const sourceFile = join11(source, file);
|
|
@@ -2348,7 +2343,7 @@ function syncSkills(targetDir, sourceDir, baseline, now = /* @__PURE__ */ new Da
|
|
|
2348
2343
|
const backedUp = [];
|
|
2349
2344
|
const skippedLinks = [];
|
|
2350
2345
|
const foreignOwned = [];
|
|
2351
|
-
for (const skill of
|
|
2346
|
+
for (const skill of readdirSync3(sourceDir, { withFileTypes: true })) {
|
|
2352
2347
|
if (!skill.isDirectory()) continue;
|
|
2353
2348
|
const targetSkill = join11(targetDir, skill.name);
|
|
2354
2349
|
if (occupiedByNonDirectory(targetSkill)) {
|
|
@@ -2418,7 +2413,7 @@ function refreshSkillBaseline(projectDir) {
|
|
|
2418
2413
|
function pruneOrphans(target, source, ext, ctx, now = /* @__PURE__ */ new Date()) {
|
|
2419
2414
|
if (!existsSync12(target) || !existsSync12(source)) return [];
|
|
2420
2415
|
const removed = [];
|
|
2421
|
-
for (const file of
|
|
2416
|
+
for (const file of readdirSync3(target)) {
|
|
2422
2417
|
if (!file.endsWith(ext)) continue;
|
|
2423
2418
|
const sourceFile = join11(source, file);
|
|
2424
2419
|
if (!existsSync12(sourceFile)) {
|
|
@@ -2427,7 +2422,7 @@ function pruneOrphans(target, source, ext, ctx, now = /* @__PURE__ */ new Date()
|
|
|
2427
2422
|
try {
|
|
2428
2423
|
const current = readFileSync11(targetFile, "utf8");
|
|
2429
2424
|
if (!isHarnessOwned2(ctx, file, current)) backupFile(targetFile, now);
|
|
2430
|
-
|
|
2425
|
+
unlinkSync2(targetFile);
|
|
2431
2426
|
removed.push(file);
|
|
2432
2427
|
} catch {
|
|
2433
2428
|
}
|
|
@@ -2440,7 +2435,7 @@ function retireMcpAllowlist(projectDir, now = /* @__PURE__ */ new Date()) {
|
|
|
2440
2435
|
if (!existsSync12(target)) return null;
|
|
2441
2436
|
try {
|
|
2442
2437
|
const backup = backupFile(target, now);
|
|
2443
|
-
|
|
2438
|
+
unlinkSync2(target);
|
|
2444
2439
|
return backup;
|
|
2445
2440
|
} catch {
|
|
2446
2441
|
return null;
|
|
@@ -2604,7 +2599,7 @@ function applySupersededCleanup(spec, projectDir, found) {
|
|
|
2604
2599
|
const kept = [];
|
|
2605
2600
|
for (const asset of found) {
|
|
2606
2601
|
try {
|
|
2607
|
-
|
|
2602
|
+
unlinkSync3(join12(projectDir, asset.target));
|
|
2608
2603
|
removed.push(asset.target);
|
|
2609
2604
|
} catch {
|
|
2610
2605
|
kept.push(asset.target);
|
|
@@ -2942,7 +2937,7 @@ function chmodHooksSync(hookDir) {
|
|
|
2942
2937
|
}
|
|
2943
2938
|
}
|
|
2944
2939
|
function listHookFiles(hookDir) {
|
|
2945
|
-
return
|
|
2940
|
+
return readdirSync4(hookDir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".sh")).map((e2) => resolve(hookDir, e2.name));
|
|
2946
2941
|
}
|
|
2947
2942
|
|
|
2948
2943
|
// src/commands/install-render.ts
|
|
@@ -3116,7 +3111,24 @@ function renderCliArtifacts(log, spec, report) {
|
|
|
3116
3111
|
}
|
|
3117
3112
|
if (report.opencode) {
|
|
3118
3113
|
log(assetRow("success", "opencode.json", "$schema + 5 keys"));
|
|
3119
|
-
|
|
3114
|
+
if (report.opencode.skillFiles.length > 0) {
|
|
3115
|
+
log(
|
|
3116
|
+
assetRow(
|
|
3117
|
+
"success",
|
|
3118
|
+
".agents/skills/",
|
|
3119
|
+
`${report.opencode.skillFiles.length} dev-method skills (codex\xB7antigravity \uC640 \uAC19\uC740 \uC790\uB9AC)`
|
|
3120
|
+
)
|
|
3121
|
+
);
|
|
3122
|
+
}
|
|
3123
|
+
if (report.opencode.retiredCommands.length > 0) {
|
|
3124
|
+
log(
|
|
3125
|
+
assetRow(
|
|
3126
|
+
"skip",
|
|
3127
|
+
".opencode/commands/",
|
|
3128
|
+
`${report.opencode.retiredCommands.length} retired \xB7 \uAC19\uC740 \uC2A4\uD0AC\uC774 .agents/skills/ \uB85C \uC62E\uACA8\uC84C\uB2E4 (\uBC31\uC5C5 \uB0A8\uAE40)`
|
|
3129
|
+
)
|
|
3130
|
+
);
|
|
3131
|
+
}
|
|
3120
3132
|
}
|
|
3121
3133
|
if (report.antigravity) {
|
|
3122
3134
|
if (report.antigravity.rulesFile) {
|
|
@@ -3350,6 +3362,39 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3350
3362
|
)
|
|
3351
3363
|
);
|
|
3352
3364
|
}
|
|
3365
|
+
if (baseline.updateMode.externalSkillsRefreshed > 0) {
|
|
3366
|
+
log(
|
|
3367
|
+
assetRow(
|
|
3368
|
+
"success",
|
|
3369
|
+
"external skills",
|
|
3370
|
+
`${baseline.updateMode.externalSkillsRefreshed} refreshed from upstream`
|
|
3371
|
+
)
|
|
3372
|
+
);
|
|
3373
|
+
}
|
|
3374
|
+
if (baseline.updateMode.externalSkillsFailed.length > 0) {
|
|
3375
|
+
const failed = baseline.updateMode.externalSkillsFailed;
|
|
3376
|
+
const head = failed[0];
|
|
3377
|
+
const meta = failed.length === 1 ? `${head.id}: ${head.message}` : `${failed.map((f) => f.id).join(", ")} (${failed.length}\uAC74) \xB7 \uC608: ${head.message}`;
|
|
3378
|
+
log(assetRow("skip", "external skills", `${meta} \xB7 \uB2E4\uC74C update \uC5D0\uC11C \uB2E4\uC2DC \uC2DC\uB3C4\uD55C\uB2E4`));
|
|
3379
|
+
}
|
|
3380
|
+
if (baseline.updateMode.externalSkillsNotInCatalog.length > 0) {
|
|
3381
|
+
log(
|
|
3382
|
+
assetRow(
|
|
3383
|
+
"skip",
|
|
3384
|
+
"external skills",
|
|
3385
|
+
`${baseline.updateMode.externalSkillsNotInCatalog.join(", ")} \xB7 \uCE74\uD0C8\uB85C\uADF8\uC5D0 \uC5C6\uC5B4 \uAC31\uC2E0 \uB300\uC0C1\uC774 \uC544\uB2C8\uB2E4`
|
|
3386
|
+
)
|
|
3387
|
+
);
|
|
3388
|
+
}
|
|
3389
|
+
if (baseline.updateMode.externalSkillsUnknown) {
|
|
3390
|
+
log(
|
|
3391
|
+
assetRow(
|
|
3392
|
+
"skip",
|
|
3393
|
+
"external skills",
|
|
3394
|
+
"\uC124\uCE58 \uAE30\uB85D\uC774 \uC5C6\uC5B4 \uAC31\uC2E0 \uB300\uC0C1\uC744 \uD310\uC815\uD560 \uC218 \uC5C6\uB2E4 \xB7 `agent-harness install` \uB85C \uB2E4\uC2DC \uAE54\uBA74 \uAE30\uB85D\uB41C\uB2E4"
|
|
3395
|
+
)
|
|
3396
|
+
);
|
|
3397
|
+
}
|
|
3353
3398
|
return;
|
|
3354
3399
|
}
|
|
3355
3400
|
if (baseline.backups) {
|
|
@@ -3816,8 +3861,16 @@ function registerListCommand(cli2) {
|
|
|
3816
3861
|
// src/commands/uninstall.ts
|
|
3817
3862
|
init_esm_shims();
|
|
3818
3863
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3819
|
-
import {
|
|
3820
|
-
|
|
3864
|
+
import {
|
|
3865
|
+
existsSync as existsSync15,
|
|
3866
|
+
lstatSync as lstatSync2,
|
|
3867
|
+
readdirSync as readdirSync5,
|
|
3868
|
+
readFileSync as readFileSync14,
|
|
3869
|
+
rmdirSync,
|
|
3870
|
+
rmSync,
|
|
3871
|
+
writeFileSync as writeFileSync7
|
|
3872
|
+
} from "fs";
|
|
3873
|
+
import { dirname as dirname5, join as join14, relative as relative2, resolve as resolve4, sep as sep2 } from "path";
|
|
3821
3874
|
|
|
3822
3875
|
// src/uninstall-interactive.ts
|
|
3823
3876
|
init_esm_shims();
|
|
@@ -4953,8 +5006,13 @@ function uninstallAction(options, deps = {}) {
|
|
|
4953
5006
|
const logSurvives = selectedIds !== null;
|
|
4954
5007
|
const { succeeded, failed, removedIds } = executeReverse(plan, log, logSurvives);
|
|
4955
5008
|
if (!keepTemplates) {
|
|
4956
|
-
const { rootClaudeMdKept, importStripped } = removeTemplates(
|
|
5009
|
+
const { rootClaudeMdKept, importStripped, external } = removeTemplates(
|
|
5010
|
+
installLog,
|
|
5011
|
+
projectDir,
|
|
5012
|
+
rm
|
|
5013
|
+
);
|
|
4957
5014
|
log(` ${status.success(`templates removed: ${formatTemplateList(installLog)}`)}`);
|
|
5015
|
+
for (const line of externalRemovalLines(external)) log(line);
|
|
4958
5016
|
if (importStripped) {
|
|
4959
5017
|
log(` ${status.success("CLAUDE.md \u2014 harness @import removed (\uBCF8\uBB38 \uBCF4\uC874)")}`);
|
|
4960
5018
|
}
|
|
@@ -5080,6 +5138,7 @@ function dryRunLines(plan, installLog, projectDir, keepTemplates, rootFiles) {
|
|
|
5080
5138
|
if (hasRootImport(projectDir)) {
|
|
5081
5139
|
lines.push(" \u25CB strip harness @import from CLAUDE.md (\uBCF8\uBB38 \uBCF4\uC874)");
|
|
5082
5140
|
}
|
|
5141
|
+
lines.push(...previewExternalLines(installLog, projectDir));
|
|
5083
5142
|
}
|
|
5084
5143
|
lines.push(...advisoryLines(plan, projectDir, rootFiles), "");
|
|
5085
5144
|
return lines;
|
|
@@ -5205,13 +5264,46 @@ function removeTemplates(log, projectDir, rm) {
|
|
|
5205
5264
|
rm(join14(projectDir, log.templates.claudeDir));
|
|
5206
5265
|
if (log.templates.codexDir) rm(join14(projectDir, log.templates.codexDir));
|
|
5207
5266
|
if (log.templates.opencodeDir) rm(join14(projectDir, log.templates.opencodeDir));
|
|
5267
|
+
const external = removeExternalFiles(log, projectDir, rm);
|
|
5208
5268
|
const importStripped = stripRootImport(projectDir);
|
|
5209
5269
|
const rootMd = log.templates.rootClaudeMd;
|
|
5210
5270
|
if (rootMd) {
|
|
5211
|
-
if (rootClaudeMdModified(log, projectDir))
|
|
5271
|
+
if (rootClaudeMdModified(log, projectDir))
|
|
5272
|
+
return { rootClaudeMdKept: true, importStripped, external };
|
|
5212
5273
|
rm(join14(projectDir, rootMd.path));
|
|
5213
5274
|
}
|
|
5214
|
-
return { rootClaudeMdKept: false, importStripped };
|
|
5275
|
+
return { rootClaudeMdKept: false, importStripped, external };
|
|
5276
|
+
}
|
|
5277
|
+
function removeExternalFiles(log, projectDir, rm) {
|
|
5278
|
+
const removed = [];
|
|
5279
|
+
const kept = [];
|
|
5280
|
+
for (const { path, sha256 } of log.externalFiles ?? []) {
|
|
5281
|
+
const abs = join14(projectDir, path);
|
|
5282
|
+
if (!existsSync15(abs)) continue;
|
|
5283
|
+
if (!lstatSync2(abs).isFile()) continue;
|
|
5284
|
+
if (hashContent(readFileSync14(abs, "utf8")) !== sha256) {
|
|
5285
|
+
kept.push(path);
|
|
5286
|
+
continue;
|
|
5287
|
+
}
|
|
5288
|
+
rm(abs);
|
|
5289
|
+
removed.push(path);
|
|
5290
|
+
}
|
|
5291
|
+
for (const path of removed) pruneEmptyDirsUpward(projectDir, dirname5(join14(projectDir, path)));
|
|
5292
|
+
return { removed, kept };
|
|
5293
|
+
}
|
|
5294
|
+
function pruneEmptyDirsUpward(projectDir, startDir) {
|
|
5295
|
+
let cursor = startDir;
|
|
5296
|
+
while (true) {
|
|
5297
|
+
const rel = relative2(projectDir, cursor);
|
|
5298
|
+
if (rel === "" || rel.startsWith(`..${sep2}`) || rel === "..") return;
|
|
5299
|
+
try {
|
|
5300
|
+
if (readdirSync5(cursor).length > 0) return;
|
|
5301
|
+
rmdirSync(cursor);
|
|
5302
|
+
} catch {
|
|
5303
|
+
return;
|
|
5304
|
+
}
|
|
5305
|
+
cursor = dirname5(cursor);
|
|
5306
|
+
}
|
|
5215
5307
|
}
|
|
5216
5308
|
function stripRootImport(projectDir) {
|
|
5217
5309
|
const current = readRootClaudeMd(projectDir);
|
|
@@ -5224,6 +5316,33 @@ function stripRootImport(projectDir) {
|
|
|
5224
5316
|
return false;
|
|
5225
5317
|
}
|
|
5226
5318
|
}
|
|
5319
|
+
function previewExternalLines(installLog, projectDir) {
|
|
5320
|
+
const lines = [];
|
|
5321
|
+
let removable = 0;
|
|
5322
|
+
for (const { path, sha256 } of installLog.externalFiles ?? []) {
|
|
5323
|
+
const abs = join14(projectDir, path);
|
|
5324
|
+
if (!existsSync15(abs)) continue;
|
|
5325
|
+
if (hashContent(readFileSync14(abs, "utf8")) !== sha256) {
|
|
5326
|
+
lines.push(` \u25CB keep ${path} (modified since install \u2014 preserved)`);
|
|
5327
|
+
continue;
|
|
5328
|
+
}
|
|
5329
|
+
removable += 1;
|
|
5330
|
+
}
|
|
5331
|
+
if (removable > 0) lines.unshift(` \u25CB remove ${removable} CLI output file(s)`);
|
|
5332
|
+
return lines;
|
|
5333
|
+
}
|
|
5334
|
+
function externalRemovalLines(external) {
|
|
5335
|
+
const lines = [];
|
|
5336
|
+
if (external.removed.length > 0) {
|
|
5337
|
+
lines.push(` ${status.success(`CLI outputs removed: ${external.removed.length} file(s)`)}`);
|
|
5338
|
+
}
|
|
5339
|
+
for (const path of external.kept) {
|
|
5340
|
+
lines.push(
|
|
5341
|
+
` ${c.yellow("\u2298")} ${path} kept \u2014 modified since install. Remove manually if intended.`
|
|
5342
|
+
);
|
|
5343
|
+
}
|
|
5344
|
+
return lines;
|
|
5345
|
+
}
|
|
5227
5346
|
function hasRootImport(projectDir) {
|
|
5228
5347
|
const current = readRootClaudeMd(projectDir);
|
|
5229
5348
|
return current !== null && stripHarnessImport(current) !== null;
|