@anthropologies/claudestory 0.1.58 → 0.1.60
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 +309 -106
- package/dist/mcp.js +240 -50
- 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
|
}
|
|
@@ -6315,6 +6356,22 @@ function djb2Hash(content) {
|
|
|
6315
6356
|
}
|
|
6316
6357
|
return hash.toString(36);
|
|
6317
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
|
+
}
|
|
6318
6375
|
var StageContext;
|
|
6319
6376
|
var init_types2 = __esm({
|
|
6320
6377
|
"src/autonomous/stages/types.ts"() {
|
|
@@ -6703,6 +6760,7 @@ var init_plan_review = __esm({
|
|
|
6703
6760
|
"src/autonomous/stages/plan-review.ts"() {
|
|
6704
6761
|
"use strict";
|
|
6705
6762
|
init_esm_shims();
|
|
6763
|
+
init_types2();
|
|
6706
6764
|
init_review_depth();
|
|
6707
6765
|
PlanReviewStage = class {
|
|
6708
6766
|
id = "PLAN_REVIEW";
|
|
@@ -6713,6 +6771,31 @@ var init_plan_review = __esm({
|
|
|
6713
6771
|
const reviewer = nextReviewer(existingReviews, backends);
|
|
6714
6772
|
const risk = ctx.state.ticket?.risk ?? "low";
|
|
6715
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
|
+
}
|
|
6716
6799
|
return {
|
|
6717
6800
|
instruction: [
|
|
6718
6801
|
`# Plan Review \u2014 Round ${roundNum} of ${Math.max(minRounds, roundNum)} minimum`,
|
|
@@ -6774,9 +6857,19 @@ var init_plan_review = __esm({
|
|
|
6774
6857
|
nextAction = "PLAN_REVIEW";
|
|
6775
6858
|
}
|
|
6776
6859
|
const reviewsForWrite = isReject ? { ...ctx.state.reviews, plan: [] } : { ...ctx.state.reviews, plan: planReviews };
|
|
6777
|
-
|
|
6860
|
+
const stateUpdate = {
|
|
6778
6861
|
reviews: reviewsForWrite
|
|
6779
|
-
}
|
|
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);
|
|
6780
6873
|
ctx.appendEvent("plan_review", {
|
|
6781
6874
|
round: roundNum,
|
|
6782
6875
|
verdict,
|
|
@@ -7140,13 +7233,42 @@ var init_test = __esm({
|
|
|
7140
7233
|
}
|
|
7141
7234
|
});
|
|
7142
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
|
+
|
|
7143
7263
|
// src/autonomous/stages/code-review.ts
|
|
7144
7264
|
var CodeReviewStage;
|
|
7145
7265
|
var init_code_review = __esm({
|
|
7146
7266
|
"src/autonomous/stages/code-review.ts"() {
|
|
7147
7267
|
"use strict";
|
|
7148
7268
|
init_esm_shims();
|
|
7269
|
+
init_types2();
|
|
7149
7270
|
init_review_depth();
|
|
7271
|
+
init_cache();
|
|
7150
7272
|
CodeReviewStage = class {
|
|
7151
7273
|
id = "CODE_REVIEW";
|
|
7152
7274
|
async enter(ctx) {
|
|
@@ -7159,6 +7281,32 @@ var init_code_review = __esm({
|
|
|
7159
7281
|
const mergeBase = ctx.state.git.mergeBase;
|
|
7160
7282
|
const diffCommand = mergeBase ? `\`git diff ${mergeBase}\`` : `\`git diff HEAD\` AND \`git ls-files --others --exclude-standard\``;
|
|
7161
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
|
+
}
|
|
7162
7310
|
return {
|
|
7163
7311
|
instruction: [
|
|
7164
7312
|
`# Code Review \u2014 Round ${roundNum} of ${rounds} minimum`,
|
|
@@ -7224,8 +7372,10 @@ var init_code_review = __esm({
|
|
|
7224
7372
|
nextAction = "CODE_REVIEW";
|
|
7225
7373
|
}
|
|
7226
7374
|
if (nextAction === "PLAN") {
|
|
7375
|
+
clearCache(ctx.dir);
|
|
7227
7376
|
ctx.writeState({
|
|
7228
7377
|
reviews: { plan: [], code: [] },
|
|
7378
|
+
lensReviewHistory: [],
|
|
7229
7379
|
ticket: ctx.state.ticket ? { ...ctx.state.ticket, realizedRisk: void 0 } : ctx.state.ticket
|
|
7230
7380
|
});
|
|
7231
7381
|
ctx.appendEvent("code_review", {
|
|
@@ -7237,9 +7387,19 @@ var init_code_review = __esm({
|
|
|
7237
7387
|
await ctx.fileDeferredFindings(findings, "code");
|
|
7238
7388
|
return { action: "back", target: "PLAN", reason: "plan_redirect" };
|
|
7239
7389
|
}
|
|
7240
|
-
|
|
7390
|
+
const stateUpdate = {
|
|
7241
7391
|
reviews: { ...ctx.state.reviews, code: codeReviews }
|
|
7242
|
-
}
|
|
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);
|
|
7243
7403
|
ctx.appendEvent("code_review", {
|
|
7244
7404
|
round: roundNum,
|
|
7245
7405
|
verdict,
|
|
@@ -8002,6 +8162,44 @@ var init_lesson_capture = __esm({
|
|
|
8002
8162
|
planFindings,
|
|
8003
8163
|
codeFindings
|
|
8004
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") : "";
|
|
8005
8203
|
return {
|
|
8006
8204
|
instruction: [
|
|
8007
8205
|
"# Capture Lessons from Review Findings",
|
|
@@ -8017,15 +8215,18 @@ var init_lesson_capture = __esm({
|
|
|
8017
8215
|
" - If it matches an existing lesson \u2192 call `claudestory_lesson_reinforce`",
|
|
8018
8216
|
' - If it\'s a new pattern \u2192 call `claudestory_lesson_create` with `source: "review"`',
|
|
8019
8217
|
"3. Skip patterns that are one-off or already well-covered",
|
|
8218
|
+
lensSection,
|
|
8219
|
+
dismissedSection,
|
|
8020
8220
|
`4. Call \`claudestory_autonomous_guide\` with completedAction: "lessons_captured"`,
|
|
8021
8221
|
"",
|
|
8022
8222
|
"```json",
|
|
8023
8223
|
`{ "sessionId": "${ctx.state.sessionId}", "action": "report", "report": { "completedAction": "lessons_captured" } }`,
|
|
8024
8224
|
"```"
|
|
8025
|
-
].join("\n"),
|
|
8225
|
+
].filter(Boolean).join("\n"),
|
|
8026
8226
|
reminders: [
|
|
8027
8227
|
"Check existing lessons first \u2014 reinforce before creating duplicates.",
|
|
8028
|
-
"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."] : []
|
|
8029
8230
|
]
|
|
8030
8231
|
};
|
|
8031
8232
|
}
|
|
@@ -8224,8 +8425,8 @@ Impact: ${nextIssue.impact}` : ""}` : `Fix issue ${next}.`,
|
|
|
8224
8425
|
});
|
|
8225
8426
|
|
|
8226
8427
|
// src/autonomous/stages/handover.ts
|
|
8227
|
-
import { writeFileSync as
|
|
8228
|
-
import { join as
|
|
8428
|
+
import { writeFileSync as writeFileSync3 } from "fs";
|
|
8429
|
+
import { join as join13 } from "path";
|
|
8229
8430
|
var HandoverStage;
|
|
8230
8431
|
var init_handover2 = __esm({
|
|
8231
8432
|
"src/autonomous/stages/handover.ts"() {
|
|
@@ -8263,8 +8464,8 @@ var init_handover2 = __esm({
|
|
|
8263
8464
|
} catch {
|
|
8264
8465
|
handoverFailed = true;
|
|
8265
8466
|
try {
|
|
8266
|
-
const fallbackPath =
|
|
8267
|
-
|
|
8467
|
+
const fallbackPath = join13(ctx.dir, "handover-fallback.md");
|
|
8468
|
+
writeFileSync3(fallbackPath, content, "utf-8");
|
|
8268
8469
|
} catch {
|
|
8269
8470
|
}
|
|
8270
8471
|
}
|
|
@@ -8353,8 +8554,8 @@ var init_stages = __esm({
|
|
|
8353
8554
|
});
|
|
8354
8555
|
|
|
8355
8556
|
// src/autonomous/version-check.ts
|
|
8356
|
-
import { readFileSync as
|
|
8357
|
-
import { join as
|
|
8557
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
8558
|
+
import { join as join14, dirname as dirname4 } from "path";
|
|
8358
8559
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
8359
8560
|
function checkVersionMismatch(runningVersion, installedVersion) {
|
|
8360
8561
|
if (!installedVersion) return null;
|
|
@@ -8366,12 +8567,12 @@ function getInstalledVersion() {
|
|
|
8366
8567
|
try {
|
|
8367
8568
|
const thisFile = fileURLToPath3(import.meta.url);
|
|
8368
8569
|
const candidates = [
|
|
8369
|
-
|
|
8370
|
-
|
|
8570
|
+
join14(dirname4(thisFile), "..", "..", "package.json"),
|
|
8571
|
+
join14(dirname4(thisFile), "..", "package.json")
|
|
8371
8572
|
];
|
|
8372
8573
|
for (const candidate of candidates) {
|
|
8373
8574
|
try {
|
|
8374
|
-
const raw =
|
|
8575
|
+
const raw = readFileSync7(candidate, "utf-8");
|
|
8375
8576
|
const pkg = JSON.parse(raw);
|
|
8376
8577
|
if (pkg.version) return pkg.version;
|
|
8377
8578
|
} catch {
|
|
@@ -8383,7 +8584,7 @@ function getInstalledVersion() {
|
|
|
8383
8584
|
}
|
|
8384
8585
|
}
|
|
8385
8586
|
function getRunningVersion() {
|
|
8386
|
-
return "0.1.
|
|
8587
|
+
return "0.1.60";
|
|
8387
8588
|
}
|
|
8388
8589
|
var init_version_check = __esm({
|
|
8389
8590
|
"src/autonomous/version-check.ts"() {
|
|
@@ -8393,23 +8594,23 @@ var init_version_check = __esm({
|
|
|
8393
8594
|
});
|
|
8394
8595
|
|
|
8395
8596
|
// src/autonomous/guide.ts
|
|
8396
|
-
import { readFileSync as
|
|
8397
|
-
import { join as
|
|
8597
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, readdirSync as readdirSync4 } from "fs";
|
|
8598
|
+
import { join as join15 } from "path";
|
|
8398
8599
|
function buildGuideRecommendOptions(root) {
|
|
8399
8600
|
const opts = {};
|
|
8400
8601
|
try {
|
|
8401
|
-
const handoversDir =
|
|
8602
|
+
const handoversDir = join15(root, ".story", "handovers");
|
|
8402
8603
|
const files = readdirSync4(handoversDir, "utf-8").filter((f) => f.endsWith(".md")).sort();
|
|
8403
8604
|
if (files.length > 0) {
|
|
8404
|
-
opts.latestHandoverContent =
|
|
8605
|
+
opts.latestHandoverContent = readFileSync8(join15(handoversDir, files[files.length - 1]), "utf-8");
|
|
8405
8606
|
}
|
|
8406
8607
|
} catch {
|
|
8407
8608
|
}
|
|
8408
8609
|
try {
|
|
8409
|
-
const snapshotsDir =
|
|
8610
|
+
const snapshotsDir = join15(root, ".story", "snapshots");
|
|
8410
8611
|
const snapFiles = readdirSync4(snapshotsDir, "utf-8").filter((f) => f.endsWith(".json")).sort();
|
|
8411
8612
|
if (snapFiles.length > 0) {
|
|
8412
|
-
const raw =
|
|
8613
|
+
const raw = readFileSync8(join15(snapshotsDir, snapFiles[snapFiles.length - 1]), "utf-8");
|
|
8413
8614
|
const snap = JSON.parse(raw);
|
|
8414
8615
|
if (snap.issues) {
|
|
8415
8616
|
opts.previousOpenIssueCount = snap.issues.filter((i) => i.status !== "resolved").length;
|
|
@@ -8791,7 +8992,7 @@ Staged: ${stagedResult.data.join(", ")}`
|
|
|
8791
8992
|
}
|
|
8792
8993
|
}
|
|
8793
8994
|
const { state: projectState, warnings } = await loadProject(root);
|
|
8794
|
-
const handoversDir =
|
|
8995
|
+
const handoversDir = join15(root, ".story", "handovers");
|
|
8795
8996
|
const ctx = { state: projectState, warnings, root, handoversDir, format: "md" };
|
|
8796
8997
|
let handoverText = "";
|
|
8797
8998
|
try {
|
|
@@ -8808,7 +9009,7 @@ Staged: ${stagedResult.data.join(", ")}`
|
|
|
8808
9009
|
}
|
|
8809
9010
|
} catch {
|
|
8810
9011
|
}
|
|
8811
|
-
const rulesText = readFileSafe2(
|
|
9012
|
+
const rulesText = readFileSafe2(join15(root, "RULES.md"));
|
|
8812
9013
|
const lessonDigest = buildLessonDigest(projectState.lessons);
|
|
8813
9014
|
const digestParts = [
|
|
8814
9015
|
handoverText ? `## Recent Handovers
|
|
@@ -8824,7 +9025,7 @@ ${rulesText}` : "",
|
|
|
8824
9025
|
].filter(Boolean);
|
|
8825
9026
|
const digest = digestParts.join("\n\n---\n\n");
|
|
8826
9027
|
try {
|
|
8827
|
-
|
|
9028
|
+
writeFileSync4(join15(dir, "context-digest.md"), digest, "utf-8");
|
|
8828
9029
|
} catch {
|
|
8829
9030
|
}
|
|
8830
9031
|
if (mode !== "auto" && args.ticketId) {
|
|
@@ -9576,7 +9777,7 @@ function guideError(err) {
|
|
|
9576
9777
|
}
|
|
9577
9778
|
function readFileSafe2(path2) {
|
|
9578
9779
|
try {
|
|
9579
|
-
return
|
|
9780
|
+
return readFileSync8(path2, "utf-8");
|
|
9580
9781
|
} catch {
|
|
9581
9782
|
return "";
|
|
9582
9783
|
}
|
|
@@ -9829,8 +10030,8 @@ var init_session_report_formatter = __esm({
|
|
|
9829
10030
|
});
|
|
9830
10031
|
|
|
9831
10032
|
// src/cli/commands/session-report.ts
|
|
9832
|
-
import { readFileSync as
|
|
9833
|
-
import { join as
|
|
10033
|
+
import { readFileSync as readFileSync9, existsSync as existsSync11 } from "fs";
|
|
10034
|
+
import { join as join16 } from "path";
|
|
9834
10035
|
async function handleSessionReport(sessionId, root, format = "md") {
|
|
9835
10036
|
if (!UUID_REGEX.test(sessionId)) {
|
|
9836
10037
|
return {
|
|
@@ -9841,7 +10042,7 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9841
10042
|
};
|
|
9842
10043
|
}
|
|
9843
10044
|
const dir = sessionDir(root, sessionId);
|
|
9844
|
-
if (!
|
|
10045
|
+
if (!existsSync11(dir)) {
|
|
9845
10046
|
return {
|
|
9846
10047
|
output: `Error: Session ${sessionId} not found.`,
|
|
9847
10048
|
exitCode: 1,
|
|
@@ -9849,8 +10050,8 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9849
10050
|
isError: true
|
|
9850
10051
|
};
|
|
9851
10052
|
}
|
|
9852
|
-
const statePath2 =
|
|
9853
|
-
if (!
|
|
10053
|
+
const statePath2 = join16(dir, "state.json");
|
|
10054
|
+
if (!existsSync11(statePath2)) {
|
|
9854
10055
|
return {
|
|
9855
10056
|
output: `Error: Session ${sessionId} corrupt \u2014 state.json missing.`,
|
|
9856
10057
|
exitCode: 1,
|
|
@@ -9859,7 +10060,7 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9859
10060
|
};
|
|
9860
10061
|
}
|
|
9861
10062
|
try {
|
|
9862
|
-
const rawJson = JSON.parse(
|
|
10063
|
+
const rawJson = JSON.parse(readFileSync9(statePath2, "utf-8"));
|
|
9863
10064
|
if (rawJson && typeof rawJson === "object" && "schemaVersion" in rawJson && rawJson.schemaVersion !== CURRENT_SESSION_SCHEMA_VERSION) {
|
|
9864
10065
|
return {
|
|
9865
10066
|
output: `Error: Session ${sessionId} \u2014 unsupported session schema version ${rawJson.schemaVersion}.`,
|
|
@@ -9888,7 +10089,7 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9888
10089
|
const events = readEvents(dir);
|
|
9889
10090
|
let planContent = null;
|
|
9890
10091
|
try {
|
|
9891
|
-
planContent =
|
|
10092
|
+
planContent = readFileSync9(join16(dir, "plan.md"), "utf-8");
|
|
9892
10093
|
} catch {
|
|
9893
10094
|
}
|
|
9894
10095
|
let gitLog = null;
|
|
@@ -9917,7 +10118,7 @@ var init_session_report = __esm({
|
|
|
9917
10118
|
});
|
|
9918
10119
|
|
|
9919
10120
|
// src/cli/commands/phase.ts
|
|
9920
|
-
import { join as
|
|
10121
|
+
import { join as join17, resolve as resolve6 } from "path";
|
|
9921
10122
|
function validatePhaseId(id) {
|
|
9922
10123
|
if (id.length > PHASE_ID_MAX_LENGTH) {
|
|
9923
10124
|
throw new CliValidationError("invalid_input", `Phase ID "${id}" exceeds ${PHASE_ID_MAX_LENGTH} characters`);
|
|
@@ -10106,21 +10307,21 @@ async function handlePhaseDelete(id, reassign, format, root) {
|
|
|
10106
10307
|
const updated = { ...ticket, phase: reassign, order: maxOrder };
|
|
10107
10308
|
const parsed = TicketSchema.parse(updated);
|
|
10108
10309
|
const content = serializeJSON(parsed);
|
|
10109
|
-
const target =
|
|
10310
|
+
const target = join17(wrapDir, "tickets", `${parsed.id}.json`);
|
|
10110
10311
|
operations.push({ op: "write", target, content });
|
|
10111
10312
|
}
|
|
10112
10313
|
for (const issue of affectedIssues) {
|
|
10113
10314
|
const updated = { ...issue, phase: reassign };
|
|
10114
10315
|
const parsed = IssueSchema.parse(updated);
|
|
10115
10316
|
const content = serializeJSON(parsed);
|
|
10116
|
-
const target =
|
|
10317
|
+
const target = join17(wrapDir, "issues", `${parsed.id}.json`);
|
|
10117
10318
|
operations.push({ op: "write", target, content });
|
|
10118
10319
|
}
|
|
10119
10320
|
const newPhases = state.roadmap.phases.filter((p) => p.id !== id);
|
|
10120
10321
|
const newRoadmap = { ...state.roadmap, phases: newPhases };
|
|
10121
10322
|
const parsedRoadmap = RoadmapSchema.parse(newRoadmap);
|
|
10122
10323
|
const roadmapContent = serializeJSON(parsedRoadmap);
|
|
10123
|
-
const roadmapTarget =
|
|
10324
|
+
const roadmapTarget = join17(wrapDir, "roadmap.json");
|
|
10124
10325
|
operations.push({ op: "write", target: roadmapTarget, content: roadmapContent });
|
|
10125
10326
|
await runTransactionUnlocked(root, operations);
|
|
10126
10327
|
} else {
|
|
@@ -10153,14 +10354,14 @@ var init_phase = __esm({
|
|
|
10153
10354
|
|
|
10154
10355
|
// src/mcp/tools.ts
|
|
10155
10356
|
import { z as z10 } from "zod";
|
|
10156
|
-
import { join as
|
|
10357
|
+
import { join as join18 } from "path";
|
|
10157
10358
|
function formatMcpError(code, message) {
|
|
10158
10359
|
return `[${code}] ${message}`;
|
|
10159
10360
|
}
|
|
10160
10361
|
async function runMcpReadTool(pinnedRoot, handler) {
|
|
10161
10362
|
try {
|
|
10162
10363
|
const { state, warnings } = await loadProject(pinnedRoot);
|
|
10163
|
-
const handoversDir =
|
|
10364
|
+
const handoversDir = join18(pinnedRoot, ".story", "handovers");
|
|
10164
10365
|
const ctx = { state, warnings, root: pinnedRoot, handoversDir, format: "md" };
|
|
10165
10366
|
const result = await handler(ctx);
|
|
10166
10367
|
if (result.errorCode && INFRASTRUCTURE_ERROR_CODES.includes(result.errorCode)) {
|
|
@@ -10748,10 +10949,10 @@ var init_tools = __esm({
|
|
|
10748
10949
|
|
|
10749
10950
|
// src/core/init.ts
|
|
10750
10951
|
import { mkdir as mkdir4, stat as stat2, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
|
|
10751
|
-
import { join as
|
|
10952
|
+
import { join as join19, resolve as resolve7 } from "path";
|
|
10752
10953
|
async function initProject(root, options) {
|
|
10753
10954
|
const absRoot = resolve7(root);
|
|
10754
|
-
const wrapDir =
|
|
10955
|
+
const wrapDir = join19(absRoot, ".story");
|
|
10755
10956
|
let exists = false;
|
|
10756
10957
|
try {
|
|
10757
10958
|
const s = await stat2(wrapDir);
|
|
@@ -10771,11 +10972,11 @@ async function initProject(root, options) {
|
|
|
10771
10972
|
".story/ already exists. Use --force to overwrite config and roadmap."
|
|
10772
10973
|
);
|
|
10773
10974
|
}
|
|
10774
|
-
await mkdir4(
|
|
10775
|
-
await mkdir4(
|
|
10776
|
-
await mkdir4(
|
|
10777
|
-
await mkdir4(
|
|
10778
|
-
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 });
|
|
10779
10980
|
const created = [
|
|
10780
10981
|
".story/config.json",
|
|
10781
10982
|
".story/roadmap.json",
|
|
@@ -10815,7 +11016,7 @@ async function initProject(root, options) {
|
|
|
10815
11016
|
};
|
|
10816
11017
|
await writeConfig(config, absRoot);
|
|
10817
11018
|
await writeRoadmap(roadmap, absRoot);
|
|
10818
|
-
const gitignorePath =
|
|
11019
|
+
const gitignorePath = join19(wrapDir, ".gitignore");
|
|
10819
11020
|
await ensureGitignoreEntries(gitignorePath, STORY_GITIGNORE_ENTRIES);
|
|
10820
11021
|
const warnings = [];
|
|
10821
11022
|
if (options.force && exists) {
|
|
@@ -10862,8 +11063,8 @@ var init_init = __esm({
|
|
|
10862
11063
|
|
|
10863
11064
|
// src/mcp/index.ts
|
|
10864
11065
|
var mcp_exports = {};
|
|
10865
|
-
import { realpathSync as realpathSync2, existsSync as
|
|
10866
|
-
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";
|
|
10867
11068
|
import { z as z11 } from "zod";
|
|
10868
11069
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10869
11070
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -10878,7 +11079,7 @@ function tryDiscoverRoot() {
|
|
|
10878
11079
|
const resolved = resolve8(envRoot);
|
|
10879
11080
|
try {
|
|
10880
11081
|
const canonical = realpathSync2(resolved);
|
|
10881
|
-
if (
|
|
11082
|
+
if (existsSync12(join20(canonical, CONFIG_PATH2))) {
|
|
10882
11083
|
return canonical;
|
|
10883
11084
|
}
|
|
10884
11085
|
process.stderr.write(`Warning: No .story/config.json at ${canonical}
|
|
@@ -10981,7 +11182,7 @@ var init_mcp = __esm({
|
|
|
10981
11182
|
init_init();
|
|
10982
11183
|
ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
|
|
10983
11184
|
CONFIG_PATH2 = ".story/config.json";
|
|
10984
|
-
version = "0.1.
|
|
11185
|
+
version = "0.1.60";
|
|
10985
11186
|
main().catch((err) => {
|
|
10986
11187
|
process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
|
|
10987
11188
|
`);
|
|
@@ -11017,7 +11218,7 @@ __export(run_exports, {
|
|
|
11017
11218
|
runReadCommand: () => runReadCommand,
|
|
11018
11219
|
writeOutput: () => writeOutput
|
|
11019
11220
|
});
|
|
11020
|
-
import { join as
|
|
11221
|
+
import { join as join21 } from "path";
|
|
11021
11222
|
function writeOutput(text) {
|
|
11022
11223
|
try {
|
|
11023
11224
|
process.stdout.write(text + "\n");
|
|
@@ -11045,7 +11246,7 @@ async function runReadCommand(format, handler) {
|
|
|
11045
11246
|
return;
|
|
11046
11247
|
}
|
|
11047
11248
|
const { state, warnings } = await loadProject(root);
|
|
11048
|
-
const handoversDir =
|
|
11249
|
+
const handoversDir = join21(root, ".story", "handovers");
|
|
11049
11250
|
const result = await handler({ state, warnings, root, handoversDir, format });
|
|
11050
11251
|
writeOutput(result.output);
|
|
11051
11252
|
let exitCode = result.exitCode ?? ExitCode.OK;
|
|
@@ -11080,7 +11281,7 @@ async function runDeleteCommand(format, force, handler) {
|
|
|
11080
11281
|
return;
|
|
11081
11282
|
}
|
|
11082
11283
|
const { state, warnings } = await loadProject(root);
|
|
11083
|
-
const handoversDir =
|
|
11284
|
+
const handoversDir = join21(root, ".story", "handovers");
|
|
11084
11285
|
if (!force && hasIntegrityWarnings(warnings)) {
|
|
11085
11286
|
writeOutput(
|
|
11086
11287
|
formatError(
|
|
@@ -11594,8 +11795,8 @@ __export(setup_skill_exports, {
|
|
|
11594
11795
|
resolveSkillSourceDir: () => resolveSkillSourceDir
|
|
11595
11796
|
});
|
|
11596
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";
|
|
11597
|
-
import { existsSync as
|
|
11598
|
-
import { join as
|
|
11798
|
+
import { existsSync as existsSync13 } from "fs";
|
|
11799
|
+
import { join as join22, dirname as dirname5 } from "path";
|
|
11599
11800
|
import { homedir } from "os";
|
|
11600
11801
|
import { execFileSync } from "child_process";
|
|
11601
11802
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -11604,10 +11805,10 @@ function log(msg) {
|
|
|
11604
11805
|
}
|
|
11605
11806
|
function resolveSkillSourceDir() {
|
|
11606
11807
|
const thisDir = dirname5(fileURLToPath4(import.meta.url));
|
|
11607
|
-
const bundledPath =
|
|
11608
|
-
if (
|
|
11609
|
-
const sourcePath =
|
|
11610
|
-
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;
|
|
11611
11812
|
throw new Error(
|
|
11612
11813
|
`Cannot find bundled skill files. Checked:
|
|
11613
11814
|
${bundledPath}
|
|
@@ -11617,31 +11818,31 @@ function resolveSkillSourceDir() {
|
|
|
11617
11818
|
async function copyDirRecursive(srcDir, destDir) {
|
|
11618
11819
|
const tmpDir = destDir + ".tmp";
|
|
11619
11820
|
const bakDir = destDir + ".bak";
|
|
11620
|
-
if (!
|
|
11821
|
+
if (!existsSync13(destDir) && existsSync13(bakDir)) {
|
|
11621
11822
|
await rename2(bakDir, destDir);
|
|
11622
11823
|
}
|
|
11623
|
-
if (
|
|
11624
|
-
if (
|
|
11824
|
+
if (existsSync13(tmpDir)) await rm(tmpDir, { recursive: true, force: true });
|
|
11825
|
+
if (existsSync13(bakDir)) await rm(bakDir, { recursive: true, force: true });
|
|
11625
11826
|
await mkdir5(tmpDir, { recursive: true });
|
|
11626
11827
|
const entries = await readdir4(srcDir, { withFileTypes: true, recursive: true });
|
|
11627
11828
|
const written = [];
|
|
11628
11829
|
for (const entry of entries) {
|
|
11629
11830
|
if (!entry.isFile()) continue;
|
|
11630
11831
|
const parent = entry.parentPath ?? entry.path ?? srcDir;
|
|
11631
|
-
const relativePath =
|
|
11632
|
-
const srcPath =
|
|
11633
|
-
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);
|
|
11634
11835
|
await mkdir5(dirname5(destPath), { recursive: true });
|
|
11635
11836
|
await copyFile(srcPath, destPath);
|
|
11636
11837
|
written.push(relativePath);
|
|
11637
11838
|
}
|
|
11638
|
-
if (
|
|
11839
|
+
if (existsSync13(destDir)) {
|
|
11639
11840
|
await rename2(destDir, bakDir);
|
|
11640
11841
|
}
|
|
11641
11842
|
try {
|
|
11642
11843
|
await rename2(tmpDir, destDir);
|
|
11643
11844
|
} catch (err) {
|
|
11644
|
-
if (
|
|
11845
|
+
if (existsSync13(bakDir)) await rename2(bakDir, destDir).catch(() => {
|
|
11645
11846
|
});
|
|
11646
11847
|
await rm(tmpDir, { recursive: true, force: true }).catch(() => {
|
|
11647
11848
|
});
|
|
@@ -11657,9 +11858,9 @@ function isHookWithCommand(entry, command) {
|
|
|
11657
11858
|
return e.type === "command" && typeof e.command === "string" && e.command.trim() === command;
|
|
11658
11859
|
}
|
|
11659
11860
|
async function registerHook(hookType, hookEntry, settingsPath, matcher) {
|
|
11660
|
-
const path2 = settingsPath ??
|
|
11861
|
+
const path2 = settingsPath ?? join22(homedir(), ".claude", "settings.json");
|
|
11661
11862
|
let raw = "{}";
|
|
11662
|
-
if (
|
|
11863
|
+
if (existsSync13(path2)) {
|
|
11663
11864
|
try {
|
|
11664
11865
|
raw = await readFile5(path2, "utf-8");
|
|
11665
11866
|
} catch {
|
|
@@ -11755,9 +11956,9 @@ async function registerStopHook(settingsPath) {
|
|
|
11755
11956
|
return registerHook("Stop", { type: "command", command: STOP_HOOK_COMMAND, async: true }, settingsPath);
|
|
11756
11957
|
}
|
|
11757
11958
|
async function removeHook(hookType, command, settingsPath) {
|
|
11758
|
-
const path2 = settingsPath ??
|
|
11959
|
+
const path2 = settingsPath ?? join22(homedir(), ".claude", "settings.json");
|
|
11759
11960
|
let raw = "{}";
|
|
11760
|
-
if (
|
|
11961
|
+
if (existsSync13(path2)) {
|
|
11761
11962
|
try {
|
|
11762
11963
|
raw = await readFile5(path2, "utf-8");
|
|
11763
11964
|
} catch {
|
|
@@ -11802,7 +12003,7 @@ async function removeHook(hookType, command, settingsPath) {
|
|
|
11802
12003
|
}
|
|
11803
12004
|
async function handleSetupSkill(options = {}) {
|
|
11804
12005
|
const { skipHooks = false } = options;
|
|
11805
|
-
const skillDir =
|
|
12006
|
+
const skillDir = join22(homedir(), ".claude", "skills", "story");
|
|
11806
12007
|
await mkdir5(skillDir, { recursive: true });
|
|
11807
12008
|
let srcSkillDir;
|
|
11808
12009
|
try {
|
|
@@ -11815,38 +12016,40 @@ async function handleSetupSkill(options = {}) {
|
|
|
11815
12016
|
process.exitCode = 1;
|
|
11816
12017
|
return;
|
|
11817
12018
|
}
|
|
11818
|
-
const oldPrimeDir =
|
|
11819
|
-
if (
|
|
12019
|
+
const oldPrimeDir = join22(homedir(), ".claude", "skills", "prime");
|
|
12020
|
+
if (existsSync13(oldPrimeDir)) {
|
|
11820
12021
|
await rm(oldPrimeDir, { recursive: true, force: true });
|
|
11821
12022
|
log("Removed old /prime skill (migrated to /story)");
|
|
11822
12023
|
}
|
|
11823
|
-
const existed =
|
|
11824
|
-
const skillContent = await readFile5(
|
|
11825
|
-
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");
|
|
11826
12027
|
const supportFiles = ["setup-flow.md", "autonomous-mode.md", "reference.md"];
|
|
11827
12028
|
const writtenFiles = ["SKILL.md"];
|
|
11828
12029
|
const missingFiles = [];
|
|
11829
12030
|
for (const filename of supportFiles) {
|
|
11830
|
-
const srcPath =
|
|
11831
|
-
if (
|
|
12031
|
+
const srcPath = join22(srcSkillDir, filename);
|
|
12032
|
+
if (existsSync13(srcPath)) {
|
|
11832
12033
|
const content = await readFile5(srcPath, "utf-8");
|
|
11833
|
-
await writeFile3(
|
|
12034
|
+
await writeFile3(join22(skillDir, filename), content, "utf-8");
|
|
11834
12035
|
writtenFiles.push(filename);
|
|
11835
12036
|
} else {
|
|
11836
12037
|
missingFiles.push(filename);
|
|
11837
12038
|
}
|
|
11838
12039
|
}
|
|
11839
|
-
const
|
|
11840
|
-
|
|
11841
|
-
|
|
11842
|
-
|
|
11843
|
-
|
|
11844
|
-
|
|
11845
|
-
|
|
11846
|
-
|
|
11847
|
-
|
|
12040
|
+
for (const subdir of ["design", "review-lenses"]) {
|
|
12041
|
+
const srcDir = join22(srcSkillDir, subdir);
|
|
12042
|
+
if (existsSync13(srcDir)) {
|
|
12043
|
+
const destDir = join22(skillDir, subdir);
|
|
12044
|
+
try {
|
|
12045
|
+
const files = await copyDirRecursive(srcDir, destDir);
|
|
12046
|
+
for (const f of files) writtenFiles.push(`${subdir}/${f}`);
|
|
12047
|
+
} catch (err) {
|
|
12048
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
12049
|
+
process.stderr.write(`Warning: ${subdir} skill copy failed: ${msg}
|
|
11848
12050
|
`);
|
|
11849
|
-
|
|
12051
|
+
missingFiles.push(`${subdir}/`);
|
|
12052
|
+
}
|
|
11850
12053
|
}
|
|
11851
12054
|
}
|
|
11852
12055
|
log(`${existed ? "Updated" : "Installed"} /story skill at ${skillDir}/`);
|
|
@@ -11961,8 +12164,8 @@ var hook_status_exports = {};
|
|
|
11961
12164
|
__export(hook_status_exports, {
|
|
11962
12165
|
handleHookStatus: () => handleHookStatus
|
|
11963
12166
|
});
|
|
11964
|
-
import { readFileSync as
|
|
11965
|
-
import { join as
|
|
12167
|
+
import { readFileSync as readFileSync10, writeFileSync as writeFileSync5, renameSync as renameSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
12168
|
+
import { join as join23 } from "path";
|
|
11966
12169
|
async function readStdinSilent() {
|
|
11967
12170
|
try {
|
|
11968
12171
|
const chunks = [];
|
|
@@ -11979,8 +12182,8 @@ async function readStdinSilent() {
|
|
|
11979
12182
|
function atomicWriteSync(targetPath, content) {
|
|
11980
12183
|
const tmp = `${targetPath}.${process.pid}.tmp`;
|
|
11981
12184
|
try {
|
|
11982
|
-
|
|
11983
|
-
|
|
12185
|
+
writeFileSync5(tmp, content, "utf-8");
|
|
12186
|
+
renameSync3(tmp, targetPath);
|
|
11984
12187
|
return true;
|
|
11985
12188
|
} catch {
|
|
11986
12189
|
try {
|
|
@@ -12012,10 +12215,10 @@ function activePayload(session) {
|
|
|
12012
12215
|
};
|
|
12013
12216
|
}
|
|
12014
12217
|
function ensureGitignore(root) {
|
|
12015
|
-
const gitignorePath =
|
|
12218
|
+
const gitignorePath = join23(root, ".story", ".gitignore");
|
|
12016
12219
|
let existing = "";
|
|
12017
12220
|
try {
|
|
12018
|
-
existing =
|
|
12221
|
+
existing = readFileSync10(gitignorePath, "utf-8");
|
|
12019
12222
|
} catch {
|
|
12020
12223
|
}
|
|
12021
12224
|
const lines = existing.split("\n").map((l) => l.trim());
|
|
@@ -12025,13 +12228,13 @@ function ensureGitignore(root) {
|
|
|
12025
12228
|
if (content.length > 0 && !content.endsWith("\n")) content += "\n";
|
|
12026
12229
|
content += missing.join("\n") + "\n";
|
|
12027
12230
|
try {
|
|
12028
|
-
|
|
12231
|
+
writeFileSync5(gitignorePath, content, "utf-8");
|
|
12029
12232
|
} catch {
|
|
12030
12233
|
}
|
|
12031
12234
|
}
|
|
12032
12235
|
function writeStatus(root, payload) {
|
|
12033
12236
|
ensureGitignore(root);
|
|
12034
|
-
const statusPath =
|
|
12237
|
+
const statusPath = join23(root, ".story", "status.json");
|
|
12035
12238
|
const content = JSON.stringify(payload, null, 2) + "\n";
|
|
12036
12239
|
atomicWriteSync(statusPath, content);
|
|
12037
12240
|
}
|
|
@@ -12090,8 +12293,8 @@ var config_update_exports = {};
|
|
|
12090
12293
|
__export(config_update_exports, {
|
|
12091
12294
|
handleConfigSetOverrides: () => handleConfigSetOverrides
|
|
12092
12295
|
});
|
|
12093
|
-
import { readFileSync as
|
|
12094
|
-
import { join as
|
|
12296
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
12297
|
+
import { join as join24 } from "path";
|
|
12095
12298
|
async function handleConfigSetOverrides(root, format, options) {
|
|
12096
12299
|
const { json: jsonArg, clear } = options;
|
|
12097
12300
|
if (!clear && !jsonArg) {
|
|
@@ -12119,8 +12322,8 @@ async function handleConfigSetOverrides(root, format, options) {
|
|
|
12119
12322
|
}
|
|
12120
12323
|
let resultOverrides = null;
|
|
12121
12324
|
await withProjectLock(root, { strict: false }, async () => {
|
|
12122
|
-
const configPath =
|
|
12123
|
-
const rawContent =
|
|
12325
|
+
const configPath = join24(root, ".story", "config.json");
|
|
12326
|
+
const rawContent = readFileSync11(configPath, "utf-8");
|
|
12124
12327
|
const raw = JSON.parse(rawContent);
|
|
12125
12328
|
if (clear) {
|
|
12126
12329
|
delete raw.recipeOverrides;
|
|
@@ -14463,7 +14666,7 @@ async function runCli() {
|
|
|
14463
14666
|
registerSessionCommand: registerSessionCommand2,
|
|
14464
14667
|
registerRepairCommand: registerRepairCommand2
|
|
14465
14668
|
} = await Promise.resolve().then(() => (init_register(), register_exports));
|
|
14466
|
-
const version2 = "0.1.
|
|
14669
|
+
const version2 = "0.1.60";
|
|
14467
14670
|
class HandledError extends Error {
|
|
14468
14671
|
constructor() {
|
|
14469
14672
|
super("HANDLED_ERROR");
|