@uzysjung/agent-harness 26.95.0 → 26.97.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 +1 -3
- package/README.md +4 -6
- package/dist/{chunk-DSWWKCW3.js → chunk-WKXYOTUV.js} +1 -21
- package/dist/{chunk-DSWWKCW3.js.map → chunk-WKXYOTUV.js.map} +1 -1
- package/dist/index.js +219 -216
- package/dist/index.js.map +1 -1
- package/dist/trust-tier-drift.js +1 -3
- package/dist/trust-tier-drift.js.map +1 -1
- package/package.json +1 -1
- package/templates/antigravity/AGENTS.md.template +4 -0
- package/templates/codex/AGENTS.md.template +4 -0
- package/templates/opencode/AGENTS.md.template +4 -0
- package/templates/skills/compaction-handoff/SKILL.md +264 -130
- package/templates/project-claude/_base.md +0 -23
- package/templates/project-claude/fragments/csr-fastapi/active-rules.md +0 -13
- package/templates/project-claude/fragments/csr-fastapi/agents.md +0 -5
- package/templates/project-claude/fragments/csr-fastapi/boundaries.md +0 -17
- package/templates/project-claude/fragments/csr-fastapi/commands.md +0 -5
- package/templates/project-claude/fragments/csr-fastapi/plugins.md +0 -2
- package/templates/project-claude/fragments/csr-fastapi/skills.md +0 -5
- package/templates/project-claude/fragments/csr-fastapi/stack.md +0 -6
- package/templates/project-claude/fragments/csr-fastapi/tagline.md +0 -1
- package/templates/project-claude/fragments/csr-fastify/active-rules.md +0 -13
- package/templates/project-claude/fragments/csr-fastify/agents.md +0 -5
- package/templates/project-claude/fragments/csr-fastify/boundaries.md +0 -17
- package/templates/project-claude/fragments/csr-fastify/commands.md +0 -5
- package/templates/project-claude/fragments/csr-fastify/plugins.md +0 -2
- package/templates/project-claude/fragments/csr-fastify/skills.md +0 -5
- package/templates/project-claude/fragments/csr-fastify/stack.md +0 -6
- package/templates/project-claude/fragments/csr-fastify/tagline.md +0 -1
- package/templates/project-claude/fragments/csr-supabase/active-rules.md +0 -12
- package/templates/project-claude/fragments/csr-supabase/agents.md +0 -5
- package/templates/project-claude/fragments/csr-supabase/boundaries.md +0 -18
- package/templates/project-claude/fragments/csr-supabase/commands.md +0 -5
- package/templates/project-claude/fragments/csr-supabase/plugins.md +0 -4
- package/templates/project-claude/fragments/csr-supabase/skills.md +0 -7
- package/templates/project-claude/fragments/csr-supabase/stack.md +0 -6
- package/templates/project-claude/fragments/csr-supabase/supabase-auth.md +0 -19
- package/templates/project-claude/fragments/csr-supabase/tagline.md +0 -1
- package/templates/project-claude/fragments/data/active-rules.md +0 -10
- package/templates/project-claude/fragments/data/agents.md +0 -6
- package/templates/project-claude/fragments/data/boundaries.md +0 -19
- package/templates/project-claude/fragments/data/commands.md +0 -5
- package/templates/project-claude/fragments/data/plugins.md +0 -2
- package/templates/project-claude/fragments/data/skills.md +0 -3
- package/templates/project-claude/fragments/data/stack.md +0 -7
- package/templates/project-claude/fragments/data/tagline.md +0 -1
- package/templates/project-claude/fragments/executive/active-rules.md +0 -6
- package/templates/project-claude/fragments/executive/agents.md +0 -6
- package/templates/project-claude/fragments/executive/boundaries.md +0 -16
- package/templates/project-claude/fragments/executive/commands.md +0 -10
- package/templates/project-claude/fragments/executive/plugins.md +0 -1
- package/templates/project-claude/fragments/executive/skills.md +0 -7
- package/templates/project-claude/fragments/executive/stack.md +0 -4
- package/templates/project-claude/fragments/executive/tagline.md +0 -1
- package/templates/project-claude/fragments/executive/workflow.md +0 -9
- package/templates/project-claude/fragments/growth-marketing/active-rules.md +0 -7
- package/templates/project-claude/fragments/growth-marketing/agents.md +0 -6
- package/templates/project-claude/fragments/growth-marketing/boundaries.md +0 -16
- package/templates/project-claude/fragments/growth-marketing/commands.md +0 -10
- package/templates/project-claude/fragments/growth-marketing/plugins.md +0 -9
- package/templates/project-claude/fragments/growth-marketing/skills.md +0 -8
- package/templates/project-claude/fragments/growth-marketing/stack.md +0 -7
- package/templates/project-claude/fragments/growth-marketing/tagline.md +0 -1
- package/templates/project-claude/fragments/growth-marketing/workflow.md +0 -10
- package/templates/project-claude/fragments/project-management/active-rules.md +0 -7
- package/templates/project-claude/fragments/project-management/agents.md +0 -6
- package/templates/project-claude/fragments/project-management/boundaries.md +0 -15
- package/templates/project-claude/fragments/project-management/commands.md +0 -9
- package/templates/project-claude/fragments/project-management/plugins.md +0 -6
- package/templates/project-claude/fragments/project-management/skills.md +0 -5
- package/templates/project-claude/fragments/project-management/stack.md +0 -4
- package/templates/project-claude/fragments/project-management/tagline.md +0 -1
- package/templates/project-claude/fragments/project-management/workflow.md +0 -11
- package/templates/project-claude/fragments/ssr-htmx/active-rules.md +0 -11
- package/templates/project-claude/fragments/ssr-htmx/agents.md +0 -5
- package/templates/project-claude/fragments/ssr-htmx/boundaries.md +0 -19
- package/templates/project-claude/fragments/ssr-htmx/commands.md +0 -5
- package/templates/project-claude/fragments/ssr-htmx/plugins.md +0 -2
- package/templates/project-claude/fragments/ssr-htmx/skills.md +0 -3
- package/templates/project-claude/fragments/ssr-htmx/stack.md +0 -6
- package/templates/project-claude/fragments/ssr-htmx/tagline.md +0 -1
- package/templates/project-claude/fragments/ssr-nextjs/active-rules.md +0 -12
- package/templates/project-claude/fragments/ssr-nextjs/agents.md +0 -5
- package/templates/project-claude/fragments/ssr-nextjs/boundaries.md +0 -19
- package/templates/project-claude/fragments/ssr-nextjs/commands.md +0 -5
- package/templates/project-claude/fragments/ssr-nextjs/plugins.md +0 -2
- package/templates/project-claude/fragments/ssr-nextjs/skills.md +0 -5
- package/templates/project-claude/fragments/ssr-nextjs/stack.md +0 -5
- package/templates/project-claude/fragments/ssr-nextjs/tagline.md +0 -1
- package/templates/project-claude/fragments/tooling/active-rules.md +0 -11
- package/templates/project-claude/fragments/tooling/agents.md +0 -5
- package/templates/project-claude/fragments/tooling/boundaries.md +0 -16
- package/templates/project-claude/fragments/tooling/commands.md +0 -3
- package/templates/project-claude/fragments/tooling/skills.md +0 -4
- package/templates/project-claude/fragments/tooling/stack.md +0 -5
- package/templates/project-claude/fragments/tooling/tagline.md +0 -1
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
hasUiTrack,
|
|
15
15
|
init_esm_shims,
|
|
16
16
|
isAssetSelected
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-WKXYOTUV.js";
|
|
18
18
|
|
|
19
19
|
// node_modules/sisteransi/src/index.js
|
|
20
20
|
var require_src = __commonJS({
|
|
@@ -699,7 +699,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
699
699
|
// package.json
|
|
700
700
|
var package_default = {
|
|
701
701
|
name: "@uzysjung/agent-harness",
|
|
702
|
-
version: "26.
|
|
702
|
+
version: "26.97.0",
|
|
703
703
|
description: "Curate vetted AI-coding skills & plugins by your tech stack \u2014 install only what you need, across Claude Code, Codex, OpenCode & Antigravity",
|
|
704
704
|
type: "module",
|
|
705
705
|
publishConfig: {
|
|
@@ -928,13 +928,13 @@ function padDisplay(s, width) {
|
|
|
928
928
|
init_esm_shims();
|
|
929
929
|
import {
|
|
930
930
|
chmodSync as chmodSync2,
|
|
931
|
-
existsSync as
|
|
931
|
+
existsSync as existsSync11,
|
|
932
932
|
mkdirSync as mkdirSync4,
|
|
933
933
|
readdirSync as readdirSync3,
|
|
934
|
-
readFileSync as
|
|
934
|
+
readFileSync as readFileSync11,
|
|
935
935
|
writeFileSync as writeFileSync9
|
|
936
936
|
} from "fs";
|
|
937
|
-
import { dirname as dirname4, join as
|
|
937
|
+
import { basename as basename4, dirname as dirname4, join as join10, resolve } from "path";
|
|
938
938
|
|
|
939
939
|
// src/antigravity/transform.ts
|
|
940
940
|
init_esm_shims();
|
|
@@ -948,7 +948,7 @@ function renameSlashes(text) {
|
|
|
948
948
|
}
|
|
949
949
|
function renderAgentsMd(params) {
|
|
950
950
|
const body = params.claudeMd.replace(/^#\s+.*\r?\n/, "").trim();
|
|
951
|
-
const replaced = params.template.replaceAll("{PROJECT_NAME}", params.projectName).replaceAll("{PROJECT_RULES}", body);
|
|
951
|
+
const replaced = params.template.replaceAll("{PROJECT_NAME}", params.projectName).replaceAll("{PROJECT_RULES}", body).replaceAll("{PROJECT_CONTEXT}", params.projectContext);
|
|
952
952
|
return renameSlashes(replaced);
|
|
953
953
|
}
|
|
954
954
|
|
|
@@ -1048,6 +1048,98 @@ function ensureProjectSkeleton(projectDir) {
|
|
|
1048
1048
|
}
|
|
1049
1049
|
}
|
|
1050
1050
|
|
|
1051
|
+
// src/project-claude-merge.ts
|
|
1052
|
+
init_esm_shims();
|
|
1053
|
+
var TRACK_DISPLAY_NAMES = {
|
|
1054
|
+
tooling: "Tooling",
|
|
1055
|
+
"csr-supabase": "CSR Supabase",
|
|
1056
|
+
"csr-fastify": "CSR Fastify",
|
|
1057
|
+
"csr-fastapi": "CSR FastAPI",
|
|
1058
|
+
"ssr-htmx": "SSR HTMX",
|
|
1059
|
+
"ssr-nextjs": "SSR Next.js",
|
|
1060
|
+
data: "Data",
|
|
1061
|
+
executive: "Executive",
|
|
1062
|
+
full: "Full",
|
|
1063
|
+
"project-management": "Project Management",
|
|
1064
|
+
"growth-marketing": "Growth Marketing"
|
|
1065
|
+
};
|
|
1066
|
+
var FULL_EXPANSION = TRACKS.filter((t) => t !== "full");
|
|
1067
|
+
var FILL_SECTIONS = [
|
|
1068
|
+
"identity",
|
|
1069
|
+
"stack",
|
|
1070
|
+
"architecture",
|
|
1071
|
+
"installed-assets",
|
|
1072
|
+
"boundaries",
|
|
1073
|
+
"verify"
|
|
1074
|
+
];
|
|
1075
|
+
var FILL_SPECS = {
|
|
1076
|
+
identity: {
|
|
1077
|
+
title: "Identity & Purpose",
|
|
1078
|
+
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.`,
|
|
1079
|
+
placeholder: "what this project is, who it is for, and why it exists"
|
|
1080
|
+
},
|
|
1081
|
+
stack: {
|
|
1082
|
+
title: "Stack & Commands",
|
|
1083
|
+
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.",
|
|
1084
|
+
placeholder: "languages, runtimes, package manager, and the install/build/test/run commands"
|
|
1085
|
+
},
|
|
1086
|
+
architecture: {
|
|
1087
|
+
title: "Architecture & Layout",
|
|
1088
|
+
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.",
|
|
1089
|
+
placeholder: "where things live, the entry points, and how data flows between layers"
|
|
1090
|
+
},
|
|
1091
|
+
"installed-assets": {
|
|
1092
|
+
title: "Installed Harness Assets",
|
|
1093
|
+
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 universal Rule 1-12 (they live in the rules layer) \u2014 cross-reference them instead. Delete this comment when done.",
|
|
1094
|
+
placeholder: "the installed rules/skills/agents/commands and when to use each"
|
|
1095
|
+
},
|
|
1096
|
+
boundaries: {
|
|
1097
|
+
title: "Boundaries \u2014 Always / Ask First / Never",
|
|
1098
|
+
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.`,
|
|
1099
|
+
placeholder: "what to always do, ask before doing, and never do in this repo"
|
|
1100
|
+
},
|
|
1101
|
+
verify: {
|
|
1102
|
+
title: "Verification Gate",
|
|
1103
|
+
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.',
|
|
1104
|
+
placeholder: "the single command that proves a change is safe, and the done bar"
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
var SCAFFOLD_BANNER = [
|
|
1108
|
+
"> \u2699\uFE0F **SCAFFOLD \u2014 not filled in yet.** The sections below are a fill-in template for THIS project, not verified facts.",
|
|
1109
|
+
"> 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.",
|
|
1110
|
+
"> The universal harness rules (Rule 1\u201312) live in the separate rules layer. This file is **project-specific context only**."
|
|
1111
|
+
].join("\n");
|
|
1112
|
+
function renderFillScaffold() {
|
|
1113
|
+
const blocks = FILL_SECTIONS.map((id) => {
|
|
1114
|
+
const spec = FILL_SPECS[id];
|
|
1115
|
+
return `## ${spec.title}
|
|
1116
|
+
|
|
1117
|
+
<!-- FILL:${id} \u2014 ${spec.prompt} -->
|
|
1118
|
+
|
|
1119
|
+
_(not filled yet \u2014 ${spec.placeholder})_`;
|
|
1120
|
+
});
|
|
1121
|
+
return `${SCAFFOLD_BANNER}
|
|
1122
|
+
|
|
1123
|
+
${blocks.join("\n\n")}`;
|
|
1124
|
+
}
|
|
1125
|
+
function mergeProjectClaude(tracks, opts) {
|
|
1126
|
+
const expanded = expandTracks(tracks);
|
|
1127
|
+
const trackList = expanded.map((t) => TRACK_DISPLAY_NAMES[t]).join(", ");
|
|
1128
|
+
const header = `# ${opts.projectName}
|
|
1129
|
+
|
|
1130
|
+
> Active track(s): ${trackList}`;
|
|
1131
|
+
return `${header}
|
|
1132
|
+
|
|
1133
|
+
${renderFillScaffold()}
|
|
1134
|
+
`;
|
|
1135
|
+
}
|
|
1136
|
+
function expandTracks(tracks) {
|
|
1137
|
+
if (tracks.includes("full")) {
|
|
1138
|
+
return FULL_EXPANSION;
|
|
1139
|
+
}
|
|
1140
|
+
return tracks;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1051
1143
|
// src/antigravity/transform.ts
|
|
1052
1144
|
function runAntigravityTransform(params) {
|
|
1053
1145
|
const { harnessRoot, projectDir, selectedInternalSkills = [] } = params;
|
|
@@ -1077,7 +1169,14 @@ function writeRules(harnessRoot, projectDir) {
|
|
|
1077
1169
|
const rulesDir = join2(projectDir, ".agents", "rules");
|
|
1078
1170
|
ensureDir(rulesDir);
|
|
1079
1171
|
const target = join2(rulesDir, "uzys-harness.md");
|
|
1080
|
-
|
|
1172
|
+
const rulesOut = renderAgentsMd({
|
|
1173
|
+
template,
|
|
1174
|
+
claudeMd,
|
|
1175
|
+
projectName: basename(projectDir),
|
|
1176
|
+
projectContext: renderFillScaffold()
|
|
1177
|
+
});
|
|
1178
|
+
backupFileIfChanged(target, rulesOut);
|
|
1179
|
+
writeFileSync(target, rulesOut);
|
|
1081
1180
|
return target;
|
|
1082
1181
|
}
|
|
1083
1182
|
|
|
@@ -1210,7 +1309,14 @@ function runCodexTransform(params) {
|
|
|
1210
1309
|
const mcp = readOptionalJson(join4(harnessRoot, ".mcp.json"));
|
|
1211
1310
|
const agentsMdPath = join4(projectDir, "AGENTS.md");
|
|
1212
1311
|
ensureDir(projectDir);
|
|
1213
|
-
|
|
1312
|
+
const agentsMdOut = renderAgentsMd({
|
|
1313
|
+
template: agentsTemplate,
|
|
1314
|
+
claudeMd,
|
|
1315
|
+
projectName,
|
|
1316
|
+
projectContext: renderFillScaffold()
|
|
1317
|
+
});
|
|
1318
|
+
backupFileIfChanged(agentsMdPath, agentsMdOut);
|
|
1319
|
+
writeFileSync3(agentsMdPath, agentsMdOut);
|
|
1214
1320
|
const configTomlPath = join4(projectDir, ".codex/config.toml");
|
|
1215
1321
|
ensureDir(join4(projectDir, ".codex"));
|
|
1216
1322
|
writeFileSync3(
|
|
@@ -1999,7 +2105,7 @@ function renameSlashes2(text) {
|
|
|
1999
2105
|
}
|
|
2000
2106
|
function renderAgentsMd2(params) {
|
|
2001
2107
|
const body = params.claudeMd.replace(/^#\s+.*\r?\n/, "").trim();
|
|
2002
|
-
const replaced = params.template.replaceAll("{PROJECT_NAME}", params.projectName).replaceAll("{PROJECT_RULES}", body);
|
|
2108
|
+
const replaced = params.template.replaceAll("{PROJECT_NAME}", params.projectName).replaceAll("{PROJECT_RULES}", body).replaceAll("{PROJECT_CONTEXT}", params.projectContext);
|
|
2003
2109
|
return renameSlashes2(replaced);
|
|
2004
2110
|
}
|
|
2005
2111
|
|
|
@@ -2093,7 +2199,14 @@ function runOpencodeTransform(params) {
|
|
|
2093
2199
|
const mcp = readOptionalJson2(join8(harnessRoot, ".mcp.json"));
|
|
2094
2200
|
ensureDir(projectDir);
|
|
2095
2201
|
const agentsMdPath = join8(projectDir, "AGENTS.md");
|
|
2096
|
-
|
|
2202
|
+
const agentsMdOut = renderAgentsMd2({
|
|
2203
|
+
template: agentsTemplate,
|
|
2204
|
+
claudeMd,
|
|
2205
|
+
projectName,
|
|
2206
|
+
projectContext: renderFillScaffold()
|
|
2207
|
+
});
|
|
2208
|
+
backupFileIfChanged(agentsMdPath, agentsMdOut);
|
|
2209
|
+
writeFileSync7(agentsMdPath, agentsMdOut);
|
|
2097
2210
|
const opencodeJsonPath = join8(projectDir, "opencode.json");
|
|
2098
2211
|
writeFileSync7(opencodeJsonPath, renderOpencodeJson({ template: opencodeTemplate, mcp }));
|
|
2099
2212
|
const cmdDir = join8(projectDir, ".opencode/commands");
|
|
@@ -2127,134 +2240,6 @@ function readOptionalJson2(path) {
|
|
|
2127
2240
|
}
|
|
2128
2241
|
}
|
|
2129
2242
|
|
|
2130
|
-
// src/project-claude-merge.ts
|
|
2131
|
-
init_esm_shims();
|
|
2132
|
-
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
2133
|
-
import { join as join9 } from "path";
|
|
2134
|
-
var SECTIONS = [
|
|
2135
|
-
"stack",
|
|
2136
|
-
"workflow",
|
|
2137
|
-
"active-rules",
|
|
2138
|
-
"agents",
|
|
2139
|
-
"skills",
|
|
2140
|
-
"plugins",
|
|
2141
|
-
"commands",
|
|
2142
|
-
"boundaries",
|
|
2143
|
-
"supabase-auth"
|
|
2144
|
-
];
|
|
2145
|
-
var SECTION_TITLES = {
|
|
2146
|
-
stack: "Stack",
|
|
2147
|
-
workflow: "Workflow",
|
|
2148
|
-
"active-rules": "Active Rules",
|
|
2149
|
-
agents: "Agents",
|
|
2150
|
-
skills: "Skills",
|
|
2151
|
-
plugins: "Plugins",
|
|
2152
|
-
commands: "Commands",
|
|
2153
|
-
boundaries: "Boundaries",
|
|
2154
|
-
"supabase-auth": "Supabase auth configuration"
|
|
2155
|
-
};
|
|
2156
|
-
var TRACK_DISPLAY_NAMES = {
|
|
2157
|
-
tooling: "Tooling",
|
|
2158
|
-
"csr-supabase": "CSR Supabase",
|
|
2159
|
-
"csr-fastify": "CSR Fastify",
|
|
2160
|
-
"csr-fastapi": "CSR FastAPI",
|
|
2161
|
-
"ssr-htmx": "SSR HTMX",
|
|
2162
|
-
"ssr-nextjs": "SSR Next.js",
|
|
2163
|
-
data: "Data",
|
|
2164
|
-
executive: "Executive",
|
|
2165
|
-
full: "Full",
|
|
2166
|
-
"project-management": "Project Management",
|
|
2167
|
-
"growth-marketing": "Growth Marketing"
|
|
2168
|
-
};
|
|
2169
|
-
var FULL_EXPANSION = [
|
|
2170
|
-
"tooling",
|
|
2171
|
-
"csr-fastapi",
|
|
2172
|
-
"csr-fastify",
|
|
2173
|
-
"csr-supabase",
|
|
2174
|
-
"ssr-htmx",
|
|
2175
|
-
"ssr-nextjs",
|
|
2176
|
-
"data",
|
|
2177
|
-
"executive",
|
|
2178
|
-
"project-management",
|
|
2179
|
-
"growth-marketing"
|
|
2180
|
-
];
|
|
2181
|
-
function mergeProjectClaude(tracks, opts) {
|
|
2182
|
-
const expanded = expandTracks(tracks);
|
|
2183
|
-
const baseRaw = readFileSync10(join9(opts.baseDir, "_base.md"), "utf8");
|
|
2184
|
-
let output = baseRaw;
|
|
2185
|
-
output = output.replace("<!-- INSERT: track-list -->", trackList(expanded));
|
|
2186
|
-
output = output.replace("<!-- INSERT: tagline -->", taglineList(expanded, opts.baseDir));
|
|
2187
|
-
for (const section of SECTIONS) {
|
|
2188
|
-
const marker = `<!-- INSERT: ${section} -->`;
|
|
2189
|
-
const block = renderSection(section, expanded, opts.baseDir);
|
|
2190
|
-
if (block === null) {
|
|
2191
|
-
output = stripMarkerLine(output, marker);
|
|
2192
|
-
} else {
|
|
2193
|
-
output = output.replace(marker, block);
|
|
2194
|
-
}
|
|
2195
|
-
}
|
|
2196
|
-
return `${output.replace(/\n{3,}/g, "\n\n").trimEnd()}
|
|
2197
|
-
`;
|
|
2198
|
-
}
|
|
2199
|
-
function expandTracks(tracks) {
|
|
2200
|
-
if (tracks.includes("full")) {
|
|
2201
|
-
return FULL_EXPANSION;
|
|
2202
|
-
}
|
|
2203
|
-
return tracks;
|
|
2204
|
-
}
|
|
2205
|
-
function trackList(tracks) {
|
|
2206
|
-
return tracks.map((t) => TRACK_DISPLAY_NAMES[t]).join(", ");
|
|
2207
|
-
}
|
|
2208
|
-
function taglineList(tracks, baseDir) {
|
|
2209
|
-
const taglines = [];
|
|
2210
|
-
for (const t of tracks) {
|
|
2211
|
-
const path = join9(baseDir, "fragments", t, "tagline.md");
|
|
2212
|
-
if (!existsSync10(path)) {
|
|
2213
|
-
continue;
|
|
2214
|
-
}
|
|
2215
|
-
const body = readFileSync10(path, "utf8").trim();
|
|
2216
|
-
if (body) {
|
|
2217
|
-
taglines.push(body);
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
return taglines.join(" / ");
|
|
2221
|
-
}
|
|
2222
|
-
function renderSection(section, tracks, baseDir) {
|
|
2223
|
-
const present = [];
|
|
2224
|
-
for (const t of tracks) {
|
|
2225
|
-
const path = join9(baseDir, "fragments", t, `${section}.md`);
|
|
2226
|
-
if (!existsSync10(path)) {
|
|
2227
|
-
continue;
|
|
2228
|
-
}
|
|
2229
|
-
const body = readFileSync10(path, "utf8").trim();
|
|
2230
|
-
if (body) {
|
|
2231
|
-
present.push({ track: t, body });
|
|
2232
|
-
}
|
|
2233
|
-
}
|
|
2234
|
-
if (present.length === 0) {
|
|
2235
|
-
return null;
|
|
2236
|
-
}
|
|
2237
|
-
const title = `## ${SECTION_TITLES[section]}`;
|
|
2238
|
-
if (present.length === 1 && present[0]) {
|
|
2239
|
-
return `${title}
|
|
2240
|
-
|
|
2241
|
-
${present[0].body}
|
|
2242
|
-
`;
|
|
2243
|
-
}
|
|
2244
|
-
const blocks = present.map(({ track, body }) => `### ${TRACK_DISPLAY_NAMES[track]}
|
|
2245
|
-
|
|
2246
|
-
${body}`);
|
|
2247
|
-
return `${title}
|
|
2248
|
-
|
|
2249
|
-
${blocks.join("\n\n")}
|
|
2250
|
-
`;
|
|
2251
|
-
}
|
|
2252
|
-
function stripMarkerLine(text, marker) {
|
|
2253
|
-
const escaped = marker.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2254
|
-
const pattern = new RegExp(`\\n*${escaped}\\n*`);
|
|
2255
|
-
return text.replace(pattern, "\n");
|
|
2256
|
-
}
|
|
2257
|
-
|
|
2258
2243
|
// src/settings-merge.ts
|
|
2259
2244
|
init_esm_shims();
|
|
2260
2245
|
function addPreToolUseHook(settings, matcher, command) {
|
|
@@ -2283,15 +2268,15 @@ function addPreToolUseHook(settings, matcher, command) {
|
|
|
2283
2268
|
init_esm_shims();
|
|
2284
2269
|
import {
|
|
2285
2270
|
copyFileSync as copyFileSync2,
|
|
2286
|
-
existsSync as
|
|
2271
|
+
existsSync as existsSync10,
|
|
2287
2272
|
readdirSync as readdirSync2,
|
|
2288
|
-
readFileSync as
|
|
2273
|
+
readFileSync as readFileSync10,
|
|
2289
2274
|
unlinkSync,
|
|
2290
2275
|
writeFileSync as writeFileSync8
|
|
2291
2276
|
} from "fs";
|
|
2292
|
-
import { join as
|
|
2277
|
+
import { join as join9 } from "path";
|
|
2293
2278
|
function runUpdateMode(projectDir, templatesDir) {
|
|
2294
|
-
const claudeDir =
|
|
2279
|
+
const claudeDir = join9(projectDir, ".claude");
|
|
2295
2280
|
const report = {
|
|
2296
2281
|
updated: {},
|
|
2297
2282
|
pruned: {},
|
|
@@ -2300,26 +2285,26 @@ function runUpdateMode(projectDir, templatesDir) {
|
|
|
2300
2285
|
};
|
|
2301
2286
|
const targets = [
|
|
2302
2287
|
{
|
|
2303
|
-
target:
|
|
2304
|
-
source:
|
|
2288
|
+
target: join9(claudeDir, "rules"),
|
|
2289
|
+
source: join9(templatesDir, "rules"),
|
|
2305
2290
|
pattern: ".md",
|
|
2306
2291
|
label: ".claude/rules"
|
|
2307
2292
|
},
|
|
2308
2293
|
{
|
|
2309
|
-
target:
|
|
2310
|
-
source:
|
|
2294
|
+
target: join9(claudeDir, "agents"),
|
|
2295
|
+
source: join9(templatesDir, "agents"),
|
|
2311
2296
|
pattern: ".md",
|
|
2312
2297
|
label: ".claude/agents"
|
|
2313
2298
|
},
|
|
2314
2299
|
{
|
|
2315
|
-
target:
|
|
2316
|
-
source:
|
|
2300
|
+
target: join9(claudeDir, "commands/uzys"),
|
|
2301
|
+
source: join9(templatesDir, "commands/uzys"),
|
|
2317
2302
|
pattern: ".md",
|
|
2318
2303
|
label: ".claude/commands/uzys"
|
|
2319
2304
|
},
|
|
2320
2305
|
{
|
|
2321
|
-
target:
|
|
2322
|
-
source:
|
|
2306
|
+
target: join9(claudeDir, "hooks"),
|
|
2307
|
+
source: join9(templatesDir, "hooks"),
|
|
2323
2308
|
pattern: ".sh",
|
|
2324
2309
|
label: ".claude/hooks"
|
|
2325
2310
|
}
|
|
@@ -2328,26 +2313,26 @@ function runUpdateMode(projectDir, templatesDir) {
|
|
|
2328
2313
|
report.updated[t.label] = updateDir(t.target, t.source, t.pattern);
|
|
2329
2314
|
report.pruned[t.label] = pruneOrphans(t.target, t.source, t.pattern);
|
|
2330
2315
|
}
|
|
2331
|
-
const claudeMd =
|
|
2332
|
-
const templateMd =
|
|
2333
|
-
if (
|
|
2316
|
+
const claudeMd = join9(claudeDir, "CLAUDE.md");
|
|
2317
|
+
const templateMd = join9(templatesDir, "CLAUDE.md");
|
|
2318
|
+
if (existsSync10(claudeMd) && existsSync10(templateMd)) {
|
|
2334
2319
|
copyFileSync2(templateMd, claudeMd);
|
|
2335
2320
|
report.claudeMdUpdated = true;
|
|
2336
2321
|
}
|
|
2337
|
-
const settingsPath =
|
|
2338
|
-
if (
|
|
2339
|
-
report.staleHookRefs = cleanStaleHookRefs(settingsPath,
|
|
2322
|
+
const settingsPath = join9(claudeDir, "settings.json");
|
|
2323
|
+
if (existsSync10(settingsPath)) {
|
|
2324
|
+
report.staleHookRefs = cleanStaleHookRefs(settingsPath, join9(claudeDir, "hooks"));
|
|
2340
2325
|
}
|
|
2341
2326
|
return report;
|
|
2342
2327
|
}
|
|
2343
2328
|
function updateDir(target, source, ext) {
|
|
2344
|
-
if (!
|
|
2329
|
+
if (!existsSync10(target) || !existsSync10(source)) return 0;
|
|
2345
2330
|
let count = 0;
|
|
2346
2331
|
for (const file of readdirSync2(target)) {
|
|
2347
2332
|
if (!file.endsWith(ext)) continue;
|
|
2348
|
-
const targetFile =
|
|
2349
|
-
const sourceFile =
|
|
2350
|
-
if (
|
|
2333
|
+
const targetFile = join9(target, file);
|
|
2334
|
+
const sourceFile = join9(source, file);
|
|
2335
|
+
if (existsSync10(sourceFile)) {
|
|
2351
2336
|
copyFileSync2(sourceFile, targetFile);
|
|
2352
2337
|
count++;
|
|
2353
2338
|
}
|
|
@@ -2355,13 +2340,13 @@ function updateDir(target, source, ext) {
|
|
|
2355
2340
|
return count;
|
|
2356
2341
|
}
|
|
2357
2342
|
function pruneOrphans(target, source, ext) {
|
|
2358
|
-
if (!
|
|
2343
|
+
if (!existsSync10(target) || !existsSync10(source)) return [];
|
|
2359
2344
|
const removed = [];
|
|
2360
2345
|
for (const file of readdirSync2(target)) {
|
|
2361
2346
|
if (!file.endsWith(ext)) continue;
|
|
2362
|
-
const sourceFile =
|
|
2363
|
-
if (!
|
|
2364
|
-
const targetFile =
|
|
2347
|
+
const sourceFile = join9(source, file);
|
|
2348
|
+
if (!existsSync10(sourceFile)) {
|
|
2349
|
+
const targetFile = join9(target, file);
|
|
2365
2350
|
try {
|
|
2366
2351
|
unlinkSync(targetFile);
|
|
2367
2352
|
removed.push(file);
|
|
@@ -2374,7 +2359,7 @@ function pruneOrphans(target, source, ext) {
|
|
|
2374
2359
|
function cleanStaleHookRefs(settingsPath, hooksDir) {
|
|
2375
2360
|
let settings;
|
|
2376
2361
|
try {
|
|
2377
|
-
settings = JSON.parse(
|
|
2362
|
+
settings = JSON.parse(readFileSync10(settingsPath, "utf8"));
|
|
2378
2363
|
} catch {
|
|
2379
2364
|
return [];
|
|
2380
2365
|
}
|
|
@@ -2402,7 +2387,7 @@ function keepHookRef(hook, hooksDir, removed) {
|
|
|
2402
2387
|
const refMatch = (hook?.command ?? "").match(/\/\.claude\/hooks\/([^"\s/]+\.sh)/);
|
|
2403
2388
|
if (!refMatch?.[1]) return true;
|
|
2404
2389
|
const fname = refMatch[1];
|
|
2405
|
-
const exists =
|
|
2390
|
+
const exists = existsSync10(join9(hooksDir, fname));
|
|
2406
2391
|
if (!exists && !removed.includes(fname)) removed.push(fname);
|
|
2407
2392
|
return exists;
|
|
2408
2393
|
}
|
|
@@ -2413,12 +2398,12 @@ var KARPATHY_ASSET_ID = "karpathy-coder";
|
|
|
2413
2398
|
function runInstall(ctx) {
|
|
2414
2399
|
const { harnessRoot, projectDir, spec } = ctx;
|
|
2415
2400
|
const mode = ctx.mode ?? "fresh";
|
|
2416
|
-
const templatesDir =
|
|
2417
|
-
if (!
|
|
2401
|
+
const templatesDir = join10(harnessRoot, "templates");
|
|
2402
|
+
if (!existsSync11(templatesDir)) {
|
|
2418
2403
|
throw new Error(`Templates dir not found: ${templatesDir}`);
|
|
2419
2404
|
}
|
|
2420
|
-
const claudeDir =
|
|
2421
|
-
if (mode === "update" && !
|
|
2405
|
+
const claudeDir = join10(projectDir, ".claude");
|
|
2406
|
+
if (mode === "update" && !existsSync11(claudeDir)) {
|
|
2422
2407
|
throw new Error(`Update mode requires existing .claude/ at ${claudeDir}`);
|
|
2423
2408
|
}
|
|
2424
2409
|
const backupPath = resolveBackupPath(ctx, mode, claudeDir);
|
|
@@ -2426,7 +2411,7 @@ function runInstall(ctx) {
|
|
|
2426
2411
|
return runUpdateInstall(ctx, templatesDir, backupPath);
|
|
2427
2412
|
}
|
|
2428
2413
|
const manifestSpec = buildManifestSpec(spec);
|
|
2429
|
-
const base = spec.cli.includes("claude") ? installClaudeBaseline(manifestSpec,
|
|
2414
|
+
const base = spec.cli.includes("claude") ? installClaudeBaseline(manifestSpec, projectDir, templatesDir) : emptyClaudeBaseline();
|
|
2430
2415
|
const mcpResult = composeAndWriteMcp(harnessRoot, projectDir, spec);
|
|
2431
2416
|
const baseline = {
|
|
2432
2417
|
filesCopied: base.filesCopied,
|
|
@@ -2512,7 +2497,7 @@ function emptyClaudeBaseline() {
|
|
|
2512
2497
|
backups: []
|
|
2513
2498
|
};
|
|
2514
2499
|
}
|
|
2515
|
-
function installClaudeBaseline(manifestSpec,
|
|
2500
|
+
function installClaudeBaseline(manifestSpec, projectDir, templatesDir) {
|
|
2516
2501
|
ensureProjectSkeleton(projectDir);
|
|
2517
2502
|
const result = emptyClaudeBaseline();
|
|
2518
2503
|
const manifest = buildManifest(manifestSpec);
|
|
@@ -2520,15 +2505,15 @@ function installClaudeBaseline(manifestSpec, harnessRoot, projectDir, templatesD
|
|
|
2520
2505
|
if (!entry.applies(manifestSpec)) {
|
|
2521
2506
|
continue;
|
|
2522
2507
|
}
|
|
2523
|
-
const source =
|
|
2524
|
-
const target =
|
|
2525
|
-
if (!
|
|
2508
|
+
const source = join10(templatesDir, entry.source);
|
|
2509
|
+
const target = join10(projectDir, entry.target);
|
|
2510
|
+
if (!existsSync11(source)) {
|
|
2526
2511
|
result.skipped += 1;
|
|
2527
2512
|
continue;
|
|
2528
2513
|
}
|
|
2529
2514
|
if (entry.type === "file") {
|
|
2530
2515
|
if (entry.target === ".claude/settings.json") {
|
|
2531
|
-
const backup = backupFileIfChanged(target,
|
|
2516
|
+
const backup = backupFileIfChanged(target, readFileSync11(source, "utf-8"));
|
|
2532
2517
|
if (backup) {
|
|
2533
2518
|
result.backups.push(backup);
|
|
2534
2519
|
}
|
|
@@ -2541,12 +2526,12 @@ function installClaudeBaseline(manifestSpec, harnessRoot, projectDir, templatesD
|
|
|
2541
2526
|
}
|
|
2542
2527
|
accumulateCategory(result.categories, entry);
|
|
2543
2528
|
}
|
|
2544
|
-
const hookDir =
|
|
2545
|
-
if (
|
|
2529
|
+
const hookDir = join10(projectDir, ".claude/hooks");
|
|
2530
|
+
if (existsSync11(hookDir)) {
|
|
2546
2531
|
chmodHooksSync(hookDir);
|
|
2547
2532
|
}
|
|
2548
2533
|
writeInstalledTracks(projectDir, manifestSpec.tracks);
|
|
2549
|
-
const rootClaudeMd = writeRootClaudeMd(
|
|
2534
|
+
const rootClaudeMd = writeRootClaudeMd(projectDir, manifestSpec.tracks);
|
|
2550
2535
|
result.rootClaudeMd = { tracks: manifestSpec.tracks };
|
|
2551
2536
|
result.rootClaudeMdLog = { path: "CLAUDE.md", sha256: hashContent(rootClaudeMd.content) };
|
|
2552
2537
|
if (rootClaudeMd.backup) {
|
|
@@ -2649,10 +2634,10 @@ function wireKarpathyHook(spec, external, harnessRoot, projectDir) {
|
|
|
2649
2634
|
if (!karpathyResult?.ok) {
|
|
2650
2635
|
return { wired: false, reason: "plugin-install-failed" };
|
|
2651
2636
|
}
|
|
2652
|
-
const sourceHook =
|
|
2653
|
-
const targetHook =
|
|
2637
|
+
const sourceHook = join10(harnessRoot, "templates/hooks/karpathy-gate.sh");
|
|
2638
|
+
const targetHook = join10(projectDir, ".claude/hooks/karpathy-gate.sh");
|
|
2654
2639
|
let hookScriptCopied = false;
|
|
2655
|
-
if (
|
|
2640
|
+
if (existsSync11(sourceHook)) {
|
|
2656
2641
|
copyFile(sourceHook, targetHook);
|
|
2657
2642
|
try {
|
|
2658
2643
|
chmodSync2(targetHook, 493);
|
|
@@ -2660,10 +2645,10 @@ function wireKarpathyHook(spec, external, harnessRoot, projectDir) {
|
|
|
2660
2645
|
}
|
|
2661
2646
|
hookScriptCopied = true;
|
|
2662
2647
|
}
|
|
2663
|
-
const settingsPath =
|
|
2648
|
+
const settingsPath = join10(projectDir, ".claude/settings.json");
|
|
2664
2649
|
let settingsUpdated = false;
|
|
2665
|
-
if (
|
|
2666
|
-
const raw =
|
|
2650
|
+
if (existsSync11(settingsPath)) {
|
|
2651
|
+
const raw = readFileSync11(settingsPath, "utf8");
|
|
2667
2652
|
let before;
|
|
2668
2653
|
try {
|
|
2669
2654
|
before = JSON.parse(raw);
|
|
@@ -2682,10 +2667,10 @@ function wireKarpathyHook(spec, external, harnessRoot, projectDir) {
|
|
|
2682
2667
|
return { wired: true, settingsUpdated, hookScriptCopied };
|
|
2683
2668
|
}
|
|
2684
2669
|
function composeAndWriteMcp(harnessRoot, projectDir, spec) {
|
|
2685
|
-
const mcpPath =
|
|
2670
|
+
const mcpPath = join10(projectDir, ".mcp.json");
|
|
2686
2671
|
const composed = composeMcpJson({
|
|
2687
|
-
templateMcpPath:
|
|
2688
|
-
trackMapPath:
|
|
2672
|
+
templateMcpPath: join10(harnessRoot, "templates/mcp.json"),
|
|
2673
|
+
trackMapPath: join10(harnessRoot, "templates/track-mcp-map.tsv"),
|
|
2689
2674
|
existingPath: mcpPath,
|
|
2690
2675
|
tracks: spec.tracks
|
|
2691
2676
|
});
|
|
@@ -2711,16 +2696,15 @@ function accumulateCategory(cats, entry) {
|
|
|
2711
2696
|
}
|
|
2712
2697
|
}
|
|
2713
2698
|
function writeInstalledTracks(projectDir, tracks) {
|
|
2714
|
-
const path =
|
|
2699
|
+
const path = join10(projectDir, ".claude/.installed-tracks");
|
|
2715
2700
|
mkdirSync4(dirname4(path), { recursive: true });
|
|
2716
2701
|
const sorted = [...new Set(tracks)].sort().join("\n");
|
|
2717
2702
|
writeFileSync9(path, `${sorted}
|
|
2718
2703
|
`);
|
|
2719
2704
|
}
|
|
2720
|
-
function writeRootClaudeMd(
|
|
2721
|
-
const
|
|
2722
|
-
const
|
|
2723
|
-
const target = join11(projectDir, "CLAUDE.md");
|
|
2705
|
+
function writeRootClaudeMd(projectDir, tracks) {
|
|
2706
|
+
const content = mergeProjectClaude(tracks, { projectName: basename4(projectDir) });
|
|
2707
|
+
const target = join10(projectDir, "CLAUDE.md");
|
|
2724
2708
|
const backup = backupFileIfChanged(target, content);
|
|
2725
2709
|
writeFileSync9(target, content);
|
|
2726
2710
|
return { content, backup };
|
|
@@ -2956,8 +2940,27 @@ function renderFinalSummary(log, spec, report, fromWizard) {
|
|
|
2956
2940
|
const primary = (spec.cli.includes("claude") ? "claude" : spec.cli[0]) ?? "claude";
|
|
2957
2941
|
const label = CLI_SUMMARY_LABELS[primary];
|
|
2958
2942
|
log(infoRow("NEXT", `Open ${c.bold(label)} \u2014 installed rules & skills are now active`));
|
|
2943
|
+
const scaffoldFiles = scaffoldFilesForCli(spec.cli);
|
|
2944
|
+
if (scaffoldFiles.length > 0) {
|
|
2945
|
+
log(
|
|
2946
|
+
infoRow(
|
|
2947
|
+
"FILL",
|
|
2948
|
+
`${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`
|
|
2949
|
+
)
|
|
2950
|
+
);
|
|
2951
|
+
}
|
|
2959
2952
|
log("");
|
|
2960
2953
|
}
|
|
2954
|
+
function scaffoldFilesForCli(cli2) {
|
|
2955
|
+
const files = [];
|
|
2956
|
+
if (cli2.includes("claude")) {
|
|
2957
|
+
files.push("CLAUDE.md");
|
|
2958
|
+
}
|
|
2959
|
+
if (cli2.some((target) => target !== "claude")) {
|
|
2960
|
+
files.push("AGENTS.md");
|
|
2961
|
+
}
|
|
2962
|
+
return files;
|
|
2963
|
+
}
|
|
2961
2964
|
function formatAssetMeta(asset, version) {
|
|
2962
2965
|
const m = asset.method;
|
|
2963
2966
|
const v2 = version ? ` ${c.dim(`v${version.replace(/^v/, "")}`)}` : "";
|
|
@@ -3065,7 +3068,7 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false) {
|
|
|
3065
3068
|
assetRow(
|
|
3066
3069
|
"success",
|
|
3067
3070
|
"CLAUDE.md (root)",
|
|
3068
|
-
`
|
|
3071
|
+
`fill-in scaffold \xB7 ${n} track${n > 1 ? "s" : ""} noted`,
|
|
3069
3072
|
TEMPLATES_COL
|
|
3070
3073
|
)
|
|
3071
3074
|
);
|
|
@@ -3332,8 +3335,8 @@ function registerInstallCommand(cli2) {
|
|
|
3332
3335
|
// src/commands/uninstall.ts
|
|
3333
3336
|
init_esm_shims();
|
|
3334
3337
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3335
|
-
import { existsSync as
|
|
3336
|
-
import { join as
|
|
3338
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, rmSync } from "fs";
|
|
3339
|
+
import { join as join11, resolve as resolve3 } from "path";
|
|
3337
3340
|
function uninstallAction(options, deps = {}) {
|
|
3338
3341
|
const log = deps.log ?? console.log;
|
|
3339
3342
|
const err = deps.err ?? console.error;
|
|
@@ -3505,22 +3508,22 @@ function buildGlobalAdvisoryCmd(asset) {
|
|
|
3505
3508
|
}
|
|
3506
3509
|
}
|
|
3507
3510
|
function removeTemplates(log, projectDir, rm) {
|
|
3508
|
-
rm(
|
|
3509
|
-
if (log.templates.codexDir) rm(
|
|
3510
|
-
if (log.templates.opencodeDir) rm(
|
|
3511
|
+
rm(join11(projectDir, log.templates.claudeDir));
|
|
3512
|
+
if (log.templates.codexDir) rm(join11(projectDir, log.templates.codexDir));
|
|
3513
|
+
if (log.templates.opencodeDir) rm(join11(projectDir, log.templates.opencodeDir));
|
|
3511
3514
|
const rootMd = log.templates.rootClaudeMd;
|
|
3512
3515
|
if (rootMd) {
|
|
3513
3516
|
if (rootClaudeMdModified(log, projectDir)) return { rootClaudeMdKept: true };
|
|
3514
|
-
rm(
|
|
3517
|
+
rm(join11(projectDir, rootMd.path));
|
|
3515
3518
|
}
|
|
3516
3519
|
return { rootClaudeMdKept: false };
|
|
3517
3520
|
}
|
|
3518
3521
|
function rootClaudeMdModified(log, projectDir) {
|
|
3519
3522
|
const rootMd = log.templates.rootClaudeMd;
|
|
3520
3523
|
if (!rootMd) return false;
|
|
3521
|
-
const path =
|
|
3522
|
-
if (!
|
|
3523
|
-
return hashContent(
|
|
3524
|
+
const path = join11(projectDir, rootMd.path);
|
|
3525
|
+
if (!existsSync12(path)) return false;
|
|
3526
|
+
return hashContent(readFileSync12(path, "utf8")) !== rootMd.sha256;
|
|
3524
3527
|
}
|
|
3525
3528
|
function formatTemplateList(log) {
|
|
3526
3529
|
const items = [log.templates.claudeDir];
|
|
@@ -3532,7 +3535,7 @@ function defaultSpawn2(cmd, args) {
|
|
|
3532
3535
|
return spawnSync2(cmd, [...args], { encoding: "utf8", stdio: "pipe", timeout: 12e4 });
|
|
3533
3536
|
}
|
|
3534
3537
|
function defaultRm(path) {
|
|
3535
|
-
if (
|
|
3538
|
+
if (existsSync12(path)) {
|
|
3536
3539
|
rmSync(path, { recursive: true, force: true });
|
|
3537
3540
|
}
|
|
3538
3541
|
}
|
|
@@ -4563,9 +4566,9 @@ function summarizeState(state) {
|
|
|
4563
4566
|
if (state.state === "new") {
|
|
4564
4567
|
return "No prior install detected \u2014 new install flow.";
|
|
4565
4568
|
}
|
|
4566
|
-
const
|
|
4569
|
+
const trackList = state.tracks.length > 0 ? state.tracks.join(", ") : "(no tracks resolved)";
|
|
4567
4570
|
const sourceLabel = state.source === "metafile" ? "via .claude/.installed-tracks" : state.source === "legacy" ? "via legacy rules/*.md heuristic" : "via no source";
|
|
4568
|
-
return `Existing install detected ${sourceLabel}. Tracks: ${
|
|
4571
|
+
return `Existing install detected ${sourceLabel}. Tracks: ${trackList}.`;
|
|
4569
4572
|
}
|
|
4570
4573
|
|
|
4571
4574
|
// src/wizard-steps.ts
|
|
@@ -4808,8 +4811,8 @@ Proceed?`,
|
|
|
4808
4811
|
|
|
4809
4812
|
// src/state.ts
|
|
4810
4813
|
init_esm_shims();
|
|
4811
|
-
import { existsSync as
|
|
4812
|
-
import { join as
|
|
4814
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13 } from "fs";
|
|
4815
|
+
import { join as join12 } from "path";
|
|
4813
4816
|
var META_FILE = ".claude/.installed-tracks";
|
|
4814
4817
|
var LEGACY_SIGNATURES = [
|
|
4815
4818
|
{ rule: "htmx.md", track: "ssr-htmx" },
|
|
@@ -4819,13 +4822,13 @@ var LEGACY_SIGNATURES = [
|
|
|
4819
4822
|
{ rule: "cli-development.md", track: "tooling" }
|
|
4820
4823
|
];
|
|
4821
4824
|
function detectInstallState(projectDir) {
|
|
4822
|
-
const claudeDir =
|
|
4823
|
-
const hasClaudeDir =
|
|
4825
|
+
const claudeDir = join12(projectDir, ".claude");
|
|
4826
|
+
const hasClaudeDir = existsSync13(claudeDir);
|
|
4824
4827
|
if (!hasClaudeDir) {
|
|
4825
4828
|
return { state: "new", tracks: [], source: "none", hasClaudeDir: false };
|
|
4826
4829
|
}
|
|
4827
|
-
const metaPath =
|
|
4828
|
-
if (
|
|
4830
|
+
const metaPath = join12(projectDir, META_FILE);
|
|
4831
|
+
if (existsSync13(metaPath)) {
|
|
4829
4832
|
const tracks2 = readMetafile(metaPath);
|
|
4830
4833
|
return { state: "existing", tracks: tracks2, source: "metafile", hasClaudeDir: true };
|
|
4831
4834
|
}
|
|
@@ -4833,7 +4836,7 @@ function detectInstallState(projectDir) {
|
|
|
4833
4836
|
return { state: "existing", tracks, source: "legacy", hasClaudeDir: true };
|
|
4834
4837
|
}
|
|
4835
4838
|
function readMetafile(path) {
|
|
4836
|
-
const raw =
|
|
4839
|
+
const raw = readFileSync13(path, "utf8");
|
|
4837
4840
|
const seen = /* @__PURE__ */ new Set();
|
|
4838
4841
|
for (const line of raw.split(/\s+/)) {
|
|
4839
4842
|
const trimmed = line.trim();
|
|
@@ -4844,13 +4847,13 @@ function readMetafile(path) {
|
|
|
4844
4847
|
return [...seen].sort();
|
|
4845
4848
|
}
|
|
4846
4849
|
function inferFromLegacySignatures(projectDir) {
|
|
4847
|
-
const rulesDir =
|
|
4848
|
-
if (!
|
|
4850
|
+
const rulesDir = join12(projectDir, ".claude/rules");
|
|
4851
|
+
if (!existsSync13(rulesDir)) {
|
|
4849
4852
|
return [];
|
|
4850
4853
|
}
|
|
4851
4854
|
const found = /* @__PURE__ */ new Set();
|
|
4852
4855
|
for (const sig of LEGACY_SIGNATURES) {
|
|
4853
|
-
if (
|
|
4856
|
+
if (existsSync13(join12(rulesDir, sig.rule))) {
|
|
4854
4857
|
found.add(sig.track);
|
|
4855
4858
|
}
|
|
4856
4859
|
}
|