@uzysjung/agent-harness 26.88.0 → 26.89.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 +3 -26
- package/README.md +8 -32
- package/dist/{chunk-QHYH6P32.js → chunk-EZZOJPG4.js} +1 -11
- package/dist/chunk-EZZOJPG4.js.map +1 -0
- package/dist/index.js +262 -748
- package/dist/index.js.map +1 -1
- package/dist/trust-tier-drift.js +1 -1
- package/package.json +1 -1
- package/templates/agents/plan-checker.md +3 -4
- package/templates/agents/reviewer.md +1 -1
- package/templates/antigravity/AGENTS.md.template +0 -28
- package/templates/codex/AGENTS.md.template +0 -25
- package/templates/codex/README.md +5 -15
- package/templates/codex/config.toml.template +0 -10
- package/templates/codex/hooks/README.md +0 -2
- package/templates/hooks/checkpoint-snapshot.sh +1 -22
- package/templates/hooks/session-start.sh +1 -1
- package/templates/hooks/spec-drift-check.sh +2 -20
- package/templates/opencode/AGENTS.md.template +3 -38
- package/templates/opencode/README.md +0 -4
- package/templates/opencode/opencode.json.template +1 -3
- package/templates/project-claude/fragments/csr-fastapi/boundaries.md +0 -1
- package/templates/project-claude/fragments/csr-fastapi/commands.md +0 -1
- package/templates/project-claude/fragments/csr-fastapi/plugins.md +1 -1
- package/templates/project-claude/fragments/csr-fastify/boundaries.md +0 -1
- package/templates/project-claude/fragments/csr-fastify/commands.md +0 -1
- package/templates/project-claude/fragments/csr-fastify/plugins.md +1 -1
- package/templates/project-claude/fragments/csr-supabase/boundaries.md +0 -1
- package/templates/project-claude/fragments/csr-supabase/commands.md +0 -1
- package/templates/project-claude/fragments/csr-supabase/plugins.md +1 -1
- package/templates/project-claude/fragments/csr-supabase/supabase-auth.md +0 -2
- package/templates/project-claude/fragments/data/boundaries.md +0 -1
- package/templates/project-claude/fragments/data/commands.md +0 -1
- package/templates/project-claude/fragments/data/plugins.md +1 -1
- package/templates/project-claude/fragments/executive/boundaries.md +0 -1
- package/templates/project-claude/fragments/executive/commands.md +0 -1
- package/templates/project-claude/fragments/executive/skills.md +1 -1
- package/templates/project-claude/fragments/executive/workflow.md +1 -2
- package/templates/project-claude/fragments/growth-marketing/boundaries.md +0 -1
- package/templates/project-claude/fragments/growth-marketing/commands.md +0 -1
- package/templates/project-claude/fragments/growth-marketing/skills.md +1 -1
- package/templates/project-claude/fragments/growth-marketing/workflow.md +1 -2
- package/templates/project-claude/fragments/project-management/boundaries.md +0 -1
- package/templates/project-claude/fragments/project-management/commands.md +0 -1
- package/templates/project-claude/fragments/project-management/skills.md +1 -1
- package/templates/project-claude/fragments/project-management/workflow.md +1 -2
- package/templates/project-claude/fragments/ssr-htmx/boundaries.md +0 -1
- package/templates/project-claude/fragments/ssr-htmx/commands.md +0 -1
- package/templates/project-claude/fragments/ssr-htmx/plugins.md +1 -1
- package/templates/project-claude/fragments/ssr-nextjs/boundaries.md +0 -1
- package/templates/project-claude/fragments/ssr-nextjs/commands.md +0 -1
- package/templates/project-claude/fragments/ssr-nextjs/plugins.md +1 -1
- package/templates/project-claude/fragments/tooling/boundaries.md +0 -1
- package/templates/project-claude/fragments/tooling/commands.md +0 -1
- package/templates/project-claude/fragments/tooling/skills.md +1 -1
- package/templates/rules/git-policy.md +2 -2
- package/templates/rules/ship-checklist.md +3 -3
- package/templates/settings.json +0 -13
- package/templates/skills/gh-issue-workflow/SKILL.md +8 -8
- package/templates/skills/north-star/SKILL.md +4 -4
- package/templates/skills/ui-visual-review/SKILL.md +6 -6
- package/dist/chunk-QHYH6P32.js.map +0 -1
- package/templates/codex/hooks/gate-check.sh +0 -7
- package/templates/codex/skills/uzys-build/SKILL.md +0 -24
- package/templates/codex/skills/uzys-plan/SKILL.md +0 -24
- package/templates/codex/skills/uzys-review/SKILL.md +0 -24
- package/templates/codex/skills/uzys-ship/SKILL.md +0 -24
- package/templates/codex/skills/uzys-spec/SKILL.md +0 -28
- package/templates/codex/skills/uzys-test/SKILL.md +0 -24
- package/templates/commands/uzys/auto.md +0 -190
- package/templates/commands/uzys/build.md +0 -42
- package/templates/commands/uzys/plan.md +0 -55
- package/templates/commands/uzys/review.md +0 -44
- package/templates/commands/uzys/ship.md +0 -49
- package/templates/commands/uzys/spec.md +0 -93
- package/templates/commands/uzys/test.md +0 -58
- package/templates/hooks/agentshield-gate.sh +0 -101
- package/templates/hooks/gate-check.sh +0 -138
- package/templates/opencode/.opencode/commands/uzys-build.md +0 -22
- package/templates/opencode/.opencode/commands/uzys-plan.md +0 -22
- package/templates/opencode/.opencode/commands/uzys-review.md +0 -22
- package/templates/opencode/.opencode/commands/uzys-ship.md +0 -22
- package/templates/opencode/.opencode/commands/uzys-spec.md +0 -28
- package/templates/opencode/.opencode/commands/uzys-test.md +0 -22
- package/templates/opencode/.opencode/plugins/uzys-harness.ts +0 -146
- package/templates/project-claude/fragments/csr-fastapi/workflow.md +0 -8
- package/templates/project-claude/fragments/csr-fastify/workflow.md +0 -8
- package/templates/project-claude/fragments/csr-supabase/workflow.md +0 -8
- package/templates/project-claude/fragments/data/workflow.md +0 -9
- package/templates/project-claude/fragments/ssr-htmx/workflow.md +0 -8
- package/templates/project-claude/fragments/ssr-nextjs/workflow.md +0 -8
- package/templates/project-claude/fragments/tooling/workflow.md +0 -5
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-EZZOJPG4.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.89.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: {
|
|
@@ -789,11 +789,8 @@ function resolveScope(scope) {
|
|
|
789
789
|
}
|
|
790
790
|
var DEFAULT_OPTIONS = {
|
|
791
791
|
withPrune: false,
|
|
792
|
-
withCodexSkills: false,
|
|
793
792
|
withCodexTrust: false,
|
|
794
|
-
withKarpathyHook: false
|
|
795
|
-
withCodexPrompts: false,
|
|
796
|
-
withAntigravityGlobal: false
|
|
793
|
+
withKarpathyHook: false
|
|
797
794
|
};
|
|
798
795
|
|
|
799
796
|
// src/cli-targets.ts
|
|
@@ -931,19 +928,18 @@ function padDisplay(s, width) {
|
|
|
931
928
|
init_esm_shims();
|
|
932
929
|
import {
|
|
933
930
|
chmodSync as chmodSync2,
|
|
934
|
-
existsSync as
|
|
935
|
-
mkdirSync as
|
|
936
|
-
readdirSync as
|
|
937
|
-
readFileSync as
|
|
938
|
-
writeFileSync as
|
|
931
|
+
existsSync as existsSync12,
|
|
932
|
+
mkdirSync as mkdirSync4,
|
|
933
|
+
readdirSync as readdirSync3,
|
|
934
|
+
readFileSync as readFileSync12,
|
|
935
|
+
writeFileSync as writeFileSync9
|
|
939
936
|
} from "fs";
|
|
940
|
-
import { dirname as dirname4, join as
|
|
937
|
+
import { dirname as dirname4, join as join11, resolve } from "path";
|
|
941
938
|
|
|
942
|
-
// src/antigravity/
|
|
939
|
+
// src/antigravity/transform.ts
|
|
943
940
|
init_esm_shims();
|
|
944
|
-
import {
|
|
945
|
-
import {
|
|
946
|
-
import { join } from "path";
|
|
941
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
942
|
+
import { basename, join as join2 } from "path";
|
|
947
943
|
|
|
948
944
|
// src/codex/agents-md.ts
|
|
949
945
|
init_esm_shims();
|
|
@@ -956,67 +952,8 @@ function renderAgentsMd(params) {
|
|
|
956
952
|
return renameSlashes(replaced);
|
|
957
953
|
}
|
|
958
954
|
|
|
959
|
-
// src/antigravity/opt-in.ts
|
|
960
|
-
var PHASES = ["spec", "plan", "build", "test", "review", "ship"];
|
|
961
|
-
function runAntigravityOptIn(ctx) {
|
|
962
|
-
const geminiHome = ctx.geminiHome ?? join(homedir(), ".gemini");
|
|
963
|
-
const skillsTarget = join(geminiHome, "antigravity", "skills");
|
|
964
|
-
const workflowsTarget = join(geminiHome, "antigravity", "global_workflows");
|
|
965
|
-
if (!ctx.enabled) {
|
|
966
|
-
return {
|
|
967
|
-
skillsInstalled: { enabled: false, count: 0, targetDir: skillsTarget },
|
|
968
|
-
workflowsInstalled: { enabled: false, count: 0, targetDir: workflowsTarget }
|
|
969
|
-
};
|
|
970
|
-
}
|
|
971
|
-
let skillsCount = 0;
|
|
972
|
-
for (const phase of PHASES) {
|
|
973
|
-
const src = join(ctx.projectDir, ".agents", "skills", `uzys-${phase}`);
|
|
974
|
-
if (!existsSync(src)) continue;
|
|
975
|
-
const dst = join(skillsTarget, `uzys-${phase}`);
|
|
976
|
-
mkdirSync(dst, { recursive: true });
|
|
977
|
-
cpSync(src, dst, { recursive: true });
|
|
978
|
-
skillsCount++;
|
|
979
|
-
}
|
|
980
|
-
let workflowsCount = 0;
|
|
981
|
-
if (ctx.harnessRoot) {
|
|
982
|
-
mkdirSync(workflowsTarget, { recursive: true });
|
|
983
|
-
for (const phase of PHASES) {
|
|
984
|
-
const src = join(ctx.harnessRoot, "templates/commands/uzys", `${phase}.md`);
|
|
985
|
-
if (!existsSync(src)) continue;
|
|
986
|
-
const body = renameSlashes(readFileSync(src, "utf8"));
|
|
987
|
-
const dst = join(workflowsTarget, `uzys-${phase}.md`);
|
|
988
|
-
writeFileSync(dst, body);
|
|
989
|
-
workflowsCount++;
|
|
990
|
-
}
|
|
991
|
-
}
|
|
992
|
-
return {
|
|
993
|
-
skillsInstalled: { enabled: true, count: skillsCount, targetDir: skillsTarget },
|
|
994
|
-
workflowsInstalled: { enabled: true, count: workflowsCount, targetDir: workflowsTarget }
|
|
995
|
-
};
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
// src/antigravity/transform.ts
|
|
999
|
-
init_esm_shims();
|
|
1000
|
-
import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1001
|
-
import { basename, join as join3 } from "path";
|
|
1002
|
-
|
|
1003
955
|
// src/codex/skills.ts
|
|
1004
956
|
init_esm_shims();
|
|
1005
|
-
function renderSkill(params) {
|
|
1006
|
-
const { description, body } = parseSource(params.source);
|
|
1007
|
-
const finalDescription = description || `uzys-${params.phase} phase skill (Codex \uD3EC\uD305)`;
|
|
1008
|
-
const escapedDesc = finalDescription.replace(/"/g, '\\"');
|
|
1009
|
-
const renamedBody = renameSlashes(body).trimEnd();
|
|
1010
|
-
return [
|
|
1011
|
-
"---",
|
|
1012
|
-
`name: uzys-${params.phase}`,
|
|
1013
|
-
`description: "${escapedDesc}"`,
|
|
1014
|
-
"---",
|
|
1015
|
-
"",
|
|
1016
|
-
renamedBody,
|
|
1017
|
-
""
|
|
1018
|
-
].join("\n");
|
|
1019
|
-
}
|
|
1020
957
|
function renderBundledSkill(source) {
|
|
1021
958
|
const trimmed = source.trimEnd();
|
|
1022
959
|
const lines = trimmed.split(/\r?\n/);
|
|
@@ -1044,60 +981,30 @@ ${portBody(body)}
|
|
|
1044
981
|
function portBody(body) {
|
|
1045
982
|
return renameSlashes(body).replace(/CLAUDE_PROJECT_DIR/g, "CODEX_PROJECT_DIR").trimEnd();
|
|
1046
983
|
}
|
|
1047
|
-
function parseSource(source) {
|
|
1048
|
-
const lines = source.split(/\r?\n/);
|
|
1049
|
-
if (lines[0] === "---") {
|
|
1050
|
-
let descMatch = "";
|
|
1051
|
-
let secondDelimAt = -1;
|
|
1052
|
-
for (let i = 1; i < lines.length; i++) {
|
|
1053
|
-
const line = lines[i] ?? "";
|
|
1054
|
-
if (line === "---") {
|
|
1055
|
-
secondDelimAt = i;
|
|
1056
|
-
break;
|
|
1057
|
-
}
|
|
1058
|
-
const match = line.match(/^description:\s*(.*)$/);
|
|
1059
|
-
if (match) {
|
|
1060
|
-
descMatch = stripQuotes(match[1] ?? "");
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
const body2 = secondDelimAt >= 0 ? lines.slice(secondDelimAt + 1).join("\n") : source;
|
|
1064
|
-
return { description: descMatch, body: body2.replace(/^\n+/, "") };
|
|
1065
|
-
}
|
|
1066
|
-
const firstLine = lines[0] ?? "";
|
|
1067
|
-
const body = lines.slice(1).join("\n");
|
|
1068
|
-
return { description: firstLine.trim(), body };
|
|
1069
|
-
}
|
|
1070
|
-
function stripQuotes(raw) {
|
|
1071
|
-
const trimmed = raw.trim();
|
|
1072
|
-
if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
1073
|
-
return trimmed.slice(1, -1);
|
|
1074
|
-
}
|
|
1075
|
-
return trimmed;
|
|
1076
|
-
}
|
|
1077
984
|
|
|
1078
985
|
// src/fs-ops.ts
|
|
1079
986
|
init_esm_shims();
|
|
1080
|
-
import { copyFileSync, cpSync
|
|
1081
|
-
import { dirname, join
|
|
987
|
+
import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, renameSync } from "fs";
|
|
988
|
+
import { dirname, join } from "path";
|
|
1082
989
|
function ensureDir(path) {
|
|
1083
|
-
|
|
990
|
+
mkdirSync(path, { recursive: true });
|
|
1084
991
|
}
|
|
1085
992
|
function copyFile(source, target) {
|
|
1086
|
-
if (!
|
|
993
|
+
if (!existsSync(source)) {
|
|
1087
994
|
throw new Error(`Source not found: ${source}`);
|
|
1088
995
|
}
|
|
1089
|
-
|
|
996
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
1090
997
|
copyFileSync(source, target);
|
|
1091
998
|
}
|
|
1092
999
|
function copyDir(source, target) {
|
|
1093
|
-
if (!
|
|
1000
|
+
if (!existsSync(source)) {
|
|
1094
1001
|
throw new Error(`Source dir not found: ${source}`);
|
|
1095
1002
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1003
|
+
mkdirSync(target, { recursive: true });
|
|
1004
|
+
cpSync(source, target, { recursive: true, force: true });
|
|
1098
1005
|
}
|
|
1099
1006
|
function backupDir(target, now = /* @__PURE__ */ new Date()) {
|
|
1100
|
-
if (!
|
|
1007
|
+
if (!existsSync(target)) {
|
|
1101
1008
|
return null;
|
|
1102
1009
|
}
|
|
1103
1010
|
const backup = `${target}.backup-${formatStamp(now)}`;
|
|
@@ -1105,18 +1012,18 @@ function backupDir(target, now = /* @__PURE__ */ new Date()) {
|
|
|
1105
1012
|
return backup;
|
|
1106
1013
|
}
|
|
1107
1014
|
function copyBackupDir(target, now = /* @__PURE__ */ new Date()) {
|
|
1108
|
-
if (!
|
|
1015
|
+
if (!existsSync(target)) {
|
|
1109
1016
|
return null;
|
|
1110
1017
|
}
|
|
1111
1018
|
const backup = `${target}.backup-${formatStamp(now)}`;
|
|
1112
|
-
|
|
1019
|
+
cpSync(target, backup, { recursive: true });
|
|
1113
1020
|
return backup;
|
|
1114
1021
|
}
|
|
1115
1022
|
function backupFileIfChanged(target, newContent, now = /* @__PURE__ */ new Date()) {
|
|
1116
|
-
if (!
|
|
1023
|
+
if (!existsSync(target)) {
|
|
1117
1024
|
return null;
|
|
1118
1025
|
}
|
|
1119
|
-
if (
|
|
1026
|
+
if (readFileSync(target, "utf-8") === newContent) {
|
|
1120
1027
|
return null;
|
|
1121
1028
|
}
|
|
1122
1029
|
const backup = `${target}.backup-${formatStamp(now)}`;
|
|
@@ -1137,128 +1044,58 @@ function ensureProjectSkeleton(projectDir) {
|
|
|
1137
1044
|
"docs/decisions"
|
|
1138
1045
|
];
|
|
1139
1046
|
for (const d2 of dirs) {
|
|
1140
|
-
|
|
1047
|
+
mkdirSync(join(projectDir, d2), { recursive: true });
|
|
1141
1048
|
}
|
|
1142
1049
|
}
|
|
1143
1050
|
|
|
1144
1051
|
// src/antigravity/transform.ts
|
|
1145
|
-
var PHASES2 = ["spec", "plan", "build", "test", "review", "ship"];
|
|
1146
1052
|
function runAntigravityTransform(params) {
|
|
1147
|
-
const { harnessRoot, projectDir,
|
|
1053
|
+
const { harnessRoot, projectDir, selectedInternalSkills = [] } = params;
|
|
1148
1054
|
const rulesFile = writeRules(harnessRoot, projectDir);
|
|
1149
1055
|
const skillFiles = [];
|
|
1150
|
-
const workflowFiles = [];
|
|
1151
1056
|
for (const id of selectedInternalSkills) {
|
|
1152
|
-
const src =
|
|
1153
|
-
if (!
|
|
1057
|
+
const src = join2(harnessRoot, "templates/skills", id, "SKILL.md");
|
|
1058
|
+
if (!existsSync2(src)) {
|
|
1154
1059
|
continue;
|
|
1155
1060
|
}
|
|
1156
|
-
const skillDir =
|
|
1061
|
+
const skillDir = join2(projectDir, ".agents", "skills", id);
|
|
1157
1062
|
ensureDir(skillDir);
|
|
1158
|
-
const target =
|
|
1159
|
-
|
|
1063
|
+
const target = join2(skillDir, "SKILL.md");
|
|
1064
|
+
writeFileSync(target, renderBundledSkill(readFileSync2(src, "utf8")));
|
|
1160
1065
|
skillFiles.push(target);
|
|
1161
1066
|
}
|
|
1162
|
-
|
|
1163
|
-
for (const phase of PHASES2) {
|
|
1164
|
-
const skillDir = join3(projectDir, ".agents", "skills", `uzys-${phase}`);
|
|
1165
|
-
ensureDir(skillDir);
|
|
1166
|
-
const cmdSrc = join3(harnessRoot, "templates/commands/uzys", `${phase}.md`);
|
|
1167
|
-
let source = "";
|
|
1168
|
-
if (existsSync3(cmdSrc)) {
|
|
1169
|
-
source = readFileSync3(cmdSrc, "utf8");
|
|
1170
|
-
} else {
|
|
1171
|
-
const fallback = join3(harnessRoot, "templates/codex/skills", `uzys-${phase}/SKILL.md`);
|
|
1172
|
-
if (existsSync3(fallback)) {
|
|
1173
|
-
source = readFileSync3(fallback, "utf8");
|
|
1174
|
-
}
|
|
1175
|
-
}
|
|
1176
|
-
const skillTarget = join3(skillDir, "SKILL.md");
|
|
1177
|
-
writeFileSync2(skillTarget, renderSkill({ source, phase }));
|
|
1178
|
-
skillFiles.push(skillTarget);
|
|
1179
|
-
if (source) {
|
|
1180
|
-
const workflowDir = join3(projectDir, ".agents", "workflows");
|
|
1181
|
-
ensureDir(workflowDir);
|
|
1182
|
-
const workflowTarget = join3(workflowDir, `uzys-${phase}.md`);
|
|
1183
|
-
writeFileSync2(workflowTarget, renameSlashes(source));
|
|
1184
|
-
workflowFiles.push(workflowTarget);
|
|
1185
|
-
}
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
return { rulesFile, skillFiles, workflowFiles };
|
|
1067
|
+
return { rulesFile, skillFiles };
|
|
1189
1068
|
}
|
|
1190
1069
|
function writeRules(harnessRoot, projectDir) {
|
|
1191
|
-
const claudeMdPath =
|
|
1192
|
-
const templatePath =
|
|
1193
|
-
if (!
|
|
1070
|
+
const claudeMdPath = join2(harnessRoot, "templates/CLAUDE.md");
|
|
1071
|
+
const templatePath = join2(harnessRoot, "templates/antigravity/AGENTS.md.template");
|
|
1072
|
+
if (!existsSync2(claudeMdPath) || !existsSync2(templatePath)) {
|
|
1194
1073
|
return null;
|
|
1195
1074
|
}
|
|
1196
|
-
const claudeMd =
|
|
1197
|
-
const template =
|
|
1198
|
-
const rulesDir =
|
|
1075
|
+
const claudeMd = readFileSync2(claudeMdPath, "utf8");
|
|
1076
|
+
const template = readFileSync2(templatePath, "utf8");
|
|
1077
|
+
const rulesDir = join2(projectDir, ".agents", "rules");
|
|
1199
1078
|
ensureDir(rulesDir);
|
|
1200
|
-
const target =
|
|
1201
|
-
|
|
1079
|
+
const target = join2(rulesDir, "uzys-harness.md");
|
|
1080
|
+
writeFileSync(target, renderAgentsMd({ template, claudeMd, projectName: basename(projectDir) }));
|
|
1202
1081
|
return target;
|
|
1203
1082
|
}
|
|
1204
1083
|
|
|
1205
1084
|
// src/codex/opt-in.ts
|
|
1206
1085
|
init_esm_shims();
|
|
1207
|
-
import {
|
|
1208
|
-
import {
|
|
1209
|
-
import { join as join4 } from "path";
|
|
1210
|
-
|
|
1211
|
-
// src/codex/prompts.ts
|
|
1212
|
-
init_esm_shims();
|
|
1213
|
-
function renderCodexPrompt(params) {
|
|
1214
|
-
const { description, body } = parseSource2(params.source);
|
|
1215
|
-
const finalDescription = description || `uzys-${params.phase} phase`;
|
|
1216
|
-
const escapedDesc = finalDescription.replace(/"/g, '\\"');
|
|
1217
|
-
const renamedBody = renameSlashes(body).trimEnd();
|
|
1218
|
-
return ["---", `description: "${escapedDesc}"`, "---", "", renamedBody, ""].join("\n");
|
|
1219
|
-
}
|
|
1220
|
-
function parseSource2(source) {
|
|
1221
|
-
const lines = source.split(/\r?\n/);
|
|
1222
|
-
if (lines[0] === "---") {
|
|
1223
|
-
let descMatch = "";
|
|
1224
|
-
let secondDelimAt = -1;
|
|
1225
|
-
for (let i = 1; i < lines.length; i++) {
|
|
1226
|
-
const line = lines[i] ?? "";
|
|
1227
|
-
if (line === "---") {
|
|
1228
|
-
secondDelimAt = i;
|
|
1229
|
-
break;
|
|
1230
|
-
}
|
|
1231
|
-
const m = line.match(/^description:\s*"?(.+?)"?\s*$/);
|
|
1232
|
-
if (m?.[1]) descMatch = m[1];
|
|
1233
|
-
}
|
|
1234
|
-
if (secondDelimAt >= 0) {
|
|
1235
|
-
return {
|
|
1236
|
-
description: descMatch,
|
|
1237
|
-
body: lines.slice(secondDelimAt + 1).join("\n").replace(/^\n+/, "")
|
|
1238
|
-
};
|
|
1239
|
-
}
|
|
1240
|
-
return {
|
|
1241
|
-
description: "",
|
|
1242
|
-
body: lines.slice(1).join("\n")
|
|
1243
|
-
};
|
|
1244
|
-
}
|
|
1245
|
-
const firstLine = lines[0] ?? "";
|
|
1246
|
-
const description = firstLine.replace(/^#+\s*/, "").slice(0, 200);
|
|
1247
|
-
const body = lines.slice(1).join("\n").replace(/^\n+/, "");
|
|
1248
|
-
return { description, body };
|
|
1249
|
-
}
|
|
1250
|
-
var CODEX_PROMPT_PHASES = ["spec", "plan", "build", "test", "review", "ship"];
|
|
1086
|
+
import { homedir } from "os";
|
|
1087
|
+
import { join as join3 } from "path";
|
|
1251
1088
|
|
|
1252
1089
|
// src/codex/trust-entry.ts
|
|
1253
1090
|
init_esm_shims();
|
|
1254
|
-
import { existsSync as
|
|
1091
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1255
1092
|
import { dirname as dirname2 } from "path";
|
|
1256
1093
|
var TRUST_BLOCK_REGEX = /\[projects\."([^"]+)"\]/g;
|
|
1257
1094
|
function registerTrustEntry(opts) {
|
|
1258
1095
|
const { configPath, projectDir } = opts;
|
|
1259
1096
|
try {
|
|
1260
|
-
|
|
1261
|
-
const existing =
|
|
1097
|
+
mkdirSync2(dirname2(configPath), { recursive: true });
|
|
1098
|
+
const existing = existsSync3(configPath) ? readFileSync3(configPath, "utf8") : "";
|
|
1262
1099
|
if (hasTrustEntry(existing, projectDir)) {
|
|
1263
1100
|
return { status: "already-present" };
|
|
1264
1101
|
}
|
|
@@ -1266,7 +1103,7 @@ function registerTrustEntry(opts) {
|
|
|
1266
1103
|
[projects."${projectDir}"]
|
|
1267
1104
|
trust_level = "trusted"
|
|
1268
1105
|
`;
|
|
1269
|
-
|
|
1106
|
+
writeFileSync2(configPath, existing + block);
|
|
1270
1107
|
return { status: "registered" };
|
|
1271
1108
|
} catch (e2) {
|
|
1272
1109
|
return {
|
|
@@ -1281,100 +1118,23 @@ function hasTrustEntry(configContent, projectDir) {
|
|
|
1281
1118
|
}
|
|
1282
1119
|
|
|
1283
1120
|
// src/codex/opt-in.ts
|
|
1284
|
-
var PHASES3 = ["spec", "plan", "build", "test", "review", "ship"];
|
|
1285
1121
|
function runCodexOptIn(ctx) {
|
|
1286
|
-
const codexHome = ctx.codexHome ??
|
|
1287
|
-
const
|
|
1288
|
-
const
|
|
1289
|
-
const configPath = join4(codexHome, "config.toml");
|
|
1290
|
-
let skillsCount = 0;
|
|
1291
|
-
if (ctx.withCodexSkills) {
|
|
1292
|
-
skillsCount = copyCodexSkills(ctx.projectDir, skillsTarget);
|
|
1293
|
-
}
|
|
1294
|
-
let trustResult = {
|
|
1295
|
-
enabled: false,
|
|
1296
|
-
status: "registered"
|
|
1297
|
-
};
|
|
1298
|
-
if (ctx.withCodexTrust) {
|
|
1299
|
-
const result = registerTrustEntry({
|
|
1300
|
-
configPath,
|
|
1301
|
-
projectDir: ctx.projectDir
|
|
1302
|
-
});
|
|
1303
|
-
trustResult = { enabled: true, ...result };
|
|
1304
|
-
}
|
|
1305
|
-
let promptsCount = 0;
|
|
1306
|
-
if (ctx.withCodexPrompts) {
|
|
1307
|
-
promptsCount = copyCodexPrompts(ctx.harnessRoot, ctx.projectDir, promptsTarget);
|
|
1308
|
-
}
|
|
1122
|
+
const codexHome = ctx.codexHome ?? join3(homedir(), ".codex");
|
|
1123
|
+
const configPath = join3(codexHome, "config.toml");
|
|
1124
|
+
const result = registerTrustEntry({ configPath, projectDir: ctx.projectDir });
|
|
1309
1125
|
return {
|
|
1310
|
-
skillsInstalled: {
|
|
1311
|
-
enabled: ctx.withCodexSkills,
|
|
1312
|
-
count: skillsCount,
|
|
1313
|
-
targetDir: skillsTarget
|
|
1314
|
-
},
|
|
1315
1126
|
trustEntry: {
|
|
1316
|
-
enabled:
|
|
1317
|
-
status:
|
|
1318
|
-
...
|
|
1319
|
-
},
|
|
1320
|
-
promptsInstalled: {
|
|
1321
|
-
enabled: ctx.withCodexPrompts,
|
|
1322
|
-
count: promptsCount,
|
|
1323
|
-
targetDir: promptsTarget
|
|
1127
|
+
enabled: true,
|
|
1128
|
+
status: result.status,
|
|
1129
|
+
...result.message ? { message: result.message } : {}
|
|
1324
1130
|
}
|
|
1325
1131
|
};
|
|
1326
1132
|
}
|
|
1327
|
-
function copyCodexPrompts(harnessRoot, projectDir, promptsTarget) {
|
|
1328
|
-
const candidates = [];
|
|
1329
|
-
if (harnessRoot) {
|
|
1330
|
-
candidates.push(join4(harnessRoot, "templates/commands/uzys"));
|
|
1331
|
-
}
|
|
1332
|
-
candidates.push(join4(projectDir, ".claude/commands/uzys"));
|
|
1333
|
-
const sourceDir = candidates.find((p2) => existsSync5(p2));
|
|
1334
|
-
if (!sourceDir) return 0;
|
|
1335
|
-
mkdirSync4(promptsTarget, { recursive: true });
|
|
1336
|
-
let count = 0;
|
|
1337
|
-
for (const phase of CODEX_PROMPT_PHASES) {
|
|
1338
|
-
const src = join4(sourceDir, `${phase}.md`);
|
|
1339
|
-
if (!existsSync5(src)) continue;
|
|
1340
|
-
const source = readFileSync5(src, "utf8");
|
|
1341
|
-
const dst = join4(promptsTarget, `uzys-${phase}.md`);
|
|
1342
|
-
writeFileSync4(dst, renderCodexPrompt({ source, phase }));
|
|
1343
|
-
count++;
|
|
1344
|
-
}
|
|
1345
|
-
return count;
|
|
1346
|
-
}
|
|
1347
|
-
function copyCodexSkills(projectDir, skillsTarget) {
|
|
1348
|
-
const sourceDir = join4(projectDir, ".agents", "skills");
|
|
1349
|
-
if (!existsSync5(sourceDir)) {
|
|
1350
|
-
return 0;
|
|
1351
|
-
}
|
|
1352
|
-
mkdirSync4(skillsTarget, { recursive: true });
|
|
1353
|
-
let count = 0;
|
|
1354
|
-
for (const phase of PHASES3) {
|
|
1355
|
-
const src = join4(sourceDir, `uzys-${phase}`);
|
|
1356
|
-
if (!existsSync5(src)) continue;
|
|
1357
|
-
const dest = join4(skillsTarget, `uzys-${phase}`);
|
|
1358
|
-
cpSync3(src, dest, { recursive: true });
|
|
1359
|
-
count++;
|
|
1360
|
-
}
|
|
1361
|
-
try {
|
|
1362
|
-
for (const entry of readdirSync(sourceDir)) {
|
|
1363
|
-
if (!entry.startsWith("uzys-")) continue;
|
|
1364
|
-
const phase = entry.slice("uzys-".length);
|
|
1365
|
-
if (PHASES3.includes(phase)) continue;
|
|
1366
|
-
cpSync3(join4(sourceDir, entry), join4(skillsTarget, entry), { recursive: true });
|
|
1367
|
-
count++;
|
|
1368
|
-
}
|
|
1369
|
-
} catch {
|
|
1370
|
-
}
|
|
1371
|
-
return count;
|
|
1372
|
-
}
|
|
1373
1133
|
|
|
1374
1134
|
// src/codex/transform.ts
|
|
1375
1135
|
init_esm_shims();
|
|
1376
|
-
import { chmodSync, existsSync as
|
|
1377
|
-
import { basename as basename2, join as
|
|
1136
|
+
import { chmodSync, existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
1137
|
+
import { basename as basename2, join as join4 } from "path";
|
|
1378
1138
|
|
|
1379
1139
|
// src/codex/config-toml.ts
|
|
1380
1140
|
init_esm_shims();
|
|
@@ -1439,22 +1199,21 @@ function quoteIfNeeded(name) {
|
|
|
1439
1199
|
}
|
|
1440
1200
|
|
|
1441
1201
|
// src/codex/transform.ts
|
|
1442
|
-
var
|
|
1443
|
-
var HOOK_NAMES = ["session-start", "hito-counter", "gate-check"];
|
|
1202
|
+
var HOOK_NAMES = ["session-start", "hito-counter"];
|
|
1444
1203
|
var ENV_VAR_RENAME = /CLAUDE_PROJECT_DIR/g;
|
|
1445
1204
|
function runCodexTransform(params) {
|
|
1446
|
-
const { harnessRoot, projectDir,
|
|
1447
|
-
const claudeMd = readRequired(
|
|
1448
|
-
const agentsTemplate = readRequired(
|
|
1449
|
-
const configTemplate = readRequired(
|
|
1205
|
+
const { harnessRoot, projectDir, selectedInternalSkills = [] } = params;
|
|
1206
|
+
const claudeMd = readRequired(join4(harnessRoot, "templates/CLAUDE.md"));
|
|
1207
|
+
const agentsTemplate = readRequired(join4(harnessRoot, "templates/codex/AGENTS.md.template"));
|
|
1208
|
+
const configTemplate = readRequired(join4(harnessRoot, "templates/codex/config.toml.template"));
|
|
1450
1209
|
const projectName = basename2(projectDir);
|
|
1451
|
-
const mcp = readOptionalJson(
|
|
1452
|
-
const agentsMdPath =
|
|
1210
|
+
const mcp = readOptionalJson(join4(harnessRoot, ".mcp.json"));
|
|
1211
|
+
const agentsMdPath = join4(projectDir, "AGENTS.md");
|
|
1453
1212
|
ensureDir(projectDir);
|
|
1454
|
-
|
|
1455
|
-
const configTomlPath =
|
|
1456
|
-
ensureDir(
|
|
1457
|
-
|
|
1213
|
+
writeFileSync3(agentsMdPath, renderAgentsMd({ template: agentsTemplate, claudeMd, projectName }));
|
|
1214
|
+
const configTomlPath = join4(projectDir, ".codex/config.toml");
|
|
1215
|
+
ensureDir(join4(projectDir, ".codex"));
|
|
1216
|
+
writeFileSync3(
|
|
1458
1217
|
configTomlPath,
|
|
1459
1218
|
renderConfigToml({
|
|
1460
1219
|
template: configTemplate,
|
|
@@ -1463,80 +1222,46 @@ function runCodexTransform(params) {
|
|
|
1463
1222
|
mcp
|
|
1464
1223
|
})
|
|
1465
1224
|
);
|
|
1466
|
-
const hookDir =
|
|
1225
|
+
const hookDir = join4(projectDir, ".codex/hooks");
|
|
1467
1226
|
ensureDir(hookDir);
|
|
1468
1227
|
const hookFiles = [];
|
|
1469
1228
|
for (const hook of HOOK_NAMES) {
|
|
1470
|
-
const src =
|
|
1471
|
-
if (!
|
|
1229
|
+
const src = join4(harnessRoot, "templates/hooks", `${hook}.sh`);
|
|
1230
|
+
if (!existsSync4(src)) {
|
|
1472
1231
|
continue;
|
|
1473
1232
|
}
|
|
1474
|
-
const ported =
|
|
1475
|
-
const target =
|
|
1476
|
-
|
|
1233
|
+
const ported = readFileSync4(src, "utf8").replace(ENV_VAR_RENAME, "CODEX_PROJECT_DIR");
|
|
1234
|
+
const target = join4(hookDir, `${hook}.sh`);
|
|
1235
|
+
writeFileSync3(target, ported);
|
|
1477
1236
|
chmodSync(target, 493);
|
|
1478
1237
|
hookFiles.push(target);
|
|
1479
1238
|
}
|
|
1480
1239
|
const skillFiles = [];
|
|
1481
|
-
if (withUzysHarness) {
|
|
1482
|
-
for (const phase of PHASES4) {
|
|
1483
|
-
const skillDir = join5(projectDir, ".agents", "skills", `uzys-${phase}`);
|
|
1484
|
-
ensureDir(skillDir);
|
|
1485
|
-
const cmdSrc = join5(harnessRoot, "templates/commands/uzys", `${phase}.md`);
|
|
1486
|
-
let source = "";
|
|
1487
|
-
if (existsSync6(cmdSrc)) {
|
|
1488
|
-
source = readFileSync6(cmdSrc, "utf8");
|
|
1489
|
-
} else {
|
|
1490
|
-
const fallback = join5(harnessRoot, "templates/codex/skills", `uzys-${phase}/SKILL.md`);
|
|
1491
|
-
if (existsSync6(fallback)) {
|
|
1492
|
-
source = readFileSync6(fallback, "utf8");
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
const target = join5(skillDir, "SKILL.md");
|
|
1496
|
-
writeFileSync5(target, renderSkill({ source, phase }));
|
|
1497
|
-
skillFiles.push(target);
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
1240
|
for (const id of selectedInternalSkills) {
|
|
1501
|
-
const src =
|
|
1502
|
-
if (!
|
|
1241
|
+
const src = join4(harnessRoot, "templates/skills", id, "SKILL.md");
|
|
1242
|
+
if (!existsSync4(src)) {
|
|
1503
1243
|
continue;
|
|
1504
1244
|
}
|
|
1505
|
-
const skillDir =
|
|
1245
|
+
const skillDir = join4(projectDir, ".agents", "skills", id);
|
|
1506
1246
|
ensureDir(skillDir);
|
|
1507
|
-
const target =
|
|
1508
|
-
|
|
1247
|
+
const target = join4(skillDir, "SKILL.md");
|
|
1248
|
+
writeFileSync3(target, renderBundledSkill(readFileSync4(src, "utf8")));
|
|
1509
1249
|
skillFiles.push(target);
|
|
1510
1250
|
}
|
|
1511
|
-
|
|
1512
|
-
if (withUzysHarness) {
|
|
1513
|
-
const promptDir = join5(projectDir, ".codex", "prompts");
|
|
1514
|
-
ensureDir(promptDir);
|
|
1515
|
-
for (const phase of PHASES4) {
|
|
1516
|
-
const cmdSrc = join5(harnessRoot, "templates/commands/uzys", `${phase}.md`);
|
|
1517
|
-
if (!existsSync6(cmdSrc)) {
|
|
1518
|
-
continue;
|
|
1519
|
-
}
|
|
1520
|
-
const source = readFileSync6(cmdSrc, "utf8");
|
|
1521
|
-
const target = join5(promptDir, `uzys-${phase}.md`);
|
|
1522
|
-
writeFileSync5(target, renderCodexPrompt({ source, phase }));
|
|
1523
|
-
promptFiles.push(target);
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
return { agentsMdPath, configTomlPath, hookFiles, skillFiles, promptFiles };
|
|
1251
|
+
return { agentsMdPath, configTomlPath, hookFiles, skillFiles };
|
|
1527
1252
|
}
|
|
1528
1253
|
function readRequired(path) {
|
|
1529
|
-
if (!
|
|
1254
|
+
if (!existsSync4(path)) {
|
|
1530
1255
|
throw new Error(`Codex transform: required source missing: ${path}`);
|
|
1531
1256
|
}
|
|
1532
|
-
return
|
|
1257
|
+
return readFileSync4(path, "utf8");
|
|
1533
1258
|
}
|
|
1534
1259
|
function readOptionalJson(path) {
|
|
1535
|
-
if (!
|
|
1260
|
+
if (!existsSync4(path)) {
|
|
1536
1261
|
return null;
|
|
1537
1262
|
}
|
|
1538
1263
|
try {
|
|
1539
|
-
return JSON.parse(
|
|
1264
|
+
return JSON.parse(readFileSync4(path, "utf8"));
|
|
1540
1265
|
} catch {
|
|
1541
1266
|
return null;
|
|
1542
1267
|
}
|
|
@@ -1544,8 +1269,8 @@ function readOptionalJson(path) {
|
|
|
1544
1269
|
|
|
1545
1270
|
// src/env-files.ts
|
|
1546
1271
|
init_esm_shims();
|
|
1547
|
-
import { appendFileSync, existsSync as
|
|
1548
|
-
import { join as
|
|
1272
|
+
import { appendFileSync, existsSync as existsSync5, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
1273
|
+
import { join as join5 } from "path";
|
|
1549
1274
|
var ENV_EXAMPLE_BODY = `# .env.example \u2014 csr-supabase Track
|
|
1550
1275
|
# Copy to .env (gitignored) and fill in values: cp .env.example .env
|
|
1551
1276
|
|
|
@@ -1582,19 +1307,19 @@ function writeEnvExample(projectDir, tracks) {
|
|
|
1582
1307
|
if (!tracks.some((t) => ENV_EXAMPLE_TRACKS.includes(t))) {
|
|
1583
1308
|
return false;
|
|
1584
1309
|
}
|
|
1585
|
-
const path =
|
|
1586
|
-
if (
|
|
1310
|
+
const path = join5(projectDir, ".env.example");
|
|
1311
|
+
if (existsSync5(path)) {
|
|
1587
1312
|
return false;
|
|
1588
1313
|
}
|
|
1589
|
-
|
|
1314
|
+
writeFileSync4(path, ENV_EXAMPLE_BODY);
|
|
1590
1315
|
return true;
|
|
1591
1316
|
}
|
|
1592
1317
|
function addGitignoreEnv(projectDir) {
|
|
1593
|
-
const path =
|
|
1594
|
-
if (!
|
|
1318
|
+
const path = join5(projectDir, ".gitignore");
|
|
1319
|
+
if (!existsSync5(path)) {
|
|
1595
1320
|
return false;
|
|
1596
1321
|
}
|
|
1597
|
-
const content =
|
|
1322
|
+
const content = readFileSync5(path, "utf8");
|
|
1598
1323
|
if (GITIGNORE_ENV_PATTERN.test(content)) {
|
|
1599
1324
|
return false;
|
|
1600
1325
|
}
|
|
@@ -1608,11 +1333,11 @@ function addGitignoreEnv(projectDir) {
|
|
|
1608
1333
|
var NPX_SKILLS_AGENT_DIRS = [".factory/", ".goose/"];
|
|
1609
1334
|
var GITIGNORE_NPX_SKILLS_HEADER = "# npx skills add multi-CLI cache (auto-added by agent-harness)";
|
|
1610
1335
|
function addGitignoreNpxSkillsAgents(projectDir) {
|
|
1611
|
-
const path =
|
|
1612
|
-
if (!
|
|
1336
|
+
const path = join5(projectDir, ".gitignore");
|
|
1337
|
+
if (!existsSync5(path)) {
|
|
1613
1338
|
return [];
|
|
1614
1339
|
}
|
|
1615
|
-
const content =
|
|
1340
|
+
const content = readFileSync5(path, "utf8");
|
|
1616
1341
|
const missing = NPX_SKILLS_AGENT_DIRS.filter((pattern) => {
|
|
1617
1342
|
const lineRegex = new RegExp(`^${pattern.replace(/\./g, "\\.").replace(/\//g, "/")}\\s*$`, "m");
|
|
1618
1343
|
return !lineRegex.test(content);
|
|
@@ -1628,17 +1353,17 @@ ${block}
|
|
|
1628
1353
|
return [...missing];
|
|
1629
1354
|
}
|
|
1630
1355
|
function writeMcpAllowlist(projectDir) {
|
|
1631
|
-
const allowlistPath =
|
|
1632
|
-
if (
|
|
1356
|
+
const allowlistPath = join5(projectDir, ".mcp-allowlist");
|
|
1357
|
+
if (existsSync5(allowlistPath)) {
|
|
1633
1358
|
return null;
|
|
1634
1359
|
}
|
|
1635
|
-
const mcpPath =
|
|
1636
|
-
if (!
|
|
1360
|
+
const mcpPath = join5(projectDir, ".mcp.json");
|
|
1361
|
+
if (!existsSync5(mcpPath)) {
|
|
1637
1362
|
return null;
|
|
1638
1363
|
}
|
|
1639
1364
|
let names = [];
|
|
1640
1365
|
try {
|
|
1641
|
-
const parsed = JSON.parse(
|
|
1366
|
+
const parsed = JSON.parse(readFileSync5(mcpPath, "utf8"));
|
|
1642
1367
|
names = Object.keys(parsed.mcpServers ?? {}).sort();
|
|
1643
1368
|
} catch {
|
|
1644
1369
|
return null;
|
|
@@ -1654,16 +1379,16 @@ function writeMcpAllowlist(projectDir) {
|
|
|
1654
1379
|
...names,
|
|
1655
1380
|
""
|
|
1656
1381
|
].join("\n");
|
|
1657
|
-
|
|
1382
|
+
writeFileSync4(allowlistPath, body);
|
|
1658
1383
|
return names;
|
|
1659
1384
|
}
|
|
1660
1385
|
|
|
1661
1386
|
// src/external-installer.ts
|
|
1662
1387
|
init_esm_shims();
|
|
1663
1388
|
import { spawnSync } from "child_process";
|
|
1664
|
-
import { existsSync as
|
|
1665
|
-
import { homedir as
|
|
1666
|
-
import { join as
|
|
1389
|
+
import { existsSync as existsSync6, readdirSync, readFileSync as readFileSync6 } from "fs";
|
|
1390
|
+
import { homedir as homedir2 } from "os";
|
|
1391
|
+
import { join as join6 } from "path";
|
|
1667
1392
|
var DEFAULT_SPAWN_TIMEOUT_MS = 12e4;
|
|
1668
1393
|
function runExternalInstall(ctx, deps = {}) {
|
|
1669
1394
|
const log = deps.log ?? console.log;
|
|
@@ -1731,8 +1456,8 @@ function installOne(asset, ctx) {
|
|
|
1731
1456
|
cwd
|
|
1732
1457
|
);
|
|
1733
1458
|
case "shell-script": {
|
|
1734
|
-
const scriptPath =
|
|
1735
|
-
if (!
|
|
1459
|
+
const scriptPath = join6(ctx.harnessRoot, method.script);
|
|
1460
|
+
if (!existsSync6(scriptPath)) {
|
|
1736
1461
|
return {
|
|
1737
1462
|
asset,
|
|
1738
1463
|
ok: false,
|
|
@@ -1822,17 +1547,17 @@ function detectVersion(method, spawn) {
|
|
|
1822
1547
|
if (at2 <= 0) return void 0;
|
|
1823
1548
|
const plugin = method.pluginId.slice(0, at2);
|
|
1824
1549
|
const marketplaceShort = method.pluginId.slice(at2 + 1);
|
|
1825
|
-
const cacheBase =
|
|
1826
|
-
if (!
|
|
1827
|
-
const versions =
|
|
1550
|
+
const cacheBase = join6(homedir2(), ".claude/plugins/cache", marketplaceShort, plugin);
|
|
1551
|
+
if (!existsSync6(cacheBase)) return void 0;
|
|
1552
|
+
const versions = readdirSync(cacheBase).filter((v2) => /^\d/.test(v2)).sort();
|
|
1828
1553
|
return versions.at(-1);
|
|
1829
1554
|
}
|
|
1830
1555
|
case "npm": {
|
|
1831
1556
|
const npmRoot = getNpmGlobalRoot(spawn);
|
|
1832
1557
|
if (!npmRoot) return void 0;
|
|
1833
|
-
const pkgJson =
|
|
1834
|
-
if (!
|
|
1835
|
-
const parsed = JSON.parse(
|
|
1558
|
+
const pkgJson = join6(npmRoot, method.pkg, "package.json");
|
|
1559
|
+
if (!existsSync6(pkgJson)) return void 0;
|
|
1560
|
+
const parsed = JSON.parse(readFileSync6(pkgJson, "utf8"));
|
|
1836
1561
|
return parsed.version;
|
|
1837
1562
|
}
|
|
1838
1563
|
default:
|
|
@@ -1860,8 +1585,8 @@ function getNpmGlobalRoot(spawn) {
|
|
|
1860
1585
|
// src/install-log.ts
|
|
1861
1586
|
init_esm_shims();
|
|
1862
1587
|
import { createHash } from "crypto";
|
|
1863
|
-
import { existsSync as
|
|
1864
|
-
import { dirname as dirname3, join as
|
|
1588
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
1589
|
+
import { dirname as dirname3, join as join7 } from "path";
|
|
1865
1590
|
var INSTALL_LOG_FILENAME = ".harness-install.json";
|
|
1866
1591
|
var INSTALL_LOG_VERSION = 1;
|
|
1867
1592
|
function buildAssetEntries(report, scope) {
|
|
@@ -1919,17 +1644,17 @@ function hashContent(content) {
|
|
|
1919
1644
|
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
1920
1645
|
}
|
|
1921
1646
|
function writeInstallLog(projectDir, log) {
|
|
1922
|
-
const path =
|
|
1923
|
-
|
|
1924
|
-
|
|
1647
|
+
const path = join7(projectDir, ".claude", INSTALL_LOG_FILENAME);
|
|
1648
|
+
mkdirSync3(dirname3(path), { recursive: true });
|
|
1649
|
+
writeFileSync5(path, `${JSON.stringify(log, null, 2)}
|
|
1925
1650
|
`, "utf8");
|
|
1926
1651
|
return path;
|
|
1927
1652
|
}
|
|
1928
1653
|
function readInstallLog(projectDir) {
|
|
1929
|
-
const path =
|
|
1930
|
-
if (!
|
|
1654
|
+
const path = join7(projectDir, ".claude", INSTALL_LOG_FILENAME);
|
|
1655
|
+
if (!existsSync7(path)) return null;
|
|
1931
1656
|
try {
|
|
1932
|
-
const parsed = JSON.parse(
|
|
1657
|
+
const parsed = JSON.parse(readFileSync7(path, "utf8"));
|
|
1933
1658
|
if (Array.isArray(parsed.assets)) {
|
|
1934
1659
|
parsed.assets = parsed.assets.map(
|
|
1935
1660
|
(a) => a.method === "npm-global" ? { ...a, method: "npm" } : a
|
|
@@ -1941,7 +1666,7 @@ function readInstallLog(projectDir) {
|
|
|
1941
1666
|
}
|
|
1942
1667
|
}
|
|
1943
1668
|
function installLogPath(projectDir) {
|
|
1944
|
-
return
|
|
1669
|
+
return join7(projectDir, ".claude", INSTALL_LOG_FILENAME);
|
|
1945
1670
|
}
|
|
1946
1671
|
|
|
1947
1672
|
// src/manifest.ts
|
|
@@ -1998,7 +1723,6 @@ function resolveRules(spec) {
|
|
|
1998
1723
|
}
|
|
1999
1724
|
return [...set].sort();
|
|
2000
1725
|
}
|
|
2001
|
-
var UZYS_COMMANDS = ["spec", "plan", "build", "test", "review", "ship", "auto"];
|
|
2002
1726
|
var CORE_AGENTS = ["reviewer", "data-analyst", "strategist"];
|
|
2003
1727
|
var CORE_AGENTS_ECC = ["code-reviewer", "security-reviewer"];
|
|
2004
1728
|
var DEV_AGENTS = ["plan-checker"];
|
|
@@ -2006,8 +1730,6 @@ var DEV_AGENTS_ECC = ["silent-failure-hunter", "build-error-resolver"];
|
|
|
2006
1730
|
var ALWAYS_HOOKS = [
|
|
2007
1731
|
"session-start.sh",
|
|
2008
1732
|
"protect-files.sh",
|
|
2009
|
-
"gate-check.sh",
|
|
2010
|
-
"agentshield-gate.sh",
|
|
2011
1733
|
"mcp-pre-exec.sh",
|
|
2012
1734
|
"spec-drift-check.sh",
|
|
2013
1735
|
"checkpoint-snapshot.sh",
|
|
@@ -2031,14 +1753,6 @@ function buildManifest(spec) {
|
|
|
2031
1753
|
applies: all
|
|
2032
1754
|
});
|
|
2033
1755
|
}
|
|
2034
|
-
for (const cmd of UZYS_COMMANDS) {
|
|
2035
|
-
m.push({
|
|
2036
|
-
source: `commands/uzys/${cmd}.md`,
|
|
2037
|
-
target: `.claude/commands/uzys/${cmd}.md`,
|
|
2038
|
-
type: "file",
|
|
2039
|
-
applies: (s) => Boolean(s.withUzysHarness)
|
|
2040
|
-
});
|
|
2041
|
-
}
|
|
2042
1756
|
m.push({
|
|
2043
1757
|
source: "commands/ecc",
|
|
2044
1758
|
target: ".claude/commands/ecc",
|
|
@@ -2200,7 +1914,7 @@ function buildManifest(spec) {
|
|
|
2200
1914
|
|
|
2201
1915
|
// src/mcp-merge.ts
|
|
2202
1916
|
init_esm_shims();
|
|
2203
|
-
import { existsSync as
|
|
1917
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "fs";
|
|
2204
1918
|
function parseTrackMcpMap(raw) {
|
|
2205
1919
|
const rows = [];
|
|
2206
1920
|
for (const line of raw.split(/\r?\n/)) {
|
|
@@ -2251,15 +1965,15 @@ function mergeMcpServers(base, rows, tracks) {
|
|
|
2251
1965
|
return out;
|
|
2252
1966
|
}
|
|
2253
1967
|
function composeMcpJson(opts) {
|
|
2254
|
-
const base = JSON.parse(
|
|
2255
|
-
const merged = opts.existingPath &&
|
|
2256
|
-
const mapRaw =
|
|
1968
|
+
const base = JSON.parse(readFileSync8(opts.templateMcpPath, "utf8"));
|
|
1969
|
+
const merged = opts.existingPath && existsSync8(opts.existingPath) ? mergeUserBase(base, opts.existingPath) : base;
|
|
1970
|
+
const mapRaw = existsSync8(opts.trackMapPath) ? readFileSync8(opts.trackMapPath, "utf8") : "";
|
|
2257
1971
|
const rows = parseTrackMcpMap(mapRaw);
|
|
2258
1972
|
return mergeMcpServers(merged, rows, opts.tracks);
|
|
2259
1973
|
}
|
|
2260
1974
|
function mergeUserBase(base, existingPath) {
|
|
2261
1975
|
try {
|
|
2262
|
-
const existing = JSON.parse(
|
|
1976
|
+
const existing = JSON.parse(readFileSync8(existingPath, "utf8"));
|
|
2263
1977
|
return {
|
|
2264
1978
|
...base,
|
|
2265
1979
|
mcpServers: { ...base.mcpServers, ...existing.mcpServers }
|
|
@@ -2269,14 +1983,14 @@ function mergeUserBase(base, existingPath) {
|
|
|
2269
1983
|
}
|
|
2270
1984
|
}
|
|
2271
1985
|
function writeMcpJson(path, mcp) {
|
|
2272
|
-
|
|
1986
|
+
writeFileSync6(path, `${JSON.stringify(mcp, null, 2)}
|
|
2273
1987
|
`);
|
|
2274
1988
|
}
|
|
2275
1989
|
|
|
2276
1990
|
// src/opencode/transform.ts
|
|
2277
1991
|
init_esm_shims();
|
|
2278
|
-
import {
|
|
2279
|
-
import { basename as basename3, join as
|
|
1992
|
+
import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
1993
|
+
import { basename as basename3, join as join8 } from "path";
|
|
2280
1994
|
|
|
2281
1995
|
// src/opencode/agents-md.ts
|
|
2282
1996
|
init_esm_shims();
|
|
@@ -2291,30 +2005,6 @@ function renderAgentsMd2(params) {
|
|
|
2291
2005
|
|
|
2292
2006
|
// src/opencode/commands.ts
|
|
2293
2007
|
init_esm_shims();
|
|
2294
|
-
var AGENT_BY_PHASE = {
|
|
2295
|
-
spec: "plan",
|
|
2296
|
-
plan: "plan",
|
|
2297
|
-
build: "build",
|
|
2298
|
-
test: "build",
|
|
2299
|
-
review: "plan",
|
|
2300
|
-
ship: "build"
|
|
2301
|
-
};
|
|
2302
|
-
function renderCommand(params) {
|
|
2303
|
-
const { description, body } = parseSource3(params.source);
|
|
2304
|
-
const finalDescription = description || `uzys-${params.phase} phase command (OpenCode \uD3EC\uD305)`;
|
|
2305
|
-
const escapedDesc = finalDescription.replace(/"/g, '\\"');
|
|
2306
|
-
const agent = AGENT_BY_PHASE[params.phase] ?? "build";
|
|
2307
|
-
const renamedBody = renameSlashes2(body).trimEnd();
|
|
2308
|
-
return [
|
|
2309
|
-
"---",
|
|
2310
|
-
`description: "${escapedDesc}"`,
|
|
2311
|
-
`agent: ${agent}`,
|
|
2312
|
-
"---",
|
|
2313
|
-
"",
|
|
2314
|
-
renamedBody,
|
|
2315
|
-
""
|
|
2316
|
-
].join("\n");
|
|
2317
|
-
}
|
|
2318
2008
|
function renderCommandFromSkill(source, id) {
|
|
2319
2009
|
const { description, body } = parseSkillFrontmatter(source);
|
|
2320
2010
|
const finalDescription = description || `${id} (dev-method skill, OpenCode command fallback)`;
|
|
@@ -2358,36 +2048,13 @@ function parseSkillFrontmatter(source) {
|
|
|
2358
2048
|
}
|
|
2359
2049
|
description = collected.join(" ").replace(/\s+/g, " ").trim();
|
|
2360
2050
|
} else {
|
|
2361
|
-
description =
|
|
2051
|
+
description = stripQuotes(raw);
|
|
2362
2052
|
}
|
|
2363
2053
|
}
|
|
2364
2054
|
const body = secondDelimAt >= 0 ? lines.slice(secondDelimAt + 1).join("\n").replace(/^\n+/, "") : source;
|
|
2365
2055
|
return { description, body };
|
|
2366
2056
|
}
|
|
2367
|
-
function
|
|
2368
|
-
const lines = source.split(/\r?\n/);
|
|
2369
|
-
if (lines[0] === "---") {
|
|
2370
|
-
let descMatch = "";
|
|
2371
|
-
let secondDelimAt = -1;
|
|
2372
|
-
for (let i = 1; i < lines.length; i++) {
|
|
2373
|
-
const line = lines[i] ?? "";
|
|
2374
|
-
if (line === "---") {
|
|
2375
|
-
secondDelimAt = i;
|
|
2376
|
-
break;
|
|
2377
|
-
}
|
|
2378
|
-
const match = line.match(/^description:\s*(.*)$/);
|
|
2379
|
-
if (match) {
|
|
2380
|
-
descMatch = stripQuotes2(match[1] ?? "");
|
|
2381
|
-
}
|
|
2382
|
-
}
|
|
2383
|
-
const body2 = secondDelimAt >= 0 ? lines.slice(secondDelimAt + 1).join("\n") : source;
|
|
2384
|
-
return { description: descMatch, body: body2.replace(/^\n+/, "") };
|
|
2385
|
-
}
|
|
2386
|
-
const firstLine = lines[0] ?? "";
|
|
2387
|
-
const body = lines.slice(1).join("\n");
|
|
2388
|
-
return { description: firstLine.trim(), body };
|
|
2389
|
-
}
|
|
2390
|
-
function stripQuotes2(raw) {
|
|
2057
|
+
function stripQuotes(raw) {
|
|
2391
2058
|
const trimmed = raw.trim();
|
|
2392
2059
|
if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
2393
2060
|
return trimmed.slice(1, -1);
|
|
@@ -2415,75 +2082,46 @@ function parseTemplate(template) {
|
|
|
2415
2082
|
}
|
|
2416
2083
|
|
|
2417
2084
|
// src/opencode/transform.ts
|
|
2418
|
-
var PHASES5 = ["spec", "plan", "build", "test", "review", "ship"];
|
|
2419
2085
|
function runOpencodeTransform(params) {
|
|
2420
2086
|
const { harnessRoot, projectDir, selectedInternalSkills = [] } = params;
|
|
2421
|
-
const claudeMd = readRequired2(
|
|
2422
|
-
const agentsTemplate = readRequired2(
|
|
2087
|
+
const claudeMd = readRequired2(join8(harnessRoot, "templates/CLAUDE.md"));
|
|
2088
|
+
const agentsTemplate = readRequired2(join8(harnessRoot, "templates/opencode/AGENTS.md.template"));
|
|
2423
2089
|
const opencodeTemplate = readRequired2(
|
|
2424
|
-
|
|
2090
|
+
join8(harnessRoot, "templates/opencode/opencode.json.template")
|
|
2425
2091
|
);
|
|
2426
2092
|
const projectName = basename3(projectDir);
|
|
2427
|
-
const mcp = readOptionalJson2(
|
|
2093
|
+
const mcp = readOptionalJson2(join8(harnessRoot, ".mcp.json"));
|
|
2428
2094
|
ensureDir(projectDir);
|
|
2429
|
-
const agentsMdPath =
|
|
2430
|
-
|
|
2431
|
-
const opencodeJsonPath =
|
|
2432
|
-
|
|
2433
|
-
const cmdDir =
|
|
2095
|
+
const agentsMdPath = join8(projectDir, "AGENTS.md");
|
|
2096
|
+
writeFileSync7(agentsMdPath, renderAgentsMd2({ template: agentsTemplate, claudeMd, projectName }));
|
|
2097
|
+
const opencodeJsonPath = join8(projectDir, "opencode.json");
|
|
2098
|
+
writeFileSync7(opencodeJsonPath, renderOpencodeJson({ template: opencodeTemplate, mcp }));
|
|
2099
|
+
const cmdDir = join8(projectDir, ".opencode/commands");
|
|
2434
2100
|
ensureDir(cmdDir);
|
|
2435
2101
|
const commandFiles = [];
|
|
2436
|
-
for (const phase of PHASES5) {
|
|
2437
|
-
const cmdSrc = join9(harnessRoot, "templates/commands/uzys", `${phase}.md`);
|
|
2438
|
-
let source = "";
|
|
2439
|
-
if (existsSync11(cmdSrc)) {
|
|
2440
|
-
source = readFileSync11(cmdSrc, "utf8");
|
|
2441
|
-
} else {
|
|
2442
|
-
const fallback = join9(
|
|
2443
|
-
harnessRoot,
|
|
2444
|
-
"templates/opencode/.opencode/commands",
|
|
2445
|
-
`uzys-${phase}.md`
|
|
2446
|
-
);
|
|
2447
|
-
if (existsSync11(fallback)) {
|
|
2448
|
-
source = readFileSync11(fallback, "utf8");
|
|
2449
|
-
}
|
|
2450
|
-
}
|
|
2451
|
-
const target = join9(cmdDir, `uzys-${phase}.md`);
|
|
2452
|
-
writeFileSync9(target, renderCommand({ source, phase }));
|
|
2453
|
-
commandFiles.push(target);
|
|
2454
|
-
}
|
|
2455
2102
|
for (const id of selectedInternalSkills) {
|
|
2456
|
-
const src =
|
|
2457
|
-
if (!
|
|
2103
|
+
const src = join8(harnessRoot, "templates/skills", id, "SKILL.md");
|
|
2104
|
+
if (!existsSync9(src)) {
|
|
2458
2105
|
continue;
|
|
2459
2106
|
}
|
|
2460
|
-
const target =
|
|
2461
|
-
|
|
2107
|
+
const target = join8(cmdDir, `${id}.md`);
|
|
2108
|
+
writeFileSync7(target, renderCommandFromSkill(readFileSync9(src, "utf8"), id));
|
|
2462
2109
|
commandFiles.push(target);
|
|
2463
2110
|
}
|
|
2464
|
-
|
|
2465
|
-
ensureDir(pluginDir);
|
|
2466
|
-
const pluginPath = join9(pluginDir, "uzys-harness.ts");
|
|
2467
|
-
const pluginSrc = join9(harnessRoot, "templates/opencode/.opencode/plugins/uzys-harness.ts");
|
|
2468
|
-
if (existsSync11(pluginSrc)) {
|
|
2469
|
-
copyFileSync2(pluginSrc, pluginPath);
|
|
2470
|
-
} else {
|
|
2471
|
-
writeFileSync9(pluginPath, "// uzys-harness plugin stub (template missing)\n");
|
|
2472
|
-
}
|
|
2473
|
-
return { agentsMdPath, opencodeJsonPath, commandFiles, pluginPath };
|
|
2111
|
+
return { agentsMdPath, opencodeJsonPath, commandFiles };
|
|
2474
2112
|
}
|
|
2475
2113
|
function readRequired2(path) {
|
|
2476
|
-
if (!
|
|
2114
|
+
if (!existsSync9(path)) {
|
|
2477
2115
|
throw new Error(`OpenCode transform: required source missing: ${path}`);
|
|
2478
2116
|
}
|
|
2479
|
-
return
|
|
2117
|
+
return readFileSync9(path, "utf8");
|
|
2480
2118
|
}
|
|
2481
2119
|
function readOptionalJson2(path) {
|
|
2482
|
-
if (!
|
|
2120
|
+
if (!existsSync9(path)) {
|
|
2483
2121
|
return null;
|
|
2484
2122
|
}
|
|
2485
2123
|
try {
|
|
2486
|
-
return JSON.parse(
|
|
2124
|
+
return JSON.parse(readFileSync9(path, "utf8"));
|
|
2487
2125
|
} catch {
|
|
2488
2126
|
return null;
|
|
2489
2127
|
}
|
|
@@ -2491,8 +2129,8 @@ function readOptionalJson2(path) {
|
|
|
2491
2129
|
|
|
2492
2130
|
// src/project-claude-merge.ts
|
|
2493
2131
|
init_esm_shims();
|
|
2494
|
-
import { existsSync as
|
|
2495
|
-
import { join as
|
|
2132
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
2133
|
+
import { join as join9 } from "path";
|
|
2496
2134
|
var SECTIONS = [
|
|
2497
2135
|
"stack",
|
|
2498
2136
|
"workflow",
|
|
@@ -2542,7 +2180,7 @@ var FULL_EXPANSION = [
|
|
|
2542
2180
|
];
|
|
2543
2181
|
function mergeProjectClaude(tracks, opts) {
|
|
2544
2182
|
const expanded = expandTracks(tracks);
|
|
2545
|
-
const baseRaw =
|
|
2183
|
+
const baseRaw = readFileSync10(join9(opts.baseDir, "_base.md"), "utf8");
|
|
2546
2184
|
let output = baseRaw;
|
|
2547
2185
|
output = output.replace("<!-- INSERT: track-list -->", trackList(expanded));
|
|
2548
2186
|
output = output.replace("<!-- INSERT: tagline -->", taglineList(expanded, opts.baseDir));
|
|
@@ -2570,11 +2208,11 @@ function trackList(tracks) {
|
|
|
2570
2208
|
function taglineList(tracks, baseDir) {
|
|
2571
2209
|
const taglines = [];
|
|
2572
2210
|
for (const t of tracks) {
|
|
2573
|
-
const path =
|
|
2574
|
-
if (!
|
|
2211
|
+
const path = join9(baseDir, "fragments", t, "tagline.md");
|
|
2212
|
+
if (!existsSync10(path)) {
|
|
2575
2213
|
continue;
|
|
2576
2214
|
}
|
|
2577
|
-
const body =
|
|
2215
|
+
const body = readFileSync10(path, "utf8").trim();
|
|
2578
2216
|
if (body) {
|
|
2579
2217
|
taglines.push(body);
|
|
2580
2218
|
}
|
|
@@ -2584,11 +2222,11 @@ function taglineList(tracks, baseDir) {
|
|
|
2584
2222
|
function renderSection(section, tracks, baseDir) {
|
|
2585
2223
|
const present = [];
|
|
2586
2224
|
for (const t of tracks) {
|
|
2587
|
-
const path =
|
|
2588
|
-
if (!
|
|
2225
|
+
const path = join9(baseDir, "fragments", t, `${section}.md`);
|
|
2226
|
+
if (!existsSync10(path)) {
|
|
2589
2227
|
continue;
|
|
2590
2228
|
}
|
|
2591
|
-
const body =
|
|
2229
|
+
const body = readFileSync10(path, "utf8").trim();
|
|
2592
2230
|
if (body) {
|
|
2593
2231
|
present.push({ track: t, body });
|
|
2594
2232
|
}
|
|
@@ -2644,16 +2282,16 @@ function addPreToolUseHook(settings, matcher, command) {
|
|
|
2644
2282
|
// src/update-mode.ts
|
|
2645
2283
|
init_esm_shims();
|
|
2646
2284
|
import {
|
|
2647
|
-
copyFileSync as
|
|
2648
|
-
existsSync as
|
|
2649
|
-
readdirSync as
|
|
2650
|
-
readFileSync as
|
|
2285
|
+
copyFileSync as copyFileSync2,
|
|
2286
|
+
existsSync as existsSync11,
|
|
2287
|
+
readdirSync as readdirSync2,
|
|
2288
|
+
readFileSync as readFileSync11,
|
|
2651
2289
|
unlinkSync,
|
|
2652
|
-
writeFileSync as
|
|
2290
|
+
writeFileSync as writeFileSync8
|
|
2653
2291
|
} from "fs";
|
|
2654
|
-
import { join as
|
|
2292
|
+
import { join as join10 } from "path";
|
|
2655
2293
|
function runUpdateMode(projectDir, templatesDir) {
|
|
2656
|
-
const claudeDir =
|
|
2294
|
+
const claudeDir = join10(projectDir, ".claude");
|
|
2657
2295
|
const report = {
|
|
2658
2296
|
updated: {},
|
|
2659
2297
|
pruned: {},
|
|
@@ -2662,26 +2300,26 @@ function runUpdateMode(projectDir, templatesDir) {
|
|
|
2662
2300
|
};
|
|
2663
2301
|
const targets = [
|
|
2664
2302
|
{
|
|
2665
|
-
target:
|
|
2666
|
-
source:
|
|
2303
|
+
target: join10(claudeDir, "rules"),
|
|
2304
|
+
source: join10(templatesDir, "rules"),
|
|
2667
2305
|
pattern: ".md",
|
|
2668
2306
|
label: ".claude/rules"
|
|
2669
2307
|
},
|
|
2670
2308
|
{
|
|
2671
|
-
target:
|
|
2672
|
-
source:
|
|
2309
|
+
target: join10(claudeDir, "agents"),
|
|
2310
|
+
source: join10(templatesDir, "agents"),
|
|
2673
2311
|
pattern: ".md",
|
|
2674
2312
|
label: ".claude/agents"
|
|
2675
2313
|
},
|
|
2676
2314
|
{
|
|
2677
|
-
target:
|
|
2678
|
-
source:
|
|
2315
|
+
target: join10(claudeDir, "commands/uzys"),
|
|
2316
|
+
source: join10(templatesDir, "commands/uzys"),
|
|
2679
2317
|
pattern: ".md",
|
|
2680
2318
|
label: ".claude/commands/uzys"
|
|
2681
2319
|
},
|
|
2682
2320
|
{
|
|
2683
|
-
target:
|
|
2684
|
-
source:
|
|
2321
|
+
target: join10(claudeDir, "hooks"),
|
|
2322
|
+
source: join10(templatesDir, "hooks"),
|
|
2685
2323
|
pattern: ".sh",
|
|
2686
2324
|
label: ".claude/hooks"
|
|
2687
2325
|
}
|
|
@@ -2690,40 +2328,40 @@ function runUpdateMode(projectDir, templatesDir) {
|
|
|
2690
2328
|
report.updated[t.label] = updateDir(t.target, t.source, t.pattern);
|
|
2691
2329
|
report.pruned[t.label] = pruneOrphans(t.target, t.source, t.pattern);
|
|
2692
2330
|
}
|
|
2693
|
-
const claudeMd =
|
|
2694
|
-
const templateMd =
|
|
2695
|
-
if (
|
|
2696
|
-
|
|
2331
|
+
const claudeMd = join10(claudeDir, "CLAUDE.md");
|
|
2332
|
+
const templateMd = join10(templatesDir, "CLAUDE.md");
|
|
2333
|
+
if (existsSync11(claudeMd) && existsSync11(templateMd)) {
|
|
2334
|
+
copyFileSync2(templateMd, claudeMd);
|
|
2697
2335
|
report.claudeMdUpdated = true;
|
|
2698
2336
|
}
|
|
2699
|
-
const settingsPath =
|
|
2700
|
-
if (
|
|
2701
|
-
report.staleHookRefs = cleanStaleHookRefs(settingsPath,
|
|
2337
|
+
const settingsPath = join10(claudeDir, "settings.json");
|
|
2338
|
+
if (existsSync11(settingsPath)) {
|
|
2339
|
+
report.staleHookRefs = cleanStaleHookRefs(settingsPath, join10(claudeDir, "hooks"));
|
|
2702
2340
|
}
|
|
2703
2341
|
return report;
|
|
2704
2342
|
}
|
|
2705
2343
|
function updateDir(target, source, ext) {
|
|
2706
|
-
if (!
|
|
2344
|
+
if (!existsSync11(target) || !existsSync11(source)) return 0;
|
|
2707
2345
|
let count = 0;
|
|
2708
|
-
for (const file of
|
|
2346
|
+
for (const file of readdirSync2(target)) {
|
|
2709
2347
|
if (!file.endsWith(ext)) continue;
|
|
2710
|
-
const targetFile =
|
|
2711
|
-
const sourceFile =
|
|
2712
|
-
if (
|
|
2713
|
-
|
|
2348
|
+
const targetFile = join10(target, file);
|
|
2349
|
+
const sourceFile = join10(source, file);
|
|
2350
|
+
if (existsSync11(sourceFile)) {
|
|
2351
|
+
copyFileSync2(sourceFile, targetFile);
|
|
2714
2352
|
count++;
|
|
2715
2353
|
}
|
|
2716
2354
|
}
|
|
2717
2355
|
return count;
|
|
2718
2356
|
}
|
|
2719
2357
|
function pruneOrphans(target, source, ext) {
|
|
2720
|
-
if (!
|
|
2358
|
+
if (!existsSync11(target) || !existsSync11(source)) return [];
|
|
2721
2359
|
const removed = [];
|
|
2722
|
-
for (const file of
|
|
2360
|
+
for (const file of readdirSync2(target)) {
|
|
2723
2361
|
if (!file.endsWith(ext)) continue;
|
|
2724
|
-
const sourceFile =
|
|
2725
|
-
if (!
|
|
2726
|
-
const targetFile =
|
|
2362
|
+
const sourceFile = join10(source, file);
|
|
2363
|
+
if (!existsSync11(sourceFile)) {
|
|
2364
|
+
const targetFile = join10(target, file);
|
|
2727
2365
|
try {
|
|
2728
2366
|
unlinkSync(targetFile);
|
|
2729
2367
|
removed.push(file);
|
|
@@ -2736,7 +2374,7 @@ function pruneOrphans(target, source, ext) {
|
|
|
2736
2374
|
function cleanStaleHookRefs(settingsPath, hooksDir) {
|
|
2737
2375
|
let settings;
|
|
2738
2376
|
try {
|
|
2739
|
-
settings = JSON.parse(
|
|
2377
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
2740
2378
|
} catch {
|
|
2741
2379
|
return [];
|
|
2742
2380
|
}
|
|
@@ -2755,7 +2393,7 @@ function cleanStaleHookRefs(settingsPath, hooksDir) {
|
|
|
2755
2393
|
}
|
|
2756
2394
|
if (removed.length > 0) {
|
|
2757
2395
|
const next = { ...settings, hooks: cleanedHooks };
|
|
2758
|
-
|
|
2396
|
+
writeFileSync8(settingsPath, `${JSON.stringify(next, null, 2)}
|
|
2759
2397
|
`);
|
|
2760
2398
|
}
|
|
2761
2399
|
return removed;
|
|
@@ -2764,7 +2402,7 @@ function keepHookRef(hook, hooksDir, removed) {
|
|
|
2764
2402
|
const refMatch = (hook?.command ?? "").match(/\/\.claude\/hooks\/([^"\s/]+\.sh)/);
|
|
2765
2403
|
if (!refMatch?.[1]) return true;
|
|
2766
2404
|
const fname = refMatch[1];
|
|
2767
|
-
const exists =
|
|
2405
|
+
const exists = existsSync11(join10(hooksDir, fname));
|
|
2768
2406
|
if (!exists && !removed.includes(fname)) removed.push(fname);
|
|
2769
2407
|
return exists;
|
|
2770
2408
|
}
|
|
@@ -2775,12 +2413,12 @@ var KARPATHY_ASSET_ID = "karpathy-coder";
|
|
|
2775
2413
|
function runInstall(ctx) {
|
|
2776
2414
|
const { harnessRoot, projectDir, spec } = ctx;
|
|
2777
2415
|
const mode = ctx.mode ?? "fresh";
|
|
2778
|
-
const templatesDir =
|
|
2779
|
-
if (!
|
|
2416
|
+
const templatesDir = join11(harnessRoot, "templates");
|
|
2417
|
+
if (!existsSync12(templatesDir)) {
|
|
2780
2418
|
throw new Error(`Templates dir not found: ${templatesDir}`);
|
|
2781
2419
|
}
|
|
2782
|
-
const claudeDir =
|
|
2783
|
-
if (mode === "update" && !
|
|
2420
|
+
const claudeDir = join11(projectDir, ".claude");
|
|
2421
|
+
if (mode === "update" && !existsSync12(claudeDir)) {
|
|
2784
2422
|
throw new Error(`Update mode requires existing .claude/ at ${claudeDir}`);
|
|
2785
2423
|
}
|
|
2786
2424
|
const backupPath = resolveBackupPath(ctx, mode, claudeDir);
|
|
@@ -2797,13 +2435,7 @@ function runInstall(ctx) {
|
|
|
2797
2435
|
backup: backupPath,
|
|
2798
2436
|
installedTracks: [...spec.tracks].sort(),
|
|
2799
2437
|
mcpServers: Object.keys(mcpResult.mcpServers).sort(),
|
|
2800
|
-
...runCliTransforms(
|
|
2801
|
-
spec,
|
|
2802
|
-
harnessRoot,
|
|
2803
|
-
projectDir,
|
|
2804
|
-
manifestSpec.withUzysHarness,
|
|
2805
|
-
manifestSpec.selectedInternalSkills
|
|
2806
|
-
),
|
|
2438
|
+
...runCliTransforms(spec, harnessRoot, projectDir, manifestSpec.selectedInternalSkills),
|
|
2807
2439
|
updateMode: null,
|
|
2808
2440
|
mode,
|
|
2809
2441
|
envFiles: writeEnvironmentFiles(projectDir, spec.tracks),
|
|
@@ -2835,7 +2467,6 @@ function runUpdateInstall(ctx, templatesDir, backupPath) {
|
|
|
2835
2467
|
codexOptIn: null,
|
|
2836
2468
|
opencode: null,
|
|
2837
2469
|
antigravity: null,
|
|
2838
|
-
antigravityOptIn: null,
|
|
2839
2470
|
updateMode: updateReport,
|
|
2840
2471
|
mode: "update",
|
|
2841
2472
|
envFiles: {
|
|
@@ -2858,7 +2489,6 @@ function buildManifestSpec(spec) {
|
|
|
2858
2489
|
return {
|
|
2859
2490
|
tracks: spec.tracks,
|
|
2860
2491
|
withTauri: isAssetSelected("tauri-desktop", selectionCtx),
|
|
2861
|
-
withUzysHarness: isAssetSelected("uzys-harness", selectionCtx),
|
|
2862
2492
|
// v26.55.0 — withEcc gating (ADR-016). ECC cherry-pick (agents/skills/commands) 항목 토글.
|
|
2863
2493
|
// withPrune 은 ecc-plugin 사용을 전제 (이전 applyOptionRules `withEcc ||= withPrune` 의미 보존).
|
|
2864
2494
|
withEcc: isAssetSelected("ecc-plugin", selectionCtx) || spec.options.withPrune,
|
|
@@ -2886,15 +2516,15 @@ function installClaudeBaseline(manifestSpec, harnessRoot, projectDir, templatesD
|
|
|
2886
2516
|
if (!entry.applies(manifestSpec)) {
|
|
2887
2517
|
continue;
|
|
2888
2518
|
}
|
|
2889
|
-
const source =
|
|
2890
|
-
const target =
|
|
2891
|
-
if (!
|
|
2519
|
+
const source = join11(templatesDir, entry.source);
|
|
2520
|
+
const target = join11(projectDir, entry.target);
|
|
2521
|
+
if (!existsSync12(source)) {
|
|
2892
2522
|
result.skipped += 1;
|
|
2893
2523
|
continue;
|
|
2894
2524
|
}
|
|
2895
2525
|
if (entry.type === "file") {
|
|
2896
2526
|
if (entry.target === ".claude/settings.json") {
|
|
2897
|
-
const backup = backupFileIfChanged(target,
|
|
2527
|
+
const backup = backupFileIfChanged(target, readFileSync12(source, "utf-8"));
|
|
2898
2528
|
if (backup) {
|
|
2899
2529
|
result.backups.push(backup);
|
|
2900
2530
|
}
|
|
@@ -2907,8 +2537,8 @@ function installClaudeBaseline(manifestSpec, harnessRoot, projectDir, templatesD
|
|
|
2907
2537
|
}
|
|
2908
2538
|
accumulateCategory(result.categories, entry);
|
|
2909
2539
|
}
|
|
2910
|
-
const hookDir =
|
|
2911
|
-
if (
|
|
2540
|
+
const hookDir = join11(projectDir, ".claude/hooks");
|
|
2541
|
+
if (existsSync12(hookDir)) {
|
|
2912
2542
|
chmodHooksSync(hookDir);
|
|
2913
2543
|
}
|
|
2914
2544
|
writeInstalledTracks(projectDir, manifestSpec.tracks);
|
|
@@ -2929,25 +2559,18 @@ function writeEnvironmentFiles(projectDir, tracks) {
|
|
|
2929
2559
|
gitignoreNpxSkillsAdded: addGitignoreNpxSkillsAgents(projectDir)
|
|
2930
2560
|
};
|
|
2931
2561
|
}
|
|
2932
|
-
function runCliTransforms(spec, harnessRoot, projectDir,
|
|
2562
|
+
function runCliTransforms(spec, harnessRoot, projectDir, selectedInternalSkills) {
|
|
2933
2563
|
let codex = null;
|
|
2934
2564
|
let codexOptIn = null;
|
|
2935
2565
|
if (spec.cli.includes("codex")) {
|
|
2936
2566
|
codex = runCodexTransform({
|
|
2937
2567
|
harnessRoot,
|
|
2938
2568
|
projectDir,
|
|
2939
|
-
withUzysHarness: uzysHarnessSelected,
|
|
2940
2569
|
selectedInternalSkills
|
|
2941
2570
|
});
|
|
2942
2571
|
const installScope = spec.scope ?? "project";
|
|
2943
|
-
if (installScope === "global" &&
|
|
2944
|
-
codexOptIn = runCodexOptIn({
|
|
2945
|
-
projectDir,
|
|
2946
|
-
harnessRoot,
|
|
2947
|
-
withCodexSkills: spec.options.withCodexSkills,
|
|
2948
|
-
withCodexTrust: spec.options.withCodexTrust,
|
|
2949
|
-
withCodexPrompts: spec.options.withCodexPrompts
|
|
2950
|
-
});
|
|
2572
|
+
if (installScope === "global" && spec.options.withCodexTrust) {
|
|
2573
|
+
codexOptIn = runCodexOptIn({ projectDir });
|
|
2951
2574
|
}
|
|
2952
2575
|
}
|
|
2953
2576
|
let opencode = null;
|
|
@@ -2955,24 +2578,14 @@ function runCliTransforms(spec, harnessRoot, projectDir, uzysHarnessSelected, se
|
|
|
2955
2578
|
opencode = runOpencodeTransform({ harnessRoot, projectDir, selectedInternalSkills });
|
|
2956
2579
|
}
|
|
2957
2580
|
let antigravity = null;
|
|
2958
|
-
let antigravityOptIn = null;
|
|
2959
2581
|
if (spec.cli.includes("antigravity")) {
|
|
2960
2582
|
antigravity = runAntigravityTransform({
|
|
2961
2583
|
harnessRoot,
|
|
2962
2584
|
projectDir,
|
|
2963
|
-
withUzysHarness: uzysHarnessSelected,
|
|
2964
2585
|
selectedInternalSkills
|
|
2965
2586
|
});
|
|
2966
|
-
const installScope = spec.scope ?? "project";
|
|
2967
|
-
if (installScope === "global" && spec.options.withAntigravityGlobal) {
|
|
2968
|
-
antigravityOptIn = runAntigravityOptIn({
|
|
2969
|
-
projectDir,
|
|
2970
|
-
harnessRoot,
|
|
2971
|
-
enabled: true
|
|
2972
|
-
});
|
|
2973
|
-
}
|
|
2974
2587
|
}
|
|
2975
|
-
return { codex, codexOptIn, opencode, antigravity
|
|
2588
|
+
return { codex, codexOptIn, opencode, antigravity };
|
|
2976
2589
|
}
|
|
2977
2590
|
function runExternalPhase(ctx) {
|
|
2978
2591
|
if (ctx.runExternal === null) {
|
|
@@ -3032,10 +2645,10 @@ function wireKarpathyHook(spec, external, harnessRoot, projectDir) {
|
|
|
3032
2645
|
if (!karpathyResult?.ok) {
|
|
3033
2646
|
return { wired: false, reason: "plugin-install-failed" };
|
|
3034
2647
|
}
|
|
3035
|
-
const sourceHook =
|
|
3036
|
-
const targetHook =
|
|
2648
|
+
const sourceHook = join11(harnessRoot, "templates/hooks/karpathy-gate.sh");
|
|
2649
|
+
const targetHook = join11(projectDir, ".claude/hooks/karpathy-gate.sh");
|
|
3037
2650
|
let hookScriptCopied = false;
|
|
3038
|
-
if (
|
|
2651
|
+
if (existsSync12(sourceHook)) {
|
|
3039
2652
|
copyFile(sourceHook, targetHook);
|
|
3040
2653
|
try {
|
|
3041
2654
|
chmodSync2(targetHook, 493);
|
|
@@ -3043,10 +2656,10 @@ function wireKarpathyHook(spec, external, harnessRoot, projectDir) {
|
|
|
3043
2656
|
}
|
|
3044
2657
|
hookScriptCopied = true;
|
|
3045
2658
|
}
|
|
3046
|
-
const settingsPath =
|
|
2659
|
+
const settingsPath = join11(projectDir, ".claude/settings.json");
|
|
3047
2660
|
let settingsUpdated = false;
|
|
3048
|
-
if (
|
|
3049
|
-
const raw =
|
|
2661
|
+
if (existsSync12(settingsPath)) {
|
|
2662
|
+
const raw = readFileSync12(settingsPath, "utf8");
|
|
3050
2663
|
let before;
|
|
3051
2664
|
try {
|
|
3052
2665
|
before = JSON.parse(raw);
|
|
@@ -3057,7 +2670,7 @@ function wireKarpathyHook(spec, external, harnessRoot, projectDir) {
|
|
|
3057
2670
|
const beforeStr = JSON.stringify(before);
|
|
3058
2671
|
const afterStr = JSON.stringify(after);
|
|
3059
2672
|
if (beforeStr !== afterStr) {
|
|
3060
|
-
|
|
2673
|
+
writeFileSync9(settingsPath, `${JSON.stringify(after, null, 2)}
|
|
3061
2674
|
`);
|
|
3062
2675
|
settingsUpdated = true;
|
|
3063
2676
|
}
|
|
@@ -3065,10 +2678,10 @@ function wireKarpathyHook(spec, external, harnessRoot, projectDir) {
|
|
|
3065
2678
|
return { wired: true, settingsUpdated, hookScriptCopied };
|
|
3066
2679
|
}
|
|
3067
2680
|
function composeAndWriteMcp(harnessRoot, projectDir, spec) {
|
|
3068
|
-
const mcpPath =
|
|
2681
|
+
const mcpPath = join11(projectDir, ".mcp.json");
|
|
3069
2682
|
const composed = composeMcpJson({
|
|
3070
|
-
templateMcpPath:
|
|
3071
|
-
trackMapPath:
|
|
2683
|
+
templateMcpPath: join11(harnessRoot, "templates/mcp.json"),
|
|
2684
|
+
trackMapPath: join11(harnessRoot, "templates/track-mcp-map.tsv"),
|
|
3072
2685
|
existingPath: mcpPath,
|
|
3073
2686
|
tracks: spec.tracks
|
|
3074
2687
|
});
|
|
@@ -3094,18 +2707,18 @@ function accumulateCategory(cats, entry) {
|
|
|
3094
2707
|
}
|
|
3095
2708
|
}
|
|
3096
2709
|
function writeInstalledTracks(projectDir, tracks) {
|
|
3097
|
-
const path =
|
|
3098
|
-
|
|
2710
|
+
const path = join11(projectDir, ".claude/.installed-tracks");
|
|
2711
|
+
mkdirSync4(dirname4(path), { recursive: true });
|
|
3099
2712
|
const sorted = [...new Set(tracks)].sort().join("\n");
|
|
3100
|
-
|
|
2713
|
+
writeFileSync9(path, `${sorted}
|
|
3101
2714
|
`);
|
|
3102
2715
|
}
|
|
3103
2716
|
function writeRootClaudeMd(harnessRoot, projectDir, tracks) {
|
|
3104
|
-
const baseDir =
|
|
2717
|
+
const baseDir = join11(harnessRoot, "templates/project-claude");
|
|
3105
2718
|
const content = mergeProjectClaude(tracks, { baseDir });
|
|
3106
|
-
const target =
|
|
2719
|
+
const target = join11(projectDir, "CLAUDE.md");
|
|
3107
2720
|
const backup = backupFileIfChanged(target, content);
|
|
3108
|
-
|
|
2721
|
+
writeFileSync9(target, content);
|
|
3109
2722
|
return { content, backup };
|
|
3110
2723
|
}
|
|
3111
2724
|
function chmodHooksSync(hookDir) {
|
|
@@ -3117,7 +2730,7 @@ function chmodHooksSync(hookDir) {
|
|
|
3117
2730
|
}
|
|
3118
2731
|
}
|
|
3119
2732
|
function listHookFiles(hookDir) {
|
|
3120
|
-
return
|
|
2733
|
+
return readdirSync3(hookDir, { withFileTypes: true }).filter((e2) => e2.isFile() && e2.name.endsWith(".sh")).map((e2) => resolve(hookDir, e2.name));
|
|
3121
2734
|
}
|
|
3122
2735
|
|
|
3123
2736
|
// src/commands/install-render.ts
|
|
@@ -3243,54 +2856,25 @@ function renderCliArtifacts(log, spec, report) {
|
|
|
3243
2856
|
if (report.codex) {
|
|
3244
2857
|
log(assetRow("success", ".codex/config.toml", "settings + [mcp_servers.*]"));
|
|
3245
2858
|
log(assetRow("success", ".codex/hooks/", `${report.codex.hookFiles.length} files`));
|
|
3246
|
-
|
|
3247
|
-
assetRow(
|
|
3248
|
-
"success",
|
|
3249
|
-
".agents/skills/uzys-*/SKILL.md",
|
|
3250
|
-
`${report.codex.skillFiles.length} skills ($uzys-spec mention)`
|
|
3251
|
-
)
|
|
3252
|
-
);
|
|
3253
|
-
if (report.codex.promptFiles.length > 0) {
|
|
2859
|
+
if (report.codex.skillFiles.length > 0) {
|
|
3254
2860
|
log(
|
|
3255
2861
|
assetRow(
|
|
3256
2862
|
"success",
|
|
3257
|
-
".
|
|
3258
|
-
`${report.codex.
|
|
2863
|
+
".agents/skills/<id>/SKILL.md",
|
|
2864
|
+
`${report.codex.skillFiles.length} skills`
|
|
3259
2865
|
)
|
|
3260
2866
|
);
|
|
3261
2867
|
}
|
|
3262
|
-
if (report.codexOptIn) {
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
"~/.codex/skills/uzys-*",
|
|
3268
|
-
`${report.codexOptIn.skillsInstalled.count} copied (global opt-in)`
|
|
3269
|
-
)
|
|
3270
|
-
);
|
|
3271
|
-
}
|
|
3272
|
-
if (report.codexOptIn.trustEntry.enabled) {
|
|
3273
|
-
const trust = report.codexOptIn.trustEntry;
|
|
3274
|
-
const kind = trust.status === "error" ? "skip" : "success";
|
|
3275
|
-
const meta = trust.status === "registered" ? '[projects."<dir>"] trust_level="trusted"' : trust.status === "already-present" ? "already present" : trust.message ?? "error";
|
|
3276
|
-
log(assetRow(kind, "~/.codex/config.toml trust entry", meta));
|
|
3277
|
-
}
|
|
3278
|
-
if (report.codexOptIn.promptsInstalled.enabled) {
|
|
3279
|
-
const count = report.codexOptIn.promptsInstalled.count;
|
|
3280
|
-
log(
|
|
3281
|
-
assetRow(
|
|
3282
|
-
count > 0 ? "success" : "skip",
|
|
3283
|
-
"~/.codex/prompts/uzys-*",
|
|
3284
|
-
`${count} markdown copied (/uzys-spec slash \uB4F1\uB85D)`
|
|
3285
|
-
)
|
|
3286
|
-
);
|
|
3287
|
-
}
|
|
2868
|
+
if (report.codexOptIn?.trustEntry.enabled) {
|
|
2869
|
+
const trust = report.codexOptIn.trustEntry;
|
|
2870
|
+
const kind = trust.status === "error" ? "skip" : "success";
|
|
2871
|
+
const meta = trust.status === "registered" ? '[projects."<dir>"] trust_level="trusted"' : trust.status === "already-present" ? "already present" : trust.message ?? "error";
|
|
2872
|
+
log(assetRow(kind, "~/.codex/config.toml trust entry", meta));
|
|
3288
2873
|
}
|
|
3289
2874
|
}
|
|
3290
2875
|
if (report.opencode) {
|
|
3291
2876
|
log(assetRow("success", "opencode.json", "$schema + 5 keys"));
|
|
3292
2877
|
log(assetRow("success", ".opencode/commands/", `${report.opencode.commandFiles.length} files`));
|
|
3293
|
-
log(assetRow("success", ".opencode/plugins/uzys-harness.ts", "self-contained plugin"));
|
|
3294
2878
|
}
|
|
3295
2879
|
if (report.antigravity) {
|
|
3296
2880
|
if (report.antigravity.rulesFile) {
|
|
@@ -3300,41 +2884,11 @@ function renderCliArtifacts(log, spec, report) {
|
|
|
3300
2884
|
log(
|
|
3301
2885
|
assetRow(
|
|
3302
2886
|
"success",
|
|
3303
|
-
".agents/skills
|
|
2887
|
+
".agents/skills/<id>/SKILL.md",
|
|
3304
2888
|
`${report.antigravity.skillFiles.length} skills`
|
|
3305
2889
|
)
|
|
3306
2890
|
);
|
|
3307
2891
|
}
|
|
3308
|
-
if (report.antigravity.workflowFiles.length > 0) {
|
|
3309
|
-
log(
|
|
3310
|
-
assetRow(
|
|
3311
|
-
"success",
|
|
3312
|
-
".agents/workflows/uzys-*.md",
|
|
3313
|
-
`${report.antigravity.workflowFiles.length} workflows`
|
|
3314
|
-
)
|
|
3315
|
-
);
|
|
3316
|
-
}
|
|
3317
|
-
if (report.antigravityOptIn) {
|
|
3318
|
-
const opt = report.antigravityOptIn;
|
|
3319
|
-
if (opt.skillsInstalled.enabled) {
|
|
3320
|
-
log(
|
|
3321
|
-
assetRow(
|
|
3322
|
-
"success",
|
|
3323
|
-
"~/.gemini/antigravity/skills/uzys-*",
|
|
3324
|
-
`${opt.skillsInstalled.count} copied (global opt-in)`
|
|
3325
|
-
)
|
|
3326
|
-
);
|
|
3327
|
-
}
|
|
3328
|
-
if (opt.workflowsInstalled.enabled) {
|
|
3329
|
-
log(
|
|
3330
|
-
assetRow(
|
|
3331
|
-
"success",
|
|
3332
|
-
"~/.gemini/antigravity/global_workflows/uzys-*",
|
|
3333
|
-
`${opt.workflowsInstalled.count} copied (global opt-in)`
|
|
3334
|
-
)
|
|
3335
|
-
);
|
|
3336
|
-
}
|
|
3337
|
-
}
|
|
3338
2892
|
}
|
|
3339
2893
|
log("");
|
|
3340
2894
|
}
|
|
@@ -3395,15 +2949,9 @@ function renderFinalSummary(log, spec, report, fromWizard) {
|
|
|
3395
2949
|
}
|
|
3396
2950
|
}
|
|
3397
2951
|
log("");
|
|
3398
|
-
const
|
|
3399
|
-
const
|
|
3400
|
-
|
|
3401
|
-
log(infoRow("NEXT", `${c.bold("claude")} \u2192 ${c.cyan("/uzys:spec")}`));
|
|
3402
|
-
} else {
|
|
3403
|
-
const primary = (hasClaude ? "claude" : spec.cli[0]) ?? "claude";
|
|
3404
|
-
const label = CLI_SUMMARY_LABELS[primary];
|
|
3405
|
-
log(infoRow("NEXT", `Open ${c.bold(label)} \u2014 installed rules & skills are now active`));
|
|
3406
|
-
}
|
|
2952
|
+
const primary = (spec.cli.includes("claude") ? "claude" : spec.cli[0]) ?? "claude";
|
|
2953
|
+
const label = CLI_SUMMARY_LABELS[primary];
|
|
2954
|
+
log(infoRow("NEXT", `Open ${c.bold(label)} \u2014 installed rules & skills are now active`));
|
|
3407
2955
|
log("");
|
|
3408
2956
|
}
|
|
3409
2957
|
function formatAssetMeta(asset, version) {
|
|
@@ -3487,12 +3035,12 @@ function renderPhase1Rows(log, baseline, verbose = false, withEcc = false) {
|
|
|
3487
3035
|
phase1Row(
|
|
3488
3036
|
"hooks",
|
|
3489
3037
|
cats.hooks.length,
|
|
3490
|
-
"session-start \xB7
|
|
3038
|
+
"session-start \xB7 spec-drift \xB7 checkpoint \xB7 mcp-pre-exec (security)",
|
|
3491
3039
|
cats.hooks
|
|
3492
3040
|
);
|
|
3493
3041
|
}
|
|
3494
3042
|
if (cats.commands > 0) {
|
|
3495
|
-
phase1Row("commands", cats.commands, "
|
|
3043
|
+
phase1Row("commands", cats.commands, "/ecc:* (ECC plugin OFF fallback)");
|
|
3496
3044
|
}
|
|
3497
3045
|
if (cats.skills.length > 0) {
|
|
3498
3046
|
phase1Row(
|
|
@@ -3645,18 +3193,6 @@ function installAction(options, deps = {}) {
|
|
|
3645
3193
|
for (const w3 of validated.warnings) {
|
|
3646
3194
|
err(c.yellow(`[WARN] ${w3}`));
|
|
3647
3195
|
}
|
|
3648
|
-
if (validated.ok && options.withCodexPrompts === true && !validated.cli.includes("codex")) {
|
|
3649
|
-
err(
|
|
3650
|
-
c.yellow(
|
|
3651
|
-
"[WARN] --with-codex-prompts requires --cli codex. Skipping (no Codex prompts will be installed)."
|
|
3652
|
-
)
|
|
3653
|
-
);
|
|
3654
|
-
}
|
|
3655
|
-
if (validated.ok && options.codexPrompts === false && !validated.cli.includes("codex")) {
|
|
3656
|
-
err(
|
|
3657
|
-
c.yellow("[WARN] --no-codex-prompts has no effect without --cli codex (already excluded).")
|
|
3658
|
-
);
|
|
3659
|
-
}
|
|
3660
3196
|
if (!validated.ok) {
|
|
3661
3197
|
err(status.failure(c.red(`ERROR: ${validated.message}`)));
|
|
3662
3198
|
exit(1);
|
|
@@ -3681,18 +3217,11 @@ function installAction(options, deps = {}) {
|
|
|
3681
3217
|
tracks: options.track ?? [],
|
|
3682
3218
|
...userOverride ? { userOverride } : {},
|
|
3683
3219
|
// v26.81.0 (ADR-022, BREAKING) — 자산 1:1 boolean 13종 삭제. 자산 선택은 위
|
|
3684
|
-
// userOverride(--with <id>)로 일원화. 잔존 = 설치 동작
|
|
3220
|
+
// userOverride(--with <id>)로 일원화. 잔존 = 설치 동작 옵션만.
|
|
3685
3221
|
options: {
|
|
3686
3222
|
withPrune: options.withPrune === true,
|
|
3687
|
-
withCodexSkills: options.withCodexSkills === true,
|
|
3688
3223
|
withCodexTrust: options.withCodexTrust === true,
|
|
3689
|
-
withKarpathyHook: options.withKarpathyHook === true
|
|
3690
|
-
// v26.64.0 (ADR-020, BREAKING) — ADR-012/017 supersede. cli=codex 자동 default ON 폐기.
|
|
3691
|
-
// withCodexPrompts 는 사용자 명시 `--with-codex-prompts` 시에만 ON.
|
|
3692
|
-
// `--no-codex-prompts` 는 backward-compat noop (default 가 이미 false).
|
|
3693
|
-
// scope=global 일 때만 ~/.codex/prompts/ 에 실 write (installer.ts 참조).
|
|
3694
|
-
withCodexPrompts: options.withCodexPrompts === true && options.codexPrompts !== false,
|
|
3695
|
-
withAntigravityGlobal: options.withAntigravityGlobal === true
|
|
3224
|
+
withKarpathyHook: options.withKarpathyHook === true
|
|
3696
3225
|
},
|
|
3697
3226
|
cli: validated.cli,
|
|
3698
3227
|
projectDir: resolve2(options.projectDir ?? process.cwd()),
|
|
@@ -3784,35 +3313,23 @@ function registerInstallCommand(cli2) {
|
|
|
3784
3313
|
).option(
|
|
3785
3314
|
"--without <asset-id>",
|
|
3786
3315
|
"[Asset] Force-exclude External Asset id (drop from preset recommendation). Repeatable. v26.47.0+"
|
|
3787
|
-
).option(
|
|
3788
|
-
"--with-codex-prompts",
|
|
3789
|
-
"[Codex] Unify Codex slash (~/.codex/prompts/uzys-*.md). Requires --cli codex. (v26.46.0+)"
|
|
3790
|
-
).option(
|
|
3791
|
-
"--no-codex-prompts",
|
|
3792
|
-
"[Codex] Backward-compat noop \u2014 Codex slash is opt-in via --with-codex-prompts (v26.64.0 ADR-020)"
|
|
3793
|
-
).option(
|
|
3794
|
-
"--with-codex-skills",
|
|
3795
|
-
"[Codex] Codex global opt-in: copy uzys-* skills to ~/.codex/skills/"
|
|
3796
3316
|
).option(
|
|
3797
3317
|
"--with-codex-trust",
|
|
3798
3318
|
"[Codex] Codex global opt-in: register trust entry in ~/.codex/config.toml"
|
|
3799
|
-
).option(
|
|
3800
|
-
"--with-antigravity-global",
|
|
3801
|
-
"[Behavior] Antigravity global opt-in: copy uzys-* to ~/.gemini/antigravity/{skills,global_workflows}/. Requires --cli antigravity + --scope global. (v26.67.0+)"
|
|
3802
3319
|
).option(
|
|
3803
3320
|
"--with-prune",
|
|
3804
3321
|
"[Behavior] Prune ECC items beyond curated 89 (use with --with ecc-plugin)"
|
|
3805
3322
|
).option(
|
|
3806
3323
|
"--with-karpathy-hook",
|
|
3807
3324
|
"[Behavior] karpathy-coder pre-commit hook (.claude/settings.json PreToolUse Write|Edit)"
|
|
3808
|
-
).option("--verbose", "[Misc] Show installed file lists per category (default: counts only)").example("install --track tooling --with
|
|
3325
|
+
).option("--verbose", "[Misc] Show installed file lists per category (default: counts only)").example("install --track tooling --with karpathy-coder").example("install --track csr-supabase --cli claude --cli codex").example("install --track csr-supabase --without netlify-cli --with railway-skills").action((options) => installAction(options));
|
|
3809
3326
|
}
|
|
3810
3327
|
|
|
3811
3328
|
// src/commands/uninstall.ts
|
|
3812
3329
|
init_esm_shims();
|
|
3813
3330
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
3814
|
-
import { existsSync as
|
|
3815
|
-
import { join as
|
|
3331
|
+
import { existsSync as existsSync13, readFileSync as readFileSync13, rmSync } from "fs";
|
|
3332
|
+
import { join as join12, resolve as resolve3 } from "path";
|
|
3816
3333
|
function uninstallAction(options, deps = {}) {
|
|
3817
3334
|
const log = deps.log ?? console.log;
|
|
3818
3335
|
const err = deps.err ?? console.error;
|
|
@@ -3984,22 +3501,22 @@ function buildGlobalAdvisoryCmd(asset) {
|
|
|
3984
3501
|
}
|
|
3985
3502
|
}
|
|
3986
3503
|
function removeTemplates(log, projectDir, rm) {
|
|
3987
|
-
rm(
|
|
3988
|
-
if (log.templates.codexDir) rm(
|
|
3989
|
-
if (log.templates.opencodeDir) rm(
|
|
3504
|
+
rm(join12(projectDir, log.templates.claudeDir));
|
|
3505
|
+
if (log.templates.codexDir) rm(join12(projectDir, log.templates.codexDir));
|
|
3506
|
+
if (log.templates.opencodeDir) rm(join12(projectDir, log.templates.opencodeDir));
|
|
3990
3507
|
const rootMd = log.templates.rootClaudeMd;
|
|
3991
3508
|
if (rootMd) {
|
|
3992
3509
|
if (rootClaudeMdModified(log, projectDir)) return { rootClaudeMdKept: true };
|
|
3993
|
-
rm(
|
|
3510
|
+
rm(join12(projectDir, rootMd.path));
|
|
3994
3511
|
}
|
|
3995
3512
|
return { rootClaudeMdKept: false };
|
|
3996
3513
|
}
|
|
3997
3514
|
function rootClaudeMdModified(log, projectDir) {
|
|
3998
3515
|
const rootMd = log.templates.rootClaudeMd;
|
|
3999
3516
|
if (!rootMd) return false;
|
|
4000
|
-
const path =
|
|
4001
|
-
if (!
|
|
4002
|
-
return hashContent(
|
|
3517
|
+
const path = join12(projectDir, rootMd.path);
|
|
3518
|
+
if (!existsSync13(path)) return false;
|
|
3519
|
+
return hashContent(readFileSync13(path, "utf8")) !== rootMd.sha256;
|
|
4003
3520
|
}
|
|
4004
3521
|
function formatTemplateList(log) {
|
|
4005
3522
|
const items = [log.templates.claudeDir];
|
|
@@ -4011,7 +3528,7 @@ function defaultSpawn2(cmd, args) {
|
|
|
4011
3528
|
return spawnSync2(cmd, [...args], { encoding: "utf8", stdio: "pipe", timeout: 12e4 });
|
|
4012
3529
|
}
|
|
4013
3530
|
function defaultRm(path) {
|
|
4014
|
-
if (
|
|
3531
|
+
if (existsSync13(path)) {
|
|
4015
3532
|
rmSync(path, { recursive: true, force: true });
|
|
4016
3533
|
}
|
|
4017
3534
|
}
|
|
@@ -5287,8 +4804,8 @@ Proceed?`,
|
|
|
5287
4804
|
|
|
5288
4805
|
// src/state.ts
|
|
5289
4806
|
init_esm_shims();
|
|
5290
|
-
import { existsSync as
|
|
5291
|
-
import { join as
|
|
4807
|
+
import { existsSync as existsSync14, readFileSync as readFileSync14 } from "fs";
|
|
4808
|
+
import { join as join13 } from "path";
|
|
5292
4809
|
var META_FILE = ".claude/.installed-tracks";
|
|
5293
4810
|
var LEGACY_SIGNATURES = [
|
|
5294
4811
|
{ rule: "htmx.md", track: "ssr-htmx" },
|
|
@@ -5298,13 +4815,13 @@ var LEGACY_SIGNATURES = [
|
|
|
5298
4815
|
{ rule: "cli-development.md", track: "tooling" }
|
|
5299
4816
|
];
|
|
5300
4817
|
function detectInstallState(projectDir) {
|
|
5301
|
-
const claudeDir =
|
|
5302
|
-
const hasClaudeDir =
|
|
4818
|
+
const claudeDir = join13(projectDir, ".claude");
|
|
4819
|
+
const hasClaudeDir = existsSync14(claudeDir);
|
|
5303
4820
|
if (!hasClaudeDir) {
|
|
5304
4821
|
return { state: "new", tracks: [], source: "none", hasClaudeDir: false };
|
|
5305
4822
|
}
|
|
5306
|
-
const metaPath =
|
|
5307
|
-
if (
|
|
4823
|
+
const metaPath = join13(projectDir, META_FILE);
|
|
4824
|
+
if (existsSync14(metaPath)) {
|
|
5308
4825
|
const tracks2 = readMetafile(metaPath);
|
|
5309
4826
|
return { state: "existing", tracks: tracks2, source: "metafile", hasClaudeDir: true };
|
|
5310
4827
|
}
|
|
@@ -5312,7 +4829,7 @@ function detectInstallState(projectDir) {
|
|
|
5312
4829
|
return { state: "existing", tracks, source: "legacy", hasClaudeDir: true };
|
|
5313
4830
|
}
|
|
5314
4831
|
function readMetafile(path) {
|
|
5315
|
-
const raw =
|
|
4832
|
+
const raw = readFileSync14(path, "utf8");
|
|
5316
4833
|
const seen = /* @__PURE__ */ new Set();
|
|
5317
4834
|
for (const line of raw.split(/\s+/)) {
|
|
5318
4835
|
const trimmed = line.trim();
|
|
@@ -5323,13 +4840,13 @@ function readMetafile(path) {
|
|
|
5323
4840
|
return [...seen].sort();
|
|
5324
4841
|
}
|
|
5325
4842
|
function inferFromLegacySignatures(projectDir) {
|
|
5326
|
-
const rulesDir =
|
|
5327
|
-
if (!
|
|
4843
|
+
const rulesDir = join13(projectDir, ".claude/rules");
|
|
4844
|
+
if (!existsSync14(rulesDir)) {
|
|
5328
4845
|
return [];
|
|
5329
4846
|
}
|
|
5330
4847
|
const found = /* @__PURE__ */ new Set();
|
|
5331
4848
|
for (const sig of LEGACY_SIGNATURES) {
|
|
5332
|
-
if (
|
|
4849
|
+
if (existsSync14(join13(rulesDir, sig.rule))) {
|
|
5333
4850
|
found.add(sig.track);
|
|
5334
4851
|
}
|
|
5335
4852
|
}
|
|
@@ -5353,11 +4870,8 @@ function toOptionFlags(keys) {
|
|
|
5353
4870
|
const picked = new Set(keys);
|
|
5354
4871
|
return {
|
|
5355
4872
|
withPrune: picked.has("withPrune"),
|
|
5356
|
-
withCodexSkills: picked.has("withCodexSkills"),
|
|
5357
4873
|
withCodexTrust: picked.has("withCodexTrust"),
|
|
5358
|
-
withKarpathyHook: picked.has("withKarpathyHook")
|
|
5359
|
-
withCodexPrompts: false,
|
|
5360
|
-
withAntigravityGlobal: picked.has("withAntigravityGlobal")
|
|
4874
|
+
withKarpathyHook: picked.has("withKarpathyHook")
|
|
5361
4875
|
};
|
|
5362
4876
|
}
|
|
5363
4877
|
async function runInteractive(projectDir, deps = {}) {
|