cleargate 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/MANIFEST.json +30 -16
- package/dist/cli.cjs +485 -51
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +480 -47
- package/dist/cli.js.map +1 -1
- package/dist/templates/cleargate-planning/.claude/agents/architect.md +24 -0
- package/dist/templates/cleargate-planning/.claude/agents/developer.md +24 -0
- package/dist/templates/cleargate-planning/.claude/agents/reporter.md +74 -0
- package/dist/templates/cleargate-planning/.claude/hooks/pre-edit-gate.sh +162 -0
- package/dist/templates/cleargate-planning/.claude/hooks/session-start.sh +10 -7
- package/dist/templates/cleargate-planning/.claude/hooks/stamp-and-gate.sh +9 -8
- package/dist/templates/cleargate-planning/.claude/hooks/token-ledger.sh +36 -13
- package/dist/templates/cleargate-planning/.claude/settings.json +9 -0
- package/dist/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +55 -0
- package/dist/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +7 -7
- package/dist/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +137 -40
- package/dist/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +93 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/constants.mjs +8 -4
- package/dist/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +9 -1
- package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +74 -0
- package/dist/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +65 -1
- package/dist/templates/cleargate-planning/.cleargate/scripts/state.schema.json +31 -8
- package/dist/templates/cleargate-planning/.cleargate/scripts/update_state.mjs +93 -8
- package/dist/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +19 -4
- package/dist/templates/cleargate-planning/.cleargate/templates/hotfix.md +58 -0
- package/dist/templates/cleargate-planning/.cleargate/templates/sprint_report.md +32 -2
- package/dist/templates/cleargate-planning/.cleargate/templates/story.md +3 -1
- package/dist/templates/cleargate-planning/CLAUDE.md +1 -1
- package/dist/templates/cleargate-planning/MANIFEST.json +30 -16
- package/package.json +1 -1
- package/templates/cleargate-planning/.claude/agents/architect.md +24 -0
- package/templates/cleargate-planning/.claude/agents/developer.md +24 -0
- package/templates/cleargate-planning/.claude/agents/reporter.md +74 -0
- package/templates/cleargate-planning/.claude/hooks/pre-edit-gate.sh +162 -0
- package/templates/cleargate-planning/.claude/hooks/session-start.sh +10 -7
- package/templates/cleargate-planning/.claude/hooks/stamp-and-gate.sh +9 -8
- package/templates/cleargate-planning/.claude/hooks/token-ledger.sh +36 -13
- package/templates/cleargate-planning/.claude/settings.json +9 -0
- package/templates/cleargate-planning/.cleargate/knowledge/cleargate-protocol.md +55 -0
- package/templates/cleargate-planning/.cleargate/knowledge/readiness-gates.md +7 -7
- package/templates/cleargate-planning/.cleargate/scripts/assert_story_files.mjs +137 -40
- package/templates/cleargate-planning/.cleargate/scripts/close_sprint.mjs +93 -0
- package/templates/cleargate-planning/.cleargate/scripts/constants.mjs +8 -4
- package/templates/cleargate-planning/.cleargate/scripts/init_sprint.mjs +9 -1
- package/templates/cleargate-planning/.cleargate/scripts/pre_gate_common.sh +74 -0
- package/templates/cleargate-planning/.cleargate/scripts/pre_gate_runner.sh +65 -1
- package/templates/cleargate-planning/.cleargate/scripts/state.schema.json +31 -8
- package/templates/cleargate-planning/.cleargate/scripts/update_state.mjs +93 -8
- package/templates/cleargate-planning/.cleargate/templates/Sprint Plan Template.md +19 -4
- package/templates/cleargate-planning/.cleargate/templates/hotfix.md +58 -0
- package/templates/cleargate-planning/.cleargate/templates/sprint_report.md +32 -2
- package/templates/cleargate-planning/.cleargate/templates/story.md +3 -1
- package/templates/cleargate-planning/CLAUDE.md +1 -1
- package/templates/cleargate-planning/MANIFEST.json +30 -16
package/dist/cli.cjs
CHANGED
|
@@ -627,7 +627,7 @@ var import_commander = require("commander");
|
|
|
627
627
|
// package.json
|
|
628
628
|
var package_default = {
|
|
629
629
|
name: "cleargate",
|
|
630
|
-
version: "0.
|
|
630
|
+
version: "0.6.1",
|
|
631
631
|
private: false,
|
|
632
632
|
type: "module",
|
|
633
633
|
description: "Planning framework for Claude Code agents \u2014 sprint/epic/story protocol, four-agent loop (architect/developer/qa/reporter), Karpathy-style awareness wiki.",
|
|
@@ -1900,11 +1900,17 @@ init_cjs_shims();
|
|
|
1900
1900
|
var fs15 = __toESM(require("fs"), 1);
|
|
1901
1901
|
var path16 = __toESM(require("path"), 1);
|
|
1902
1902
|
var import_node_url5 = require("url");
|
|
1903
|
+
var import_node_child_process3 = require("child_process");
|
|
1903
1904
|
|
|
1904
1905
|
// src/init/copy-payload.ts
|
|
1905
1906
|
init_cjs_shims();
|
|
1906
1907
|
var fs7 = __toESM(require("fs"), 1);
|
|
1907
1908
|
var path7 = __toESM(require("path"), 1);
|
|
1909
|
+
var PIN_PLACEHOLDER = "__CLEARGATE_VERSION__";
|
|
1910
|
+
var HOOK_FILES_WITH_PIN = /* @__PURE__ */ new Set([
|
|
1911
|
+
".claude/hooks/stamp-and-gate.sh",
|
|
1912
|
+
".claude/hooks/session-start.sh"
|
|
1913
|
+
]);
|
|
1908
1914
|
function listFilesRecursive(dir) {
|
|
1909
1915
|
const results = [];
|
|
1910
1916
|
function walk(current, rel) {
|
|
@@ -1932,10 +1938,15 @@ function copyPayload(payloadDir, targetCwd, opts) {
|
|
|
1932
1938
|
const srcPath = path7.join(payloadDir, relPath);
|
|
1933
1939
|
const dstPath = path7.join(targetCwd, relPath);
|
|
1934
1940
|
fs7.mkdirSync(path7.dirname(dstPath), { recursive: true });
|
|
1935
|
-
|
|
1941
|
+
let srcContent = fs7.readFileSync(srcPath);
|
|
1942
|
+
if (opts.pinVersion && HOOK_FILES_WITH_PIN.has(relPath)) {
|
|
1943
|
+
const text = srcContent.toString("utf8").replaceAll(PIN_PLACEHOLDER, opts.pinVersion);
|
|
1944
|
+
srcContent = text;
|
|
1945
|
+
}
|
|
1946
|
+
const srcBuffer = typeof srcContent === "string" ? Buffer.from(srcContent, "utf8") : srcContent;
|
|
1936
1947
|
if (fs7.existsSync(dstPath)) {
|
|
1937
1948
|
const dstContent = fs7.readFileSync(dstPath);
|
|
1938
|
-
if (
|
|
1949
|
+
if (srcBuffer.equals(dstContent)) {
|
|
1939
1950
|
report.skipped++;
|
|
1940
1951
|
report.actions.push({ action: "skipped", relPath });
|
|
1941
1952
|
continue;
|
|
@@ -1945,11 +1956,11 @@ function copyPayload(payloadDir, targetCwd, opts) {
|
|
|
1945
1956
|
report.actions.push({ action: "skipped", relPath });
|
|
1946
1957
|
continue;
|
|
1947
1958
|
}
|
|
1948
|
-
fs7.writeFileSync(dstPath,
|
|
1959
|
+
fs7.writeFileSync(dstPath, srcBuffer);
|
|
1949
1960
|
report.overwritten++;
|
|
1950
1961
|
report.actions.push({ action: "overwritten", relPath });
|
|
1951
1962
|
} else {
|
|
1952
|
-
fs7.writeFileSync(dstPath,
|
|
1963
|
+
fs7.writeFileSync(dstPath, srcBuffer);
|
|
1953
1964
|
report.created++;
|
|
1954
1965
|
report.actions.push({ action: "created", relPath });
|
|
1955
1966
|
}
|
|
@@ -2798,7 +2809,7 @@ init_cjs_shims();
|
|
|
2798
2809
|
var readline3 = __toESM(require("readline"), 1);
|
|
2799
2810
|
async function promptYesNo(question, defaultYes, opts) {
|
|
2800
2811
|
const stdoutFn = opts?.stdout ?? ((s) => process.stdout.write(s));
|
|
2801
|
-
stdoutFn(question + "
|
|
2812
|
+
stdoutFn(question + " ");
|
|
2802
2813
|
const inputStream = opts?.stdin ?? process.stdin;
|
|
2803
2814
|
return new Promise((resolve14) => {
|
|
2804
2815
|
const rl = readline3.createInterface({
|
|
@@ -2829,7 +2840,7 @@ async function promptYesNo(question, defaultYes, opts) {
|
|
|
2829
2840
|
}
|
|
2830
2841
|
async function promptEmail(question, defaultValue, opts) {
|
|
2831
2842
|
const stdoutFn = opts?.stdout ?? ((s) => process.stdout.write(s));
|
|
2832
|
-
stdoutFn(question + "
|
|
2843
|
+
stdoutFn(question + " ");
|
|
2833
2844
|
const inputStream = opts?.stdin ?? process.stdin;
|
|
2834
2845
|
return new Promise((resolve14) => {
|
|
2835
2846
|
const rl = readline3.createInterface({
|
|
@@ -2922,6 +2933,17 @@ function resolveIdentity(projectRoot, opts = {}) {
|
|
|
2922
2933
|
// src/commands/init.ts
|
|
2923
2934
|
var HOOK_ADDITION = {
|
|
2924
2935
|
hooks: {
|
|
2936
|
+
PreToolUse: [
|
|
2937
|
+
{
|
|
2938
|
+
matcher: "Edit|Write",
|
|
2939
|
+
hooks: [
|
|
2940
|
+
{
|
|
2941
|
+
type: "command",
|
|
2942
|
+
command: "${CLAUDE_PROJECT_DIR}/.claude/hooks/pre-edit-gate.sh"
|
|
2943
|
+
}
|
|
2944
|
+
]
|
|
2945
|
+
}
|
|
2946
|
+
],
|
|
2925
2947
|
PostToolUse: [
|
|
2926
2948
|
{
|
|
2927
2949
|
matcher: "Edit|Write",
|
|
@@ -2958,6 +2980,17 @@ function writeAtomic(filePath, content) {
|
|
|
2958
2980
|
fs15.writeFileSync(tmpPath, content, "utf8");
|
|
2959
2981
|
fs15.renameSync(tmpPath, filePath);
|
|
2960
2982
|
}
|
|
2983
|
+
function readPackageVersion(packageJsonPath) {
|
|
2984
|
+
try {
|
|
2985
|
+
const raw = fs15.readFileSync(packageJsonPath, "utf8");
|
|
2986
|
+
const pkg = JSON.parse(raw);
|
|
2987
|
+
if (typeof pkg.version === "string" && pkg.version.length > 0) {
|
|
2988
|
+
return pkg.version;
|
|
2989
|
+
}
|
|
2990
|
+
} catch {
|
|
2991
|
+
}
|
|
2992
|
+
return null;
|
|
2993
|
+
}
|
|
2961
2994
|
async function initHandler(opts = {}) {
|
|
2962
2995
|
const cwd = opts.cwd ?? process.cwd();
|
|
2963
2996
|
const force = opts.force ?? false;
|
|
@@ -2968,6 +3001,7 @@ async function initHandler(opts = {}) {
|
|
|
2968
3001
|
const runWikiBuild = opts.runWikiBuild ?? wikiBuildHandler;
|
|
2969
3002
|
const promptYesNoFn = opts.promptYesNo ?? promptYesNo;
|
|
2970
3003
|
const promptEmailFn = opts.promptEmail ?? promptEmail;
|
|
3004
|
+
const spawnSyncFn = opts.spawnSyncFn ?? import_node_child_process3.spawnSync;
|
|
2971
3005
|
if (!fs15.existsSync(cwd)) {
|
|
2972
3006
|
stderr(`[cleargate init] ERROR: target directory does not exist: ${cwd}
|
|
2973
3007
|
`);
|
|
@@ -3029,7 +3063,14 @@ async function initHandler(opts = {}) {
|
|
|
3029
3063
|
}
|
|
3030
3064
|
}
|
|
3031
3065
|
}
|
|
3032
|
-
|
|
3066
|
+
let pinVersion;
|
|
3067
|
+
if (opts.pin) {
|
|
3068
|
+
pinVersion = opts.pin;
|
|
3069
|
+
} else {
|
|
3070
|
+
const payloadParent = path16.resolve(payloadDir, "..", "..");
|
|
3071
|
+
pinVersion = readPackageVersion(path16.join(payloadParent, "package.json")) ?? readPackageVersion(path16.join(path16.dirname((0, import_node_url5.fileURLToPath)(importMetaUrl)), "..", "package.json")) ?? "latest";
|
|
3072
|
+
}
|
|
3073
|
+
const copyReport = copyPayload(payloadDir, cwd, { force, pinVersion });
|
|
3033
3074
|
for (const action of copyReport.actions) {
|
|
3034
3075
|
const verb = action.action === "created" ? "Created" : action.action === "overwritten" ? "Overwritten" : "Skipped (exists)";
|
|
3035
3076
|
stdout(`[cleargate init] ${verb} ${action.relPath}
|
|
@@ -3110,6 +3151,46 @@ async function initHandler(opts = {}) {
|
|
|
3110
3151
|
`);
|
|
3111
3152
|
}
|
|
3112
3153
|
}
|
|
3154
|
+
{
|
|
3155
|
+
const distCliPath = path16.join(cwd, "cleargate-cli", "dist", "cli.js");
|
|
3156
|
+
let branch = null;
|
|
3157
|
+
let branchLabel = "";
|
|
3158
|
+
if (fs15.existsSync(distCliPath)) {
|
|
3159
|
+
branch = { cmd: "node", args: [distCliPath, "--version"] };
|
|
3160
|
+
branchLabel = `local dist (${distCliPath})`;
|
|
3161
|
+
} else {
|
|
3162
|
+
const whichResult = spawnSyncFn("command", ["-v", "cleargate"], {
|
|
3163
|
+
shell: true,
|
|
3164
|
+
encoding: "utf8",
|
|
3165
|
+
timeout: 3e3
|
|
3166
|
+
});
|
|
3167
|
+
if (whichResult.status === 0) {
|
|
3168
|
+
branch = { cmd: "cleargate", args: ["--version"] };
|
|
3169
|
+
branchLabel = "PATH (global install)";
|
|
3170
|
+
} else {
|
|
3171
|
+
branch = { cmd: "npx", args: ["-y", `cleargate@${pinVersion}`, "--version"] };
|
|
3172
|
+
branchLabel = `npx cleargate@${pinVersion} (cold-start ~600ms first call)`;
|
|
3173
|
+
}
|
|
3174
|
+
}
|
|
3175
|
+
if (branch !== null) {
|
|
3176
|
+
const probeResult = spawnSyncFn(branch.cmd, branch.args, {
|
|
3177
|
+
encoding: "utf8",
|
|
3178
|
+
timeout: 15e3
|
|
3179
|
+
});
|
|
3180
|
+
if (probeResult.status === 0) {
|
|
3181
|
+
stdout(`[cleargate init] \u{1F7E2} cleargate CLI resolved via ${branchLabel}
|
|
3182
|
+
`);
|
|
3183
|
+
} else {
|
|
3184
|
+
stdout(
|
|
3185
|
+
`[cleargate init] \u{1F7E1} cleargate CLI: not resolvable in this environment.
|
|
3186
|
+
[cleargate init] Attempted: ${branchLabel}
|
|
3187
|
+
[cleargate init] This is a warning, not a fatal error. Hooks will no-op until resolved.
|
|
3188
|
+
[cleargate init] Fix: npm i -g cleargate@${pinVersion} or npx cleargate@${pinVersion} doctor
|
|
3189
|
+
`
|
|
3190
|
+
);
|
|
3191
|
+
}
|
|
3192
|
+
}
|
|
3193
|
+
}
|
|
3113
3194
|
const existingParticipant = readParticipant(cwd);
|
|
3114
3195
|
if (existingParticipant === null) {
|
|
3115
3196
|
const identityOpts = opts.identityOpts ?? {};
|
|
@@ -3127,8 +3208,10 @@ async function initHandler(opts = {}) {
|
|
|
3127
3208
|
stdout(`[cleargate init] Participant identity: ${finalEmail} (inferred)
|
|
3128
3209
|
`);
|
|
3129
3210
|
} else {
|
|
3130
|
-
const
|
|
3131
|
-
const
|
|
3211
|
+
const isNoreply = gitEmail !== null && /@users\.noreply\.github\.com$/i.test(gitEmail);
|
|
3212
|
+
const defaultEmail = gitEmail !== null && !isNoreply ? gitEmail : "user@localhost";
|
|
3213
|
+
stdout("\n");
|
|
3214
|
+
const question = `Participant email (press Enter for default) [${defaultEmail}]:`;
|
|
3132
3215
|
const answer = await promptEmailFn(question, defaultEmail);
|
|
3133
3216
|
await writeParticipant(cwd, answer, "prompted", now);
|
|
3134
3217
|
stdout(`[cleargate init] Participant identity: ${answer} (prompted)
|
|
@@ -3146,7 +3229,7 @@ async function initHandler(opts = {}) {
|
|
|
3146
3229
|
init_cjs_shims();
|
|
3147
3230
|
var fs16 = __toESM(require("fs"), 1);
|
|
3148
3231
|
var path17 = __toESM(require("path"), 1);
|
|
3149
|
-
var
|
|
3232
|
+
var import_node_child_process4 = require("child_process");
|
|
3150
3233
|
var EXCLUDED_SUFFIXES2 = [
|
|
3151
3234
|
".cleargate/knowledge/",
|
|
3152
3235
|
".cleargate/templates/",
|
|
@@ -3298,7 +3381,7 @@ function checkContentUnchanged(absRawPath, sha, relRawPath, gitRunner) {
|
|
|
3298
3381
|
}
|
|
3299
3382
|
}
|
|
3300
3383
|
function defaultGitRunner(cmd, args) {
|
|
3301
|
-
const result = (0,
|
|
3384
|
+
const result = (0, import_node_child_process4.spawnSync)(cmd, args, { encoding: "utf8" });
|
|
3302
3385
|
if (result.status !== 0) return "\0__NONZERO__";
|
|
3303
3386
|
return result.stdout ?? "";
|
|
3304
3387
|
}
|
|
@@ -3550,7 +3633,7 @@ function loadWikiPages(wikiRoot) {
|
|
|
3550
3633
|
init_cjs_shims();
|
|
3551
3634
|
var fs18 = __toESM(require("fs"), 1);
|
|
3552
3635
|
var path19 = __toESM(require("path"), 1);
|
|
3553
|
-
var
|
|
3636
|
+
var import_node_child_process5 = require("child_process");
|
|
3554
3637
|
var import_js_yaml3 = __toESM(require("js-yaml"), 1);
|
|
3555
3638
|
|
|
3556
3639
|
// src/lib/work-item-type.ts
|
|
@@ -3646,7 +3729,7 @@ function checkStaleCommit(page, repoRoot, gitRunner) {
|
|
|
3646
3729
|
if (gitRunner) {
|
|
3647
3730
|
currentSha = gitRunner("git", ["log", "-1", "--format=%H", "--", rawPath]).trim();
|
|
3648
3731
|
} else {
|
|
3649
|
-
const result = (0,
|
|
3732
|
+
const result = (0, import_node_child_process5.spawnSync)("git", ["log", "-1", "--format=%H", "--", rawPath], {
|
|
3650
3733
|
encoding: "utf8",
|
|
3651
3734
|
cwd: repoRoot
|
|
3652
3735
|
});
|
|
@@ -4391,6 +4474,7 @@ function applyStatusFix(rawText, newStatus) {
|
|
|
4391
4474
|
init_cjs_shims();
|
|
4392
4475
|
var fs22 = __toESM(require("fs"), 1);
|
|
4393
4476
|
var path24 = __toESM(require("path"), 1);
|
|
4477
|
+
var import_node_child_process6 = require("child_process");
|
|
4394
4478
|
|
|
4395
4479
|
// src/lib/pricing.ts
|
|
4396
4480
|
init_cjs_shims();
|
|
@@ -4440,6 +4524,7 @@ function selectMode(flags) {
|
|
|
4440
4524
|
if (flags.checkScaffold) modes.push("check-scaffold");
|
|
4441
4525
|
if (flags.sessionStart) modes.push("session-start");
|
|
4442
4526
|
if (flags.pricing) modes.push("pricing");
|
|
4527
|
+
if (flags.canEdit) modes.push("can-edit");
|
|
4443
4528
|
if (modes.length > 1) {
|
|
4444
4529
|
throw new Error(
|
|
4445
4530
|
`cleargate doctor: mutually exclusive flags set: ${modes.join(", ")}. Use only one mode flag at a time.`
|
|
@@ -4464,7 +4549,18 @@ function parseHookLogLine(line) {
|
|
|
4464
4549
|
file: m[5].trim()
|
|
4465
4550
|
};
|
|
4466
4551
|
}
|
|
4467
|
-
function runHookHealth(stdout, cwd, now) {
|
|
4552
|
+
function runHookHealth(stdout, cwd, now, outcome) {
|
|
4553
|
+
const cleargateDir = path24.join(cwd, ".cleargate");
|
|
4554
|
+
if (!fs22.existsSync(cleargateDir)) {
|
|
4555
|
+
stdout("cleargate misconfigured: no .cleargate/ found. Run: cleargate init");
|
|
4556
|
+
if (outcome) outcome.configError = true;
|
|
4557
|
+
return;
|
|
4558
|
+
}
|
|
4559
|
+
const manifestPath = path24.join(cwd, "cleargate-planning", "MANIFEST.json");
|
|
4560
|
+
if (!fs22.existsSync(manifestPath)) {
|
|
4561
|
+
stdout(`cleargate misconfigured: cleargate-planning/MANIFEST.json not found. Run: cleargate init`);
|
|
4562
|
+
if (outcome) outcome.configError = true;
|
|
4563
|
+
}
|
|
4468
4564
|
const settingsPath = path24.join(cwd, ".claude", "settings.json");
|
|
4469
4565
|
if (!fs22.existsSync(settingsPath)) {
|
|
4470
4566
|
stdout("[doctor] No .claude/settings.json found \u2014 hook config unavailable.");
|
|
@@ -4601,7 +4697,57 @@ function parseCachedGateResult2(raw) {
|
|
|
4601
4697
|
failing_criteria: parsed.failing_criteria ?? []
|
|
4602
4698
|
};
|
|
4603
4699
|
}
|
|
4604
|
-
|
|
4700
|
+
function emitResolverStatusLine(cwd, stdout) {
|
|
4701
|
+
const distCliPath = path24.join(cwd, "cleargate-cli", "dist", "cli.js");
|
|
4702
|
+
if (fs22.existsSync(distCliPath)) {
|
|
4703
|
+
stdout(`cleargate CLI: local dist \u2014 ${distCliPath}`);
|
|
4704
|
+
return;
|
|
4705
|
+
}
|
|
4706
|
+
const whichResult = (0, import_node_child_process6.spawnSync)("command", ["-v", "cleargate"], {
|
|
4707
|
+
shell: true,
|
|
4708
|
+
encoding: "utf8",
|
|
4709
|
+
timeout: 3e3
|
|
4710
|
+
});
|
|
4711
|
+
if (whichResult.status === 0) {
|
|
4712
|
+
stdout("cleargate CLI: PATH (global install) \u2014 cleargate");
|
|
4713
|
+
return;
|
|
4714
|
+
}
|
|
4715
|
+
let pinVersion = "unknown";
|
|
4716
|
+
const hookPath = path24.join(cwd, ".claude", "hooks", "stamp-and-gate.sh");
|
|
4717
|
+
if (fs22.existsSync(hookPath)) {
|
|
4718
|
+
try {
|
|
4719
|
+
const hookContent = fs22.readFileSync(hookPath, "utf-8");
|
|
4720
|
+
const pinMatch = hookContent.match(/^#\s*cleargate-pin:\s*(\S+)\s*$/m);
|
|
4721
|
+
if (pinMatch?.[1]) {
|
|
4722
|
+
pinVersion = pinMatch[1];
|
|
4723
|
+
} else {
|
|
4724
|
+
const npxMatch = hookContent.match(/@cleargate\/cli@([^\s"']+)/);
|
|
4725
|
+
if (npxMatch?.[1]) pinVersion = npxMatch[1];
|
|
4726
|
+
}
|
|
4727
|
+
} catch {
|
|
4728
|
+
}
|
|
4729
|
+
}
|
|
4730
|
+
if (pinVersion === "unknown") {
|
|
4731
|
+
stdout("cleargate CLI: \u{1F534} not resolvable \u2014 hooks will no-op. Fix: npm i -g cleargate or npx cleargate doctor");
|
|
4732
|
+
} else {
|
|
4733
|
+
stdout(`cleargate CLI: npx @cleargate/cli@${pinVersion} (cold-start ~600ms first call)`);
|
|
4734
|
+
}
|
|
4735
|
+
}
|
|
4736
|
+
var PLANNING_FIRST_REMINDER = `Triage first, draft second:
|
|
4737
|
+
Before any Edit/Write that creates user-facing code, you must:
|
|
4738
|
+
(1) classify the request (Epic / Story / CR / Bug),
|
|
4739
|
+
(2) draft a work item under .cleargate/delivery/pending-sync/ from .cleargate/templates/,
|
|
4740
|
+
(3) halt at Gate 1 (Proposal approval) for human sign-off.
|
|
4741
|
+
Bypass this only if the user has explicitly waived planning in this conversation.`;
|
|
4742
|
+
async function runSessionStart(cwd, stdout, outcome) {
|
|
4743
|
+
const resolverLines = [];
|
|
4744
|
+
emitResolverStatusLine(cwd, (line) => {
|
|
4745
|
+
stdout(line);
|
|
4746
|
+
resolverLines.push(line);
|
|
4747
|
+
});
|
|
4748
|
+
if (outcome && resolverLines.some((l) => l.includes("\u{1F534}"))) {
|
|
4749
|
+
outcome.configError = true;
|
|
4750
|
+
}
|
|
4605
4751
|
const pendingSyncDir = path24.join(cwd, ".cleargate", "delivery", "pending-sync");
|
|
4606
4752
|
let files;
|
|
4607
4753
|
try {
|
|
@@ -4610,6 +4756,7 @@ async function runSessionStart(cwd, stdout) {
|
|
|
4610
4756
|
return;
|
|
4611
4757
|
}
|
|
4612
4758
|
const blocked = [];
|
|
4759
|
+
let hasApprovedStory = false;
|
|
4613
4760
|
for (const filePath of files) {
|
|
4614
4761
|
let raw;
|
|
4615
4762
|
try {
|
|
@@ -4624,6 +4771,9 @@ async function runSessionStart(cwd, stdout) {
|
|
|
4624
4771
|
} catch {
|
|
4625
4772
|
continue;
|
|
4626
4773
|
}
|
|
4774
|
+
if (fm["approved"] === true) {
|
|
4775
|
+
hasApprovedStory = true;
|
|
4776
|
+
}
|
|
4627
4777
|
const gate2 = parseCachedGateResult2(fm["cached_gate_result"]);
|
|
4628
4778
|
if (!gate2 || gate2.pass !== false) continue;
|
|
4629
4779
|
const idKeys = ["story_id", "epic_id", "proposal_id", "cr_id", "bug_id", "sprint_id"];
|
|
@@ -4641,9 +4791,19 @@ async function runSessionStart(cwd, stdout) {
|
|
|
4641
4791
|
const firstCriterionId = gate2.failing_criteria.length > 0 ? gate2.failing_criteria[0]?.id ?? "" : "";
|
|
4642
4792
|
blocked.push({ id: itemId, firstCriterionId });
|
|
4643
4793
|
}
|
|
4794
|
+
const activesentinel = path24.join(cwd, ".cleargate", "sprint-runs", ".active");
|
|
4795
|
+
const sprintActive = fs22.existsSync(activesentinel);
|
|
4796
|
+
const shouldRemind = !hasApprovedStory && !sprintActive;
|
|
4797
|
+
if (shouldRemind) {
|
|
4798
|
+
stdout(PLANNING_FIRST_REMINDER);
|
|
4799
|
+
if (blocked.length > 0) {
|
|
4800
|
+
stdout("");
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4644
4803
|
if (blocked.length === 0) {
|
|
4645
4804
|
return;
|
|
4646
4805
|
}
|
|
4806
|
+
if (outcome) outcome.blocker = true;
|
|
4647
4807
|
const overflow = blocked.length > SESSION_START_MAX_ITEMS ? blocked.length - SESSION_START_MAX_ITEMS : 0;
|
|
4648
4808
|
const visible = blocked.slice(0, SESSION_START_MAX_ITEMS);
|
|
4649
4809
|
const lines = [`${blocked.length} items blocked:`];
|
|
@@ -4660,10 +4820,11 @@ async function runSessionStart(cwd, stdout) {
|
|
|
4660
4820
|
}
|
|
4661
4821
|
stdout(output);
|
|
4662
4822
|
}
|
|
4663
|
-
async function runPricing(filePath, cwd, stdout, stderr, exit) {
|
|
4823
|
+
async function runPricing(filePath, cwd, stdout, stderr, exit, outcome) {
|
|
4664
4824
|
if (!filePath) {
|
|
4665
4825
|
stderr("cleargate doctor --pricing: missing <file> argument");
|
|
4666
|
-
|
|
4826
|
+
if (outcome) outcome.configError = true;
|
|
4827
|
+
exit(2);
|
|
4667
4828
|
return;
|
|
4668
4829
|
}
|
|
4669
4830
|
const absPath = path24.isAbsolute(filePath) ? filePath : path24.resolve(cwd, filePath);
|
|
@@ -4672,12 +4833,14 @@ async function runPricing(filePath, cwd, stdout, stderr, exit) {
|
|
|
4672
4833
|
raw = fs22.readFileSync(absPath, "utf-8");
|
|
4673
4834
|
} catch {
|
|
4674
4835
|
stderr(`cleargate doctor --pricing: cannot read file: ${absPath}`);
|
|
4675
|
-
|
|
4836
|
+
if (outcome) outcome.configError = true;
|
|
4837
|
+
exit(2);
|
|
4676
4838
|
return;
|
|
4677
4839
|
}
|
|
4678
4840
|
if (!raw.trimStart().startsWith("---")) {
|
|
4679
4841
|
stderr(`cleargate doctor --pricing: file has no frontmatter: ${absPath}`);
|
|
4680
|
-
|
|
4842
|
+
if (outcome) outcome.configError = true;
|
|
4843
|
+
exit(2);
|
|
4681
4844
|
return;
|
|
4682
4845
|
}
|
|
4683
4846
|
let fm;
|
|
@@ -4685,12 +4848,14 @@ async function runPricing(filePath, cwd, stdout, stderr, exit) {
|
|
|
4685
4848
|
fm = parseFrontmatter(raw).fm;
|
|
4686
4849
|
} catch {
|
|
4687
4850
|
stderr(`cleargate doctor --pricing: cannot parse frontmatter in: ${absPath}`);
|
|
4688
|
-
|
|
4851
|
+
if (outcome) outcome.configError = true;
|
|
4852
|
+
exit(2);
|
|
4689
4853
|
return;
|
|
4690
4854
|
}
|
|
4691
4855
|
const draftTokensRaw = fm["draft_tokens"];
|
|
4692
4856
|
if (!draftTokensRaw) {
|
|
4693
4857
|
stdout("draft_tokens unpopulated \u2014 run cleargate stamp-tokens first");
|
|
4858
|
+
if (outcome) outcome.blocker = true;
|
|
4694
4859
|
exit(1);
|
|
4695
4860
|
return;
|
|
4696
4861
|
}
|
|
@@ -4702,16 +4867,19 @@ async function runPricing(filePath, cwd, stdout, stderr, exit) {
|
|
|
4702
4867
|
draftTokens = JSON.parse(draftTokensRaw);
|
|
4703
4868
|
} catch {
|
|
4704
4869
|
stdout("draft_tokens unpopulated \u2014 run cleargate stamp-tokens first");
|
|
4870
|
+
if (outcome) outcome.blocker = true;
|
|
4705
4871
|
exit(1);
|
|
4706
4872
|
return;
|
|
4707
4873
|
}
|
|
4708
4874
|
} else {
|
|
4709
4875
|
stdout("draft_tokens unpopulated \u2014 run cleargate stamp-tokens first");
|
|
4876
|
+
if (outcome) outcome.blocker = true;
|
|
4710
4877
|
exit(1);
|
|
4711
4878
|
return;
|
|
4712
4879
|
}
|
|
4713
4880
|
if (draftTokens.input === null && draftTokens.output === null && draftTokens.cache_read === null && draftTokens.cache_creation === null) {
|
|
4714
4881
|
stdout("draft_tokens unpopulated \u2014 run cleargate stamp-tokens first");
|
|
4882
|
+
if (outcome) outcome.blocker = true;
|
|
4715
4883
|
exit(1);
|
|
4716
4884
|
return;
|
|
4717
4885
|
}
|
|
@@ -4729,18 +4897,95 @@ async function runPricing(filePath, cwd, stdout, stderr, exit) {
|
|
|
4729
4897
|
`${fileName}: ${model} \u2014 input:${input} output:${output} cache_read:${cacheRead} cache_creation:${cacheCreation} \u2248 $${usd.toFixed(4)}`
|
|
4730
4898
|
);
|
|
4731
4899
|
}
|
|
4900
|
+
function globMatch(pattern, filePath) {
|
|
4901
|
+
const normalPattern = pattern.replace(/\\/g, "/");
|
|
4902
|
+
const normalFile = filePath.replace(/\\/g, "/");
|
|
4903
|
+
const regexStr = normalPattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "\0").replace(/\*/g, "[^/]*").replace(//g, ".*");
|
|
4904
|
+
const re = new RegExp(`^${regexStr}$`);
|
|
4905
|
+
return re.test(normalFile);
|
|
4906
|
+
}
|
|
4907
|
+
async function runCanEdit(filePath, cwd, stdout, exit, outcome) {
|
|
4908
|
+
const activeSentinel = path24.join(cwd, ".cleargate", "sprint-runs", ".active");
|
|
4909
|
+
if (fs22.existsSync(activeSentinel)) {
|
|
4910
|
+
stdout("allowed: sprint active");
|
|
4911
|
+
return;
|
|
4912
|
+
}
|
|
4913
|
+
const pendingSyncDir = path24.join(cwd, ".cleargate", "delivery", "pending-sync");
|
|
4914
|
+
let files;
|
|
4915
|
+
try {
|
|
4916
|
+
files = fs22.readdirSync(pendingSyncDir).filter((f) => f.endsWith(".md")).map((f) => path24.join(pendingSyncDir, f));
|
|
4917
|
+
} catch {
|
|
4918
|
+
stdout("blocked: no_approved_stories");
|
|
4919
|
+
if (outcome) outcome.blocker = true;
|
|
4920
|
+
exit(1);
|
|
4921
|
+
return;
|
|
4922
|
+
}
|
|
4923
|
+
let hasApprovedStory = false;
|
|
4924
|
+
let coveredByStory = false;
|
|
4925
|
+
for (const storyPath of files) {
|
|
4926
|
+
let raw;
|
|
4927
|
+
try {
|
|
4928
|
+
raw = fs22.readFileSync(storyPath, "utf-8");
|
|
4929
|
+
} catch {
|
|
4930
|
+
continue;
|
|
4931
|
+
}
|
|
4932
|
+
if (!raw.trimStart().startsWith("---")) continue;
|
|
4933
|
+
let fm;
|
|
4934
|
+
try {
|
|
4935
|
+
fm = parseFrontmatter(raw).fm;
|
|
4936
|
+
} catch {
|
|
4937
|
+
continue;
|
|
4938
|
+
}
|
|
4939
|
+
if (fm["approved"] !== true) continue;
|
|
4940
|
+
hasApprovedStory = true;
|
|
4941
|
+
const implFilesRaw = fm["implementation_files"];
|
|
4942
|
+
if (implFilesRaw === void 0 || implFilesRaw === null) {
|
|
4943
|
+
coveredByStory = true;
|
|
4944
|
+
break;
|
|
4945
|
+
}
|
|
4946
|
+
if (Array.isArray(implFilesRaw)) {
|
|
4947
|
+
for (const pattern of implFilesRaw) {
|
|
4948
|
+
if (typeof pattern !== "string") continue;
|
|
4949
|
+
if (globMatch(pattern, filePath)) {
|
|
4950
|
+
coveredByStory = true;
|
|
4951
|
+
break;
|
|
4952
|
+
}
|
|
4953
|
+
}
|
|
4954
|
+
}
|
|
4955
|
+
if (coveredByStory) break;
|
|
4956
|
+
}
|
|
4957
|
+
if (!hasApprovedStory) {
|
|
4958
|
+
stdout("blocked: no_approved_stories");
|
|
4959
|
+
if (outcome) outcome.blocker = true;
|
|
4960
|
+
exit(1);
|
|
4961
|
+
return;
|
|
4962
|
+
}
|
|
4963
|
+
if (!coveredByStory) {
|
|
4964
|
+
stdout("blocked: file_not_in_implementation_files");
|
|
4965
|
+
if (outcome) outcome.blocker = true;
|
|
4966
|
+
exit(1);
|
|
4967
|
+
return;
|
|
4968
|
+
}
|
|
4969
|
+
stdout("allowed");
|
|
4970
|
+
}
|
|
4732
4971
|
async function doctorHandler(flags, cli) {
|
|
4733
4972
|
const cwd = cli?.cwd ?? process.cwd();
|
|
4734
4973
|
const now = cli?.now ? cli.now() : /* @__PURE__ */ new Date();
|
|
4735
4974
|
const stdout = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
4736
4975
|
const stderr = cli?.stderr ?? ((s) => process.stderr.write(s + "\n"));
|
|
4737
4976
|
const exit = cli?.exit ?? ((code) => process.exit(code));
|
|
4977
|
+
const outcome = { configError: false, blocker: false };
|
|
4978
|
+
let exitedEarly = false;
|
|
4979
|
+
const wrappedExit = (code) => {
|
|
4980
|
+
exitedEarly = true;
|
|
4981
|
+
return exit(code);
|
|
4982
|
+
};
|
|
4738
4983
|
let mode;
|
|
4739
4984
|
try {
|
|
4740
4985
|
mode = selectMode(flags);
|
|
4741
4986
|
} catch (err) {
|
|
4742
4987
|
stderr(err.message);
|
|
4743
|
-
exit(
|
|
4988
|
+
exit(2);
|
|
4744
4989
|
return;
|
|
4745
4990
|
}
|
|
4746
4991
|
switch (mode) {
|
|
@@ -4748,27 +4993,39 @@ async function doctorHandler(flags, cli) {
|
|
|
4748
4993
|
await runCheckScaffold(flags, cli ?? {}, cwd, now, stdout, stderr);
|
|
4749
4994
|
break;
|
|
4750
4995
|
case "hook-health":
|
|
4751
|
-
runHookHealth(stdout, cwd, now);
|
|
4996
|
+
runHookHealth(stdout, cwd, now, outcome);
|
|
4752
4997
|
break;
|
|
4753
4998
|
case "session-start":
|
|
4754
|
-
await runSessionStart(cwd, stdout);
|
|
4999
|
+
await runSessionStart(cwd, stdout, outcome);
|
|
4755
5000
|
break;
|
|
4756
5001
|
case "pricing":
|
|
4757
|
-
await runPricing(flags.pricingFile ?? "", cwd, stdout, stderr,
|
|
5002
|
+
await runPricing(flags.pricingFile ?? "", cwd, stdout, stderr, wrappedExit, outcome);
|
|
5003
|
+
break;
|
|
5004
|
+
case "can-edit":
|
|
5005
|
+
await runCanEdit(flags.canEditFile ?? "", cwd, stdout, wrappedExit, outcome);
|
|
4758
5006
|
break;
|
|
4759
5007
|
default: {
|
|
4760
5008
|
const exhaustiveCheck = mode;
|
|
4761
5009
|
stderr(`cleargate doctor: unknown mode '${String(exhaustiveCheck)}'`);
|
|
4762
|
-
exit(
|
|
5010
|
+
exit(2);
|
|
5011
|
+
return;
|
|
4763
5012
|
}
|
|
4764
5013
|
}
|
|
5014
|
+
if (exitedEarly) return;
|
|
5015
|
+
if (outcome.configError) {
|
|
5016
|
+
exit(2);
|
|
5017
|
+
} else if (outcome.blocker) {
|
|
5018
|
+
exit(1);
|
|
5019
|
+
} else {
|
|
5020
|
+
exit(0);
|
|
5021
|
+
}
|
|
4765
5022
|
}
|
|
4766
5023
|
|
|
4767
5024
|
// src/commands/gate.ts
|
|
4768
5025
|
init_cjs_shims();
|
|
4769
5026
|
var fs26 = __toESM(require("fs"), 1);
|
|
4770
5027
|
var path27 = __toESM(require("path"), 1);
|
|
4771
|
-
var
|
|
5028
|
+
var import_node_child_process7 = require("child_process");
|
|
4772
5029
|
|
|
4773
5030
|
// src/commands/execution-mode.ts
|
|
4774
5031
|
init_cjs_shims();
|
|
@@ -4877,6 +5134,14 @@ function parsePredicate(src) {
|
|
|
4877
5134
|
const value = parseValue(rawVal);
|
|
4878
5135
|
return { kind: "frontmatter", ref, field, op, value };
|
|
4879
5136
|
}
|
|
5137
|
+
const markerNotMatch = s.match(/^body does not contain marker ['"]([A-Z]+)['"]$/);
|
|
5138
|
+
if (markerNotMatch) {
|
|
5139
|
+
const marker = markerNotMatch[1];
|
|
5140
|
+
if (marker !== "TBD" && marker !== "TODO" && marker !== "FIXME") {
|
|
5141
|
+
throw new Error(`unsupported predicate shape: ${src}`);
|
|
5142
|
+
}
|
|
5143
|
+
return { kind: "marker-absence", marker };
|
|
5144
|
+
}
|
|
4880
5145
|
const bodyNotMatch = s.match(/^body does not contain ['"](.+)['"]$/);
|
|
4881
5146
|
if (bodyNotMatch) {
|
|
4882
5147
|
return { kind: "body-contains", needle: bodyNotMatch[1], negated: true };
|
|
@@ -4932,6 +5197,8 @@ function evaluate(predicate, doc, opts) {
|
|
|
4932
5197
|
return evalFrontmatter(parsed, doc, projectRoot);
|
|
4933
5198
|
case "body-contains":
|
|
4934
5199
|
return evalBodyContains(parsed, doc);
|
|
5200
|
+
case "marker-absence":
|
|
5201
|
+
return evalMarkerAbsence(parsed, doc);
|
|
4935
5202
|
case "section":
|
|
4936
5203
|
return evalSection(parsed, doc);
|
|
4937
5204
|
case "file-exists":
|
|
@@ -4954,6 +5221,26 @@ function evalFrontmatter(parsed, doc, projectRoot) {
|
|
|
4954
5221
|
detail: `frontmatter key '${parsed.ref}' is missing or null in ${doc.absPath}`
|
|
4955
5222
|
};
|
|
4956
5223
|
}
|
|
5224
|
+
const refStr = String(refVal);
|
|
5225
|
+
const looksLikeProse = refStr.length > 200 || /[ —–:()\n]/.test(refStr);
|
|
5226
|
+
if (looksLikeProse) {
|
|
5227
|
+
const waiver = doc.fm["proposal_gate_waiver"];
|
|
5228
|
+
const hasExplicitWaiver = waiver !== null && waiver !== void 0 && waiver !== false && String(waiver).trim() !== "" && String(waiver).trim() !== "false";
|
|
5229
|
+
const approvedBy = doc.fm["approved_by"];
|
|
5230
|
+
const approvedAt = doc.fm["approved_at"];
|
|
5231
|
+
const hasApprovalFields = approvedBy !== null && approvedBy !== void 0 && String(approvedBy).trim() !== "" && approvedAt !== null && approvedAt !== void 0 && String(approvedAt).trim() !== "";
|
|
5232
|
+
const hasWaiver = hasExplicitWaiver || hasApprovalFields;
|
|
5233
|
+
if (hasWaiver) {
|
|
5234
|
+
return {
|
|
5235
|
+
pass: true,
|
|
5236
|
+
detail: `context_source is prose; proposal-gate waiver per frontmatter approved_by/approved_at`
|
|
5237
|
+
};
|
|
5238
|
+
}
|
|
5239
|
+
return {
|
|
5240
|
+
pass: false,
|
|
5241
|
+
detail: `context_source is prose but no proposal_gate_waiver (approved_by + approved_at) found in frontmatter`
|
|
5242
|
+
};
|
|
5243
|
+
}
|
|
4957
5244
|
const linkedPath = resolveLinkedPath(String(refVal), doc.absPath, projectRoot);
|
|
4958
5245
|
if (!linkedPath) {
|
|
4959
5246
|
return {
|
|
@@ -5078,6 +5365,38 @@ function evalBodyContains(parsed, doc) {
|
|
|
5078
5365
|
return { pass: false, detail: `'${needle}' not found in body` };
|
|
5079
5366
|
}
|
|
5080
5367
|
}
|
|
5368
|
+
function evalMarkerAbsence(parsed, doc) {
|
|
5369
|
+
const { marker } = parsed;
|
|
5370
|
+
const lines = doc.body.split("\n");
|
|
5371
|
+
const templateSelfRefRe = /^\s*-\s*\[[x ]\]\s*0\s*"TBDs?"\s*exist/i;
|
|
5372
|
+
const markerRe = new RegExp(
|
|
5373
|
+
`(?:^|(?<=\\())${marker}(?=:)|\\(${marker}\\)|\\[${marker}\\]|(?<=//\\s*)${marker}(?!\\w)|(?<=#\\s*)${marker}(?!\\w)`,
|
|
5374
|
+
// # MARKER (comment)
|
|
5375
|
+
"g"
|
|
5376
|
+
);
|
|
5377
|
+
const bareLineRe = new RegExp(`^${marker}$`);
|
|
5378
|
+
const violations = [];
|
|
5379
|
+
for (let i = 0; i < lines.length; i++) {
|
|
5380
|
+
const line = lines[i];
|
|
5381
|
+
if (templateSelfRefRe.test(line)) continue;
|
|
5382
|
+
const trimmed = line.trim();
|
|
5383
|
+
if (bareLineRe.test(trimmed)) {
|
|
5384
|
+
violations.push(i + 1);
|
|
5385
|
+
continue;
|
|
5386
|
+
}
|
|
5387
|
+
markerRe.lastIndex = 0;
|
|
5388
|
+
if (markerRe.test(line)) {
|
|
5389
|
+
violations.push(i + 1);
|
|
5390
|
+
}
|
|
5391
|
+
}
|
|
5392
|
+
if (violations.length > 0) {
|
|
5393
|
+
return {
|
|
5394
|
+
pass: false,
|
|
5395
|
+
detail: `${violations.length} marker occurrence${violations.length === 1 ? "" : "s"} of '${marker}' at line${violations.length === 1 ? "" : "s"} ${violations.join(", ")}`
|
|
5396
|
+
};
|
|
5397
|
+
}
|
|
5398
|
+
return { pass: true, detail: `no '${marker}' markers found in body` };
|
|
5399
|
+
}
|
|
5081
5400
|
function evalSection(parsed, doc) {
|
|
5082
5401
|
const body = doc.body;
|
|
5083
5402
|
const rawParts = body.split(/^(?=## )/m);
|
|
@@ -5446,7 +5765,7 @@ function gateQaHandler(opts, cli) {
|
|
|
5446
5765
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
5447
5766
|
const stderrFn = cli?.stderr ?? ((s) => process.stderr.write(s + "\n"));
|
|
5448
5767
|
const exitFn = cli?.exit ?? ((code2) => process.exit(code2));
|
|
5449
|
-
const spawnFn = cli?.spawnFn ??
|
|
5768
|
+
const spawnFn = cli?.spawnFn ?? import_node_child_process7.spawnSync;
|
|
5450
5769
|
const sprintId = cli?.sprintId ?? "SPRINT-UNKNOWN";
|
|
5451
5770
|
const mode = readSprintExecutionMode(sprintId, {
|
|
5452
5771
|
sprintFilePath: cli?.sprintFilePath,
|
|
@@ -5472,7 +5791,7 @@ function gateArchHandler(opts, cli) {
|
|
|
5472
5791
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
5473
5792
|
const stderrFn = cli?.stderr ?? ((s) => process.stderr.write(s + "\n"));
|
|
5474
5793
|
const exitFn = cli?.exit ?? ((code2) => process.exit(code2));
|
|
5475
|
-
const spawnFn = cli?.spawnFn ??
|
|
5794
|
+
const spawnFn = cli?.spawnFn ?? import_node_child_process7.spawnSync;
|
|
5476
5795
|
const sprintId = cli?.sprintId ?? "SPRINT-UNKNOWN";
|
|
5477
5796
|
const mode = readSprintExecutionMode(sprintId, {
|
|
5478
5797
|
sprintFilePath: cli?.sprintFilePath,
|
|
@@ -5497,13 +5816,13 @@ function gateArchHandler(opts, cli) {
|
|
|
5497
5816
|
|
|
5498
5817
|
// src/commands/gate-run.ts
|
|
5499
5818
|
init_cjs_shims();
|
|
5500
|
-
var
|
|
5819
|
+
var import_node_child_process8 = require("child_process");
|
|
5501
5820
|
var KNOWN_GATES = ["precommit", "test", "typecheck", "lint"];
|
|
5502
5821
|
function gateRunHandler(name, opts, cli) {
|
|
5503
5822
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
5504
5823
|
const stderrFn = cli?.stderr ?? ((s) => process.stderr.write(s + "\n"));
|
|
5505
5824
|
const exitFn = cli?.exit ?? ((code) => process.exit(code));
|
|
5506
|
-
const spawnFn = cli?.spawnFn ??
|
|
5825
|
+
const spawnFn = cli?.spawnFn ?? import_node_child_process8.spawnSync;
|
|
5507
5826
|
const cwd = cli?.cwd ?? process.cwd();
|
|
5508
5827
|
const configLoaderFn = cli?.configLoader ?? loadWikiConfig;
|
|
5509
5828
|
if (!KNOWN_GATES.includes(name)) {
|
|
@@ -5536,7 +5855,7 @@ function gateRunHandler(name, opts, cli) {
|
|
|
5536
5855
|
init_cjs_shims();
|
|
5537
5856
|
var fs27 = __toESM(require("fs"), 1);
|
|
5538
5857
|
var path28 = __toESM(require("path"), 1);
|
|
5539
|
-
var
|
|
5858
|
+
var import_node_child_process9 = require("child_process");
|
|
5540
5859
|
var import_js_yaml7 = __toESM(require("js-yaml"), 1);
|
|
5541
5860
|
var TERMINAL_STATUSES2 = /* @__PURE__ */ new Set(["Completed", "Done", "Abandoned", "Closed", "Resolved"]);
|
|
5542
5861
|
function resolveRunScript(opts) {
|
|
@@ -5551,7 +5870,7 @@ function sprintInitHandler(opts, cli) {
|
|
|
5551
5870
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
5552
5871
|
const stderrFn = cli?.stderr ?? ((s) => process.stderr.write(s + "\n"));
|
|
5553
5872
|
const exitFn = cli?.exit ?? defaultExit;
|
|
5554
|
-
const spawnFn = cli?.spawnFn ??
|
|
5873
|
+
const spawnFn = cli?.spawnFn ?? import_node_child_process9.spawnSync;
|
|
5555
5874
|
const mode = readSprintExecutionMode(opts.sprintId, {
|
|
5556
5875
|
sprintFilePath: cli?.sprintFilePath,
|
|
5557
5876
|
cwd: cli?.cwd
|
|
@@ -5573,7 +5892,7 @@ function sprintCloseHandler(opts, cli) {
|
|
|
5573
5892
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
5574
5893
|
const stderrFn = cli?.stderr ?? ((s) => process.stderr.write(s + "\n"));
|
|
5575
5894
|
const exitFn = cli?.exit ?? defaultExit;
|
|
5576
|
-
const spawnFn = cli?.spawnFn ??
|
|
5895
|
+
const spawnFn = cli?.spawnFn ?? import_node_child_process9.spawnSync;
|
|
5577
5896
|
const mode = readSprintExecutionMode(opts.sprintId, {
|
|
5578
5897
|
sprintFilePath: cli?.sprintFilePath,
|
|
5579
5898
|
cwd: cli?.cwd
|
|
@@ -5678,7 +5997,7 @@ async function sprintArchiveHandler(opts, cli) {
|
|
|
5678
5997
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
5679
5998
|
const stderrFn = cli?.stderr ?? ((s) => process.stderr.write(s + "\n"));
|
|
5680
5999
|
const exitFn = cli?.exit ?? defaultExit;
|
|
5681
|
-
const spawnFn = cli?.spawnFn ??
|
|
6000
|
+
const spawnFn = cli?.spawnFn ?? import_node_child_process9.spawnSync;
|
|
5682
6001
|
const cwd = cli?.cwd ?? process.cwd();
|
|
5683
6002
|
const wikiBuildFn = cli?.wikiBuildFn ?? (async (wCwd, wStdout) => {
|
|
5684
6003
|
const fakeExit = (code) => {
|
|
@@ -5903,7 +6222,7 @@ async function sprintArchiveHandler(opts, cli) {
|
|
|
5903
6222
|
init_cjs_shims();
|
|
5904
6223
|
var fs28 = __toESM(require("fs"), 1);
|
|
5905
6224
|
var path29 = __toESM(require("path"), 1);
|
|
5906
|
-
var
|
|
6225
|
+
var import_node_child_process10 = require("child_process");
|
|
5907
6226
|
function defaultExit2(code) {
|
|
5908
6227
|
return process.exit(code);
|
|
5909
6228
|
}
|
|
@@ -5929,7 +6248,7 @@ function storyStartHandler(opts, cli) {
|
|
|
5929
6248
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
5930
6249
|
const stderrFn = cli?.stderr ?? ((s) => process.stderr.write(s + "\n"));
|
|
5931
6250
|
const exitFn = cli?.exit ?? defaultExit2;
|
|
5932
|
-
const spawnFn = cli?.spawnFn ??
|
|
6251
|
+
const spawnFn = cli?.spawnFn ?? import_node_child_process10.spawnSync;
|
|
5933
6252
|
const cwd = cli?.cwd ?? process.cwd();
|
|
5934
6253
|
const sprintId = cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? "SPRINT-UNKNOWN";
|
|
5935
6254
|
const mode = readSprintExecutionMode(sprintId, {
|
|
@@ -6007,7 +6326,7 @@ function storyCompleteHandler(opts, cli) {
|
|
|
6007
6326
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
6008
6327
|
const stderrFn = cli?.stderr ?? ((s) => process.stderr.write(s + "\n"));
|
|
6009
6328
|
const exitFn = cli?.exit ?? defaultExit2;
|
|
6010
|
-
const spawnFn = cli?.spawnFn ??
|
|
6329
|
+
const spawnFn = cli?.spawnFn ?? import_node_child_process10.spawnSync;
|
|
6011
6330
|
const cwd = cli?.cwd ?? process.cwd();
|
|
6012
6331
|
const sprintId = cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? "SPRINT-UNKNOWN";
|
|
6013
6332
|
const mode = readSprintExecutionMode(sprintId, {
|
|
@@ -6118,7 +6437,7 @@ function storyCompleteHandler(opts, cli) {
|
|
|
6118
6437
|
// src/commands/state.ts
|
|
6119
6438
|
init_cjs_shims();
|
|
6120
6439
|
var path30 = __toESM(require("path"), 1);
|
|
6121
|
-
var
|
|
6440
|
+
var import_node_child_process11 = require("child_process");
|
|
6122
6441
|
function defaultExit3(code) {
|
|
6123
6442
|
return process.exit(code);
|
|
6124
6443
|
}
|
|
@@ -6131,7 +6450,7 @@ function stateUpdateHandler(opts, cli) {
|
|
|
6131
6450
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
6132
6451
|
const stderrFn = cli?.stderr ?? ((s) => process.stderr.write(s + "\n"));
|
|
6133
6452
|
const exitFn = cli?.exit ?? defaultExit3;
|
|
6134
|
-
const spawnFn = cli?.spawnFn ??
|
|
6453
|
+
const spawnFn = cli?.spawnFn ?? import_node_child_process11.spawnSync;
|
|
6135
6454
|
const cwd = cli?.cwd;
|
|
6136
6455
|
const sprintId = cli?.sprintId ?? resolveSprintIdFromSentinel(cwd) ?? "SPRINT-UNKNOWN";
|
|
6137
6456
|
const mode = readSprintExecutionMode(sprintId, {
|
|
@@ -6158,7 +6477,7 @@ function stateValidateHandler(opts, cli) {
|
|
|
6158
6477
|
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
6159
6478
|
const stderrFn = cli?.stderr ?? ((s) => process.stderr.write(s + "\n"));
|
|
6160
6479
|
const exitFn = cli?.exit ?? defaultExit3;
|
|
6161
|
-
const spawnFn = cli?.spawnFn ??
|
|
6480
|
+
const spawnFn = cli?.spawnFn ?? import_node_child_process11.spawnSync;
|
|
6162
6481
|
const mode = readSprintExecutionMode(opts.sprintId, {
|
|
6163
6482
|
sprintFilePath: cli?.sprintFilePath,
|
|
6164
6483
|
cwd: cli?.cwd
|
|
@@ -6635,7 +6954,7 @@ async function promptMergeChoice(opts) {
|
|
|
6635
6954
|
|
|
6636
6955
|
// src/lib/editor.ts
|
|
6637
6956
|
init_cjs_shims();
|
|
6638
|
-
var
|
|
6957
|
+
var import_node_child_process12 = require("child_process");
|
|
6639
6958
|
async function openInEditor(filePath, opts) {
|
|
6640
6959
|
const env = opts?.env ?? process.env;
|
|
6641
6960
|
const editor = opts?.editor ?? env["EDITOR"] ?? env["VISUAL"];
|
|
@@ -6643,7 +6962,7 @@ async function openInEditor(filePath, opts) {
|
|
|
6643
6962
|
throw new Error("$EDITOR not set; cannot [e]dit option. Set the EDITOR environment variable.");
|
|
6644
6963
|
}
|
|
6645
6964
|
return new Promise((resolve14, reject) => {
|
|
6646
|
-
const child = (0,
|
|
6965
|
+
const child = (0, import_node_child_process12.spawn)(editor, [filePath], {
|
|
6647
6966
|
stdio: "inherit",
|
|
6648
6967
|
env: { ...env }
|
|
6649
6968
|
});
|
|
@@ -6918,7 +7237,7 @@ init_cjs_shims();
|
|
|
6918
7237
|
var fs31 = __toESM(require("fs"), 1);
|
|
6919
7238
|
var fsp2 = __toESM(require("fs/promises"), 1);
|
|
6920
7239
|
var path34 = __toESM(require("path"), 1);
|
|
6921
|
-
var
|
|
7240
|
+
var import_node_child_process13 = require("child_process");
|
|
6922
7241
|
var USER_ARTIFACT_TIERS = ["user-artifact"];
|
|
6923
7242
|
var FRAMEWORK_TIERS = ["protocol", "template", "agent", "hook", "skill", "cli-config", "derived"];
|
|
6924
7243
|
function parseTierList(raw) {
|
|
@@ -6959,7 +7278,7 @@ function resolveProjectName(target) {
|
|
|
6959
7278
|
function detectUncommittedChanges(target, manifestPaths, gitRunner) {
|
|
6960
7279
|
const run = gitRunner ?? ((args) => {
|
|
6961
7280
|
try {
|
|
6962
|
-
const out = (0,
|
|
7281
|
+
const out = (0, import_node_child_process13.execSync)(["git", ...args].join(" "), {
|
|
6963
7282
|
cwd: target,
|
|
6964
7283
|
stdio: ["pipe", "pipe", "pipe"],
|
|
6965
7284
|
encoding: "utf-8"
|
|
@@ -9165,6 +9484,109 @@ async function adminLoginHandler(opts = {}) {
|
|
|
9165
9484
|
stdout(`Credentials saved to ${authFilePath} (chmod 600).`);
|
|
9166
9485
|
}
|
|
9167
9486
|
|
|
9487
|
+
// src/commands/hotfix.ts
|
|
9488
|
+
init_cjs_shims();
|
|
9489
|
+
var fs36 = __toESM(require("fs"), 1);
|
|
9490
|
+
var path47 = __toESM(require("path"), 1);
|
|
9491
|
+
function defaultExit4(code) {
|
|
9492
|
+
return process.exit(code);
|
|
9493
|
+
}
|
|
9494
|
+
var SLUG_RE = /^[a-z0-9-]+$/;
|
|
9495
|
+
var HOTFIX_FILE_RE = /^HOTFIX-(\d+)_.*\.md$/;
|
|
9496
|
+
function maxHotfixId(pendingDir) {
|
|
9497
|
+
let max = 0;
|
|
9498
|
+
let entries;
|
|
9499
|
+
try {
|
|
9500
|
+
entries = fs36.readdirSync(pendingDir);
|
|
9501
|
+
} catch {
|
|
9502
|
+
return 0;
|
|
9503
|
+
}
|
|
9504
|
+
for (const entry of entries) {
|
|
9505
|
+
const m = HOTFIX_FILE_RE.exec(entry);
|
|
9506
|
+
if (m) {
|
|
9507
|
+
const n = parseInt(m[1], 10);
|
|
9508
|
+
if (n > max) max = n;
|
|
9509
|
+
}
|
|
9510
|
+
}
|
|
9511
|
+
return max;
|
|
9512
|
+
}
|
|
9513
|
+
function countActiveHotfixes(repoRoot) {
|
|
9514
|
+
const pendingDir = path47.join(repoRoot, ".cleargate", "delivery", "pending-sync");
|
|
9515
|
+
const archiveDir = path47.join(repoRoot, ".cleargate", "delivery", "archive");
|
|
9516
|
+
const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1e3;
|
|
9517
|
+
let count = 0;
|
|
9518
|
+
let pendingEntries = [];
|
|
9519
|
+
try {
|
|
9520
|
+
pendingEntries = fs36.readdirSync(pendingDir);
|
|
9521
|
+
} catch {
|
|
9522
|
+
}
|
|
9523
|
+
for (const entry of pendingEntries) {
|
|
9524
|
+
if (entry.startsWith("HOTFIX-") && entry.endsWith(".md")) count++;
|
|
9525
|
+
}
|
|
9526
|
+
let archiveEntries = [];
|
|
9527
|
+
try {
|
|
9528
|
+
archiveEntries = fs36.readdirSync(archiveDir);
|
|
9529
|
+
} catch {
|
|
9530
|
+
}
|
|
9531
|
+
for (const entry of archiveEntries) {
|
|
9532
|
+
if (entry.startsWith("HOTFIX-") && entry.endsWith(".md")) {
|
|
9533
|
+
try {
|
|
9534
|
+
const stat = fs36.statSync(path47.join(archiveDir, entry));
|
|
9535
|
+
if (stat.mtimeMs >= sevenDaysAgo) count++;
|
|
9536
|
+
} catch {
|
|
9537
|
+
}
|
|
9538
|
+
}
|
|
9539
|
+
}
|
|
9540
|
+
return count;
|
|
9541
|
+
}
|
|
9542
|
+
function resolveTemplatePath(repoRoot) {
|
|
9543
|
+
return path47.join(repoRoot, ".cleargate", "templates", "hotfix.md");
|
|
9544
|
+
}
|
|
9545
|
+
function hotfixNewHandler(opts, cli) {
|
|
9546
|
+
const stdoutFn = cli?.stdout ?? ((s) => process.stdout.write(s + "\n"));
|
|
9547
|
+
const stderrFn = cli?.stderr ?? ((s) => process.stderr.write(s + "\n"));
|
|
9548
|
+
const exitFn = cli?.exit ?? defaultExit4;
|
|
9549
|
+
const repoRoot = cli?.cwd ?? process.cwd();
|
|
9550
|
+
const now = cli?.now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
9551
|
+
if (!SLUG_RE.test(opts.slug)) {
|
|
9552
|
+
stderrFn(`[cleargate hotfix new] slug must match ^[a-z0-9-]+$ (got: "${opts.slug}")`);
|
|
9553
|
+
return exitFn(1);
|
|
9554
|
+
}
|
|
9555
|
+
const activeCount = countActiveHotfixes(repoRoot);
|
|
9556
|
+
if (activeCount >= 3) {
|
|
9557
|
+
stderrFn(
|
|
9558
|
+
`Hotfix cap: \u22643 per rolling 7-day window. Currently ${activeCount} active. Bundle into a sprint or downgrade one to a CR.`
|
|
9559
|
+
);
|
|
9560
|
+
return exitFn(1);
|
|
9561
|
+
}
|
|
9562
|
+
const pendingDir = path47.join(repoRoot, ".cleargate", "delivery", "pending-sync");
|
|
9563
|
+
const maxId = maxHotfixId(pendingDir);
|
|
9564
|
+
const nextId = maxId + 1;
|
|
9565
|
+
const idStr = `HOTFIX-${String(nextId).padStart(3, "0")}`;
|
|
9566
|
+
const templatePath = resolveTemplatePath(repoRoot);
|
|
9567
|
+
let templateContent;
|
|
9568
|
+
try {
|
|
9569
|
+
templateContent = fs36.readFileSync(templatePath, "utf8");
|
|
9570
|
+
} catch {
|
|
9571
|
+
stderrFn(`[cleargate hotfix new] template not found: ${templatePath}`);
|
|
9572
|
+
return exitFn(2);
|
|
9573
|
+
}
|
|
9574
|
+
const content = templateContent.replace(/\{ID\}/g, idStr).replace(/\{SLUG\}/g, opts.slug).replace(/\{ISO\}/g, now);
|
|
9575
|
+
const fileSlug = opts.slug.replace(/-/g, "_");
|
|
9576
|
+
const fileName = `${idStr}_${fileSlug}.md`;
|
|
9577
|
+
const outPath = path47.join(pendingDir, fileName);
|
|
9578
|
+
try {
|
|
9579
|
+
fs36.mkdirSync(pendingDir, { recursive: true });
|
|
9580
|
+
fs36.writeFileSync(outPath, content, "utf8");
|
|
9581
|
+
} catch (err) {
|
|
9582
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
9583
|
+
stderrFn(`[cleargate hotfix new] write failed: ${msg}`);
|
|
9584
|
+
return exitFn(1);
|
|
9585
|
+
}
|
|
9586
|
+
stdoutFn(`[cleargate hotfix new] created: ${outPath}`);
|
|
9587
|
+
return exitFn(0);
|
|
9588
|
+
}
|
|
9589
|
+
|
|
9168
9590
|
// src/cli.ts
|
|
9169
9591
|
var program = new import_commander.Command();
|
|
9170
9592
|
program.name("cleargate").description("ClearGate CLI \u2014 connects AI agent teams to the ClearGate MCP server").version(package_default.version, "-V, --version").option("--profile <name>", "configuration profile to use", "default").option("--mcp-url <url>", "MCP server URL (overrides config file and env)").showHelpAfterError("(use `cleargate --help`)");
|
|
@@ -9181,8 +9603,8 @@ program.command("join <invite-url>").description("join a ClearGate workspace usi
|
|
|
9181
9603
|
...cmdOpts.code !== void 0 ? { code: cmdOpts.code } : {}
|
|
9182
9604
|
});
|
|
9183
9605
|
});
|
|
9184
|
-
program.command("init").description("initialise a repo with ClearGate scaffold (CLAUDE.md block, hook config, agents, templates)").option("--force", "overwrite existing files that differ from the bundled payload").option("--yes", "non-interactive: accept all defaults without prompting").action(async (opts) => {
|
|
9185
|
-
await initHandler({ force: opts.force ?? false, yes: opts.yes ?? false });
|
|
9606
|
+
program.command("init").description("initialise a repo with ClearGate scaffold (CLAUDE.md block, hook config, agents, templates)").option("--force", "overwrite existing files that differ from the bundled payload").option("--yes", "non-interactive: accept all defaults without prompting").option("--pin <ver>", "CR-009: pin hook resolver to a specific cleargate CLI version (default: package version)").action(async (opts) => {
|
|
9607
|
+
await initHandler({ force: opts.force ?? false, yes: opts.yes ?? false, pin: opts.pin });
|
|
9186
9608
|
});
|
|
9187
9609
|
program.command("whoami").description("print the currently authenticated agent identity").action(async () => {
|
|
9188
9610
|
const { whoamiHandler: whoamiHandler2 } = await Promise.resolve().then(() => (init_whoami(), whoami_exports));
|
|
@@ -9304,14 +9726,20 @@ admin.command("bootstrap-root <handle>").description("seed the first root admin
|
|
|
9304
9726
|
const { bootstrapRootHandler: bootstrapRootHandler2 } = await Promise.resolve().then(() => (init_bootstrap_root(), bootstrap_root_exports));
|
|
9305
9727
|
await bootstrapRootHandler2({ handle, databaseUrl: opts.databaseUrl, force: opts.force ?? false });
|
|
9306
9728
|
});
|
|
9307
|
-
program.command("doctor").description("diagnose scaffold drift, hook health, blocked items, and token cost").option("--check-scaffold", "check scaffold files for drift against install snapshot").option("--session-start-mode", "hidden: enables daily throttle (used by session-start hook)", false).option("--session-start", "emit blocked pending-sync items summary (used by SessionStart hook)").option("--pricing <file>", "compute USD cost estimate from a work item's draft_tokens").option("-v, --verbose", "show per-file drift detail").addHelpText("after", [
|
|
9729
|
+
program.command("doctor").description("diagnose scaffold drift, hook health, blocked items, and token cost").option("--check-scaffold", "check scaffold files for drift against install snapshot").option("--session-start-mode", "hidden: enables daily throttle (used by session-start hook)", false).option("--session-start", "emit blocked pending-sync items summary (used by SessionStart hook)").option("--pricing <file>", "compute USD cost estimate from a work item's draft_tokens").option("--can-edit <file>", "CR-008: exit 0 if editing file is allowed, exit 1 if planning required").option("--cwd <dir>", "working directory for the doctor check (default: process.cwd())").option("-v, --verbose", "show per-file drift detail").addHelpText("after", [
|
|
9308
9730
|
"",
|
|
9309
9731
|
"Modes (mutually exclusive):",
|
|
9310
9732
|
" --check-scaffold Compute drift for all tracked scaffold files.",
|
|
9311
9733
|
" Writes .cleargate/.drift-state.json.",
|
|
9312
9734
|
" --session-start List blocked pending-sync items (\u226410, \u2264100 tokens).",
|
|
9313
9735
|
" --pricing <file> Compute USD estimate from a work item's draft_tokens.",
|
|
9314
|
-
"
|
|
9736
|
+
" --can-edit <file> Check if editing a file requires a planning work item.",
|
|
9737
|
+
" (default) Print a minimal hook-config health report.",
|
|
9738
|
+
"",
|
|
9739
|
+
"Exit codes:",
|
|
9740
|
+
" 0 Clean \u2014 no blockers, no config errors.",
|
|
9741
|
+
" 1 Blocked items or advisory issues \u2014 see stdout.",
|
|
9742
|
+
" 2 ClearGate misconfigured or partially installed \u2014 see stdout for remediation."
|
|
9315
9743
|
].join("\n")).action(async (opts) => {
|
|
9316
9744
|
await doctorHandler({
|
|
9317
9745
|
checkScaffold: opts.checkScaffold,
|
|
@@ -9319,8 +9747,10 @@ program.command("doctor").description("diagnose scaffold drift, hook health, blo
|
|
|
9319
9747
|
sessionStart: opts.sessionStart,
|
|
9320
9748
|
pricing: !!opts.pricing,
|
|
9321
9749
|
pricingFile: opts.pricing,
|
|
9750
|
+
canEdit: !!opts.canEdit,
|
|
9751
|
+
canEditFile: opts.canEdit,
|
|
9322
9752
|
verbose: opts.verbose
|
|
9323
|
-
});
|
|
9753
|
+
}, opts.cwd ? { cwd: opts.cwd } : void 0);
|
|
9324
9754
|
});
|
|
9325
9755
|
program.command("upgrade").description("three-way merge scaffold files with upstream changes").option("--dry-run", "print plan without making any changes").option("--yes", 'auto-accept "take theirs" for all merge-3way files (non-interactive)').option("--only <tier>", "restrict to a specific scaffold tier (protocol/template/agent/hook/skill/cli-config)").addHelpText("after", [
|
|
9326
9756
|
"",
|
|
@@ -9342,7 +9772,7 @@ program.command("uninstall").description("remove ClearGate scaffold from a proje
|
|
|
9342
9772
|
"",
|
|
9343
9773
|
"Always removed (no prompt): .claude/agents/*.md, ClearGate hooks,",
|
|
9344
9774
|
" .claude/skills/flashcard/, CLAUDE.md CLEARGATE block,",
|
|
9345
|
-
"
|
|
9775
|
+
" `cleargate` from package.json, .install-manifest.json, .drift-state.json.",
|
|
9346
9776
|
"",
|
|
9347
9777
|
"Non-git targets: uncommitted-changes check is skipped silently."
|
|
9348
9778
|
].join("\n")).action(async (opts) => {
|
|
@@ -9395,5 +9825,9 @@ program.command("sync-log").description("filter and print sync-log entries").opt
|
|
|
9395
9825
|
limit: opts.limit !== void 0 ? parseInt(opts.limit, 10) : 50
|
|
9396
9826
|
});
|
|
9397
9827
|
});
|
|
9828
|
+
var hotfix = program.command("hotfix").description("hotfix lane commands (off-sprint trivial fix scaffolding)");
|
|
9829
|
+
hotfix.command("new <slug>").description("scaffold a new HOTFIX-NNN_<slug>.md in pending-sync/").action((slug) => {
|
|
9830
|
+
hotfixNewHandler({ slug });
|
|
9831
|
+
});
|
|
9398
9832
|
void program.parseAsync(process.argv);
|
|
9399
9833
|
//# sourceMappingURL=cli.cjs.map
|