@uzysjung/agent-harness 26.150.0 → 26.152.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.ko.md +2 -2
- package/README.md +2 -2
- package/dist/{chunk-NKBUDHPC.js → chunk-EQDC2AAU.js} +125 -69
- package/dist/chunk-EQDC2AAU.js.map +1 -0
- package/dist/index.js +269 -284
- package/dist/index.js.map +1 -1
- package/dist/trust-tier-drift.js +1 -1
- package/package.json +1 -1
- package/templates/CLAUDE.md +145 -164
- package/templates/agents/reviewer.md +4 -5
- package/templates/antigravity/AGENTS.md.template +3 -23
- package/templates/codex/AGENTS.md.template +5 -56
- package/templates/hooks/protect-files.sh +4 -0
- package/templates/opencode/AGENTS.md.template +4 -52
- package/templates/opencode/opencode.json.template +0 -8
- package/templates/rules/change-management.md +2 -4
- package/templates/rules/cli-development.md +2 -4
- package/templates/rules/doc-governance.md +3 -1
- package/templates/rules/git-policy.md +1 -1
- package/templates/rules/ship-checklist.md +3 -3
- package/templates/rules/test-policy.md +3 -8
- package/templates/settings.json +1 -16
- package/templates/skills/audit-harness-fit/README.md +113 -0
- package/templates/skills/audit-harness-fit/SKILL.md +64 -433
- package/templates/skills/audit-harness-fit/evals/scenarios.yaml +222 -0
- package/templates/skills/audit-harness-fit/references/apply.md +66 -0
- package/templates/skills/audit-harness-fit/references/audit.md +160 -0
- package/templates/skills/audit-harness-fit/references/populate.md +74 -0
- package/templates/skills/audit-harness-fit/references/verification.md +123 -0
- package/templates/skills/audit-service-gaps/SKILL.md +0 -2
- package/templates/skills/compaction-handoff/SKILL.md +2 -2
- package/templates/skills/model-orchestration/SKILL.md +13 -24
- package/templates/skills/natural-korean/SKILL.md +45 -0
- package/templates/skills/north-star/references/roadmap-method.md +2 -2
- package/templates/skills/{task-brief → objective-brief}/SKILL.md +17 -16
- package/templates/skills/recurrence-prevention/SKILL.md +16 -16
- package/templates/track-mcp-map.tsv +1 -1
- package/dist/chunk-NKBUDHPC.js.map +0 -1
- package/templates/agents/build-error-resolver.md +0 -111
- package/templates/agents/code-reviewer.md +0 -237
- package/templates/agents/plan-checker.md +0 -116
- package/templates/agents/security-reviewer.md +0 -108
- package/templates/agents/silent-failure-hunter.md +0 -50
- package/templates/hooks/task-brief-nudge.sh +0 -57
- package/templates/skills/agent-introspection-debugging/SKILL.md +0 -153
- package/templates/skills/audit-harness-fit/references/official-criteria.md +0 -367
- package/templates/skills/continuous-learning-v2/SKILL.md +0 -361
- package/templates/skills/continuous-learning-v2/agents/observer-loop.sh +0 -362
- package/templates/skills/continuous-learning-v2/agents/observer.md +0 -189
- package/templates/skills/continuous-learning-v2/agents/session-guardian.sh +0 -150
- package/templates/skills/continuous-learning-v2/agents/start-observer.sh +0 -252
- package/templates/skills/continuous-learning-v2/config.json +0 -8
- package/templates/skills/continuous-learning-v2/hooks/observe.sh +0 -585
- package/templates/skills/continuous-learning-v2/scripts/detect-project.sh +0 -322
- package/templates/skills/continuous-learning-v2/scripts/instinct-cli.py +0 -1956
- package/templates/skills/continuous-learning-v2/scripts/lib/homunculus-dir.sh +0 -31
- package/templates/skills/continuous-learning-v2/scripts/migrate-homunculus.sh +0 -68
- package/templates/skills/continuous-learning-v2/scripts/test_parse_instinct.py +0 -1420
- package/templates/skills/deep-research/SKILL.md +0 -179
- package/templates/skills/deep-research/agents/openai.yaml +0 -7
- package/templates/skills/eval-harness/SKILL.md +0 -306
- package/templates/skills/eval-harness/agents/openai.yaml +0 -7
- package/templates/skills/humanize-korean/SKILL.md +0 -228
- package/templates/skills/spec-scaling/SKILL.md +0 -89
- package/templates/skills/strategic-compact/SKILL.md +0 -145
- package/templates/skills/strategic-compact/suggest-compact.sh +0 -54
- package/templates/skills/verification-loop/SKILL.md +0 -250
- package/templates/skills/verification-loop/agents/openai.yaml +0 -7
- package/templates/skills/verification-loop/references/tracks.md +0 -49
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
CATEGORIES,
|
|
5
5
|
CATEGORY_TITLES,
|
|
6
6
|
CLI_BASES,
|
|
7
|
+
CONTINUOUS_SKILLS,
|
|
7
8
|
DEFAULT_OPTIONS,
|
|
8
9
|
DEV_METHOD_SKILL_IDS,
|
|
9
10
|
EXTERNAL_ASSETS,
|
|
@@ -12,6 +13,10 @@ import {
|
|
|
12
13
|
INSTALL_LOG_DIR,
|
|
13
14
|
INTERNAL_BUNDLED_SKILL_IDS,
|
|
14
15
|
POLICY_DIRS,
|
|
16
|
+
RENAMED_SKILL_IDS,
|
|
17
|
+
RETIRED_AGENTS,
|
|
18
|
+
RETIRED_AGENT_IDS,
|
|
19
|
+
RETIRED_SKILL_IDS,
|
|
15
20
|
TRACKS,
|
|
16
21
|
__commonJS,
|
|
17
22
|
__toESM,
|
|
@@ -56,8 +61,9 @@ import {
|
|
|
56
61
|
stripHarnessImport,
|
|
57
62
|
summarizeContextCost,
|
|
58
63
|
upsertHarnessImport,
|
|
64
|
+
withContinuousSkillsNote,
|
|
59
65
|
writeInstallLog
|
|
60
|
-
} from "./chunk-
|
|
66
|
+
} from "./chunk-EQDC2AAU.js";
|
|
61
67
|
|
|
62
68
|
// node_modules/sisteransi/src/index.js
|
|
63
69
|
var require_src = __commonJS({
|
|
@@ -742,7 +748,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
742
748
|
// package.json
|
|
743
749
|
var package_default = {
|
|
744
750
|
name: "@uzysjung/agent-harness",
|
|
745
|
-
version: "26.
|
|
751
|
+
version: "26.152.0",
|
|
746
752
|
description: "Curate vetted AI-coding skills & plugins by your tech stack \u2014 install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
|
|
747
753
|
type: "module",
|
|
748
754
|
publishConfig: {
|
|
@@ -980,12 +986,11 @@ function padDisplay(s, width) {
|
|
|
980
986
|
// src/installer.ts
|
|
981
987
|
init_esm_shims();
|
|
982
988
|
import {
|
|
983
|
-
chmodSync as
|
|
989
|
+
chmodSync as chmodSync3,
|
|
984
990
|
existsSync as existsSync13,
|
|
985
991
|
mkdirSync as mkdirSync4,
|
|
986
992
|
readdirSync as readdirSync4,
|
|
987
993
|
readFileSync as readFileSync12,
|
|
988
|
-
unlinkSync as unlinkSync3,
|
|
989
994
|
writeFileSync as writeFileSync6
|
|
990
995
|
} from "fs";
|
|
991
996
|
import { basename as basename5, dirname as dirname4, join as join12, resolve } from "path";
|
|
@@ -1033,8 +1038,8 @@ init_esm_shims();
|
|
|
1033
1038
|
|
|
1034
1039
|
// src/antigravity/transform.ts
|
|
1035
1040
|
init_esm_shims();
|
|
1036
|
-
import { existsSync as
|
|
1037
|
-
import { basename, join as
|
|
1041
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
1042
|
+
import { basename, join as join5 } from "path";
|
|
1038
1043
|
|
|
1039
1044
|
// src/codex/agents-md.ts
|
|
1040
1045
|
init_esm_shims();
|
|
@@ -1049,6 +1054,8 @@ function renderAgentsMd(params) {
|
|
|
1049
1054
|
|
|
1050
1055
|
// src/codex/skills.ts
|
|
1051
1056
|
init_esm_shims();
|
|
1057
|
+
import { chmodSync, existsSync as existsSync2, readFileSync } from "fs";
|
|
1058
|
+
import { join as join2 } from "path";
|
|
1052
1059
|
function renderBundledSkill(source) {
|
|
1053
1060
|
const trimmed = source.trimEnd();
|
|
1054
1061
|
const lines = trimmed.split(/\r?\n/);
|
|
@@ -1076,16 +1083,46 @@ ${portBody(body)}
|
|
|
1076
1083
|
function portBody(body) {
|
|
1077
1084
|
return renameSlashes(body).replace(/CLAUDE_PROJECT_DIR/g, "CODEX_PROJECT_DIR").trimEnd();
|
|
1078
1085
|
}
|
|
1086
|
+
function writeBundledSkillDirs(params) {
|
|
1087
|
+
const { harnessRoot, projectDir, skillIds, writer } = params;
|
|
1088
|
+
const written = [];
|
|
1089
|
+
for (const id of skillIds) {
|
|
1090
|
+
const srcDir = join2(harnessRoot, "templates/skills", id);
|
|
1091
|
+
const srcSkill = join2(srcDir, "SKILL.md");
|
|
1092
|
+
if (!existsSync2(srcSkill)) {
|
|
1093
|
+
continue;
|
|
1094
|
+
}
|
|
1095
|
+
const targetDir = join2(projectDir, ".agents", "skills", id);
|
|
1096
|
+
const targetSkill = join2(targetDir, "SKILL.md");
|
|
1097
|
+
if (!writer.write(targetSkill, renderBundledSkill(readFileSync(srcSkill, "utf8")))) {
|
|
1098
|
+
continue;
|
|
1099
|
+
}
|
|
1100
|
+
written.push(targetSkill);
|
|
1101
|
+
for (const rel of listFilesRecursive(srcDir)) {
|
|
1102
|
+
if (rel === "SKILL.md") continue;
|
|
1103
|
+
if (rel.split("/").some((segment) => segment.startsWith("."))) continue;
|
|
1104
|
+
const segments = rel.split("/");
|
|
1105
|
+
const raw = readFileSync(join2(srcDir, ...segments), "utf8");
|
|
1106
|
+
const ported = rel.endsWith(".md") || rel.endsWith(".sh") ? `${portBody(raw)}
|
|
1107
|
+
` : raw;
|
|
1108
|
+
const target = join2(targetDir, ...segments);
|
|
1109
|
+
if (!writer.write(target, ported, { createInRefresh: true })) continue;
|
|
1110
|
+
if (rel.endsWith(".sh")) chmodSync(target, 493);
|
|
1111
|
+
written.push(target);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
return written;
|
|
1115
|
+
}
|
|
1079
1116
|
|
|
1080
1117
|
// src/owned-write.ts
|
|
1081
1118
|
init_esm_shims();
|
|
1082
|
-
import { existsSync as
|
|
1119
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
1083
1120
|
import { dirname, relative, sep } from "path";
|
|
1084
1121
|
|
|
1085
1122
|
// src/foreign-slot.ts
|
|
1086
1123
|
init_esm_shims();
|
|
1087
1124
|
import { lstatSync } from "fs";
|
|
1088
|
-
import { join as
|
|
1125
|
+
import { join as join3 } from "path";
|
|
1089
1126
|
var SKILL_SLOT = /^((?:\.claude|\.agents)\/skills\/[^/]+)(?:\/|$)/;
|
|
1090
1127
|
function occupiedByNonDirectory(path) {
|
|
1091
1128
|
const stat = lstatSync(path, { throwIfNoEntry: false });
|
|
@@ -1094,15 +1131,15 @@ function occupiedByNonDirectory(path) {
|
|
|
1094
1131
|
function foreignOwnedTarget(projectDir, rel) {
|
|
1095
1132
|
const slot = SKILL_SLOT.exec(rel)?.[1];
|
|
1096
1133
|
if (slot === void 0) return null;
|
|
1097
|
-
if (occupiedByNonDirectory(
|
|
1134
|
+
if (occupiedByNonDirectory(join3(projectDir, slot))) return slot;
|
|
1098
1135
|
if (rel !== slot) {
|
|
1099
1136
|
const parts = rel.slice(slot.length + 1).split("/");
|
|
1100
1137
|
let cursor = slot;
|
|
1101
1138
|
for (const part of parts.slice(0, -1)) {
|
|
1102
1139
|
cursor = `${cursor}/${part}`;
|
|
1103
|
-
if (occupiedByNonDirectory(
|
|
1140
|
+
if (occupiedByNonDirectory(join3(projectDir, cursor))) return cursor;
|
|
1104
1141
|
}
|
|
1105
|
-
const leaf = lstatSync(
|
|
1142
|
+
const leaf = lstatSync(join3(projectDir, rel), { throwIfNoEntry: false });
|
|
1106
1143
|
if (leaf !== void 0 && !leaf.isFile()) return rel;
|
|
1107
1144
|
}
|
|
1108
1145
|
return null;
|
|
@@ -1125,13 +1162,13 @@ function createOwnedWriter(projectDir, baseline, options = {}) {
|
|
|
1125
1162
|
return false;
|
|
1126
1163
|
}
|
|
1127
1164
|
const digest = hashContent(content);
|
|
1128
|
-
if (!
|
|
1165
|
+
if (!existsSync3(absPath)) {
|
|
1129
1166
|
if (refreshOnly && !opts?.createInRefresh) return false;
|
|
1130
1167
|
mkdirSync(dirname(absPath), { recursive: true });
|
|
1131
1168
|
writeFileSync(absPath, content);
|
|
1132
1169
|
updated++;
|
|
1133
1170
|
} else {
|
|
1134
|
-
const current =
|
|
1171
|
+
const current = readFileSync2(absPath, "utf8");
|
|
1135
1172
|
if (current !== content) {
|
|
1136
1173
|
if (!isHarnessOwned(baseline, rel, current)) {
|
|
1137
1174
|
backupPaths.push(backupFile(absPath, now));
|
|
@@ -1158,8 +1195,8 @@ function createOwnedWriter(projectDir, baseline, options = {}) {
|
|
|
1158
1195
|
|
|
1159
1196
|
// src/rules-port.ts
|
|
1160
1197
|
init_esm_shims();
|
|
1161
|
-
import { existsSync as
|
|
1162
|
-
import { join as
|
|
1198
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
1199
|
+
import { join as join4 } from "path";
|
|
1163
1200
|
function stripClaudeFrontmatter(source) {
|
|
1164
1201
|
if (!source.startsWith("---\n")) return source.trim();
|
|
1165
1202
|
const end = source.indexOf("\n---", 4);
|
|
@@ -1169,9 +1206,9 @@ function stripClaudeFrontmatter(source) {
|
|
|
1169
1206
|
function portRules(harnessRoot, ruleNames) {
|
|
1170
1207
|
const ported = [];
|
|
1171
1208
|
for (const name of ruleNames) {
|
|
1172
|
-
const src =
|
|
1173
|
-
if (!
|
|
1174
|
-
ported.push({ name, body: stripClaudeFrontmatter(
|
|
1209
|
+
const src = join4(harnessRoot, "templates/rules", `${name}.md`);
|
|
1210
|
+
if (!existsSync4(src)) continue;
|
|
1211
|
+
ported.push({ name, body: stripClaudeFrontmatter(readFileSync3(src, "utf8")) });
|
|
1175
1212
|
}
|
|
1176
1213
|
return ported;
|
|
1177
1214
|
}
|
|
@@ -1191,27 +1228,23 @@ function runAntigravityTransform(params) {
|
|
|
1191
1228
|
refreshOnly
|
|
1192
1229
|
} = params;
|
|
1193
1230
|
const writer = createOwnedWriter(projectDir, baseline, { refreshOnly: refreshOnly ?? false });
|
|
1194
|
-
const rulesFile = writeRules(harnessRoot, projectDir, writer);
|
|
1231
|
+
const rulesFile = writeRules(harnessRoot, projectDir, writer, selectedInternalSkills);
|
|
1195
1232
|
const antigravityInstalled = rulesFile !== null;
|
|
1196
1233
|
const harnessRuleFiles = [];
|
|
1197
1234
|
for (const rule of portRules(harnessRoot, rules)) {
|
|
1198
|
-
const target =
|
|
1235
|
+
const target = join5(projectDir, ".agents", "rules", `${rule.name}.md`);
|
|
1199
1236
|
if (!writer.write(target, `${rule.body}
|
|
1200
1237
|
`, { createInRefresh: antigravityInstalled })) {
|
|
1201
1238
|
continue;
|
|
1202
1239
|
}
|
|
1203
1240
|
harnessRuleFiles.push(target);
|
|
1204
1241
|
}
|
|
1205
|
-
const skillFiles =
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
const target = join4(projectDir, ".agents", "skills", id, "SKILL.md");
|
|
1212
|
-
if (!writer.write(target, renderBundledSkill(readFileSync3(src, "utf8")))) continue;
|
|
1213
|
-
skillFiles.push(target);
|
|
1214
|
-
}
|
|
1242
|
+
const skillFiles = writeBundledSkillDirs({
|
|
1243
|
+
harnessRoot,
|
|
1244
|
+
projectDir,
|
|
1245
|
+
skillIds: selectedInternalSkills,
|
|
1246
|
+
writer
|
|
1247
|
+
});
|
|
1215
1248
|
return {
|
|
1216
1249
|
rulesFile,
|
|
1217
1250
|
harnessRuleFiles,
|
|
@@ -1219,20 +1252,24 @@ function runAntigravityTransform(params) {
|
|
|
1219
1252
|
ownership: writer.result()
|
|
1220
1253
|
};
|
|
1221
1254
|
}
|
|
1222
|
-
function writeRules(harnessRoot, projectDir, writer) {
|
|
1223
|
-
const claudeMdPath =
|
|
1224
|
-
const templatePath =
|
|
1225
|
-
if (!
|
|
1255
|
+
function writeRules(harnessRoot, projectDir, writer, selectedInternalSkills) {
|
|
1256
|
+
const claudeMdPath = join5(harnessRoot, "templates/CLAUDE.md");
|
|
1257
|
+
const templatePath = join5(harnessRoot, "templates/antigravity/AGENTS.md.template");
|
|
1258
|
+
if (!existsSync5(claudeMdPath) || !existsSync5(templatePath)) {
|
|
1226
1259
|
return null;
|
|
1227
1260
|
}
|
|
1228
|
-
const claudeMd =
|
|
1229
|
-
const template =
|
|
1230
|
-
const target =
|
|
1261
|
+
const claudeMd = readFileSync4(claudeMdPath, "utf8");
|
|
1262
|
+
const template = readFileSync4(templatePath, "utf8");
|
|
1263
|
+
const target = join5(projectDir, ".agents", "rules", "uzys-harness.md");
|
|
1231
1264
|
const rulesOut = renderAgentsMd({
|
|
1232
1265
|
template,
|
|
1233
1266
|
claudeMd,
|
|
1234
1267
|
projectName: basename(projectDir),
|
|
1235
|
-
|
|
1268
|
+
// ADR-085 — 상시 스킬 안내는 앵커가 아니라 여기(프로젝트 맥락)에, 깔린 것만.
|
|
1269
|
+
projectContext: withContinuousSkillsNote(
|
|
1270
|
+
renderFillScaffold("antigravity-rule"),
|
|
1271
|
+
selectedInternalSkills
|
|
1272
|
+
)
|
|
1236
1273
|
});
|
|
1237
1274
|
return writer.write(target, rulesOut) ? target : null;
|
|
1238
1275
|
}
|
|
@@ -1240,18 +1277,18 @@ function writeRules(harnessRoot, projectDir, writer) {
|
|
|
1240
1277
|
// src/codex/opt-in.ts
|
|
1241
1278
|
init_esm_shims();
|
|
1242
1279
|
import { homedir } from "os";
|
|
1243
|
-
import { join as
|
|
1280
|
+
import { join as join6 } from "path";
|
|
1244
1281
|
|
|
1245
1282
|
// src/codex/trust-entry.ts
|
|
1246
1283
|
init_esm_shims();
|
|
1247
|
-
import { existsSync as
|
|
1284
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "fs";
|
|
1248
1285
|
import { dirname as dirname2 } from "path";
|
|
1249
1286
|
var TRUST_BLOCK_REGEX = /\[projects\."([^"]+)"\]/g;
|
|
1250
1287
|
function registerTrustEntry(opts) {
|
|
1251
1288
|
const { configPath, projectDir } = opts;
|
|
1252
1289
|
try {
|
|
1253
1290
|
mkdirSync2(dirname2(configPath), { recursive: true });
|
|
1254
|
-
const existing =
|
|
1291
|
+
const existing = existsSync6(configPath) ? readFileSync5(configPath, "utf8") : "";
|
|
1255
1292
|
if (hasTrustEntry(existing, projectDir)) {
|
|
1256
1293
|
return { status: "already-present" };
|
|
1257
1294
|
}
|
|
@@ -1275,8 +1312,8 @@ function hasTrustEntry(configContent, projectDir) {
|
|
|
1275
1312
|
|
|
1276
1313
|
// src/codex/opt-in.ts
|
|
1277
1314
|
function runCodexOptIn(ctx) {
|
|
1278
|
-
const codexHome = ctx.codexHome ??
|
|
1279
|
-
const configPath =
|
|
1315
|
+
const codexHome = ctx.codexHome ?? join6(homedir(), ".codex");
|
|
1316
|
+
const configPath = join6(codexHome, "config.toml");
|
|
1280
1317
|
const result = registerTrustEntry({ configPath, projectDir: ctx.projectDir });
|
|
1281
1318
|
return {
|
|
1282
1319
|
trustEntry: {
|
|
@@ -1289,8 +1326,8 @@ function runCodexOptIn(ctx) {
|
|
|
1289
1326
|
|
|
1290
1327
|
// src/codex/transform.ts
|
|
1291
1328
|
init_esm_shims();
|
|
1292
|
-
import { chmodSync, existsSync as
|
|
1293
|
-
import { basename as basename2, join as
|
|
1329
|
+
import { chmodSync as chmodSync2, existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
|
|
1330
|
+
import { basename as basename2, join as join7 } from "path";
|
|
1294
1331
|
|
|
1295
1332
|
// src/codex/config-toml.ts
|
|
1296
1333
|
init_esm_shims();
|
|
@@ -1367,23 +1404,27 @@ function runCodexTransform(params) {
|
|
|
1367
1404
|
refreshOnly
|
|
1368
1405
|
} = params;
|
|
1369
1406
|
const writer = createOwnedWriter(projectDir, baseline, { refreshOnly: refreshOnly ?? false });
|
|
1370
|
-
const claudeMd = readRequired(
|
|
1371
|
-
const agentsTemplate = readRequired(
|
|
1372
|
-
const configTemplate = readRequired(
|
|
1407
|
+
const claudeMd = readRequired(join7(harnessRoot, "templates/CLAUDE.md"));
|
|
1408
|
+
const agentsTemplate = readRequired(join7(harnessRoot, "templates/codex/AGENTS.md.template"));
|
|
1409
|
+
const configTemplate = readRequired(join7(harnessRoot, "templates/codex/config.toml.template"));
|
|
1373
1410
|
const projectName = basename2(projectDir);
|
|
1374
|
-
const mcp = readOptionalJson(
|
|
1375
|
-
const agentsMdPath =
|
|
1411
|
+
const mcp = readOptionalJson(join7(harnessRoot, ".mcp.json"));
|
|
1412
|
+
const agentsMdPath = join7(projectDir, "AGENTS.md");
|
|
1376
1413
|
ensureDir(projectDir);
|
|
1377
1414
|
const agentsMdOut = renderAgentsMd({
|
|
1378
1415
|
template: agentsTemplate,
|
|
1379
1416
|
claudeMd,
|
|
1380
1417
|
projectName,
|
|
1381
|
-
|
|
1418
|
+
// ADR-085 — 상시 스킬 안내는 앵커가 아니라 여기(프로젝트 맥락)에, 깔린 것만.
|
|
1419
|
+
projectContext: withContinuousSkillsNote(
|
|
1420
|
+
renderFillScaffold("agents-md"),
|
|
1421
|
+
selectedInternalSkills
|
|
1422
|
+
),
|
|
1382
1423
|
// Codex 는 룰 디렉터리가 없다 — 룰이 AGENTS.md 본문에 들어가야 도달한다(§Harness Rules).
|
|
1383
1424
|
harnessRules: renderRulesBlock(portRules(harnessRoot, rules))
|
|
1384
1425
|
});
|
|
1385
1426
|
writer.write(agentsMdPath, agentsMdOut);
|
|
1386
|
-
const configTomlPath =
|
|
1427
|
+
const configTomlPath = join7(projectDir, ".codex/config.toml");
|
|
1387
1428
|
writer.write(
|
|
1388
1429
|
configTomlPath,
|
|
1389
1430
|
renderConfigToml({
|
|
@@ -1393,29 +1434,25 @@ function runCodexTransform(params) {
|
|
|
1393
1434
|
mcp
|
|
1394
1435
|
})
|
|
1395
1436
|
);
|
|
1396
|
-
const hookDir =
|
|
1437
|
+
const hookDir = join7(projectDir, ".codex/hooks");
|
|
1397
1438
|
const hookFiles = [];
|
|
1398
1439
|
for (const hook of HOOK_NAMES) {
|
|
1399
|
-
const src =
|
|
1400
|
-
if (!
|
|
1440
|
+
const src = join7(harnessRoot, "templates/hooks", `${hook}.sh`);
|
|
1441
|
+
if (!existsSync7(src)) {
|
|
1401
1442
|
continue;
|
|
1402
1443
|
}
|
|
1403
|
-
const ported =
|
|
1404
|
-
const target =
|
|
1444
|
+
const ported = readFileSync6(src, "utf8").replace(ENV_VAR_RENAME, "CODEX_PROJECT_DIR");
|
|
1445
|
+
const target = join7(hookDir, `${hook}.sh`);
|
|
1405
1446
|
if (!writer.write(target, ported)) continue;
|
|
1406
|
-
|
|
1447
|
+
chmodSync2(target, 493);
|
|
1407
1448
|
hookFiles.push(target);
|
|
1408
1449
|
}
|
|
1409
|
-
const skillFiles =
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
const target = join6(projectDir, ".agents", "skills", id, "SKILL.md");
|
|
1416
|
-
if (!writer.write(target, renderBundledSkill(readFileSync5(src, "utf8")))) continue;
|
|
1417
|
-
skillFiles.push(target);
|
|
1418
|
-
}
|
|
1450
|
+
const skillFiles = writeBundledSkillDirs({
|
|
1451
|
+
harnessRoot,
|
|
1452
|
+
projectDir,
|
|
1453
|
+
skillIds: selectedInternalSkills,
|
|
1454
|
+
writer
|
|
1455
|
+
});
|
|
1419
1456
|
return {
|
|
1420
1457
|
agentsMdPath,
|
|
1421
1458
|
configTomlPath,
|
|
@@ -1425,17 +1462,17 @@ function runCodexTransform(params) {
|
|
|
1425
1462
|
};
|
|
1426
1463
|
}
|
|
1427
1464
|
function readRequired(path) {
|
|
1428
|
-
if (!
|
|
1465
|
+
if (!existsSync7(path)) {
|
|
1429
1466
|
throw new Error(`Codex transform: required source missing: ${path}`);
|
|
1430
1467
|
}
|
|
1431
|
-
return
|
|
1468
|
+
return readFileSync6(path, "utf8");
|
|
1432
1469
|
}
|
|
1433
1470
|
function readOptionalJson(path) {
|
|
1434
|
-
if (!
|
|
1471
|
+
if (!existsSync7(path)) {
|
|
1435
1472
|
return null;
|
|
1436
1473
|
}
|
|
1437
1474
|
try {
|
|
1438
|
-
return JSON.parse(
|
|
1475
|
+
return JSON.parse(readFileSync6(path, "utf8"));
|
|
1439
1476
|
} catch {
|
|
1440
1477
|
return null;
|
|
1441
1478
|
}
|
|
@@ -1443,8 +1480,8 @@ function readOptionalJson(path) {
|
|
|
1443
1480
|
|
|
1444
1481
|
// src/opencode/transform.ts
|
|
1445
1482
|
init_esm_shims();
|
|
1446
|
-
import { existsSync as
|
|
1447
|
-
import { basename as basename3, join as
|
|
1483
|
+
import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync7, unlinkSync } from "fs";
|
|
1484
|
+
import { basename as basename3, join as join8 } from "path";
|
|
1448
1485
|
|
|
1449
1486
|
// src/opencode/agents-md.ts
|
|
1450
1487
|
init_esm_shims();
|
|
@@ -1487,46 +1524,46 @@ function runOpencodeTransform(params) {
|
|
|
1487
1524
|
refreshOnly
|
|
1488
1525
|
} = params;
|
|
1489
1526
|
const writer = createOwnedWriter(projectDir, baseline, { refreshOnly: refreshOnly ?? false });
|
|
1490
|
-
const claudeMd = readRequired2(
|
|
1491
|
-
const agentsTemplate = readRequired2(
|
|
1527
|
+
const claudeMd = readRequired2(join8(harnessRoot, "templates/CLAUDE.md"));
|
|
1528
|
+
const agentsTemplate = readRequired2(join8(harnessRoot, "templates/opencode/AGENTS.md.template"));
|
|
1492
1529
|
const opencodeTemplate = readRequired2(
|
|
1493
|
-
|
|
1530
|
+
join8(harnessRoot, "templates/opencode/opencode.json.template")
|
|
1494
1531
|
);
|
|
1495
1532
|
const projectName = basename3(projectDir);
|
|
1496
|
-
const mcp = readOptionalJson2(
|
|
1533
|
+
const mcp = readOptionalJson2(join8(harnessRoot, ".mcp.json"));
|
|
1497
1534
|
ensureDir(projectDir);
|
|
1498
|
-
const agentsMdPath =
|
|
1535
|
+
const agentsMdPath = join8(projectDir, "AGENTS.md");
|
|
1499
1536
|
const agentsMdOut = renderAgentsMd2({
|
|
1500
1537
|
template: agentsTemplate,
|
|
1501
1538
|
claudeMd,
|
|
1502
1539
|
projectName,
|
|
1503
|
-
|
|
1540
|
+
// ADR-085 — 상시 스킬 안내는 앵커가 아니라 여기(프로젝트 맥락)에, 깔린 것만.
|
|
1541
|
+
projectContext: withContinuousSkillsNote(
|
|
1542
|
+
renderFillScaffold("agents-md"),
|
|
1543
|
+
selectedInternalSkills
|
|
1544
|
+
),
|
|
1504
1545
|
// codex 와 **같은 파일**(프로젝트 루트 `AGENTS.md`)이다. 두 transform 이 서로 다른 본문을
|
|
1505
1546
|
// 쓰면 나중에 도는 쪽이 앞선 쪽을 덮어써, codex+opencode 조합에서 룰이 통째로 사라진다
|
|
1506
1547
|
// (독립 검증 C-1 실측). 같은 내용을 쓰면 순서가 결과를 바꾸지 않는다.
|
|
1507
1548
|
harnessRules: renderRulesBlock(portRules(harnessRoot, rules))
|
|
1508
1549
|
});
|
|
1509
1550
|
writer.write(agentsMdPath, agentsMdOut);
|
|
1510
|
-
const opencodeJsonPath =
|
|
1551
|
+
const opencodeJsonPath = join8(projectDir, "opencode.json");
|
|
1511
1552
|
writer.write(opencodeJsonPath, renderOpencodeJson({ template: opencodeTemplate, mcp }));
|
|
1512
|
-
const skillFiles =
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
const target = join7(projectDir, ".agents", "skills", id, "SKILL.md");
|
|
1519
|
-
if (!writer.write(target, renderBundledSkill(readFileSync6(src, "utf8")))) continue;
|
|
1520
|
-
skillFiles.push(target);
|
|
1521
|
-
}
|
|
1553
|
+
const skillFiles = writeBundledSkillDirs({
|
|
1554
|
+
harnessRoot,
|
|
1555
|
+
projectDir,
|
|
1556
|
+
skillIds: selectedInternalSkills,
|
|
1557
|
+
writer
|
|
1558
|
+
});
|
|
1522
1559
|
const retiredCommands = [];
|
|
1523
|
-
const cmdDir =
|
|
1524
|
-
if (
|
|
1560
|
+
const cmdDir = join8(projectDir, ".opencode/commands");
|
|
1561
|
+
if (existsSync8(cmdDir)) {
|
|
1525
1562
|
for (const entry of readdirSync(cmdDir)) {
|
|
1526
1563
|
if (!entry.endsWith(".md")) continue;
|
|
1527
1564
|
const id = entry.slice(0, -3);
|
|
1528
|
-
if (!
|
|
1529
|
-
const victim =
|
|
1565
|
+
if (!existsSync8(join8(harnessRoot, "templates/skills", id, "SKILL.md"))) continue;
|
|
1566
|
+
const victim = join8(cmdDir, entry);
|
|
1530
1567
|
try {
|
|
1531
1568
|
backupFile(victim);
|
|
1532
1569
|
unlinkSync(victim);
|
|
@@ -1544,17 +1581,17 @@ function runOpencodeTransform(params) {
|
|
|
1544
1581
|
};
|
|
1545
1582
|
}
|
|
1546
1583
|
function readRequired2(path) {
|
|
1547
|
-
if (!
|
|
1584
|
+
if (!existsSync8(path)) {
|
|
1548
1585
|
throw new Error(`OpenCode transform: required source missing: ${path}`);
|
|
1549
1586
|
}
|
|
1550
|
-
return
|
|
1587
|
+
return readFileSync7(path, "utf8");
|
|
1551
1588
|
}
|
|
1552
1589
|
function readOptionalJson2(path) {
|
|
1553
|
-
if (!
|
|
1590
|
+
if (!existsSync8(path)) {
|
|
1554
1591
|
return null;
|
|
1555
1592
|
}
|
|
1556
1593
|
try {
|
|
1557
|
-
return JSON.parse(
|
|
1594
|
+
return JSON.parse(readFileSync7(path, "utf8"));
|
|
1558
1595
|
} catch {
|
|
1559
1596
|
return null;
|
|
1560
1597
|
}
|
|
@@ -1646,8 +1683,8 @@ function runCliTransforms(params) {
|
|
|
1646
1683
|
|
|
1647
1684
|
// src/env-files.ts
|
|
1648
1685
|
init_esm_shims();
|
|
1649
|
-
import { appendFileSync, existsSync as
|
|
1650
|
-
import { join as
|
|
1686
|
+
import { appendFileSync, existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
|
|
1687
|
+
import { join as join9 } from "path";
|
|
1651
1688
|
var ENV_EXAMPLE_BODY = `# .env.example \u2014 csr-supabase Track
|
|
1652
1689
|
# Copy to .env (gitignored) and fill in values: cp .env.example .env
|
|
1653
1690
|
|
|
@@ -1684,19 +1721,19 @@ function writeEnvExample(projectDir, tracks) {
|
|
|
1684
1721
|
if (!tracks.some((t) => ENV_EXAMPLE_TRACKS.includes(t))) {
|
|
1685
1722
|
return false;
|
|
1686
1723
|
}
|
|
1687
|
-
const path =
|
|
1688
|
-
if (
|
|
1724
|
+
const path = join9(projectDir, ".env.example");
|
|
1725
|
+
if (existsSync9(path)) {
|
|
1689
1726
|
return false;
|
|
1690
1727
|
}
|
|
1691
1728
|
writeFileSync3(path, ENV_EXAMPLE_BODY);
|
|
1692
1729
|
return true;
|
|
1693
1730
|
}
|
|
1694
1731
|
function addGitignoreEnv(projectDir) {
|
|
1695
|
-
const path =
|
|
1696
|
-
if (!
|
|
1732
|
+
const path = join9(projectDir, ".gitignore");
|
|
1733
|
+
if (!existsSync9(path)) {
|
|
1697
1734
|
return false;
|
|
1698
1735
|
}
|
|
1699
|
-
const content =
|
|
1736
|
+
const content = readFileSync8(path, "utf8");
|
|
1700
1737
|
if (GITIGNORE_ENV_PATTERN.test(content)) {
|
|
1701
1738
|
return false;
|
|
1702
1739
|
}
|
|
@@ -1710,11 +1747,11 @@ function addGitignoreEnv(projectDir) {
|
|
|
1710
1747
|
var AGENT_ARTIFACT_DIRS = [".factory/", ".goose/", ".uzys-agent-harness/"];
|
|
1711
1748
|
var GITIGNORE_AGENT_ARTIFACT_HEADER = "# agent CLI / harness \uC790\uB3D9 \uC0DD\uC131\uBB3C (auto-added by agent-harness)";
|
|
1712
1749
|
function addGitignoreAgentArtifacts(projectDir) {
|
|
1713
|
-
const path =
|
|
1714
|
-
if (!
|
|
1750
|
+
const path = join9(projectDir, ".gitignore");
|
|
1751
|
+
if (!existsSync9(path)) {
|
|
1715
1752
|
return [];
|
|
1716
1753
|
}
|
|
1717
|
-
const content =
|
|
1754
|
+
const content = readFileSync8(path, "utf8");
|
|
1718
1755
|
const missing = AGENT_ARTIFACT_DIRS.filter((pattern) => {
|
|
1719
1756
|
const lineRegex = new RegExp(`^${pattern.replace(/\./g, "\\.").replace(/\//g, "/")}\\s*$`, "m");
|
|
1720
1757
|
return !lineRegex.test(content);
|
|
@@ -1733,9 +1770,9 @@ ${block}
|
|
|
1733
1770
|
// src/external-installer.ts
|
|
1734
1771
|
init_esm_shims();
|
|
1735
1772
|
import { spawnSync } from "child_process";
|
|
1736
|
-
import { existsSync as
|
|
1773
|
+
import { existsSync as existsSync10, readdirSync as readdirSync2, readFileSync as readFileSync9 } from "fs";
|
|
1737
1774
|
import { homedir as homedir2 } from "os";
|
|
1738
|
-
import { join as
|
|
1775
|
+
import { join as join10 } from "path";
|
|
1739
1776
|
var DEFAULT_SPAWN_TIMEOUT_MS = 12e4;
|
|
1740
1777
|
function selectExternalTargets(assets, ctx) {
|
|
1741
1778
|
const conditionPassed = filterApplicableAssets(assets, ctx).filter(
|
|
@@ -1811,8 +1848,8 @@ function installOne(asset, ctx) {
|
|
|
1811
1848
|
cwd
|
|
1812
1849
|
);
|
|
1813
1850
|
case "shell-script": {
|
|
1814
|
-
const scriptPath =
|
|
1815
|
-
if (!
|
|
1851
|
+
const scriptPath = join10(ctx.harnessRoot, method.script);
|
|
1852
|
+
if (!existsSync10(scriptPath)) {
|
|
1816
1853
|
return {
|
|
1817
1854
|
asset,
|
|
1818
1855
|
ok: false,
|
|
@@ -1942,17 +1979,17 @@ function detectVersion(method, spawn) {
|
|
|
1942
1979
|
if (at2 <= 0) return void 0;
|
|
1943
1980
|
const plugin = method.pluginId.slice(0, at2);
|
|
1944
1981
|
const marketplaceShort = method.pluginId.slice(at2 + 1);
|
|
1945
|
-
const cacheBase =
|
|
1946
|
-
if (!
|
|
1982
|
+
const cacheBase = join10(homedir2(), ".claude/plugins/cache", marketplaceShort, plugin);
|
|
1983
|
+
if (!existsSync10(cacheBase)) return void 0;
|
|
1947
1984
|
const versions = readdirSync2(cacheBase).filter((v2) => /^\d/.test(v2)).sort();
|
|
1948
1985
|
return versions.at(-1);
|
|
1949
1986
|
}
|
|
1950
1987
|
case "npm": {
|
|
1951
1988
|
const npmRoot = getNpmGlobalRoot(spawn);
|
|
1952
1989
|
if (!npmRoot) return void 0;
|
|
1953
|
-
const pkgJson =
|
|
1954
|
-
if (!
|
|
1955
|
-
const parsed = JSON.parse(
|
|
1990
|
+
const pkgJson = join10(npmRoot, method.pkg, "package.json");
|
|
1991
|
+
if (!existsSync10(pkgJson)) return void 0;
|
|
1992
|
+
const parsed = JSON.parse(readFileSync9(pkgJson, "utf8"));
|
|
1956
1993
|
return parsed.version;
|
|
1957
1994
|
}
|
|
1958
1995
|
default:
|
|
@@ -1979,7 +2016,7 @@ function getNpmGlobalRoot(spawn) {
|
|
|
1979
2016
|
|
|
1980
2017
|
// src/mcp-merge.ts
|
|
1981
2018
|
init_esm_shims();
|
|
1982
|
-
import { existsSync as
|
|
2019
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10, writeFileSync as writeFileSync4 } from "fs";
|
|
1983
2020
|
function parseTrackMcpMap(raw) {
|
|
1984
2021
|
const rows = [];
|
|
1985
2022
|
for (const line of raw.split(/\r?\n/)) {
|
|
@@ -2030,15 +2067,15 @@ function mergeMcpServers(base, rows, tracks) {
|
|
|
2030
2067
|
return out;
|
|
2031
2068
|
}
|
|
2032
2069
|
function composeMcpJson(opts) {
|
|
2033
|
-
const base = JSON.parse(
|
|
2034
|
-
const merged = opts.existingPath &&
|
|
2035
|
-
const mapRaw =
|
|
2070
|
+
const base = JSON.parse(readFileSync10(opts.templateMcpPath, "utf8"));
|
|
2071
|
+
const merged = opts.existingPath && existsSync11(opts.existingPath) ? mergeUserBase(base, opts.existingPath) : base;
|
|
2072
|
+
const mapRaw = existsSync11(opts.trackMapPath) ? readFileSync10(opts.trackMapPath, "utf8") : "";
|
|
2036
2073
|
const rows = parseTrackMcpMap(mapRaw);
|
|
2037
2074
|
return mergeMcpServers(merged, rows, opts.tracks);
|
|
2038
2075
|
}
|
|
2039
2076
|
function mergeUserBase(base, existingPath) {
|
|
2040
2077
|
try {
|
|
2041
|
-
const existing = JSON.parse(
|
|
2078
|
+
const existing = JSON.parse(readFileSync10(existingPath, "utf8"));
|
|
2042
2079
|
return {
|
|
2043
2080
|
...base,
|
|
2044
2081
|
mcpServers: { ...base.mcpServers, ...existing.mcpServers }
|
|
@@ -2052,41 +2089,6 @@ function writeMcpJson(path, mcp) {
|
|
|
2052
2089
|
`);
|
|
2053
2090
|
}
|
|
2054
2091
|
|
|
2055
|
-
// src/superseded.ts
|
|
2056
|
-
init_esm_shims();
|
|
2057
|
-
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "fs";
|
|
2058
|
-
import { join as join10 } from "path";
|
|
2059
|
-
function sameTracks(a, b2) {
|
|
2060
|
-
if (a.length !== b2.length) return false;
|
|
2061
|
-
const sa = [...a].sort();
|
|
2062
|
-
const sb = [...b2].sort();
|
|
2063
|
-
return sa.every((t, i) => t === sb[i]);
|
|
2064
|
-
}
|
|
2065
|
-
function policyKey(claudeRelative) {
|
|
2066
|
-
return POLICY_DIRS.some(
|
|
2067
|
-
({ dir, ext }) => claudeRelative.startsWith(`${dir}/`) && claudeRelative.endsWith(ext)
|
|
2068
|
-
) ? claudeRelative : null;
|
|
2069
|
-
}
|
|
2070
|
-
function findSuperseded(projectDir, spec, previous) {
|
|
2071
|
-
if (!previous) return [];
|
|
2072
|
-
if (!sameTracks(previous.spec.tracks, spec.tracks)) return [];
|
|
2073
|
-
const baseline = new Map((previous.policyFiles ?? []).map((f) => [f.path, f.sha256]));
|
|
2074
|
-
if (baseline.size === 0) return [];
|
|
2075
|
-
const out = [];
|
|
2076
|
-
for (const entry of buildManifest(spec)) {
|
|
2077
|
-
if (entry.applies(spec)) continue;
|
|
2078
|
-
const id = classifyBaselineTarget(entry.target)?.id;
|
|
2079
|
-
if (!id) continue;
|
|
2080
|
-
const key = policyKey(entry.target.slice(".claude/".length));
|
|
2081
|
-
if (!key) continue;
|
|
2082
|
-
const abs = join10(projectDir, entry.target);
|
|
2083
|
-
if (!existsSync11(abs)) continue;
|
|
2084
|
-
if (!isHarnessOwned(baseline, key, readFileSync10(abs, "utf-8"))) continue;
|
|
2085
|
-
out.push({ target: entry.target, id, source: entry.source });
|
|
2086
|
-
}
|
|
2087
|
-
return out;
|
|
2088
|
-
}
|
|
2089
|
-
|
|
2090
2092
|
// src/update-mode.ts
|
|
2091
2093
|
init_esm_shims();
|
|
2092
2094
|
import {
|
|
@@ -2118,6 +2120,7 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
|
2118
2120
|
claudeMdUpdated: false,
|
|
2119
2121
|
anchorCreated: false,
|
|
2120
2122
|
rootImportAdded: false,
|
|
2123
|
+
rootBlockRefreshed: false,
|
|
2121
2124
|
legacyAnchor: null,
|
|
2122
2125
|
skillsBackedUp: [],
|
|
2123
2126
|
skillsSkippedLinks: [],
|
|
@@ -2132,6 +2135,7 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
|
2132
2135
|
installedNew: [],
|
|
2133
2136
|
restored: [],
|
|
2134
2137
|
needsReinstall: [],
|
|
2138
|
+
retiredAgents: [],
|
|
2135
2139
|
mcpAllowlistRetired: null
|
|
2136
2140
|
};
|
|
2137
2141
|
const fresh = installNewAssets(projectDir, templatesDir);
|
|
@@ -2170,6 +2174,7 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
|
2170
2174
|
report.staleHookRefs = cleanStaleHookRefs(settingsPath, claudeDir);
|
|
2171
2175
|
}
|
|
2172
2176
|
report.mcpAllowlistRetired = retireMcpAllowlist(projectDir);
|
|
2177
|
+
report.retiredAgents = staleAgentFiles(claudeDir);
|
|
2173
2178
|
const external = refreshExternalCli(projectDir, harnessRoot);
|
|
2174
2179
|
report.externalUpdated = external.externalUpdated;
|
|
2175
2180
|
report.externalBackedUp = external.externalBackedUp;
|
|
@@ -2186,10 +2191,24 @@ function runUpdateMode(projectDir, templatesDir, harnessRoot, deps = {}) {
|
|
|
2186
2191
|
const skillRefresh = (deps.refreshSkills ?? refreshExternalSkills)(projectDir);
|
|
2187
2192
|
report.externalSkillsRefreshed = skillRefresh.refreshed;
|
|
2188
2193
|
report.externalSkillsFailed = skillRefresh.failed;
|
|
2189
|
-
report.externalSkillsNotInCatalog =
|
|
2194
|
+
report.externalSkillsNotInCatalog = [
|
|
2195
|
+
...skillRefresh.notInCatalog,
|
|
2196
|
+
...staleSkillDirs(claudeDir).filter((id) => !skillRefresh.notInCatalog.includes(id))
|
|
2197
|
+
];
|
|
2190
2198
|
report.externalSkillsUnknown = skillRefresh.unknown;
|
|
2191
2199
|
return report;
|
|
2192
2200
|
}
|
|
2201
|
+
function staleSkillDirs(claudeDir) {
|
|
2202
|
+
const dir = join11(claudeDir, "skills");
|
|
2203
|
+
if (!existsSync12(dir)) return [];
|
|
2204
|
+
const stale = (id) => RENAMED_SKILL_IDS[id] !== void 0 || RETIRED_SKILL_IDS.includes(id);
|
|
2205
|
+
return readdirSync3(dir, { withFileTypes: true }).filter((e2) => e2.isDirectory() && stale(e2.name)).map((e2) => e2.name);
|
|
2206
|
+
}
|
|
2207
|
+
function staleAgentFiles(claudeDir) {
|
|
2208
|
+
const dir = join11(claudeDir, "agents");
|
|
2209
|
+
if (!existsSync12(dir)) return [];
|
|
2210
|
+
return readdirSync3(dir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".md")).map((e2) => basename4(e2.name, ".md")).filter((id) => RETIRED_AGENT_IDS.includes(id));
|
|
2211
|
+
}
|
|
2193
2212
|
function installNewAssets(projectDir, templatesDir) {
|
|
2194
2213
|
const installed = [];
|
|
2195
2214
|
const restored = [];
|
|
@@ -2245,6 +2264,7 @@ function syncHarnessAnchor(projectDir, templatesDir, report) {
|
|
|
2245
2264
|
copyFileSync(templateMd, anchor);
|
|
2246
2265
|
if (existed) {
|
|
2247
2266
|
report.claudeMdUpdated = true;
|
|
2267
|
+
report.rootBlockRefreshed = upsertRootImport(projectDir);
|
|
2248
2268
|
return;
|
|
2249
2269
|
}
|
|
2250
2270
|
report.anchorCreated = true;
|
|
@@ -2256,7 +2276,12 @@ function upsertRootImport(projectDir) {
|
|
|
2256
2276
|
const existing = existsSync12(target) ? readFileSync11(target, "utf8") : null;
|
|
2257
2277
|
const next = upsertHarnessImport(existing, {
|
|
2258
2278
|
projectName: basename4(projectDir),
|
|
2259
|
-
tracks: installedTracks(projectDir)
|
|
2279
|
+
tracks: installedTracks(projectDir),
|
|
2280
|
+
// ADR-085 — update 는 선택 목록을 인자로 받지 않는다. `syncSkills` 와 같은 판정을 쓴다:
|
|
2281
|
+
// `.claude/skills/<id>` 가 있으면 그 스킬은 이 설치가 고른 것이다.
|
|
2282
|
+
continuousSkills: CONTINUOUS_SKILLS.map((s) => s.id).filter(
|
|
2283
|
+
(id) => existsSync12(join11(projectDir, ".claude/skills", id))
|
|
2284
|
+
)
|
|
2260
2285
|
});
|
|
2261
2286
|
if (next === existing) return false;
|
|
2262
2287
|
writeFileSync5(target, next);
|
|
@@ -2518,7 +2543,6 @@ function runInstall(ctx) {
|
|
|
2518
2543
|
const manifestSpec = buildManifestSpec(spec);
|
|
2519
2544
|
const policyBase = new Map((previousLog?.policyFiles ?? []).map((f) => [f.path, f.sha256]));
|
|
2520
2545
|
const baselineExcluded = new Set(spec.baselineExclude ?? []);
|
|
2521
|
-
const supersededFound = findSuperseded(projectDir, manifestSpec, previousLog);
|
|
2522
2546
|
const base = spec.cli.includes("claude") ? installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBase, baselineExcluded) : (
|
|
2523
2547
|
// claude 미선택이어도 CLI 중립 자산은 깔린다. manifest 전체가 `.claude/` baseline 안에서만
|
|
2524
2548
|
// 돌던 탓에 이 자산들이 claude 설치에만 도달했는데, **배포 룰 본문이 이 스크립트들을
|
|
@@ -2574,10 +2598,7 @@ function runInstall(ctx) {
|
|
|
2574
2598
|
baselineForeignOwned: [
|
|
2575
2599
|
...base.foreignOwned,
|
|
2576
2600
|
...externalForeignOwned.filter((f) => !base.foreignOwned.includes(f))
|
|
2577
|
-
]
|
|
2578
|
-
// 밀려난 자산 정리는 **manifest 복사가 끝난 뒤**에 한다. 앞에서 지우면 이번 설치가 다시
|
|
2579
|
-
// 깔 수도 있는 파일을 지웠다 되돌리는 셈이라, 무엇이 최종 상태인지 보고가 흐려진다.
|
|
2580
|
-
superseded: applySupersededCleanup(spec, projectDir, supersededFound)
|
|
2601
|
+
]
|
|
2581
2602
|
};
|
|
2582
2603
|
ctx.onProgress?.({ type: "baseline-complete", baseline });
|
|
2583
2604
|
const external = runExternalPhase(ctx);
|
|
@@ -2593,21 +2614,6 @@ function runInstall(ctx) {
|
|
|
2593
2614
|
);
|
|
2594
2615
|
return { ...baseline, external, staleHookRefs };
|
|
2595
2616
|
}
|
|
2596
|
-
function applySupersededCleanup(spec, projectDir, found) {
|
|
2597
|
-
if (found.length === 0) return { removed: [], kept: [] };
|
|
2598
|
-
if (!spec.cleanSuperseded) return { removed: [], kept: found.map((f) => f.target) };
|
|
2599
|
-
const removed = [];
|
|
2600
|
-
const kept = [];
|
|
2601
|
-
for (const asset of found) {
|
|
2602
|
-
try {
|
|
2603
|
-
unlinkSync3(join12(projectDir, asset.target));
|
|
2604
|
-
removed.push(asset.target);
|
|
2605
|
-
} catch {
|
|
2606
|
-
kept.push(asset.target);
|
|
2607
|
-
}
|
|
2608
|
-
}
|
|
2609
|
-
return { removed, kept };
|
|
2610
|
-
}
|
|
2611
2617
|
function healStaleHookRefs(spec, projectDir) {
|
|
2612
2618
|
if (!spec.cli.includes("claude")) return [];
|
|
2613
2619
|
const settingsPath = join12(projectDir, ".claude/settings.json");
|
|
@@ -2628,9 +2634,6 @@ function runUpdateInstall(ctx, templatesDir, backupPath) {
|
|
|
2628
2634
|
baselineExcluded: [],
|
|
2629
2635
|
baselineExcludedOnDisk: [],
|
|
2630
2636
|
baselineForeignOwned: [],
|
|
2631
|
-
// update 는 밀려난 자산을 다루지 않는다 — 그 판정은 사용자가 옵션을 고르는 설치 경로의
|
|
2632
|
-
// 것이고, update 는 무엇을 골랐는지 다시 묻지 않는다.
|
|
2633
|
-
superseded: { removed: [], kept: [] },
|
|
2634
2637
|
backup: backupPath,
|
|
2635
2638
|
installedTracks: [...ctx.spec.tracks].sort(),
|
|
2636
2639
|
mcpServers: [],
|
|
@@ -2760,7 +2763,11 @@ function installClaudeBaseline(manifestSpec, projectDir, templatesDir, policyBas
|
|
|
2760
2763
|
chmodHooksSync(hookDir);
|
|
2761
2764
|
}
|
|
2762
2765
|
writeInstalledTracks(projectDir, manifestSpec.tracks);
|
|
2763
|
-
const rootClaudeMd = writeRootClaudeMd(
|
|
2766
|
+
const rootClaudeMd = writeRootClaudeMd(
|
|
2767
|
+
projectDir,
|
|
2768
|
+
manifestSpec.tracks,
|
|
2769
|
+
manifestSpec.selectedInternalSkills ?? []
|
|
2770
|
+
);
|
|
2764
2771
|
result.rootClaudeMd = { tracks: manifestSpec.tracks, created: rootClaudeMd.created };
|
|
2765
2772
|
result.rootClaudeMdLog = harnessAnchorLog(projectDir);
|
|
2766
2773
|
return result;
|
|
@@ -2906,10 +2913,14 @@ function writeInstalledTracks(projectDir, tracks) {
|
|
|
2906
2913
|
writeFileSync6(path, `${sorted}
|
|
2907
2914
|
`);
|
|
2908
2915
|
}
|
|
2909
|
-
function writeRootClaudeMd(projectDir, tracks) {
|
|
2916
|
+
function writeRootClaudeMd(projectDir, tracks, continuousSkills) {
|
|
2910
2917
|
const target = join12(projectDir, "CLAUDE.md");
|
|
2911
2918
|
const existing = existsSync13(target) ? readFileSync12(target, "utf-8") : null;
|
|
2912
|
-
const content = upsertHarnessImport(existing, {
|
|
2919
|
+
const content = upsertHarnessImport(existing, {
|
|
2920
|
+
projectName: basename5(projectDir),
|
|
2921
|
+
tracks,
|
|
2922
|
+
continuousSkills
|
|
2923
|
+
});
|
|
2913
2924
|
if (content !== existing) {
|
|
2914
2925
|
writeFileSync6(target, content);
|
|
2915
2926
|
}
|
|
@@ -2918,7 +2929,7 @@ function writeRootClaudeMd(projectDir, tracks) {
|
|
|
2918
2929
|
function chmodHooksSync(hookDir) {
|
|
2919
2930
|
for (const file of listHookFiles(hookDir)) {
|
|
2920
2931
|
try {
|
|
2921
|
-
|
|
2932
|
+
chmodSync3(file, 493);
|
|
2922
2933
|
} catch {
|
|
2923
2934
|
}
|
|
2924
2935
|
}
|
|
@@ -3085,8 +3096,8 @@ function renderCliArtifacts(log, spec, report) {
|
|
|
3085
3096
|
log(
|
|
3086
3097
|
assetRow(
|
|
3087
3098
|
"success",
|
|
3088
|
-
".agents/skills/<id>/
|
|
3089
|
-
`${report.codex.skillFiles
|
|
3099
|
+
".agents/skills/<id>/",
|
|
3100
|
+
`${countSkillDirs(report.codex.skillFiles)} skills`
|
|
3090
3101
|
)
|
|
3091
3102
|
);
|
|
3092
3103
|
}
|
|
@@ -3104,7 +3115,7 @@ function renderCliArtifacts(log, spec, report) {
|
|
|
3104
3115
|
assetRow(
|
|
3105
3116
|
"success",
|
|
3106
3117
|
".agents/skills/",
|
|
3107
|
-
`${report.opencode.skillFiles
|
|
3118
|
+
`${countSkillDirs(report.opencode.skillFiles)} dev-method skills (codex\xB7antigravity \uC640 \uAC19\uC740 \uC790\uB9AC)`
|
|
3108
3119
|
)
|
|
3109
3120
|
);
|
|
3110
3121
|
}
|
|
@@ -3126,8 +3137,8 @@ function renderCliArtifacts(log, spec, report) {
|
|
|
3126
3137
|
log(
|
|
3127
3138
|
assetRow(
|
|
3128
3139
|
"success",
|
|
3129
|
-
".agents/skills/<id>/
|
|
3130
|
-
`${report.antigravity.skillFiles
|
|
3140
|
+
".agents/skills/<id>/",
|
|
3141
|
+
`${countSkillDirs(report.antigravity.skillFiles)} skills`
|
|
3131
3142
|
)
|
|
3132
3143
|
);
|
|
3133
3144
|
}
|
|
@@ -3193,15 +3204,25 @@ function renderFinalSummary(log, spec, report, fromWizard) {
|
|
|
3193
3204
|
log(infoRow("NEXT", `Open ${c.bold(label)} \u2014 installed rules & skills are now active`));
|
|
3194
3205
|
const scaffoldFiles = scaffoldFilesForCli(spec.cli);
|
|
3195
3206
|
if (scaffoldFiles.length > 0) {
|
|
3207
|
+
const auditInstalled = buildManifestSpec(spec).selectedInternalSkills?.includes("audit-harness-fit");
|
|
3208
|
+
const populateHint = auditInstalled ? `, or ask the ${c.bold("audit-harness-fit")} skill to fill it from repository evidence` : "";
|
|
3196
3209
|
log(
|
|
3197
3210
|
infoRow(
|
|
3198
3211
|
"FILL",
|
|
3199
|
-
`${scaffoldFiles.map((f) => c.bold(f)).join(" \xB7 ")} \u2014 a fill-in scaffold. Open and paste each ${c.bold("<!-- FILL: \u2026 -->")} prompt to your agent to tailor it to this project`
|
|
3212
|
+
`${scaffoldFiles.map((f) => c.bold(f)).join(" \xB7 ")} \u2014 a fill-in scaffold. Open and paste each ${c.bold("<!-- FILL: \u2026 -->")} prompt to your agent to tailor it to this project${populateHint}`
|
|
3200
3213
|
)
|
|
3201
3214
|
);
|
|
3202
3215
|
}
|
|
3203
3216
|
log("");
|
|
3204
3217
|
}
|
|
3218
|
+
function countSkillDirs(skillFiles) {
|
|
3219
|
+
const ids = /* @__PURE__ */ new Set();
|
|
3220
|
+
for (const f of skillFiles) {
|
|
3221
|
+
const m = /[\\/]\.agents[\\/]skills[\\/]([^\\/]+)[\\/]/.exec(f);
|
|
3222
|
+
if (m?.[1] !== void 0) ids.add(m[1]);
|
|
3223
|
+
}
|
|
3224
|
+
return ids.size;
|
|
3225
|
+
}
|
|
3205
3226
|
function scaffoldFilesForCli(cli2) {
|
|
3206
3227
|
const files = [];
|
|
3207
3228
|
if (cli2.includes("claude")) {
|
|
@@ -3268,6 +3289,15 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3268
3289
|
if (baseline.updateMode.rootImportAdded) {
|
|
3269
3290
|
log(assetRow("success", "CLAUDE.md", `${HARNESS_IMPORT_LINE} import added`));
|
|
3270
3291
|
}
|
|
3292
|
+
if (baseline.updateMode.rootBlockRefreshed) {
|
|
3293
|
+
log(
|
|
3294
|
+
assetRow(
|
|
3295
|
+
"success",
|
|
3296
|
+
"CLAUDE.md",
|
|
3297
|
+
"harness block refreshed (skills that apply continuously)"
|
|
3298
|
+
)
|
|
3299
|
+
);
|
|
3300
|
+
}
|
|
3271
3301
|
if (baseline.updateMode.legacyAnchor) {
|
|
3272
3302
|
log(
|
|
3273
3303
|
assetRow(
|
|
@@ -3313,6 +3343,16 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3313
3343
|
)
|
|
3314
3344
|
);
|
|
3315
3345
|
}
|
|
3346
|
+
for (const id of baseline.updateMode.retiredAgents) {
|
|
3347
|
+
const instead = RETIRED_AGENTS.find((a) => a.id === id)?.instead;
|
|
3348
|
+
log(
|
|
3349
|
+
assetRow(
|
|
3350
|
+
"skip",
|
|
3351
|
+
"agents",
|
|
3352
|
+
`${id} \xB7 \uC774 \uB9B4\uB9AC\uC988\uC5D0\uC11C \uC740\uD1F4 \u2014 .claude/agents/${id}.md \uB97C \uC9C0\uC6CC\uB3C4 \uB41C\uB2E4` + (instead === void 0 ? "" : ` \xB7 ${instead}`)
|
|
3353
|
+
)
|
|
3354
|
+
);
|
|
3355
|
+
}
|
|
3316
3356
|
if (baseline.updateMode.policyBackedUp.length > 0) {
|
|
3317
3357
|
log(
|
|
3318
3358
|
assetRow(
|
|
@@ -3366,13 +3406,37 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3366
3406
|
log(assetRow("skip", "external skills", `${meta} \xB7 \uB2E4\uC74C update \uC5D0\uC11C \uB2E4\uC2DC \uC2DC\uB3C4\uD55C\uB2E4`));
|
|
3367
3407
|
}
|
|
3368
3408
|
if (baseline.updateMode.externalSkillsNotInCatalog.length > 0) {
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3409
|
+
const ids = baseline.updateMode.externalSkillsNotInCatalog;
|
|
3410
|
+
for (const id of ids) {
|
|
3411
|
+
const renamedTo = RENAMED_SKILL_IDS[id];
|
|
3412
|
+
if (renamedTo) {
|
|
3413
|
+
log(
|
|
3414
|
+
assetRow(
|
|
3415
|
+
"skip",
|
|
3416
|
+
"skills",
|
|
3417
|
+
`${id} \uB294 ${renamedTo} \uAC00 \uB410\uB2E4 \xB7 update \uB4A4 .claude/skills/${id} \uB97C \uC9C0\uC6B0\uACE0 ${renamedTo} \uB97C \uC120\uD0DD\uD574 \uBC1B\uB294\uB2E4`
|
|
3418
|
+
)
|
|
3419
|
+
);
|
|
3420
|
+
} else if (RETIRED_SKILL_IDS.includes(id)) {
|
|
3421
|
+
log(
|
|
3422
|
+
assetRow(
|
|
3423
|
+
"skip",
|
|
3424
|
+
"skills",
|
|
3425
|
+
`${id} \xB7 \uC774 \uB9B4\uB9AC\uC988\uC5D0\uC11C \uC740\uD1F4 \u2014 .claude/skills/${id} \uB97C \uC9C0\uC6CC\uB3C4 \uB41C\uB2E4`
|
|
3426
|
+
)
|
|
3427
|
+
);
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
const unknown = ids.filter((id) => !RENAMED_SKILL_IDS[id] && !RETIRED_SKILL_IDS.includes(id));
|
|
3431
|
+
if (unknown.length > 0) {
|
|
3432
|
+
log(
|
|
3433
|
+
assetRow(
|
|
3434
|
+
"skip",
|
|
3435
|
+
"external skills",
|
|
3436
|
+
`${unknown.join(", ")} \xB7 \uCE74\uD0C8\uB85C\uADF8\uC5D0 \uC5C6\uC5B4 \uAC31\uC2E0 \uB300\uC0C1\uC774 \uC544\uB2C8\uB2E4`
|
|
3437
|
+
)
|
|
3438
|
+
);
|
|
3439
|
+
}
|
|
3376
3440
|
}
|
|
3377
3441
|
if (baseline.updateMode.externalSkillsUnknown) {
|
|
3378
3442
|
log(
|
|
@@ -3412,17 +3476,12 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3412
3476
|
phase1Row(
|
|
3413
3477
|
"agents",
|
|
3414
3478
|
cats.agents.length,
|
|
3415
|
-
"
|
|
3479
|
+
"independent verifier \xB7 implementation lane \xB7 domain lanes (track)",
|
|
3416
3480
|
cats.agents
|
|
3417
3481
|
);
|
|
3418
3482
|
}
|
|
3419
3483
|
if (cats.hooks.length > 0) {
|
|
3420
|
-
phase1Row(
|
|
3421
|
-
"hooks",
|
|
3422
|
-
cats.hooks.length,
|
|
3423
|
-
"session-start \xB7 protect-files \xB7 task-brief-nudge",
|
|
3424
|
-
cats.hooks
|
|
3425
|
-
);
|
|
3484
|
+
phase1Row("hooks", cats.hooks.length, "session-start \xB7 protect-files", cats.hooks);
|
|
3426
3485
|
}
|
|
3427
3486
|
if (cats.commands > 0) {
|
|
3428
3487
|
phase1Row("commands", cats.commands, "slash commands");
|
|
@@ -3431,7 +3490,7 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3431
3490
|
phase1Row(
|
|
3432
3491
|
"skills",
|
|
3433
3492
|
cats.skills.length,
|
|
3434
|
-
"
|
|
3493
|
+
"bundled methodology skills (track \xB7 opt-in)",
|
|
3435
3494
|
cats.skills
|
|
3436
3495
|
);
|
|
3437
3496
|
}
|
|
@@ -3462,24 +3521,6 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false, claud
|
|
|
3462
3521
|
)
|
|
3463
3522
|
);
|
|
3464
3523
|
}
|
|
3465
|
-
if (baseline.superseded.removed.length > 0) {
|
|
3466
|
-
log(
|
|
3467
|
-
assetRow(
|
|
3468
|
-
"success",
|
|
3469
|
-
"superseded \u2014 cleaned",
|
|
3470
|
-
`${baseline.superseded.removed.length} \u2014 ${baseline.superseded.removed.map((t) => t.replace(/^\.claude\//, "")).join(", ")} \xB7 \uB418\uB3CC\uB9AC\uB824\uBA74 \uADF8 \uC790\uC0B0 \uC5C6\uC774 \uC7AC\uC124\uCE58`
|
|
3471
|
-
)
|
|
3472
|
-
);
|
|
3473
|
-
}
|
|
3474
|
-
if (baseline.superseded.kept.length > 0) {
|
|
3475
|
-
log(
|
|
3476
|
-
assetRow(
|
|
3477
|
-
"skip",
|
|
3478
|
-
"superseded \u2014 kept",
|
|
3479
|
-
`${baseline.superseded.kept.length} \u2014 ${baseline.superseded.kept.map((t) => t.replace(/^\.claude\//, "")).join(", ")} \xB7 \uC774\uBC88 \uC120\uD0DD\uC774 \uB300\uCCB4\uD588\uC9C0\uB9CC \uC9C0\uC6B0\uC9C0 \uC54A\uC558\uB2E4 (\uC81C\uAC70: agent-harness uninstall)`
|
|
3480
|
-
)
|
|
3481
|
-
);
|
|
3482
|
-
}
|
|
3483
3524
|
const TEMPLATES_COL = 28;
|
|
3484
3525
|
if (baseline.rootClaudeMd) {
|
|
3485
3526
|
const n = baseline.rootClaudeMd.tracks.length;
|
|
@@ -5593,7 +5634,7 @@ var INSTALL_TARGET_PAGES = [
|
|
|
5593
5634
|
// 2026-08-16 — 트랙이 고르는 자산을 **맨 앞에서** 전부 보여준다. 그전까지 이 항목들은 화면에
|
|
5594
5635
|
// 한 번도 안 나온 채 깔렸고, 사용자는 설치가 끝난 뒤 요약에서 처음 봤다.
|
|
5595
5636
|
//
|
|
5596
|
-
// 2페이지로 나눈 근거는 실측이다: 최대 트랙(`full`)이 32항목(rules 6 · agents 9 · hooks 3 ·
|
|
5637
|
+
// 2페이지로 나눈 근거는 실측이다: 최대 트랙(`full`)이 32항목(당시 실측 — rules 6 · agents 9 · hooks 3 ·
|
|
5597
5638
|
// skills 14)이라 clack groupMultiselect 의 "페이지당 ≤ ~30행" 제약을 한 페이지로는 못 지킨다.
|
|
5598
5639
|
// 가르는 축은 역할이다 — 정책·가드가 한쪽, 실행 능력이 다른 쪽.
|
|
5599
5640
|
{ label: "Track baseline \u2014 Rules & Hooks (\uC815\uCC45\xB7\uAC00\uB4DC)", cats: [], baseline: ["rules", "hooks"] },
|
|
@@ -5775,23 +5816,6 @@ Proceed?`,
|
|
|
5775
5816
|
});
|
|
5776
5817
|
return q(result) ? null : result;
|
|
5777
5818
|
},
|
|
5778
|
-
confirmSupersededCleanup: async (items, context) => {
|
|
5779
|
-
const names = items.map((i) => i.target.replace(/^\.claude\//, "")).join("\n ");
|
|
5780
|
-
const result = await ue({
|
|
5781
|
-
message: [
|
|
5782
|
-
`${context.by.join(" \xB7 ")} \uC744(\uB97C) \uACE8\uB77C\uC11C \uC544\uB798 ${items.length}\uAC1C\uAC00 \uC124\uCE58 \uB300\uC0C1\uC5D0\uC11C \uBE60\uC84C\uC2B5\uB2C8\uB2E4.`,
|
|
5783
|
-
"\uADF8\uB7F0\uB370 \uC774\uC804 \uC124\uCE58\uBCF8\uC774 \uB514\uC2A4\uD06C\uC5D0 \uADF8\uB300\uB85C \uB0A8\uC544 \uC788\uC2B5\uB2C8\uB2E4:",
|
|
5784
|
-
` ${names}`,
|
|
5785
|
-
"",
|
|
5786
|
-
`\uADF8\uB300\uB85C \uB450\uBA74 \uAC19\uC740 \uC77C\uC744 \uD558\uB294 \uC5D0\uC774\uC804\uD2B8\uAC00 \uB450 \uBC8C\uC774\uACE0, \uB9E4 \uC138\uC158 ~${context.tokens} tok \uB97C \uACC4\uC18D \uBB45\uB2C8\uB2E4.`,
|
|
5787
|
-
"(\uD558\uB124\uC2A4\uAC00 \uAE54\uC558\uACE0 \uC218\uC815\uB418\uC9C0 \uC54A\uC740 \uD30C\uC77C\uB9CC \uB300\uC0C1\uC785\uB2C8\uB2E4 \u2014 \uC9C1\uC811 \uACE0\uCE5C \uD30C\uC77C\uC740 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4)",
|
|
5788
|
-
"",
|
|
5789
|
-
"\uC815\uB9AC\uD560\uAE4C\uC694?"
|
|
5790
|
-
].join("\n"),
|
|
5791
|
-
initialValue: true
|
|
5792
|
-
});
|
|
5793
|
-
return q(result) ? null : result;
|
|
5794
|
-
},
|
|
5795
5819
|
selectInstallTargets: async (initialChecked, step, recap) => {
|
|
5796
5820
|
const pages = INSTALL_TARGET_PAGES;
|
|
5797
5821
|
const baselineTargets = listBaselineTargets({ tracks: recap?.tracks ?? [] });
|
|
@@ -6057,50 +6081,11 @@ ${summary}`
|
|
|
6057
6081
|
...userOverride ? { userOverride } : {},
|
|
6058
6082
|
...baselineExclude.length > 0 ? { baselineExclude } : {}
|
|
6059
6083
|
};
|
|
6060
|
-
const superseded = findSuperseded(
|
|
6061
|
-
projectDir,
|
|
6062
|
-
buildManifestSpec(spec),
|
|
6063
|
-
readInstallLog(projectDir)
|
|
6064
|
-
);
|
|
6065
|
-
let cleanSuperseded = false;
|
|
6066
|
-
if (superseded.length > 0) {
|
|
6067
|
-
const cause = supersededCause(projectDir, spec, readInstallLog(projectDir), superseded);
|
|
6068
|
-
const answer = await prompts.confirmSupersededCleanup(superseded, {
|
|
6069
|
-
by: cause.length > 0 ? cause : ["\uC774\uBC88 \uC120\uD0DD"],
|
|
6070
|
-
tokens: supersededTokens(superseded)
|
|
6071
|
-
});
|
|
6072
|
-
if (answer === null) {
|
|
6073
|
-
step = "confirm";
|
|
6074
|
-
continue;
|
|
6075
|
-
}
|
|
6076
|
-
cleanSuperseded = answer;
|
|
6077
|
-
}
|
|
6078
6084
|
prompts.outro(stepLabel(WIZARD.INSTALL, "Installing..."));
|
|
6079
|
-
return {
|
|
6080
|
-
ok: true,
|
|
6081
|
-
mode,
|
|
6082
|
-
spec: { ...spec, ...cleanSuperseded ? { cleanSuperseded } : {} }
|
|
6083
|
-
};
|
|
6085
|
+
return { ok: true, mode, spec };
|
|
6084
6086
|
}
|
|
6085
6087
|
}
|
|
6086
6088
|
}
|
|
6087
|
-
function supersededTokens(items) {
|
|
6088
|
-
const r = residentCost(items.map((i) => ({ source: i.source, target: i.target })));
|
|
6089
|
-
return r.rules + r.agentDescriptors + r.skillDescriptors;
|
|
6090
|
-
}
|
|
6091
|
-
function supersededCause(projectDir, spec, log, found) {
|
|
6092
|
-
const selected = spec.userOverride?.forceInclude ?? [];
|
|
6093
|
-
return selected.filter((id) => {
|
|
6094
|
-
const without = {
|
|
6095
|
-
...spec,
|
|
6096
|
-
userOverride: {
|
|
6097
|
-
forceInclude: selected.filter((x) => x !== id),
|
|
6098
|
-
forceExclude: [...spec.userOverride?.forceExclude ?? [], id]
|
|
6099
|
-
}
|
|
6100
|
-
};
|
|
6101
|
-
return findSuperseded(projectDir, buildManifestSpec(without), log).length < found.length;
|
|
6102
|
-
});
|
|
6103
|
-
}
|
|
6104
6089
|
function tracksEqual(a, b2) {
|
|
6105
6090
|
if (a.length !== b2.length) return false;
|
|
6106
6091
|
const sortedA = [...a].sort();
|