@anthropologies/claudestory 0.1.14 → 0.1.16
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 +247 -7
- package/dist/index.d.ts +40 -40
- package/dist/mcp.js +177 -5
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -562,6 +562,29 @@ var init_handover_parser = __esm({
|
|
|
562
562
|
});
|
|
563
563
|
|
|
564
564
|
// src/core/project-loader.ts
|
|
565
|
+
var project_loader_exports = {};
|
|
566
|
+
__export(project_loader_exports, {
|
|
567
|
+
atomicWrite: () => atomicWrite,
|
|
568
|
+
deleteIssue: () => deleteIssue,
|
|
569
|
+
deleteNote: () => deleteNote,
|
|
570
|
+
deleteTicket: () => deleteTicket,
|
|
571
|
+
guardPath: () => guardPath,
|
|
572
|
+
loadProject: () => loadProject,
|
|
573
|
+
runTransaction: () => runTransaction,
|
|
574
|
+
runTransactionUnlocked: () => runTransactionUnlocked,
|
|
575
|
+
serializeJSON: () => serializeJSON,
|
|
576
|
+
sortKeysDeep: () => sortKeysDeep,
|
|
577
|
+
withProjectLock: () => withProjectLock,
|
|
578
|
+
writeConfig: () => writeConfig,
|
|
579
|
+
writeIssue: () => writeIssue,
|
|
580
|
+
writeIssueUnlocked: () => writeIssueUnlocked,
|
|
581
|
+
writeNote: () => writeNote,
|
|
582
|
+
writeNoteUnlocked: () => writeNoteUnlocked,
|
|
583
|
+
writeRoadmap: () => writeRoadmap,
|
|
584
|
+
writeRoadmapUnlocked: () => writeRoadmapUnlocked,
|
|
585
|
+
writeTicket: () => writeTicket,
|
|
586
|
+
writeTicketUnlocked: () => writeTicketUnlocked
|
|
587
|
+
});
|
|
565
588
|
import {
|
|
566
589
|
readdir as readdir2,
|
|
567
590
|
readFile as readFile2,
|
|
@@ -920,6 +943,12 @@ async function runTransactionUnlocked(root, operations) {
|
|
|
920
943
|
throw new ProjectLoaderError("io_error", "Transaction failed", err);
|
|
921
944
|
}
|
|
922
945
|
}
|
|
946
|
+
async function runTransaction(root, operations) {
|
|
947
|
+
const wrapDir = resolve2(root, ".story");
|
|
948
|
+
await withLock(wrapDir, async () => {
|
|
949
|
+
await runTransactionUnlocked(root, operations);
|
|
950
|
+
});
|
|
951
|
+
}
|
|
923
952
|
async function doRecoverTransaction(wrapDir) {
|
|
924
953
|
const journalPath = join3(wrapDir, ".txn.json");
|
|
925
954
|
let entries;
|
|
@@ -5237,6 +5266,19 @@ async function handleReportPlan(root, dir, state, report) {
|
|
|
5237
5266
|
});
|
|
5238
5267
|
}
|
|
5239
5268
|
const risk = assessRisk(void 0, void 0);
|
|
5269
|
+
if (state.ticket) {
|
|
5270
|
+
try {
|
|
5271
|
+
const { withProjectLock: withProjectLock2, writeTicketUnlocked: writeTicketUnlocked2 } = await Promise.resolve().then(() => (init_project_loader(), project_loader_exports));
|
|
5272
|
+
await withProjectLock2(root, { strict: false }, async ({ state: projectState }) => {
|
|
5273
|
+
const ticket = projectState.ticketByID(state.ticket.id);
|
|
5274
|
+
if (ticket && ticket.status !== "inprogress") {
|
|
5275
|
+
const updated = { ...ticket, status: "inprogress" };
|
|
5276
|
+
await writeTicketUnlocked2(updated, root);
|
|
5277
|
+
}
|
|
5278
|
+
});
|
|
5279
|
+
} catch {
|
|
5280
|
+
}
|
|
5281
|
+
}
|
|
5240
5282
|
const written = writeSessionSync(dir, {
|
|
5241
5283
|
...state,
|
|
5242
5284
|
state: "PLAN_REVIEW",
|
|
@@ -5594,11 +5636,11 @@ async function handleReportComplete(root, dir, state, report) {
|
|
|
5594
5636
|
const maxTickets = updated.config.maxTicketsPerSession;
|
|
5595
5637
|
let nextState;
|
|
5596
5638
|
let advice = "ok";
|
|
5597
|
-
if (
|
|
5639
|
+
if (maxTickets > 0 && ticketsDone >= maxTickets) {
|
|
5598
5640
|
nextState = "HANDOVER";
|
|
5599
|
-
|
|
5600
|
-
} else if (maxTickets > 0 && ticketsDone >= maxTickets) {
|
|
5641
|
+
} else if (pressure === "critical") {
|
|
5601
5642
|
nextState = "HANDOVER";
|
|
5643
|
+
advice = "compact-now";
|
|
5602
5644
|
} else if (pressure === "high") {
|
|
5603
5645
|
advice = "consider-compact";
|
|
5604
5646
|
nextState = "PICK_TICKET";
|
|
@@ -5678,6 +5720,36 @@ async function handleReportHandover(root, dir, state, report) {
|
|
|
5678
5720
|
} catch {
|
|
5679
5721
|
}
|
|
5680
5722
|
}
|
|
5723
|
+
const pressureLevel = state.contextPressure?.level ?? "low";
|
|
5724
|
+
const maxTickets = state.config.maxTicketsPerSession;
|
|
5725
|
+
const capReached = maxTickets > 0 && state.completedTickets.length >= maxTickets;
|
|
5726
|
+
let hasMoreTickets = false;
|
|
5727
|
+
try {
|
|
5728
|
+
const { state: ps } = await loadProject(root);
|
|
5729
|
+
hasMoreTickets = nextTickets(ps, 1).kind === "found";
|
|
5730
|
+
} catch {
|
|
5731
|
+
}
|
|
5732
|
+
if (pressureLevel === "critical" && hasMoreTickets && !capReached) {
|
|
5733
|
+
return guideResult(state, "HANDOVER", {
|
|
5734
|
+
instruction: [
|
|
5735
|
+
"# Context Compaction Needed",
|
|
5736
|
+
"",
|
|
5737
|
+
`${state.completedTickets.length} ticket(s) completed so far. Handover written. Context is large \u2014 time to compact and continue.`,
|
|
5738
|
+
"",
|
|
5739
|
+
'Call `claudestory_autonomous_guide` with `action: "pre_compact"` now:',
|
|
5740
|
+
"```json",
|
|
5741
|
+
`{ "sessionId": "${state.sessionId}", "action": "pre_compact" }`,
|
|
5742
|
+
"```",
|
|
5743
|
+
"",
|
|
5744
|
+
'After pre_compact responds, run `/compact`, then call with `action: "resume"` to continue working on more tickets.'
|
|
5745
|
+
].join("\n"),
|
|
5746
|
+
reminders: [
|
|
5747
|
+
"Do NOT stop. This is a context compaction, not a session end.",
|
|
5748
|
+
"Call pre_compact \u2192 /compact \u2192 resume to continue autonomous work."
|
|
5749
|
+
],
|
|
5750
|
+
contextAdvice: "compact-now"
|
|
5751
|
+
});
|
|
5752
|
+
}
|
|
5681
5753
|
const written = writeSessionSync(dir, {
|
|
5682
5754
|
...state,
|
|
5683
5755
|
state: "SESSION_END",
|
|
@@ -5725,6 +5797,40 @@ async function handleResume(root, args) {
|
|
|
5725
5797
|
resumeFromRevision: null,
|
|
5726
5798
|
contextPressure: { ...info.state.contextPressure, compactionCount: (info.state.contextPressure?.compactionCount ?? 0) + 1 }
|
|
5727
5799
|
});
|
|
5800
|
+
if (resumeState === "PICK_TICKET") {
|
|
5801
|
+
let candidatesText = "No ticket candidates available.";
|
|
5802
|
+
let topCandidate = null;
|
|
5803
|
+
try {
|
|
5804
|
+
const { state: ps } = await loadProject(root);
|
|
5805
|
+
const result = nextTickets(ps, 5);
|
|
5806
|
+
if (result.kind === "found") {
|
|
5807
|
+
topCandidate = result.candidates[0] ?? null;
|
|
5808
|
+
candidatesText = result.candidates.map(
|
|
5809
|
+
(c, i) => `${i + 1}. **${c.ticket.id}: ${c.ticket.title}** (${c.ticket.type})`
|
|
5810
|
+
).join("\n");
|
|
5811
|
+
}
|
|
5812
|
+
} catch {
|
|
5813
|
+
}
|
|
5814
|
+
return guideResult(written, "PICK_TICKET", {
|
|
5815
|
+
instruction: [
|
|
5816
|
+
"# Resumed After Compact \u2014 Continue Working",
|
|
5817
|
+
"",
|
|
5818
|
+
`${written.completedTickets.length} ticket(s) done so far. Context compacted. Pick the next ticket immediately.`,
|
|
5819
|
+
"",
|
|
5820
|
+
candidatesText,
|
|
5821
|
+
"",
|
|
5822
|
+
topCandidate ? `Pick **${topCandidate.ticket.id}** by calling \`claudestory_autonomous_guide\` now:` : "Pick a ticket now:",
|
|
5823
|
+
"```json",
|
|
5824
|
+
topCandidate ? `{ "sessionId": "${written.sessionId}", "action": "report", "report": { "completedAction": "ticket_picked", "ticketId": "${topCandidate.ticket.id}" } }` : `{ "sessionId": "${written.sessionId}", "action": "report", "report": { "completedAction": "ticket_picked", "ticketId": "T-XXX" } }`,
|
|
5825
|
+
"```"
|
|
5826
|
+
].join("\n"),
|
|
5827
|
+
reminders: [
|
|
5828
|
+
"Do NOT stop or summarize. Pick the next ticket IMMEDIATELY.",
|
|
5829
|
+
"Do NOT ask the user for confirmation.",
|
|
5830
|
+
"You are in autonomous mode \u2014 continue working."
|
|
5831
|
+
]
|
|
5832
|
+
});
|
|
5833
|
+
}
|
|
5728
5834
|
return guideResult(written, resumeState, {
|
|
5729
5835
|
instruction: [
|
|
5730
5836
|
"# Resumed After Compact",
|
|
@@ -5736,6 +5842,7 @@ async function handleResume(root, args) {
|
|
|
5736
5842
|
].join("\n"),
|
|
5737
5843
|
reminders: [
|
|
5738
5844
|
"Do NOT use plan mode.",
|
|
5845
|
+
"Do NOT stop or summarize.",
|
|
5739
5846
|
"Call autonomous_guide after completing each step."
|
|
5740
5847
|
]
|
|
5741
5848
|
});
|
|
@@ -5751,11 +5858,12 @@ async function handlePreCompact(root, args) {
|
|
|
5751
5858
|
return guideError(new Error(`Session ${args.sessionId} is already in COMPACT state. Call action: "resume" to continue.`));
|
|
5752
5859
|
}
|
|
5753
5860
|
const headResult = await gitHead(root);
|
|
5861
|
+
const resumeTarget = info.state.state === "HANDOVER" ? "PICK_TICKET" : info.state.state;
|
|
5754
5862
|
const written = writeSessionSync(info.dir, {
|
|
5755
5863
|
...refreshLease(info.state),
|
|
5756
5864
|
state: "COMPACT",
|
|
5757
5865
|
previousState: info.state.state,
|
|
5758
|
-
preCompactState:
|
|
5866
|
+
preCompactState: resumeTarget,
|
|
5759
5867
|
resumeFromRevision: info.state.revision,
|
|
5760
5868
|
git: {
|
|
5761
5869
|
...info.state.git,
|
|
@@ -6835,7 +6943,7 @@ var init_mcp = __esm({
|
|
|
6835
6943
|
init_init();
|
|
6836
6944
|
ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
|
|
6837
6945
|
CONFIG_PATH2 = ".story/config.json";
|
|
6838
|
-
version = "0.1.
|
|
6946
|
+
version = "0.1.16";
|
|
6839
6947
|
main().catch((err) => {
|
|
6840
6948
|
process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
|
|
6841
6949
|
`);
|
|
@@ -7714,10 +7822,95 @@ var init_hook_status = __esm({
|
|
|
7714
7822
|
}
|
|
7715
7823
|
});
|
|
7716
7824
|
|
|
7825
|
+
// src/cli/commands/config-update.ts
|
|
7826
|
+
var config_update_exports = {};
|
|
7827
|
+
__export(config_update_exports, {
|
|
7828
|
+
handleConfigSetOverrides: () => handleConfigSetOverrides
|
|
7829
|
+
});
|
|
7830
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
7831
|
+
import { join as join15 } from "path";
|
|
7832
|
+
async function handleConfigSetOverrides(root, format, options) {
|
|
7833
|
+
const { json: jsonArg, clear } = options;
|
|
7834
|
+
if (!clear && !jsonArg) {
|
|
7835
|
+
return {
|
|
7836
|
+
output: format === "json" ? JSON.stringify({ version: 1, error: "Provide --json or --clear" }) : "Error: Provide --json or --clear",
|
|
7837
|
+
errorCode: "invalid_input"
|
|
7838
|
+
};
|
|
7839
|
+
}
|
|
7840
|
+
let parsedOverrides = {};
|
|
7841
|
+
if (jsonArg) {
|
|
7842
|
+
try {
|
|
7843
|
+
parsedOverrides = JSON.parse(jsonArg);
|
|
7844
|
+
if (typeof parsedOverrides !== "object" || parsedOverrides === null || Array.isArray(parsedOverrides)) {
|
|
7845
|
+
return {
|
|
7846
|
+
output: format === "json" ? JSON.stringify({ version: 1, error: "Invalid JSON: expected an object" }) : "Error: Invalid JSON: expected an object",
|
|
7847
|
+
errorCode: "invalid_input"
|
|
7848
|
+
};
|
|
7849
|
+
}
|
|
7850
|
+
} catch {
|
|
7851
|
+
return {
|
|
7852
|
+
output: format === "json" ? JSON.stringify({ version: 1, error: "Invalid JSON syntax" }) : "Error: Invalid JSON syntax",
|
|
7853
|
+
errorCode: "invalid_input"
|
|
7854
|
+
};
|
|
7855
|
+
}
|
|
7856
|
+
}
|
|
7857
|
+
let resultOverrides = null;
|
|
7858
|
+
await withProjectLock(root, { strict: false }, async () => {
|
|
7859
|
+
const configPath = join15(root, ".story", "config.json");
|
|
7860
|
+
const rawContent = readFileSync4(configPath, "utf-8");
|
|
7861
|
+
const raw = JSON.parse(rawContent);
|
|
7862
|
+
if (clear) {
|
|
7863
|
+
delete raw.recipeOverrides;
|
|
7864
|
+
} else {
|
|
7865
|
+
const existing = raw.recipeOverrides ?? {};
|
|
7866
|
+
const merged = { ...existing, ...parsedOverrides };
|
|
7867
|
+
for (const [k, v] of Object.entries(merged)) {
|
|
7868
|
+
if (v === null) delete merged[k];
|
|
7869
|
+
}
|
|
7870
|
+
if (Object.keys(merged).length === 0) {
|
|
7871
|
+
delete raw.recipeOverrides;
|
|
7872
|
+
} else {
|
|
7873
|
+
raw.recipeOverrides = merged;
|
|
7874
|
+
}
|
|
7875
|
+
}
|
|
7876
|
+
const validated = ConfigSchema.safeParse(raw);
|
|
7877
|
+
if (!validated.success) {
|
|
7878
|
+
const message = validated.error.issues.map((i) => i.message).join("; ");
|
|
7879
|
+
throw new ProjectLoaderError(
|
|
7880
|
+
"invalid_input",
|
|
7881
|
+
`Invalid config after merge: ${message}`
|
|
7882
|
+
);
|
|
7883
|
+
}
|
|
7884
|
+
await guardPath(configPath, root);
|
|
7885
|
+
await atomicWrite(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
7886
|
+
resultOverrides = raw.recipeOverrides ?? null;
|
|
7887
|
+
});
|
|
7888
|
+
const data = { recipeOverrides: resultOverrides };
|
|
7889
|
+
if (format === "json") {
|
|
7890
|
+
return { output: JSON.stringify({ version: 1, data }, null, 2) };
|
|
7891
|
+
}
|
|
7892
|
+
if (resultOverrides === null) {
|
|
7893
|
+
return { output: "Recipe overrides cleared (using recipe defaults)." };
|
|
7894
|
+
}
|
|
7895
|
+
const lines = Object.entries(resultOverrides).map(([k, v]) => ` ${k}: ${JSON.stringify(v)}`);
|
|
7896
|
+
return { output: `Recipe overrides updated:
|
|
7897
|
+
${lines.join("\n")}` };
|
|
7898
|
+
}
|
|
7899
|
+
var init_config_update = __esm({
|
|
7900
|
+
"src/cli/commands/config-update.ts"() {
|
|
7901
|
+
"use strict";
|
|
7902
|
+
init_esm_shims();
|
|
7903
|
+
init_project_loader();
|
|
7904
|
+
init_config();
|
|
7905
|
+
init_errors();
|
|
7906
|
+
}
|
|
7907
|
+
});
|
|
7908
|
+
|
|
7717
7909
|
// src/cli/register.ts
|
|
7718
7910
|
var register_exports = {};
|
|
7719
7911
|
__export(register_exports, {
|
|
7720
7912
|
registerBlockerCommand: () => registerBlockerCommand,
|
|
7913
|
+
registerConfigCommand: () => registerConfigCommand,
|
|
7721
7914
|
registerExportCommand: () => registerExportCommand,
|
|
7722
7915
|
registerHandoverCommand: () => registerHandoverCommand,
|
|
7723
7916
|
registerHookStatusCommand: () => registerHookStatusCommand,
|
|
@@ -9283,6 +9476,51 @@ function registerHookStatusCommand(yargs) {
|
|
|
9283
9476
|
}
|
|
9284
9477
|
);
|
|
9285
9478
|
}
|
|
9479
|
+
function registerConfigCommand(yargs) {
|
|
9480
|
+
return yargs.command(
|
|
9481
|
+
"config",
|
|
9482
|
+
"Manage project configuration",
|
|
9483
|
+
(y) => y.command(
|
|
9484
|
+
"set-overrides",
|
|
9485
|
+
"Set or clear recipe overrides in config.json",
|
|
9486
|
+
(y2) => y2.option("json", {
|
|
9487
|
+
type: "string",
|
|
9488
|
+
describe: "JSON object to merge into recipeOverrides"
|
|
9489
|
+
}).option("clear", {
|
|
9490
|
+
type: "boolean",
|
|
9491
|
+
describe: "Remove recipeOverrides entirely (reset to defaults)"
|
|
9492
|
+
}).option("format", {
|
|
9493
|
+
choices: ["json", "md"],
|
|
9494
|
+
default: "md",
|
|
9495
|
+
describe: "Output format"
|
|
9496
|
+
}),
|
|
9497
|
+
async (argv) => {
|
|
9498
|
+
const { handleConfigSetOverrides: handleConfigSetOverrides2 } = await Promise.resolve().then(() => (init_config_update(), config_update_exports));
|
|
9499
|
+
const { writeOutput: writeOutput2 } = await Promise.resolve().then(() => (init_run(), run_exports));
|
|
9500
|
+
const format = argv.format;
|
|
9501
|
+
try {
|
|
9502
|
+
const result = await handleConfigSetOverrides2(
|
|
9503
|
+
process.cwd(),
|
|
9504
|
+
format,
|
|
9505
|
+
{ json: argv.json, clear: argv.clear === true }
|
|
9506
|
+
);
|
|
9507
|
+
writeOutput2(result.output);
|
|
9508
|
+
if (result.errorCode) process.exitCode = 1;
|
|
9509
|
+
} catch (err) {
|
|
9510
|
+
const { formatError: formatError2, ExitCode: ExitCode2 } = await Promise.resolve().then(() => (init_output_formatter(), output_formatter_exports));
|
|
9511
|
+
const { ProjectLoaderError: ProjectLoaderError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
|
|
9512
|
+
if (err instanceof ProjectLoaderError2) {
|
|
9513
|
+
writeOutput2(formatError2(err.code, err.message, format));
|
|
9514
|
+
} else {
|
|
9515
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
9516
|
+
writeOutput2(formatError2("io_error", message, format));
|
|
9517
|
+
}
|
|
9518
|
+
process.exitCode = ExitCode2.USER_ERROR;
|
|
9519
|
+
}
|
|
9520
|
+
}
|
|
9521
|
+
).demandCommand(1, "Specify a config subcommand. Available: set-overrides")
|
|
9522
|
+
);
|
|
9523
|
+
}
|
|
9286
9524
|
var init_register = __esm({
|
|
9287
9525
|
"src/cli/register.ts"() {
|
|
9288
9526
|
"use strict";
|
|
@@ -9337,9 +9575,10 @@ async function runCli() {
|
|
|
9337
9575
|
registerReferenceCommand: registerReferenceCommand2,
|
|
9338
9576
|
registerSelftestCommand: registerSelftestCommand2,
|
|
9339
9577
|
registerSetupSkillCommand: registerSetupSkillCommand2,
|
|
9340
|
-
registerHookStatusCommand: registerHookStatusCommand2
|
|
9578
|
+
registerHookStatusCommand: registerHookStatusCommand2,
|
|
9579
|
+
registerConfigCommand: registerConfigCommand2
|
|
9341
9580
|
} = await Promise.resolve().then(() => (init_register(), register_exports));
|
|
9342
|
-
const version2 = "0.1.
|
|
9581
|
+
const version2 = "0.1.16";
|
|
9343
9582
|
class HandledError extends Error {
|
|
9344
9583
|
constructor() {
|
|
9345
9584
|
super("HANDLED_ERROR");
|
|
@@ -9378,6 +9617,7 @@ async function runCli() {
|
|
|
9378
9617
|
cli = registerSelftestCommand2(cli);
|
|
9379
9618
|
cli = registerSetupSkillCommand2(cli);
|
|
9380
9619
|
cli = registerHookStatusCommand2(cli);
|
|
9620
|
+
cli = registerConfigCommand2(cli);
|
|
9381
9621
|
function handleUnexpectedError(err) {
|
|
9382
9622
|
if (err instanceof HandledError) return;
|
|
9383
9623
|
const message = err instanceof Error ? err.message : String(err);
|
package/dist/index.d.ts
CHANGED
|
@@ -1237,15 +1237,40 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1237
1237
|
file: z.ZodString;
|
|
1238
1238
|
message: z.ZodString;
|
|
1239
1239
|
}, "strip", z.ZodTypeAny, {
|
|
1240
|
-
message: string;
|
|
1241
1240
|
type: string;
|
|
1241
|
+
message: string;
|
|
1242
1242
|
file: string;
|
|
1243
1243
|
}, {
|
|
1244
|
-
message: string;
|
|
1245
1244
|
type: string;
|
|
1245
|
+
message: string;
|
|
1246
1246
|
file: string;
|
|
1247
1247
|
}>, "many">>;
|
|
1248
1248
|
}, "strip", z.ZodTypeAny, {
|
|
1249
|
+
version: 1;
|
|
1250
|
+
config: {
|
|
1251
|
+
version: number;
|
|
1252
|
+
type: string;
|
|
1253
|
+
language: string;
|
|
1254
|
+
project: string;
|
|
1255
|
+
features: {
|
|
1256
|
+
issues: boolean;
|
|
1257
|
+
tickets: boolean;
|
|
1258
|
+
handovers: boolean;
|
|
1259
|
+
roadmap: boolean;
|
|
1260
|
+
reviews: boolean;
|
|
1261
|
+
} & {
|
|
1262
|
+
[k: string]: unknown;
|
|
1263
|
+
};
|
|
1264
|
+
schemaVersion?: number | undefined;
|
|
1265
|
+
recipe?: string | undefined;
|
|
1266
|
+
recipeOverrides?: {
|
|
1267
|
+
maxTicketsPerSession?: number | undefined;
|
|
1268
|
+
compactThreshold?: string | undefined;
|
|
1269
|
+
reviewBackends?: string[] | undefined;
|
|
1270
|
+
} | undefined;
|
|
1271
|
+
} & {
|
|
1272
|
+
[k: string]: unknown;
|
|
1273
|
+
};
|
|
1249
1274
|
issues: z.objectOutputType<{
|
|
1250
1275
|
id: z.ZodString;
|
|
1251
1276
|
title: z.ZodString;
|
|
@@ -1281,8 +1306,8 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1281
1306
|
lastModifiedBy: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1282
1307
|
}, z.ZodTypeAny, "passthrough">[];
|
|
1283
1308
|
roadmap: {
|
|
1284
|
-
title: string;
|
|
1285
1309
|
date: string;
|
|
1310
|
+
title: string;
|
|
1286
1311
|
phases: z.objectOutputType<{
|
|
1287
1312
|
id: z.ZodString;
|
|
1288
1313
|
label: z.ZodString;
|
|
@@ -1300,7 +1325,6 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1300
1325
|
} & {
|
|
1301
1326
|
[k: string]: unknown;
|
|
1302
1327
|
};
|
|
1303
|
-
version: 1;
|
|
1304
1328
|
project: string;
|
|
1305
1329
|
notes: z.objectOutputType<{
|
|
1306
1330
|
id: z.ZodString;
|
|
@@ -1312,11 +1336,19 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1312
1336
|
updatedDate: z.ZodEffects<z.ZodString, string, string>;
|
|
1313
1337
|
}, z.ZodTypeAny, "passthrough">[];
|
|
1314
1338
|
createdAt: string;
|
|
1315
|
-
|
|
1339
|
+
handoverFilenames: string[];
|
|
1340
|
+
warnings?: {
|
|
1316
1341
|
type: string;
|
|
1342
|
+
message: string;
|
|
1343
|
+
file: string;
|
|
1344
|
+
}[] | undefined;
|
|
1345
|
+
}, {
|
|
1346
|
+
version: 1;
|
|
1347
|
+
config: {
|
|
1317
1348
|
version: number;
|
|
1318
|
-
|
|
1349
|
+
type: string;
|
|
1319
1350
|
language: string;
|
|
1351
|
+
project: string;
|
|
1320
1352
|
features: {
|
|
1321
1353
|
issues: boolean;
|
|
1322
1354
|
tickets: boolean;
|
|
@@ -1336,13 +1368,6 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1336
1368
|
} & {
|
|
1337
1369
|
[k: string]: unknown;
|
|
1338
1370
|
};
|
|
1339
|
-
handoverFilenames: string[];
|
|
1340
|
-
warnings?: {
|
|
1341
|
-
message: string;
|
|
1342
|
-
type: string;
|
|
1343
|
-
file: string;
|
|
1344
|
-
}[] | undefined;
|
|
1345
|
-
}, {
|
|
1346
1371
|
issues: z.objectInputType<{
|
|
1347
1372
|
id: z.ZodString;
|
|
1348
1373
|
title: z.ZodString;
|
|
@@ -1378,8 +1403,8 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1378
1403
|
lastModifiedBy: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
1379
1404
|
}, z.ZodTypeAny, "passthrough">[];
|
|
1380
1405
|
roadmap: {
|
|
1381
|
-
title: string;
|
|
1382
1406
|
date: string;
|
|
1407
|
+
title: string;
|
|
1383
1408
|
phases: z.objectInputType<{
|
|
1384
1409
|
id: z.ZodString;
|
|
1385
1410
|
label: z.ZodString;
|
|
@@ -1397,33 +1422,8 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1397
1422
|
} & {
|
|
1398
1423
|
[k: string]: unknown;
|
|
1399
1424
|
};
|
|
1400
|
-
version: 1;
|
|
1401
1425
|
project: string;
|
|
1402
1426
|
createdAt: string;
|
|
1403
|
-
config: {
|
|
1404
|
-
type: string;
|
|
1405
|
-
version: number;
|
|
1406
|
-
project: string;
|
|
1407
|
-
language: string;
|
|
1408
|
-
features: {
|
|
1409
|
-
issues: boolean;
|
|
1410
|
-
tickets: boolean;
|
|
1411
|
-
handovers: boolean;
|
|
1412
|
-
roadmap: boolean;
|
|
1413
|
-
reviews: boolean;
|
|
1414
|
-
} & {
|
|
1415
|
-
[k: string]: unknown;
|
|
1416
|
-
};
|
|
1417
|
-
schemaVersion?: number | undefined;
|
|
1418
|
-
recipe?: string | undefined;
|
|
1419
|
-
recipeOverrides?: {
|
|
1420
|
-
maxTicketsPerSession?: number | undefined;
|
|
1421
|
-
compactThreshold?: string | undefined;
|
|
1422
|
-
reviewBackends?: string[] | undefined;
|
|
1423
|
-
} | undefined;
|
|
1424
|
-
} & {
|
|
1425
|
-
[k: string]: unknown;
|
|
1426
|
-
};
|
|
1427
1427
|
notes?: z.objectInputType<{
|
|
1428
1428
|
id: z.ZodString;
|
|
1429
1429
|
title: z.ZodNullable<z.ZodString>;
|
|
@@ -1434,8 +1434,8 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1434
1434
|
updatedDate: z.ZodEffects<z.ZodString, string, string>;
|
|
1435
1435
|
}, z.ZodTypeAny, "passthrough">[] | undefined;
|
|
1436
1436
|
warnings?: {
|
|
1437
|
-
message: string;
|
|
1438
1437
|
type: string;
|
|
1438
|
+
message: string;
|
|
1439
1439
|
file: string;
|
|
1440
1440
|
}[] | undefined;
|
|
1441
1441
|
handoverFilenames?: string[] | undefined;
|
package/dist/mcp.js
CHANGED
|
@@ -504,6 +504,29 @@ var init_handover_parser = __esm({
|
|
|
504
504
|
});
|
|
505
505
|
|
|
506
506
|
// src/core/project-loader.ts
|
|
507
|
+
var project_loader_exports = {};
|
|
508
|
+
__export(project_loader_exports, {
|
|
509
|
+
atomicWrite: () => atomicWrite,
|
|
510
|
+
deleteIssue: () => deleteIssue,
|
|
511
|
+
deleteNote: () => deleteNote,
|
|
512
|
+
deleteTicket: () => deleteTicket,
|
|
513
|
+
guardPath: () => guardPath,
|
|
514
|
+
loadProject: () => loadProject,
|
|
515
|
+
runTransaction: () => runTransaction,
|
|
516
|
+
runTransactionUnlocked: () => runTransactionUnlocked,
|
|
517
|
+
serializeJSON: () => serializeJSON,
|
|
518
|
+
sortKeysDeep: () => sortKeysDeep,
|
|
519
|
+
withProjectLock: () => withProjectLock,
|
|
520
|
+
writeConfig: () => writeConfig,
|
|
521
|
+
writeIssue: () => writeIssue,
|
|
522
|
+
writeIssueUnlocked: () => writeIssueUnlocked,
|
|
523
|
+
writeNote: () => writeNote,
|
|
524
|
+
writeNoteUnlocked: () => writeNoteUnlocked,
|
|
525
|
+
writeRoadmap: () => writeRoadmap,
|
|
526
|
+
writeRoadmapUnlocked: () => writeRoadmapUnlocked,
|
|
527
|
+
writeTicket: () => writeTicket,
|
|
528
|
+
writeTicketUnlocked: () => writeTicketUnlocked
|
|
529
|
+
});
|
|
507
530
|
import {
|
|
508
531
|
readdir as readdir2,
|
|
509
532
|
readFile as readFile2,
|
|
@@ -807,6 +830,67 @@ async function withProjectLock(root, options, handler) {
|
|
|
807
830
|
await handler(result);
|
|
808
831
|
});
|
|
809
832
|
}
|
|
833
|
+
async function runTransactionUnlocked(root, operations) {
|
|
834
|
+
const wrapDir = resolve2(root, ".story");
|
|
835
|
+
const journalPath = join3(wrapDir, ".txn.json");
|
|
836
|
+
const entries = [];
|
|
837
|
+
let commitStarted = false;
|
|
838
|
+
try {
|
|
839
|
+
for (const op of operations) {
|
|
840
|
+
if (op.op === "write") {
|
|
841
|
+
const tempPath = `${op.target}.${process.pid}.tmp`;
|
|
842
|
+
entries.push({ op: "write", target: op.target, tempPath });
|
|
843
|
+
} else {
|
|
844
|
+
entries.push({ op: "delete", target: op.target });
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
const journal = { entries, commitStarted: false };
|
|
848
|
+
await fsyncWrite(journalPath, JSON.stringify(journal, null, 2));
|
|
849
|
+
for (const op of operations) {
|
|
850
|
+
if (op.op === "write") {
|
|
851
|
+
const tempPath = `${op.target}.${process.pid}.tmp`;
|
|
852
|
+
await fsyncWrite(tempPath, op.content);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
journal.commitStarted = true;
|
|
856
|
+
await fsyncWrite(journalPath, JSON.stringify(journal, null, 2));
|
|
857
|
+
commitStarted = true;
|
|
858
|
+
for (const entry of entries) {
|
|
859
|
+
if (entry.op === "write" && entry.tempPath) {
|
|
860
|
+
await rename(entry.tempPath, entry.target);
|
|
861
|
+
} else if (entry.op === "delete") {
|
|
862
|
+
try {
|
|
863
|
+
await unlink(entry.target);
|
|
864
|
+
} catch {
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
await unlink(journalPath);
|
|
869
|
+
} catch (err) {
|
|
870
|
+
if (!commitStarted) {
|
|
871
|
+
for (const entry of entries) {
|
|
872
|
+
if (entry.tempPath) {
|
|
873
|
+
try {
|
|
874
|
+
await unlink(entry.tempPath);
|
|
875
|
+
} catch {
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
try {
|
|
880
|
+
await unlink(journalPath);
|
|
881
|
+
} catch {
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
if (err instanceof ProjectLoaderError) throw err;
|
|
885
|
+
throw new ProjectLoaderError("io_error", "Transaction failed", err);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
async function runTransaction(root, operations) {
|
|
889
|
+
const wrapDir = resolve2(root, ".story");
|
|
890
|
+
await withLock(wrapDir, async () => {
|
|
891
|
+
await runTransactionUnlocked(root, operations);
|
|
892
|
+
});
|
|
893
|
+
}
|
|
810
894
|
async function doRecoverTransaction(wrapDir) {
|
|
811
895
|
const journalPath = join3(wrapDir, ".txn.json");
|
|
812
896
|
let entries;
|
|
@@ -996,6 +1080,15 @@ async function atomicWrite(targetPath, content) {
|
|
|
996
1080
|
);
|
|
997
1081
|
}
|
|
998
1082
|
}
|
|
1083
|
+
async function fsyncWrite(filePath, content) {
|
|
1084
|
+
const fh = await open(filePath, "w");
|
|
1085
|
+
try {
|
|
1086
|
+
await fh.writeFile(content, "utf-8");
|
|
1087
|
+
await fh.sync();
|
|
1088
|
+
} finally {
|
|
1089
|
+
await fh.close();
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
999
1092
|
async function guardPath(target, root) {
|
|
1000
1093
|
let resolvedRoot;
|
|
1001
1094
|
try {
|
|
@@ -4739,6 +4832,19 @@ async function handleReportPlan(root, dir, state, report) {
|
|
|
4739
4832
|
});
|
|
4740
4833
|
}
|
|
4741
4834
|
const risk = assessRisk(void 0, void 0);
|
|
4835
|
+
if (state.ticket) {
|
|
4836
|
+
try {
|
|
4837
|
+
const { withProjectLock: withProjectLock2, writeTicketUnlocked: writeTicketUnlocked2 } = await Promise.resolve().then(() => (init_project_loader(), project_loader_exports));
|
|
4838
|
+
await withProjectLock2(root, { strict: false }, async ({ state: projectState }) => {
|
|
4839
|
+
const ticket = projectState.ticketByID(state.ticket.id);
|
|
4840
|
+
if (ticket && ticket.status !== "inprogress") {
|
|
4841
|
+
const updated = { ...ticket, status: "inprogress" };
|
|
4842
|
+
await writeTicketUnlocked2(updated, root);
|
|
4843
|
+
}
|
|
4844
|
+
});
|
|
4845
|
+
} catch {
|
|
4846
|
+
}
|
|
4847
|
+
}
|
|
4742
4848
|
const written = writeSessionSync(dir, {
|
|
4743
4849
|
...state,
|
|
4744
4850
|
state: "PLAN_REVIEW",
|
|
@@ -5096,11 +5202,11 @@ async function handleReportComplete(root, dir, state, report) {
|
|
|
5096
5202
|
const maxTickets = updated.config.maxTicketsPerSession;
|
|
5097
5203
|
let nextState;
|
|
5098
5204
|
let advice = "ok";
|
|
5099
|
-
if (
|
|
5205
|
+
if (maxTickets > 0 && ticketsDone >= maxTickets) {
|
|
5100
5206
|
nextState = "HANDOVER";
|
|
5101
|
-
|
|
5102
|
-
} else if (maxTickets > 0 && ticketsDone >= maxTickets) {
|
|
5207
|
+
} else if (pressure === "critical") {
|
|
5103
5208
|
nextState = "HANDOVER";
|
|
5209
|
+
advice = "compact-now";
|
|
5104
5210
|
} else if (pressure === "high") {
|
|
5105
5211
|
advice = "consider-compact";
|
|
5106
5212
|
nextState = "PICK_TICKET";
|
|
@@ -5180,6 +5286,36 @@ async function handleReportHandover(root, dir, state, report) {
|
|
|
5180
5286
|
} catch {
|
|
5181
5287
|
}
|
|
5182
5288
|
}
|
|
5289
|
+
const pressureLevel = state.contextPressure?.level ?? "low";
|
|
5290
|
+
const maxTickets = state.config.maxTicketsPerSession;
|
|
5291
|
+
const capReached = maxTickets > 0 && state.completedTickets.length >= maxTickets;
|
|
5292
|
+
let hasMoreTickets = false;
|
|
5293
|
+
try {
|
|
5294
|
+
const { state: ps } = await loadProject(root);
|
|
5295
|
+
hasMoreTickets = nextTickets(ps, 1).kind === "found";
|
|
5296
|
+
} catch {
|
|
5297
|
+
}
|
|
5298
|
+
if (pressureLevel === "critical" && hasMoreTickets && !capReached) {
|
|
5299
|
+
return guideResult(state, "HANDOVER", {
|
|
5300
|
+
instruction: [
|
|
5301
|
+
"# Context Compaction Needed",
|
|
5302
|
+
"",
|
|
5303
|
+
`${state.completedTickets.length} ticket(s) completed so far. Handover written. Context is large \u2014 time to compact and continue.`,
|
|
5304
|
+
"",
|
|
5305
|
+
'Call `claudestory_autonomous_guide` with `action: "pre_compact"` now:',
|
|
5306
|
+
"```json",
|
|
5307
|
+
`{ "sessionId": "${state.sessionId}", "action": "pre_compact" }`,
|
|
5308
|
+
"```",
|
|
5309
|
+
"",
|
|
5310
|
+
'After pre_compact responds, run `/compact`, then call with `action: "resume"` to continue working on more tickets.'
|
|
5311
|
+
].join("\n"),
|
|
5312
|
+
reminders: [
|
|
5313
|
+
"Do NOT stop. This is a context compaction, not a session end.",
|
|
5314
|
+
"Call pre_compact \u2192 /compact \u2192 resume to continue autonomous work."
|
|
5315
|
+
],
|
|
5316
|
+
contextAdvice: "compact-now"
|
|
5317
|
+
});
|
|
5318
|
+
}
|
|
5183
5319
|
const written = writeSessionSync(dir, {
|
|
5184
5320
|
...state,
|
|
5185
5321
|
state: "SESSION_END",
|
|
@@ -5227,6 +5363,40 @@ async function handleResume(root, args) {
|
|
|
5227
5363
|
resumeFromRevision: null,
|
|
5228
5364
|
contextPressure: { ...info.state.contextPressure, compactionCount: (info.state.contextPressure?.compactionCount ?? 0) + 1 }
|
|
5229
5365
|
});
|
|
5366
|
+
if (resumeState === "PICK_TICKET") {
|
|
5367
|
+
let candidatesText = "No ticket candidates available.";
|
|
5368
|
+
let topCandidate = null;
|
|
5369
|
+
try {
|
|
5370
|
+
const { state: ps } = await loadProject(root);
|
|
5371
|
+
const result = nextTickets(ps, 5);
|
|
5372
|
+
if (result.kind === "found") {
|
|
5373
|
+
topCandidate = result.candidates[0] ?? null;
|
|
5374
|
+
candidatesText = result.candidates.map(
|
|
5375
|
+
(c, i) => `${i + 1}. **${c.ticket.id}: ${c.ticket.title}** (${c.ticket.type})`
|
|
5376
|
+
).join("\n");
|
|
5377
|
+
}
|
|
5378
|
+
} catch {
|
|
5379
|
+
}
|
|
5380
|
+
return guideResult(written, "PICK_TICKET", {
|
|
5381
|
+
instruction: [
|
|
5382
|
+
"# Resumed After Compact \u2014 Continue Working",
|
|
5383
|
+
"",
|
|
5384
|
+
`${written.completedTickets.length} ticket(s) done so far. Context compacted. Pick the next ticket immediately.`,
|
|
5385
|
+
"",
|
|
5386
|
+
candidatesText,
|
|
5387
|
+
"",
|
|
5388
|
+
topCandidate ? `Pick **${topCandidate.ticket.id}** by calling \`claudestory_autonomous_guide\` now:` : "Pick a ticket now:",
|
|
5389
|
+
"```json",
|
|
5390
|
+
topCandidate ? `{ "sessionId": "${written.sessionId}", "action": "report", "report": { "completedAction": "ticket_picked", "ticketId": "${topCandidate.ticket.id}" } }` : `{ "sessionId": "${written.sessionId}", "action": "report", "report": { "completedAction": "ticket_picked", "ticketId": "T-XXX" } }`,
|
|
5391
|
+
"```"
|
|
5392
|
+
].join("\n"),
|
|
5393
|
+
reminders: [
|
|
5394
|
+
"Do NOT stop or summarize. Pick the next ticket IMMEDIATELY.",
|
|
5395
|
+
"Do NOT ask the user for confirmation.",
|
|
5396
|
+
"You are in autonomous mode \u2014 continue working."
|
|
5397
|
+
]
|
|
5398
|
+
});
|
|
5399
|
+
}
|
|
5230
5400
|
return guideResult(written, resumeState, {
|
|
5231
5401
|
instruction: [
|
|
5232
5402
|
"# Resumed After Compact",
|
|
@@ -5238,6 +5408,7 @@ async function handleResume(root, args) {
|
|
|
5238
5408
|
].join("\n"),
|
|
5239
5409
|
reminders: [
|
|
5240
5410
|
"Do NOT use plan mode.",
|
|
5411
|
+
"Do NOT stop or summarize.",
|
|
5241
5412
|
"Call autonomous_guide after completing each step."
|
|
5242
5413
|
]
|
|
5243
5414
|
});
|
|
@@ -5253,11 +5424,12 @@ async function handlePreCompact(root, args) {
|
|
|
5253
5424
|
return guideError(new Error(`Session ${args.sessionId} is already in COMPACT state. Call action: "resume" to continue.`));
|
|
5254
5425
|
}
|
|
5255
5426
|
const headResult = await gitHead(root);
|
|
5427
|
+
const resumeTarget = info.state.state === "HANDOVER" ? "PICK_TICKET" : info.state.state;
|
|
5256
5428
|
const written = writeSessionSync(info.dir, {
|
|
5257
5429
|
...refreshLease(info.state),
|
|
5258
5430
|
state: "COMPACT",
|
|
5259
5431
|
previousState: info.state.state,
|
|
5260
|
-
preCompactState:
|
|
5432
|
+
preCompactState: resumeTarget,
|
|
5261
5433
|
resumeFromRevision: info.state.revision,
|
|
5262
5434
|
git: {
|
|
5263
5435
|
...info.state.git,
|
|
@@ -6034,7 +6206,7 @@ async function ensureGitignoreEntries(gitignorePath, entries) {
|
|
|
6034
6206
|
// src/mcp/index.ts
|
|
6035
6207
|
var ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
|
|
6036
6208
|
var CONFIG_PATH2 = ".story/config.json";
|
|
6037
|
-
var version = "0.1.
|
|
6209
|
+
var version = "0.1.16";
|
|
6038
6210
|
function tryDiscoverRoot() {
|
|
6039
6211
|
const envRoot = process.env[ENV_VAR2];
|
|
6040
6212
|
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.16",
|
|
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": [
|