@anthropologies/claudestory 0.1.33 → 0.1.35
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/cli.js +213 -24
- package/dist/index.d.ts +80 -40
- package/dist/index.js +3 -1
- package/dist/mcp.js +70 -22
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -298,7 +298,9 @@ var init_config = __esm({
|
|
|
298
298
|
recipeOverrides: z7.object({
|
|
299
299
|
maxTicketsPerSession: z7.number().min(0).optional(),
|
|
300
300
|
compactThreshold: z7.string().optional(),
|
|
301
|
-
reviewBackends: z7.array(z7.string()).optional()
|
|
301
|
+
reviewBackends: z7.array(z7.string()).optional(),
|
|
302
|
+
handoverInterval: z7.number().min(0).optional(),
|
|
303
|
+
stages: z7.record(z7.record(z7.unknown())).optional()
|
|
302
304
|
}).optional()
|
|
303
305
|
}).passthrough();
|
|
304
306
|
}
|
|
@@ -3380,7 +3382,20 @@ function validateParentTicket(parentId, ticketId, state) {
|
|
|
3380
3382
|
throw new CliValidationError("invalid_input", `Parent ticket ${parentId} not found`);
|
|
3381
3383
|
}
|
|
3382
3384
|
}
|
|
3385
|
+
function buildErrorMultiset(findings) {
|
|
3386
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3387
|
+
const messages = /* @__PURE__ */ new Map();
|
|
3388
|
+
for (const f of findings) {
|
|
3389
|
+
if (f.level !== "error") continue;
|
|
3390
|
+
const key = `${f.code}|${f.entity ?? ""}|${f.message}`;
|
|
3391
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
3392
|
+
messages.set(key, f.message);
|
|
3393
|
+
}
|
|
3394
|
+
return { counts, messages };
|
|
3395
|
+
}
|
|
3383
3396
|
function validatePostWriteState(candidate, state, isCreate) {
|
|
3397
|
+
const preResult = validateProject(state);
|
|
3398
|
+
const { counts: preErrors } = buildErrorMultiset(preResult.findings);
|
|
3384
3399
|
const existingTickets = [...state.tickets];
|
|
3385
3400
|
if (isCreate) {
|
|
3386
3401
|
existingTickets.push(candidate);
|
|
@@ -3397,11 +3412,17 @@ function validatePostWriteState(candidate, state, isCreate) {
|
|
|
3397
3412
|
config: state.config,
|
|
3398
3413
|
handoverFilenames: [...state.handoverFilenames]
|
|
3399
3414
|
});
|
|
3400
|
-
const
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
3415
|
+
const postResult = validateProject(postState);
|
|
3416
|
+
const { counts: postErrors, messages: postMessages } = buildErrorMultiset(postResult.findings);
|
|
3417
|
+
const newErrors = [];
|
|
3418
|
+
for (const [key, postCount] of postErrors) {
|
|
3419
|
+
const preCount = preErrors.get(key) ?? 0;
|
|
3420
|
+
if (postCount > preCount) {
|
|
3421
|
+
newErrors.push(postMessages.get(key) ?? key);
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
if (newErrors.length > 0) {
|
|
3425
|
+
throw new CliValidationError("validation_failed", `Write would create invalid state: ${newErrors.join("; ")}`);
|
|
3405
3426
|
}
|
|
3406
3427
|
}
|
|
3407
3428
|
async function handleTicketCreate(args, format, root) {
|
|
@@ -3585,7 +3606,20 @@ function validateRelatedTickets(ids, state) {
|
|
|
3585
3606
|
}
|
|
3586
3607
|
}
|
|
3587
3608
|
}
|
|
3609
|
+
function buildErrorMultiset2(findings) {
|
|
3610
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3611
|
+
const messages = /* @__PURE__ */ new Map();
|
|
3612
|
+
for (const f of findings) {
|
|
3613
|
+
if (f.level !== "error") continue;
|
|
3614
|
+
const key = `${f.code}|${f.entity ?? ""}|${f.message}`;
|
|
3615
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
3616
|
+
messages.set(key, f.message);
|
|
3617
|
+
}
|
|
3618
|
+
return { counts, messages };
|
|
3619
|
+
}
|
|
3588
3620
|
function validatePostWriteIssueState(candidate, state, isCreate) {
|
|
3621
|
+
const preResult = validateProject(state);
|
|
3622
|
+
const { counts: preErrors } = buildErrorMultiset2(preResult.findings);
|
|
3589
3623
|
const existingIssues = [...state.issues];
|
|
3590
3624
|
if (isCreate) {
|
|
3591
3625
|
existingIssues.push(candidate);
|
|
@@ -3602,11 +3636,17 @@ function validatePostWriteIssueState(candidate, state, isCreate) {
|
|
|
3602
3636
|
config: state.config,
|
|
3603
3637
|
handoverFilenames: [...state.handoverFilenames]
|
|
3604
3638
|
});
|
|
3605
|
-
const
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3639
|
+
const postResult = validateProject(postState);
|
|
3640
|
+
const { counts: postErrors, messages: postMessages } = buildErrorMultiset2(postResult.findings);
|
|
3641
|
+
const newErrors = [];
|
|
3642
|
+
for (const [key, postCount] of postErrors) {
|
|
3643
|
+
const preCount = preErrors.get(key) ?? 0;
|
|
3644
|
+
if (postCount > preCount) {
|
|
3645
|
+
newErrors.push(postMessages.get(key) ?? key);
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
if (newErrors.length > 0) {
|
|
3649
|
+
throw new CliValidationError("validation_failed", `Write would create invalid state: ${newErrors.join("; ")}`);
|
|
3610
3650
|
}
|
|
3611
3651
|
}
|
|
3612
3652
|
async function handleIssueCreate(args, format, root) {
|
|
@@ -5904,7 +5944,19 @@ function resolveRecipe(recipeName, projectOverrides) {
|
|
|
5904
5944
|
}
|
|
5905
5945
|
}
|
|
5906
5946
|
let pipeline = raw.pipeline ? [...raw.pipeline] : [...DEFAULT_PIPELINE];
|
|
5907
|
-
const
|
|
5947
|
+
const recipeStages = raw.stages ?? {};
|
|
5948
|
+
const stageOverrides = projectOverrides?.stages ?? {};
|
|
5949
|
+
const stages2 = {};
|
|
5950
|
+
for (const [key, value] of Object.entries(recipeStages)) {
|
|
5951
|
+
const override = stageOverrides[key];
|
|
5952
|
+
const safeOverride = override && typeof override === "object" && !Array.isArray(override) ? override : {};
|
|
5953
|
+
stages2[key] = { ...value, ...safeOverride };
|
|
5954
|
+
}
|
|
5955
|
+
for (const [key, value] of Object.entries(stageOverrides)) {
|
|
5956
|
+
if (!stages2[key] && value && typeof value === "object" && !Array.isArray(value)) {
|
|
5957
|
+
stages2[key] = { ...value };
|
|
5958
|
+
}
|
|
5959
|
+
}
|
|
5908
5960
|
if (stages2.WRITE_TESTS?.enabled) {
|
|
5909
5961
|
const implementIdx = pipeline.indexOf("IMPLEMENT");
|
|
5910
5962
|
if (implementIdx !== -1 && !pipeline.includes("WRITE_TESTS")) {
|
|
@@ -8059,6 +8111,9 @@ async function handleStart(root, args) {
|
|
|
8059
8111
|
if (typeof overrides.compactThreshold === "string") sessionConfig.compactThreshold = overrides.compactThreshold;
|
|
8060
8112
|
if (Array.isArray(overrides.reviewBackends)) sessionConfig.reviewBackends = overrides.reviewBackends;
|
|
8061
8113
|
if (typeof overrides.handoverInterval === "number") sessionConfig.handoverInterval = overrides.handoverInterval;
|
|
8114
|
+
if (overrides.stages && typeof overrides.stages === "object") {
|
|
8115
|
+
sessionConfig.stageOverrides = overrides.stages;
|
|
8116
|
+
}
|
|
8062
8117
|
}
|
|
8063
8118
|
} catch {
|
|
8064
8119
|
}
|
|
@@ -8068,7 +8123,8 @@ async function handleStart(root, args) {
|
|
|
8068
8123
|
const resolvedRecipe = resolveRecipe(recipe, {
|
|
8069
8124
|
maxTicketsPerSession: sessionConfig.maxTicketsPerSession,
|
|
8070
8125
|
compactThreshold: sessionConfig.compactThreshold,
|
|
8071
|
-
reviewBackends: sessionConfig.reviewBackends
|
|
8126
|
+
reviewBackends: sessionConfig.reviewBackends,
|
|
8127
|
+
stages: sessionConfig.stageOverrides
|
|
8072
8128
|
});
|
|
8073
8129
|
const session = createSession(root, recipe, wsId, sessionConfig);
|
|
8074
8130
|
const dir = sessionDir(root, session.sessionId);
|
|
@@ -8868,14 +8924,6 @@ async function handleCancel(root, args) {
|
|
|
8868
8924
|
if (info.state.state === "SESSION_END" || info.state.status === "completed") {
|
|
8869
8925
|
return guideError(new Error("Session already ended."));
|
|
8870
8926
|
}
|
|
8871
|
-
const CANCELLABLE_STATES = /* @__PURE__ */ new Set(["PICK_TICKET", "COMPLETE", "HANDOVER"]);
|
|
8872
|
-
if (info.state.recipe === "coding" && !CANCELLABLE_STATES.has(info.state.state)) {
|
|
8873
|
-
const sessionMode = info.state.mode ?? "auto";
|
|
8874
|
-
const modeGuidance = sessionMode === "plan" ? "Plan mode sessions end after plan review approval \u2014 continue to that step." : sessionMode === "review" ? "Review mode sessions end after code review approval \u2014 continue to that step." : sessionMode === "guided" ? "Guided mode sessions end after ticket completion \u2014 continue to FINALIZE." : "Complete the current ticket and write a handover to end the session.";
|
|
8875
|
-
return guideError(new Error(
|
|
8876
|
-
`Cannot cancel a coding session from ${info.state.state}. ${modeGuidance}`
|
|
8877
|
-
));
|
|
8878
|
-
}
|
|
8879
8927
|
await recoverPendingMutation(info.dir, info.state, root);
|
|
8880
8928
|
const cancelInfo = findSessionById(root, args.sessionId) ?? info;
|
|
8881
8929
|
let ticketReleased = false;
|
|
@@ -10363,7 +10411,7 @@ var init_mcp = __esm({
|
|
|
10363
10411
|
init_init();
|
|
10364
10412
|
ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
|
|
10365
10413
|
CONFIG_PATH2 = ".story/config.json";
|
|
10366
|
-
version = "0.1.
|
|
10414
|
+
version = "0.1.35";
|
|
10367
10415
|
main().catch((err) => {
|
|
10368
10416
|
process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
|
|
10369
10417
|
`);
|
|
@@ -10511,6 +10559,102 @@ var init_run = __esm({
|
|
|
10511
10559
|
}
|
|
10512
10560
|
});
|
|
10513
10561
|
|
|
10562
|
+
// src/cli/commands/repair.ts
|
|
10563
|
+
function computeRepairs(state, warnings) {
|
|
10564
|
+
const integrityWarning = warnings.find(
|
|
10565
|
+
(w) => INTEGRITY_WARNING_TYPES.includes(w.type)
|
|
10566
|
+
);
|
|
10567
|
+
if (integrityWarning) {
|
|
10568
|
+
return {
|
|
10569
|
+
fixes: [],
|
|
10570
|
+
error: `Cannot repair: data integrity issue in ${integrityWarning.file}: ${integrityWarning.message}. Fix the corrupt file first, then retry.`,
|
|
10571
|
+
tickets: [],
|
|
10572
|
+
issues: []
|
|
10573
|
+
};
|
|
10574
|
+
}
|
|
10575
|
+
const fixes = [];
|
|
10576
|
+
const modifiedTickets = [];
|
|
10577
|
+
const modifiedIssues = [];
|
|
10578
|
+
const ticketIDs = new Set(state.tickets.map((t) => t.id));
|
|
10579
|
+
const phaseIDs = new Set(state.roadmap.phases.map((p) => {
|
|
10580
|
+
const id = p.id;
|
|
10581
|
+
return typeof id === "object" && id !== null ? id.rawValue ?? String(id) : String(id);
|
|
10582
|
+
}));
|
|
10583
|
+
for (const ticket of state.tickets) {
|
|
10584
|
+
let modified = false;
|
|
10585
|
+
let blockedBy = [...ticket.blockedBy];
|
|
10586
|
+
let parentTicket = ticket.parentTicket;
|
|
10587
|
+
let phase = ticket.phase;
|
|
10588
|
+
const validBlockedBy = blockedBy.filter((ref) => ticketIDs.has(ref));
|
|
10589
|
+
if (validBlockedBy.length < blockedBy.length) {
|
|
10590
|
+
const removed = blockedBy.filter((ref) => !ticketIDs.has(ref));
|
|
10591
|
+
blockedBy = validBlockedBy;
|
|
10592
|
+
modified = true;
|
|
10593
|
+
fixes.push({ entity: ticket.id, field: "blockedBy", description: `Removed stale refs: ${removed.join(", ")}` });
|
|
10594
|
+
}
|
|
10595
|
+
if (parentTicket && !ticketIDs.has(parentTicket)) {
|
|
10596
|
+
fixes.push({ entity: ticket.id, field: "parentTicket", description: `Cleared stale ref: ${parentTicket}` });
|
|
10597
|
+
parentTicket = null;
|
|
10598
|
+
modified = true;
|
|
10599
|
+
}
|
|
10600
|
+
const phaseRaw = typeof phase === "object" && phase !== null ? phase.rawValue ?? String(phase) : phase != null ? String(phase) : null;
|
|
10601
|
+
if (phaseRaw && !phaseIDs.has(phaseRaw)) {
|
|
10602
|
+
fixes.push({ entity: ticket.id, field: "phase", description: `Cleared stale phase: ${phaseRaw}` });
|
|
10603
|
+
phase = null;
|
|
10604
|
+
modified = true;
|
|
10605
|
+
}
|
|
10606
|
+
if (modified) {
|
|
10607
|
+
modifiedTickets.push({ ...ticket, blockedBy, parentTicket, phase });
|
|
10608
|
+
}
|
|
10609
|
+
}
|
|
10610
|
+
for (const issue of state.issues) {
|
|
10611
|
+
let modified = false;
|
|
10612
|
+
let relatedTickets = [...issue.relatedTickets];
|
|
10613
|
+
let phase = issue.phase;
|
|
10614
|
+
const validRelated = relatedTickets.filter((ref) => ticketIDs.has(ref));
|
|
10615
|
+
if (validRelated.length < relatedTickets.length) {
|
|
10616
|
+
const removed = relatedTickets.filter((ref) => !ticketIDs.has(ref));
|
|
10617
|
+
relatedTickets = validRelated;
|
|
10618
|
+
modified = true;
|
|
10619
|
+
fixes.push({ entity: issue.id, field: "relatedTickets", description: `Removed stale refs: ${removed.join(", ")}` });
|
|
10620
|
+
}
|
|
10621
|
+
const issuePhaseRaw = typeof phase === "object" && phase !== null ? phase.rawValue ?? String(phase) : phase != null ? String(phase) : null;
|
|
10622
|
+
if (issuePhaseRaw && !phaseIDs.has(issuePhaseRaw)) {
|
|
10623
|
+
fixes.push({ entity: issue.id, field: "phase", description: `Cleared stale phase: ${issuePhaseRaw}` });
|
|
10624
|
+
phase = null;
|
|
10625
|
+
modified = true;
|
|
10626
|
+
}
|
|
10627
|
+
if (modified) {
|
|
10628
|
+
modifiedIssues.push({ ...issue, relatedTickets, phase });
|
|
10629
|
+
}
|
|
10630
|
+
}
|
|
10631
|
+
return { fixes, tickets: modifiedTickets, issues: modifiedIssues };
|
|
10632
|
+
}
|
|
10633
|
+
function handleRepair(ctx, dryRun) {
|
|
10634
|
+
const { fixes, error } = computeRepairs(ctx.state, ctx.warnings);
|
|
10635
|
+
if (error) {
|
|
10636
|
+
return { output: error, errorCode: "project_corrupt" };
|
|
10637
|
+
}
|
|
10638
|
+
if (fixes.length === 0) {
|
|
10639
|
+
return { output: "No stale references found. Project is clean." };
|
|
10640
|
+
}
|
|
10641
|
+
const lines = [`Found ${fixes.length} stale reference(s)${dryRun ? " (dry run)" : ""}:`, ""];
|
|
10642
|
+
for (const fix of fixes) {
|
|
10643
|
+
lines.push(`- ${fix.entity}.${fix.field}: ${fix.description}`);
|
|
10644
|
+
}
|
|
10645
|
+
if (dryRun) {
|
|
10646
|
+
lines.push("", "Run without --dry-run to apply fixes.");
|
|
10647
|
+
}
|
|
10648
|
+
return { output: lines.join("\n") };
|
|
10649
|
+
}
|
|
10650
|
+
var init_repair = __esm({
|
|
10651
|
+
"src/cli/commands/repair.ts"() {
|
|
10652
|
+
"use strict";
|
|
10653
|
+
init_esm_shims();
|
|
10654
|
+
init_errors();
|
|
10655
|
+
}
|
|
10656
|
+
});
|
|
10657
|
+
|
|
10514
10658
|
// src/cli/commands/init.ts
|
|
10515
10659
|
import { basename as basename2 } from "path";
|
|
10516
10660
|
function registerInitCommand(yargs) {
|
|
@@ -11595,6 +11739,7 @@ __export(register_exports, {
|
|
|
11595
11739
|
registerRecapCommand: () => registerRecapCommand,
|
|
11596
11740
|
registerRecommendCommand: () => registerRecommendCommand,
|
|
11597
11741
|
registerReferenceCommand: () => registerReferenceCommand,
|
|
11742
|
+
registerRepairCommand: () => registerRepairCommand,
|
|
11598
11743
|
registerSelftestCommand: () => registerSelftestCommand,
|
|
11599
11744
|
registerSessionCommand: () => registerSessionCommand,
|
|
11600
11745
|
registerSetupSkillCommand: () => registerSetupSkillCommand,
|
|
@@ -11625,6 +11770,47 @@ function registerValidateCommand(yargs) {
|
|
|
11625
11770
|
}
|
|
11626
11771
|
);
|
|
11627
11772
|
}
|
|
11773
|
+
function registerRepairCommand(yargs) {
|
|
11774
|
+
return yargs.command(
|
|
11775
|
+
"repair",
|
|
11776
|
+
"Fix stale references in .story/ data",
|
|
11777
|
+
(y) => y.option("dry-run", { type: "boolean", default: false, describe: "Show what would be fixed without writing" }),
|
|
11778
|
+
async (argv) => {
|
|
11779
|
+
const dryRun = argv["dry-run"];
|
|
11780
|
+
if (dryRun) {
|
|
11781
|
+
await runReadCommand("md", (ctx) => handleRepair(ctx, true));
|
|
11782
|
+
} else {
|
|
11783
|
+
const { withProjectLock: withProjectLock2, writeTicketUnlocked: writeTicketUnlocked2, writeIssueUnlocked: writeIssueUnlocked2, runTransactionUnlocked: runTransactionUnlocked2 } = await Promise.resolve().then(() => (init_project_loader(), project_loader_exports));
|
|
11784
|
+
const root = (await Promise.resolve().then(() => (init_project_root_discovery(), project_root_discovery_exports))).discoverProjectRoot();
|
|
11785
|
+
await withProjectLock2(root, { strict: false }, async ({ state, warnings }) => {
|
|
11786
|
+
const result = computeRepairs(state, warnings);
|
|
11787
|
+
if (result.error) {
|
|
11788
|
+
writeOutput(result.error);
|
|
11789
|
+
process.exitCode = ExitCode.USER_ERROR;
|
|
11790
|
+
return;
|
|
11791
|
+
}
|
|
11792
|
+
if (result.fixes.length === 0) {
|
|
11793
|
+
writeOutput("No stale references found. Project is clean.");
|
|
11794
|
+
return;
|
|
11795
|
+
}
|
|
11796
|
+
await runTransactionUnlocked2(root, async () => {
|
|
11797
|
+
for (const ticket of result.tickets) {
|
|
11798
|
+
await writeTicketUnlocked2(ticket, root);
|
|
11799
|
+
}
|
|
11800
|
+
for (const issue of result.issues) {
|
|
11801
|
+
await writeIssueUnlocked2(issue, root);
|
|
11802
|
+
}
|
|
11803
|
+
});
|
|
11804
|
+
const lines = [`Fixed ${result.fixes.length} stale reference(s):`, ""];
|
|
11805
|
+
for (const fix of result.fixes) {
|
|
11806
|
+
lines.push(`- ${fix.entity}.${fix.field}: ${fix.description}`);
|
|
11807
|
+
}
|
|
11808
|
+
writeOutput(lines.join("\n"));
|
|
11809
|
+
});
|
|
11810
|
+
}
|
|
11811
|
+
}
|
|
11812
|
+
);
|
|
11813
|
+
}
|
|
11628
11814
|
function registerHandoverCommand(yargs) {
|
|
11629
11815
|
return yargs.command(
|
|
11630
11816
|
"handover",
|
|
@@ -13596,6 +13782,7 @@ var init_register = __esm({
|
|
|
13596
13782
|
init_output_formatter();
|
|
13597
13783
|
init_status();
|
|
13598
13784
|
init_validate();
|
|
13785
|
+
init_repair();
|
|
13599
13786
|
init_handover();
|
|
13600
13787
|
init_blocker();
|
|
13601
13788
|
init_ticket2();
|
|
@@ -13645,9 +13832,10 @@ async function runCli() {
|
|
|
13645
13832
|
registerSetupSkillCommand: registerSetupSkillCommand2,
|
|
13646
13833
|
registerHookStatusCommand: registerHookStatusCommand2,
|
|
13647
13834
|
registerConfigCommand: registerConfigCommand2,
|
|
13648
|
-
registerSessionCommand: registerSessionCommand2
|
|
13835
|
+
registerSessionCommand: registerSessionCommand2,
|
|
13836
|
+
registerRepairCommand: registerRepairCommand2
|
|
13649
13837
|
} = await Promise.resolve().then(() => (init_register(), register_exports));
|
|
13650
|
-
const version2 = "0.1.
|
|
13838
|
+
const version2 = "0.1.35";
|
|
13651
13839
|
class HandledError extends Error {
|
|
13652
13840
|
constructor() {
|
|
13653
13841
|
super("HANDLED_ERROR");
|
|
@@ -13679,6 +13867,7 @@ async function runCli() {
|
|
|
13679
13867
|
cli = registerHandoverCommand2(cli);
|
|
13680
13868
|
cli = registerBlockerCommand2(cli);
|
|
13681
13869
|
cli = registerValidateCommand2(cli);
|
|
13870
|
+
cli = registerRepairCommand2(cli);
|
|
13682
13871
|
cli = registerSnapshotCommand2(cli);
|
|
13683
13872
|
cli = registerRecapCommand2(cli);
|
|
13684
13873
|
cli = registerExportCommand2(cli);
|
package/dist/index.d.ts
CHANGED
|
@@ -348,14 +348,20 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
348
348
|
maxTicketsPerSession: z.ZodOptional<z.ZodNumber>;
|
|
349
349
|
compactThreshold: z.ZodOptional<z.ZodString>;
|
|
350
350
|
reviewBackends: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
351
|
+
handoverInterval: z.ZodOptional<z.ZodNumber>;
|
|
352
|
+
stages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
351
353
|
}, "strip", z.ZodTypeAny, {
|
|
352
354
|
maxTicketsPerSession?: number | undefined;
|
|
353
355
|
compactThreshold?: string | undefined;
|
|
354
356
|
reviewBackends?: string[] | undefined;
|
|
357
|
+
handoverInterval?: number | undefined;
|
|
358
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
355
359
|
}, {
|
|
356
360
|
maxTicketsPerSession?: number | undefined;
|
|
357
361
|
compactThreshold?: string | undefined;
|
|
358
362
|
reviewBackends?: string[] | undefined;
|
|
363
|
+
handoverInterval?: number | undefined;
|
|
364
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
359
365
|
}>>;
|
|
360
366
|
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
361
367
|
version: z.ZodNumber;
|
|
@@ -387,14 +393,20 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
387
393
|
maxTicketsPerSession: z.ZodOptional<z.ZodNumber>;
|
|
388
394
|
compactThreshold: z.ZodOptional<z.ZodString>;
|
|
389
395
|
reviewBackends: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
396
|
+
handoverInterval: z.ZodOptional<z.ZodNumber>;
|
|
397
|
+
stages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
390
398
|
}, "strip", z.ZodTypeAny, {
|
|
391
399
|
maxTicketsPerSession?: number | undefined;
|
|
392
400
|
compactThreshold?: string | undefined;
|
|
393
401
|
reviewBackends?: string[] | undefined;
|
|
402
|
+
handoverInterval?: number | undefined;
|
|
403
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
394
404
|
}, {
|
|
395
405
|
maxTicketsPerSession?: number | undefined;
|
|
396
406
|
compactThreshold?: string | undefined;
|
|
397
407
|
reviewBackends?: string[] | undefined;
|
|
408
|
+
handoverInterval?: number | undefined;
|
|
409
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
398
410
|
}>>;
|
|
399
411
|
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
400
412
|
version: z.ZodNumber;
|
|
@@ -426,14 +438,20 @@ declare const ConfigSchema: z.ZodObject<{
|
|
|
426
438
|
maxTicketsPerSession: z.ZodOptional<z.ZodNumber>;
|
|
427
439
|
compactThreshold: z.ZodOptional<z.ZodString>;
|
|
428
440
|
reviewBackends: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
441
|
+
handoverInterval: z.ZodOptional<z.ZodNumber>;
|
|
442
|
+
stages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
429
443
|
}, "strip", z.ZodTypeAny, {
|
|
430
444
|
maxTicketsPerSession?: number | undefined;
|
|
431
445
|
compactThreshold?: string | undefined;
|
|
432
446
|
reviewBackends?: string[] | undefined;
|
|
447
|
+
handoverInterval?: number | undefined;
|
|
448
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
433
449
|
}, {
|
|
434
450
|
maxTicketsPerSession?: number | undefined;
|
|
435
451
|
compactThreshold?: string | undefined;
|
|
436
452
|
reviewBackends?: string[] | undefined;
|
|
453
|
+
handoverInterval?: number | undefined;
|
|
454
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
437
455
|
}>>;
|
|
438
456
|
}, z.ZodTypeAny, "passthrough">>;
|
|
439
457
|
type Config = z.infer<typeof ConfigSchema>;
|
|
@@ -950,14 +968,20 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
950
968
|
maxTicketsPerSession: z.ZodOptional<z.ZodNumber>;
|
|
951
969
|
compactThreshold: z.ZodOptional<z.ZodString>;
|
|
952
970
|
reviewBackends: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
971
|
+
handoverInterval: z.ZodOptional<z.ZodNumber>;
|
|
972
|
+
stages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
953
973
|
}, "strip", z.ZodTypeAny, {
|
|
954
974
|
maxTicketsPerSession?: number | undefined;
|
|
955
975
|
compactThreshold?: string | undefined;
|
|
956
976
|
reviewBackends?: string[] | undefined;
|
|
977
|
+
handoverInterval?: number | undefined;
|
|
978
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
957
979
|
}, {
|
|
958
980
|
maxTicketsPerSession?: number | undefined;
|
|
959
981
|
compactThreshold?: string | undefined;
|
|
960
982
|
reviewBackends?: string[] | undefined;
|
|
983
|
+
handoverInterval?: number | undefined;
|
|
984
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
961
985
|
}>>;
|
|
962
986
|
}, "passthrough", z.ZodTypeAny, z.objectOutputType<{
|
|
963
987
|
version: z.ZodNumber;
|
|
@@ -989,14 +1013,20 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
989
1013
|
maxTicketsPerSession: z.ZodOptional<z.ZodNumber>;
|
|
990
1014
|
compactThreshold: z.ZodOptional<z.ZodString>;
|
|
991
1015
|
reviewBackends: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
1016
|
+
handoverInterval: z.ZodOptional<z.ZodNumber>;
|
|
1017
|
+
stages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
992
1018
|
}, "strip", z.ZodTypeAny, {
|
|
993
1019
|
maxTicketsPerSession?: number | undefined;
|
|
994
1020
|
compactThreshold?: string | undefined;
|
|
995
1021
|
reviewBackends?: string[] | undefined;
|
|
1022
|
+
handoverInterval?: number | undefined;
|
|
1023
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
996
1024
|
}, {
|
|
997
1025
|
maxTicketsPerSession?: number | undefined;
|
|
998
1026
|
compactThreshold?: string | undefined;
|
|
999
1027
|
reviewBackends?: string[] | undefined;
|
|
1028
|
+
handoverInterval?: number | undefined;
|
|
1029
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
1000
1030
|
}>>;
|
|
1001
1031
|
}, z.ZodTypeAny, "passthrough">, z.objectInputType<{
|
|
1002
1032
|
version: z.ZodNumber;
|
|
@@ -1028,14 +1058,20 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1028
1058
|
maxTicketsPerSession: z.ZodOptional<z.ZodNumber>;
|
|
1029
1059
|
compactThreshold: z.ZodOptional<z.ZodString>;
|
|
1030
1060
|
reviewBackends: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
1061
|
+
handoverInterval: z.ZodOptional<z.ZodNumber>;
|
|
1062
|
+
stages: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
1031
1063
|
}, "strip", z.ZodTypeAny, {
|
|
1032
1064
|
maxTicketsPerSession?: number | undefined;
|
|
1033
1065
|
compactThreshold?: string | undefined;
|
|
1034
1066
|
reviewBackends?: string[] | undefined;
|
|
1067
|
+
handoverInterval?: number | undefined;
|
|
1068
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
1035
1069
|
}, {
|
|
1036
1070
|
maxTicketsPerSession?: number | undefined;
|
|
1037
1071
|
compactThreshold?: string | undefined;
|
|
1038
1072
|
reviewBackends?: string[] | undefined;
|
|
1073
|
+
handoverInterval?: number | undefined;
|
|
1074
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
1039
1075
|
}>>;
|
|
1040
1076
|
}, z.ZodTypeAny, "passthrough">>;
|
|
1041
1077
|
roadmap: z.ZodObject<{
|
|
@@ -1334,40 +1370,15 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1334
1370
|
file: z.ZodString;
|
|
1335
1371
|
message: z.ZodString;
|
|
1336
1372
|
}, "strip", z.ZodTypeAny, {
|
|
1337
|
-
type: string;
|
|
1338
1373
|
message: string;
|
|
1374
|
+
type: string;
|
|
1339
1375
|
file: string;
|
|
1340
1376
|
}, {
|
|
1341
|
-
type: string;
|
|
1342
1377
|
message: string;
|
|
1378
|
+
type: string;
|
|
1343
1379
|
file: string;
|
|
1344
1380
|
}>, "many">>;
|
|
1345
1381
|
}, "strip", z.ZodTypeAny, {
|
|
1346
|
-
version: 1;
|
|
1347
|
-
config: {
|
|
1348
|
-
version: number;
|
|
1349
|
-
type: string;
|
|
1350
|
-
language: string;
|
|
1351
|
-
project: string;
|
|
1352
|
-
features: {
|
|
1353
|
-
issues: boolean;
|
|
1354
|
-
tickets: boolean;
|
|
1355
|
-
handovers: boolean;
|
|
1356
|
-
roadmap: boolean;
|
|
1357
|
-
reviews: boolean;
|
|
1358
|
-
} & {
|
|
1359
|
-
[k: string]: unknown;
|
|
1360
|
-
};
|
|
1361
|
-
schemaVersion?: number | undefined;
|
|
1362
|
-
recipe?: string | undefined;
|
|
1363
|
-
recipeOverrides?: {
|
|
1364
|
-
maxTicketsPerSession?: number | undefined;
|
|
1365
|
-
compactThreshold?: string | undefined;
|
|
1366
|
-
reviewBackends?: string[] | undefined;
|
|
1367
|
-
} | undefined;
|
|
1368
|
-
} & {
|
|
1369
|
-
[k: string]: unknown;
|
|
1370
|
-
};
|
|
1371
1382
|
issues: z.objectOutputType<{
|
|
1372
1383
|
id: z.ZodString;
|
|
1373
1384
|
title: z.ZodString;
|
|
@@ -1404,8 +1415,8 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1404
1415
|
claimedBySession: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1405
1416
|
}, z.ZodTypeAny, "passthrough">[];
|
|
1406
1417
|
roadmap: {
|
|
1407
|
-
date: string;
|
|
1408
1418
|
title: string;
|
|
1419
|
+
date: string;
|
|
1409
1420
|
phases: z.objectOutputType<{
|
|
1410
1421
|
id: z.ZodString;
|
|
1411
1422
|
label: z.ZodString;
|
|
@@ -1423,6 +1434,7 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1423
1434
|
} & {
|
|
1424
1435
|
[k: string]: unknown;
|
|
1425
1436
|
};
|
|
1437
|
+
version: 1;
|
|
1426
1438
|
project: string;
|
|
1427
1439
|
notes: z.objectOutputType<{
|
|
1428
1440
|
id: z.ZodString;
|
|
@@ -1448,19 +1460,11 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1448
1460
|
status: z.ZodEnum<["active", "deprecated", "superseded"]>;
|
|
1449
1461
|
}, z.ZodTypeAny, "passthrough">[];
|
|
1450
1462
|
createdAt: string;
|
|
1451
|
-
handoverFilenames: string[];
|
|
1452
|
-
warnings?: {
|
|
1453
|
-
type: string;
|
|
1454
|
-
message: string;
|
|
1455
|
-
file: string;
|
|
1456
|
-
}[] | undefined;
|
|
1457
|
-
}, {
|
|
1458
|
-
version: 1;
|
|
1459
1463
|
config: {
|
|
1460
|
-
version: number;
|
|
1461
1464
|
type: string;
|
|
1462
|
-
|
|
1465
|
+
version: number;
|
|
1463
1466
|
project: string;
|
|
1467
|
+
language: string;
|
|
1464
1468
|
features: {
|
|
1465
1469
|
issues: boolean;
|
|
1466
1470
|
tickets: boolean;
|
|
@@ -1476,10 +1480,19 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1476
1480
|
maxTicketsPerSession?: number | undefined;
|
|
1477
1481
|
compactThreshold?: string | undefined;
|
|
1478
1482
|
reviewBackends?: string[] | undefined;
|
|
1483
|
+
handoverInterval?: number | undefined;
|
|
1484
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
1479
1485
|
} | undefined;
|
|
1480
1486
|
} & {
|
|
1481
1487
|
[k: string]: unknown;
|
|
1482
1488
|
};
|
|
1489
|
+
handoverFilenames: string[];
|
|
1490
|
+
warnings?: {
|
|
1491
|
+
message: string;
|
|
1492
|
+
type: string;
|
|
1493
|
+
file: string;
|
|
1494
|
+
}[] | undefined;
|
|
1495
|
+
}, {
|
|
1483
1496
|
issues: z.objectInputType<{
|
|
1484
1497
|
id: z.ZodString;
|
|
1485
1498
|
title: z.ZodString;
|
|
@@ -1516,8 +1529,8 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1516
1529
|
claimedBySession: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1517
1530
|
}, z.ZodTypeAny, "passthrough">[];
|
|
1518
1531
|
roadmap: {
|
|
1519
|
-
date: string;
|
|
1520
1532
|
title: string;
|
|
1533
|
+
date: string;
|
|
1521
1534
|
phases: z.objectInputType<{
|
|
1522
1535
|
id: z.ZodString;
|
|
1523
1536
|
label: z.ZodString;
|
|
@@ -1535,8 +1548,35 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1535
1548
|
} & {
|
|
1536
1549
|
[k: string]: unknown;
|
|
1537
1550
|
};
|
|
1551
|
+
version: 1;
|
|
1538
1552
|
project: string;
|
|
1539
1553
|
createdAt: string;
|
|
1554
|
+
config: {
|
|
1555
|
+
type: string;
|
|
1556
|
+
version: number;
|
|
1557
|
+
project: string;
|
|
1558
|
+
language: string;
|
|
1559
|
+
features: {
|
|
1560
|
+
issues: boolean;
|
|
1561
|
+
tickets: boolean;
|
|
1562
|
+
handovers: boolean;
|
|
1563
|
+
roadmap: boolean;
|
|
1564
|
+
reviews: boolean;
|
|
1565
|
+
} & {
|
|
1566
|
+
[k: string]: unknown;
|
|
1567
|
+
};
|
|
1568
|
+
schemaVersion?: number | undefined;
|
|
1569
|
+
recipe?: string | undefined;
|
|
1570
|
+
recipeOverrides?: {
|
|
1571
|
+
maxTicketsPerSession?: number | undefined;
|
|
1572
|
+
compactThreshold?: string | undefined;
|
|
1573
|
+
reviewBackends?: string[] | undefined;
|
|
1574
|
+
handoverInterval?: number | undefined;
|
|
1575
|
+
stages?: Record<string, Record<string, unknown>> | undefined;
|
|
1576
|
+
} | undefined;
|
|
1577
|
+
} & {
|
|
1578
|
+
[k: string]: unknown;
|
|
1579
|
+
};
|
|
1540
1580
|
notes?: z.objectInputType<{
|
|
1541
1581
|
id: z.ZodString;
|
|
1542
1582
|
title: z.ZodNullable<z.ZodString>;
|
|
@@ -1561,8 +1601,8 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1561
1601
|
status: z.ZodEnum<["active", "deprecated", "superseded"]>;
|
|
1562
1602
|
}, z.ZodTypeAny, "passthrough">[] | undefined;
|
|
1563
1603
|
warnings?: {
|
|
1564
|
-
type: string;
|
|
1565
1604
|
message: string;
|
|
1605
|
+
type: string;
|
|
1566
1606
|
file: string;
|
|
1567
1607
|
}[] | undefined;
|
|
1568
1608
|
handoverFilenames?: string[] | undefined;
|
package/dist/index.js
CHANGED
|
@@ -140,7 +140,9 @@ var ConfigSchema = z6.object({
|
|
|
140
140
|
recipeOverrides: z6.object({
|
|
141
141
|
maxTicketsPerSession: z6.number().min(0).optional(),
|
|
142
142
|
compactThreshold: z6.string().optional(),
|
|
143
|
-
reviewBackends: z6.array(z6.string()).optional()
|
|
143
|
+
reviewBackends: z6.array(z6.string()).optional(),
|
|
144
|
+
handoverInterval: z6.number().min(0).optional(),
|
|
145
|
+
stages: z6.record(z6.record(z6.unknown())).optional()
|
|
144
146
|
}).optional()
|
|
145
147
|
}).passthrough();
|
|
146
148
|
|
package/dist/mcp.js
CHANGED
|
@@ -240,7 +240,9 @@ var init_config = __esm({
|
|
|
240
240
|
recipeOverrides: z7.object({
|
|
241
241
|
maxTicketsPerSession: z7.number().min(0).optional(),
|
|
242
242
|
compactThreshold: z7.string().optional(),
|
|
243
|
-
reviewBackends: z7.array(z7.string()).optional()
|
|
243
|
+
reviewBackends: z7.array(z7.string()).optional(),
|
|
244
|
+
handoverInterval: z7.number().min(0).optional(),
|
|
245
|
+
stages: z7.record(z7.record(z7.unknown())).optional()
|
|
244
246
|
}).optional()
|
|
245
247
|
}).passthrough();
|
|
246
248
|
}
|
|
@@ -2982,7 +2984,20 @@ function validateRelatedTickets(ids, state) {
|
|
|
2982
2984
|
}
|
|
2983
2985
|
}
|
|
2984
2986
|
}
|
|
2987
|
+
function buildErrorMultiset2(findings) {
|
|
2988
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2989
|
+
const messages = /* @__PURE__ */ new Map();
|
|
2990
|
+
for (const f of findings) {
|
|
2991
|
+
if (f.level !== "error") continue;
|
|
2992
|
+
const key = `${f.code}|${f.entity ?? ""}|${f.message}`;
|
|
2993
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
2994
|
+
messages.set(key, f.message);
|
|
2995
|
+
}
|
|
2996
|
+
return { counts, messages };
|
|
2997
|
+
}
|
|
2985
2998
|
function validatePostWriteIssueState(candidate, state, isCreate) {
|
|
2999
|
+
const preResult = validateProject(state);
|
|
3000
|
+
const { counts: preErrors } = buildErrorMultiset2(preResult.findings);
|
|
2986
3001
|
const existingIssues = [...state.issues];
|
|
2987
3002
|
if (isCreate) {
|
|
2988
3003
|
existingIssues.push(candidate);
|
|
@@ -2999,11 +3014,17 @@ function validatePostWriteIssueState(candidate, state, isCreate) {
|
|
|
2999
3014
|
config: state.config,
|
|
3000
3015
|
handoverFilenames: [...state.handoverFilenames]
|
|
3001
3016
|
});
|
|
3002
|
-
const
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3017
|
+
const postResult = validateProject(postState);
|
|
3018
|
+
const { counts: postErrors, messages: postMessages } = buildErrorMultiset2(postResult.findings);
|
|
3019
|
+
const newErrors = [];
|
|
3020
|
+
for (const [key, postCount] of postErrors) {
|
|
3021
|
+
const preCount = preErrors.get(key) ?? 0;
|
|
3022
|
+
if (postCount > preCount) {
|
|
3023
|
+
newErrors.push(postMessages.get(key) ?? key);
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
3026
|
+
if (newErrors.length > 0) {
|
|
3027
|
+
throw new CliValidationError("validation_failed", `Write would create invalid state: ${newErrors.join("; ")}`);
|
|
3007
3028
|
}
|
|
3008
3029
|
}
|
|
3009
3030
|
async function handleIssueCreate(args, format, root) {
|
|
@@ -4203,7 +4224,20 @@ function validateParentTicket(parentId, ticketId, state) {
|
|
|
4203
4224
|
throw new CliValidationError("invalid_input", `Parent ticket ${parentId} not found`);
|
|
4204
4225
|
}
|
|
4205
4226
|
}
|
|
4227
|
+
function buildErrorMultiset(findings) {
|
|
4228
|
+
const counts = /* @__PURE__ */ new Map();
|
|
4229
|
+
const messages = /* @__PURE__ */ new Map();
|
|
4230
|
+
for (const f of findings) {
|
|
4231
|
+
if (f.level !== "error") continue;
|
|
4232
|
+
const key = `${f.code}|${f.entity ?? ""}|${f.message}`;
|
|
4233
|
+
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
4234
|
+
messages.set(key, f.message);
|
|
4235
|
+
}
|
|
4236
|
+
return { counts, messages };
|
|
4237
|
+
}
|
|
4206
4238
|
function validatePostWriteState(candidate, state, isCreate) {
|
|
4239
|
+
const preResult = validateProject(state);
|
|
4240
|
+
const { counts: preErrors } = buildErrorMultiset(preResult.findings);
|
|
4207
4241
|
const existingTickets = [...state.tickets];
|
|
4208
4242
|
if (isCreate) {
|
|
4209
4243
|
existingTickets.push(candidate);
|
|
@@ -4220,11 +4254,17 @@ function validatePostWriteState(candidate, state, isCreate) {
|
|
|
4220
4254
|
config: state.config,
|
|
4221
4255
|
handoverFilenames: [...state.handoverFilenames]
|
|
4222
4256
|
});
|
|
4223
|
-
const
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4257
|
+
const postResult = validateProject(postState);
|
|
4258
|
+
const { counts: postErrors, messages: postMessages } = buildErrorMultiset(postResult.findings);
|
|
4259
|
+
const newErrors = [];
|
|
4260
|
+
for (const [key, postCount] of postErrors) {
|
|
4261
|
+
const preCount = preErrors.get(key) ?? 0;
|
|
4262
|
+
if (postCount > preCount) {
|
|
4263
|
+
newErrors.push(postMessages.get(key) ?? key);
|
|
4264
|
+
}
|
|
4265
|
+
}
|
|
4266
|
+
if (newErrors.length > 0) {
|
|
4267
|
+
throw new CliValidationError("validation_failed", `Write would create invalid state: ${newErrors.join("; ")}`);
|
|
4228
4268
|
}
|
|
4229
4269
|
}
|
|
4230
4270
|
async function handleTicketCreate(args, format, root) {
|
|
@@ -5512,7 +5552,19 @@ function resolveRecipe(recipeName, projectOverrides) {
|
|
|
5512
5552
|
}
|
|
5513
5553
|
}
|
|
5514
5554
|
let pipeline = raw.pipeline ? [...raw.pipeline] : [...DEFAULT_PIPELINE];
|
|
5515
|
-
const
|
|
5555
|
+
const recipeStages = raw.stages ?? {};
|
|
5556
|
+
const stageOverrides = projectOverrides?.stages ?? {};
|
|
5557
|
+
const stages2 = {};
|
|
5558
|
+
for (const [key, value] of Object.entries(recipeStages)) {
|
|
5559
|
+
const override = stageOverrides[key];
|
|
5560
|
+
const safeOverride = override && typeof override === "object" && !Array.isArray(override) ? override : {};
|
|
5561
|
+
stages2[key] = { ...value, ...safeOverride };
|
|
5562
|
+
}
|
|
5563
|
+
for (const [key, value] of Object.entries(stageOverrides)) {
|
|
5564
|
+
if (!stages2[key] && value && typeof value === "object" && !Array.isArray(value)) {
|
|
5565
|
+
stages2[key] = { ...value };
|
|
5566
|
+
}
|
|
5567
|
+
}
|
|
5516
5568
|
if (stages2.WRITE_TESTS?.enabled) {
|
|
5517
5569
|
const implementIdx = pipeline.indexOf("IMPLEMENT");
|
|
5518
5570
|
if (implementIdx !== -1 && !pipeline.includes("WRITE_TESTS")) {
|
|
@@ -7531,6 +7583,9 @@ async function handleStart(root, args) {
|
|
|
7531
7583
|
if (typeof overrides.compactThreshold === "string") sessionConfig.compactThreshold = overrides.compactThreshold;
|
|
7532
7584
|
if (Array.isArray(overrides.reviewBackends)) sessionConfig.reviewBackends = overrides.reviewBackends;
|
|
7533
7585
|
if (typeof overrides.handoverInterval === "number") sessionConfig.handoverInterval = overrides.handoverInterval;
|
|
7586
|
+
if (overrides.stages && typeof overrides.stages === "object") {
|
|
7587
|
+
sessionConfig.stageOverrides = overrides.stages;
|
|
7588
|
+
}
|
|
7534
7589
|
}
|
|
7535
7590
|
} catch {
|
|
7536
7591
|
}
|
|
@@ -7540,7 +7595,8 @@ async function handleStart(root, args) {
|
|
|
7540
7595
|
const resolvedRecipe = resolveRecipe(recipe, {
|
|
7541
7596
|
maxTicketsPerSession: sessionConfig.maxTicketsPerSession,
|
|
7542
7597
|
compactThreshold: sessionConfig.compactThreshold,
|
|
7543
|
-
reviewBackends: sessionConfig.reviewBackends
|
|
7598
|
+
reviewBackends: sessionConfig.reviewBackends,
|
|
7599
|
+
stages: sessionConfig.stageOverrides
|
|
7544
7600
|
});
|
|
7545
7601
|
const session = createSession(root, recipe, wsId, sessionConfig);
|
|
7546
7602
|
const dir = sessionDir(root, session.sessionId);
|
|
@@ -8341,14 +8397,6 @@ async function handleCancel(root, args) {
|
|
|
8341
8397
|
if (info.state.state === "SESSION_END" || info.state.status === "completed") {
|
|
8342
8398
|
return guideError(new Error("Session already ended."));
|
|
8343
8399
|
}
|
|
8344
|
-
const CANCELLABLE_STATES = /* @__PURE__ */ new Set(["PICK_TICKET", "COMPLETE", "HANDOVER"]);
|
|
8345
|
-
if (info.state.recipe === "coding" && !CANCELLABLE_STATES.has(info.state.state)) {
|
|
8346
|
-
const sessionMode = info.state.mode ?? "auto";
|
|
8347
|
-
const modeGuidance = sessionMode === "plan" ? "Plan mode sessions end after plan review approval \u2014 continue to that step." : sessionMode === "review" ? "Review mode sessions end after code review approval \u2014 continue to that step." : sessionMode === "guided" ? "Guided mode sessions end after ticket completion \u2014 continue to FINALIZE." : "Complete the current ticket and write a handover to end the session.";
|
|
8348
|
-
return guideError(new Error(
|
|
8349
|
-
`Cannot cancel a coding session from ${info.state.state}. ${modeGuidance}`
|
|
8350
|
-
));
|
|
8351
|
-
}
|
|
8352
8400
|
await recoverPendingMutation(info.dir, info.state, root);
|
|
8353
8401
|
const cancelInfo = findSessionById(root, args.sessionId) ?? info;
|
|
8354
8402
|
let ticketReleased = false;
|
|
@@ -9511,7 +9559,7 @@ async function ensureGitignoreEntries(gitignorePath, entries) {
|
|
|
9511
9559
|
// src/mcp/index.ts
|
|
9512
9560
|
var ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
|
|
9513
9561
|
var CONFIG_PATH2 = ".story/config.json";
|
|
9514
|
-
var version = "0.1.
|
|
9562
|
+
var version = "0.1.35";
|
|
9515
9563
|
function tryDiscoverRoot() {
|
|
9516
9564
|
const envRoot = process.env[ENV_VAR2];
|
|
9517
9565
|
if (envRoot) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anthropologies/claudestory",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.35",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"description": "Cross-session context persistence for AI coding projects. Tracks tickets, issues, roadmap, and handovers so every session builds on the last.",
|
|
6
6
|
"keywords": [
|