@anthropologies/claudestory 0.1.57 → 0.1.59
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 +346 -133
- package/dist/mcp.js +286 -84
- package/package.json +1 -1
- package/src/skill/SKILL.md +20 -1
- package/src/skill/review-lenses/references/judge.md +18 -0
- package/src/skill/review-lenses/references/lens-accessibility.md +17 -0
- package/src/skill/review-lenses/references/lens-api-design.md +14 -0
- package/src/skill/review-lenses/references/lens-clean-code.md +15 -0
- package/src/skill/review-lenses/references/lens-concurrency.md +16 -0
- package/src/skill/review-lenses/references/lens-error-handling.md +15 -0
- package/src/skill/review-lenses/references/lens-performance.md +16 -0
- package/src/skill/review-lenses/references/lens-security.md +17 -0
- package/src/skill/review-lenses/references/lens-test-quality.md +16 -0
- package/src/skill/review-lenses/references/merger.md +13 -0
- package/src/skill/review-lenses/references/shared-preamble.md +10 -0
- package/src/skill/review-lenses/review-lenses.md +59 -0
package/dist/cli.js
CHANGED
|
@@ -5444,8 +5444,48 @@ var init_session_types = __esm({
|
|
|
5444
5444
|
maxTicketsPerSession: z9.number().min(0).default(5),
|
|
5445
5445
|
handoverInterval: z9.number().min(0).default(3),
|
|
5446
5446
|
compactThreshold: z9.string().default("high"),
|
|
5447
|
-
reviewBackends: z9.array(z9.string()).default(["codex", "agent"])
|
|
5447
|
+
reviewBackends: z9.array(z9.string()).default(["codex", "agent"]),
|
|
5448
|
+
// T-181: Multi-lens review config
|
|
5449
|
+
lensConfig: z9.object({
|
|
5450
|
+
lenses: z9.union([z9.literal("auto"), z9.array(z9.string())]).default("auto"),
|
|
5451
|
+
maxLenses: z9.number().min(1).max(8).default(8),
|
|
5452
|
+
lensTimeout: z9.union([
|
|
5453
|
+
z9.number(),
|
|
5454
|
+
z9.object({ default: z9.number(), opus: z9.number() })
|
|
5455
|
+
]).default({ default: 60, opus: 120 }),
|
|
5456
|
+
findingBudget: z9.number().min(1).default(10),
|
|
5457
|
+
confidenceFloor: z9.number().min(0).max(1).default(0.6),
|
|
5458
|
+
tokenBudgetPerLens: z9.number().min(1e3).default(32e3),
|
|
5459
|
+
hotPaths: z9.array(z9.string()).default([]),
|
|
5460
|
+
lensModels: z9.record(z9.string()).default({ default: "sonnet", security: "opus", concurrency: "opus" })
|
|
5461
|
+
}).optional(),
|
|
5462
|
+
blockingPolicy: z9.object({
|
|
5463
|
+
neverBlock: z9.array(z9.string()).default([]),
|
|
5464
|
+
alwaysBlock: z9.array(z9.string()).default(["injection", "auth-bypass", "hardcoded-secrets"]),
|
|
5465
|
+
planReviewBlockingLenses: z9.array(z9.string()).default(["security", "error-handling"])
|
|
5466
|
+
}).optional(),
|
|
5467
|
+
requireSecretsGate: z9.boolean().default(false),
|
|
5468
|
+
requireAccessibility: z9.boolean().default(false),
|
|
5469
|
+
testMapping: z9.object({
|
|
5470
|
+
strategy: z9.literal("convention"),
|
|
5471
|
+
patterns: z9.array(z9.object({
|
|
5472
|
+
source: z9.string(),
|
|
5473
|
+
test: z9.string()
|
|
5474
|
+
}))
|
|
5475
|
+
}).optional()
|
|
5448
5476
|
}).default({ maxTicketsPerSession: 5, compactThreshold: "high", reviewBackends: ["codex", "agent"], handoverInterval: 3 }),
|
|
5477
|
+
// T-181: Lens review findings history (for lessons feedback loop)
|
|
5478
|
+
lensReviewHistory: z9.array(z9.object({
|
|
5479
|
+
ticketId: z9.string(),
|
|
5480
|
+
stage: z9.enum(["CODE_REVIEW", "PLAN_REVIEW"]),
|
|
5481
|
+
lens: z9.string(),
|
|
5482
|
+
category: z9.string(),
|
|
5483
|
+
severity: z9.string(),
|
|
5484
|
+
disposition: z9.enum(["open", "addressed", "contested", "deferred"]),
|
|
5485
|
+
description: z9.string(),
|
|
5486
|
+
dismissReason: z9.string().optional(),
|
|
5487
|
+
timestamp: z9.string()
|
|
5488
|
+
})).default([]),
|
|
5449
5489
|
// T-123: Issue sweep tracking
|
|
5450
5490
|
issueSweepState: z9.object({
|
|
5451
5491
|
remaining: z9.array(z9.string()),
|
|
@@ -5474,7 +5514,8 @@ var init_session_types = __esm({
|
|
|
5474
5514
|
resolvedDefaults: z9.object({
|
|
5475
5515
|
maxTicketsPerSession: z9.number(),
|
|
5476
5516
|
compactThreshold: z9.string(),
|
|
5477
|
-
reviewBackends: z9.array(z9.string())
|
|
5517
|
+
reviewBackends: z9.array(z9.string()),
|
|
5518
|
+
handoverInterval: z9.number().optional()
|
|
5478
5519
|
}).optional()
|
|
5479
5520
|
}).passthrough();
|
|
5480
5521
|
}
|
|
@@ -5667,7 +5708,8 @@ function refreshLease(state) {
|
|
|
5667
5708
|
contextPressure: {
|
|
5668
5709
|
...state.contextPressure,
|
|
5669
5710
|
guideCallCount: newCallCount,
|
|
5670
|
-
|
|
5711
|
+
// ISS-084: Include resolved issues in work count
|
|
5712
|
+
ticketsCompleted: (state.completedTickets?.length ?? 0) + (state.resolvedIssues?.length ?? 0)
|
|
5671
5713
|
}
|
|
5672
5714
|
};
|
|
5673
5715
|
}
|
|
@@ -5885,7 +5927,7 @@ var init_state_machine = __esm({
|
|
|
5885
5927
|
VERIFY: ["FINALIZE", "IMPLEMENT", "VERIFY"],
|
|
5886
5928
|
// pass → FINALIZE, fail → IMPLEMENT, retry
|
|
5887
5929
|
FINALIZE: ["COMPLETE", "PICK_TICKET"],
|
|
5888
|
-
// PICK_TICKET for
|
|
5930
|
+
// ISS-084: issues now route through COMPLETE too; PICK_TICKET kept for in-flight session compat
|
|
5889
5931
|
COMPLETE: ["PICK_TICKET", "HANDOVER", "ISSUE_SWEEP", "SESSION_END"],
|
|
5890
5932
|
ISSUE_FIX: ["FINALIZE", "PICK_TICKET", "ISSUE_FIX"],
|
|
5891
5933
|
// T-153: fix done → FINALIZE, cancel → PICK_TICKET, retry self
|
|
@@ -5905,7 +5947,7 @@ var init_state_machine = __esm({
|
|
|
5905
5947
|
// src/autonomous/context-pressure.ts
|
|
5906
5948
|
function evaluatePressure(state) {
|
|
5907
5949
|
const calls = state.contextPressure?.guideCallCount ?? state.guideCallCount ?? 0;
|
|
5908
|
-
const tickets = state.
|
|
5950
|
+
const tickets = (state.completedTickets?.length ?? 0) + (state.resolvedIssues?.length ?? 0);
|
|
5909
5951
|
const eventsBytes = state.contextPressure?.eventsLogBytes ?? 0;
|
|
5910
5952
|
const tier = state.config?.compactThreshold ?? "high";
|
|
5911
5953
|
const t = THRESHOLDS[tier] ?? THRESHOLDS["high"];
|
|
@@ -6314,6 +6356,22 @@ function djb2Hash(content) {
|
|
|
6314
6356
|
}
|
|
6315
6357
|
return hash.toString(36);
|
|
6316
6358
|
}
|
|
6359
|
+
function buildLensHistoryUpdate(findings, existing, ticketId, stage) {
|
|
6360
|
+
const existingKeys = new Set(
|
|
6361
|
+
existing.map((e) => `${e.ticketId}:${e.stage}:${e.lens}:${e.category}`)
|
|
6362
|
+
);
|
|
6363
|
+
const newEntries = findings.map((f) => ({
|
|
6364
|
+
ticketId,
|
|
6365
|
+
stage,
|
|
6366
|
+
lens: typeof f.lens === "string" && f.lens !== "" ? f.lens : "unknown",
|
|
6367
|
+
category: f.category,
|
|
6368
|
+
severity: f.severity,
|
|
6369
|
+
disposition: f.disposition ?? "open",
|
|
6370
|
+
description: f.description,
|
|
6371
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6372
|
+
})).filter((e) => !existingKeys.has(`${e.ticketId}:${e.stage}:${e.lens}:${e.category}`));
|
|
6373
|
+
return newEntries.length > 0 ? [...existing, ...newEntries] : null;
|
|
6374
|
+
}
|
|
6317
6375
|
var StageContext;
|
|
6318
6376
|
var init_types2 = __esm({
|
|
6319
6377
|
"src/autonomous/stages/types.ts"() {
|
|
@@ -6448,17 +6506,22 @@ var init_pick_ticket = __esm({
|
|
|
6448
6506
|
(c, i) => `${i + 1}. **${c.ticket.id}: ${c.ticket.title}** (${c.ticket.type})`
|
|
6449
6507
|
).join("\n");
|
|
6450
6508
|
}
|
|
6451
|
-
const
|
|
6452
|
-
|
|
6453
|
-
);
|
|
6509
|
+
const allOpenIssues = projectState.issues.filter((i) => i.status === "open");
|
|
6510
|
+
const highIssues = allOpenIssues.filter((i) => i.severity === "critical" || i.severity === "high");
|
|
6511
|
+
const otherIssues = allOpenIssues.filter((i) => i.severity !== "critical" && i.severity !== "high");
|
|
6454
6512
|
let issuesText = "";
|
|
6455
6513
|
if (highIssues.length > 0) {
|
|
6456
6514
|
issuesText = "\n\n## Open Issues (high+ severity)\n\n" + highIssues.map(
|
|
6457
6515
|
(i, idx) => `${idx + 1}. **${i.id}: ${i.title}** (${i.severity})`
|
|
6458
6516
|
).join("\n");
|
|
6459
6517
|
}
|
|
6518
|
+
if (otherIssues.length > 0) {
|
|
6519
|
+
issuesText += "\n\n## Open Issues (medium/low)\n\n" + otherIssues.map(
|
|
6520
|
+
(i, idx) => `${idx + 1}. **${i.id}: ${i.title}** (${i.severity})`
|
|
6521
|
+
).join("\n");
|
|
6522
|
+
}
|
|
6460
6523
|
const topCandidate = candidates.kind === "found" ? candidates.candidates[0] : null;
|
|
6461
|
-
const hasIssues =
|
|
6524
|
+
const hasIssues = allOpenIssues.length > 0;
|
|
6462
6525
|
if (!topCandidate && candidates.kind !== "found" && !hasIssues) {
|
|
6463
6526
|
return { action: "goto", target: "COMPLETE" };
|
|
6464
6527
|
}
|
|
@@ -6479,7 +6542,7 @@ var init_pick_ticket = __esm({
|
|
|
6479
6542
|
"",
|
|
6480
6543
|
"Or to fix an issue:",
|
|
6481
6544
|
"```json",
|
|
6482
|
-
`{ "sessionId": "${ctx.state.sessionId}", "action": "report", "report": { "completedAction": "issue_picked", "issueId": "${highIssues[0].id}" } }`,
|
|
6545
|
+
`{ "sessionId": "${ctx.state.sessionId}", "action": "report", "report": { "completedAction": "issue_picked", "issueId": "${(highIssues[0] ?? allOpenIssues[0]).id}" } }`,
|
|
6483
6546
|
"```"
|
|
6484
6547
|
] : []
|
|
6485
6548
|
].join("\n"),
|
|
@@ -6697,6 +6760,7 @@ var init_plan_review = __esm({
|
|
|
6697
6760
|
"src/autonomous/stages/plan-review.ts"() {
|
|
6698
6761
|
"use strict";
|
|
6699
6762
|
init_esm_shims();
|
|
6763
|
+
init_types2();
|
|
6700
6764
|
init_review_depth();
|
|
6701
6765
|
PlanReviewStage = class {
|
|
6702
6766
|
id = "PLAN_REVIEW";
|
|
@@ -6707,6 +6771,31 @@ var init_plan_review = __esm({
|
|
|
6707
6771
|
const reviewer = nextReviewer(existingReviews, backends);
|
|
6708
6772
|
const risk = ctx.state.ticket?.risk ?? "low";
|
|
6709
6773
|
const minRounds = requiredRounds(risk);
|
|
6774
|
+
if (reviewer === "lenses") {
|
|
6775
|
+
return {
|
|
6776
|
+
instruction: [
|
|
6777
|
+
`# Multi-Lens Plan Review \u2014 Round ${roundNum} of ${Math.max(minRounds, roundNum)} minimum`,
|
|
6778
|
+
"",
|
|
6779
|
+
"This round uses the **multi-lens review orchestrator** for plan review. It fans out to specialized review agents (Clean Code, Security, Error Handling, and more) in parallel to evaluate the plan from multiple perspectives.",
|
|
6780
|
+
"",
|
|
6781
|
+
"1. Read the plan file",
|
|
6782
|
+
"2. Call `prepareLensReview()` with the plan text (stage: PLAN_REVIEW)",
|
|
6783
|
+
"3. Spawn all lens subagents in parallel",
|
|
6784
|
+
"4. Collect results and pass through the merger and judge pipeline",
|
|
6785
|
+
"5. Report the final SynthesisResult verdict and findings",
|
|
6786
|
+
"",
|
|
6787
|
+
"When done, call `claudestory_autonomous_guide` with:",
|
|
6788
|
+
"```json",
|
|
6789
|
+
`{ "sessionId": "${ctx.state.sessionId}", "action": "report", "report": { "completedAction": "plan_review_round", "verdict": "<approve|revise|reject>", "findings": [...] } }`,
|
|
6790
|
+
"```"
|
|
6791
|
+
].join("\n"),
|
|
6792
|
+
reminders: [
|
|
6793
|
+
"Report the exact verdict and findings from the synthesizer.",
|
|
6794
|
+
"Lens subagents run in parallel with read-only tools (Read, Grep, Glob)."
|
|
6795
|
+
],
|
|
6796
|
+
transitionedFrom: ctx.state.previousState ?? void 0
|
|
6797
|
+
};
|
|
6798
|
+
}
|
|
6710
6799
|
return {
|
|
6711
6800
|
instruction: [
|
|
6712
6801
|
`# Plan Review \u2014 Round ${roundNum} of ${Math.max(minRounds, roundNum)} minimum`,
|
|
@@ -6768,9 +6857,19 @@ var init_plan_review = __esm({
|
|
|
6768
6857
|
nextAction = "PLAN_REVIEW";
|
|
6769
6858
|
}
|
|
6770
6859
|
const reviewsForWrite = isReject ? { ...ctx.state.reviews, plan: [] } : { ...ctx.state.reviews, plan: planReviews };
|
|
6771
|
-
|
|
6860
|
+
const stateUpdate = {
|
|
6772
6861
|
reviews: reviewsForWrite
|
|
6773
|
-
}
|
|
6862
|
+
};
|
|
6863
|
+
if (reviewerBackend === "lenses" && findings.length > 0) {
|
|
6864
|
+
const updated = buildLensHistoryUpdate(
|
|
6865
|
+
findings,
|
|
6866
|
+
ctx.state.lensReviewHistory ?? [],
|
|
6867
|
+
ctx.state.ticket?.id ?? "unknown",
|
|
6868
|
+
"PLAN_REVIEW"
|
|
6869
|
+
);
|
|
6870
|
+
if (updated) stateUpdate.lensReviewHistory = updated;
|
|
6871
|
+
}
|
|
6872
|
+
ctx.writeState(stateUpdate);
|
|
6774
6873
|
ctx.appendEvent("plan_review", {
|
|
6775
6874
|
round: roundNum,
|
|
6776
6875
|
verdict,
|
|
@@ -7134,13 +7233,42 @@ var init_test = __esm({
|
|
|
7134
7233
|
}
|
|
7135
7234
|
});
|
|
7136
7235
|
|
|
7236
|
+
// src/autonomous/review-lenses/schema-validator.ts
|
|
7237
|
+
var init_schema_validator = __esm({
|
|
7238
|
+
"src/autonomous/review-lenses/schema-validator.ts"() {
|
|
7239
|
+
"use strict";
|
|
7240
|
+
init_esm_shims();
|
|
7241
|
+
}
|
|
7242
|
+
});
|
|
7243
|
+
|
|
7244
|
+
// src/autonomous/review-lenses/cache.ts
|
|
7245
|
+
import { createHash } from "crypto";
|
|
7246
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync9, rmSync as rmSync2, renameSync as renameSync2 } from "fs";
|
|
7247
|
+
import { join as join12 } from "path";
|
|
7248
|
+
function clearCache(sessionDir2) {
|
|
7249
|
+
const dir = join12(sessionDir2, CACHE_DIR);
|
|
7250
|
+
if (!existsSync9(dir)) return;
|
|
7251
|
+
rmSync2(dir, { recursive: true, force: true });
|
|
7252
|
+
}
|
|
7253
|
+
var CACHE_DIR;
|
|
7254
|
+
var init_cache = __esm({
|
|
7255
|
+
"src/autonomous/review-lenses/cache.ts"() {
|
|
7256
|
+
"use strict";
|
|
7257
|
+
init_esm_shims();
|
|
7258
|
+
init_schema_validator();
|
|
7259
|
+
CACHE_DIR = "lens-cache";
|
|
7260
|
+
}
|
|
7261
|
+
});
|
|
7262
|
+
|
|
7137
7263
|
// src/autonomous/stages/code-review.ts
|
|
7138
7264
|
var CodeReviewStage;
|
|
7139
7265
|
var init_code_review = __esm({
|
|
7140
7266
|
"src/autonomous/stages/code-review.ts"() {
|
|
7141
7267
|
"use strict";
|
|
7142
7268
|
init_esm_shims();
|
|
7269
|
+
init_types2();
|
|
7143
7270
|
init_review_depth();
|
|
7271
|
+
init_cache();
|
|
7144
7272
|
CodeReviewStage = class {
|
|
7145
7273
|
id = "CODE_REVIEW";
|
|
7146
7274
|
async enter(ctx) {
|
|
@@ -7153,6 +7281,32 @@ var init_code_review = __esm({
|
|
|
7153
7281
|
const mergeBase = ctx.state.git.mergeBase;
|
|
7154
7282
|
const diffCommand = mergeBase ? `\`git diff ${mergeBase}\`` : `\`git diff HEAD\` AND \`git ls-files --others --exclude-standard\``;
|
|
7155
7283
|
const diffReminder = mergeBase ? `Run: git diff ${mergeBase} \u2014 pass FULL output to reviewer.` : "Run: git diff HEAD + git ls-files --others --exclude-standard \u2014 pass FULL output to reviewer.";
|
|
7284
|
+
if (reviewer === "lenses") {
|
|
7285
|
+
return {
|
|
7286
|
+
instruction: [
|
|
7287
|
+
`# Multi-Lens Code Review \u2014 Round ${roundNum} of ${rounds} minimum`,
|
|
7288
|
+
"",
|
|
7289
|
+
`Capture the diff with: ${diffCommand}`,
|
|
7290
|
+
"",
|
|
7291
|
+
"This round uses the **multi-lens review orchestrator**. It fans out to specialized review agents (Clean Code, Security, Error Handling, and more) in parallel, then synthesizes findings into a single verdict.",
|
|
7292
|
+
"",
|
|
7293
|
+
"1. Capture the full diff",
|
|
7294
|
+
"2. Call `prepareLensReview()` with the diff and changed file list",
|
|
7295
|
+
"3. Spawn all lens subagents in parallel (each prompt is provided by the orchestrator)",
|
|
7296
|
+
"4. Collect results and pass through the merger and judge pipeline",
|
|
7297
|
+
"5. Report the final SynthesisResult verdict and findings",
|
|
7298
|
+
"",
|
|
7299
|
+
"When done, report verdict and findings."
|
|
7300
|
+
].join("\n"),
|
|
7301
|
+
reminders: [
|
|
7302
|
+
diffReminder,
|
|
7303
|
+
"Do NOT compress or summarize the diff.",
|
|
7304
|
+
"Lens subagents run in parallel with read-only tools (Read, Grep, Glob).",
|
|
7305
|
+
"If the reviewer flags pre-existing issues unrelated to your changes, file them as issues using claudestory_issue_create with severity and impact. Do not fix them in this ticket."
|
|
7306
|
+
],
|
|
7307
|
+
transitionedFrom: ctx.state.previousState ?? void 0
|
|
7308
|
+
};
|
|
7309
|
+
}
|
|
7156
7310
|
return {
|
|
7157
7311
|
instruction: [
|
|
7158
7312
|
`# Code Review \u2014 Round ${roundNum} of ${rounds} minimum`,
|
|
@@ -7218,8 +7372,10 @@ var init_code_review = __esm({
|
|
|
7218
7372
|
nextAction = "CODE_REVIEW";
|
|
7219
7373
|
}
|
|
7220
7374
|
if (nextAction === "PLAN") {
|
|
7375
|
+
clearCache(ctx.dir);
|
|
7221
7376
|
ctx.writeState({
|
|
7222
7377
|
reviews: { plan: [], code: [] },
|
|
7378
|
+
lensReviewHistory: [],
|
|
7223
7379
|
ticket: ctx.state.ticket ? { ...ctx.state.ticket, realizedRisk: void 0 } : ctx.state.ticket
|
|
7224
7380
|
});
|
|
7225
7381
|
ctx.appendEvent("code_review", {
|
|
@@ -7231,9 +7387,19 @@ var init_code_review = __esm({
|
|
|
7231
7387
|
await ctx.fileDeferredFindings(findings, "code");
|
|
7232
7388
|
return { action: "back", target: "PLAN", reason: "plan_redirect" };
|
|
7233
7389
|
}
|
|
7234
|
-
|
|
7390
|
+
const stateUpdate = {
|
|
7235
7391
|
reviews: { ...ctx.state.reviews, code: codeReviews }
|
|
7236
|
-
}
|
|
7392
|
+
};
|
|
7393
|
+
if (reviewerBackend === "lenses" && findings.length > 0) {
|
|
7394
|
+
const updated = buildLensHistoryUpdate(
|
|
7395
|
+
findings,
|
|
7396
|
+
ctx.state.lensReviewHistory ?? [],
|
|
7397
|
+
ctx.state.ticket?.id ?? "unknown",
|
|
7398
|
+
"CODE_REVIEW"
|
|
7399
|
+
);
|
|
7400
|
+
if (updated) stateUpdate.lensReviewHistory = updated;
|
|
7401
|
+
}
|
|
7402
|
+
ctx.writeState(stateUpdate);
|
|
7237
7403
|
ctx.appendEvent("code_review", {
|
|
7238
7404
|
round: roundNum,
|
|
7239
7405
|
verdict,
|
|
@@ -7798,7 +7964,7 @@ var init_finalize = __esm({
|
|
|
7798
7964
|
}
|
|
7799
7965
|
});
|
|
7800
7966
|
ctx.appendEvent("commit", { commitHash: normalizedHash, issueId: currentIssue.id });
|
|
7801
|
-
return { action: "goto", target: "
|
|
7967
|
+
return { action: "goto", target: "COMPLETE" };
|
|
7802
7968
|
}
|
|
7803
7969
|
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;
|
|
7804
7970
|
ctx.writeState({
|
|
@@ -7836,6 +8002,8 @@ var init_complete = __esm({
|
|
|
7836
8002
|
finalizeCheckpoint: null
|
|
7837
8003
|
});
|
|
7838
8004
|
const ticketsDone = ctx.state.completedTickets.length;
|
|
8005
|
+
const issuesDone = (ctx.state.resolvedIssues ?? []).length;
|
|
8006
|
+
const totalWorkDone = ticketsDone + issuesDone;
|
|
7839
8007
|
const maxTickets = ctx.state.config.maxTicketsPerSession;
|
|
7840
8008
|
const mode = ctx.state.mode ?? "auto";
|
|
7841
8009
|
if (mode !== "auto") {
|
|
@@ -7856,15 +8024,17 @@ var init_complete = __esm({
|
|
|
7856
8024
|
};
|
|
7857
8025
|
}
|
|
7858
8026
|
const handoverInterval = ctx.state.config.handoverInterval ?? 5;
|
|
7859
|
-
if (handoverInterval > 0 &&
|
|
8027
|
+
if (handoverInterval > 0 && totalWorkDone > 0 && totalWorkDone % handoverInterval === 0) {
|
|
7860
8028
|
try {
|
|
7861
8029
|
const { handleHandoverCreate: handleHandoverCreate3 } = await Promise.resolve().then(() => (init_handover(), handover_exports));
|
|
7862
8030
|
const completedIds = ctx.state.completedTickets.map((t) => t.id).join(", ");
|
|
8031
|
+
const resolvedIds = (ctx.state.resolvedIssues ?? []).join(", ");
|
|
7863
8032
|
const content = [
|
|
7864
|
-
`# Checkpoint \u2014 ${
|
|
8033
|
+
`# Checkpoint \u2014 ${totalWorkDone} items completed`,
|
|
7865
8034
|
"",
|
|
7866
8035
|
`**Session:** ${ctx.state.sessionId}`,
|
|
7867
|
-
`**Tickets:** ${completedIds}
|
|
8036
|
+
...completedIds ? [`**Tickets:** ${completedIds}`] : [],
|
|
8037
|
+
...resolvedIds ? [`**Issues resolved:** ${resolvedIds}`] : [],
|
|
7868
8038
|
"",
|
|
7869
8039
|
"This is an automatic mid-session checkpoint. The session is still active."
|
|
7870
8040
|
].join("\n");
|
|
@@ -7878,24 +8048,19 @@ var init_complete = __esm({
|
|
|
7878
8048
|
await saveSnapshot2(ctx.root, loadResult);
|
|
7879
8049
|
} catch {
|
|
7880
8050
|
}
|
|
7881
|
-
ctx.appendEvent("checkpoint", { ticketsDone, interval: handoverInterval });
|
|
8051
|
+
ctx.appendEvent("checkpoint", { ticketsDone, issuesDone, totalWorkDone, interval: handoverInterval });
|
|
7882
8052
|
}
|
|
8053
|
+
const { state: projectState } = await ctx.loadProject();
|
|
7883
8054
|
let nextTarget;
|
|
7884
|
-
if (maxTickets > 0 &&
|
|
8055
|
+
if (maxTickets > 0 && totalWorkDone >= maxTickets) {
|
|
7885
8056
|
nextTarget = "HANDOVER";
|
|
7886
8057
|
} else {
|
|
7887
|
-
|
|
7888
|
-
|
|
7889
|
-
const { state: projectState } = await ctx.loadProject();
|
|
7890
|
-
const nextResult = nextTickets(projectState, 1);
|
|
7891
|
-
if (nextResult.kind !== "found") {
|
|
7892
|
-
const highIssues = projectState.issues.filter(
|
|
7893
|
-
(i) => i.status === "open" && (i.severity === "critical" || i.severity === "high")
|
|
7894
|
-
);
|
|
7895
|
-
if (highIssues.length > 0) {
|
|
8058
|
+
const nextResult = nextTickets(projectState, 1);
|
|
8059
|
+
if (nextResult.kind === "found") {
|
|
7896
8060
|
nextTarget = "PICK_TICKET";
|
|
7897
8061
|
} else {
|
|
7898
|
-
|
|
8062
|
+
const openIssues = projectState.issues.filter((i) => i.status === "open");
|
|
8063
|
+
nextTarget = openIssues.length > 0 ? "PICK_TICKET" : "HANDOVER";
|
|
7899
8064
|
}
|
|
7900
8065
|
}
|
|
7901
8066
|
if (nextTarget === "HANDOVER") {
|
|
@@ -7910,7 +8075,7 @@ var init_complete = __esm({
|
|
|
7910
8075
|
target: "HANDOVER",
|
|
7911
8076
|
result: {
|
|
7912
8077
|
instruction: [
|
|
7913
|
-
`# Session Complete \u2014 ${ticketsDone} ticket(s) done`,
|
|
8078
|
+
`# Session Complete \u2014 ${ticketsDone} ticket(s) and ${issuesDone} issue(s) done`,
|
|
7914
8079
|
"",
|
|
7915
8080
|
"Write a session handover summarizing what was accomplished, decisions made, and what's next.",
|
|
7916
8081
|
"",
|
|
@@ -7979,6 +8144,7 @@ var init_lesson_capture = __esm({
|
|
|
7979
8144
|
const planReviews = ctx.state.reviews.plan ?? [];
|
|
7980
8145
|
const codeReviews = ctx.state.reviews.code ?? [];
|
|
7981
8146
|
const ticketsDone = ctx.state.completedTickets.length;
|
|
8147
|
+
const issuesDone = (ctx.state.resolvedIssues ?? []).length;
|
|
7982
8148
|
const planFindings = planReviews.reduce((sum, r) => sum + (r.findingCount ?? 0), 0);
|
|
7983
8149
|
const planCritical = planReviews.reduce((sum, r) => sum + (r.criticalCount ?? 0), 0);
|
|
7984
8150
|
const planMajor = planReviews.reduce((sum, r) => sum + (r.majorCount ?? 0), 0);
|
|
@@ -7996,11 +8162,49 @@ var init_lesson_capture = __esm({
|
|
|
7996
8162
|
planFindings,
|
|
7997
8163
|
codeFindings
|
|
7998
8164
|
});
|
|
8165
|
+
const lensHistory = ctx.state.lensReviewHistory ?? [];
|
|
8166
|
+
const dismissed = lensHistory.filter(
|
|
8167
|
+
(f) => f.disposition === "contested"
|
|
8168
|
+
);
|
|
8169
|
+
const tupleCounts = /* @__PURE__ */ new Map();
|
|
8170
|
+
for (const f of lensHistory) {
|
|
8171
|
+
const key = `${f.lens}:${f.category}`;
|
|
8172
|
+
const entry = tupleCounts.get(key) ?? { total: 0, dismissed: 0 };
|
|
8173
|
+
entry.total++;
|
|
8174
|
+
if (f.disposition === "contested") {
|
|
8175
|
+
entry.dismissed++;
|
|
8176
|
+
}
|
|
8177
|
+
tupleCounts.set(key, entry);
|
|
8178
|
+
}
|
|
8179
|
+
const falsePositivePatterns = [];
|
|
8180
|
+
for (const [key, counts] of tupleCounts) {
|
|
8181
|
+
if (counts.total >= 5 && counts.dismissed / counts.total >= 0.6) {
|
|
8182
|
+
falsePositivePatterns.push(
|
|
8183
|
+
`- **${key}**: ${counts.dismissed}/${counts.total} dismissed (${Math.round(counts.dismissed / counts.total * 100)}%)`
|
|
8184
|
+
);
|
|
8185
|
+
}
|
|
8186
|
+
}
|
|
8187
|
+
const lensSection = falsePositivePatterns.length > 0 ? [
|
|
8188
|
+
"",
|
|
8189
|
+
"## Lens False Positive Patterns",
|
|
8190
|
+
"",
|
|
8191
|
+
"These (lens, category) tuples have a >60% dismissal rate over 5+ reviews. Create a lesson for each:",
|
|
8192
|
+
...falsePositivePatterns,
|
|
8193
|
+
"",
|
|
8194
|
+
'For each pattern above, create a lesson: "Lens X tends to flag category Y, but this project considers it acceptable because [reason from dismiss history]."',
|
|
8195
|
+
'Tag with `source: "lens-feedback"` and `tags: ["lens", lens-name]`.'
|
|
8196
|
+
].join("\n") : "";
|
|
8197
|
+
const dismissedSection = dismissed.length > 0 && falsePositivePatterns.length === 0 ? [
|
|
8198
|
+
"",
|
|
8199
|
+
`## Dismissed Lens Findings (${dismissed.length})`,
|
|
8200
|
+
"",
|
|
8201
|
+
"Some lens findings were dismissed this session. Not enough data for automatic patterns yet (need 5+ reviews per tuple at >60% dismissal rate)."
|
|
8202
|
+
].join("\n") : "";
|
|
7999
8203
|
return {
|
|
8000
8204
|
instruction: [
|
|
8001
8205
|
"# Capture Lessons from Review Findings",
|
|
8002
8206
|
"",
|
|
8003
|
-
`This session completed ${ticketsDone} ticket(s). Review summary:`,
|
|
8207
|
+
`This session completed ${ticketsDone} ticket(s) and ${issuesDone} issue(s). Review summary:`,
|
|
8004
8208
|
`- **Plan reviews:** ${planReviews.length} round(s), ${planCritical} critical, ${planMajor} major, ${planFindings} total findings`,
|
|
8005
8209
|
`- **Code reviews:** ${codeReviews.length} round(s), ${codeCritical} critical, ${codeMajor} major, ${codeFindings} total findings`,
|
|
8006
8210
|
"",
|
|
@@ -8011,15 +8215,18 @@ var init_lesson_capture = __esm({
|
|
|
8011
8215
|
" - If it matches an existing lesson \u2192 call `claudestory_lesson_reinforce`",
|
|
8012
8216
|
' - If it\'s a new pattern \u2192 call `claudestory_lesson_create` with `source: "review"`',
|
|
8013
8217
|
"3. Skip patterns that are one-off or already well-covered",
|
|
8218
|
+
lensSection,
|
|
8219
|
+
dismissedSection,
|
|
8014
8220
|
`4. Call \`claudestory_autonomous_guide\` with completedAction: "lessons_captured"`,
|
|
8015
8221
|
"",
|
|
8016
8222
|
"```json",
|
|
8017
8223
|
`{ "sessionId": "${ctx.state.sessionId}", "action": "report", "report": { "completedAction": "lessons_captured" } }`,
|
|
8018
8224
|
"```"
|
|
8019
|
-
].join("\n"),
|
|
8225
|
+
].filter(Boolean).join("\n"),
|
|
8020
8226
|
reminders: [
|
|
8021
8227
|
"Check existing lessons first \u2014 reinforce before creating duplicates.",
|
|
8022
|
-
"Only capture patterns worth remembering across sessions."
|
|
8228
|
+
"Only capture patterns worth remembering across sessions.",
|
|
8229
|
+
...falsePositivePatterns.length > 0 ? ["Lens false positive patterns MUST be captured as lessons \u2014 they improve future reviews."] : []
|
|
8023
8230
|
]
|
|
8024
8231
|
};
|
|
8025
8232
|
}
|
|
@@ -8218,8 +8425,8 @@ Impact: ${nextIssue.impact}` : ""}` : `Fix issue ${next}.`,
|
|
|
8218
8425
|
});
|
|
8219
8426
|
|
|
8220
8427
|
// src/autonomous/stages/handover.ts
|
|
8221
|
-
import { writeFileSync as
|
|
8222
|
-
import { join as
|
|
8428
|
+
import { writeFileSync as writeFileSync3 } from "fs";
|
|
8429
|
+
import { join as join13 } from "path";
|
|
8223
8430
|
var HandoverStage;
|
|
8224
8431
|
var init_handover2 = __esm({
|
|
8225
8432
|
"src/autonomous/stages/handover.ts"() {
|
|
@@ -8231,9 +8438,10 @@ var init_handover2 = __esm({
|
|
|
8231
8438
|
id = "HANDOVER";
|
|
8232
8439
|
async enter(ctx) {
|
|
8233
8440
|
const ticketsDone = ctx.state.completedTickets.length;
|
|
8441
|
+
const issuesDone = (ctx.state.resolvedIssues ?? []).length;
|
|
8234
8442
|
return {
|
|
8235
8443
|
instruction: [
|
|
8236
|
-
`# Session Complete \u2014 ${ticketsDone} ticket(s) done`,
|
|
8444
|
+
`# Session Complete \u2014 ${ticketsDone} ticket(s) and ${issuesDone} issue(s) done`,
|
|
8237
8445
|
"",
|
|
8238
8446
|
"Write a session handover summarizing what was accomplished, decisions made, and what's next.",
|
|
8239
8447
|
"",
|
|
@@ -8256,8 +8464,8 @@ var init_handover2 = __esm({
|
|
|
8256
8464
|
} catch {
|
|
8257
8465
|
handoverFailed = true;
|
|
8258
8466
|
try {
|
|
8259
|
-
const fallbackPath =
|
|
8260
|
-
|
|
8467
|
+
const fallbackPath = join13(ctx.dir, "handover-fallback.md");
|
|
8468
|
+
writeFileSync3(fallbackPath, content, "utf-8");
|
|
8261
8469
|
} catch {
|
|
8262
8470
|
}
|
|
8263
8471
|
}
|
|
@@ -8280,18 +8488,22 @@ var init_handover2 = __esm({
|
|
|
8280
8488
|
});
|
|
8281
8489
|
ctx.appendEvent("session_end", {
|
|
8282
8490
|
ticketsCompleted: ctx.state.completedTickets.length,
|
|
8491
|
+
issuesResolved: (ctx.state.resolvedIssues ?? []).length,
|
|
8283
8492
|
handoverFailed
|
|
8284
8493
|
});
|
|
8285
8494
|
const ticketsDone = ctx.state.completedTickets.length;
|
|
8495
|
+
const issuesDone = (ctx.state.resolvedIssues ?? []).length;
|
|
8496
|
+
const resolvedList = (ctx.state.resolvedIssues ?? []).map((id) => `- ${id} (resolved)`).join("\n");
|
|
8286
8497
|
return {
|
|
8287
8498
|
action: "advance",
|
|
8288
8499
|
result: {
|
|
8289
8500
|
instruction: [
|
|
8290
8501
|
"# Session Complete",
|
|
8291
8502
|
"",
|
|
8292
|
-
`${ticketsDone} ticket(s) completed.${handoverFailed ? " Handover creation failed \u2014 fallback saved to session directory." : " Handover written."}${stashPopFailed ? " Auto-stash pop failed \u2014 run `git stash pop` manually." : ""} Session ended.`,
|
|
8503
|
+
`${ticketsDone} ticket(s) and ${issuesDone} issue(s) completed.${handoverFailed ? " Handover creation failed \u2014 fallback saved to session directory." : " Handover written."}${stashPopFailed ? " Auto-stash pop failed \u2014 run `git stash pop` manually." : ""} Session ended.`,
|
|
8293
8504
|
"",
|
|
8294
|
-
ctx.state.completedTickets.map((t) => `- ${t.id}${t.title ? `: ${t.title}` : ""} (${t.commitHash ?? "no commit"})`).join("\n")
|
|
8505
|
+
ctx.state.completedTickets.map((t) => `- ${t.id}${t.title ? `: ${t.title}` : ""} (${t.commitHash ?? "no commit"})`).join("\n"),
|
|
8506
|
+
...resolvedList ? [resolvedList] : []
|
|
8295
8507
|
].join("\n"),
|
|
8296
8508
|
reminders: [],
|
|
8297
8509
|
transitionedFrom: "HANDOVER"
|
|
@@ -8342,8 +8554,8 @@ var init_stages = __esm({
|
|
|
8342
8554
|
});
|
|
8343
8555
|
|
|
8344
8556
|
// src/autonomous/version-check.ts
|
|
8345
|
-
import { readFileSync as
|
|
8346
|
-
import { join as
|
|
8557
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
8558
|
+
import { join as join14, dirname as dirname4 } from "path";
|
|
8347
8559
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8348
8560
|
function checkVersionMismatch(runningVersion, installedVersion) {
|
|
8349
8561
|
if (!installedVersion) return null;
|
|
@@ -8355,12 +8567,12 @@ function getInstalledVersion() {
|
|
|
8355
8567
|
try {
|
|
8356
8568
|
const thisFile = fileURLToPath3(import.meta.url);
|
|
8357
8569
|
const candidates = [
|
|
8358
|
-
|
|
8359
|
-
|
|
8570
|
+
join14(dirname4(thisFile), "..", "..", "package.json"),
|
|
8571
|
+
join14(dirname4(thisFile), "..", "package.json")
|
|
8360
8572
|
];
|
|
8361
8573
|
for (const candidate of candidates) {
|
|
8362
8574
|
try {
|
|
8363
|
-
const raw =
|
|
8575
|
+
const raw = readFileSync7(candidate, "utf-8");
|
|
8364
8576
|
const pkg = JSON.parse(raw);
|
|
8365
8577
|
if (pkg.version) return pkg.version;
|
|
8366
8578
|
} catch {
|
|
@@ -8372,7 +8584,7 @@ function getInstalledVersion() {
|
|
|
8372
8584
|
}
|
|
8373
8585
|
}
|
|
8374
8586
|
function getRunningVersion() {
|
|
8375
|
-
return "0.1.
|
|
8587
|
+
return "0.1.59";
|
|
8376
8588
|
}
|
|
8377
8589
|
var init_version_check = __esm({
|
|
8378
8590
|
"src/autonomous/version-check.ts"() {
|
|
@@ -8382,23 +8594,23 @@ var init_version_check = __esm({
|
|
|
8382
8594
|
});
|
|
8383
8595
|
|
|
8384
8596
|
// src/autonomous/guide.ts
|
|
8385
|
-
import { readFileSync as
|
|
8386
|
-
import { join as
|
|
8597
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, readdirSync as readdirSync4 } from "fs";
|
|
8598
|
+
import { join as join15 } from "path";
|
|
8387
8599
|
function buildGuideRecommendOptions(root) {
|
|
8388
8600
|
const opts = {};
|
|
8389
8601
|
try {
|
|
8390
|
-
const handoversDir =
|
|
8602
|
+
const handoversDir = join15(root, ".story", "handovers");
|
|
8391
8603
|
const files = readdirSync4(handoversDir, "utf-8").filter((f) => f.endsWith(".md")).sort();
|
|
8392
8604
|
if (files.length > 0) {
|
|
8393
|
-
opts.latestHandoverContent =
|
|
8605
|
+
opts.latestHandoverContent = readFileSync8(join15(handoversDir, files[files.length - 1]), "utf-8");
|
|
8394
8606
|
}
|
|
8395
8607
|
} catch {
|
|
8396
8608
|
}
|
|
8397
8609
|
try {
|
|
8398
|
-
const snapshotsDir =
|
|
8610
|
+
const snapshotsDir = join15(root, ".story", "snapshots");
|
|
8399
8611
|
const snapFiles = readdirSync4(snapshotsDir, "utf-8").filter((f) => f.endsWith(".json")).sort();
|
|
8400
8612
|
if (snapFiles.length > 0) {
|
|
8401
|
-
const raw =
|
|
8613
|
+
const raw = readFileSync8(join15(snapshotsDir, snapFiles[snapFiles.length - 1]), "utf-8");
|
|
8402
8614
|
const snap = JSON.parse(raw);
|
|
8403
8615
|
if (snap.issues) {
|
|
8404
8616
|
opts.previousOpenIssueCount = snap.issues.filter((i) => i.status !== "resolved").length;
|
|
@@ -8780,7 +8992,7 @@ Staged: ${stagedResult.data.join(", ")}`
|
|
|
8780
8992
|
}
|
|
8781
8993
|
}
|
|
8782
8994
|
const { state: projectState, warnings } = await loadProject(root);
|
|
8783
|
-
const handoversDir =
|
|
8995
|
+
const handoversDir = join15(root, ".story", "handovers");
|
|
8784
8996
|
const ctx = { state: projectState, warnings, root, handoversDir, format: "md" };
|
|
8785
8997
|
let handoverText = "";
|
|
8786
8998
|
try {
|
|
@@ -8797,7 +9009,7 @@ Staged: ${stagedResult.data.join(", ")}`
|
|
|
8797
9009
|
}
|
|
8798
9010
|
} catch {
|
|
8799
9011
|
}
|
|
8800
|
-
const rulesText = readFileSafe2(
|
|
9012
|
+
const rulesText = readFileSafe2(join15(root, "RULES.md"));
|
|
8801
9013
|
const lessonDigest = buildLessonDigest(projectState.lessons);
|
|
8802
9014
|
const digestParts = [
|
|
8803
9015
|
handoverText ? `## Recent Handovers
|
|
@@ -8813,7 +9025,7 @@ ${rulesText}` : "",
|
|
|
8813
9025
|
].filter(Boolean);
|
|
8814
9026
|
const digest = digestParts.join("\n\n---\n\n");
|
|
8815
9027
|
try {
|
|
8816
|
-
|
|
9028
|
+
writeFileSync4(join15(dir, "context-digest.md"), digest, "utf-8");
|
|
8817
9029
|
} catch {
|
|
8818
9030
|
}
|
|
8819
9031
|
if (mode !== "auto" && args.ticketId) {
|
|
@@ -9318,7 +9530,7 @@ ${driftPreamble}Recovered to state: **${mapping.state}**. Continue from here.`,
|
|
|
9318
9530
|
instruction: [
|
|
9319
9531
|
"# Resumed After Compact \u2014 Continue Working",
|
|
9320
9532
|
"",
|
|
9321
|
-
`${written.completedTickets.length} ticket(s) done so far. Context compacted. Pick the next ticket immediately.`,
|
|
9533
|
+
`${written.completedTickets.length} ticket(s) and ${(written.resolvedIssues ?? []).length} issue(s) done so far. Context compacted. Pick the next ticket or issue immediately.`,
|
|
9322
9534
|
"",
|
|
9323
9535
|
candidatesText,
|
|
9324
9536
|
"",
|
|
@@ -9434,7 +9646,8 @@ async function handleCancel(root, args) {
|
|
|
9434
9646
|
return guideError(new Error("Session already ended."));
|
|
9435
9647
|
}
|
|
9436
9648
|
const isAutoMode = info.state.mode === "auto" || !info.state.mode;
|
|
9437
|
-
const
|
|
9649
|
+
const totalDone = info.state.completedTickets.length + (info.state.resolvedIssues?.length ?? 0);
|
|
9650
|
+
const hasTicketsRemaining = info.state.config.maxTicketsPerSession === 0 || totalDone < info.state.config.maxTicketsPerSession;
|
|
9438
9651
|
const isWorkingState = !["SESSION_END", "HANDOVER", "COMPACT"].includes(info.state.state);
|
|
9439
9652
|
if (isAutoMode && hasTicketsRemaining && isWorkingState) {
|
|
9440
9653
|
return {
|
|
@@ -9443,7 +9656,7 @@ async function handleCancel(root, args) {
|
|
|
9443
9656
|
text: [
|
|
9444
9657
|
"# Cancel Rejected \u2014 Session Still Active",
|
|
9445
9658
|
"",
|
|
9446
|
-
`You have completed ${info.state.completedTickets.length} ticket(s) with more work remaining.`,
|
|
9659
|
+
`You have completed ${info.state.completedTickets.length} ticket(s) and ${(info.state.resolvedIssues ?? []).length} issue(s) with more work remaining.`,
|
|
9447
9660
|
"Do NOT cancel an autonomous session due to context size.",
|
|
9448
9661
|
"If you need to manage context, Claude Code handles compaction automatically.",
|
|
9449
9662
|
"",
|
|
@@ -9511,14 +9724,14 @@ async function handleCancel(root, args) {
|
|
|
9511
9724
|
});
|
|
9512
9725
|
const stashNote = stashPopFailed ? " Auto-stash pop failed \u2014 run `git stash pop` manually." : "";
|
|
9513
9726
|
return {
|
|
9514
|
-
content: [{ type: "text", text: `Session ${args.sessionId} cancelled. ${written.completedTickets.length} ticket(s) were completed.${stashNote}` }]
|
|
9727
|
+
content: [{ type: "text", text: `Session ${args.sessionId} cancelled. ${written.completedTickets.length} ticket(s) and ${(written.resolvedIssues ?? []).length} issue(s) were completed.${stashNote}` }]
|
|
9515
9728
|
};
|
|
9516
9729
|
}
|
|
9517
9730
|
function guideResult(state, currentState, opts) {
|
|
9518
9731
|
const summary = {
|
|
9519
9732
|
ticket: state.ticket ? `${state.ticket.id}: ${state.ticket.title}` : "none",
|
|
9520
9733
|
risk: state.ticket?.risk ?? "unknown",
|
|
9521
|
-
completed: state.completedTickets.map((t) => t.id),
|
|
9734
|
+
completed: [...state.completedTickets.map((t) => t.id), ...state.resolvedIssues ?? []],
|
|
9522
9735
|
currentStep: currentState,
|
|
9523
9736
|
contextPressure: state.contextPressure?.level ?? "low",
|
|
9524
9737
|
branch: state.git?.branch ?? null
|
|
@@ -9564,7 +9777,7 @@ function guideError(err) {
|
|
|
9564
9777
|
}
|
|
9565
9778
|
function readFileSafe2(path2) {
|
|
9566
9779
|
try {
|
|
9567
|
-
return
|
|
9780
|
+
return readFileSync8(path2, "utf-8");
|
|
9568
9781
|
} catch {
|
|
9569
9782
|
return "";
|
|
9570
9783
|
}
|
|
@@ -9817,8 +10030,8 @@ var init_session_report_formatter = __esm({
|
|
|
9817
10030
|
});
|
|
9818
10031
|
|
|
9819
10032
|
// src/cli/commands/session-report.ts
|
|
9820
|
-
import { readFileSync as
|
|
9821
|
-
import { join as
|
|
10033
|
+
import { readFileSync as readFileSync9, existsSync as existsSync11 } from "fs";
|
|
10034
|
+
import { join as join16 } from "path";
|
|
9822
10035
|
async function handleSessionReport(sessionId, root, format = "md") {
|
|
9823
10036
|
if (!UUID_REGEX.test(sessionId)) {
|
|
9824
10037
|
return {
|
|
@@ -9829,7 +10042,7 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9829
10042
|
};
|
|
9830
10043
|
}
|
|
9831
10044
|
const dir = sessionDir(root, sessionId);
|
|
9832
|
-
if (!
|
|
10045
|
+
if (!existsSync11(dir)) {
|
|
9833
10046
|
return {
|
|
9834
10047
|
output: `Error: Session ${sessionId} not found.`,
|
|
9835
10048
|
exitCode: 1,
|
|
@@ -9837,8 +10050,8 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9837
10050
|
isError: true
|
|
9838
10051
|
};
|
|
9839
10052
|
}
|
|
9840
|
-
const statePath2 =
|
|
9841
|
-
if (!
|
|
10053
|
+
const statePath2 = join16(dir, "state.json");
|
|
10054
|
+
if (!existsSync11(statePath2)) {
|
|
9842
10055
|
return {
|
|
9843
10056
|
output: `Error: Session ${sessionId} corrupt \u2014 state.json missing.`,
|
|
9844
10057
|
exitCode: 1,
|
|
@@ -9847,7 +10060,7 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9847
10060
|
};
|
|
9848
10061
|
}
|
|
9849
10062
|
try {
|
|
9850
|
-
const rawJson = JSON.parse(
|
|
10063
|
+
const rawJson = JSON.parse(readFileSync9(statePath2, "utf-8"));
|
|
9851
10064
|
if (rawJson && typeof rawJson === "object" && "schemaVersion" in rawJson && rawJson.schemaVersion !== CURRENT_SESSION_SCHEMA_VERSION) {
|
|
9852
10065
|
return {
|
|
9853
10066
|
output: `Error: Session ${sessionId} \u2014 unsupported session schema version ${rawJson.schemaVersion}.`,
|
|
@@ -9876,7 +10089,7 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9876
10089
|
const events = readEvents(dir);
|
|
9877
10090
|
let planContent = null;
|
|
9878
10091
|
try {
|
|
9879
|
-
planContent =
|
|
10092
|
+
planContent = readFileSync9(join16(dir, "plan.md"), "utf-8");
|
|
9880
10093
|
} catch {
|
|
9881
10094
|
}
|
|
9882
10095
|
let gitLog = null;
|
|
@@ -9905,7 +10118,7 @@ var init_session_report = __esm({
|
|
|
9905
10118
|
});
|
|
9906
10119
|
|
|
9907
10120
|
// src/cli/commands/phase.ts
|
|
9908
|
-
import { join as
|
|
10121
|
+
import { join as join17, resolve as resolve6 } from "path";
|
|
9909
10122
|
function validatePhaseId(id) {
|
|
9910
10123
|
if (id.length > PHASE_ID_MAX_LENGTH) {
|
|
9911
10124
|
throw new CliValidationError("invalid_input", `Phase ID "${id}" exceeds ${PHASE_ID_MAX_LENGTH} characters`);
|
|
@@ -10094,21 +10307,21 @@ async function handlePhaseDelete(id, reassign, format, root) {
|
|
|
10094
10307
|
const updated = { ...ticket, phase: reassign, order: maxOrder };
|
|
10095
10308
|
const parsed = TicketSchema.parse(updated);
|
|
10096
10309
|
const content = serializeJSON(parsed);
|
|
10097
|
-
const target =
|
|
10310
|
+
const target = join17(wrapDir, "tickets", `${parsed.id}.json`);
|
|
10098
10311
|
operations.push({ op: "write", target, content });
|
|
10099
10312
|
}
|
|
10100
10313
|
for (const issue of affectedIssues) {
|
|
10101
10314
|
const updated = { ...issue, phase: reassign };
|
|
10102
10315
|
const parsed = IssueSchema.parse(updated);
|
|
10103
10316
|
const content = serializeJSON(parsed);
|
|
10104
|
-
const target =
|
|
10317
|
+
const target = join17(wrapDir, "issues", `${parsed.id}.json`);
|
|
10105
10318
|
operations.push({ op: "write", target, content });
|
|
10106
10319
|
}
|
|
10107
10320
|
const newPhases = state.roadmap.phases.filter((p) => p.id !== id);
|
|
10108
10321
|
const newRoadmap = { ...state.roadmap, phases: newPhases };
|
|
10109
10322
|
const parsedRoadmap = RoadmapSchema.parse(newRoadmap);
|
|
10110
10323
|
const roadmapContent = serializeJSON(parsedRoadmap);
|
|
10111
|
-
const roadmapTarget =
|
|
10324
|
+
const roadmapTarget = join17(wrapDir, "roadmap.json");
|
|
10112
10325
|
operations.push({ op: "write", target: roadmapTarget, content: roadmapContent });
|
|
10113
10326
|
await runTransactionUnlocked(root, operations);
|
|
10114
10327
|
} else {
|
|
@@ -10141,14 +10354,14 @@ var init_phase = __esm({
|
|
|
10141
10354
|
|
|
10142
10355
|
// src/mcp/tools.ts
|
|
10143
10356
|
import { z as z10 } from "zod";
|
|
10144
|
-
import { join as
|
|
10357
|
+
import { join as join18 } from "path";
|
|
10145
10358
|
function formatMcpError(code, message) {
|
|
10146
10359
|
return `[${code}] ${message}`;
|
|
10147
10360
|
}
|
|
10148
10361
|
async function runMcpReadTool(pinnedRoot, handler) {
|
|
10149
10362
|
try {
|
|
10150
10363
|
const { state, warnings } = await loadProject(pinnedRoot);
|
|
10151
|
-
const handoversDir =
|
|
10364
|
+
const handoversDir = join18(pinnedRoot, ".story", "handovers");
|
|
10152
10365
|
const ctx = { state, warnings, root: pinnedRoot, handoversDir, format: "md" };
|
|
10153
10366
|
const result = await handler(ctx);
|
|
10154
10367
|
if (result.errorCode && INFRASTRUCTURE_ERROR_CODES.includes(result.errorCode)) {
|
|
@@ -10736,10 +10949,10 @@ var init_tools = __esm({
|
|
|
10736
10949
|
|
|
10737
10950
|
// src/core/init.ts
|
|
10738
10951
|
import { mkdir as mkdir4, stat as stat2, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
|
|
10739
|
-
import { join as
|
|
10952
|
+
import { join as join19, resolve as resolve7 } from "path";
|
|
10740
10953
|
async function initProject(root, options) {
|
|
10741
10954
|
const absRoot = resolve7(root);
|
|
10742
|
-
const wrapDir =
|
|
10955
|
+
const wrapDir = join19(absRoot, ".story");
|
|
10743
10956
|
let exists = false;
|
|
10744
10957
|
try {
|
|
10745
10958
|
const s = await stat2(wrapDir);
|
|
@@ -10759,11 +10972,11 @@ async function initProject(root, options) {
|
|
|
10759
10972
|
".story/ already exists. Use --force to overwrite config and roadmap."
|
|
10760
10973
|
);
|
|
10761
10974
|
}
|
|
10762
|
-
await mkdir4(
|
|
10763
|
-
await mkdir4(
|
|
10764
|
-
await mkdir4(
|
|
10765
|
-
await mkdir4(
|
|
10766
|
-
await mkdir4(
|
|
10975
|
+
await mkdir4(join19(wrapDir, "tickets"), { recursive: true });
|
|
10976
|
+
await mkdir4(join19(wrapDir, "issues"), { recursive: true });
|
|
10977
|
+
await mkdir4(join19(wrapDir, "handovers"), { recursive: true });
|
|
10978
|
+
await mkdir4(join19(wrapDir, "notes"), { recursive: true });
|
|
10979
|
+
await mkdir4(join19(wrapDir, "lessons"), { recursive: true });
|
|
10767
10980
|
const created = [
|
|
10768
10981
|
".story/config.json",
|
|
10769
10982
|
".story/roadmap.json",
|
|
@@ -10803,7 +11016,7 @@ async function initProject(root, options) {
|
|
|
10803
11016
|
};
|
|
10804
11017
|
await writeConfig(config, absRoot);
|
|
10805
11018
|
await writeRoadmap(roadmap, absRoot);
|
|
10806
|
-
const gitignorePath =
|
|
11019
|
+
const gitignorePath = join19(wrapDir, ".gitignore");
|
|
10807
11020
|
await ensureGitignoreEntries(gitignorePath, STORY_GITIGNORE_ENTRIES);
|
|
10808
11021
|
const warnings = [];
|
|
10809
11022
|
if (options.force && exists) {
|
|
@@ -10850,8 +11063,8 @@ var init_init = __esm({
|
|
|
10850
11063
|
|
|
10851
11064
|
// src/mcp/index.ts
|
|
10852
11065
|
var mcp_exports = {};
|
|
10853
|
-
import { realpathSync as realpathSync2, existsSync as
|
|
10854
|
-
import { resolve as resolve8, join as
|
|
11066
|
+
import { realpathSync as realpathSync2, existsSync as existsSync12 } from "fs";
|
|
11067
|
+
import { resolve as resolve8, join as join20, isAbsolute } from "path";
|
|
10855
11068
|
import { z as z11 } from "zod";
|
|
10856
11069
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10857
11070
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -10866,7 +11079,7 @@ function tryDiscoverRoot() {
|
|
|
10866
11079
|
const resolved = resolve8(envRoot);
|
|
10867
11080
|
try {
|
|
10868
11081
|
const canonical = realpathSync2(resolved);
|
|
10869
|
-
if (
|
|
11082
|
+
if (existsSync12(join20(canonical, CONFIG_PATH2))) {
|
|
10870
11083
|
return canonical;
|
|
10871
11084
|
}
|
|
10872
11085
|
process.stderr.write(`Warning: No .story/config.json at ${canonical}
|
|
@@ -10969,7 +11182,7 @@ var init_mcp = __esm({
|
|
|
10969
11182
|
init_init();
|
|
10970
11183
|
ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
|
|
10971
11184
|
CONFIG_PATH2 = ".story/config.json";
|
|
10972
|
-
version = "0.1.
|
|
11185
|
+
version = "0.1.59";
|
|
10973
11186
|
main().catch((err) => {
|
|
10974
11187
|
process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
|
|
10975
11188
|
`);
|
|
@@ -11005,7 +11218,7 @@ __export(run_exports, {
|
|
|
11005
11218
|
runReadCommand: () => runReadCommand,
|
|
11006
11219
|
writeOutput: () => writeOutput
|
|
11007
11220
|
});
|
|
11008
|
-
import { join as
|
|
11221
|
+
import { join as join21 } from "path";
|
|
11009
11222
|
function writeOutput(text) {
|
|
11010
11223
|
try {
|
|
11011
11224
|
process.stdout.write(text + "\n");
|
|
@@ -11033,7 +11246,7 @@ async function runReadCommand(format, handler) {
|
|
|
11033
11246
|
return;
|
|
11034
11247
|
}
|
|
11035
11248
|
const { state, warnings } = await loadProject(root);
|
|
11036
|
-
const handoversDir =
|
|
11249
|
+
const handoversDir = join21(root, ".story", "handovers");
|
|
11037
11250
|
const result = await handler({ state, warnings, root, handoversDir, format });
|
|
11038
11251
|
writeOutput(result.output);
|
|
11039
11252
|
let exitCode = result.exitCode ?? ExitCode.OK;
|
|
@@ -11068,7 +11281,7 @@ async function runDeleteCommand(format, force, handler) {
|
|
|
11068
11281
|
return;
|
|
11069
11282
|
}
|
|
11070
11283
|
const { state, warnings } = await loadProject(root);
|
|
11071
|
-
const handoversDir =
|
|
11284
|
+
const handoversDir = join21(root, ".story", "handovers");
|
|
11072
11285
|
if (!force && hasIntegrityWarnings(warnings)) {
|
|
11073
11286
|
writeOutput(
|
|
11074
11287
|
formatError(
|
|
@@ -11582,8 +11795,8 @@ __export(setup_skill_exports, {
|
|
|
11582
11795
|
resolveSkillSourceDir: () => resolveSkillSourceDir
|
|
11583
11796
|
});
|
|
11584
11797
|
import { mkdir as mkdir5, writeFile as writeFile3, readFile as readFile5, readdir as readdir4, copyFile, rm, rename as rename2, unlink as unlink3 } from "fs/promises";
|
|
11585
|
-
import { existsSync as
|
|
11586
|
-
import { join as
|
|
11798
|
+
import { existsSync as existsSync13 } from "fs";
|
|
11799
|
+
import { join as join22, dirname as dirname5 } from "path";
|
|
11587
11800
|
import { homedir } from "os";
|
|
11588
11801
|
import { execFileSync } from "child_process";
|
|
11589
11802
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -11592,10 +11805,10 @@ function log(msg) {
|
|
|
11592
11805
|
}
|
|
11593
11806
|
function resolveSkillSourceDir() {
|
|
11594
11807
|
const thisDir = dirname5(fileURLToPath4(import.meta.url));
|
|
11595
|
-
const bundledPath =
|
|
11596
|
-
if (
|
|
11597
|
-
const sourcePath =
|
|
11598
|
-
if (
|
|
11808
|
+
const bundledPath = join22(thisDir, "..", "src", "skill");
|
|
11809
|
+
if (existsSync13(join22(bundledPath, "SKILL.md"))) return bundledPath;
|
|
11810
|
+
const sourcePath = join22(thisDir, "..", "..", "skill");
|
|
11811
|
+
if (existsSync13(join22(sourcePath, "SKILL.md"))) return sourcePath;
|
|
11599
11812
|
throw new Error(
|
|
11600
11813
|
`Cannot find bundled skill files. Checked:
|
|
11601
11814
|
${bundledPath}
|
|
@@ -11605,31 +11818,31 @@ function resolveSkillSourceDir() {
|
|
|
11605
11818
|
async function copyDirRecursive(srcDir, destDir) {
|
|
11606
11819
|
const tmpDir = destDir + ".tmp";
|
|
11607
11820
|
const bakDir = destDir + ".bak";
|
|
11608
|
-
if (!
|
|
11821
|
+
if (!existsSync13(destDir) && existsSync13(bakDir)) {
|
|
11609
11822
|
await rename2(bakDir, destDir);
|
|
11610
11823
|
}
|
|
11611
|
-
if (
|
|
11612
|
-
if (
|
|
11824
|
+
if (existsSync13(tmpDir)) await rm(tmpDir, { recursive: true, force: true });
|
|
11825
|
+
if (existsSync13(bakDir)) await rm(bakDir, { recursive: true, force: true });
|
|
11613
11826
|
await mkdir5(tmpDir, { recursive: true });
|
|
11614
11827
|
const entries = await readdir4(srcDir, { withFileTypes: true, recursive: true });
|
|
11615
11828
|
const written = [];
|
|
11616
11829
|
for (const entry of entries) {
|
|
11617
11830
|
if (!entry.isFile()) continue;
|
|
11618
11831
|
const parent = entry.parentPath ?? entry.path ?? srcDir;
|
|
11619
|
-
const relativePath =
|
|
11620
|
-
const srcPath =
|
|
11621
|
-
const destPath =
|
|
11832
|
+
const relativePath = join22(parent, entry.name).slice(srcDir.length + 1);
|
|
11833
|
+
const srcPath = join22(srcDir, relativePath);
|
|
11834
|
+
const destPath = join22(tmpDir, relativePath);
|
|
11622
11835
|
await mkdir5(dirname5(destPath), { recursive: true });
|
|
11623
11836
|
await copyFile(srcPath, destPath);
|
|
11624
11837
|
written.push(relativePath);
|
|
11625
11838
|
}
|
|
11626
|
-
if (
|
|
11839
|
+
if (existsSync13(destDir)) {
|
|
11627
11840
|
await rename2(destDir, bakDir);
|
|
11628
11841
|
}
|
|
11629
11842
|
try {
|
|
11630
11843
|
await rename2(tmpDir, destDir);
|
|
11631
11844
|
} catch (err) {
|
|
11632
|
-
if (
|
|
11845
|
+
if (existsSync13(bakDir)) await rename2(bakDir, destDir).catch(() => {
|
|
11633
11846
|
});
|
|
11634
11847
|
await rm(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
11635
11848
|
});
|
|
@@ -11645,9 +11858,9 @@ function isHookWithCommand(entry, command) {
|
|
|
11645
11858
|
return e.type === "command" && typeof e.command === "string" && e.command.trim() === command;
|
|
11646
11859
|
}
|
|
11647
11860
|
async function registerHook(hookType, hookEntry, settingsPath, matcher) {
|
|
11648
|
-
const path2 = settingsPath ??
|
|
11861
|
+
const path2 = settingsPath ?? join22(homedir(), ".claude", "settings.json");
|
|
11649
11862
|
let raw = "{}";
|
|
11650
|
-
if (
|
|
11863
|
+
if (existsSync13(path2)) {
|
|
11651
11864
|
try {
|
|
11652
11865
|
raw = await readFile5(path2, "utf-8");
|
|
11653
11866
|
} catch {
|
|
@@ -11743,9 +11956,9 @@ async function registerStopHook(settingsPath) {
|
|
|
11743
11956
|
return registerHook("Stop", { type: "command", command: STOP_HOOK_COMMAND, async: true }, settingsPath);
|
|
11744
11957
|
}
|
|
11745
11958
|
async function removeHook(hookType, command, settingsPath) {
|
|
11746
|
-
const path2 = settingsPath ??
|
|
11959
|
+
const path2 = settingsPath ?? join22(homedir(), ".claude", "settings.json");
|
|
11747
11960
|
let raw = "{}";
|
|
11748
|
-
if (
|
|
11961
|
+
if (existsSync13(path2)) {
|
|
11749
11962
|
try {
|
|
11750
11963
|
raw = await readFile5(path2, "utf-8");
|
|
11751
11964
|
} catch {
|
|
@@ -11790,7 +12003,7 @@ async function removeHook(hookType, command, settingsPath) {
|
|
|
11790
12003
|
}
|
|
11791
12004
|
async function handleSetupSkill(options = {}) {
|
|
11792
12005
|
const { skipHooks = false } = options;
|
|
11793
|
-
const skillDir =
|
|
12006
|
+
const skillDir = join22(homedir(), ".claude", "skills", "story");
|
|
11794
12007
|
await mkdir5(skillDir, { recursive: true });
|
|
11795
12008
|
let srcSkillDir;
|
|
11796
12009
|
try {
|
|
@@ -11803,30 +12016,30 @@ async function handleSetupSkill(options = {}) {
|
|
|
11803
12016
|
process.exitCode = 1;
|
|
11804
12017
|
return;
|
|
11805
12018
|
}
|
|
11806
|
-
const oldPrimeDir =
|
|
11807
|
-
if (
|
|
12019
|
+
const oldPrimeDir = join22(homedir(), ".claude", "skills", "prime");
|
|
12020
|
+
if (existsSync13(oldPrimeDir)) {
|
|
11808
12021
|
await rm(oldPrimeDir, { recursive: true, force: true });
|
|
11809
12022
|
log("Removed old /prime skill (migrated to /story)");
|
|
11810
12023
|
}
|
|
11811
|
-
const existed =
|
|
11812
|
-
const skillContent = await readFile5(
|
|
11813
|
-
await writeFile3(
|
|
12024
|
+
const existed = existsSync13(join22(skillDir, "SKILL.md"));
|
|
12025
|
+
const skillContent = await readFile5(join22(srcSkillDir, "SKILL.md"), "utf-8");
|
|
12026
|
+
await writeFile3(join22(skillDir, "SKILL.md"), skillContent, "utf-8");
|
|
11814
12027
|
const supportFiles = ["setup-flow.md", "autonomous-mode.md", "reference.md"];
|
|
11815
12028
|
const writtenFiles = ["SKILL.md"];
|
|
11816
12029
|
const missingFiles = [];
|
|
11817
12030
|
for (const filename of supportFiles) {
|
|
11818
|
-
const srcPath =
|
|
11819
|
-
if (
|
|
12031
|
+
const srcPath = join22(srcSkillDir, filename);
|
|
12032
|
+
if (existsSync13(srcPath)) {
|
|
11820
12033
|
const content = await readFile5(srcPath, "utf-8");
|
|
11821
|
-
await writeFile3(
|
|
12034
|
+
await writeFile3(join22(skillDir, filename), content, "utf-8");
|
|
11822
12035
|
writtenFiles.push(filename);
|
|
11823
12036
|
} else {
|
|
11824
12037
|
missingFiles.push(filename);
|
|
11825
12038
|
}
|
|
11826
12039
|
}
|
|
11827
|
-
const designSrcDir =
|
|
11828
|
-
if (
|
|
11829
|
-
const designDestDir =
|
|
12040
|
+
const designSrcDir = join22(srcSkillDir, "design");
|
|
12041
|
+
if (existsSync13(designSrcDir)) {
|
|
12042
|
+
const designDestDir = join22(skillDir, "design");
|
|
11830
12043
|
try {
|
|
11831
12044
|
const designFiles = await copyDirRecursive(designSrcDir, designDestDir);
|
|
11832
12045
|
for (const f of designFiles) writtenFiles.push(`design/${f}`);
|
|
@@ -11949,8 +12162,8 @@ var hook_status_exports = {};
|
|
|
11949
12162
|
__export(hook_status_exports, {
|
|
11950
12163
|
handleHookStatus: () => handleHookStatus
|
|
11951
12164
|
});
|
|
11952
|
-
import { readFileSync as
|
|
11953
|
-
import { join as
|
|
12165
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, renameSync as renameSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
12166
|
+
import { join as join23 } from "path";
|
|
11954
12167
|
async function readStdinSilent() {
|
|
11955
12168
|
try {
|
|
11956
12169
|
const chunks = [];
|
|
@@ -11967,8 +12180,8 @@ async function readStdinSilent() {
|
|
|
11967
12180
|
function atomicWriteSync(targetPath, content) {
|
|
11968
12181
|
const tmp = `${targetPath}.${process.pid}.tmp`;
|
|
11969
12182
|
try {
|
|
11970
|
-
|
|
11971
|
-
|
|
12183
|
+
writeFileSync5(tmp, content, "utf-8");
|
|
12184
|
+
renameSync3(tmp, targetPath);
|
|
11972
12185
|
return true;
|
|
11973
12186
|
} catch {
|
|
11974
12187
|
try {
|
|
@@ -12000,10 +12213,10 @@ function activePayload(session) {
|
|
|
12000
12213
|
};
|
|
12001
12214
|
}
|
|
12002
12215
|
function ensureGitignore(root) {
|
|
12003
|
-
const gitignorePath =
|
|
12216
|
+
const gitignorePath = join23(root, ".story", ".gitignore");
|
|
12004
12217
|
let existing = "";
|
|
12005
12218
|
try {
|
|
12006
|
-
existing =
|
|
12219
|
+
existing = readFileSync10(gitignorePath, "utf-8");
|
|
12007
12220
|
} catch {
|
|
12008
12221
|
}
|
|
12009
12222
|
const lines = existing.split("\n").map((l) => l.trim());
|
|
@@ -12013,13 +12226,13 @@ function ensureGitignore(root) {
|
|
|
12013
12226
|
if (content.length > 0 && !content.endsWith("\n")) content += "\n";
|
|
12014
12227
|
content += missing.join("\n") + "\n";
|
|
12015
12228
|
try {
|
|
12016
|
-
|
|
12229
|
+
writeFileSync5(gitignorePath, content, "utf-8");
|
|
12017
12230
|
} catch {
|
|
12018
12231
|
}
|
|
12019
12232
|
}
|
|
12020
12233
|
function writeStatus(root, payload) {
|
|
12021
12234
|
ensureGitignore(root);
|
|
12022
|
-
const statusPath =
|
|
12235
|
+
const statusPath = join23(root, ".story", "status.json");
|
|
12023
12236
|
const content = JSON.stringify(payload, null, 2) + "\n";
|
|
12024
12237
|
atomicWriteSync(statusPath, content);
|
|
12025
12238
|
}
|
|
@@ -12078,8 +12291,8 @@ var config_update_exports = {};
|
|
|
12078
12291
|
__export(config_update_exports, {
|
|
12079
12292
|
handleConfigSetOverrides: () => handleConfigSetOverrides
|
|
12080
12293
|
});
|
|
12081
|
-
import { readFileSync as
|
|
12082
|
-
import { join as
|
|
12294
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
12295
|
+
import { join as join24 } from "path";
|
|
12083
12296
|
async function handleConfigSetOverrides(root, format, options) {
|
|
12084
12297
|
const { json: jsonArg, clear } = options;
|
|
12085
12298
|
if (!clear && !jsonArg) {
|
|
@@ -12107,8 +12320,8 @@ async function handleConfigSetOverrides(root, format, options) {
|
|
|
12107
12320
|
}
|
|
12108
12321
|
let resultOverrides = null;
|
|
12109
12322
|
await withProjectLock(root, { strict: false }, async () => {
|
|
12110
|
-
const configPath =
|
|
12111
|
-
const rawContent =
|
|
12323
|
+
const configPath = join24(root, ".story", "config.json");
|
|
12324
|
+
const rawContent = readFileSync11(configPath, "utf-8");
|
|
12112
12325
|
const raw = JSON.parse(rawContent);
|
|
12113
12326
|
if (clear) {
|
|
12114
12327
|
delete raw.recipeOverrides;
|
|
@@ -14451,7 +14664,7 @@ async function runCli() {
|
|
|
14451
14664
|
registerSessionCommand: registerSessionCommand2,
|
|
14452
14665
|
registerRepairCommand: registerRepairCommand2
|
|
14453
14666
|
} = await Promise.resolve().then(() => (init_register(), register_exports));
|
|
14454
|
-
const version2 = "0.1.
|
|
14667
|
+
const version2 = "0.1.59";
|
|
14455
14668
|
class HandledError extends Error {
|
|
14456
14669
|
constructor() {
|
|
14457
14670
|
super("HANDLED_ERROR");
|