@uzysjung/agent-harness 26.153.0 → 26.154.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-OYNSK6QF.js → chunk-242TVGMZ.js} +558 -541
- package/dist/chunk-242TVGMZ.js.map +1 -0
- package/dist/index.js +577 -301
- package/dist/index.js.map +1 -1
- package/dist/trust-tier-drift.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-OYNSK6QF.js.map +0 -1
|
@@ -70,6 +70,14 @@ var DEFAULT_OPTIONS = {
|
|
|
70
70
|
withPrune: false,
|
|
71
71
|
withCodexTrust: false
|
|
72
72
|
};
|
|
73
|
+
var UPDATE_GROUPS = [
|
|
74
|
+
"skills",
|
|
75
|
+
"new-skills",
|
|
76
|
+
"rules",
|
|
77
|
+
"anchor",
|
|
78
|
+
"hooks",
|
|
79
|
+
"external"
|
|
80
|
+
];
|
|
73
81
|
|
|
74
82
|
// src/external-assets.ts
|
|
75
83
|
init_esm_shims();
|
|
@@ -1120,138 +1128,515 @@ function filterApplicableAssets(assets, ctx) {
|
|
|
1120
1128
|
return assets.filter((a) => shouldInstallAsset(a, ctx));
|
|
1121
1129
|
}
|
|
1122
1130
|
|
|
1123
|
-
// src/
|
|
1131
|
+
// src/context-cost.ts
|
|
1124
1132
|
init_esm_shims();
|
|
1133
|
+
import { existsSync, readFileSync } from "fs";
|
|
1134
|
+
import { join, resolve } from "path";
|
|
1135
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1125
1136
|
|
|
1126
|
-
// src/
|
|
1137
|
+
// src/project-claude-merge.ts
|
|
1127
1138
|
init_esm_shims();
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1139
|
+
var HARNESS_ANCHOR_FILE = "CLAUDE-uzys-harness.md";
|
|
1140
|
+
var HARNESS_IMPORT_LINE = `@${HARNESS_ANCHOR_FILE}`;
|
|
1141
|
+
var IMPORT_MARKER_START = "<!-- uzys-harness:import:start -->";
|
|
1142
|
+
var IMPORT_MARKER_END = "<!-- uzys-harness:import:end -->";
|
|
1143
|
+
function renderContinuousSkillsNote(selectedInternalSkills) {
|
|
1144
|
+
const lines = CONTINUOUS_SKILLS.filter((s) => selectedInternalSkills.includes(s.id)).map(
|
|
1145
|
+
(s) => `- \`${s.id}\` \u2014 ${s.whenToApply}.`
|
|
1146
|
+
);
|
|
1147
|
+
if (lines.length === 0) return "";
|
|
1148
|
+
return [
|
|
1149
|
+
"## Skills that apply continuously",
|
|
1150
|
+
"",
|
|
1151
|
+
"These installed skills apply to every response or delegation; no prompt ever looks like their job, so open them from this list:",
|
|
1152
|
+
"",
|
|
1153
|
+
...lines
|
|
1154
|
+
].join("\n");
|
|
1155
|
+
}
|
|
1156
|
+
function withContinuousSkillsNote(scaffold, selectedInternalSkills) {
|
|
1157
|
+
const note = renderContinuousSkillsNote(selectedInternalSkills);
|
|
1158
|
+
return note ? `${scaffold}
|
|
1131
1159
|
|
|
1132
|
-
|
|
1133
|
-
init_esm_shims();
|
|
1134
|
-
import {
|
|
1135
|
-
copyFileSync,
|
|
1136
|
-
cpSync,
|
|
1137
|
-
existsSync,
|
|
1138
|
-
constants as fsConstants,
|
|
1139
|
-
mkdirSync,
|
|
1140
|
-
readdirSync,
|
|
1141
|
-
readFileSync,
|
|
1142
|
-
realpathSync,
|
|
1143
|
-
renameSync,
|
|
1144
|
-
rmdirSync
|
|
1145
|
-
} from "fs";
|
|
1146
|
-
import { dirname, join } from "path";
|
|
1147
|
-
function ensureDir(path2) {
|
|
1148
|
-
mkdirSync(path2, { recursive: true });
|
|
1160
|
+
${note}` : scaffold;
|
|
1149
1161
|
}
|
|
1150
|
-
function
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1162
|
+
function importBlock(continuousSkills) {
|
|
1163
|
+
const note = renderContinuousSkillsNote(continuousSkills);
|
|
1164
|
+
const body = note ? `${HARNESS_IMPORT_LINE}
|
|
1165
|
+
|
|
1166
|
+
${note}` : HARNESS_IMPORT_LINE;
|
|
1167
|
+
return `${IMPORT_MARKER_START}
|
|
1168
|
+
${body}
|
|
1169
|
+
${IMPORT_MARKER_END}`;
|
|
1156
1170
|
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1171
|
+
var TRACK_DISPLAY_NAMES = {
|
|
1172
|
+
tooling: "Tooling",
|
|
1173
|
+
"csr-supabase": "CSR Supabase",
|
|
1174
|
+
"csr-fastify": "CSR Fastify",
|
|
1175
|
+
"csr-fastapi": "CSR FastAPI",
|
|
1176
|
+
"ssr-htmx": "SSR HTMX",
|
|
1177
|
+
"ssr-nextjs": "SSR Next.js",
|
|
1178
|
+
data: "Data",
|
|
1179
|
+
executive: "Executive",
|
|
1180
|
+
full: "Full",
|
|
1181
|
+
"project-management": "Project Management",
|
|
1182
|
+
"growth-marketing": "Growth Marketing"
|
|
1183
|
+
};
|
|
1184
|
+
var FULL_EXPANSION = TRACKS.filter((t) => t !== "full");
|
|
1185
|
+
var FILL_SECTIONS = [
|
|
1186
|
+
"identity",
|
|
1187
|
+
"stack",
|
|
1188
|
+
"architecture",
|
|
1189
|
+
"installed-assets",
|
|
1190
|
+
"boundaries",
|
|
1191
|
+
"verify"
|
|
1192
|
+
];
|
|
1193
|
+
var FILL_SPECS = {
|
|
1194
|
+
identity: {
|
|
1195
|
+
title: "Identity & Purpose",
|
|
1196
|
+
prompt: `Replace the H1 title above with this project's real name, then state in 1-2 plain sentences what it does, who uses it, and why it exists. Sources: README.md, the package.json / pyproject.toml "description", docs/. Do NOT describe the harness itself. Delete this comment when done.`,
|
|
1197
|
+
placeholder: "what this project is, who it is for, and why it exists"
|
|
1198
|
+
},
|
|
1199
|
+
stack: {
|
|
1200
|
+
title: "Stack & Commands",
|
|
1201
|
+
prompt: "Replace the list below with this project's REAL stack. Inspect package.json / pyproject.toml / go.mod / Cargo.toml / Gemfile + lockfiles and any Makefile/justfile/package scripts. List the language(s)+versions, framework(s), package manager, and the exact install/build/test/run commands. Verify each command exists before writing it \u2014 never guess. Delete lines that do not apply, then delete this comment.",
|
|
1202
|
+
placeholder: "languages, runtimes, package manager, and the install/build/test/run commands"
|
|
1203
|
+
},
|
|
1204
|
+
architecture: {
|
|
1205
|
+
title: "Architecture & Layout",
|
|
1206
|
+
prompt: "Map this repository. List each top-level source directory and what it holds, the entry point(s), the 3-5 files a newcomer reads first, and how data/requests flow between the main layers. View the real tree (e.g. `git ls-files | sed 's#/.*##' | sort -u`) \u2014 do not assume a framework's conventional layout. Flag any generated/vendored directories that must never be hand-edited. If this is a single small module, say so in one line. Delete this comment when done.",
|
|
1207
|
+
placeholder: "where things live, the entry points, and how data flows between layers"
|
|
1208
|
+
},
|
|
1209
|
+
"installed-assets": {
|
|
1210
|
+
title: "Installed Harness Assets",
|
|
1211
|
+
prompt: "List the harness assets installed in this project (rules / skills / agents / commands) and add one line each on when to reach for it here. Verify against .claude/rules/*.md, .claude/skills/, .claude/agents/, .claude/commands/ (or this CLI's equivalent) and list ONLY assets whose file exists on disk \u2014 do not invent any. Do NOT restate the harness's working principles (they live in this project's harness anchor and rules layer) \u2014 cross-reference them instead. Delete this comment when done.",
|
|
1212
|
+
placeholder: "the installed rules/skills/agents/commands and when to use each"
|
|
1213
|
+
},
|
|
1214
|
+
boundaries: {
|
|
1215
|
+
title: "Boundaries \u2014 Always / Ask First / Never",
|
|
1216
|
+
prompt: `Fill the Always / Ask First / Never lists with this repo's real red lines. Read the CI config, CODEOWNERS, .gitignore, and release/deploy scripts. Never must include secrets, generated files, force-push, and direct commits to the default branch, plus any repo-specific "do not touch" directories. Every entry must be specific and enforceable here. Delete this comment when done.`,
|
|
1217
|
+
placeholder: "what to always do, ask before doing, and never do in this repo"
|
|
1218
|
+
},
|
|
1219
|
+
verify: {
|
|
1220
|
+
title: "Verification Gate",
|
|
1221
|
+
prompt: 'State the single command (or short sequence) that PROVES a change is safe here \u2014 the test/lint/typecheck/build gate you run before committing \u2014 plus the coverage/CI threshold and what "done" means. Sources: package scripts, CI workflow files, CONTRIBUTING. An agent must be able to self-verify without guessing. Delete this comment when done.',
|
|
1222
|
+
placeholder: "the single command that proves a change is safe, and the done bar"
|
|
1160
1223
|
}
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
if (foreign !== null) {
|
|
1166
|
-
skipped.push(foreign);
|
|
1167
|
-
continue;
|
|
1168
|
-
}
|
|
1169
|
-
copyFile(join(source, rel), join(target, rel));
|
|
1224
|
+
};
|
|
1225
|
+
function anchorLine(surface) {
|
|
1226
|
+
if (surface === "claude") {
|
|
1227
|
+
return `> The working principles live in this project's harness anchor \`${HARNESS_ANCHOR_FILE}\`, imported at the bottom of this file. Everything above that import is **project-specific context only**.`;
|
|
1170
1228
|
}
|
|
1171
|
-
|
|
1229
|
+
const self = surface === "antigravity-rule" ? "`.agents/rules/uzys-harness.md`" : "`AGENTS.md`";
|
|
1230
|
+
return `> **This file (${self}) is the harness anchor** \u2014 it carries the working principles in full, further down. Only the \`## \u2026\` sections immediately below are project-specific context for you to fill in.`;
|
|
1172
1231
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
return candidate;
|
|
1180
|
-
} catch (err) {
|
|
1181
|
-
if (err.code !== "EEXIST") throw err;
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
throw new Error(`Cannot allocate a backup path for ${base} (${MAX_BACKUP_CLAIMS} taken)`);
|
|
1232
|
+
function scaffoldBanner(surface) {
|
|
1233
|
+
return [
|
|
1234
|
+
"> \u2699\uFE0F **SCAFFOLD \u2014 not filled in yet.** The sections below are a fill-in template for THIS project, not verified facts.",
|
|
1235
|
+
"> To fill: open this file and paste each `<!-- FILL: \u2026 -->` comment's instruction into your coding agent (e.g. Claude Code) \u2014 it will inspect the real repo and write the section. You can also fill them by hand; the comments are the instructions.",
|
|
1236
|
+
anchorLine(surface)
|
|
1237
|
+
].join("\n");
|
|
1185
1238
|
}
|
|
1186
|
-
function
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1239
|
+
function renderFillScaffold(surface = "claude") {
|
|
1240
|
+
const blocks = FILL_SECTIONS.map((id) => {
|
|
1241
|
+
const spec = FILL_SPECS[id];
|
|
1242
|
+
return `## ${spec.title}
|
|
1243
|
+
|
|
1244
|
+
<!-- FILL:${id} \u2014 ${spec.prompt} -->
|
|
1245
|
+
|
|
1246
|
+
_(not filled yet \u2014 ${spec.placeholder})_`;
|
|
1247
|
+
});
|
|
1248
|
+
return `${scaffoldBanner(surface)}
|
|
1249
|
+
|
|
1250
|
+
${blocks.join("\n\n")}`;
|
|
1194
1251
|
}
|
|
1195
|
-
function
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
return
|
|
1252
|
+
function mergeProjectClaude(opts) {
|
|
1253
|
+
const expanded = expandTracks(opts.tracks);
|
|
1254
|
+
const trackList = expanded.map((t) => TRACK_DISPLAY_NAMES[t]).join(", ");
|
|
1255
|
+
const header = `# ${opts.projectName}
|
|
1256
|
+
|
|
1257
|
+
> Active track(s): ${trackList}`;
|
|
1258
|
+
return `${header}
|
|
1259
|
+
|
|
1260
|
+
${renderFillScaffold()}
|
|
1261
|
+
`;
|
|
1202
1262
|
}
|
|
1203
|
-
function
|
|
1204
|
-
|
|
1205
|
-
|
|
1263
|
+
function upsertHarnessImport(existing, opts) {
|
|
1264
|
+
const block = importBlock(opts.continuousSkills ?? []);
|
|
1265
|
+
if (existing === null) {
|
|
1266
|
+
return `${mergeProjectClaude(opts)}
|
|
1267
|
+
${block}
|
|
1268
|
+
`;
|
|
1206
1269
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1270
|
+
const start = existing.indexOf(IMPORT_MARKER_START);
|
|
1271
|
+
if (start !== -1) {
|
|
1272
|
+
const endAt = existing.indexOf(IMPORT_MARKER_END, start);
|
|
1273
|
+
if (endAt === -1) return existing;
|
|
1274
|
+
const current = existing.slice(start, endAt + IMPORT_MARKER_END.length);
|
|
1275
|
+
if (current === block) return existing;
|
|
1276
|
+
return `${existing.slice(0, start)}${block}${existing.slice(endAt + IMPORT_MARKER_END.length)}`;
|
|
1209
1277
|
}
|
|
1210
|
-
|
|
1278
|
+
if (hasHarnessImport(existing)) {
|
|
1279
|
+
return existing;
|
|
1280
|
+
}
|
|
1281
|
+
const body = existing.endsWith("\n") ? existing : `${existing}
|
|
1282
|
+
`;
|
|
1283
|
+
return `${body}
|
|
1284
|
+
${block}
|
|
1285
|
+
`;
|
|
1211
1286
|
}
|
|
1212
|
-
function
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
);
|
|
1287
|
+
function stripHarnessImport(text) {
|
|
1288
|
+
const start = text.indexOf(IMPORT_MARKER_START);
|
|
1289
|
+
if (start === -1) return null;
|
|
1290
|
+
const endAt = text.indexOf(IMPORT_MARKER_END, start);
|
|
1291
|
+
if (endAt === -1) return null;
|
|
1292
|
+
const before = text.slice(0, start).replace(/\n+$/, "\n");
|
|
1293
|
+
const after = text.slice(endAt + IMPORT_MARKER_END.length).replace(/^\n+/, "");
|
|
1294
|
+
return `${before}${after}`;
|
|
1217
1295
|
}
|
|
1218
|
-
function
|
|
1219
|
-
if (
|
|
1220
|
-
|
|
1221
|
-
for (const
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
} else if (entry.isFile()) {
|
|
1226
|
-
out.push(rel);
|
|
1296
|
+
function hasHarnessImport(text) {
|
|
1297
|
+
if (text.includes(IMPORT_MARKER_START)) return true;
|
|
1298
|
+
let inFence = false;
|
|
1299
|
+
for (const line of text.split("\n")) {
|
|
1300
|
+
if (/^\s*(?:```|~~~)/.test(line)) {
|
|
1301
|
+
inFence = !inFence;
|
|
1302
|
+
continue;
|
|
1227
1303
|
}
|
|
1304
|
+
if (!inFence && line.trim() === HARNESS_IMPORT_LINE) return true;
|
|
1228
1305
|
}
|
|
1229
|
-
return
|
|
1230
|
-
}
|
|
1231
|
-
function formatStamp(now) {
|
|
1232
|
-
return now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z").slice(0, 15);
|
|
1306
|
+
return false;
|
|
1233
1307
|
}
|
|
1234
|
-
function
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
".claude/rules",
|
|
1238
|
-
".claude/skills",
|
|
1239
|
-
".claude/agents",
|
|
1240
|
-
".claude/hooks",
|
|
1241
|
-
"docs/decisions"
|
|
1242
|
-
];
|
|
1243
|
-
for (const d of dirs) {
|
|
1244
|
-
mkdirSync(join(projectDir, d), { recursive: true });
|
|
1308
|
+
function expandTracks(tracks) {
|
|
1309
|
+
if (tracks.includes("full")) {
|
|
1310
|
+
return FULL_EXPANSION;
|
|
1245
1311
|
}
|
|
1312
|
+
return tracks;
|
|
1246
1313
|
}
|
|
1247
1314
|
|
|
1248
|
-
// src/
|
|
1249
|
-
var
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1315
|
+
// src/context-cost.ts
|
|
1316
|
+
var CHARS_PER_TOKEN = 4;
|
|
1317
|
+
function estimateTokens(chars) {
|
|
1318
|
+
return Math.ceil(chars / CHARS_PER_TOKEN);
|
|
1319
|
+
}
|
|
1320
|
+
function resolveBundleRoot() {
|
|
1321
|
+
return resolve(fileURLToPath2(new URL(".", import.meta.url)), "..");
|
|
1322
|
+
}
|
|
1323
|
+
function extractFrontmatter(content) {
|
|
1324
|
+
const m = content.match(/^?---\r?\n([\s\S]*?)\r?\n---/);
|
|
1325
|
+
return m?.[1] ?? null;
|
|
1326
|
+
}
|
|
1327
|
+
function assetDescriptorTokens(assetId, root = resolveBundleRoot()) {
|
|
1328
|
+
const asset = EXTERNAL_ASSETS.find((a) => a.id === assetId);
|
|
1329
|
+
if (!asset || asset.method.kind !== "internal") return null;
|
|
1330
|
+
const skillMd = join(root, "templates", "skills", asset.method.key, "SKILL.md");
|
|
1331
|
+
if (!existsSync(skillMd)) return null;
|
|
1332
|
+
const fm = extractFrontmatter(readFileSync(skillMd, "utf8"));
|
|
1333
|
+
return fm === null ? null : estimateTokens(fm.length);
|
|
1334
|
+
}
|
|
1335
|
+
function assetBodyTokens(assetId, root = resolveBundleRoot()) {
|
|
1336
|
+
const asset = EXTERNAL_ASSETS.find((a) => a.id === assetId);
|
|
1337
|
+
if (!asset || asset.method.kind !== "internal") return null;
|
|
1338
|
+
const skillMd = join(root, "templates", "skills", asset.method.key, "SKILL.md");
|
|
1339
|
+
if (!existsSync(skillMd)) return null;
|
|
1340
|
+
const content = readFileSync(skillMd, "utf8");
|
|
1341
|
+
const fm = extractFrontmatter(content);
|
|
1342
|
+
if (fm === null) return estimateTokens(content.length);
|
|
1343
|
+
const close = content.indexOf("\n---", content.indexOf(fm) + fm.length);
|
|
1344
|
+
const body = close === -1 ? "" : content.slice(close + "\n---".length);
|
|
1345
|
+
return estimateTokens(body.trim().length);
|
|
1346
|
+
}
|
|
1347
|
+
function assetCostRows(assetIds, root = resolveBundleRoot()) {
|
|
1348
|
+
return assetIds.map((id) => ({
|
|
1349
|
+
id,
|
|
1350
|
+
descriptorTokens: assetDescriptorTokens(id, root),
|
|
1351
|
+
bodyTokens: assetBodyTokens(id, root)
|
|
1352
|
+
})).sort((a, b) => (b.bodyTokens ?? -1) - (a.bodyTokens ?? -1));
|
|
1353
|
+
}
|
|
1354
|
+
function makeResidentCost(base) {
|
|
1355
|
+
return {
|
|
1356
|
+
...base,
|
|
1357
|
+
perSkillDescriptor: base.perSkillDescriptor ?? {},
|
|
1358
|
+
total: base.rules + base.projectClaudeMd + base.skillDescriptors + base.agentDescriptors,
|
|
1359
|
+
directive: {
|
|
1360
|
+
items: base.items.rules + base.items.claudeMd,
|
|
1361
|
+
tokens: base.rules + base.projectClaudeMd
|
|
1362
|
+
},
|
|
1363
|
+
firing: {
|
|
1364
|
+
items: base.items.skills + base.items.agents,
|
|
1365
|
+
tokens: base.skillDescriptors + base.agentDescriptors
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1369
|
+
function fileTokens(path2) {
|
|
1370
|
+
return existsSync(path2) ? estimateTokens(readFileSync(path2, "utf8").trim().length) : 0;
|
|
1371
|
+
}
|
|
1372
|
+
function descriptorTokens(path2) {
|
|
1373
|
+
if (!existsSync(path2)) return 0;
|
|
1374
|
+
const fm = extractFrontmatter(readFileSync(path2, "utf8"));
|
|
1375
|
+
return fm === null ? 0 : estimateTokens(fm.length);
|
|
1376
|
+
}
|
|
1377
|
+
function landsOnDisk(target, cli) {
|
|
1378
|
+
if (target.startsWith(".claude/agents/")) return cli.includes("claude");
|
|
1379
|
+
return true;
|
|
1380
|
+
}
|
|
1381
|
+
function residentCost(entries, root = resolveBundleRoot()) {
|
|
1382
|
+
const tpl = (source) => join(root, "templates", source);
|
|
1383
|
+
const measured = (e) => e.file ?? tpl(e.source);
|
|
1384
|
+
const perSkill = {};
|
|
1385
|
+
let rules = 0;
|
|
1386
|
+
let skillDescriptors = 0;
|
|
1387
|
+
let agentDescriptors = 0;
|
|
1388
|
+
let ruleItems = 0;
|
|
1389
|
+
let skillItems = 0;
|
|
1390
|
+
let agentItems = 0;
|
|
1391
|
+
for (const e of entries) {
|
|
1392
|
+
if (e.target.startsWith(".claude/rules/")) {
|
|
1393
|
+
rules += fileTokens(measured(e));
|
|
1394
|
+
ruleItems += 1;
|
|
1395
|
+
} else if (e.target.startsWith(".claude/agents/")) {
|
|
1396
|
+
agentDescriptors += descriptorTokens(measured(e));
|
|
1397
|
+
agentItems += 1;
|
|
1398
|
+
} else if (e.target.startsWith(".claude/skills/")) {
|
|
1399
|
+
const t = descriptorTokens(join(measured(e), "SKILL.md"));
|
|
1400
|
+
skillDescriptors += t;
|
|
1401
|
+
skillItems += 1;
|
|
1402
|
+
const id = /\.claude\/skills\/([^/]+)/.exec(e.target)?.[1];
|
|
1403
|
+
if (id !== void 0) perSkill[id] = (perSkill[id] ?? 0) + t;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
const harnessAnchor = fileTokens(join(root, "templates", "CLAUDE.md"));
|
|
1407
|
+
const projectScaffold = estimateTokens(
|
|
1408
|
+
withContinuousSkillsNote(
|
|
1409
|
+
renderFillScaffold(),
|
|
1410
|
+
CONTINUOUS_SKILLS.map((s) => s.id)
|
|
1411
|
+
).trim().length
|
|
1412
|
+
);
|
|
1413
|
+
const projectClaudeMd = harnessAnchor + projectScaffold;
|
|
1414
|
+
const claudeMdItems = (harnessAnchor > 0 ? 1 : 0) + (projectScaffold > 0 ? 1 : 0);
|
|
1415
|
+
return makeResidentCost({
|
|
1416
|
+
rules,
|
|
1417
|
+
projectClaudeMd,
|
|
1418
|
+
skillDescriptors,
|
|
1419
|
+
agentDescriptors,
|
|
1420
|
+
perSkillDescriptor: perSkill,
|
|
1421
|
+
items: {
|
|
1422
|
+
rules: ruleItems,
|
|
1423
|
+
skills: skillItems,
|
|
1424
|
+
agents: agentItems,
|
|
1425
|
+
claudeMd: claudeMdItems,
|
|
1426
|
+
total: ruleItems + skillItems + agentItems + claudeMdItems
|
|
1427
|
+
}
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
function summarizeContextCost(assetIds, root = resolveBundleRoot()) {
|
|
1431
|
+
let measuredTokens = 0;
|
|
1432
|
+
let measuredCount = 0;
|
|
1433
|
+
let unmeasuredCount = 0;
|
|
1434
|
+
for (const id of assetIds) {
|
|
1435
|
+
const tokens = assetDescriptorTokens(id, root);
|
|
1436
|
+
if (tokens === null) unmeasuredCount += 1;
|
|
1437
|
+
else {
|
|
1438
|
+
measuredTokens += tokens;
|
|
1439
|
+
measuredCount += 1;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
return { measuredTokens, measuredCount, unmeasuredCount };
|
|
1443
|
+
}
|
|
1444
|
+
function formatResidentCostLine(r, unmeasuredCount) {
|
|
1445
|
+
if (r.total === 0) return null;
|
|
1446
|
+
const parts = [
|
|
1447
|
+
`directives ${r.directive.items} ~${r.directive.tokens}`,
|
|
1448
|
+
`[rules ${r.items.rules} ~${r.rules} \xB7 CLAUDE.md ${r.items.claudeMd} ~${r.projectClaudeMd}]`,
|
|
1449
|
+
`triggers ${r.firing.items} ~${r.firing.tokens}`,
|
|
1450
|
+
`[skills ${r.items.skills} ~${r.skillDescriptors} \xB7 agents ${r.items.agents} ~${r.agentDescriptors}]`
|
|
1451
|
+
].join(" \xB7 ");
|
|
1452
|
+
const external = unmeasuredCount > 0 ? ` \xB7 ${unmeasuredCount} external asset${unmeasuredCount === 1 ? "" : "s"} unmeasured` : "";
|
|
1453
|
+
return `session-start context cost: ${r.items.total} items resident \xB7 ~${r.total} tokens/session (${parts}${external})`;
|
|
1454
|
+
}
|
|
1455
|
+
var RESIDENT_ROWS = [
|
|
1456
|
+
{
|
|
1457
|
+
// ADR-083 — 축 소계를 **행으로** 낸다. 소계가 없으면 읽는 사람이 네 줄을 머리로 더해야
|
|
1458
|
+
// 하고, 그러면 "무엇을 줄여야 하나"가 표에서 안 보인다.
|
|
1459
|
+
label: "\uC9C0\uC2DC\uBB38",
|
|
1460
|
+
gap: 13,
|
|
1461
|
+
count: (r) => r.directive.items,
|
|
1462
|
+
tokens: (r) => r.directive.tokens
|
|
1463
|
+
},
|
|
1464
|
+
{ label: " rules", gap: 12, count: (r) => r.items.rules, tokens: (r) => r.rules },
|
|
1465
|
+
{
|
|
1466
|
+
// 한 행에 두 파일(앵커 + 스캐폴드). 개수 열이 2 를 뱉어 그 사실을 드러낸다 — 라벨로
|
|
1467
|
+
// 하나만 이름 붙이면 v26.140.0 이전과 같은 거짓 라벨이 된다.
|
|
1468
|
+
label: " CLAUDE.md",
|
|
1469
|
+
gap: 8,
|
|
1470
|
+
count: (r) => r.items.claudeMd,
|
|
1471
|
+
tokens: (r) => r.projectClaudeMd
|
|
1472
|
+
},
|
|
1473
|
+
{
|
|
1474
|
+
label: "\uBC1C\uD654 \uD45C\uBA74",
|
|
1475
|
+
gap: 10,
|
|
1476
|
+
count: (r) => r.firing.items,
|
|
1477
|
+
tokens: (r) => r.firing.tokens
|
|
1478
|
+
},
|
|
1479
|
+
{
|
|
1480
|
+
label: " skill descriptors",
|
|
1481
|
+
gap: 0,
|
|
1482
|
+
count: (r) => r.items.skills,
|
|
1483
|
+
tokens: (r) => r.skillDescriptors
|
|
1484
|
+
},
|
|
1485
|
+
{
|
|
1486
|
+
label: " agent descriptors",
|
|
1487
|
+
gap: 0,
|
|
1488
|
+
count: (r) => r.items.agents,
|
|
1489
|
+
tokens: (r) => r.agentDescriptors
|
|
1490
|
+
}
|
|
1491
|
+
];
|
|
1492
|
+
function formatResidentCostBlock(r) {
|
|
1493
|
+
const n = (v) => String(v).padStart(3);
|
|
1494
|
+
return [
|
|
1495
|
+
" \uAC1C\uC218 \uD1A0\uD070",
|
|
1496
|
+
...RESIDENT_ROWS.map(
|
|
1497
|
+
(row) => ` ${row.label}${" ".repeat(row.gap)}${n(row.count(r))}\uAC1C ~${row.tokens(r)}`
|
|
1498
|
+
),
|
|
1499
|
+
" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
1500
|
+
` \uC0C1\uC8FC \uD569\uACC4 ${n(r.items.total)}\uAC1C \uC0C1\uC8FC \xB7 ~${r.total} tokens/\uC138\uC158`,
|
|
1501
|
+
"",
|
|
1502
|
+
" \uC9C0\uC2DC\uBB38 = \uC6B0\uB9AC\uAC00 \uD56D\uC0C1 \uC77D\uD788\uAC8C \uB123\uC740 \uAC83. **\uC791\uC744\uC218\uB85D \uC88B\uC740 \uAC12\uC774 \uC544\uB2C8\uB2E4** \u2014 \uD310\uB2E8\uC740",
|
|
1503
|
+
" '\uBD88\uD544\uC694\uD55C \uC9C0\uC2DC\uBB38\uC774 \uC788\uB098'\uB85C \uD55C\uB2E4. ratchet \uC740 \uC870\uC6A9\uD55C \uC99D\uAC00\uB9CC \uB9C9\uB294\uB2E4.",
|
|
1504
|
+
" \uBC1C\uD654 \uD45C\uBA74 = \uC2A4\uD0AC\xB7\uC5D0\uC774\uC804\uD2B8\uAC00 \uBD88\uB9AC\uB824\uACE0 \uC0C1\uC8FC\uC2DC\uD0A4\uB294 descriptor. \uAE4E\uC73C\uBA74 \uC548 \uBD88\uB9B0\uB2E4."
|
|
1505
|
+
];
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
// src/manifest.ts
|
|
1509
|
+
init_esm_shims();
|
|
1510
|
+
|
|
1511
|
+
// src/install-log.ts
|
|
1512
|
+
init_esm_shims();
|
|
1513
|
+
import { createHash } from "crypto";
|
|
1514
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, rmSync, writeFileSync } from "fs";
|
|
1515
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
1516
|
+
|
|
1517
|
+
// src/fs-ops.ts
|
|
1518
|
+
init_esm_shims();
|
|
1519
|
+
import {
|
|
1520
|
+
copyFileSync,
|
|
1521
|
+
cpSync,
|
|
1522
|
+
existsSync as existsSync2,
|
|
1523
|
+
constants as fsConstants,
|
|
1524
|
+
mkdirSync,
|
|
1525
|
+
readdirSync,
|
|
1526
|
+
readFileSync as readFileSync2,
|
|
1527
|
+
realpathSync,
|
|
1528
|
+
renameSync,
|
|
1529
|
+
rmdirSync
|
|
1530
|
+
} from "fs";
|
|
1531
|
+
import { dirname, join as join2 } from "path";
|
|
1532
|
+
function ensureDir(path2) {
|
|
1533
|
+
mkdirSync(path2, { recursive: true });
|
|
1534
|
+
}
|
|
1535
|
+
function copyFile(source, target) {
|
|
1536
|
+
if (!existsSync2(source)) {
|
|
1537
|
+
throw new Error(`Source not found: ${source}`);
|
|
1538
|
+
}
|
|
1539
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
1540
|
+
copyFileSync(source, target);
|
|
1541
|
+
}
|
|
1542
|
+
function copyDir(source, target, foreignOf) {
|
|
1543
|
+
if (!existsSync2(source)) {
|
|
1544
|
+
throw new Error(`Source dir not found: ${source}`);
|
|
1545
|
+
}
|
|
1546
|
+
mkdirSync(target, { recursive: true });
|
|
1547
|
+
const skipped = [];
|
|
1548
|
+
for (const rel of listFilesRecursive(source)) {
|
|
1549
|
+
const foreign = foreignOf(rel);
|
|
1550
|
+
if (foreign !== null) {
|
|
1551
|
+
skipped.push(foreign);
|
|
1552
|
+
continue;
|
|
1553
|
+
}
|
|
1554
|
+
copyFile(join2(source, rel), join2(target, rel));
|
|
1555
|
+
}
|
|
1556
|
+
return skipped;
|
|
1557
|
+
}
|
|
1558
|
+
var MAX_BACKUP_CLAIMS = 100;
|
|
1559
|
+
function claimBackupPath(base, claim) {
|
|
1560
|
+
for (let n = 1; n <= MAX_BACKUP_CLAIMS; n++) {
|
|
1561
|
+
const candidate = n === 1 ? base : `${base}-${n}`;
|
|
1562
|
+
try {
|
|
1563
|
+
claim(candidate);
|
|
1564
|
+
return candidate;
|
|
1565
|
+
} catch (err) {
|
|
1566
|
+
if (err.code !== "EEXIST") throw err;
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
throw new Error(`Cannot allocate a backup path for ${base} (${MAX_BACKUP_CLAIMS} taken)`);
|
|
1570
|
+
}
|
|
1571
|
+
function backupDir(target, now = /* @__PURE__ */ new Date()) {
|
|
1572
|
+
if (!existsSync2(target)) {
|
|
1573
|
+
return null;
|
|
1574
|
+
}
|
|
1575
|
+
const backup = claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) => mkdirSync(c));
|
|
1576
|
+
rmdirSync(backup);
|
|
1577
|
+
renameSync(target, backup);
|
|
1578
|
+
return backup;
|
|
1579
|
+
}
|
|
1580
|
+
function copyBackupDir(target, now = /* @__PURE__ */ new Date()) {
|
|
1581
|
+
if (!existsSync2(target)) {
|
|
1582
|
+
return null;
|
|
1583
|
+
}
|
|
1584
|
+
const backup = claimBackupPath(`${target}.backup-${formatStamp(now)}`, (c) => mkdirSync(c));
|
|
1585
|
+
cpSync(realpathSync(target), backup, { recursive: true });
|
|
1586
|
+
return backup;
|
|
1587
|
+
}
|
|
1588
|
+
function backupFileIfChanged(target, newContent, now = /* @__PURE__ */ new Date()) {
|
|
1589
|
+
if (!existsSync2(target)) {
|
|
1590
|
+
return null;
|
|
1591
|
+
}
|
|
1592
|
+
if (readFileSync2(target, "utf-8") === newContent) {
|
|
1593
|
+
return null;
|
|
1594
|
+
}
|
|
1595
|
+
return backupFile(target, now);
|
|
1596
|
+
}
|
|
1597
|
+
function backupFile(target, now = /* @__PURE__ */ new Date()) {
|
|
1598
|
+
return claimBackupPath(
|
|
1599
|
+
`${target}.backup-${formatStamp(now)}`,
|
|
1600
|
+
(c) => copyFileSync(target, c, fsConstants.COPYFILE_EXCL)
|
|
1601
|
+
);
|
|
1602
|
+
}
|
|
1603
|
+
function listFilesRecursive(dir, prefix = "") {
|
|
1604
|
+
if (!existsSync2(dir)) return [];
|
|
1605
|
+
const out = [];
|
|
1606
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
1607
|
+
const rel = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
1608
|
+
if (entry.isDirectory()) {
|
|
1609
|
+
out.push(...listFilesRecursive(join2(dir, entry.name), rel));
|
|
1610
|
+
} else if (entry.isFile()) {
|
|
1611
|
+
out.push(rel);
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
return out;
|
|
1615
|
+
}
|
|
1616
|
+
function formatStamp(now) {
|
|
1617
|
+
return now.toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "Z").slice(0, 15);
|
|
1618
|
+
}
|
|
1619
|
+
function ensureProjectSkeleton(projectDir) {
|
|
1620
|
+
const dirs = [
|
|
1621
|
+
".claude/commands/uzys",
|
|
1622
|
+
".claude/rules",
|
|
1623
|
+
".claude/skills",
|
|
1624
|
+
".claude/agents",
|
|
1625
|
+
".claude/hooks",
|
|
1626
|
+
"docs/decisions"
|
|
1627
|
+
];
|
|
1628
|
+
for (const d of dirs) {
|
|
1629
|
+
mkdirSync(join2(projectDir, d), { recursive: true });
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
// src/install-log.ts
|
|
1634
|
+
var INSTALL_LOG_FILENAME = ".harness-install.json";
|
|
1635
|
+
var INSTALL_LOG_DIR = ".uzys-agent-harness";
|
|
1636
|
+
var LEGACY_INSTALL_LOG_DIR = ".claude";
|
|
1637
|
+
var INSTALL_LOG_VERSION = 1;
|
|
1638
|
+
var POLICY_DIRS = [
|
|
1639
|
+
{ dir: "rules", ext: ".md" },
|
|
1255
1640
|
{ dir: "agents", ext: ".md" },
|
|
1256
1641
|
{ dir: "commands/uzys", ext: ".md" },
|
|
1257
1642
|
{ dir: "hooks", ext: ".sh" }
|
|
@@ -1371,247 +1756,69 @@ function isHarnessOwned(baseline, key, current) {
|
|
|
1371
1756
|
function mergeExternalFiles(projectDir, previous, current) {
|
|
1372
1757
|
const byPath = /* @__PURE__ */ new Map();
|
|
1373
1758
|
for (const file of [...previous ?? [], ...current]) byPath.set(file.path, file);
|
|
1374
|
-
return [...byPath.values()].filter((f) =>
|
|
1759
|
+
return [...byPath.values()].filter((f) => existsSync3(join3(projectDir, f.path)));
|
|
1375
1760
|
}
|
|
1376
|
-
function collectSkillHashes(projectDir) {
|
|
1377
|
-
const skillsDir =
|
|
1378
|
-
return listFilesRecursive(skillsDir).map((rel) => ({
|
|
1761
|
+
function collectSkillHashes(projectDir, templatesDir) {
|
|
1762
|
+
const skillsDir = join3(projectDir, ".claude/skills");
|
|
1763
|
+
return listFilesRecursive(skillsDir).filter((rel) => existsSync3(join3(templatesDir, "skills", rel))).map((rel) => ({
|
|
1379
1764
|
path: rel,
|
|
1380
|
-
sha256: hashContent(
|
|
1765
|
+
sha256: hashContent(readFileSync3(join3(skillsDir, rel), "utf8"))
|
|
1381
1766
|
}));
|
|
1382
1767
|
}
|
|
1383
1768
|
function collectPolicyHashes(projectDir, templatesDir) {
|
|
1384
1769
|
const out = [];
|
|
1385
1770
|
for (const { dir, ext } of POLICY_DIRS) {
|
|
1386
|
-
const targetDir =
|
|
1387
|
-
const sourceDir =
|
|
1771
|
+
const targetDir = join3(projectDir, ".claude", dir);
|
|
1772
|
+
const sourceDir = join3(templatesDir, dir);
|
|
1388
1773
|
for (const rel of listFilesRecursive(targetDir)) {
|
|
1389
1774
|
if (!rel.endsWith(ext)) continue;
|
|
1390
|
-
if (!
|
|
1775
|
+
if (!existsSync3(join3(sourceDir, rel))) continue;
|
|
1391
1776
|
out.push({
|
|
1392
1777
|
path: `${dir}/${rel}`,
|
|
1393
|
-
sha256: hashContent(
|
|
1778
|
+
sha256: hashContent(readFileSync3(join3(targetDir, rel), "utf8"))
|
|
1394
1779
|
});
|
|
1395
1780
|
}
|
|
1396
|
-
}
|
|
1397
|
-
return out;
|
|
1398
|
-
}
|
|
1399
|
-
function writeInstallLog(projectDir, log) {
|
|
1400
|
-
const path2 = installLogPath(projectDir);
|
|
1401
|
-
mkdirSync2(dirname2(path2), { recursive: true });
|
|
1402
|
-
writeFileSync(path2, `${JSON.stringify(log, null, 2)}
|
|
1403
|
-
`, "utf8");
|
|
1404
|
-
migrateAwayLegacyLog(projectDir);
|
|
1405
|
-
return path2;
|
|
1406
|
-
}
|
|
1407
|
-
function migrateAwayLegacyLog(projectDir) {
|
|
1408
|
-
const legacy = legacyInstallLogPath(projectDir);
|
|
1409
|
-
if (!existsSync2(legacy)) return;
|
|
1410
|
-
try {
|
|
1411
|
-
rmSync(legacy);
|
|
1412
|
-
} catch {
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
function readInstallLog(projectDir) {
|
|
1416
|
-
const path2 = [installLogPath(projectDir), legacyInstallLogPath(projectDir)].find(
|
|
1417
|
-
(p) => existsSync2(p)
|
|
1418
|
-
);
|
|
1419
|
-
if (!path2) return null;
|
|
1420
|
-
try {
|
|
1421
|
-
const parsed = JSON.parse(readFileSync2(path2, "utf8"));
|
|
1422
|
-
if (Array.isArray(parsed.assets)) {
|
|
1423
|
-
parsed.assets = parsed.assets.map(
|
|
1424
|
-
(a) => a.method === "npm-global" ? { ...a, method: "npm" } : a
|
|
1425
|
-
);
|
|
1426
|
-
}
|
|
1427
|
-
return parsed;
|
|
1428
|
-
} catch {
|
|
1429
|
-
return null;
|
|
1430
|
-
}
|
|
1431
|
-
}
|
|
1432
|
-
function installLogPath(projectDir) {
|
|
1433
|
-
return join2(projectDir, INSTALL_LOG_DIR, INSTALL_LOG_FILENAME);
|
|
1434
|
-
}
|
|
1435
|
-
function legacyInstallLogPath(projectDir) {
|
|
1436
|
-
return join2(projectDir, LEGACY_INSTALL_LOG_DIR, INSTALL_LOG_FILENAME);
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
// src/project-claude-merge.ts
|
|
1440
|
-
init_esm_shims();
|
|
1441
|
-
var HARNESS_ANCHOR_FILE = "CLAUDE-uzys-harness.md";
|
|
1442
|
-
var HARNESS_IMPORT_LINE = `@${HARNESS_ANCHOR_FILE}`;
|
|
1443
|
-
var IMPORT_MARKER_START = "<!-- uzys-harness:import:start -->";
|
|
1444
|
-
var IMPORT_MARKER_END = "<!-- uzys-harness:import:end -->";
|
|
1445
|
-
function renderContinuousSkillsNote(selectedInternalSkills) {
|
|
1446
|
-
const lines = CONTINUOUS_SKILLS.filter((s) => selectedInternalSkills.includes(s.id)).map(
|
|
1447
|
-
(s) => `- \`${s.id}\` \u2014 ${s.whenToApply}.`
|
|
1448
|
-
);
|
|
1449
|
-
if (lines.length === 0) return "";
|
|
1450
|
-
return [
|
|
1451
|
-
"## Skills that apply continuously",
|
|
1452
|
-
"",
|
|
1453
|
-
"These installed skills apply to every response or delegation; no prompt ever looks like their job, so open them from this list:",
|
|
1454
|
-
"",
|
|
1455
|
-
...lines
|
|
1456
|
-
].join("\n");
|
|
1457
|
-
}
|
|
1458
|
-
function withContinuousSkillsNote(scaffold, selectedInternalSkills) {
|
|
1459
|
-
const note = renderContinuousSkillsNote(selectedInternalSkills);
|
|
1460
|
-
return note ? `${scaffold}
|
|
1461
|
-
|
|
1462
|
-
${note}` : scaffold;
|
|
1463
|
-
}
|
|
1464
|
-
function importBlock(continuousSkills) {
|
|
1465
|
-
const note = renderContinuousSkillsNote(continuousSkills);
|
|
1466
|
-
const body = note ? `${HARNESS_IMPORT_LINE}
|
|
1467
|
-
|
|
1468
|
-
${note}` : HARNESS_IMPORT_LINE;
|
|
1469
|
-
return `${IMPORT_MARKER_START}
|
|
1470
|
-
${body}
|
|
1471
|
-
${IMPORT_MARKER_END}`;
|
|
1472
|
-
}
|
|
1473
|
-
var TRACK_DISPLAY_NAMES = {
|
|
1474
|
-
tooling: "Tooling",
|
|
1475
|
-
"csr-supabase": "CSR Supabase",
|
|
1476
|
-
"csr-fastify": "CSR Fastify",
|
|
1477
|
-
"csr-fastapi": "CSR FastAPI",
|
|
1478
|
-
"ssr-htmx": "SSR HTMX",
|
|
1479
|
-
"ssr-nextjs": "SSR Next.js",
|
|
1480
|
-
data: "Data",
|
|
1481
|
-
executive: "Executive",
|
|
1482
|
-
full: "Full",
|
|
1483
|
-
"project-management": "Project Management",
|
|
1484
|
-
"growth-marketing": "Growth Marketing"
|
|
1485
|
-
};
|
|
1486
|
-
var FULL_EXPANSION = TRACKS.filter((t) => t !== "full");
|
|
1487
|
-
var FILL_SECTIONS = [
|
|
1488
|
-
"identity",
|
|
1489
|
-
"stack",
|
|
1490
|
-
"architecture",
|
|
1491
|
-
"installed-assets",
|
|
1492
|
-
"boundaries",
|
|
1493
|
-
"verify"
|
|
1494
|
-
];
|
|
1495
|
-
var FILL_SPECS = {
|
|
1496
|
-
identity: {
|
|
1497
|
-
title: "Identity & Purpose",
|
|
1498
|
-
prompt: `Replace the H1 title above with this project's real name, then state in 1-2 plain sentences what it does, who uses it, and why it exists. Sources: README.md, the package.json / pyproject.toml "description", docs/. Do NOT describe the harness itself. Delete this comment when done.`,
|
|
1499
|
-
placeholder: "what this project is, who it is for, and why it exists"
|
|
1500
|
-
},
|
|
1501
|
-
stack: {
|
|
1502
|
-
title: "Stack & Commands",
|
|
1503
|
-
prompt: "Replace the list below with this project's REAL stack. Inspect package.json / pyproject.toml / go.mod / Cargo.toml / Gemfile + lockfiles and any Makefile/justfile/package scripts. List the language(s)+versions, framework(s), package manager, and the exact install/build/test/run commands. Verify each command exists before writing it \u2014 never guess. Delete lines that do not apply, then delete this comment.",
|
|
1504
|
-
placeholder: "languages, runtimes, package manager, and the install/build/test/run commands"
|
|
1505
|
-
},
|
|
1506
|
-
architecture: {
|
|
1507
|
-
title: "Architecture & Layout",
|
|
1508
|
-
prompt: "Map this repository. List each top-level source directory and what it holds, the entry point(s), the 3-5 files a newcomer reads first, and how data/requests flow between the main layers. View the real tree (e.g. `git ls-files | sed 's#/.*##' | sort -u`) \u2014 do not assume a framework's conventional layout. Flag any generated/vendored directories that must never be hand-edited. If this is a single small module, say so in one line. Delete this comment when done.",
|
|
1509
|
-
placeholder: "where things live, the entry points, and how data flows between layers"
|
|
1510
|
-
},
|
|
1511
|
-
"installed-assets": {
|
|
1512
|
-
title: "Installed Harness Assets",
|
|
1513
|
-
prompt: "List the harness assets installed in this project (rules / skills / agents / commands) and add one line each on when to reach for it here. Verify against .claude/rules/*.md, .claude/skills/, .claude/agents/, .claude/commands/ (or this CLI's equivalent) and list ONLY assets whose file exists on disk \u2014 do not invent any. Do NOT restate the harness's working principles (they live in this project's harness anchor and rules layer) \u2014 cross-reference them instead. Delete this comment when done.",
|
|
1514
|
-
placeholder: "the installed rules/skills/agents/commands and when to use each"
|
|
1515
|
-
},
|
|
1516
|
-
boundaries: {
|
|
1517
|
-
title: "Boundaries \u2014 Always / Ask First / Never",
|
|
1518
|
-
prompt: `Fill the Always / Ask First / Never lists with this repo's real red lines. Read the CI config, CODEOWNERS, .gitignore, and release/deploy scripts. Never must include secrets, generated files, force-push, and direct commits to the default branch, plus any repo-specific "do not touch" directories. Every entry must be specific and enforceable here. Delete this comment when done.`,
|
|
1519
|
-
placeholder: "what to always do, ask before doing, and never do in this repo"
|
|
1520
|
-
},
|
|
1521
|
-
verify: {
|
|
1522
|
-
title: "Verification Gate",
|
|
1523
|
-
prompt: 'State the single command (or short sequence) that PROVES a change is safe here \u2014 the test/lint/typecheck/build gate you run before committing \u2014 plus the coverage/CI threshold and what "done" means. Sources: package scripts, CI workflow files, CONTRIBUTING. An agent must be able to self-verify without guessing. Delete this comment when done.',
|
|
1524
|
-
placeholder: "the single command that proves a change is safe, and the done bar"
|
|
1525
|
-
}
|
|
1526
|
-
};
|
|
1527
|
-
function anchorLine(surface) {
|
|
1528
|
-
if (surface === "claude") {
|
|
1529
|
-
return `> The working principles live in this project's harness anchor \`${HARNESS_ANCHOR_FILE}\`, imported at the bottom of this file. Everything above that import is **project-specific context only**.`;
|
|
1530
|
-
}
|
|
1531
|
-
const self = surface === "antigravity-rule" ? "`.agents/rules/uzys-harness.md`" : "`AGENTS.md`";
|
|
1532
|
-
return `> **This file (${self}) is the harness anchor** \u2014 it carries the working principles in full, further down. Only the \`## \u2026\` sections immediately below are project-specific context for you to fill in.`;
|
|
1533
|
-
}
|
|
1534
|
-
function scaffoldBanner(surface) {
|
|
1535
|
-
return [
|
|
1536
|
-
"> \u2699\uFE0F **SCAFFOLD \u2014 not filled in yet.** The sections below are a fill-in template for THIS project, not verified facts.",
|
|
1537
|
-
"> To fill: open this file and paste each `<!-- FILL: \u2026 -->` comment's instruction into your coding agent (e.g. Claude Code) \u2014 it will inspect the real repo and write the section. You can also fill them by hand; the comments are the instructions.",
|
|
1538
|
-
anchorLine(surface)
|
|
1539
|
-
].join("\n");
|
|
1540
|
-
}
|
|
1541
|
-
function renderFillScaffold(surface = "claude") {
|
|
1542
|
-
const blocks = FILL_SECTIONS.map((id) => {
|
|
1543
|
-
const spec = FILL_SPECS[id];
|
|
1544
|
-
return `## ${spec.title}
|
|
1545
|
-
|
|
1546
|
-
<!-- FILL:${id} \u2014 ${spec.prompt} -->
|
|
1547
|
-
|
|
1548
|
-
_(not filled yet \u2014 ${spec.placeholder})_`;
|
|
1549
|
-
});
|
|
1550
|
-
return `${scaffoldBanner(surface)}
|
|
1551
|
-
|
|
1552
|
-
${blocks.join("\n\n")}`;
|
|
1553
|
-
}
|
|
1554
|
-
function mergeProjectClaude(opts) {
|
|
1555
|
-
const expanded = expandTracks(opts.tracks);
|
|
1556
|
-
const trackList = expanded.map((t) => TRACK_DISPLAY_NAMES[t]).join(", ");
|
|
1557
|
-
const header = `# ${opts.projectName}
|
|
1558
|
-
|
|
1559
|
-
> Active track(s): ${trackList}`;
|
|
1560
|
-
return `${header}
|
|
1561
|
-
|
|
1562
|
-
${renderFillScaffold()}
|
|
1563
|
-
`;
|
|
1564
|
-
}
|
|
1565
|
-
function upsertHarnessImport(existing, opts) {
|
|
1566
|
-
const block = importBlock(opts.continuousSkills ?? []);
|
|
1567
|
-
if (existing === null) {
|
|
1568
|
-
return `${mergeProjectClaude(opts)}
|
|
1569
|
-
${block}
|
|
1570
|
-
`;
|
|
1571
|
-
}
|
|
1572
|
-
const start = existing.indexOf(IMPORT_MARKER_START);
|
|
1573
|
-
if (start !== -1) {
|
|
1574
|
-
const endAt = existing.indexOf(IMPORT_MARKER_END, start);
|
|
1575
|
-
if (endAt === -1) return existing;
|
|
1576
|
-
const current = existing.slice(start, endAt + IMPORT_MARKER_END.length);
|
|
1577
|
-
if (current === block) return existing;
|
|
1578
|
-
return `${existing.slice(0, start)}${block}${existing.slice(endAt + IMPORT_MARKER_END.length)}`;
|
|
1579
|
-
}
|
|
1580
|
-
if (hasHarnessImport(existing)) {
|
|
1581
|
-
return existing;
|
|
1582
|
-
}
|
|
1583
|
-
const body = existing.endsWith("\n") ? existing : `${existing}
|
|
1584
|
-
`;
|
|
1585
|
-
return `${body}
|
|
1586
|
-
${block}
|
|
1587
|
-
`;
|
|
1781
|
+
}
|
|
1782
|
+
return out;
|
|
1588
1783
|
}
|
|
1589
|
-
function
|
|
1590
|
-
const
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
return `${before}${after}`;
|
|
1784
|
+
function writeInstallLog(projectDir, log) {
|
|
1785
|
+
const path2 = installLogPath(projectDir);
|
|
1786
|
+
mkdirSync2(dirname2(path2), { recursive: true });
|
|
1787
|
+
writeFileSync(path2, `${JSON.stringify(log, null, 2)}
|
|
1788
|
+
`, "utf8");
|
|
1789
|
+
migrateAwayLegacyLog(projectDir);
|
|
1790
|
+
return path2;
|
|
1597
1791
|
}
|
|
1598
|
-
function
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
continue;
|
|
1605
|
-
}
|
|
1606
|
-
if (!inFence && line.trim() === HARNESS_IMPORT_LINE) return true;
|
|
1792
|
+
function migrateAwayLegacyLog(projectDir) {
|
|
1793
|
+
const legacy = legacyInstallLogPath(projectDir);
|
|
1794
|
+
if (!existsSync3(legacy)) return;
|
|
1795
|
+
try {
|
|
1796
|
+
rmSync(legacy);
|
|
1797
|
+
} catch {
|
|
1607
1798
|
}
|
|
1608
|
-
return false;
|
|
1609
1799
|
}
|
|
1610
|
-
function
|
|
1611
|
-
|
|
1612
|
-
|
|
1800
|
+
function readInstallLog(projectDir) {
|
|
1801
|
+
const path2 = [installLogPath(projectDir), legacyInstallLogPath(projectDir)].find(
|
|
1802
|
+
(p) => existsSync3(p)
|
|
1803
|
+
);
|
|
1804
|
+
if (!path2) return null;
|
|
1805
|
+
try {
|
|
1806
|
+
const parsed = JSON.parse(readFileSync3(path2, "utf8"));
|
|
1807
|
+
if (Array.isArray(parsed.assets)) {
|
|
1808
|
+
parsed.assets = parsed.assets.map(
|
|
1809
|
+
(a) => a.method === "npm-global" ? { ...a, method: "npm" } : a
|
|
1810
|
+
);
|
|
1811
|
+
}
|
|
1812
|
+
return parsed;
|
|
1813
|
+
} catch {
|
|
1814
|
+
return null;
|
|
1613
1815
|
}
|
|
1614
|
-
|
|
1816
|
+
}
|
|
1817
|
+
function installLogPath(projectDir) {
|
|
1818
|
+
return join3(projectDir, INSTALL_LOG_DIR, INSTALL_LOG_FILENAME);
|
|
1819
|
+
}
|
|
1820
|
+
function legacyInstallLogPath(projectDir) {
|
|
1821
|
+
return join3(projectDir, LEGACY_INSTALL_LOG_DIR, INSTALL_LOG_FILENAME);
|
|
1615
1822
|
}
|
|
1616
1823
|
|
|
1617
1824
|
// src/manifest.ts
|
|
@@ -1893,199 +2100,6 @@ var CATEGORY_TITLES = {
|
|
|
1893
2100
|
"ecc-suite": "\u{1F4E6} ECC Suite"
|
|
1894
2101
|
};
|
|
1895
2102
|
|
|
1896
|
-
// src/context-cost.ts
|
|
1897
|
-
init_esm_shims();
|
|
1898
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
|
|
1899
|
-
import { join as join3, resolve } from "path";
|
|
1900
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1901
|
-
var CHARS_PER_TOKEN = 4;
|
|
1902
|
-
function estimateTokens(chars) {
|
|
1903
|
-
return Math.ceil(chars / CHARS_PER_TOKEN);
|
|
1904
|
-
}
|
|
1905
|
-
function resolveBundleRoot() {
|
|
1906
|
-
return resolve(fileURLToPath2(new URL(".", import.meta.url)), "..");
|
|
1907
|
-
}
|
|
1908
|
-
function extractFrontmatter(content) {
|
|
1909
|
-
const m = content.match(/^?---\r?\n([\s\S]*?)\r?\n---/);
|
|
1910
|
-
return m?.[1] ?? null;
|
|
1911
|
-
}
|
|
1912
|
-
function assetDescriptorTokens(assetId, root = resolveBundleRoot()) {
|
|
1913
|
-
const asset = EXTERNAL_ASSETS.find((a) => a.id === assetId);
|
|
1914
|
-
if (!asset || asset.method.kind !== "internal") return null;
|
|
1915
|
-
const skillMd = join3(root, "templates", "skills", asset.method.key, "SKILL.md");
|
|
1916
|
-
if (!existsSync3(skillMd)) return null;
|
|
1917
|
-
const fm = extractFrontmatter(readFileSync3(skillMd, "utf8"));
|
|
1918
|
-
return fm === null ? null : estimateTokens(fm.length);
|
|
1919
|
-
}
|
|
1920
|
-
function assetBodyTokens(assetId, root = resolveBundleRoot()) {
|
|
1921
|
-
const asset = EXTERNAL_ASSETS.find((a) => a.id === assetId);
|
|
1922
|
-
if (!asset || asset.method.kind !== "internal") return null;
|
|
1923
|
-
const skillMd = join3(root, "templates", "skills", asset.method.key, "SKILL.md");
|
|
1924
|
-
if (!existsSync3(skillMd)) return null;
|
|
1925
|
-
const content = readFileSync3(skillMd, "utf8");
|
|
1926
|
-
const fm = extractFrontmatter(content);
|
|
1927
|
-
if (fm === null) return estimateTokens(content.length);
|
|
1928
|
-
const close = content.indexOf("\n---", content.indexOf(fm) + fm.length);
|
|
1929
|
-
const body = close === -1 ? "" : content.slice(close + "\n---".length);
|
|
1930
|
-
return estimateTokens(body.trim().length);
|
|
1931
|
-
}
|
|
1932
|
-
function assetCostRows(assetIds, root = resolveBundleRoot()) {
|
|
1933
|
-
return assetIds.map((id) => ({
|
|
1934
|
-
id,
|
|
1935
|
-
descriptorTokens: assetDescriptorTokens(id, root),
|
|
1936
|
-
bodyTokens: assetBodyTokens(id, root)
|
|
1937
|
-
})).sort((a, b) => (b.bodyTokens ?? -1) - (a.bodyTokens ?? -1));
|
|
1938
|
-
}
|
|
1939
|
-
function makeResidentCost(base) {
|
|
1940
|
-
return {
|
|
1941
|
-
...base,
|
|
1942
|
-
perSkillDescriptor: base.perSkillDescriptor ?? {},
|
|
1943
|
-
total: base.rules + base.projectClaudeMd + base.skillDescriptors + base.agentDescriptors,
|
|
1944
|
-
directive: {
|
|
1945
|
-
items: base.items.rules + base.items.claudeMd,
|
|
1946
|
-
tokens: base.rules + base.projectClaudeMd
|
|
1947
|
-
},
|
|
1948
|
-
firing: {
|
|
1949
|
-
items: base.items.skills + base.items.agents,
|
|
1950
|
-
tokens: base.skillDescriptors + base.agentDescriptors
|
|
1951
|
-
}
|
|
1952
|
-
};
|
|
1953
|
-
}
|
|
1954
|
-
function fileTokens(path2) {
|
|
1955
|
-
return existsSync3(path2) ? estimateTokens(readFileSync3(path2, "utf8").trim().length) : 0;
|
|
1956
|
-
}
|
|
1957
|
-
function descriptorTokens(path2) {
|
|
1958
|
-
if (!existsSync3(path2)) return 0;
|
|
1959
|
-
const fm = extractFrontmatter(readFileSync3(path2, "utf8"));
|
|
1960
|
-
return fm === null ? 0 : estimateTokens(fm.length);
|
|
1961
|
-
}
|
|
1962
|
-
function residentCost(entries, root = resolveBundleRoot()) {
|
|
1963
|
-
const tpl = (source) => join3(root, "templates", source);
|
|
1964
|
-
const measured = (e) => e.file ?? tpl(e.source);
|
|
1965
|
-
const perSkill = {};
|
|
1966
|
-
let rules = 0;
|
|
1967
|
-
let skillDescriptors = 0;
|
|
1968
|
-
let agentDescriptors = 0;
|
|
1969
|
-
let ruleItems = 0;
|
|
1970
|
-
let skillItems = 0;
|
|
1971
|
-
let agentItems = 0;
|
|
1972
|
-
for (const e of entries) {
|
|
1973
|
-
if (e.target.startsWith(".claude/rules/")) {
|
|
1974
|
-
rules += fileTokens(measured(e));
|
|
1975
|
-
ruleItems += 1;
|
|
1976
|
-
} else if (e.target.startsWith(".claude/agents/")) {
|
|
1977
|
-
agentDescriptors += descriptorTokens(measured(e));
|
|
1978
|
-
agentItems += 1;
|
|
1979
|
-
} else if (e.target.startsWith(".claude/skills/")) {
|
|
1980
|
-
const t = descriptorTokens(join3(measured(e), "SKILL.md"));
|
|
1981
|
-
skillDescriptors += t;
|
|
1982
|
-
skillItems += 1;
|
|
1983
|
-
const id = /\.claude\/skills\/([^/]+)/.exec(e.target)?.[1];
|
|
1984
|
-
if (id !== void 0) perSkill[id] = (perSkill[id] ?? 0) + t;
|
|
1985
|
-
}
|
|
1986
|
-
}
|
|
1987
|
-
const harnessAnchor = fileTokens(join3(root, "templates", "CLAUDE.md"));
|
|
1988
|
-
const projectScaffold = estimateTokens(
|
|
1989
|
-
withContinuousSkillsNote(
|
|
1990
|
-
renderFillScaffold(),
|
|
1991
|
-
CONTINUOUS_SKILLS.map((s) => s.id)
|
|
1992
|
-
).trim().length
|
|
1993
|
-
);
|
|
1994
|
-
const projectClaudeMd = harnessAnchor + projectScaffold;
|
|
1995
|
-
const claudeMdItems = (harnessAnchor > 0 ? 1 : 0) + (projectScaffold > 0 ? 1 : 0);
|
|
1996
|
-
return makeResidentCost({
|
|
1997
|
-
rules,
|
|
1998
|
-
projectClaudeMd,
|
|
1999
|
-
skillDescriptors,
|
|
2000
|
-
agentDescriptors,
|
|
2001
|
-
perSkillDescriptor: perSkill,
|
|
2002
|
-
items: {
|
|
2003
|
-
rules: ruleItems,
|
|
2004
|
-
skills: skillItems,
|
|
2005
|
-
agents: agentItems,
|
|
2006
|
-
claudeMd: claudeMdItems,
|
|
2007
|
-
total: ruleItems + skillItems + agentItems + claudeMdItems
|
|
2008
|
-
}
|
|
2009
|
-
});
|
|
2010
|
-
}
|
|
2011
|
-
function summarizeContextCost(assetIds, root = resolveBundleRoot()) {
|
|
2012
|
-
let measuredTokens = 0;
|
|
2013
|
-
let measuredCount = 0;
|
|
2014
|
-
let unmeasuredCount = 0;
|
|
2015
|
-
for (const id of assetIds) {
|
|
2016
|
-
const tokens = assetDescriptorTokens(id, root);
|
|
2017
|
-
if (tokens === null) unmeasuredCount += 1;
|
|
2018
|
-
else {
|
|
2019
|
-
measuredTokens += tokens;
|
|
2020
|
-
measuredCount += 1;
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
return { measuredTokens, measuredCount, unmeasuredCount };
|
|
2024
|
-
}
|
|
2025
|
-
function formatResidentCostLine(r, unmeasuredCount) {
|
|
2026
|
-
if (r.total === 0) return null;
|
|
2027
|
-
const parts = [
|
|
2028
|
-
`directives ${r.directive.items} ~${r.directive.tokens}`,
|
|
2029
|
-
`[rules ${r.items.rules} ~${r.rules} \xB7 CLAUDE.md ${r.items.claudeMd} ~${r.projectClaudeMd}]`,
|
|
2030
|
-
`triggers ${r.firing.items} ~${r.firing.tokens}`,
|
|
2031
|
-
`[skills ${r.items.skills} ~${r.skillDescriptors} \xB7 agents ${r.items.agents} ~${r.agentDescriptors}]`
|
|
2032
|
-
].join(" \xB7 ");
|
|
2033
|
-
const external = unmeasuredCount > 0 ? ` \xB7 ${unmeasuredCount} external asset${unmeasuredCount === 1 ? "" : "s"} unmeasured` : "";
|
|
2034
|
-
return `session-start context cost: ${r.items.total} items resident \xB7 ~${r.total} tokens/session (${parts}${external})`;
|
|
2035
|
-
}
|
|
2036
|
-
var RESIDENT_ROWS = [
|
|
2037
|
-
{
|
|
2038
|
-
// ADR-083 — 축 소계를 **행으로** 낸다. 소계가 없으면 읽는 사람이 네 줄을 머리로 더해야
|
|
2039
|
-
// 하고, 그러면 "무엇을 줄여야 하나"가 표에서 안 보인다.
|
|
2040
|
-
label: "\uC9C0\uC2DC\uBB38",
|
|
2041
|
-
gap: 13,
|
|
2042
|
-
count: (r) => r.directive.items,
|
|
2043
|
-
tokens: (r) => r.directive.tokens
|
|
2044
|
-
},
|
|
2045
|
-
{ label: " rules", gap: 12, count: (r) => r.items.rules, tokens: (r) => r.rules },
|
|
2046
|
-
{
|
|
2047
|
-
// 한 행에 두 파일(앵커 + 스캐폴드). 개수 열이 2 를 뱉어 그 사실을 드러낸다 — 라벨로
|
|
2048
|
-
// 하나만 이름 붙이면 v26.140.0 이전과 같은 거짓 라벨이 된다.
|
|
2049
|
-
label: " CLAUDE.md",
|
|
2050
|
-
gap: 8,
|
|
2051
|
-
count: (r) => r.items.claudeMd,
|
|
2052
|
-
tokens: (r) => r.projectClaudeMd
|
|
2053
|
-
},
|
|
2054
|
-
{
|
|
2055
|
-
label: "\uBC1C\uD654 \uD45C\uBA74",
|
|
2056
|
-
gap: 10,
|
|
2057
|
-
count: (r) => r.firing.items,
|
|
2058
|
-
tokens: (r) => r.firing.tokens
|
|
2059
|
-
},
|
|
2060
|
-
{
|
|
2061
|
-
label: " skill descriptors",
|
|
2062
|
-
gap: 0,
|
|
2063
|
-
count: (r) => r.items.skills,
|
|
2064
|
-
tokens: (r) => r.skillDescriptors
|
|
2065
|
-
},
|
|
2066
|
-
{
|
|
2067
|
-
label: " agent descriptors",
|
|
2068
|
-
gap: 0,
|
|
2069
|
-
count: (r) => r.items.agents,
|
|
2070
|
-
tokens: (r) => r.agentDescriptors
|
|
2071
|
-
}
|
|
2072
|
-
];
|
|
2073
|
-
function formatResidentCostBlock(r) {
|
|
2074
|
-
const n = (v) => String(v).padStart(3);
|
|
2075
|
-
return [
|
|
2076
|
-
" \uAC1C\uC218 \uD1A0\uD070",
|
|
2077
|
-
...RESIDENT_ROWS.map(
|
|
2078
|
-
(row) => ` ${row.label}${" ".repeat(row.gap)}${n(row.count(r))}\uAC1C ~${row.tokens(r)}`
|
|
2079
|
-
),
|
|
2080
|
-
" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
|
|
2081
|
-
` \uC0C1\uC8FC \uD569\uACC4 ${n(r.items.total)}\uAC1C \uC0C1\uC8FC \xB7 ~${r.total} tokens/\uC138\uC158`,
|
|
2082
|
-
"",
|
|
2083
|
-
" \uC9C0\uC2DC\uBB38 = \uC6B0\uB9AC\uAC00 \uD56D\uC0C1 \uC77D\uD788\uAC8C \uB123\uC740 \uAC83. **\uC791\uC744\uC218\uB85D \uC88B\uC740 \uAC12\uC774 \uC544\uB2C8\uB2E4** \u2014 \uD310\uB2E8\uC740",
|
|
2084
|
-
" '\uBD88\uD544\uC694\uD55C \uC9C0\uC2DC\uBB38\uC774 \uC788\uB098'\uB85C \uD55C\uB2E4. ratchet \uC740 \uC870\uC6A9\uD55C \uC99D\uAC00\uB9CC \uB9C9\uB294\uB2E4.",
|
|
2085
|
-
" \uBC1C\uD654 \uD45C\uBA74 = \uC2A4\uD0AC\xB7\uC5D0\uC774\uC804\uD2B8\uAC00 \uBD88\uB9AC\uB824\uACE0 \uC0C1\uC8FC\uC2DC\uD0A4\uB294 descriptor. \uAE4E\uC73C\uBA74 \uC548 \uBD88\uB9B0\uB2E4."
|
|
2086
|
-
];
|
|
2087
|
-
}
|
|
2088
|
-
|
|
2089
2103
|
export {
|
|
2090
2104
|
__commonJS,
|
|
2091
2105
|
__toESM,
|
|
@@ -2099,6 +2113,7 @@ export {
|
|
|
2099
2113
|
isInstallScope,
|
|
2100
2114
|
resolveScope,
|
|
2101
2115
|
DEFAULT_OPTIONS,
|
|
2116
|
+
UPDATE_GROUPS,
|
|
2102
2117
|
EXTERNAL_ASSETS,
|
|
2103
2118
|
DEV_METHOD_SKILL_IDS,
|
|
2104
2119
|
INTERNAL_BUNDLED_SKILL_IDS,
|
|
@@ -2112,6 +2127,21 @@ export {
|
|
|
2112
2127
|
assetCliSupport,
|
|
2113
2128
|
assetReachesCli,
|
|
2114
2129
|
filterApplicableAssets,
|
|
2130
|
+
HARNESS_ANCHOR_FILE,
|
|
2131
|
+
HARNESS_IMPORT_LINE,
|
|
2132
|
+
withContinuousSkillsNote,
|
|
2133
|
+
renderFillScaffold,
|
|
2134
|
+
upsertHarnessImport,
|
|
2135
|
+
stripHarnessImport,
|
|
2136
|
+
estimateTokens,
|
|
2137
|
+
resolveBundleRoot,
|
|
2138
|
+
extractFrontmatter,
|
|
2139
|
+
assetCostRows,
|
|
2140
|
+
landsOnDisk,
|
|
2141
|
+
residentCost,
|
|
2142
|
+
summarizeContextCost,
|
|
2143
|
+
formatResidentCostLine,
|
|
2144
|
+
formatResidentCostBlock,
|
|
2115
2145
|
ensureDir,
|
|
2116
2146
|
copyFile,
|
|
2117
2147
|
copyDir,
|
|
@@ -2133,12 +2163,6 @@ export {
|
|
|
2133
2163
|
readInstallLog,
|
|
2134
2164
|
installLogPath,
|
|
2135
2165
|
legacyInstallLogPath,
|
|
2136
|
-
HARNESS_ANCHOR_FILE,
|
|
2137
|
-
HARNESS_IMPORT_LINE,
|
|
2138
|
-
withContinuousSkillsNote,
|
|
2139
|
-
renderFillScaffold,
|
|
2140
|
-
upsertHarnessImport,
|
|
2141
|
-
stripHarnessImport,
|
|
2142
2166
|
buildAssetSpec,
|
|
2143
2167
|
resolveRules,
|
|
2144
2168
|
ALL_RULES,
|
|
@@ -2148,13 +2172,6 @@ export {
|
|
|
2148
2172
|
isCliNeutralTarget,
|
|
2149
2173
|
buildManifest,
|
|
2150
2174
|
CATEGORIES,
|
|
2151
|
-
CATEGORY_TITLES
|
|
2152
|
-
estimateTokens,
|
|
2153
|
-
resolveBundleRoot,
|
|
2154
|
-
assetCostRows,
|
|
2155
|
-
residentCost,
|
|
2156
|
-
summarizeContextCost,
|
|
2157
|
-
formatResidentCostLine,
|
|
2158
|
-
formatResidentCostBlock
|
|
2175
|
+
CATEGORY_TITLES
|
|
2159
2176
|
};
|
|
2160
|
-
//# sourceMappingURL=chunk-
|
|
2177
|
+
//# sourceMappingURL=chunk-242TVGMZ.js.map
|