@anthropologies/claudestory 0.1.35 → 0.1.37
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 +584 -146
- package/dist/index.d.ts +18 -3
- package/dist/index.js +124 -6
- package/dist/mcp.js +540 -114
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1761,7 +1761,7 @@ function fencedBlock(content, lang) {
|
|
|
1761
1761
|
${content}
|
|
1762
1762
|
${fence}`;
|
|
1763
1763
|
}
|
|
1764
|
-
function formatStatus(state, format) {
|
|
1764
|
+
function formatStatus(state, format, activeSessions = []) {
|
|
1765
1765
|
const phases = phasesWithStatus(state);
|
|
1766
1766
|
const data = {
|
|
1767
1767
|
project: state.config.project,
|
|
@@ -1781,7 +1781,8 @@ function formatStatus(state, format) {
|
|
|
1781
1781
|
name: p.phase.name,
|
|
1782
1782
|
status: p.status,
|
|
1783
1783
|
leafCount: p.leafCount
|
|
1784
|
-
}))
|
|
1784
|
+
})),
|
|
1785
|
+
...activeSessions.length > 0 ? { activeSessions } : {}
|
|
1785
1786
|
};
|
|
1786
1787
|
if (format === "json") {
|
|
1787
1788
|
return JSON.stringify(successEnvelope(data), null, 2);
|
|
@@ -1803,6 +1804,15 @@ function formatStatus(state, format) {
|
|
|
1803
1804
|
const summary = p.phase.summary ?? truncate(p.phase.description, 80);
|
|
1804
1805
|
lines.push(`${indicator} **${escapeMarkdownInline(p.phase.name)}** (${p.leafCount} tickets) \u2014 ${escapeMarkdownInline(summary)}`);
|
|
1805
1806
|
}
|
|
1807
|
+
if (activeSessions.length > 0) {
|
|
1808
|
+
lines.push("");
|
|
1809
|
+
lines.push("## Active Sessions");
|
|
1810
|
+
lines.push("");
|
|
1811
|
+
for (const s of activeSessions) {
|
|
1812
|
+
const ticket = s.ticketId ? `${s.ticketId}: ${escapeMarkdownInline(s.ticketTitle ?? "")}` : "no ticket";
|
|
1813
|
+
lines.push(`- ${s.sessionId.slice(0, 8)}: ${s.state} -- ${ticket} (${s.mode} mode)`);
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1806
1816
|
if (state.isEmptyScaffold) {
|
|
1807
1817
|
lines.push("");
|
|
1808
1818
|
lines.push(EMPTY_SCAFFOLD_HEADING);
|
|
@@ -2691,15 +2701,71 @@ var init_output_formatter = __esm({
|
|
|
2691
2701
|
}
|
|
2692
2702
|
});
|
|
2693
2703
|
|
|
2704
|
+
// src/core/session-scan.ts
|
|
2705
|
+
import { readdirSync, readFileSync } from "fs";
|
|
2706
|
+
import { join as join4 } from "path";
|
|
2707
|
+
function scanActiveSessions(root) {
|
|
2708
|
+
const sessDir = join4(root, ".story", "sessions");
|
|
2709
|
+
let entries;
|
|
2710
|
+
try {
|
|
2711
|
+
entries = readdirSync(sessDir, { withFileTypes: true });
|
|
2712
|
+
} catch {
|
|
2713
|
+
return [];
|
|
2714
|
+
}
|
|
2715
|
+
const results = [];
|
|
2716
|
+
for (const entry of entries) {
|
|
2717
|
+
if (!entry.isDirectory()) continue;
|
|
2718
|
+
const statePath2 = join4(sessDir, entry.name, "state.json");
|
|
2719
|
+
let raw;
|
|
2720
|
+
try {
|
|
2721
|
+
raw = readFileSync(statePath2, "utf-8");
|
|
2722
|
+
} catch {
|
|
2723
|
+
continue;
|
|
2724
|
+
}
|
|
2725
|
+
let parsed;
|
|
2726
|
+
try {
|
|
2727
|
+
parsed = JSON.parse(raw);
|
|
2728
|
+
} catch {
|
|
2729
|
+
continue;
|
|
2730
|
+
}
|
|
2731
|
+
if (parsed.status !== "active") continue;
|
|
2732
|
+
if (parsed.state === "SESSION_END") continue;
|
|
2733
|
+
const lease = parsed.lease;
|
|
2734
|
+
if (lease?.expiresAt) {
|
|
2735
|
+
const expires = new Date(lease.expiresAt).getTime();
|
|
2736
|
+
if (!Number.isNaN(expires) && expires <= Date.now()) continue;
|
|
2737
|
+
} else {
|
|
2738
|
+
continue;
|
|
2739
|
+
}
|
|
2740
|
+
const ticket = parsed.ticket;
|
|
2741
|
+
results.push({
|
|
2742
|
+
sessionId: parsed.sessionId ?? entry.name,
|
|
2743
|
+
state: parsed.state ?? "unknown",
|
|
2744
|
+
mode: parsed.mode ?? "auto",
|
|
2745
|
+
ticketId: ticket?.id ?? null,
|
|
2746
|
+
ticketTitle: ticket?.title ?? null
|
|
2747
|
+
});
|
|
2748
|
+
}
|
|
2749
|
+
return results;
|
|
2750
|
+
}
|
|
2751
|
+
var init_session_scan = __esm({
|
|
2752
|
+
"src/core/session-scan.ts"() {
|
|
2753
|
+
"use strict";
|
|
2754
|
+
init_esm_shims();
|
|
2755
|
+
}
|
|
2756
|
+
});
|
|
2757
|
+
|
|
2694
2758
|
// src/cli/commands/status.ts
|
|
2695
2759
|
function handleStatus(ctx) {
|
|
2696
|
-
|
|
2760
|
+
const sessions = scanActiveSessions(ctx.root);
|
|
2761
|
+
return { output: formatStatus(ctx.state, ctx.format, sessions) };
|
|
2697
2762
|
}
|
|
2698
2763
|
var init_status = __esm({
|
|
2699
2764
|
"src/cli/commands/status.ts"() {
|
|
2700
2765
|
"use strict";
|
|
2701
2766
|
init_esm_shims();
|
|
2702
2767
|
init_output_formatter();
|
|
2768
|
+
init_session_scan();
|
|
2703
2769
|
}
|
|
2704
2770
|
});
|
|
2705
2771
|
|
|
@@ -2787,6 +2853,7 @@ function validateProject(state) {
|
|
|
2787
2853
|
}
|
|
2788
2854
|
}
|
|
2789
2855
|
}
|
|
2856
|
+
detectSupersedesCycles(state, findings);
|
|
2790
2857
|
const phaseIDCounts = /* @__PURE__ */ new Map();
|
|
2791
2858
|
for (const p of state.roadmap.phases) {
|
|
2792
2859
|
phaseIDCounts.set(p.id, (phaseIDCounts.get(p.id) ?? 0) + 1);
|
|
@@ -2991,6 +3058,33 @@ function dfsBlocked(id, state, visited, inStack, findings) {
|
|
|
2991
3058
|
inStack.delete(id);
|
|
2992
3059
|
visited.add(id);
|
|
2993
3060
|
}
|
|
3061
|
+
function detectSupersedesCycles(state, findings) {
|
|
3062
|
+
const visited = /* @__PURE__ */ new Set();
|
|
3063
|
+
const inStack = /* @__PURE__ */ new Set();
|
|
3064
|
+
for (const l of state.lessons) {
|
|
3065
|
+
if (l.supersedes == null || visited.has(l.id)) continue;
|
|
3066
|
+
dfsSupersedesChain(l.id, state, visited, inStack, findings);
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
function dfsSupersedesChain(id, state, visited, inStack, findings) {
|
|
3070
|
+
if (inStack.has(id)) {
|
|
3071
|
+
findings.push({
|
|
3072
|
+
level: "error",
|
|
3073
|
+
code: "supersedes_cycle",
|
|
3074
|
+
message: `Cycle detected in supersedes chain involving ${id}.`,
|
|
3075
|
+
entity: id
|
|
3076
|
+
});
|
|
3077
|
+
return;
|
|
3078
|
+
}
|
|
3079
|
+
if (visited.has(id)) return;
|
|
3080
|
+
inStack.add(id);
|
|
3081
|
+
const lesson = state.lessonByID(id);
|
|
3082
|
+
if (lesson?.supersedes && lesson.supersedes !== id) {
|
|
3083
|
+
dfsSupersedesChain(lesson.supersedes, state, visited, inStack, findings);
|
|
3084
|
+
}
|
|
3085
|
+
inStack.delete(id);
|
|
3086
|
+
visited.add(id);
|
|
3087
|
+
}
|
|
2994
3088
|
var init_validation = __esm({
|
|
2995
3089
|
"src/core/validation.ts"() {
|
|
2996
3090
|
"use strict";
|
|
@@ -3027,7 +3121,7 @@ __export(handover_exports, {
|
|
|
3027
3121
|
});
|
|
3028
3122
|
import { existsSync as existsSync4 } from "fs";
|
|
3029
3123
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
3030
|
-
import { join as
|
|
3124
|
+
import { join as join5, resolve as resolve4 } from "path";
|
|
3031
3125
|
function handleHandoverList(ctx) {
|
|
3032
3126
|
return { output: formatHandoverList(ctx.state.handoverFilenames, ctx.format) };
|
|
3033
3127
|
}
|
|
@@ -3112,15 +3206,15 @@ async function handleHandoverCreate(content, slugRaw, format, root) {
|
|
|
3112
3206
|
let filename;
|
|
3113
3207
|
await withProjectLock(root, { strict: false }, async () => {
|
|
3114
3208
|
const absRoot = resolve4(root);
|
|
3115
|
-
const handoversDir =
|
|
3209
|
+
const handoversDir = join5(absRoot, ".story", "handovers");
|
|
3116
3210
|
await mkdir2(handoversDir, { recursive: true });
|
|
3117
|
-
const wrapDir =
|
|
3211
|
+
const wrapDir = join5(absRoot, ".story");
|
|
3118
3212
|
const datePrefix = `${date}-`;
|
|
3119
3213
|
const seqRegex = new RegExp(`^${date}-(\\d{2})-`);
|
|
3120
3214
|
let maxSeq = 0;
|
|
3121
|
-
const { readdirSync:
|
|
3215
|
+
const { readdirSync: readdirSync5 } = await import("fs");
|
|
3122
3216
|
try {
|
|
3123
|
-
for (const f of
|
|
3217
|
+
for (const f of readdirSync5(handoversDir)) {
|
|
3124
3218
|
const m = f.match(seqRegex);
|
|
3125
3219
|
if (m) {
|
|
3126
3220
|
const n = parseInt(m[1], 10);
|
|
@@ -3137,7 +3231,7 @@ async function handleHandoverCreate(content, slugRaw, format, root) {
|
|
|
3137
3231
|
);
|
|
3138
3232
|
}
|
|
3139
3233
|
let candidate = `${date}-${String(nextSeq).padStart(2, "0")}-${slug}.md`;
|
|
3140
|
-
let candidatePath =
|
|
3234
|
+
let candidatePath = join5(handoversDir, candidate);
|
|
3141
3235
|
while (existsSync4(candidatePath)) {
|
|
3142
3236
|
nextSeq++;
|
|
3143
3237
|
if (nextSeq > 99) {
|
|
@@ -3147,7 +3241,7 @@ async function handleHandoverCreate(content, slugRaw, format, root) {
|
|
|
3147
3241
|
);
|
|
3148
3242
|
}
|
|
3149
3243
|
candidate = `${date}-${String(nextSeq).padStart(2, "0")}-${slug}.md`;
|
|
3150
|
-
candidatePath =
|
|
3244
|
+
candidatePath = join5(handoversDir, candidate);
|
|
3151
3245
|
}
|
|
3152
3246
|
await parseHandoverFilename(candidate, handoversDir);
|
|
3153
3247
|
await guardPath(candidatePath, wrapDir);
|
|
@@ -3780,11 +3874,11 @@ __export(snapshot_exports, {
|
|
|
3780
3874
|
});
|
|
3781
3875
|
import { readdir as readdir3, readFile as readFile3, mkdir as mkdir3, unlink as unlink2 } from "fs/promises";
|
|
3782
3876
|
import { existsSync as existsSync5 } from "fs";
|
|
3783
|
-
import { join as
|
|
3877
|
+
import { join as join6, resolve as resolve5 } from "path";
|
|
3784
3878
|
import { z as z8 } from "zod";
|
|
3785
3879
|
async function saveSnapshot(root, loadResult) {
|
|
3786
3880
|
const absRoot = resolve5(root);
|
|
3787
|
-
const snapshotsDir =
|
|
3881
|
+
const snapshotsDir = join6(absRoot, ".story", "snapshots");
|
|
3788
3882
|
await mkdir3(snapshotsDir, { recursive: true });
|
|
3789
3883
|
const { state, warnings } = loadResult;
|
|
3790
3884
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -3809,8 +3903,8 @@ async function saveSnapshot(root, loadResult) {
|
|
|
3809
3903
|
} : {}
|
|
3810
3904
|
};
|
|
3811
3905
|
const json = JSON.stringify(snapshot, null, 2) + "\n";
|
|
3812
|
-
const targetPath =
|
|
3813
|
-
const wrapDir =
|
|
3906
|
+
const targetPath = join6(snapshotsDir, filename);
|
|
3907
|
+
const wrapDir = join6(absRoot, ".story");
|
|
3814
3908
|
await guardPath(targetPath, wrapDir);
|
|
3815
3909
|
await atomicWrite(targetPath, json);
|
|
3816
3910
|
const pruned = await pruneSnapshots(snapshotsDir);
|
|
@@ -3818,13 +3912,13 @@ async function saveSnapshot(root, loadResult) {
|
|
|
3818
3912
|
return { filename, retained: entries.length, pruned };
|
|
3819
3913
|
}
|
|
3820
3914
|
async function loadLatestSnapshot(root) {
|
|
3821
|
-
const snapshotsDir =
|
|
3915
|
+
const snapshotsDir = join6(resolve5(root), ".story", "snapshots");
|
|
3822
3916
|
if (!existsSync5(snapshotsDir)) return null;
|
|
3823
3917
|
const files = await listSnapshotFiles(snapshotsDir);
|
|
3824
3918
|
if (files.length === 0) return null;
|
|
3825
3919
|
for (const filename of files) {
|
|
3826
3920
|
try {
|
|
3827
|
-
const content = await readFile3(
|
|
3921
|
+
const content = await readFile3(join6(snapshotsDir, filename), "utf-8");
|
|
3828
3922
|
const parsed = JSON.parse(content);
|
|
3829
3923
|
const snapshot = SnapshotV1Schema.parse(parsed);
|
|
3830
3924
|
return { snapshot, filename };
|
|
@@ -4068,7 +4162,7 @@ async function pruneSnapshots(dir) {
|
|
|
4068
4162
|
const toRemove = files.slice(MAX_SNAPSHOTS);
|
|
4069
4163
|
for (const f of toRemove) {
|
|
4070
4164
|
try {
|
|
4071
|
-
await unlink2(
|
|
4165
|
+
await unlink2(join6(dir, f));
|
|
4072
4166
|
} catch {
|
|
4073
4167
|
}
|
|
4074
4168
|
}
|
|
@@ -4487,7 +4581,7 @@ var init_lesson2 = __esm({
|
|
|
4487
4581
|
});
|
|
4488
4582
|
|
|
4489
4583
|
// src/core/recommend.ts
|
|
4490
|
-
function recommend(state, count) {
|
|
4584
|
+
function recommend(state, count, options) {
|
|
4491
4585
|
const effectiveCount = Math.max(1, Math.min(10, count));
|
|
4492
4586
|
const dedup = /* @__PURE__ */ new Map();
|
|
4493
4587
|
const phaseIndex = buildPhaseIndex(state);
|
|
@@ -4499,7 +4593,8 @@ function recommend(state, count) {
|
|
|
4499
4593
|
() => generateNearCompleteUmbrellas(state, phaseIndex),
|
|
4500
4594
|
() => generatePhaseMomentum(state),
|
|
4501
4595
|
() => generateQuickWins(state, phaseIndex),
|
|
4502
|
-
() => generateOpenIssues(state)
|
|
4596
|
+
() => generateOpenIssues(state),
|
|
4597
|
+
() => generateDebtTrend(state, options)
|
|
4503
4598
|
];
|
|
4504
4599
|
for (const gen of generators) {
|
|
4505
4600
|
for (const rec of gen()) {
|
|
@@ -4509,6 +4604,7 @@ function recommend(state, count) {
|
|
|
4509
4604
|
}
|
|
4510
4605
|
}
|
|
4511
4606
|
}
|
|
4607
|
+
applyHandoverBoost(state, dedup, options);
|
|
4512
4608
|
const curPhase = currentPhase(state);
|
|
4513
4609
|
const curPhaseIdx = curPhase ? phaseIndex.get(curPhase.id) ?? 0 : 0;
|
|
4514
4610
|
for (const [id, rec] of dedup) {
|
|
@@ -4698,7 +4794,76 @@ function sortByPhaseAndOrder(tickets, phaseIndex) {
|
|
|
4698
4794
|
return a.order - b.order;
|
|
4699
4795
|
});
|
|
4700
4796
|
}
|
|
4701
|
-
|
|
4797
|
+
function applyHandoverBoost(state, dedup, options) {
|
|
4798
|
+
if (!options?.latestHandoverContent) return;
|
|
4799
|
+
const content = options.latestHandoverContent;
|
|
4800
|
+
let actionableIds = extractTicketIdsFromActionableSections(content);
|
|
4801
|
+
if (actionableIds.size === 0) {
|
|
4802
|
+
const allIds = new Set(content.match(TICKET_ID_RE) ?? []);
|
|
4803
|
+
for (const id of allIds) {
|
|
4804
|
+
const ticket = state.ticketByID(id);
|
|
4805
|
+
if (ticket && ticket.status !== "complete" && ticket.status !== "inprogress") {
|
|
4806
|
+
actionableIds.add(id);
|
|
4807
|
+
}
|
|
4808
|
+
}
|
|
4809
|
+
}
|
|
4810
|
+
for (const id of actionableIds) {
|
|
4811
|
+
const ticket = state.ticketByID(id);
|
|
4812
|
+
if (!ticket || ticket.status === "complete") continue;
|
|
4813
|
+
const existing = dedup.get(id);
|
|
4814
|
+
if (existing) {
|
|
4815
|
+
dedup.set(id, {
|
|
4816
|
+
...existing,
|
|
4817
|
+
score: existing.score + HANDOVER_BOOST,
|
|
4818
|
+
reason: existing.reason + " (handover context)"
|
|
4819
|
+
});
|
|
4820
|
+
} else {
|
|
4821
|
+
dedup.set(id, {
|
|
4822
|
+
id,
|
|
4823
|
+
kind: "ticket",
|
|
4824
|
+
title: ticket.title,
|
|
4825
|
+
category: "handover_context",
|
|
4826
|
+
reason: "Referenced in latest handover",
|
|
4827
|
+
score: HANDOVER_BASE_SCORE
|
|
4828
|
+
});
|
|
4829
|
+
}
|
|
4830
|
+
}
|
|
4831
|
+
}
|
|
4832
|
+
function extractTicketIdsFromActionableSections(content) {
|
|
4833
|
+
const ids = /* @__PURE__ */ new Set();
|
|
4834
|
+
const lines = content.split("\n");
|
|
4835
|
+
let inActionable = false;
|
|
4836
|
+
for (const line of lines) {
|
|
4837
|
+
if (/^#+\s/.test(line)) {
|
|
4838
|
+
inActionable = ACTIONABLE_HEADING_RE.test(line);
|
|
4839
|
+
}
|
|
4840
|
+
if (inActionable) {
|
|
4841
|
+
const matches = line.match(TICKET_ID_RE);
|
|
4842
|
+
if (matches) for (const m of matches) ids.add(m);
|
|
4843
|
+
}
|
|
4844
|
+
}
|
|
4845
|
+
return ids;
|
|
4846
|
+
}
|
|
4847
|
+
function generateDebtTrend(state, options) {
|
|
4848
|
+
if (options?.previousOpenIssueCount == null) return [];
|
|
4849
|
+
const currentOpen = state.issues.filter((i) => i.status !== "resolved").length;
|
|
4850
|
+
const previous = options.previousOpenIssueCount;
|
|
4851
|
+
if (previous <= 0) return [];
|
|
4852
|
+
const growth = (currentOpen - previous) / previous;
|
|
4853
|
+
const absolute = currentOpen - previous;
|
|
4854
|
+
if (growth > DEBT_GROWTH_THRESHOLD && absolute >= DEBT_ABSOLUTE_MINIMUM) {
|
|
4855
|
+
return [{
|
|
4856
|
+
id: "DEBT_TREND",
|
|
4857
|
+
kind: "action",
|
|
4858
|
+
title: "Issue debt growing",
|
|
4859
|
+
category: "debt_trend",
|
|
4860
|
+
reason: `Open issues grew from ${previous} to ${currentOpen} (+${Math.round(growth * 100)}%). Consider triaging or resolving issues before adding features.`,
|
|
4861
|
+
score: DEBT_TREND_SCORE
|
|
4862
|
+
}];
|
|
4863
|
+
}
|
|
4864
|
+
return [];
|
|
4865
|
+
}
|
|
4866
|
+
var SEVERITY_RANK, PHASE_DISTANCE_PENALTY, MAX_PHASE_PENALTY, CATEGORY_PRIORITY, TICKET_ID_RE, ACTIONABLE_HEADING_RE, HANDOVER_BOOST, HANDOVER_BASE_SCORE, DEBT_TREND_SCORE, DEBT_GROWTH_THRESHOLD, DEBT_ABSOLUTE_MINIMUM;
|
|
4702
4867
|
var init_recommend = __esm({
|
|
4703
4868
|
"src/core/recommend.ts"() {
|
|
4704
4869
|
"use strict";
|
|
@@ -4720,17 +4885,52 @@ var init_recommend = __esm({
|
|
|
4720
4885
|
high_impact_unblock: 4,
|
|
4721
4886
|
near_complete_umbrella: 5,
|
|
4722
4887
|
phase_momentum: 6,
|
|
4723
|
-
|
|
4724
|
-
|
|
4888
|
+
debt_trend: 7,
|
|
4889
|
+
quick_win: 8,
|
|
4890
|
+
handover_context: 9,
|
|
4891
|
+
open_issue: 10
|
|
4725
4892
|
};
|
|
4893
|
+
TICKET_ID_RE = /\bT-\d{3}[a-z]?\b/g;
|
|
4894
|
+
ACTIONABLE_HEADING_RE = /^#+\s.*(next|open|remaining|todo|blocked)/im;
|
|
4895
|
+
HANDOVER_BOOST = 50;
|
|
4896
|
+
HANDOVER_BASE_SCORE = 350;
|
|
4897
|
+
DEBT_TREND_SCORE = 450;
|
|
4898
|
+
DEBT_GROWTH_THRESHOLD = 0.25;
|
|
4899
|
+
DEBT_ABSOLUTE_MINIMUM = 2;
|
|
4726
4900
|
}
|
|
4727
4901
|
});
|
|
4728
4902
|
|
|
4729
4903
|
// src/cli/commands/recommend.ts
|
|
4904
|
+
import { readFileSync as readFileSync2, readdirSync as readdirSync2 } from "fs";
|
|
4905
|
+
import { join as join7 } from "path";
|
|
4730
4906
|
function handleRecommend(ctx, count) {
|
|
4731
|
-
const
|
|
4907
|
+
const options = buildRecommendOptions(ctx);
|
|
4908
|
+
const result = recommend(ctx.state, count, options);
|
|
4732
4909
|
return { output: formatRecommendations(result, ctx.state, ctx.format) };
|
|
4733
4910
|
}
|
|
4911
|
+
function buildRecommendOptions(ctx) {
|
|
4912
|
+
const opts = {};
|
|
4913
|
+
try {
|
|
4914
|
+
const files = readdirSync2(ctx.handoversDir).filter((f) => f.endsWith(".md")).sort();
|
|
4915
|
+
if (files.length > 0) {
|
|
4916
|
+
opts.latestHandoverContent = readFileSync2(join7(ctx.handoversDir, files[files.length - 1]), "utf-8");
|
|
4917
|
+
}
|
|
4918
|
+
} catch {
|
|
4919
|
+
}
|
|
4920
|
+
try {
|
|
4921
|
+
const snapshotsDir = join7(ctx.root, ".story", "snapshots");
|
|
4922
|
+
const snapFiles = readdirSync2(snapshotsDir).filter((f) => f.endsWith(".json")).sort();
|
|
4923
|
+
if (snapFiles.length > 0) {
|
|
4924
|
+
const raw = readFileSync2(join7(snapshotsDir, snapFiles[snapFiles.length - 1]), "utf-8");
|
|
4925
|
+
const snap = JSON.parse(raw);
|
|
4926
|
+
if (snap.issues) {
|
|
4927
|
+
opts.previousOpenIssueCount = snap.issues.filter((i) => i.status !== "resolved").length;
|
|
4928
|
+
}
|
|
4929
|
+
}
|
|
4930
|
+
} catch {
|
|
4931
|
+
}
|
|
4932
|
+
return opts;
|
|
4933
|
+
}
|
|
4734
4934
|
var init_recommend2 = __esm({
|
|
4735
4935
|
"src/cli/commands/recommend.ts"() {
|
|
4736
4936
|
"use strict";
|
|
@@ -5066,6 +5266,7 @@ var init_session_types = __esm({
|
|
|
5066
5266
|
"FINALIZE",
|
|
5067
5267
|
"COMPACT",
|
|
5068
5268
|
"LESSON_CAPTURE",
|
|
5269
|
+
"ISSUE_FIX",
|
|
5069
5270
|
"ISSUE_SWEEP"
|
|
5070
5271
|
]);
|
|
5071
5272
|
IDLE_STATES = /* @__PURE__ */ new Set([
|
|
@@ -5094,6 +5295,7 @@ var init_session_types = __esm({
|
|
|
5094
5295
|
"HANDOVER",
|
|
5095
5296
|
"COMPLETE",
|
|
5096
5297
|
"LESSON_CAPTURE",
|
|
5298
|
+
"ISSUE_FIX",
|
|
5097
5299
|
"ISSUE_SWEEP",
|
|
5098
5300
|
"SESSION_END"
|
|
5099
5301
|
];
|
|
@@ -5142,6 +5344,14 @@ var init_session_types = __esm({
|
|
|
5142
5344
|
timestamp: z9.string()
|
|
5143
5345
|
})).default([])
|
|
5144
5346
|
}).default({ plan: [], code: [] }),
|
|
5347
|
+
// T-153: Current issue being fixed (null when working on a ticket)
|
|
5348
|
+
currentIssue: z9.object({
|
|
5349
|
+
id: z9.string(),
|
|
5350
|
+
title: z9.string(),
|
|
5351
|
+
severity: z9.string()
|
|
5352
|
+
}).nullable().default(null),
|
|
5353
|
+
// T-153: Issues resolved this session
|
|
5354
|
+
resolvedIssues: z9.array(z9.string()).default([]),
|
|
5145
5355
|
// Completed tickets this session
|
|
5146
5356
|
completedTickets: z9.array(z9.object({
|
|
5147
5357
|
id: z9.string(),
|
|
@@ -5280,27 +5490,27 @@ __export(session_exports, {
|
|
|
5280
5490
|
import { randomUUID } from "crypto";
|
|
5281
5491
|
import {
|
|
5282
5492
|
mkdirSync,
|
|
5283
|
-
readdirSync,
|
|
5284
|
-
readFileSync,
|
|
5493
|
+
readdirSync as readdirSync3,
|
|
5494
|
+
readFileSync as readFileSync3,
|
|
5285
5495
|
writeFileSync,
|
|
5286
5496
|
renameSync,
|
|
5287
5497
|
unlinkSync,
|
|
5288
5498
|
existsSync as existsSync6,
|
|
5289
5499
|
rmSync
|
|
5290
5500
|
} from "fs";
|
|
5291
|
-
import { join as
|
|
5501
|
+
import { join as join8 } from "path";
|
|
5292
5502
|
import lockfile2 from "proper-lockfile";
|
|
5293
5503
|
function sessionsRoot(root) {
|
|
5294
|
-
return
|
|
5504
|
+
return join8(root, ".story", SESSIONS_DIR);
|
|
5295
5505
|
}
|
|
5296
5506
|
function sessionDir(root, sessionId) {
|
|
5297
|
-
return
|
|
5507
|
+
return join8(sessionsRoot(root), sessionId);
|
|
5298
5508
|
}
|
|
5299
5509
|
function statePath(dir) {
|
|
5300
|
-
return
|
|
5510
|
+
return join8(dir, "state.json");
|
|
5301
5511
|
}
|
|
5302
5512
|
function eventsPath(dir) {
|
|
5303
|
-
return
|
|
5513
|
+
return join8(dir, "events.log");
|
|
5304
5514
|
}
|
|
5305
5515
|
function createSession(root, recipe, workspaceId, configOverrides) {
|
|
5306
5516
|
const id = randomUUID();
|
|
@@ -5356,7 +5566,7 @@ function readSession(dir) {
|
|
|
5356
5566
|
const path2 = statePath(dir);
|
|
5357
5567
|
let raw;
|
|
5358
5568
|
try {
|
|
5359
|
-
raw =
|
|
5569
|
+
raw = readFileSync3(path2, "utf-8");
|
|
5360
5570
|
} catch {
|
|
5361
5571
|
return null;
|
|
5362
5572
|
}
|
|
@@ -5399,7 +5609,7 @@ function readEvents(dir) {
|
|
|
5399
5609
|
const path2 = eventsPath(dir);
|
|
5400
5610
|
let raw;
|
|
5401
5611
|
try {
|
|
5402
|
-
raw =
|
|
5612
|
+
raw = readFileSync3(path2, "utf-8");
|
|
5403
5613
|
} catch {
|
|
5404
5614
|
return { events: [], malformedCount: 0 };
|
|
5405
5615
|
}
|
|
@@ -5456,7 +5666,7 @@ function findActiveSessionFull(root) {
|
|
|
5456
5666
|
const sessDir = sessionsRoot(root);
|
|
5457
5667
|
let entries;
|
|
5458
5668
|
try {
|
|
5459
|
-
entries =
|
|
5669
|
+
entries = readdirSync3(sessDir, { withFileTypes: true });
|
|
5460
5670
|
} catch {
|
|
5461
5671
|
return null;
|
|
5462
5672
|
}
|
|
@@ -5470,7 +5680,7 @@ function findActiveSessionFull(root) {
|
|
|
5470
5680
|
let bestGuideCall = 0;
|
|
5471
5681
|
for (const entry of entries) {
|
|
5472
5682
|
if (!entry.isDirectory()) continue;
|
|
5473
|
-
const dir =
|
|
5683
|
+
const dir = join8(sessDir, entry.name);
|
|
5474
5684
|
const session = readSession(dir);
|
|
5475
5685
|
if (!session) continue;
|
|
5476
5686
|
if (session.status !== "active") continue;
|
|
@@ -5493,7 +5703,7 @@ function findStaleSessions(root) {
|
|
|
5493
5703
|
const sessDir = sessionsRoot(root);
|
|
5494
5704
|
let entries;
|
|
5495
5705
|
try {
|
|
5496
|
-
entries =
|
|
5706
|
+
entries = readdirSync3(sessDir, { withFileTypes: true });
|
|
5497
5707
|
} catch {
|
|
5498
5708
|
return [];
|
|
5499
5709
|
}
|
|
@@ -5506,7 +5716,7 @@ function findStaleSessions(root) {
|
|
|
5506
5716
|
const results = [];
|
|
5507
5717
|
for (const entry of entries) {
|
|
5508
5718
|
if (!entry.isDirectory()) continue;
|
|
5509
|
-
const dir =
|
|
5719
|
+
const dir = join8(sessDir, entry.name);
|
|
5510
5720
|
const session = readSession(dir);
|
|
5511
5721
|
if (!session) continue;
|
|
5512
5722
|
if (session.status !== "active") continue;
|
|
@@ -5562,7 +5772,7 @@ function findResumableSession(root) {
|
|
|
5562
5772
|
const sessDir = sessionsRoot(root);
|
|
5563
5773
|
let entries;
|
|
5564
5774
|
try {
|
|
5565
|
-
entries =
|
|
5775
|
+
entries = readdirSync3(sessDir, { withFileTypes: true });
|
|
5566
5776
|
} catch {
|
|
5567
5777
|
return null;
|
|
5568
5778
|
}
|
|
@@ -5577,7 +5787,7 @@ function findResumableSession(root) {
|
|
|
5577
5787
|
let bestPreparedAt = 0;
|
|
5578
5788
|
for (const entry of entries) {
|
|
5579
5789
|
if (!entry.isDirectory()) continue;
|
|
5580
|
-
const dir =
|
|
5790
|
+
const dir = join8(sessDir, entry.name);
|
|
5581
5791
|
const session = readSession(dir);
|
|
5582
5792
|
if (!session) continue;
|
|
5583
5793
|
if (session.status !== "active") continue;
|
|
@@ -5601,7 +5811,7 @@ async function withSessionLock(root, fn) {
|
|
|
5601
5811
|
release = await lockfile2.lock(sessDir, {
|
|
5602
5812
|
retries: { retries: 3, minTimeout: 100, maxTimeout: 1e3 },
|
|
5603
5813
|
stale: 3e4,
|
|
5604
|
-
lockfilePath:
|
|
5814
|
+
lockfilePath: join8(sessDir, ".lock")
|
|
5605
5815
|
});
|
|
5606
5816
|
return await fn();
|
|
5607
5817
|
} finally {
|
|
@@ -5645,7 +5855,7 @@ var init_state_machine = __esm({
|
|
|
5645
5855
|
// start does INIT + LOAD_CONTEXT internally
|
|
5646
5856
|
LOAD_CONTEXT: ["PICK_TICKET"],
|
|
5647
5857
|
// internal (never seen by Claude)
|
|
5648
|
-
PICK_TICKET: ["PLAN", "SESSION_END"],
|
|
5858
|
+
PICK_TICKET: ["PLAN", "ISSUE_FIX", "SESSION_END"],
|
|
5649
5859
|
PLAN: ["PLAN_REVIEW"],
|
|
5650
5860
|
PLAN_REVIEW: ["IMPLEMENT", "WRITE_TESTS", "PLAN", "PLAN_REVIEW", "SESSION_END"],
|
|
5651
5861
|
// approve → IMPLEMENT/WRITE_TESTS, reject → PLAN, stay for next round; SESSION_END for tiered exit
|
|
@@ -5659,8 +5869,11 @@ var init_state_machine = __esm({
|
|
|
5659
5869
|
// approve → VERIFY/FINALIZE, reject → IMPLEMENT/PLAN, stay for next round; SESSION_END for tiered exit
|
|
5660
5870
|
VERIFY: ["FINALIZE", "IMPLEMENT", "VERIFY"],
|
|
5661
5871
|
// pass → FINALIZE, fail → IMPLEMENT, retry
|
|
5662
|
-
FINALIZE: ["COMPLETE"],
|
|
5872
|
+
FINALIZE: ["COMPLETE", "PICK_TICKET"],
|
|
5873
|
+
// PICK_TICKET for issue-fix flow (bypass COMPLETE)
|
|
5663
5874
|
COMPLETE: ["PICK_TICKET", "HANDOVER", "ISSUE_SWEEP", "SESSION_END"],
|
|
5875
|
+
ISSUE_FIX: ["FINALIZE", "PICK_TICKET", "ISSUE_FIX"],
|
|
5876
|
+
// T-153: fix done → FINALIZE, cancel → PICK_TICKET, retry self
|
|
5664
5877
|
LESSON_CAPTURE: ["ISSUE_SWEEP", "HANDOVER", "LESSON_CAPTURE"],
|
|
5665
5878
|
// advance → ISSUE_SWEEP, retry self, done → HANDOVER
|
|
5666
5879
|
ISSUE_SWEEP: ["ISSUE_SWEEP", "HANDOVER", "PICK_TICKET"],
|
|
@@ -5917,16 +6130,16 @@ var init_git_inspector = __esm({
|
|
|
5917
6130
|
});
|
|
5918
6131
|
|
|
5919
6132
|
// src/autonomous/recipes/loader.ts
|
|
5920
|
-
import { readFileSync as
|
|
5921
|
-
import { join as
|
|
6133
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
6134
|
+
import { join as join9, dirname as dirname3 } from "path";
|
|
5922
6135
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5923
6136
|
function loadRecipe(recipeName) {
|
|
5924
6137
|
if (!/^[A-Za-z0-9_-]+$/.test(recipeName)) {
|
|
5925
6138
|
throw new Error(`Invalid recipe name: ${recipeName}`);
|
|
5926
6139
|
}
|
|
5927
|
-
const recipesDir =
|
|
5928
|
-
const path2 =
|
|
5929
|
-
const raw =
|
|
6140
|
+
const recipesDir = join9(dirname3(fileURLToPath2(import.meta.url)), "..", "recipes");
|
|
6141
|
+
const path2 = join9(recipesDir, `${recipeName}.json`);
|
|
6142
|
+
const raw = readFileSync4(path2, "utf-8");
|
|
5930
6143
|
return JSON.parse(raw);
|
|
5931
6144
|
}
|
|
5932
6145
|
function resolveRecipe(recipeName, projectOverrides) {
|
|
@@ -6202,7 +6415,7 @@ var init_types2 = __esm({
|
|
|
6202
6415
|
|
|
6203
6416
|
// src/autonomous/stages/pick-ticket.ts
|
|
6204
6417
|
import { existsSync as existsSync7, unlinkSync as unlinkSync2 } from "fs";
|
|
6205
|
-
import { join as
|
|
6418
|
+
import { join as join10 } from "path";
|
|
6206
6419
|
var PickTicketStage;
|
|
6207
6420
|
var init_pick_ticket = __esm({
|
|
6208
6421
|
"src/autonomous/stages/pick-ticket.ts"() {
|
|
@@ -6220,28 +6433,52 @@ var init_pick_ticket = __esm({
|
|
|
6220
6433
|
(c, i) => `${i + 1}. **${c.ticket.id}: ${c.ticket.title}** (${c.ticket.type})`
|
|
6221
6434
|
).join("\n");
|
|
6222
6435
|
}
|
|
6436
|
+
const highIssues = projectState.issues.filter(
|
|
6437
|
+
(i) => i.status === "open" && (i.severity === "critical" || i.severity === "high")
|
|
6438
|
+
);
|
|
6439
|
+
let issuesText = "";
|
|
6440
|
+
if (highIssues.length > 0) {
|
|
6441
|
+
issuesText = "\n\n## Open Issues (high+ severity)\n\n" + highIssues.map(
|
|
6442
|
+
(i, idx) => `${idx + 1}. **${i.id}: ${i.title}** (${i.severity})`
|
|
6443
|
+
).join("\n");
|
|
6444
|
+
}
|
|
6223
6445
|
const topCandidate = candidates.kind === "found" ? candidates.candidates[0] : null;
|
|
6446
|
+
const hasIssues = highIssues.length > 0;
|
|
6224
6447
|
return {
|
|
6225
6448
|
instruction: [
|
|
6226
|
-
"# Pick a Ticket",
|
|
6449
|
+
"# Pick a Ticket or Issue",
|
|
6450
|
+
"",
|
|
6451
|
+
"## Ticket Candidates",
|
|
6227
6452
|
"",
|
|
6228
6453
|
candidatesText || "No ticket candidates found.",
|
|
6454
|
+
issuesText,
|
|
6229
6455
|
"",
|
|
6230
|
-
topCandidate ? `Pick **${topCandidate.ticket.id}** (highest priority) by calling \`claudestory_autonomous_guide\` now:` : "Pick a ticket by calling `claudestory_autonomous_guide` now:",
|
|
6456
|
+
topCandidate ? `Pick **${topCandidate.ticket.id}** (highest priority) or an open issue by calling \`claudestory_autonomous_guide\` now:` : hasIssues ? `Pick an issue to fix by calling \`claudestory_autonomous_guide\` now:` : "Pick a ticket by calling `claudestory_autonomous_guide` now:",
|
|
6231
6457
|
"```json",
|
|
6232
6458
|
topCandidate ? `{ "sessionId": "${ctx.state.sessionId}", "action": "report", "report": { "completedAction": "ticket_picked", "ticketId": "${topCandidate.ticket.id}" } }` : `{ "sessionId": "${ctx.state.sessionId}", "action": "report", "report": { "completedAction": "ticket_picked", "ticketId": "T-XXX" } }`,
|
|
6233
|
-
"```"
|
|
6459
|
+
"```",
|
|
6460
|
+
...hasIssues ? [
|
|
6461
|
+
"",
|
|
6462
|
+
"Or to fix an issue:",
|
|
6463
|
+
"```json",
|
|
6464
|
+
`{ "sessionId": "${ctx.state.sessionId}", "action": "report", "report": { "completedAction": "issue_picked", "issueId": "${highIssues[0].id}" } }`,
|
|
6465
|
+
"```"
|
|
6466
|
+
] : []
|
|
6234
6467
|
].join("\n"),
|
|
6235
6468
|
reminders: [
|
|
6236
|
-
"Do NOT stop or summarize. Call autonomous_guide IMMEDIATELY to pick a ticket.",
|
|
6469
|
+
"Do NOT stop or summarize. Call autonomous_guide IMMEDIATELY to pick a ticket or issue.",
|
|
6237
6470
|
"Do NOT ask the user for confirmation."
|
|
6238
6471
|
]
|
|
6239
6472
|
};
|
|
6240
6473
|
}
|
|
6241
6474
|
async report(ctx, report) {
|
|
6475
|
+
const issueId = report.issueId;
|
|
6476
|
+
if (issueId) {
|
|
6477
|
+
return this.handleIssuePick(ctx, issueId);
|
|
6478
|
+
}
|
|
6242
6479
|
const ticketId = report.ticketId;
|
|
6243
6480
|
if (!ticketId) {
|
|
6244
|
-
return { action: "retry", instruction: "report.ticketId is required
|
|
6481
|
+
return { action: "retry", instruction: "report.ticketId or report.issueId is required." };
|
|
6245
6482
|
}
|
|
6246
6483
|
const { state: projectState } = await ctx.loadProject();
|
|
6247
6484
|
const ticket = projectState.ticketByID(ticketId);
|
|
@@ -6257,7 +6494,7 @@ var init_pick_ticket = __esm({
|
|
|
6257
6494
|
return { action: "retry", instruction: `Ticket ${ticketId} is ${ticket.status} \u2014 pick an open ticket.` };
|
|
6258
6495
|
}
|
|
6259
6496
|
}
|
|
6260
|
-
const planPath =
|
|
6497
|
+
const planPath = join10(ctx.dir, "plan.md");
|
|
6261
6498
|
try {
|
|
6262
6499
|
if (existsSync7(planPath)) unlinkSync2(planPath);
|
|
6263
6500
|
} catch {
|
|
@@ -6292,16 +6529,34 @@ ${ticket.description}` : "",
|
|
|
6292
6529
|
}
|
|
6293
6530
|
};
|
|
6294
6531
|
}
|
|
6532
|
+
// T-153: Handle issue pick -- validate and route to ISSUE_FIX
|
|
6533
|
+
async handleIssuePick(ctx, issueId) {
|
|
6534
|
+
const { state: projectState } = await ctx.loadProject();
|
|
6535
|
+
const issue = projectState.issues.find((i) => i.id === issueId);
|
|
6536
|
+
if (!issue) {
|
|
6537
|
+
return { action: "retry", instruction: `Issue ${issueId} not found. Pick a valid issue or ticket.` };
|
|
6538
|
+
}
|
|
6539
|
+
if (issue.status !== "open") {
|
|
6540
|
+
return { action: "retry", instruction: `Issue ${issueId} is ${issue.status}. Pick an open issue.` };
|
|
6541
|
+
}
|
|
6542
|
+
ctx.updateDraft({
|
|
6543
|
+
currentIssue: { id: issue.id, title: issue.title, severity: issue.severity },
|
|
6544
|
+
ticket: void 0,
|
|
6545
|
+
reviews: { plan: [], code: [] },
|
|
6546
|
+
finalizeCheckpoint: null
|
|
6547
|
+
});
|
|
6548
|
+
return { action: "goto", target: "ISSUE_FIX" };
|
|
6549
|
+
}
|
|
6295
6550
|
};
|
|
6296
6551
|
}
|
|
6297
6552
|
});
|
|
6298
6553
|
|
|
6299
6554
|
// src/autonomous/stages/plan.ts
|
|
6300
|
-
import { existsSync as existsSync8, readFileSync as
|
|
6301
|
-
import { join as
|
|
6555
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5 } from "fs";
|
|
6556
|
+
import { join as join11 } from "path";
|
|
6302
6557
|
function readFileSafe(path2) {
|
|
6303
6558
|
try {
|
|
6304
|
-
return
|
|
6559
|
+
return readFileSync5(path2, "utf-8");
|
|
6305
6560
|
} catch {
|
|
6306
6561
|
return "";
|
|
6307
6562
|
}
|
|
@@ -6342,7 +6597,7 @@ var init_plan = __esm({
|
|
|
6342
6597
|
};
|
|
6343
6598
|
}
|
|
6344
6599
|
async report(ctx, _report) {
|
|
6345
|
-
const planPath =
|
|
6600
|
+
const planPath = join11(ctx.dir, "plan.md");
|
|
6346
6601
|
if (!existsSync8(planPath)) {
|
|
6347
6602
|
return { action: "retry", instruction: `Plan file not found at ${planPath}. Write your plan there and call me again.`, reminders: ["Save plan to .story/sessions/<id>/plan.md"] };
|
|
6348
6603
|
}
|
|
@@ -6588,7 +6843,8 @@ var init_implement = __esm({
|
|
|
6588
6843
|
reminders: [
|
|
6589
6844
|
"Follow the plan exactly. Do NOT deviate without re-planning.",
|
|
6590
6845
|
"Do NOT ask the user for confirmation.",
|
|
6591
|
-
"If you discover pre-existing bugs, failing tests not caused by your changes, or other out-of-scope problems, file them as issues using claudestory_issue_create. Do not fix them inline."
|
|
6846
|
+
"If you discover pre-existing bugs, failing tests not caused by your changes, or other out-of-scope problems, file them as issues using claudestory_issue_create. Do not fix them inline.",
|
|
6847
|
+
"Track which files you create or modify. Only these files should be staged at commit time."
|
|
6592
6848
|
],
|
|
6593
6849
|
transitionedFrom: ctx.state.previousState ?? void 0
|
|
6594
6850
|
};
|
|
@@ -7285,10 +7541,13 @@ var init_finalize = __esm({
|
|
|
7285
7541
|
"Code review passed. Time to commit.",
|
|
7286
7542
|
"",
|
|
7287
7543
|
ctx.state.ticket ? `1. Update ticket ${ctx.state.ticket.id} status to "complete" in .story/` : "",
|
|
7288
|
-
|
|
7544
|
+
ctx.state.currentIssue ? `1. Ensure issue ${ctx.state.currentIssue.id} status is "resolved" in .story/issues/` : "",
|
|
7545
|
+
"2. Stage only the files you created or modified for this work (code + .story/ changes). Do NOT use `git add -A` or `git add .`",
|
|
7289
7546
|
'3. Call me with completedAction: "files_staged"'
|
|
7290
7547
|
].filter(Boolean).join("\n"),
|
|
7291
|
-
reminders: [
|
|
7548
|
+
reminders: [
|
|
7549
|
+
ctx.state.currentIssue ? "Stage both code changes and .story/ issue update in the same commit. Only stage files related to this fix." : "Stage both code changes and .story/ ticket update in the same commit. Only stage files related to this ticket."
|
|
7550
|
+
],
|
|
7292
7551
|
transitionedFrom: ctx.state.previousState ?? void 0
|
|
7293
7552
|
};
|
|
7294
7553
|
}
|
|
@@ -7331,9 +7590,9 @@ var init_finalize = __esm({
|
|
|
7331
7590
|
const headResult = await gitHead(ctx.root);
|
|
7332
7591
|
const previousHead = ctx.state.git.expectedHead ?? ctx.state.git.initHead;
|
|
7333
7592
|
if (headResult.ok && previousHead && headResult.data.hash !== previousHead) {
|
|
7593
|
+
const treeResult = await gitDiffTreeNames(ctx.root, headResult.data.hash);
|
|
7334
7594
|
const ticketId2 = ctx.state.ticket?.id;
|
|
7335
7595
|
if (ticketId2) {
|
|
7336
|
-
const treeResult = await gitDiffTreeNames(ctx.root, headResult.data.hash);
|
|
7337
7596
|
const ticketPath = `.story/tickets/${ticketId2}.json`;
|
|
7338
7597
|
if (treeResult.ok && !treeResult.data.includes(ticketPath)) {
|
|
7339
7598
|
return {
|
|
@@ -7342,6 +7601,16 @@ var init_finalize = __esm({
|
|
|
7342
7601
|
};
|
|
7343
7602
|
}
|
|
7344
7603
|
}
|
|
7604
|
+
const earlyIssueId = ctx.state.currentIssue?.id;
|
|
7605
|
+
if (earlyIssueId) {
|
|
7606
|
+
const issuePath = `.story/issues/${earlyIssueId}.json`;
|
|
7607
|
+
if (treeResult.ok && !treeResult.data.includes(issuePath)) {
|
|
7608
|
+
return {
|
|
7609
|
+
action: "retry",
|
|
7610
|
+
instruction: `Commit detected (${headResult.data.hash.slice(0, 7)}) but issue file ${issuePath} is not in the commit. Amend the commit to include it: \`git add ${issuePath} && git commit --amend --no-edit\`, then report completedAction: "commit_done" with the new hash.`
|
|
7611
|
+
};
|
|
7612
|
+
}
|
|
7613
|
+
}
|
|
7345
7614
|
ctx.writeState({ finalizeCheckpoint: "precommit_passed" });
|
|
7346
7615
|
return this.handleCommit(ctx, { ...report, commitHash: headResult.data.hash });
|
|
7347
7616
|
}
|
|
@@ -7375,6 +7644,16 @@ var init_finalize = __esm({
|
|
|
7375
7644
|
};
|
|
7376
7645
|
}
|
|
7377
7646
|
}
|
|
7647
|
+
const issueId = ctx.state.currentIssue?.id;
|
|
7648
|
+
if (issueId) {
|
|
7649
|
+
const issuePath = `.story/issues/${issueId}.json`;
|
|
7650
|
+
if (!stagedResult.data.includes(issuePath)) {
|
|
7651
|
+
return {
|
|
7652
|
+
action: "retry",
|
|
7653
|
+
instruction: `Issue file ${issuePath} is not staged. Run \`git add ${issuePath}\` and call me again with completedAction: "files_staged".`
|
|
7654
|
+
};
|
|
7655
|
+
}
|
|
7656
|
+
}
|
|
7378
7657
|
ctx.writeState({
|
|
7379
7658
|
finalizeCheckpoint: overlapOverridden ? "staged_override" : "staged"
|
|
7380
7659
|
});
|
|
@@ -7422,6 +7701,16 @@ var init_finalize = __esm({
|
|
|
7422
7701
|
};
|
|
7423
7702
|
}
|
|
7424
7703
|
}
|
|
7704
|
+
const precommitIssueId = ctx.state.currentIssue?.id;
|
|
7705
|
+
if (precommitIssueId) {
|
|
7706
|
+
const issuePath = `.story/issues/${precommitIssueId}.json`;
|
|
7707
|
+
if (!stagedResult.data.includes(issuePath)) {
|
|
7708
|
+
return {
|
|
7709
|
+
action: "retry",
|
|
7710
|
+
instruction: `Pre-commit hooks may have modified the staged set. Issue file ${issuePath} is no longer staged. Run \`git add ${issuePath}\` and call me again with completedAction: "files_staged".`
|
|
7711
|
+
};
|
|
7712
|
+
}
|
|
7713
|
+
}
|
|
7425
7714
|
ctx.writeState({ finalizeCheckpoint: "precommit_passed" });
|
|
7426
7715
|
return {
|
|
7427
7716
|
action: "retry",
|
|
@@ -7459,6 +7748,21 @@ var init_finalize = __esm({
|
|
|
7459
7748
|
if (previousHead && normalizedHash === previousHead) {
|
|
7460
7749
|
return { action: "retry", instruction: `No new commit detected: HEAD (${normalizedHash}) has not changed. Create a commit first, then report the new hash.` };
|
|
7461
7750
|
}
|
|
7751
|
+
const currentIssue = ctx.state.currentIssue;
|
|
7752
|
+
if (currentIssue) {
|
|
7753
|
+
ctx.writeState({
|
|
7754
|
+
finalizeCheckpoint: "committed",
|
|
7755
|
+
resolvedIssues: [...ctx.state.resolvedIssues ?? [], currentIssue.id],
|
|
7756
|
+
currentIssue: null,
|
|
7757
|
+
git: {
|
|
7758
|
+
...ctx.state.git,
|
|
7759
|
+
mergeBase: normalizedHash,
|
|
7760
|
+
expectedHead: normalizedHash
|
|
7761
|
+
}
|
|
7762
|
+
});
|
|
7763
|
+
ctx.appendEvent("commit", { commitHash: normalizedHash, issueId: currentIssue.id });
|
|
7764
|
+
return { action: "goto", target: "PICK_TICKET" };
|
|
7765
|
+
}
|
|
7462
7766
|
const completedTicket = ctx.state.ticket ? { id: ctx.state.ticket.id, title: ctx.state.ticket.title, commitHash: normalizedHash, risk: ctx.state.ticket.risk, realizedRisk: ctx.state.ticket.realizedRisk } : void 0;
|
|
7463
7767
|
ctx.writeState({
|
|
7464
7768
|
finalizeCheckpoint: "committed",
|
|
@@ -7683,6 +7987,85 @@ var init_lesson_capture = __esm({
|
|
|
7683
7987
|
}
|
|
7684
7988
|
});
|
|
7685
7989
|
|
|
7990
|
+
// src/autonomous/stages/issue-fix.ts
|
|
7991
|
+
var IssueFixStage;
|
|
7992
|
+
var init_issue_fix = __esm({
|
|
7993
|
+
"src/autonomous/stages/issue-fix.ts"() {
|
|
7994
|
+
"use strict";
|
|
7995
|
+
init_esm_shims();
|
|
7996
|
+
IssueFixStage = class {
|
|
7997
|
+
id = "ISSUE_FIX";
|
|
7998
|
+
async enter(ctx) {
|
|
7999
|
+
const issue = ctx.state.currentIssue;
|
|
8000
|
+
if (!issue) {
|
|
8001
|
+
return { action: "goto", target: "PICK_TICKET" };
|
|
8002
|
+
}
|
|
8003
|
+
const { state: projectState } = await ctx.loadProject();
|
|
8004
|
+
const fullIssue = projectState.issues.find((i) => i.id === issue.id);
|
|
8005
|
+
const details = fullIssue ? [
|
|
8006
|
+
`**${fullIssue.id}**: ${fullIssue.title}`,
|
|
8007
|
+
"",
|
|
8008
|
+
`Severity: ${fullIssue.severity}`,
|
|
8009
|
+
fullIssue.impact ? `Impact: ${fullIssue.impact}` : "",
|
|
8010
|
+
fullIssue.components.length > 0 ? `Components: ${fullIssue.components.join(", ")}` : "",
|
|
8011
|
+
fullIssue.location.length > 0 ? `Location: ${fullIssue.location.join(", ")}` : ""
|
|
8012
|
+
].filter(Boolean).join("\n") : `**${issue.id}**: ${issue.title} (severity: ${issue.severity})`;
|
|
8013
|
+
return {
|
|
8014
|
+
instruction: [
|
|
8015
|
+
"# Fix Issue",
|
|
8016
|
+
"",
|
|
8017
|
+
details,
|
|
8018
|
+
"",
|
|
8019
|
+
'Fix this issue, then update its status to "resolved" in `.story/issues/`.',
|
|
8020
|
+
"Add a resolution description explaining the fix.",
|
|
8021
|
+
"",
|
|
8022
|
+
"When done, call `claudestory_autonomous_guide` with:",
|
|
8023
|
+
"```json",
|
|
8024
|
+
`{ "sessionId": "${ctx.state.sessionId}", "action": "report", "report": { "completedAction": "issue_fixed" } }`,
|
|
8025
|
+
"```"
|
|
8026
|
+
].join("\n"),
|
|
8027
|
+
reminders: [
|
|
8028
|
+
'Update the issue JSON: set status to "resolved", add resolution text, set resolvedDate.',
|
|
8029
|
+
"Do NOT ask the user for confirmation."
|
|
8030
|
+
]
|
|
8031
|
+
};
|
|
8032
|
+
}
|
|
8033
|
+
async report(ctx, _report) {
|
|
8034
|
+
const issue = ctx.state.currentIssue;
|
|
8035
|
+
if (!issue) {
|
|
8036
|
+
return { action: "goto", target: "PICK_TICKET" };
|
|
8037
|
+
}
|
|
8038
|
+
const { state: projectState } = await ctx.loadProject();
|
|
8039
|
+
const current = projectState.issues.find((i) => i.id === issue.id);
|
|
8040
|
+
if (!current || current.status !== "resolved") {
|
|
8041
|
+
return {
|
|
8042
|
+
action: "retry",
|
|
8043
|
+
instruction: `Issue ${issue.id} is still ${current?.status ?? "missing"}. Update its status to "resolved" in .story/issues/${issue.id}.json with a resolution description and resolvedDate, then report again.`,
|
|
8044
|
+
reminders: ["Set status to 'resolved', add resolution text, set resolvedDate."]
|
|
8045
|
+
};
|
|
8046
|
+
}
|
|
8047
|
+
return {
|
|
8048
|
+
action: "goto",
|
|
8049
|
+
target: "FINALIZE",
|
|
8050
|
+
result: {
|
|
8051
|
+
instruction: [
|
|
8052
|
+
"# Finalize Issue Fix",
|
|
8053
|
+
"",
|
|
8054
|
+
`Issue ${issue.id} resolved. Time to commit.`,
|
|
8055
|
+
"",
|
|
8056
|
+
`1. Ensure .story/issues/${issue.id}.json is updated with status: "resolved"`,
|
|
8057
|
+
"2. Stage only the files you modified for this fix (code + .story/ changes). Do NOT use `git add -A` or `git add .`",
|
|
8058
|
+
'3. Call me with completedAction: "files_staged"'
|
|
8059
|
+
].join("\n"),
|
|
8060
|
+
reminders: ["Stage both code changes and .story/ issue update in the same commit. Only stage files related to this fix."],
|
|
8061
|
+
transitionedFrom: "ISSUE_FIX"
|
|
8062
|
+
}
|
|
8063
|
+
};
|
|
8064
|
+
}
|
|
8065
|
+
};
|
|
8066
|
+
}
|
|
8067
|
+
});
|
|
8068
|
+
|
|
7686
8069
|
// src/autonomous/stages/issue-sweep.ts
|
|
7687
8070
|
var IssueSweepStage;
|
|
7688
8071
|
var init_issue_sweep = __esm({
|
|
@@ -7792,7 +8175,7 @@ Impact: ${nextIssue.impact}` : ""}` : `Fix issue ${next}.`,
|
|
|
7792
8175
|
|
|
7793
8176
|
// src/autonomous/stages/handover.ts
|
|
7794
8177
|
import { writeFileSync as writeFileSync2 } from "fs";
|
|
7795
|
-
import { join as
|
|
8178
|
+
import { join as join12 } from "path";
|
|
7796
8179
|
var HandoverStage;
|
|
7797
8180
|
var init_handover2 = __esm({
|
|
7798
8181
|
"src/autonomous/stages/handover.ts"() {
|
|
@@ -7829,7 +8212,7 @@ var init_handover2 = __esm({
|
|
|
7829
8212
|
} catch {
|
|
7830
8213
|
handoverFailed = true;
|
|
7831
8214
|
try {
|
|
7832
|
-
const fallbackPath =
|
|
8215
|
+
const fallbackPath = join12(ctx.dir, "handover-fallback.md");
|
|
7833
8216
|
writeFileSync2(fallbackPath, content, "utf-8");
|
|
7834
8217
|
} catch {
|
|
7835
8218
|
}
|
|
@@ -7893,6 +8276,7 @@ var init_stages = __esm({
|
|
|
7893
8276
|
init_finalize();
|
|
7894
8277
|
init_complete();
|
|
7895
8278
|
init_lesson_capture();
|
|
8279
|
+
init_issue_fix();
|
|
7896
8280
|
init_issue_sweep();
|
|
7897
8281
|
init_handover2();
|
|
7898
8282
|
registerStage(new PickTicketStage());
|
|
@@ -7907,14 +8291,39 @@ var init_stages = __esm({
|
|
|
7907
8291
|
registerStage(new FinalizeStage());
|
|
7908
8292
|
registerStage(new CompleteStage());
|
|
7909
8293
|
registerStage(new LessonCaptureStage());
|
|
8294
|
+
registerStage(new IssueFixStage());
|
|
7910
8295
|
registerStage(new IssueSweepStage());
|
|
7911
8296
|
registerStage(new HandoverStage());
|
|
7912
8297
|
}
|
|
7913
8298
|
});
|
|
7914
8299
|
|
|
7915
8300
|
// src/autonomous/guide.ts
|
|
7916
|
-
import { readFileSync as
|
|
7917
|
-
import { join as
|
|
8301
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, readdirSync as readdirSync4 } from "fs";
|
|
8302
|
+
import { join as join13 } from "path";
|
|
8303
|
+
function buildGuideRecommendOptions(root) {
|
|
8304
|
+
const opts = {};
|
|
8305
|
+
try {
|
|
8306
|
+
const handoversDir = join13(root, ".story", "handovers");
|
|
8307
|
+
const files = readdirSync4(handoversDir, "utf-8").filter((f) => f.endsWith(".md")).sort();
|
|
8308
|
+
if (files.length > 0) {
|
|
8309
|
+
opts.latestHandoverContent = readFileSync6(join13(handoversDir, files[files.length - 1]), "utf-8");
|
|
8310
|
+
}
|
|
8311
|
+
} catch {
|
|
8312
|
+
}
|
|
8313
|
+
try {
|
|
8314
|
+
const snapshotsDir = join13(root, ".story", "snapshots");
|
|
8315
|
+
const snapFiles = readdirSync4(snapshotsDir, "utf-8").filter((f) => f.endsWith(".json")).sort();
|
|
8316
|
+
if (snapFiles.length > 0) {
|
|
8317
|
+
const raw = readFileSync6(join13(snapshotsDir, snapFiles[snapFiles.length - 1]), "utf-8");
|
|
8318
|
+
const snap = JSON.parse(raw);
|
|
8319
|
+
if (snap.issues) {
|
|
8320
|
+
opts.previousOpenIssueCount = snap.issues.filter((i) => i.status !== "resolved").length;
|
|
8321
|
+
}
|
|
8322
|
+
}
|
|
8323
|
+
} catch {
|
|
8324
|
+
}
|
|
8325
|
+
return opts;
|
|
8326
|
+
}
|
|
7918
8327
|
async function recoverPendingMutation(dir, state, root) {
|
|
7919
8328
|
const mutation = state.pendingProjectMutation;
|
|
7920
8329
|
if (!mutation || typeof mutation !== "object") return state;
|
|
@@ -8286,7 +8695,7 @@ Staged: ${stagedResult.data.join(", ")}`
|
|
|
8286
8695
|
}
|
|
8287
8696
|
}
|
|
8288
8697
|
const { state: projectState, warnings } = await loadProject(root);
|
|
8289
|
-
const handoversDir =
|
|
8698
|
+
const handoversDir = join13(root, ".story", "handovers");
|
|
8290
8699
|
const ctx = { state: projectState, warnings, root, handoversDir, format: "md" };
|
|
8291
8700
|
let handoverText = "";
|
|
8292
8701
|
try {
|
|
@@ -8303,7 +8712,7 @@ Staged: ${stagedResult.data.join(", ")}`
|
|
|
8303
8712
|
}
|
|
8304
8713
|
} catch {
|
|
8305
8714
|
}
|
|
8306
|
-
const rulesText = readFileSafe2(
|
|
8715
|
+
const rulesText = readFileSafe2(join13(root, "RULES.md"));
|
|
8307
8716
|
const lessonDigest = buildLessonDigest(projectState.lessons);
|
|
8308
8717
|
const digestParts = [
|
|
8309
8718
|
handoverText ? `## Recent Handovers
|
|
@@ -8319,7 +8728,7 @@ ${rulesText}` : "",
|
|
|
8319
8728
|
].filter(Boolean);
|
|
8320
8729
|
const digest = digestParts.join("\n\n---\n\n");
|
|
8321
8730
|
try {
|
|
8322
|
-
writeFileSync3(
|
|
8731
|
+
writeFileSync3(join13(dir, "context-digest.md"), digest, "utf-8");
|
|
8323
8732
|
} catch {
|
|
8324
8733
|
}
|
|
8325
8734
|
if (mode !== "auto" && args.ticketId) {
|
|
@@ -8338,6 +8747,18 @@ ${rulesText}` : "",
|
|
|
8338
8747
|
return guideError(new Error(`Ticket ${args.ticketId} is blocked by: ${ticket.blockedBy.join(", ")}.`));
|
|
8339
8748
|
}
|
|
8340
8749
|
}
|
|
8750
|
+
if (mode !== "review") {
|
|
8751
|
+
const claimId = ticket.claimedBySession;
|
|
8752
|
+
if (claimId && typeof claimId === "string" && claimId !== session.sessionId) {
|
|
8753
|
+
const claimingSession = findSessionById(root, claimId);
|
|
8754
|
+
if (claimingSession && claimingSession.state.status === "active" && !isLeaseExpired(claimingSession.state)) {
|
|
8755
|
+
deleteSession(root, session.sessionId);
|
|
8756
|
+
return guideError(new Error(
|
|
8757
|
+
`Ticket ${args.ticketId} is claimed by active session ${claimId}. Wait for it to finish or stop it with "claudestory session stop ${claimId}".`
|
|
8758
|
+
));
|
|
8759
|
+
}
|
|
8760
|
+
}
|
|
8761
|
+
}
|
|
8341
8762
|
let entryState;
|
|
8342
8763
|
if (mode === "review") {
|
|
8343
8764
|
entryState = "CODE_REVIEW";
|
|
@@ -8433,12 +8854,22 @@ ${ticket.description}` : "",
|
|
|
8433
8854
|
} else {
|
|
8434
8855
|
candidatesText = "No tickets found.";
|
|
8435
8856
|
}
|
|
8436
|
-
const
|
|
8857
|
+
const highIssues = projectState.issues.filter(
|
|
8858
|
+
(i) => i.status === "open" && (i.severity === "critical" || i.severity === "high")
|
|
8859
|
+
);
|
|
8860
|
+
let issuesText = "";
|
|
8861
|
+
if (highIssues.length > 0) {
|
|
8862
|
+
issuesText = "\n\n## Open Issues (high+ severity)\n\n" + highIssues.map(
|
|
8863
|
+
(i, idx) => `${idx + 1}. **${i.id}: ${i.title}** (${i.severity})`
|
|
8864
|
+
).join("\n");
|
|
8865
|
+
}
|
|
8866
|
+
const guideRecOptions = buildGuideRecommendOptions(root);
|
|
8867
|
+
const recResult = recommend(projectState, 5, guideRecOptions);
|
|
8437
8868
|
let recsText = "";
|
|
8438
8869
|
if (recResult.recommendations.length > 0) {
|
|
8439
|
-
const
|
|
8440
|
-
if (
|
|
8441
|
-
recsText = "\n\n**Recommended:**\n" +
|
|
8870
|
+
const actionableRecs = recResult.recommendations.filter((r) => r.kind === "ticket" || r.kind === "issue");
|
|
8871
|
+
if (actionableRecs.length > 0) {
|
|
8872
|
+
recsText = "\n\n**Recommended:**\n" + actionableRecs.map(
|
|
8442
8873
|
(r) => `- ${r.id}: ${r.title} (${r.reason})`
|
|
8443
8874
|
).join("\n");
|
|
8444
8875
|
}
|
|
@@ -8458,21 +8889,30 @@ ${ticket.description}` : "",
|
|
|
8458
8889
|
const interval = updated.config.handoverInterval ?? 3;
|
|
8459
8890
|
const sessionDesc = maxTickets > 0 ? `Work continuously until all tickets are done or you reach ${maxTickets} tickets.` : "Work continuously until all tickets are done.";
|
|
8460
8891
|
const checkpointDesc = interval > 0 ? ` A checkpoint handover will be saved every ${interval} tickets.` : "";
|
|
8892
|
+
const hasHighIssues = highIssues.length > 0;
|
|
8461
8893
|
const instruction = [
|
|
8462
8894
|
"# Autonomous Session Started",
|
|
8463
8895
|
"",
|
|
8464
8896
|
`You are now in autonomous mode. ${sessionDesc}${checkpointDesc}`,
|
|
8465
|
-
"Do NOT stop to summarize. Do NOT ask the user. Pick a ticket and start working immediately.",
|
|
8897
|
+
"Do NOT stop to summarize. Do NOT ask the user. Pick a ticket or issue and start working immediately.",
|
|
8466
8898
|
"",
|
|
8467
8899
|
"## Ticket Candidates",
|
|
8468
8900
|
"",
|
|
8469
8901
|
candidatesText,
|
|
8902
|
+
issuesText,
|
|
8470
8903
|
recsText,
|
|
8471
8904
|
"",
|
|
8472
|
-
topCandidate ? `Pick **${topCandidate.ticket.id}** (highest priority) by calling \`claudestory_autonomous_guide\` now:` : "Pick a ticket by calling `claudestory_autonomous_guide` now:",
|
|
8905
|
+
topCandidate ? `Pick **${topCandidate.ticket.id}** (highest priority) or an open issue by calling \`claudestory_autonomous_guide\` now:` : hasHighIssues ? "Pick an issue to fix by calling `claudestory_autonomous_guide` now:" : "Pick a ticket by calling `claudestory_autonomous_guide` now:",
|
|
8473
8906
|
"```json",
|
|
8474
8907
|
topCandidate ? `{ "sessionId": "${updated.sessionId}", "action": "report", "report": { "completedAction": "ticket_picked", "ticketId": "${topCandidate.ticket.id}" } }` : `{ "sessionId": "${updated.sessionId}", "action": "report", "report": { "completedAction": "ticket_picked", "ticketId": "T-XXX" } }`,
|
|
8475
|
-
"```"
|
|
8908
|
+
"```",
|
|
8909
|
+
...hasHighIssues ? [
|
|
8910
|
+
"",
|
|
8911
|
+
"Or to fix an issue:",
|
|
8912
|
+
"```json",
|
|
8913
|
+
`{ "sessionId": "${updated.sessionId}", "action": "report", "report": { "completedAction": "issue_picked", "issueId": "${highIssues[0].id}" } }`,
|
|
8914
|
+
"```"
|
|
8915
|
+
] : []
|
|
8476
8916
|
].join("\n");
|
|
8477
8917
|
return guideResult(updated, "PICK_TICKET", {
|
|
8478
8918
|
instruction,
|
|
@@ -8658,26 +9098,7 @@ async function handleResume(root, args) {
|
|
|
8658
9098
|
));
|
|
8659
9099
|
}
|
|
8660
9100
|
if (expectedHead && headResult.data.hash !== expectedHead) {
|
|
8661
|
-
const
|
|
8662
|
-
PICK_TICKET: { state: "PICK_TICKET", resetPlan: false, resetCode: false },
|
|
8663
|
-
COMPLETE: { state: "PICK_TICKET", resetPlan: false, resetCode: false },
|
|
8664
|
-
HANDOVER: { state: "PICK_TICKET", resetPlan: false, resetCode: false },
|
|
8665
|
-
PLAN: { state: "PLAN", resetPlan: true, resetCode: false },
|
|
8666
|
-
IMPLEMENT: { state: "PLAN", resetPlan: true, resetCode: false },
|
|
8667
|
-
WRITE_TESTS: { state: "PLAN", resetPlan: true, resetCode: false },
|
|
8668
|
-
// T-139: baseline stale after HEAD change
|
|
8669
|
-
VERIFY: { state: "IMPLEMENT", resetPlan: false, resetCode: true },
|
|
8670
|
-
// T-131: reviewed code stale after HEAD drift
|
|
8671
|
-
PLAN_REVIEW: { state: "PLAN", resetPlan: true, resetCode: true },
|
|
8672
|
-
TEST: { state: "IMPLEMENT", resetPlan: false, resetCode: true },
|
|
8673
|
-
// T-128: tests invalidated by HEAD change
|
|
8674
|
-
CODE_REVIEW: { state: "PLAN", resetPlan: true, resetCode: true },
|
|
8675
|
-
FINALIZE: { state: "IMPLEMENT", resetPlan: false, resetCode: true },
|
|
8676
|
-
LESSON_CAPTURE: { state: "PICK_TICKET", resetPlan: false, resetCode: false },
|
|
8677
|
-
ISSUE_SWEEP: { state: "PICK_TICKET", resetPlan: false, resetCode: false }
|
|
8678
|
-
// T-128: post-complete, restart sweep
|
|
8679
|
-
};
|
|
8680
|
-
const mapping = recoveryMapping[resumeState] ?? { state: "PICK_TICKET", resetPlan: false, resetCode: false };
|
|
9101
|
+
const mapping = RECOVERY_MAPPING[resumeState] ?? { state: "PICK_TICKET", resetPlan: false, resetCode: false };
|
|
8681
9102
|
const recoveryReviews = {
|
|
8682
9103
|
plan: mapping.resetPlan ? [] : info.state.reviews.plan,
|
|
8683
9104
|
code: mapping.resetCode ? [] : info.state.reviews.code
|
|
@@ -9025,12 +9446,12 @@ function guideError(err) {
|
|
|
9025
9446
|
}
|
|
9026
9447
|
function readFileSafe2(path2) {
|
|
9027
9448
|
try {
|
|
9028
|
-
return
|
|
9449
|
+
return readFileSync6(path2, "utf-8");
|
|
9029
9450
|
} catch {
|
|
9030
9451
|
return "";
|
|
9031
9452
|
}
|
|
9032
9453
|
}
|
|
9033
|
-
var SEVERITY_MAP, workspaceLocks, MAX_AUTO_ADVANCE_DEPTH;
|
|
9454
|
+
var RECOVERY_MAPPING, SEVERITY_MAP, workspaceLocks, MAX_AUTO_ADVANCE_DEPTH;
|
|
9034
9455
|
var init_guide = __esm({
|
|
9035
9456
|
"src/autonomous/guide.ts"() {
|
|
9036
9457
|
"use strict";
|
|
@@ -9052,6 +9473,23 @@ var init_guide = __esm({
|
|
|
9052
9473
|
init_queries();
|
|
9053
9474
|
init_recommend();
|
|
9054
9475
|
init_handover();
|
|
9476
|
+
RECOVERY_MAPPING = {
|
|
9477
|
+
PICK_TICKET: { state: "PICK_TICKET", resetPlan: false, resetCode: false },
|
|
9478
|
+
COMPLETE: { state: "PICK_TICKET", resetPlan: false, resetCode: false },
|
|
9479
|
+
HANDOVER: { state: "PICK_TICKET", resetPlan: false, resetCode: false },
|
|
9480
|
+
PLAN: { state: "PLAN", resetPlan: true, resetCode: false },
|
|
9481
|
+
IMPLEMENT: { state: "PLAN", resetPlan: true, resetCode: false },
|
|
9482
|
+
WRITE_TESTS: { state: "PLAN", resetPlan: true, resetCode: false },
|
|
9483
|
+
BUILD: { state: "IMPLEMENT", resetPlan: false, resetCode: true },
|
|
9484
|
+
VERIFY: { state: "IMPLEMENT", resetPlan: false, resetCode: true },
|
|
9485
|
+
PLAN_REVIEW: { state: "PLAN", resetPlan: true, resetCode: true },
|
|
9486
|
+
TEST: { state: "IMPLEMENT", resetPlan: false, resetCode: true },
|
|
9487
|
+
CODE_REVIEW: { state: "PLAN", resetPlan: true, resetCode: true },
|
|
9488
|
+
FINALIZE: { state: "IMPLEMENT", resetPlan: false, resetCode: true },
|
|
9489
|
+
LESSON_CAPTURE: { state: "PICK_TICKET", resetPlan: false, resetCode: false },
|
|
9490
|
+
ISSUE_FIX: { state: "PICK_TICKET", resetPlan: false, resetCode: false },
|
|
9491
|
+
ISSUE_SWEEP: { state: "PICK_TICKET", resetPlan: false, resetCode: false }
|
|
9492
|
+
};
|
|
9055
9493
|
SEVERITY_MAP = {
|
|
9056
9494
|
critical: "critical",
|
|
9057
9495
|
major: "high",
|
|
@@ -9260,8 +9698,8 @@ var init_session_report_formatter = __esm({
|
|
|
9260
9698
|
});
|
|
9261
9699
|
|
|
9262
9700
|
// src/cli/commands/session-report.ts
|
|
9263
|
-
import { readFileSync as
|
|
9264
|
-
import { join as
|
|
9701
|
+
import { readFileSync as readFileSync7, existsSync as existsSync10 } from "fs";
|
|
9702
|
+
import { join as join14 } from "path";
|
|
9265
9703
|
async function handleSessionReport(sessionId, root, format = "md") {
|
|
9266
9704
|
if (!UUID_REGEX.test(sessionId)) {
|
|
9267
9705
|
return {
|
|
@@ -9280,7 +9718,7 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9280
9718
|
isError: true
|
|
9281
9719
|
};
|
|
9282
9720
|
}
|
|
9283
|
-
const statePath2 =
|
|
9721
|
+
const statePath2 = join14(dir, "state.json");
|
|
9284
9722
|
if (!existsSync10(statePath2)) {
|
|
9285
9723
|
return {
|
|
9286
9724
|
output: `Error: Session ${sessionId} corrupt \u2014 state.json missing.`,
|
|
@@ -9290,7 +9728,7 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9290
9728
|
};
|
|
9291
9729
|
}
|
|
9292
9730
|
try {
|
|
9293
|
-
const rawJson = JSON.parse(
|
|
9731
|
+
const rawJson = JSON.parse(readFileSync7(statePath2, "utf-8"));
|
|
9294
9732
|
if (rawJson && typeof rawJson === "object" && "schemaVersion" in rawJson && rawJson.schemaVersion !== CURRENT_SESSION_SCHEMA_VERSION) {
|
|
9295
9733
|
return {
|
|
9296
9734
|
output: `Error: Session ${sessionId} \u2014 unsupported session schema version ${rawJson.schemaVersion}.`,
|
|
@@ -9319,7 +9757,7 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9319
9757
|
const events = readEvents(dir);
|
|
9320
9758
|
let planContent = null;
|
|
9321
9759
|
try {
|
|
9322
|
-
planContent =
|
|
9760
|
+
planContent = readFileSync7(join14(dir, "plan.md"), "utf-8");
|
|
9323
9761
|
} catch {
|
|
9324
9762
|
}
|
|
9325
9763
|
let gitLog = null;
|
|
@@ -9348,7 +9786,7 @@ var init_session_report = __esm({
|
|
|
9348
9786
|
});
|
|
9349
9787
|
|
|
9350
9788
|
// src/cli/commands/phase.ts
|
|
9351
|
-
import { join as
|
|
9789
|
+
import { join as join15, resolve as resolve6 } from "path";
|
|
9352
9790
|
function validatePhaseId(id) {
|
|
9353
9791
|
if (id.length > PHASE_ID_MAX_LENGTH) {
|
|
9354
9792
|
throw new CliValidationError("invalid_input", `Phase ID "${id}" exceeds ${PHASE_ID_MAX_LENGTH} characters`);
|
|
@@ -9537,21 +9975,21 @@ async function handlePhaseDelete(id, reassign, format, root) {
|
|
|
9537
9975
|
const updated = { ...ticket, phase: reassign, order: maxOrder };
|
|
9538
9976
|
const parsed = TicketSchema.parse(updated);
|
|
9539
9977
|
const content = serializeJSON(parsed);
|
|
9540
|
-
const target =
|
|
9978
|
+
const target = join15(wrapDir, "tickets", `${parsed.id}.json`);
|
|
9541
9979
|
operations.push({ op: "write", target, content });
|
|
9542
9980
|
}
|
|
9543
9981
|
for (const issue of affectedIssues) {
|
|
9544
9982
|
const updated = { ...issue, phase: reassign };
|
|
9545
9983
|
const parsed = IssueSchema.parse(updated);
|
|
9546
9984
|
const content = serializeJSON(parsed);
|
|
9547
|
-
const target =
|
|
9985
|
+
const target = join15(wrapDir, "issues", `${parsed.id}.json`);
|
|
9548
9986
|
operations.push({ op: "write", target, content });
|
|
9549
9987
|
}
|
|
9550
9988
|
const newPhases = state.roadmap.phases.filter((p) => p.id !== id);
|
|
9551
9989
|
const newRoadmap = { ...state.roadmap, phases: newPhases };
|
|
9552
9990
|
const parsedRoadmap = RoadmapSchema.parse(newRoadmap);
|
|
9553
9991
|
const roadmapContent = serializeJSON(parsedRoadmap);
|
|
9554
|
-
const roadmapTarget =
|
|
9992
|
+
const roadmapTarget = join15(wrapDir, "roadmap.json");
|
|
9555
9993
|
operations.push({ op: "write", target: roadmapTarget, content: roadmapContent });
|
|
9556
9994
|
await runTransactionUnlocked(root, operations);
|
|
9557
9995
|
} else {
|
|
@@ -9584,14 +10022,14 @@ var init_phase = __esm({
|
|
|
9584
10022
|
|
|
9585
10023
|
// src/mcp/tools.ts
|
|
9586
10024
|
import { z as z10 } from "zod";
|
|
9587
|
-
import { join as
|
|
10025
|
+
import { join as join16 } from "path";
|
|
9588
10026
|
function formatMcpError(code, message) {
|
|
9589
10027
|
return `[${code}] ${message}`;
|
|
9590
10028
|
}
|
|
9591
10029
|
async function runMcpReadTool(pinnedRoot, handler) {
|
|
9592
10030
|
try {
|
|
9593
10031
|
const { state, warnings } = await loadProject(pinnedRoot);
|
|
9594
|
-
const handoversDir =
|
|
10032
|
+
const handoversDir = join16(pinnedRoot, ".story", "handovers");
|
|
9595
10033
|
const ctx = { state, warnings, root: pinnedRoot, handoversDir, format: "md" };
|
|
9596
10034
|
const result = await handler(ctx);
|
|
9597
10035
|
if (result.errorCode && INFRASTRUCTURE_ERROR_CODES.includes(result.errorCode)) {
|
|
@@ -10178,10 +10616,10 @@ var init_tools = __esm({
|
|
|
10178
10616
|
|
|
10179
10617
|
// src/core/init.ts
|
|
10180
10618
|
import { mkdir as mkdir4, stat as stat2, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
|
|
10181
|
-
import { join as
|
|
10619
|
+
import { join as join17, resolve as resolve7 } from "path";
|
|
10182
10620
|
async function initProject(root, options) {
|
|
10183
10621
|
const absRoot = resolve7(root);
|
|
10184
|
-
const wrapDir =
|
|
10622
|
+
const wrapDir = join17(absRoot, ".story");
|
|
10185
10623
|
let exists = false;
|
|
10186
10624
|
try {
|
|
10187
10625
|
const s = await stat2(wrapDir);
|
|
@@ -10201,11 +10639,11 @@ async function initProject(root, options) {
|
|
|
10201
10639
|
".story/ already exists. Use --force to overwrite config and roadmap."
|
|
10202
10640
|
);
|
|
10203
10641
|
}
|
|
10204
|
-
await mkdir4(
|
|
10205
|
-
await mkdir4(
|
|
10206
|
-
await mkdir4(
|
|
10207
|
-
await mkdir4(
|
|
10208
|
-
await mkdir4(
|
|
10642
|
+
await mkdir4(join17(wrapDir, "tickets"), { recursive: true });
|
|
10643
|
+
await mkdir4(join17(wrapDir, "issues"), { recursive: true });
|
|
10644
|
+
await mkdir4(join17(wrapDir, "handovers"), { recursive: true });
|
|
10645
|
+
await mkdir4(join17(wrapDir, "notes"), { recursive: true });
|
|
10646
|
+
await mkdir4(join17(wrapDir, "lessons"), { recursive: true });
|
|
10209
10647
|
const created = [
|
|
10210
10648
|
".story/config.json",
|
|
10211
10649
|
".story/roadmap.json",
|
|
@@ -10245,7 +10683,7 @@ async function initProject(root, options) {
|
|
|
10245
10683
|
};
|
|
10246
10684
|
await writeConfig(config, absRoot);
|
|
10247
10685
|
await writeRoadmap(roadmap, absRoot);
|
|
10248
|
-
const gitignorePath =
|
|
10686
|
+
const gitignorePath = join17(wrapDir, ".gitignore");
|
|
10249
10687
|
await ensureGitignoreEntries(gitignorePath, STORY_GITIGNORE_ENTRIES);
|
|
10250
10688
|
const warnings = [];
|
|
10251
10689
|
if (options.force && exists) {
|
|
@@ -10293,7 +10731,7 @@ var init_init = __esm({
|
|
|
10293
10731
|
// src/mcp/index.ts
|
|
10294
10732
|
var mcp_exports = {};
|
|
10295
10733
|
import { realpathSync as realpathSync2, existsSync as existsSync11 } from "fs";
|
|
10296
|
-
import { resolve as resolve8, join as
|
|
10734
|
+
import { resolve as resolve8, join as join18, isAbsolute } from "path";
|
|
10297
10735
|
import { z as z11 } from "zod";
|
|
10298
10736
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10299
10737
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -10308,7 +10746,7 @@ function tryDiscoverRoot() {
|
|
|
10308
10746
|
const resolved = resolve8(envRoot);
|
|
10309
10747
|
try {
|
|
10310
10748
|
const canonical = realpathSync2(resolved);
|
|
10311
|
-
if (existsSync11(
|
|
10749
|
+
if (existsSync11(join18(canonical, CONFIG_PATH2))) {
|
|
10312
10750
|
return canonical;
|
|
10313
10751
|
}
|
|
10314
10752
|
process.stderr.write(`Warning: No .story/config.json at ${canonical}
|
|
@@ -10411,7 +10849,7 @@ var init_mcp = __esm({
|
|
|
10411
10849
|
init_init();
|
|
10412
10850
|
ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
|
|
10413
10851
|
CONFIG_PATH2 = ".story/config.json";
|
|
10414
|
-
version = "0.1.
|
|
10852
|
+
version = "0.1.37";
|
|
10415
10853
|
main().catch((err) => {
|
|
10416
10854
|
process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
|
|
10417
10855
|
`);
|
|
@@ -10447,7 +10885,7 @@ __export(run_exports, {
|
|
|
10447
10885
|
runReadCommand: () => runReadCommand,
|
|
10448
10886
|
writeOutput: () => writeOutput
|
|
10449
10887
|
});
|
|
10450
|
-
import { join as
|
|
10888
|
+
import { join as join19 } from "path";
|
|
10451
10889
|
function writeOutput(text) {
|
|
10452
10890
|
try {
|
|
10453
10891
|
process.stdout.write(text + "\n");
|
|
@@ -10475,7 +10913,7 @@ async function runReadCommand(format, handler) {
|
|
|
10475
10913
|
return;
|
|
10476
10914
|
}
|
|
10477
10915
|
const { state, warnings } = await loadProject(root);
|
|
10478
|
-
const handoversDir =
|
|
10916
|
+
const handoversDir = join19(root, ".story", "handovers");
|
|
10479
10917
|
const result = await handler({ state, warnings, root, handoversDir, format });
|
|
10480
10918
|
writeOutput(result.output);
|
|
10481
10919
|
let exitCode = result.exitCode ?? ExitCode.OK;
|
|
@@ -10510,7 +10948,7 @@ async function runDeleteCommand(format, force, handler) {
|
|
|
10510
10948
|
return;
|
|
10511
10949
|
}
|
|
10512
10950
|
const { state, warnings } = await loadProject(root);
|
|
10513
|
-
const handoversDir =
|
|
10951
|
+
const handoversDir = join19(root, ".story", "handovers");
|
|
10514
10952
|
if (!force && hasIntegrityWarnings(warnings)) {
|
|
10515
10953
|
writeOutput(
|
|
10516
10954
|
formatError(
|
|
@@ -11024,7 +11462,7 @@ __export(setup_skill_exports, {
|
|
|
11024
11462
|
});
|
|
11025
11463
|
import { mkdir as mkdir5, writeFile as writeFile3, readFile as readFile5, rm, rename as rename2, unlink as unlink3 } from "fs/promises";
|
|
11026
11464
|
import { existsSync as existsSync12 } from "fs";
|
|
11027
|
-
import { join as
|
|
11465
|
+
import { join as join20, dirname as dirname4 } from "path";
|
|
11028
11466
|
import { homedir } from "os";
|
|
11029
11467
|
import { execFileSync } from "child_process";
|
|
11030
11468
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
@@ -11033,10 +11471,10 @@ function log(msg) {
|
|
|
11033
11471
|
}
|
|
11034
11472
|
function resolveSkillSourceDir() {
|
|
11035
11473
|
const thisDir = dirname4(fileURLToPath3(import.meta.url));
|
|
11036
|
-
const bundledPath =
|
|
11037
|
-
if (existsSync12(
|
|
11038
|
-
const sourcePath =
|
|
11039
|
-
if (existsSync12(
|
|
11474
|
+
const bundledPath = join20(thisDir, "..", "src", "skill");
|
|
11475
|
+
if (existsSync12(join20(bundledPath, "SKILL.md"))) return bundledPath;
|
|
11476
|
+
const sourcePath = join20(thisDir, "..", "..", "skill");
|
|
11477
|
+
if (existsSync12(join20(sourcePath, "SKILL.md"))) return sourcePath;
|
|
11040
11478
|
throw new Error(
|
|
11041
11479
|
`Cannot find bundled skill files. Checked:
|
|
11042
11480
|
${bundledPath}
|
|
@@ -11049,7 +11487,7 @@ function isHookWithCommand(entry, command) {
|
|
|
11049
11487
|
return e.type === "command" && typeof e.command === "string" && e.command.trim() === command;
|
|
11050
11488
|
}
|
|
11051
11489
|
async function registerHook(hookType, hookEntry, settingsPath, matcher) {
|
|
11052
|
-
const path2 = settingsPath ??
|
|
11490
|
+
const path2 = settingsPath ?? join20(homedir(), ".claude", "settings.json");
|
|
11053
11491
|
let raw = "{}";
|
|
11054
11492
|
if (existsSync12(path2)) {
|
|
11055
11493
|
try {
|
|
@@ -11147,7 +11585,7 @@ async function registerStopHook(settingsPath) {
|
|
|
11147
11585
|
return registerHook("Stop", { type: "command", command: STOP_HOOK_COMMAND, async: true }, settingsPath);
|
|
11148
11586
|
}
|
|
11149
11587
|
async function removeHook(hookType, command, settingsPath) {
|
|
11150
|
-
const path2 = settingsPath ??
|
|
11588
|
+
const path2 = settingsPath ?? join20(homedir(), ".claude", "settings.json");
|
|
11151
11589
|
let raw = "{}";
|
|
11152
11590
|
if (existsSync12(path2)) {
|
|
11153
11591
|
try {
|
|
@@ -11194,7 +11632,7 @@ async function removeHook(hookType, command, settingsPath) {
|
|
|
11194
11632
|
}
|
|
11195
11633
|
async function handleSetupSkill(options = {}) {
|
|
11196
11634
|
const { skipHooks = false } = options;
|
|
11197
|
-
const skillDir =
|
|
11635
|
+
const skillDir = join20(homedir(), ".claude", "skills", "story");
|
|
11198
11636
|
await mkdir5(skillDir, { recursive: true });
|
|
11199
11637
|
let srcSkillDir;
|
|
11200
11638
|
try {
|
|
@@ -11207,19 +11645,19 @@ async function handleSetupSkill(options = {}) {
|
|
|
11207
11645
|
process.exitCode = 1;
|
|
11208
11646
|
return;
|
|
11209
11647
|
}
|
|
11210
|
-
const oldPrimeDir =
|
|
11648
|
+
const oldPrimeDir = join20(homedir(), ".claude", "skills", "prime");
|
|
11211
11649
|
if (existsSync12(oldPrimeDir)) {
|
|
11212
11650
|
await rm(oldPrimeDir, { recursive: true, force: true });
|
|
11213
11651
|
log("Removed old /prime skill (migrated to /story)");
|
|
11214
11652
|
}
|
|
11215
|
-
const existed = existsSync12(
|
|
11216
|
-
const skillContent = await readFile5(
|
|
11217
|
-
await writeFile3(
|
|
11653
|
+
const existed = existsSync12(join20(skillDir, "SKILL.md"));
|
|
11654
|
+
const skillContent = await readFile5(join20(srcSkillDir, "SKILL.md"), "utf-8");
|
|
11655
|
+
await writeFile3(join20(skillDir, "SKILL.md"), skillContent, "utf-8");
|
|
11218
11656
|
let referenceWritten = false;
|
|
11219
|
-
const refSrcPath =
|
|
11657
|
+
const refSrcPath = join20(srcSkillDir, "reference.md");
|
|
11220
11658
|
if (existsSync12(refSrcPath)) {
|
|
11221
11659
|
const refContent = await readFile5(refSrcPath, "utf-8");
|
|
11222
|
-
await writeFile3(
|
|
11660
|
+
await writeFile3(join20(skillDir, "reference.md"), refContent, "utf-8");
|
|
11223
11661
|
referenceWritten = true;
|
|
11224
11662
|
}
|
|
11225
11663
|
log(`${existed ? "Updated" : "Installed"} /story skill at ${skillDir}/`);
|
|
@@ -11333,8 +11771,8 @@ var hook_status_exports = {};
|
|
|
11333
11771
|
__export(hook_status_exports, {
|
|
11334
11772
|
handleHookStatus: () => handleHookStatus
|
|
11335
11773
|
});
|
|
11336
|
-
import { readFileSync as
|
|
11337
|
-
import { join as
|
|
11774
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, renameSync as renameSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
11775
|
+
import { join as join21 } from "path";
|
|
11338
11776
|
async function readStdinSilent() {
|
|
11339
11777
|
try {
|
|
11340
11778
|
const chunks = [];
|
|
@@ -11384,10 +11822,10 @@ function activePayload(session) {
|
|
|
11384
11822
|
};
|
|
11385
11823
|
}
|
|
11386
11824
|
function ensureGitignore(root) {
|
|
11387
|
-
const gitignorePath =
|
|
11825
|
+
const gitignorePath = join21(root, ".story", ".gitignore");
|
|
11388
11826
|
let existing = "";
|
|
11389
11827
|
try {
|
|
11390
|
-
existing =
|
|
11828
|
+
existing = readFileSync8(gitignorePath, "utf-8");
|
|
11391
11829
|
} catch {
|
|
11392
11830
|
}
|
|
11393
11831
|
const lines = existing.split("\n").map((l) => l.trim());
|
|
@@ -11403,7 +11841,7 @@ function ensureGitignore(root) {
|
|
|
11403
11841
|
}
|
|
11404
11842
|
function writeStatus(root, payload) {
|
|
11405
11843
|
ensureGitignore(root);
|
|
11406
|
-
const statusPath =
|
|
11844
|
+
const statusPath = join21(root, ".story", "status.json");
|
|
11407
11845
|
const content = JSON.stringify(payload, null, 2) + "\n";
|
|
11408
11846
|
atomicWriteSync(statusPath, content);
|
|
11409
11847
|
}
|
|
@@ -11462,8 +11900,8 @@ var config_update_exports = {};
|
|
|
11462
11900
|
__export(config_update_exports, {
|
|
11463
11901
|
handleConfigSetOverrides: () => handleConfigSetOverrides
|
|
11464
11902
|
});
|
|
11465
|
-
import { readFileSync as
|
|
11466
|
-
import { join as
|
|
11903
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
11904
|
+
import { join as join22 } from "path";
|
|
11467
11905
|
async function handleConfigSetOverrides(root, format, options) {
|
|
11468
11906
|
const { json: jsonArg, clear } = options;
|
|
11469
11907
|
if (!clear && !jsonArg) {
|
|
@@ -11491,8 +11929,8 @@ async function handleConfigSetOverrides(root, format, options) {
|
|
|
11491
11929
|
}
|
|
11492
11930
|
let resultOverrides = null;
|
|
11493
11931
|
await withProjectLock(root, { strict: false }, async () => {
|
|
11494
|
-
const configPath =
|
|
11495
|
-
const rawContent =
|
|
11932
|
+
const configPath = join22(root, ".story", "config.json");
|
|
11933
|
+
const rawContent = readFileSync9(configPath, "utf-8");
|
|
11496
11934
|
const raw = JSON.parse(rawContent);
|
|
11497
11935
|
if (clear) {
|
|
11498
11936
|
delete raw.recipeOverrides;
|
|
@@ -13835,7 +14273,7 @@ async function runCli() {
|
|
|
13835
14273
|
registerSessionCommand: registerSessionCommand2,
|
|
13836
14274
|
registerRepairCommand: registerRepairCommand2
|
|
13837
14275
|
} = await Promise.resolve().then(() => (init_register(), register_exports));
|
|
13838
|
-
const version2 = "0.1.
|
|
14276
|
+
const version2 = "0.1.37";
|
|
13839
14277
|
class HandledError extends Error {
|
|
13840
14278
|
constructor() {
|
|
13841
14279
|
super("HANDLED_ERROR");
|