@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/mcp.js
CHANGED
|
@@ -3689,8 +3689,48 @@ var init_session_types = __esm({
|
|
|
3689
3689
|
maxTicketsPerSession: z9.number().min(0).default(5),
|
|
3690
3690
|
handoverInterval: z9.number().min(0).default(3),
|
|
3691
3691
|
compactThreshold: z9.string().default("high"),
|
|
3692
|
-
reviewBackends: z9.array(z9.string()).default(["codex", "agent"])
|
|
3692
|
+
reviewBackends: z9.array(z9.string()).default(["codex", "agent"]),
|
|
3693
|
+
// T-181: Multi-lens review config
|
|
3694
|
+
lensConfig: z9.object({
|
|
3695
|
+
lenses: z9.union([z9.literal("auto"), z9.array(z9.string())]).default("auto"),
|
|
3696
|
+
maxLenses: z9.number().min(1).max(8).default(8),
|
|
3697
|
+
lensTimeout: z9.union([
|
|
3698
|
+
z9.number(),
|
|
3699
|
+
z9.object({ default: z9.number(), opus: z9.number() })
|
|
3700
|
+
]).default({ default: 60, opus: 120 }),
|
|
3701
|
+
findingBudget: z9.number().min(1).default(10),
|
|
3702
|
+
confidenceFloor: z9.number().min(0).max(1).default(0.6),
|
|
3703
|
+
tokenBudgetPerLens: z9.number().min(1e3).default(32e3),
|
|
3704
|
+
hotPaths: z9.array(z9.string()).default([]),
|
|
3705
|
+
lensModels: z9.record(z9.string()).default({ default: "sonnet", security: "opus", concurrency: "opus" })
|
|
3706
|
+
}).optional(),
|
|
3707
|
+
blockingPolicy: z9.object({
|
|
3708
|
+
neverBlock: z9.array(z9.string()).default([]),
|
|
3709
|
+
alwaysBlock: z9.array(z9.string()).default(["injection", "auth-bypass", "hardcoded-secrets"]),
|
|
3710
|
+
planReviewBlockingLenses: z9.array(z9.string()).default(["security", "error-handling"])
|
|
3711
|
+
}).optional(),
|
|
3712
|
+
requireSecretsGate: z9.boolean().default(false),
|
|
3713
|
+
requireAccessibility: z9.boolean().default(false),
|
|
3714
|
+
testMapping: z9.object({
|
|
3715
|
+
strategy: z9.literal("convention"),
|
|
3716
|
+
patterns: z9.array(z9.object({
|
|
3717
|
+
source: z9.string(),
|
|
3718
|
+
test: z9.string()
|
|
3719
|
+
}))
|
|
3720
|
+
}).optional()
|
|
3693
3721
|
}).default({ maxTicketsPerSession: 5, compactThreshold: "high", reviewBackends: ["codex", "agent"], handoverInterval: 3 }),
|
|
3722
|
+
// T-181: Lens review findings history (for lessons feedback loop)
|
|
3723
|
+
lensReviewHistory: z9.array(z9.object({
|
|
3724
|
+
ticketId: z9.string(),
|
|
3725
|
+
stage: z9.enum(["CODE_REVIEW", "PLAN_REVIEW"]),
|
|
3726
|
+
lens: z9.string(),
|
|
3727
|
+
category: z9.string(),
|
|
3728
|
+
severity: z9.string(),
|
|
3729
|
+
disposition: z9.enum(["open", "addressed", "contested", "deferred"]),
|
|
3730
|
+
description: z9.string(),
|
|
3731
|
+
dismissReason: z9.string().optional(),
|
|
3732
|
+
timestamp: z9.string()
|
|
3733
|
+
})).default([]),
|
|
3694
3734
|
// T-123: Issue sweep tracking
|
|
3695
3735
|
issueSweepState: z9.object({
|
|
3696
3736
|
remaining: z9.array(z9.string()),
|
|
@@ -3719,7 +3759,8 @@ var init_session_types = __esm({
|
|
|
3719
3759
|
resolvedDefaults: z9.object({
|
|
3720
3760
|
maxTicketsPerSession: z9.number(),
|
|
3721
3761
|
compactThreshold: z9.string(),
|
|
3722
|
-
reviewBackends: z9.array(z9.string())
|
|
3762
|
+
reviewBackends: z9.array(z9.string()),
|
|
3763
|
+
handoverInterval: z9.number().optional()
|
|
3723
3764
|
}).optional()
|
|
3724
3765
|
}).passthrough();
|
|
3725
3766
|
}
|
|
@@ -4096,8 +4137,8 @@ var init_session = __esm({
|
|
|
4096
4137
|
|
|
4097
4138
|
// src/mcp/index.ts
|
|
4098
4139
|
init_esm_shims();
|
|
4099
|
-
import { realpathSync as realpathSync2, existsSync as
|
|
4100
|
-
import { resolve as resolve8, join as
|
|
4140
|
+
import { realpathSync as realpathSync2, existsSync as existsSync12 } from "fs";
|
|
4141
|
+
import { resolve as resolve8, join as join20, isAbsolute } from "path";
|
|
4101
4142
|
import { z as z11 } from "zod";
|
|
4102
4143
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4103
4144
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -4150,7 +4191,7 @@ init_errors();
|
|
|
4150
4191
|
init_helpers();
|
|
4151
4192
|
init_types();
|
|
4152
4193
|
import { z as z10 } from "zod";
|
|
4153
|
-
import { join as
|
|
4194
|
+
import { join as join18 } from "path";
|
|
4154
4195
|
|
|
4155
4196
|
// src/cli/commands/status.ts
|
|
4156
4197
|
init_esm_shims();
|
|
@@ -5444,8 +5485,8 @@ init_handover();
|
|
|
5444
5485
|
init_esm_shims();
|
|
5445
5486
|
init_session_types();
|
|
5446
5487
|
init_session();
|
|
5447
|
-
import { readFileSync as
|
|
5448
|
-
import { join as
|
|
5488
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync4, readdirSync as readdirSync4 } from "fs";
|
|
5489
|
+
import { join as join15 } from "path";
|
|
5449
5490
|
|
|
5450
5491
|
// src/autonomous/state-machine.ts
|
|
5451
5492
|
init_esm_shims();
|
|
@@ -5984,6 +6025,22 @@ function djb2Hash(content) {
|
|
|
5984
6025
|
}
|
|
5985
6026
|
return hash.toString(36);
|
|
5986
6027
|
}
|
|
6028
|
+
function buildLensHistoryUpdate(findings, existing, ticketId, stage) {
|
|
6029
|
+
const existingKeys = new Set(
|
|
6030
|
+
existing.map((e) => `${e.ticketId}:${e.stage}:${e.lens}:${e.category}`)
|
|
6031
|
+
);
|
|
6032
|
+
const newEntries = findings.map((f) => ({
|
|
6033
|
+
ticketId,
|
|
6034
|
+
stage,
|
|
6035
|
+
lens: typeof f.lens === "string" && f.lens !== "" ? f.lens : "unknown",
|
|
6036
|
+
category: f.category,
|
|
6037
|
+
severity: f.severity,
|
|
6038
|
+
disposition: f.disposition ?? "open",
|
|
6039
|
+
description: f.description,
|
|
6040
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
6041
|
+
})).filter((e) => !existingKeys.has(`${e.ticketId}:${e.stage}:${e.lens}:${e.category}`));
|
|
6042
|
+
return newEntries.length > 0 ? [...existing, ...newEntries] : null;
|
|
6043
|
+
}
|
|
5987
6044
|
|
|
5988
6045
|
// src/autonomous/stages/index.ts
|
|
5989
6046
|
init_esm_shims();
|
|
@@ -6254,6 +6311,31 @@ var PlanReviewStage = class {
|
|
|
6254
6311
|
const reviewer = nextReviewer(existingReviews, backends);
|
|
6255
6312
|
const risk = ctx.state.ticket?.risk ?? "low";
|
|
6256
6313
|
const minRounds = requiredRounds(risk);
|
|
6314
|
+
if (reviewer === "lenses") {
|
|
6315
|
+
return {
|
|
6316
|
+
instruction: [
|
|
6317
|
+
`# Multi-Lens Plan Review \u2014 Round ${roundNum} of ${Math.max(minRounds, roundNum)} minimum`,
|
|
6318
|
+
"",
|
|
6319
|
+
"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.",
|
|
6320
|
+
"",
|
|
6321
|
+
"1. Read the plan file",
|
|
6322
|
+
"2. Call `prepareLensReview()` with the plan text (stage: PLAN_REVIEW)",
|
|
6323
|
+
"3. Spawn all lens subagents in parallel",
|
|
6324
|
+
"4. Collect results and pass through the merger and judge pipeline",
|
|
6325
|
+
"5. Report the final SynthesisResult verdict and findings",
|
|
6326
|
+
"",
|
|
6327
|
+
"When done, call `claudestory_autonomous_guide` with:",
|
|
6328
|
+
"```json",
|
|
6329
|
+
`{ "sessionId": "${ctx.state.sessionId}", "action": "report", "report": { "completedAction": "plan_review_round", "verdict": "<approve|revise|reject>", "findings": [...] } }`,
|
|
6330
|
+
"```"
|
|
6331
|
+
].join("\n"),
|
|
6332
|
+
reminders: [
|
|
6333
|
+
"Report the exact verdict and findings from the synthesizer.",
|
|
6334
|
+
"Lens subagents run in parallel with read-only tools (Read, Grep, Glob)."
|
|
6335
|
+
],
|
|
6336
|
+
transitionedFrom: ctx.state.previousState ?? void 0
|
|
6337
|
+
};
|
|
6338
|
+
}
|
|
6257
6339
|
return {
|
|
6258
6340
|
instruction: [
|
|
6259
6341
|
`# Plan Review \u2014 Round ${roundNum} of ${Math.max(minRounds, roundNum)} minimum`,
|
|
@@ -6315,9 +6397,19 @@ var PlanReviewStage = class {
|
|
|
6315
6397
|
nextAction = "PLAN_REVIEW";
|
|
6316
6398
|
}
|
|
6317
6399
|
const reviewsForWrite = isReject ? { ...ctx.state.reviews, plan: [] } : { ...ctx.state.reviews, plan: planReviews };
|
|
6318
|
-
|
|
6400
|
+
const stateUpdate = {
|
|
6319
6401
|
reviews: reviewsForWrite
|
|
6320
|
-
}
|
|
6402
|
+
};
|
|
6403
|
+
if (reviewerBackend === "lenses" && findings.length > 0) {
|
|
6404
|
+
const updated = buildLensHistoryUpdate(
|
|
6405
|
+
findings,
|
|
6406
|
+
ctx.state.lensReviewHistory ?? [],
|
|
6407
|
+
ctx.state.ticket?.id ?? "unknown",
|
|
6408
|
+
"PLAN_REVIEW"
|
|
6409
|
+
);
|
|
6410
|
+
if (updated) stateUpdate.lensReviewHistory = updated;
|
|
6411
|
+
}
|
|
6412
|
+
ctx.writeState(stateUpdate);
|
|
6321
6413
|
ctx.appendEvent("plan_review", {
|
|
6322
6414
|
round: roundNum,
|
|
6323
6415
|
verdict,
|
|
@@ -6661,6 +6753,25 @@ var TestStage = class {
|
|
|
6661
6753
|
|
|
6662
6754
|
// src/autonomous/stages/code-review.ts
|
|
6663
6755
|
init_esm_shims();
|
|
6756
|
+
|
|
6757
|
+
// src/autonomous/review-lenses/cache.ts
|
|
6758
|
+
init_esm_shims();
|
|
6759
|
+
import { createHash } from "crypto";
|
|
6760
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync9, rmSync as rmSync2, renameSync as renameSync2 } from "fs";
|
|
6761
|
+
import { join as join12 } from "path";
|
|
6762
|
+
|
|
6763
|
+
// src/autonomous/review-lenses/schema-validator.ts
|
|
6764
|
+
init_esm_shims();
|
|
6765
|
+
|
|
6766
|
+
// src/autonomous/review-lenses/cache.ts
|
|
6767
|
+
var CACHE_DIR = "lens-cache";
|
|
6768
|
+
function clearCache(sessionDir2) {
|
|
6769
|
+
const dir = join12(sessionDir2, CACHE_DIR);
|
|
6770
|
+
if (!existsSync9(dir)) return;
|
|
6771
|
+
rmSync2(dir, { recursive: true, force: true });
|
|
6772
|
+
}
|
|
6773
|
+
|
|
6774
|
+
// src/autonomous/stages/code-review.ts
|
|
6664
6775
|
var CodeReviewStage = class {
|
|
6665
6776
|
id = "CODE_REVIEW";
|
|
6666
6777
|
async enter(ctx) {
|
|
@@ -6673,6 +6784,32 @@ var CodeReviewStage = class {
|
|
|
6673
6784
|
const mergeBase = ctx.state.git.mergeBase;
|
|
6674
6785
|
const diffCommand = mergeBase ? `\`git diff ${mergeBase}\`` : `\`git diff HEAD\` AND \`git ls-files --others --exclude-standard\``;
|
|
6675
6786
|
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.";
|
|
6787
|
+
if (reviewer === "lenses") {
|
|
6788
|
+
return {
|
|
6789
|
+
instruction: [
|
|
6790
|
+
`# Multi-Lens Code Review \u2014 Round ${roundNum} of ${rounds} minimum`,
|
|
6791
|
+
"",
|
|
6792
|
+
`Capture the diff with: ${diffCommand}`,
|
|
6793
|
+
"",
|
|
6794
|
+
"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.",
|
|
6795
|
+
"",
|
|
6796
|
+
"1. Capture the full diff",
|
|
6797
|
+
"2. Call `prepareLensReview()` with the diff and changed file list",
|
|
6798
|
+
"3. Spawn all lens subagents in parallel (each prompt is provided by the orchestrator)",
|
|
6799
|
+
"4. Collect results and pass through the merger and judge pipeline",
|
|
6800
|
+
"5. Report the final SynthesisResult verdict and findings",
|
|
6801
|
+
"",
|
|
6802
|
+
"When done, report verdict and findings."
|
|
6803
|
+
].join("\n"),
|
|
6804
|
+
reminders: [
|
|
6805
|
+
diffReminder,
|
|
6806
|
+
"Do NOT compress or summarize the diff.",
|
|
6807
|
+
"Lens subagents run in parallel with read-only tools (Read, Grep, Glob).",
|
|
6808
|
+
"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."
|
|
6809
|
+
],
|
|
6810
|
+
transitionedFrom: ctx.state.previousState ?? void 0
|
|
6811
|
+
};
|
|
6812
|
+
}
|
|
6676
6813
|
return {
|
|
6677
6814
|
instruction: [
|
|
6678
6815
|
`# Code Review \u2014 Round ${roundNum} of ${rounds} minimum`,
|
|
@@ -6738,8 +6875,10 @@ var CodeReviewStage = class {
|
|
|
6738
6875
|
nextAction = "CODE_REVIEW";
|
|
6739
6876
|
}
|
|
6740
6877
|
if (nextAction === "PLAN") {
|
|
6878
|
+
clearCache(ctx.dir);
|
|
6741
6879
|
ctx.writeState({
|
|
6742
6880
|
reviews: { plan: [], code: [] },
|
|
6881
|
+
lensReviewHistory: [],
|
|
6743
6882
|
ticket: ctx.state.ticket ? { ...ctx.state.ticket, realizedRisk: void 0 } : ctx.state.ticket
|
|
6744
6883
|
});
|
|
6745
6884
|
ctx.appendEvent("code_review", {
|
|
@@ -6751,9 +6890,19 @@ var CodeReviewStage = class {
|
|
|
6751
6890
|
await ctx.fileDeferredFindings(findings, "code");
|
|
6752
6891
|
return { action: "back", target: "PLAN", reason: "plan_redirect" };
|
|
6753
6892
|
}
|
|
6754
|
-
|
|
6893
|
+
const stateUpdate = {
|
|
6755
6894
|
reviews: { ...ctx.state.reviews, code: codeReviews }
|
|
6756
|
-
}
|
|
6895
|
+
};
|
|
6896
|
+
if (reviewerBackend === "lenses" && findings.length > 0) {
|
|
6897
|
+
const updated = buildLensHistoryUpdate(
|
|
6898
|
+
findings,
|
|
6899
|
+
ctx.state.lensReviewHistory ?? [],
|
|
6900
|
+
ctx.state.ticket?.id ?? "unknown",
|
|
6901
|
+
"CODE_REVIEW"
|
|
6902
|
+
);
|
|
6903
|
+
if (updated) stateUpdate.lensReviewHistory = updated;
|
|
6904
|
+
}
|
|
6905
|
+
ctx.writeState(stateUpdate);
|
|
6757
6906
|
ctx.appendEvent("code_review", {
|
|
6758
6907
|
round: roundNum,
|
|
6759
6908
|
verdict,
|
|
@@ -7482,6 +7631,44 @@ var LessonCaptureStage = class {
|
|
|
7482
7631
|
planFindings,
|
|
7483
7632
|
codeFindings
|
|
7484
7633
|
});
|
|
7634
|
+
const lensHistory = ctx.state.lensReviewHistory ?? [];
|
|
7635
|
+
const dismissed = lensHistory.filter(
|
|
7636
|
+
(f) => f.disposition === "contested"
|
|
7637
|
+
);
|
|
7638
|
+
const tupleCounts = /* @__PURE__ */ new Map();
|
|
7639
|
+
for (const f of lensHistory) {
|
|
7640
|
+
const key = `${f.lens}:${f.category}`;
|
|
7641
|
+
const entry = tupleCounts.get(key) ?? { total: 0, dismissed: 0 };
|
|
7642
|
+
entry.total++;
|
|
7643
|
+
if (f.disposition === "contested") {
|
|
7644
|
+
entry.dismissed++;
|
|
7645
|
+
}
|
|
7646
|
+
tupleCounts.set(key, entry);
|
|
7647
|
+
}
|
|
7648
|
+
const falsePositivePatterns = [];
|
|
7649
|
+
for (const [key, counts] of tupleCounts) {
|
|
7650
|
+
if (counts.total >= 5 && counts.dismissed / counts.total >= 0.6) {
|
|
7651
|
+
falsePositivePatterns.push(
|
|
7652
|
+
`- **${key}**: ${counts.dismissed}/${counts.total} dismissed (${Math.round(counts.dismissed / counts.total * 100)}%)`
|
|
7653
|
+
);
|
|
7654
|
+
}
|
|
7655
|
+
}
|
|
7656
|
+
const lensSection = falsePositivePatterns.length > 0 ? [
|
|
7657
|
+
"",
|
|
7658
|
+
"## Lens False Positive Patterns",
|
|
7659
|
+
"",
|
|
7660
|
+
"These (lens, category) tuples have a >60% dismissal rate over 5+ reviews. Create a lesson for each:",
|
|
7661
|
+
...falsePositivePatterns,
|
|
7662
|
+
"",
|
|
7663
|
+
'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]."',
|
|
7664
|
+
'Tag with `source: "lens-feedback"` and `tags: ["lens", lens-name]`.'
|
|
7665
|
+
].join("\n") : "";
|
|
7666
|
+
const dismissedSection = dismissed.length > 0 && falsePositivePatterns.length === 0 ? [
|
|
7667
|
+
"",
|
|
7668
|
+
`## Dismissed Lens Findings (${dismissed.length})`,
|
|
7669
|
+
"",
|
|
7670
|
+
"Some lens findings were dismissed this session. Not enough data for automatic patterns yet (need 5+ reviews per tuple at >60% dismissal rate)."
|
|
7671
|
+
].join("\n") : "";
|
|
7485
7672
|
return {
|
|
7486
7673
|
instruction: [
|
|
7487
7674
|
"# Capture Lessons from Review Findings",
|
|
@@ -7497,15 +7684,18 @@ var LessonCaptureStage = class {
|
|
|
7497
7684
|
" - If it matches an existing lesson \u2192 call `claudestory_lesson_reinforce`",
|
|
7498
7685
|
' - If it\'s a new pattern \u2192 call `claudestory_lesson_create` with `source: "review"`',
|
|
7499
7686
|
"3. Skip patterns that are one-off or already well-covered",
|
|
7687
|
+
lensSection,
|
|
7688
|
+
dismissedSection,
|
|
7500
7689
|
`4. Call \`claudestory_autonomous_guide\` with completedAction: "lessons_captured"`,
|
|
7501
7690
|
"",
|
|
7502
7691
|
"```json",
|
|
7503
7692
|
`{ "sessionId": "${ctx.state.sessionId}", "action": "report", "report": { "completedAction": "lessons_captured" } }`,
|
|
7504
7693
|
"```"
|
|
7505
|
-
].join("\n"),
|
|
7694
|
+
].filter(Boolean).join("\n"),
|
|
7506
7695
|
reminders: [
|
|
7507
7696
|
"Check existing lessons first \u2014 reinforce before creating duplicates.",
|
|
7508
|
-
"Only capture patterns worth remembering across sessions."
|
|
7697
|
+
"Only capture patterns worth remembering across sessions.",
|
|
7698
|
+
...falsePositivePatterns.length > 0 ? ["Lens false positive patterns MUST be captured as lessons \u2014 they improve future reviews."] : []
|
|
7509
7699
|
]
|
|
7510
7700
|
};
|
|
7511
7701
|
}
|
|
@@ -7692,8 +7882,8 @@ Impact: ${nextIssue.impact}` : ""}` : `Fix issue ${next}.`,
|
|
|
7692
7882
|
// src/autonomous/stages/handover.ts
|
|
7693
7883
|
init_esm_shims();
|
|
7694
7884
|
init_handover();
|
|
7695
|
-
import { writeFileSync as
|
|
7696
|
-
import { join as
|
|
7885
|
+
import { writeFileSync as writeFileSync3 } from "fs";
|
|
7886
|
+
import { join as join13 } from "path";
|
|
7697
7887
|
var HandoverStage = class {
|
|
7698
7888
|
id = "HANDOVER";
|
|
7699
7889
|
async enter(ctx) {
|
|
@@ -7724,8 +7914,8 @@ var HandoverStage = class {
|
|
|
7724
7914
|
} catch {
|
|
7725
7915
|
handoverFailed = true;
|
|
7726
7916
|
try {
|
|
7727
|
-
const fallbackPath =
|
|
7728
|
-
|
|
7917
|
+
const fallbackPath = join13(ctx.dir, "handover-fallback.md");
|
|
7918
|
+
writeFileSync3(fallbackPath, content, "utf-8");
|
|
7729
7919
|
} catch {
|
|
7730
7920
|
}
|
|
7731
7921
|
}
|
|
@@ -7797,8 +7987,8 @@ init_queries();
|
|
|
7797
7987
|
|
|
7798
7988
|
// src/autonomous/version-check.ts
|
|
7799
7989
|
init_esm_shims();
|
|
7800
|
-
import { readFileSync as
|
|
7801
|
-
import { join as
|
|
7990
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
7991
|
+
import { join as join14, dirname as dirname4 } from "path";
|
|
7802
7992
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7803
7993
|
function checkVersionMismatch(runningVersion, installedVersion) {
|
|
7804
7994
|
if (!installedVersion) return null;
|
|
@@ -7810,12 +8000,12 @@ function getInstalledVersion() {
|
|
|
7810
8000
|
try {
|
|
7811
8001
|
const thisFile = fileURLToPath3(import.meta.url);
|
|
7812
8002
|
const candidates = [
|
|
7813
|
-
|
|
7814
|
-
|
|
8003
|
+
join14(dirname4(thisFile), "..", "..", "package.json"),
|
|
8004
|
+
join14(dirname4(thisFile), "..", "package.json")
|
|
7815
8005
|
];
|
|
7816
8006
|
for (const candidate of candidates) {
|
|
7817
8007
|
try {
|
|
7818
|
-
const raw =
|
|
8008
|
+
const raw = readFileSync7(candidate, "utf-8");
|
|
7819
8009
|
const pkg = JSON.parse(raw);
|
|
7820
8010
|
if (pkg.version) return pkg.version;
|
|
7821
8011
|
} catch {
|
|
@@ -7827,7 +8017,7 @@ function getInstalledVersion() {
|
|
|
7827
8017
|
}
|
|
7828
8018
|
}
|
|
7829
8019
|
function getRunningVersion() {
|
|
7830
|
-
return "0.1.
|
|
8020
|
+
return "0.1.60";
|
|
7831
8021
|
}
|
|
7832
8022
|
|
|
7833
8023
|
// src/autonomous/guide.ts
|
|
@@ -7852,18 +8042,18 @@ var RECOVERY_MAPPING = {
|
|
|
7852
8042
|
function buildGuideRecommendOptions(root) {
|
|
7853
8043
|
const opts = {};
|
|
7854
8044
|
try {
|
|
7855
|
-
const handoversDir =
|
|
8045
|
+
const handoversDir = join15(root, ".story", "handovers");
|
|
7856
8046
|
const files = readdirSync4(handoversDir, "utf-8").filter((f) => f.endsWith(".md")).sort();
|
|
7857
8047
|
if (files.length > 0) {
|
|
7858
|
-
opts.latestHandoverContent =
|
|
8048
|
+
opts.latestHandoverContent = readFileSync8(join15(handoversDir, files[files.length - 1]), "utf-8");
|
|
7859
8049
|
}
|
|
7860
8050
|
} catch {
|
|
7861
8051
|
}
|
|
7862
8052
|
try {
|
|
7863
|
-
const snapshotsDir =
|
|
8053
|
+
const snapshotsDir = join15(root, ".story", "snapshots");
|
|
7864
8054
|
const snapFiles = readdirSync4(snapshotsDir, "utf-8").filter((f) => f.endsWith(".json")).sort();
|
|
7865
8055
|
if (snapFiles.length > 0) {
|
|
7866
|
-
const raw =
|
|
8056
|
+
const raw = readFileSync8(join15(snapshotsDir, snapFiles[snapFiles.length - 1]), "utf-8");
|
|
7867
8057
|
const snap = JSON.parse(raw);
|
|
7868
8058
|
if (snap.issues) {
|
|
7869
8059
|
opts.previousOpenIssueCount = snap.issues.filter((i) => i.status !== "resolved").length;
|
|
@@ -8251,7 +8441,7 @@ Staged: ${stagedResult.data.join(", ")}`
|
|
|
8251
8441
|
}
|
|
8252
8442
|
}
|
|
8253
8443
|
const { state: projectState, warnings } = await loadProject(root);
|
|
8254
|
-
const handoversDir =
|
|
8444
|
+
const handoversDir = join15(root, ".story", "handovers");
|
|
8255
8445
|
const ctx = { state: projectState, warnings, root, handoversDir, format: "md" };
|
|
8256
8446
|
let handoverText = "";
|
|
8257
8447
|
try {
|
|
@@ -8268,7 +8458,7 @@ Staged: ${stagedResult.data.join(", ")}`
|
|
|
8268
8458
|
}
|
|
8269
8459
|
} catch {
|
|
8270
8460
|
}
|
|
8271
|
-
const rulesText = readFileSafe2(
|
|
8461
|
+
const rulesText = readFileSafe2(join15(root, "RULES.md"));
|
|
8272
8462
|
const lessonDigest = buildLessonDigest(projectState.lessons);
|
|
8273
8463
|
const digestParts = [
|
|
8274
8464
|
handoverText ? `## Recent Handovers
|
|
@@ -8284,7 +8474,7 @@ ${rulesText}` : "",
|
|
|
8284
8474
|
].filter(Boolean);
|
|
8285
8475
|
const digest = digestParts.join("\n\n---\n\n");
|
|
8286
8476
|
try {
|
|
8287
|
-
|
|
8477
|
+
writeFileSync4(join15(dir, "context-digest.md"), digest, "utf-8");
|
|
8288
8478
|
} catch {
|
|
8289
8479
|
}
|
|
8290
8480
|
if (mode !== "auto" && args.ticketId) {
|
|
@@ -9037,7 +9227,7 @@ function guideError(err) {
|
|
|
9037
9227
|
}
|
|
9038
9228
|
function readFileSafe2(path2) {
|
|
9039
9229
|
try {
|
|
9040
|
-
return
|
|
9230
|
+
return readFileSync8(path2, "utf-8");
|
|
9041
9231
|
} catch {
|
|
9042
9232
|
return "";
|
|
9043
9233
|
}
|
|
@@ -9047,8 +9237,8 @@ function readFileSafe2(path2) {
|
|
|
9047
9237
|
init_esm_shims();
|
|
9048
9238
|
init_session();
|
|
9049
9239
|
init_session_types();
|
|
9050
|
-
import { readFileSync as
|
|
9051
|
-
import { join as
|
|
9240
|
+
import { readFileSync as readFileSync9, existsSync as existsSync11 } from "fs";
|
|
9241
|
+
import { join as join16 } from "path";
|
|
9052
9242
|
|
|
9053
9243
|
// src/core/session-report-formatter.ts
|
|
9054
9244
|
init_esm_shims();
|
|
@@ -9254,7 +9444,7 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9254
9444
|
};
|
|
9255
9445
|
}
|
|
9256
9446
|
const dir = sessionDir(root, sessionId);
|
|
9257
|
-
if (!
|
|
9447
|
+
if (!existsSync11(dir)) {
|
|
9258
9448
|
return {
|
|
9259
9449
|
output: `Error: Session ${sessionId} not found.`,
|
|
9260
9450
|
exitCode: 1,
|
|
@@ -9262,8 +9452,8 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9262
9452
|
isError: true
|
|
9263
9453
|
};
|
|
9264
9454
|
}
|
|
9265
|
-
const statePath2 =
|
|
9266
|
-
if (!
|
|
9455
|
+
const statePath2 = join16(dir, "state.json");
|
|
9456
|
+
if (!existsSync11(statePath2)) {
|
|
9267
9457
|
return {
|
|
9268
9458
|
output: `Error: Session ${sessionId} corrupt \u2014 state.json missing.`,
|
|
9269
9459
|
exitCode: 1,
|
|
@@ -9272,7 +9462,7 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9272
9462
|
};
|
|
9273
9463
|
}
|
|
9274
9464
|
try {
|
|
9275
|
-
const rawJson = JSON.parse(
|
|
9465
|
+
const rawJson = JSON.parse(readFileSync9(statePath2, "utf-8"));
|
|
9276
9466
|
if (rawJson && typeof rawJson === "object" && "schemaVersion" in rawJson && rawJson.schemaVersion !== CURRENT_SESSION_SCHEMA_VERSION) {
|
|
9277
9467
|
return {
|
|
9278
9468
|
output: `Error: Session ${sessionId} \u2014 unsupported session schema version ${rawJson.schemaVersion}.`,
|
|
@@ -9301,7 +9491,7 @@ async function handleSessionReport(sessionId, root, format = "md") {
|
|
|
9301
9491
|
const events = readEvents(dir);
|
|
9302
9492
|
let planContent = null;
|
|
9303
9493
|
try {
|
|
9304
|
-
planContent =
|
|
9494
|
+
planContent = readFileSync9(join16(dir, "plan.md"), "utf-8");
|
|
9305
9495
|
} catch {
|
|
9306
9496
|
}
|
|
9307
9497
|
let gitLog = null;
|
|
@@ -9326,7 +9516,7 @@ init_issue();
|
|
|
9326
9516
|
init_roadmap();
|
|
9327
9517
|
init_output_formatter();
|
|
9328
9518
|
init_helpers();
|
|
9329
|
-
import { join as
|
|
9519
|
+
import { join as join17, resolve as resolve6 } from "path";
|
|
9330
9520
|
var PHASE_ID_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
9331
9521
|
var PHASE_ID_MAX_LENGTH = 40;
|
|
9332
9522
|
function validatePhaseId(id) {
|
|
@@ -9435,7 +9625,7 @@ function formatMcpError(code, message) {
|
|
|
9435
9625
|
async function runMcpReadTool(pinnedRoot, handler) {
|
|
9436
9626
|
try {
|
|
9437
9627
|
const { state, warnings } = await loadProject(pinnedRoot);
|
|
9438
|
-
const handoversDir =
|
|
9628
|
+
const handoversDir = join18(pinnedRoot, ".story", "handovers");
|
|
9439
9629
|
const ctx = { state, warnings, root: pinnedRoot, handoversDir, format: "md" };
|
|
9440
9630
|
const result = await handler(ctx);
|
|
9441
9631
|
if (result.errorCode && INFRASTRUCTURE_ERROR_CODES.includes(result.errorCode)) {
|
|
@@ -9993,10 +10183,10 @@ init_esm_shims();
|
|
|
9993
10183
|
init_project_loader();
|
|
9994
10184
|
init_errors();
|
|
9995
10185
|
import { mkdir as mkdir4, stat as stat2, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
|
|
9996
|
-
import { join as
|
|
10186
|
+
import { join as join19, resolve as resolve7 } from "path";
|
|
9997
10187
|
async function initProject(root, options) {
|
|
9998
10188
|
const absRoot = resolve7(root);
|
|
9999
|
-
const wrapDir =
|
|
10189
|
+
const wrapDir = join19(absRoot, ".story");
|
|
10000
10190
|
let exists = false;
|
|
10001
10191
|
try {
|
|
10002
10192
|
const s = await stat2(wrapDir);
|
|
@@ -10016,11 +10206,11 @@ async function initProject(root, options) {
|
|
|
10016
10206
|
".story/ already exists. Use --force to overwrite config and roadmap."
|
|
10017
10207
|
);
|
|
10018
10208
|
}
|
|
10019
|
-
await mkdir4(
|
|
10020
|
-
await mkdir4(
|
|
10021
|
-
await mkdir4(
|
|
10022
|
-
await mkdir4(
|
|
10023
|
-
await mkdir4(
|
|
10209
|
+
await mkdir4(join19(wrapDir, "tickets"), { recursive: true });
|
|
10210
|
+
await mkdir4(join19(wrapDir, "issues"), { recursive: true });
|
|
10211
|
+
await mkdir4(join19(wrapDir, "handovers"), { recursive: true });
|
|
10212
|
+
await mkdir4(join19(wrapDir, "notes"), { recursive: true });
|
|
10213
|
+
await mkdir4(join19(wrapDir, "lessons"), { recursive: true });
|
|
10024
10214
|
const created = [
|
|
10025
10215
|
".story/config.json",
|
|
10026
10216
|
".story/roadmap.json",
|
|
@@ -10060,7 +10250,7 @@ async function initProject(root, options) {
|
|
|
10060
10250
|
};
|
|
10061
10251
|
await writeConfig(config, absRoot);
|
|
10062
10252
|
await writeRoadmap(roadmap, absRoot);
|
|
10063
|
-
const gitignorePath =
|
|
10253
|
+
const gitignorePath = join19(wrapDir, ".gitignore");
|
|
10064
10254
|
await ensureGitignoreEntries(gitignorePath, STORY_GITIGNORE_ENTRIES);
|
|
10065
10255
|
const warnings = [];
|
|
10066
10256
|
if (options.force && exists) {
|
|
@@ -10099,7 +10289,7 @@ async function ensureGitignoreEntries(gitignorePath, entries) {
|
|
|
10099
10289
|
// src/mcp/index.ts
|
|
10100
10290
|
var ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
|
|
10101
10291
|
var CONFIG_PATH2 = ".story/config.json";
|
|
10102
|
-
var version = "0.1.
|
|
10292
|
+
var version = "0.1.60";
|
|
10103
10293
|
function tryDiscoverRoot() {
|
|
10104
10294
|
const envRoot = process.env[ENV_VAR2];
|
|
10105
10295
|
if (envRoot) {
|
|
@@ -10111,7 +10301,7 @@ function tryDiscoverRoot() {
|
|
|
10111
10301
|
const resolved = resolve8(envRoot);
|
|
10112
10302
|
try {
|
|
10113
10303
|
const canonical = realpathSync2(resolved);
|
|
10114
|
-
if (
|
|
10304
|
+
if (existsSync12(join20(canonical, CONFIG_PATH2))) {
|
|
10115
10305
|
return canonical;
|
|
10116
10306
|
}
|
|
10117
10307
|
process.stderr.write(`Warning: No .story/config.json at ${canonical}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anthropologies/claudestory",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.60",
|
|
4
4
|
"license": "PolyForm-Noncommercial-1.0.0",
|
|
5
5
|
"description": "An agentic development framework. Track tickets, issues, and progress for your project so every session builds on the last.",
|
|
6
6
|
"homepage": "https://claudestory.com",
|
package/src/skill/SKILL.md
CHANGED
|
@@ -23,6 +23,7 @@ claudestory tracks tickets, issues, roadmap, and handovers in a `.story/` direct
|
|
|
23
23
|
- `/story settings` -> manage project settings (see Settings section below)
|
|
24
24
|
- `/story design` -> evaluate frontend design (read `design/design.md` in the same directory as this skill file; if not found, tell user to run `claudestory setup-skill`)
|
|
25
25
|
- `/story design <platform>` -> evaluate for specific platform: web, ios, macos, android (read `design/design.md` in the same directory as this skill file)
|
|
26
|
+
- `/story review-lenses` -> run multi-lens review on current diff (read `review-lenses/review-lenses.md` in the same directory as this skill file; if not found, tell user to run `claudestory setup-skill`). Note: the autonomous guide invokes lenses automatically when `reviewBackends` includes `"lenses"` -- this command is for manual/debug use.
|
|
26
27
|
- `/story help` -> show all capabilities (read `reference.md` in the same directory as this skill file; if not found, tell user to run `claudestory setup-skill`)
|
|
27
28
|
|
|
28
29
|
If the user's intent doesn't match any of these, use the full context load.
|
|
@@ -354,7 +355,24 @@ Do NOT search source code for this. The full config.json schema is shown below.
|
|
|
354
355
|
},
|
|
355
356
|
"LESSON_CAPTURE": { "enabled": "boolean" },
|
|
356
357
|
"ISSUE_SWEEP": { "enabled": "boolean" }
|
|
357
|
-
}
|
|
358
|
+
},
|
|
359
|
+
"lensConfig": {
|
|
360
|
+
"lenses": "\"auto\" | string[] (default: \"auto\")",
|
|
361
|
+
"maxLenses": "number (1-8, default: 8)",
|
|
362
|
+
"lensTimeout": "number | { default: number, opus: number } (default: { default: 60, opus: 120 })",
|
|
363
|
+
"findingBudget": "number (default: 10)",
|
|
364
|
+
"confidenceFloor": "number 0-1 (default: 0.6)",
|
|
365
|
+
"tokenBudgetPerLens": "number (default: 32000)",
|
|
366
|
+
"hotPaths": "string[] (glob patterns for Performance lens, default: [])",
|
|
367
|
+
"lensModels": "Record<string, string> (default: { default: sonnet, security: opus, concurrency: opus })"
|
|
368
|
+
},
|
|
369
|
+
"blockingPolicy": {
|
|
370
|
+
"neverBlock": "string[] (lens names that never produce blocking findings, default: [])",
|
|
371
|
+
"alwaysBlock": "string[] (categories that always block, default: [injection, auth-bypass, hardcoded-secrets])",
|
|
372
|
+
"planReviewBlockingLenses": "string[] (default: [security, error-handling])"
|
|
373
|
+
},
|
|
374
|
+
"requireSecretsGate": "boolean (default: false, require detect-secrets for lens reviews)",
|
|
375
|
+
"requireAccessibility": "boolean (default: false, make accessibility findings blocking)"
|
|
358
376
|
}
|
|
359
377
|
}
|
|
360
378
|
```
|
|
@@ -367,3 +385,4 @@ Additional skill documentation, loaded on demand:
|
|
|
367
385
|
- **`autonomous-mode.md`** -- Autonomous mode, review, plan, and guided execution tiers
|
|
368
386
|
- **`reference.md`** -- Full CLI command and MCP tool reference
|
|
369
387
|
- **`design/design.md`** -- Frontend design evaluation and implementation guidance, with platform references in `design/references/`
|
|
388
|
+
- **`review-lenses/review-lenses.md`** -- Multi-lens review orchestrator (8 specialized parallel reviewers), with lens prompts in `review-lenses/references/`
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: judge
|
|
3
|
+
version: v1
|
|
4
|
+
model: sonnet
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Judge
|
|
8
|
+
|
|
9
|
+
Synthesis step 2. Receives deduplicated findings and tensions from the Merger. Performs severity calibration, stage-aware verdict generation, and completeness assessment.
|
|
10
|
+
|
|
11
|
+
Verdict rules:
|
|
12
|
+
- reject: critical + confidence >= 0.8 + blocking (plan review: only security/integrity)
|
|
13
|
+
- revise: major + blocking, or any blocking tension
|
|
14
|
+
- approve: only minor/suggestion/non-blocking remain
|
|
15
|
+
|
|
16
|
+
Partial review (required lens failed): never approves, maximum is revise.
|
|
17
|
+
|
|
18
|
+
See `src/autonomous/review-lenses/judge.ts` for the full prompt.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: accessibility
|
|
3
|
+
version: v1
|
|
4
|
+
model: sonnet
|
|
5
|
+
type: surface-activated
|
|
6
|
+
maxSeverity: major
|
|
7
|
+
scope: web-first
|
|
8
|
+
activation: ".tsx, .jsx, .html, .vue, .svelte, .css, .scss"
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Accessibility Lens
|
|
12
|
+
|
|
13
|
+
Finds WCAG compliance issues preventing users with disabilities from using the application. Web-first scope. Checks: missing alt text, non-semantic HTML, missing ARIA labels, no keyboard navigation, color contrast, missing focus management, skip-to-content, form labels, ARIA landmarks, auto-playing media, missing live regions, CSS focus removal, hidden-but-focusable elements.
|
|
14
|
+
|
|
15
|
+
Native mobile/desktop accessibility is out of scope for v1.
|
|
16
|
+
|
|
17
|
+
See `src/autonomous/review-lenses/lenses/accessibility.ts` for the full prompt.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: api-design
|
|
3
|
+
version: v1
|
|
4
|
+
model: sonnet
|
|
5
|
+
type: surface-activated
|
|
6
|
+
maxSeverity: critical
|
|
7
|
+
activation: "**/api/**", route handlers, controllers, GraphQL resolvers
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# API Design Lens
|
|
11
|
+
|
|
12
|
+
Focuses on REST/GraphQL API quality -- consistency, correctness, backward compatibility, consumer experience. Checks: breaking changes, inconsistent error format, wrong HTTP status codes, non-RESTful patterns, missing pagination, naming inconsistency, missing Content-Type, overfetching/underfetching, missing idempotency, auth inconsistency.
|
|
13
|
+
|
|
14
|
+
See `src/autonomous/review-lenses/lenses/api-design.ts` for the full prompt.
|