@anthropologies/claudestory 0.1.62 → 0.1.64
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 +8644 -3917
- package/dist/index.d.ts +22 -5
- package/dist/index.js +137 -14
- package/dist/mcp.js +6215 -2553
- package/package.json +3 -2
- package/src/skill/review-lenses/review-lenses.md +3 -0
package/dist/index.d.ts
CHANGED
|
@@ -550,12 +550,13 @@ declare class ProjectState {
|
|
|
550
550
|
readonly totalTicketCount: number;
|
|
551
551
|
readonly openTicketCount: number;
|
|
552
552
|
readonly completeTicketCount: number;
|
|
553
|
-
readonly
|
|
553
|
+
readonly activeIssueCount: number;
|
|
554
554
|
readonly issuesBySeverity: ReadonlyMap<IssueSeverity, number>;
|
|
555
555
|
readonly activeNoteCount: number;
|
|
556
556
|
readonly archivedNoteCount: number;
|
|
557
557
|
readonly activeLessonCount: number;
|
|
558
558
|
readonly deprecatedLessonCount: number;
|
|
559
|
+
readonly lessonTags: readonly string[];
|
|
559
560
|
constructor(input: {
|
|
560
561
|
tickets: Ticket[];
|
|
561
562
|
issues: Issue[];
|
|
@@ -748,6 +749,12 @@ declare function readHandover(handoversDir: string, filename: string): Promise<s
|
|
|
748
749
|
* Returns the date string or null if the filename does not conform.
|
|
749
750
|
*/
|
|
750
751
|
declare function extractHandoverDate(filename: string): string | null;
|
|
752
|
+
/**
|
|
753
|
+
* Extracts the human-readable title from a handover filename.
|
|
754
|
+
* Strips `.md` extension and YYYY-MM-DD date prefix, converts remaining
|
|
755
|
+
* hyphens to spaces, and trims whitespace. Mirrors Swift parseHandoverFilename.
|
|
756
|
+
*/
|
|
757
|
+
declare function extractHandoverTitle(filename: string): string;
|
|
751
758
|
|
|
752
759
|
interface UmbrellaProgress {
|
|
753
760
|
readonly total: number;
|
|
@@ -1385,6 +1392,7 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1385
1392
|
type: string;
|
|
1386
1393
|
file: string;
|
|
1387
1394
|
}>, "many">>;
|
|
1395
|
+
gitHead: z.ZodOptional<z.ZodString>;
|
|
1388
1396
|
}, "strip", z.ZodTypeAny, {
|
|
1389
1397
|
issues: z.objectOutputType<{
|
|
1390
1398
|
id: z.ZodString;
|
|
@@ -1430,6 +1438,7 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1430
1438
|
} & {
|
|
1431
1439
|
[k: string]: unknown;
|
|
1432
1440
|
};
|
|
1441
|
+
createdAt: string;
|
|
1433
1442
|
tickets: z.objectOutputType<{
|
|
1434
1443
|
id: z.ZodString;
|
|
1435
1444
|
title: z.ZodString;
|
|
@@ -1492,13 +1501,13 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1492
1501
|
supersedes: z.ZodNullable<z.ZodString>;
|
|
1493
1502
|
status: z.ZodEnum<["active", "deprecated", "superseded"]>;
|
|
1494
1503
|
}, z.ZodTypeAny, "passthrough">[];
|
|
1495
|
-
createdAt: string;
|
|
1496
1504
|
handoverFilenames: string[];
|
|
1497
1505
|
warnings?: {
|
|
1498
1506
|
message: string;
|
|
1499
1507
|
type: string;
|
|
1500
1508
|
file: string;
|
|
1501
1509
|
}[] | undefined;
|
|
1510
|
+
gitHead?: string | undefined;
|
|
1502
1511
|
}, {
|
|
1503
1512
|
issues: z.objectInputType<{
|
|
1504
1513
|
id: z.ZodString;
|
|
@@ -1544,6 +1553,7 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1544
1553
|
} & {
|
|
1545
1554
|
[k: string]: unknown;
|
|
1546
1555
|
};
|
|
1556
|
+
createdAt: string;
|
|
1547
1557
|
tickets: z.objectInputType<{
|
|
1548
1558
|
id: z.ZodString;
|
|
1549
1559
|
title: z.ZodString;
|
|
@@ -1583,7 +1593,6 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1583
1593
|
};
|
|
1584
1594
|
version: 1;
|
|
1585
1595
|
project: string;
|
|
1586
|
-
createdAt: string;
|
|
1587
1596
|
notes?: z.objectInputType<{
|
|
1588
1597
|
id: z.ZodString;
|
|
1589
1598
|
title: z.ZodNullable<z.ZodString>;
|
|
@@ -1613,6 +1622,7 @@ declare const SnapshotV1Schema: z.ZodObject<{
|
|
|
1613
1622
|
file: string;
|
|
1614
1623
|
}[] | undefined;
|
|
1615
1624
|
handoverFilenames?: string[] | undefined;
|
|
1625
|
+
gitHead?: string | undefined;
|
|
1616
1626
|
}>;
|
|
1617
1627
|
type SnapshotV1 = z.infer<typeof SnapshotV1Schema>;
|
|
1618
1628
|
/**
|
|
@@ -1739,6 +1749,12 @@ interface SnapshotDiff {
|
|
|
1739
1749
|
removed: string[];
|
|
1740
1750
|
};
|
|
1741
1751
|
}
|
|
1752
|
+
interface RecapStaleness {
|
|
1753
|
+
status: "behind" | "diverged";
|
|
1754
|
+
snapshotSha: string;
|
|
1755
|
+
currentSha: string;
|
|
1756
|
+
commitsBehind?: number;
|
|
1757
|
+
}
|
|
1742
1758
|
interface RecapResult {
|
|
1743
1759
|
snapshot: {
|
|
1744
1760
|
filename: string;
|
|
@@ -1759,6 +1775,7 @@ interface RecapResult {
|
|
|
1759
1775
|
recentlyClearedBlockers: string[];
|
|
1760
1776
|
};
|
|
1761
1777
|
partial: boolean;
|
|
1778
|
+
staleness?: RecapStaleness;
|
|
1762
1779
|
}
|
|
1763
1780
|
/**
|
|
1764
1781
|
* Computes the diff between a snapshot state and the current state.
|
|
@@ -1770,7 +1787,7 @@ declare function diffStates(snapshotState: ProjectState, currentState: ProjectSt
|
|
|
1770
1787
|
declare function buildRecap(currentState: ProjectState, snapshotInfo: {
|
|
1771
1788
|
snapshot: SnapshotV1;
|
|
1772
1789
|
filename: string;
|
|
1773
|
-
} | null): RecapResult
|
|
1790
|
+
} | null, root: string): Promise<RecapResult>;
|
|
1774
1791
|
|
|
1775
1792
|
interface ActiveSessionSummary {
|
|
1776
1793
|
readonly sessionId: string;
|
|
@@ -1854,4 +1871,4 @@ declare function formatRecap(recap: RecapResult, state: ProjectState, format: Ou
|
|
|
1854
1871
|
declare function formatExport(state: ProjectState, mode: "all" | "phase", phaseId: string | null, format: OutputFormat): string;
|
|
1855
1872
|
declare function formatRecommendations(result: RecommendResult, state: ProjectState, format: OutputFormat): string;
|
|
1856
1873
|
|
|
1857
|
-
export { type Blocker, BlockerSchema, CURRENT_SCHEMA_VERSION, type Config, ConfigSchema, DATE_REGEX, DateSchema, EMPTY_SCAFFOLD_HEADING, ERROR_CODES, type ErrorCode, type ErrorEnvelope, ExitCode, type ExitCodeValue, type Features, FeaturesSchema, INTEGRITY_WARNING_TYPES, ISSUE_ID_REGEX, ISSUE_SEVERITIES, ISSUE_STATUSES, type InitOptions, type InitResult, type Issue, IssueIdSchema, IssueSchema, type IssueSeverity, type IssueStatus, type LoadOptions, type LoadResult, type LoadWarning, type LoadWarningType, NOTE_ID_REGEX, NOTE_STATUSES, type NextTicketAllBlocked, type NextTicketAllComplete, type NextTicketEmpty, type NextTicketOutcome, type NextTicketResult, type Note, NoteIdSchema, NoteSchema, type NoteStatus, OUTPUT_FORMATS, type OutputFormat, type PartialEnvelope, type Phase, PhaseSchema, type PhaseStatus, type PhaseWithStatus, ProjectLoaderError, ProjectState, type RecapResult, type RecommendCategory, type RecommendItemKind, type RecommendResult, type Recommendation, type Roadmap, RoadmapSchema, type SnapshotDiff, type SnapshotV1, SnapshotV1Schema, type SuccessEnvelope, TICKET_ID_REGEX, TICKET_STATUSES, TICKET_TYPES, type Ticket, TicketIdSchema, TicketSchema, type TicketStatus, type TicketType, type UmbrellaProgress, type UnblockImpact, type ValidationFinding, type ValidationLevel, type ValidationResult, type WithProjectLockOptions, atomicWrite, blockedTickets, buildRecap, currentPhase, deleteIssue, deleteNote, deleteTicket, descendantLeaves, diffStates, discoverProjectRoot, errorEnvelope, escapeMarkdownInline, extractHandoverDate, fencedBlock, formatBlockedTickets, formatBlockerList, formatError, formatExport, formatHandoverContent, formatHandoverCreateResult, formatHandoverList, formatInitResult, formatIssue, formatIssueList, formatNextTicketOutcome, formatPhaseList, formatPhaseTickets, formatRecap, formatRecommendations, formatSnapshotResult, formatStatus, formatTicket, formatTicketList, formatValidation, guardPath, initProject, isBlockerCleared, listHandovers, loadLatestSnapshot, loadProject, mergeValidation, nextIssueID, nextNoteID, nextOrder, nextTicket, nextTicketID, nextTickets, partialEnvelope, phasesWithStatus, readHandover, recommend, runTransaction, runTransactionUnlocked, saveSnapshot, serializeJSON, sortKeysDeep, successEnvelope, ticketsUnblockedBy, umbrellaProgress, validateProject, withProjectLock, writeConfig, writeIssue, writeIssueUnlocked, writeNote, writeNoteUnlocked, writeRoadmap, writeRoadmapUnlocked, writeTicket, writeTicketUnlocked };
|
|
1874
|
+
export { type Blocker, BlockerSchema, CURRENT_SCHEMA_VERSION, type Config, ConfigSchema, DATE_REGEX, DateSchema, EMPTY_SCAFFOLD_HEADING, ERROR_CODES, type ErrorCode, type ErrorEnvelope, ExitCode, type ExitCodeValue, type Features, FeaturesSchema, INTEGRITY_WARNING_TYPES, ISSUE_ID_REGEX, ISSUE_SEVERITIES, ISSUE_STATUSES, type InitOptions, type InitResult, type Issue, IssueIdSchema, IssueSchema, type IssueSeverity, type IssueStatus, type LoadOptions, type LoadResult, type LoadWarning, type LoadWarningType, NOTE_ID_REGEX, NOTE_STATUSES, type NextTicketAllBlocked, type NextTicketAllComplete, type NextTicketEmpty, type NextTicketOutcome, type NextTicketResult, type Note, NoteIdSchema, NoteSchema, type NoteStatus, OUTPUT_FORMATS, type OutputFormat, type PartialEnvelope, type Phase, PhaseSchema, type PhaseStatus, type PhaseWithStatus, ProjectLoaderError, ProjectState, type RecapResult, type RecapStaleness, type RecommendCategory, type RecommendItemKind, type RecommendResult, type Recommendation, type Roadmap, RoadmapSchema, type SnapshotDiff, type SnapshotV1, SnapshotV1Schema, type SuccessEnvelope, TICKET_ID_REGEX, TICKET_STATUSES, TICKET_TYPES, type Ticket, TicketIdSchema, TicketSchema, type TicketStatus, type TicketType, type UmbrellaProgress, type UnblockImpact, type ValidationFinding, type ValidationLevel, type ValidationResult, type WithProjectLockOptions, atomicWrite, blockedTickets, buildRecap, currentPhase, deleteIssue, deleteNote, deleteTicket, descendantLeaves, diffStates, discoverProjectRoot, errorEnvelope, escapeMarkdownInline, extractHandoverDate, extractHandoverTitle, fencedBlock, formatBlockedTickets, formatBlockerList, formatError, formatExport, formatHandoverContent, formatHandoverCreateResult, formatHandoverList, formatInitResult, formatIssue, formatIssueList, formatNextTicketOutcome, formatPhaseList, formatPhaseTickets, formatRecap, formatRecommendations, formatSnapshotResult, formatStatus, formatTicket, formatTicketList, formatValidation, guardPath, initProject, isBlockerCleared, listHandovers, loadLatestSnapshot, loadProject, mergeValidation, nextIssueID, nextNoteID, nextOrder, nextTicket, nextTicketID, nextTickets, partialEnvelope, phasesWithStatus, readHandover, recommend, runTransaction, runTransactionUnlocked, saveSnapshot, serializeJSON, sortKeysDeep, successEnvelope, ticketsUnblockedBy, umbrellaProgress, validateProject, withProjectLock, writeConfig, writeIssue, writeIssueUnlocked, writeNote, writeNoteUnlocked, writeRoadmap, writeRoadmapUnlocked, writeTicket, writeTicketUnlocked };
|
package/dist/index.js
CHANGED
|
@@ -173,12 +173,13 @@ var ProjectState = class _ProjectState {
|
|
|
173
173
|
totalTicketCount;
|
|
174
174
|
openTicketCount;
|
|
175
175
|
completeTicketCount;
|
|
176
|
-
|
|
176
|
+
activeIssueCount;
|
|
177
177
|
issuesBySeverity;
|
|
178
178
|
activeNoteCount;
|
|
179
179
|
archivedNoteCount;
|
|
180
180
|
activeLessonCount;
|
|
181
181
|
deprecatedLessonCount;
|
|
182
|
+
lessonTags;
|
|
182
183
|
constructor(input) {
|
|
183
184
|
this.tickets = input.tickets;
|
|
184
185
|
this.issues = input.issues;
|
|
@@ -259,19 +260,19 @@ var ProjectState = class _ProjectState {
|
|
|
259
260
|
lByID.set(l.id, l);
|
|
260
261
|
}
|
|
261
262
|
this.lessonsByID = lByID;
|
|
262
|
-
this.totalTicketCount =
|
|
263
|
-
this.openTicketCount =
|
|
263
|
+
this.totalTicketCount = this.leafTickets.length;
|
|
264
|
+
this.openTicketCount = this.leafTickets.filter(
|
|
264
265
|
(t) => t.status !== "complete"
|
|
265
266
|
).length;
|
|
266
|
-
this.completeTicketCount =
|
|
267
|
+
this.completeTicketCount = this.leafTickets.filter(
|
|
267
268
|
(t) => t.status === "complete"
|
|
268
269
|
).length;
|
|
269
|
-
this.
|
|
270
|
-
(i) => i.status
|
|
270
|
+
this.activeIssueCount = input.issues.filter(
|
|
271
|
+
(i) => i.status !== "resolved"
|
|
271
272
|
).length;
|
|
272
273
|
const bySev = /* @__PURE__ */ new Map();
|
|
273
274
|
for (const i of input.issues) {
|
|
274
|
-
if (i.status
|
|
275
|
+
if (i.status !== "resolved") {
|
|
275
276
|
bySev.set(i.severity, (bySev.get(i.severity) ?? 0) + 1);
|
|
276
277
|
}
|
|
277
278
|
}
|
|
@@ -288,6 +289,7 @@ var ProjectState = class _ProjectState {
|
|
|
288
289
|
this.deprecatedLessonCount = this.lessons.filter(
|
|
289
290
|
(l) => l.status === "deprecated" || l.status === "superseded"
|
|
290
291
|
).length;
|
|
292
|
+
this.lessonTags = [...new Set(this.lessons.flatMap((l) => l.tags ?? []))].sort();
|
|
291
293
|
}
|
|
292
294
|
// --- Query Methods ---
|
|
293
295
|
isUmbrella(ticket) {
|
|
@@ -502,6 +504,20 @@ function extractHandoverDate(filename) {
|
|
|
502
504
|
const match = filename.match(HANDOVER_DATE_REGEX);
|
|
503
505
|
return match ? match[0] : null;
|
|
504
506
|
}
|
|
507
|
+
function extractHandoverTitle(filename) {
|
|
508
|
+
let name = filename;
|
|
509
|
+
if (name.endsWith(".md")) {
|
|
510
|
+
name = name.slice(0, -3);
|
|
511
|
+
}
|
|
512
|
+
if (HANDOVER_DATE_REGEX.test(name)) {
|
|
513
|
+
let title = name.slice(10);
|
|
514
|
+
if (title.startsWith("-")) {
|
|
515
|
+
title = title.slice(1);
|
|
516
|
+
}
|
|
517
|
+
return title.replace(/-/g, " ").trim();
|
|
518
|
+
}
|
|
519
|
+
return name;
|
|
520
|
+
}
|
|
505
521
|
|
|
506
522
|
// src/core/project-loader.ts
|
|
507
523
|
async function loadProject(root, options) {
|
|
@@ -2095,6 +2111,75 @@ import { readdir as readdir3, readFile as readFile4, mkdir as mkdir3, unlink as
|
|
|
2095
2111
|
import { existsSync as existsSync4 } from "fs";
|
|
2096
2112
|
import { join as join5, resolve as resolve4 } from "path";
|
|
2097
2113
|
import { z as z8 } from "zod";
|
|
2114
|
+
|
|
2115
|
+
// src/autonomous/git-inspector.ts
|
|
2116
|
+
import { execFile } from "child_process";
|
|
2117
|
+
var GIT_TIMEOUT = 1e4;
|
|
2118
|
+
async function git(cwd, args, parse) {
|
|
2119
|
+
return new Promise((resolve5) => {
|
|
2120
|
+
execFile("git", args, { cwd, timeout: GIT_TIMEOUT, maxBuffer: 10 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
2121
|
+
if (err) {
|
|
2122
|
+
const message = stderr?.trim() || err.message || "unknown git error";
|
|
2123
|
+
resolve5({ ok: false, reason: "git_error", message });
|
|
2124
|
+
return;
|
|
2125
|
+
}
|
|
2126
|
+
try {
|
|
2127
|
+
resolve5({ ok: true, data: parse(stdout) });
|
|
2128
|
+
} catch (parseErr) {
|
|
2129
|
+
resolve5({ ok: false, reason: "parse_error", message: parseErr.message });
|
|
2130
|
+
}
|
|
2131
|
+
});
|
|
2132
|
+
});
|
|
2133
|
+
}
|
|
2134
|
+
var SAFE_REF = /^[0-9a-f]{4,40}$/i;
|
|
2135
|
+
async function gitIsAncestor(cwd, ancestor, descendant) {
|
|
2136
|
+
if (!SAFE_REF.test(ancestor) || !SAFE_REF.test(descendant)) {
|
|
2137
|
+
return { ok: false, reason: "git_error", message: "invalid ref format" };
|
|
2138
|
+
}
|
|
2139
|
+
return new Promise((resolve5) => {
|
|
2140
|
+
execFile(
|
|
2141
|
+
"git",
|
|
2142
|
+
["merge-base", "--is-ancestor", ancestor, descendant],
|
|
2143
|
+
{ cwd, timeout: GIT_TIMEOUT },
|
|
2144
|
+
(err) => {
|
|
2145
|
+
if (!err) {
|
|
2146
|
+
resolve5({ ok: true, data: true });
|
|
2147
|
+
return;
|
|
2148
|
+
}
|
|
2149
|
+
const code = err.code;
|
|
2150
|
+
if (code === 1) {
|
|
2151
|
+
resolve5({ ok: true, data: false });
|
|
2152
|
+
return;
|
|
2153
|
+
}
|
|
2154
|
+
resolve5({ ok: false, reason: "git_error", message: err.message });
|
|
2155
|
+
}
|
|
2156
|
+
);
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
async function gitHeadHash(cwd) {
|
|
2160
|
+
return new Promise((resolve5) => {
|
|
2161
|
+
execFile("git", ["rev-parse", "HEAD"], { cwd, timeout: 3e3 }, (err, stdout) => {
|
|
2162
|
+
if (err) {
|
|
2163
|
+
const message = err.stderr?.trim() || err.message || "unknown git error";
|
|
2164
|
+
resolve5({ ok: false, reason: "git_error", message });
|
|
2165
|
+
return;
|
|
2166
|
+
}
|
|
2167
|
+
resolve5({ ok: true, data: stdout.trim() });
|
|
2168
|
+
});
|
|
2169
|
+
});
|
|
2170
|
+
}
|
|
2171
|
+
async function gitCommitDistance(cwd, fromSha, toSha) {
|
|
2172
|
+
if (!SAFE_REF.test(fromSha) || !SAFE_REF.test(toSha)) {
|
|
2173
|
+
return { ok: false, reason: "git_error", message: "invalid ref format" };
|
|
2174
|
+
}
|
|
2175
|
+
return git(cwd, ["rev-list", "--count", `${fromSha}..${toSha}`], (out) => {
|
|
2176
|
+
const n = parseInt(out.trim(), 10);
|
|
2177
|
+
if (Number.isNaN(n)) throw new Error(`unexpected rev-list output: ${out}`);
|
|
2178
|
+
return n;
|
|
2179
|
+
});
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
// src/core/snapshot.ts
|
|
2098
2183
|
var LoadWarningSchema = z8.object({
|
|
2099
2184
|
type: z8.string(),
|
|
2100
2185
|
file: z8.string(),
|
|
@@ -2111,7 +2196,8 @@ var SnapshotV1Schema = z8.object({
|
|
|
2111
2196
|
notes: z8.array(NoteSchema).optional().default([]),
|
|
2112
2197
|
lessons: z8.array(LessonSchema).optional().default([]),
|
|
2113
2198
|
handoverFilenames: z8.array(z8.string()).optional().default([]),
|
|
2114
|
-
warnings: z8.array(LoadWarningSchema).optional()
|
|
2199
|
+
warnings: z8.array(LoadWarningSchema).optional(),
|
|
2200
|
+
gitHead: z8.string().optional()
|
|
2115
2201
|
});
|
|
2116
2202
|
var MAX_SNAPSHOTS = 20;
|
|
2117
2203
|
async function saveSnapshot(root, loadResult) {
|
|
@@ -2140,6 +2226,10 @@ async function saveSnapshot(root, loadResult) {
|
|
|
2140
2226
|
}))
|
|
2141
2227
|
} : {}
|
|
2142
2228
|
};
|
|
2229
|
+
const headResult = await gitHeadHash(absRoot);
|
|
2230
|
+
if (headResult.ok) {
|
|
2231
|
+
snapshot.gitHead = headResult.data;
|
|
2232
|
+
}
|
|
2143
2233
|
const json = JSON.stringify(snapshot, null, 2) + "\n";
|
|
2144
2234
|
const targetPath = join5(snapshotsDir, filename);
|
|
2145
2235
|
const wrapDir = join5(absRoot, ".story");
|
|
@@ -2335,7 +2425,7 @@ function diffStates(snapshotState, currentState) {
|
|
|
2335
2425
|
handovers: { added: handoversAdded, removed: handoversRemoved }
|
|
2336
2426
|
};
|
|
2337
2427
|
}
|
|
2338
|
-
function buildRecap(currentState, snapshotInfo) {
|
|
2428
|
+
async function buildRecap(currentState, snapshotInfo, root) {
|
|
2339
2429
|
const next = nextTicket(currentState);
|
|
2340
2430
|
const nextTicketAction = next.kind === "found" ? { id: next.ticket.id, title: next.ticket.title, phase: next.ticket.phase } : null;
|
|
2341
2431
|
const highSeverityIssues = currentState.issues.filter(
|
|
@@ -2365,6 +2455,30 @@ function buildRecap(currentState, snapshotInfo) {
|
|
|
2365
2455
|
});
|
|
2366
2456
|
const changes = diffStates(snapshotState, currentState);
|
|
2367
2457
|
const recentlyClearedBlockers = changes.blockers.cleared;
|
|
2458
|
+
let staleness;
|
|
2459
|
+
if (snapshot.gitHead) {
|
|
2460
|
+
const currentHeadResult = await gitHeadHash(root);
|
|
2461
|
+
if (currentHeadResult.ok) {
|
|
2462
|
+
const snapshotSha = snapshot.gitHead;
|
|
2463
|
+
const currentSha = currentHeadResult.data;
|
|
2464
|
+
if (snapshotSha !== currentSha) {
|
|
2465
|
+
const ancestorResult = await gitIsAncestor(root, snapshotSha, currentSha);
|
|
2466
|
+
if (ancestorResult.ok && ancestorResult.data) {
|
|
2467
|
+
const distResult = await gitCommitDistance(root, snapshotSha, currentSha);
|
|
2468
|
+
if (distResult.ok) {
|
|
2469
|
+
staleness = {
|
|
2470
|
+
status: "behind",
|
|
2471
|
+
snapshotSha,
|
|
2472
|
+
currentSha,
|
|
2473
|
+
commitsBehind: distResult.data
|
|
2474
|
+
};
|
|
2475
|
+
}
|
|
2476
|
+
} else if (ancestorResult.ok && !ancestorResult.data) {
|
|
2477
|
+
staleness = { status: "diverged", snapshotSha, currentSha };
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2368
2482
|
return {
|
|
2369
2483
|
snapshot: { filename, createdAt: snapshot.createdAt },
|
|
2370
2484
|
changes,
|
|
@@ -2373,7 +2487,8 @@ function buildRecap(currentState, snapshotInfo) {
|
|
|
2373
2487
|
highSeverityIssues,
|
|
2374
2488
|
recentlyClearedBlockers
|
|
2375
2489
|
},
|
|
2376
|
-
partial: (snapshot.warnings ?? []).length > 0
|
|
2490
|
+
partial: (snapshot.warnings ?? []).length > 0,
|
|
2491
|
+
...staleness ? { staleness } : {}
|
|
2377
2492
|
};
|
|
2378
2493
|
}
|
|
2379
2494
|
function formatSnapshotFilename(date) {
|
|
@@ -2457,7 +2572,7 @@ function formatStatus(state, format, activeSessions = []) {
|
|
|
2457
2572
|
completeTickets: state.completeLeafTicketCount,
|
|
2458
2573
|
openTickets: state.leafTicketCount - state.completeLeafTicketCount,
|
|
2459
2574
|
blockedTickets: state.blockedCount,
|
|
2460
|
-
openIssues: state.
|
|
2575
|
+
openIssues: state.activeIssueCount,
|
|
2461
2576
|
activeNotes: state.activeNoteCount,
|
|
2462
2577
|
archivedNotes: state.archivedNoteCount,
|
|
2463
2578
|
activeLessons: state.activeLessonCount,
|
|
@@ -2479,7 +2594,7 @@ function formatStatus(state, format, activeSessions = []) {
|
|
|
2479
2594
|
`# ${escapeMarkdownInline(state.config.project)}`,
|
|
2480
2595
|
"",
|
|
2481
2596
|
`Tickets: ${state.completeLeafTicketCount}/${state.leafTicketCount} complete, ${state.blockedCount} blocked`,
|
|
2482
|
-
`Issues: ${state.
|
|
2597
|
+
`Issues: ${state.activeIssueCount} open`,
|
|
2483
2598
|
`Notes: ${state.activeNoteCount} active, ${state.archivedNoteCount} archived`,
|
|
2484
2599
|
`Lessons: ${state.activeLessonCount} active, ${state.deprecatedLessonCount} deprecated`,
|
|
2485
2600
|
`Handovers: ${state.handoverFilenames.length}`,
|
|
@@ -2764,7 +2879,7 @@ function formatRecap(recap, state, format) {
|
|
|
2764
2879
|
lines.push("No snapshot found. Run `claudestory snapshot` to enable session diffs.");
|
|
2765
2880
|
lines.push("");
|
|
2766
2881
|
lines.push(`Tickets: ${state.completeLeafTicketCount}/${state.leafTicketCount} complete, ${state.blockedCount} blocked`);
|
|
2767
|
-
lines.push(`Issues: ${state.
|
|
2882
|
+
lines.push(`Issues: ${state.activeIssueCount} open`);
|
|
2768
2883
|
} else {
|
|
2769
2884
|
lines.push(`# ${escapeMarkdownInline(state.config.project)} \u2014 Recap`);
|
|
2770
2885
|
lines.push("");
|
|
@@ -2772,6 +2887,13 @@ function formatRecap(recap, state, format) {
|
|
|
2772
2887
|
if (recap.partial) {
|
|
2773
2888
|
lines.push("**Note:** Snapshot was taken from a project with integrity warnings. Diff may be incomplete.");
|
|
2774
2889
|
}
|
|
2890
|
+
if (recap.staleness) {
|
|
2891
|
+
if (recap.staleness.status === "diverged") {
|
|
2892
|
+
lines.push("**Warning:** Snapshot commit is not an ancestor of current HEAD (history diverged; possible rebase, force-push, or branch switch).");
|
|
2893
|
+
} else if (recap.staleness.status === "behind" && recap.staleness.commitsBehind) {
|
|
2894
|
+
lines.push(`**Warning:** Snapshot is ${recap.staleness.commitsBehind} commit(s) behind HEAD -- context may be stale.`);
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2775
2897
|
const changes = recap.changes;
|
|
2776
2898
|
const hasChanges = hasAnyChanges(changes);
|
|
2777
2899
|
if (!hasChanges) {
|
|
@@ -3033,7 +3155,7 @@ function formatFullExport(state, format) {
|
|
|
3033
3155
|
lines.push(`# ${escapeMarkdownInline(state.config.project)} \u2014 Full Export`);
|
|
3034
3156
|
lines.push("");
|
|
3035
3157
|
lines.push(`Tickets: ${state.completeLeafTicketCount}/${state.leafTicketCount} complete`);
|
|
3036
|
-
lines.push(`Issues: ${state.
|
|
3158
|
+
lines.push(`Issues: ${state.activeIssueCount} open`);
|
|
3037
3159
|
lines.push(`Notes: ${state.activeNoteCount} active, ${state.archivedNoteCount} archived`);
|
|
3038
3160
|
lines.push(`Lessons: ${state.activeLessonCount} active, ${state.deprecatedLessonCount} deprecated`);
|
|
3039
3161
|
lines.push("");
|
|
@@ -3175,6 +3297,7 @@ export {
|
|
|
3175
3297
|
errorEnvelope,
|
|
3176
3298
|
escapeMarkdownInline,
|
|
3177
3299
|
extractHandoverDate,
|
|
3300
|
+
extractHandoverTitle,
|
|
3178
3301
|
fencedBlock,
|
|
3179
3302
|
formatBlockedTickets,
|
|
3180
3303
|
formatBlockerList,
|