@anthropologies/claudestory 0.1.28 → 0.1.30

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 CHANGED
@@ -491,6 +491,15 @@ var init_project_state = __esm({
491
491
  get blockedCount() {
492
492
  return this.leafTickets.filter((t) => t.status !== "complete" && this.isBlocked(t)).length;
493
493
  }
494
+ /** True when the project has been initialized but not yet populated with tickets/issues/handovers. */
495
+ get isEmptyScaffold() {
496
+ return this.tickets.length === 0 && this.issues.length === 0 && this.handoverFilenames.length === 0 && this.isDefaultScaffoldPhases;
497
+ }
498
+ get isDefaultScaffoldPhases() {
499
+ const { phases } = this.roadmap;
500
+ if (phases.length === 0) return true;
501
+ return phases.length === 1 && phases[0].id === "p0";
502
+ }
494
503
  ticketByID(id) {
495
504
  return this.ticketsByID.get(id);
496
505
  }
@@ -1673,6 +1682,7 @@ var init_queries = __esm({
1673
1682
  // src/core/output-formatter.ts
1674
1683
  var output_formatter_exports = {};
1675
1684
  __export(output_formatter_exports, {
1685
+ EMPTY_SCAFFOLD_HEADING: () => EMPTY_SCAFFOLD_HEADING,
1676
1686
  ExitCode: () => ExitCode,
1677
1687
  errorEnvelope: () => errorEnvelope,
1678
1688
  escapeMarkdownInline: () => escapeMarkdownInline,
@@ -1763,6 +1773,7 @@ function formatStatus(state, format) {
1763
1773
  activeLessons: state.activeLessonCount,
1764
1774
  deprecatedLessons: state.deprecatedLessonCount,
1765
1775
  handovers: state.handoverFilenames.length,
1776
+ isEmptyScaffold: state.isEmptyScaffold,
1766
1777
  phases: phases.map((p) => ({
1767
1778
  id: p.phase.id,
1768
1779
  name: p.phase.name,
@@ -1790,6 +1801,13 @@ function formatStatus(state, format) {
1790
1801
  const summary = p.phase.summary ?? truncate(p.phase.description, 80);
1791
1802
  lines.push(`${indicator} **${escapeMarkdownInline(p.phase.name)}** (${p.leafCount} tickets) \u2014 ${escapeMarkdownInline(summary)}`);
1792
1803
  }
1804
+ if (state.isEmptyScaffold) {
1805
+ lines.push("");
1806
+ lines.push(EMPTY_SCAFFOLD_HEADING);
1807
+ lines.push("");
1808
+ lines.push("This project has been initialized but has no tickets, issues, or handovers yet.");
1809
+ lines.push("Run the /story setup flow to analyze your project and create an initial roadmap.");
1810
+ }
1793
1811
  return lines.join("\n");
1794
1812
  }
1795
1813
  function formatPhaseList(state, format) {
@@ -2629,11 +2647,14 @@ function formatReference(commands, mcpTools, format) {
2629
2647
  lines.push("- **/story not available:** Run `claudestory setup-skill` to install the skill");
2630
2648
  return lines.join("\n");
2631
2649
  }
2632
- function formatRecommendations(result, format) {
2650
+ function formatRecommendations(result, state, format) {
2633
2651
  if (format === "json") {
2634
- return JSON.stringify(successEnvelope(result), null, 2);
2652
+ return JSON.stringify(successEnvelope({ ...result, isEmptyScaffold: state.isEmptyScaffold }), null, 2);
2635
2653
  }
2636
2654
  if (result.recommendations.length === 0) {
2655
+ if (state.isEmptyScaffold) {
2656
+ return "No recommendations yet \u2014 this project needs tickets and phases. Run the /story setup flow to get started.";
2657
+ }
2637
2658
  return "No recommendations \u2014 all work is complete or blocked.";
2638
2659
  }
2639
2660
  const lines = ["# Recommendations", ""];
@@ -2652,12 +2673,13 @@ function formatRecommendations(result, format) {
2652
2673
  }
2653
2674
  return lines.join("\n");
2654
2675
  }
2655
- var ExitCode;
2676
+ var EMPTY_SCAFFOLD_HEADING, ExitCode;
2656
2677
  var init_output_formatter = __esm({
2657
2678
  "src/core/output-formatter.ts"() {
2658
2679
  "use strict";
2659
2680
  init_esm_shims();
2660
2681
  init_queries();
2682
+ EMPTY_SCAFFOLD_HEADING = "## Getting Started";
2661
2683
  ExitCode = {
2662
2684
  OK: 0,
2663
2685
  USER_ERROR: 1,
@@ -4667,7 +4689,7 @@ var init_recommend = __esm({
4667
4689
  // src/cli/commands/recommend.ts
4668
4690
  function handleRecommend(ctx, count) {
4669
4691
  const result = recommend(ctx.state, count);
4670
- return { output: formatRecommendations(result, ctx.format) };
4692
+ return { output: formatRecommendations(result, ctx.state, ctx.format) };
4671
4693
  }
4672
4694
  var init_recommend2 = __esm({
4673
4695
  "src/cli/commands/recommend.ts"() {
@@ -5153,11 +5175,11 @@ var init_session_types = __esm({
5153
5175
  stealReason: z9.string().optional(),
5154
5176
  // Recipe overrides (maxTicketsPerSession: 0 = no limit)
5155
5177
  config: z9.object({
5156
- maxTicketsPerSession: z9.number().min(0).default(0),
5157
- handoverInterval: z9.number().min(0).default(5),
5178
+ maxTicketsPerSession: z9.number().min(0).default(5),
5179
+ handoverInterval: z9.number().min(0).default(3),
5158
5180
  compactThreshold: z9.string().default("high"),
5159
5181
  reviewBackends: z9.array(z9.string()).default(["codex", "agent"])
5160
- }).default({ maxTicketsPerSession: 0, compactThreshold: "high", reviewBackends: ["codex", "agent"], handoverInterval: 5 }),
5182
+ }).default({ maxTicketsPerSession: 5, compactThreshold: "high", reviewBackends: ["codex", "agent"], handoverInterval: 3 }),
5161
5183
  // T-123: Issue sweep tracking
5162
5184
  issueSweepState: z9.object({
5163
5185
  remaining: z9.array(z9.string()),
@@ -5278,10 +5300,10 @@ function createSession(root, recipe, workspaceId, configOverrides) {
5278
5300
  startedAt: now,
5279
5301
  guideCallCount: 0,
5280
5302
  config: {
5281
- maxTicketsPerSession: configOverrides?.maxTicketsPerSession ?? 0,
5303
+ maxTicketsPerSession: configOverrides?.maxTicketsPerSession ?? 5,
5282
5304
  compactThreshold: configOverrides?.compactThreshold ?? "high",
5283
5305
  reviewBackends: configOverrides?.reviewBackends ?? ["codex", "agent"],
5284
- handoverInterval: configOverrides?.handoverInterval ?? 5
5306
+ handoverInterval: configOverrides?.handoverInterval ?? 3
5285
5307
  }
5286
5308
  };
5287
5309
  writeSessionSync(dir, state);
@@ -8253,7 +8275,7 @@ ${ticket.description}` : "",
8253
8275
  });
8254
8276
  const topCandidate = nextResult.kind === "found" ? nextResult.candidates[0] : null;
8255
8277
  const maxTickets = updated.config.maxTicketsPerSession;
8256
- const interval = updated.config.handoverInterval ?? 5;
8278
+ const interval = updated.config.handoverInterval ?? 3;
8257
8279
  const sessionDesc = maxTickets > 0 ? `Work continuously until all tickets are done or you reach ${maxTickets} tickets.` : "Work continuously until all tickets are done.";
8258
8280
  const checkpointDesc = interval > 0 ? ` A checkpoint handover will be saved every ${interval} tickets.` : "";
8259
8281
  const instruction = [
@@ -10217,7 +10239,7 @@ var init_mcp = __esm({
10217
10239
  init_init();
10218
10240
  ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
10219
10241
  CONFIG_PATH2 = ".story/config.json";
10220
- version = "0.1.28";
10242
+ version = "0.1.30";
10221
10243
  main().catch((err) => {
10222
10244
  process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
10223
10245
  `);
@@ -13495,7 +13517,7 @@ async function runCli() {
13495
13517
  registerConfigCommand: registerConfigCommand2,
13496
13518
  registerSessionCommand: registerSessionCommand2
13497
13519
  } = await Promise.resolve().then(() => (init_register(), register_exports));
13498
- const version2 = "0.1.28";
13520
+ const version2 = "0.1.30";
13499
13521
  class HandledError extends Error {
13500
13522
  constructor() {
13501
13523
  super("HANDLED_ERROR");
package/dist/index.d.ts CHANGED
@@ -561,6 +561,9 @@ declare class ProjectState {
561
561
  */
562
562
  isBlocked(ticket: Ticket): boolean;
563
563
  get blockedCount(): number;
564
+ /** True when the project has been initialized but not yet populated with tickets/issues/handovers. */
565
+ get isEmptyScaffold(): boolean;
566
+ private get isDefaultScaffoldPhases();
564
567
  ticketByID(id: string): Ticket | undefined;
565
568
  issueByID(id: string): Issue | undefined;
566
569
  noteByID(id: string): Note | undefined;
@@ -1722,6 +1725,8 @@ declare function buildRecap(currentState: ProjectState, snapshotInfo: {
1722
1725
  filename: string;
1723
1726
  } | null): RecapResult;
1724
1727
 
1728
+ /** SKILL PROTOCOL: SKILL.md Step 2b matches this literal string. Do not change without updating SKILL.md. */
1729
+ declare const EMPTY_SCAFFOLD_HEADING = "## Getting Started";
1725
1730
  declare const ExitCode: {
1726
1731
  readonly OK: 0;
1727
1732
  readonly USER_ERROR: 1;
@@ -1792,6 +1797,6 @@ declare function formatSnapshotResult(result: {
1792
1797
  }, format: OutputFormat): string;
1793
1798
  declare function formatRecap(recap: RecapResult, state: ProjectState, format: OutputFormat): string;
1794
1799
  declare function formatExport(state: ProjectState, mode: "all" | "phase", phaseId: string | null, format: OutputFormat): string;
1795
- declare function formatRecommendations(result: RecommendResult, format: OutputFormat): string;
1800
+ declare function formatRecommendations(result: RecommendResult, state: ProjectState, format: OutputFormat): string;
1796
1801
 
1797
- export { type Blocker, BlockerSchema, CURRENT_SCHEMA_VERSION, type Config, ConfigSchema, DATE_REGEX, DateSchema, 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 };
1802
+ 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 };
package/dist/index.js CHANGED
@@ -326,6 +326,15 @@ var ProjectState = class _ProjectState {
326
326
  get blockedCount() {
327
327
  return this.leafTickets.filter((t) => t.status !== "complete" && this.isBlocked(t)).length;
328
328
  }
329
+ /** True when the project has been initialized but not yet populated with tickets/issues/handovers. */
330
+ get isEmptyScaffold() {
331
+ return this.tickets.length === 0 && this.issues.length === 0 && this.handoverFilenames.length === 0 && this.isDefaultScaffoldPhases;
332
+ }
333
+ get isDefaultScaffoldPhases() {
334
+ const { phases } = this.roadmap;
335
+ if (phases.length === 0) return true;
336
+ return phases.length === 1 && phases[0].id === "p0";
337
+ }
329
338
  ticketByID(id) {
330
339
  return this.ticketsByID.get(id);
331
340
  }
@@ -2289,6 +2298,7 @@ async function pruneSnapshots(dir) {
2289
2298
  }
2290
2299
 
2291
2300
  // src/core/output-formatter.ts
2301
+ var EMPTY_SCAFFOLD_HEADING = "## Getting Started";
2292
2302
  var ExitCode = {
2293
2303
  OK: 0,
2294
2304
  USER_ERROR: 1,
@@ -2343,6 +2353,7 @@ function formatStatus(state, format) {
2343
2353
  activeLessons: state.activeLessonCount,
2344
2354
  deprecatedLessons: state.deprecatedLessonCount,
2345
2355
  handovers: state.handoverFilenames.length,
2356
+ isEmptyScaffold: state.isEmptyScaffold,
2346
2357
  phases: phases.map((p) => ({
2347
2358
  id: p.phase.id,
2348
2359
  name: p.phase.name,
@@ -2370,6 +2381,13 @@ function formatStatus(state, format) {
2370
2381
  const summary = p.phase.summary ?? truncate(p.phase.description, 80);
2371
2382
  lines.push(`${indicator} **${escapeMarkdownInline(p.phase.name)}** (${p.leafCount} tickets) \u2014 ${escapeMarkdownInline(summary)}`);
2372
2383
  }
2384
+ if (state.isEmptyScaffold) {
2385
+ lines.push("");
2386
+ lines.push(EMPTY_SCAFFOLD_HEADING);
2387
+ lines.push("");
2388
+ lines.push("This project has been initialized but has no tickets, issues, or handovers yet.");
2389
+ lines.push("Run the /story setup flow to analyze your project and create an initial roadmap.");
2390
+ }
2373
2391
  return lines.join("\n");
2374
2392
  }
2375
2393
  function formatPhaseList(state, format) {
@@ -2967,11 +2985,14 @@ function formatTicketOneLiner(t, state) {
2967
2985
  const blocked = state.isBlocked(t) ? " [BLOCKED]" : "";
2968
2986
  return `${status} ${t.id}: ${escapeMarkdownInline(t.title)}${blocked}`;
2969
2987
  }
2970
- function formatRecommendations(result, format) {
2988
+ function formatRecommendations(result, state, format) {
2971
2989
  if (format === "json") {
2972
- return JSON.stringify(successEnvelope(result), null, 2);
2990
+ return JSON.stringify(successEnvelope({ ...result, isEmptyScaffold: state.isEmptyScaffold }), null, 2);
2973
2991
  }
2974
2992
  if (result.recommendations.length === 0) {
2993
+ if (state.isEmptyScaffold) {
2994
+ return "No recommendations yet \u2014 this project needs tickets and phases. Run the /story setup flow to get started.";
2995
+ }
2975
2996
  return "No recommendations \u2014 all work is complete or blocked.";
2976
2997
  }
2977
2998
  const lines = ["# Recommendations", ""];
@@ -2996,6 +3017,7 @@ export {
2996
3017
  ConfigSchema,
2997
3018
  DATE_REGEX,
2998
3019
  DateSchema,
3020
+ EMPTY_SCAFFOLD_HEADING,
2999
3021
  ERROR_CODES,
3000
3022
  ExitCode,
3001
3023
  FeaturesSchema,
package/dist/mcp.js CHANGED
@@ -433,6 +433,15 @@ var init_project_state = __esm({
433
433
  get blockedCount() {
434
434
  return this.leafTickets.filter((t) => t.status !== "complete" && this.isBlocked(t)).length;
435
435
  }
436
+ /** True when the project has been initialized but not yet populated with tickets/issues/handovers. */
437
+ get isEmptyScaffold() {
438
+ return this.tickets.length === 0 && this.issues.length === 0 && this.handoverFilenames.length === 0 && this.isDefaultScaffoldPhases;
439
+ }
440
+ get isDefaultScaffoldPhases() {
441
+ const { phases } = this.roadmap;
442
+ if (phases.length === 0) return true;
443
+ return phases.length === 1 && phases[0].id === "p0";
444
+ }
436
445
  ticketByID(id) {
437
446
  return this.ticketsByID.get(id);
438
447
  }
@@ -1563,6 +1572,7 @@ function formatStatus(state, format) {
1563
1572
  activeLessons: state.activeLessonCount,
1564
1573
  deprecatedLessons: state.deprecatedLessonCount,
1565
1574
  handovers: state.handoverFilenames.length,
1575
+ isEmptyScaffold: state.isEmptyScaffold,
1566
1576
  phases: phases.map((p) => ({
1567
1577
  id: p.phase.id,
1568
1578
  name: p.phase.name,
@@ -1590,6 +1600,13 @@ function formatStatus(state, format) {
1590
1600
  const summary = p.phase.summary ?? truncate(p.phase.description, 80);
1591
1601
  lines.push(`${indicator} **${escapeMarkdownInline(p.phase.name)}** (${p.leafCount} tickets) \u2014 ${escapeMarkdownInline(summary)}`);
1592
1602
  }
1603
+ if (state.isEmptyScaffold) {
1604
+ lines.push("");
1605
+ lines.push(EMPTY_SCAFFOLD_HEADING);
1606
+ lines.push("");
1607
+ lines.push("This project has been initialized but has no tickets, issues, or handovers yet.");
1608
+ lines.push("Run the /story setup flow to analyze your project and create an initial roadmap.");
1609
+ }
1593
1610
  return lines.join("\n");
1594
1611
  }
1595
1612
  function formatPhaseList(state, format) {
@@ -2356,11 +2373,14 @@ function formatTicketOneLiner(t, state) {
2356
2373
  const blocked = state.isBlocked(t) ? " [BLOCKED]" : "";
2357
2374
  return `${status} ${t.id}: ${escapeMarkdownInline(t.title)}${blocked}`;
2358
2375
  }
2359
- function formatRecommendations(result, format) {
2376
+ function formatRecommendations(result, state, format) {
2360
2377
  if (format === "json") {
2361
- return JSON.stringify(successEnvelope(result), null, 2);
2378
+ return JSON.stringify(successEnvelope({ ...result, isEmptyScaffold: state.isEmptyScaffold }), null, 2);
2362
2379
  }
2363
2380
  if (result.recommendations.length === 0) {
2381
+ if (state.isEmptyScaffold) {
2382
+ return "No recommendations yet \u2014 this project needs tickets and phases. Run the /story setup flow to get started.";
2383
+ }
2364
2384
  return "No recommendations \u2014 all work is complete or blocked.";
2365
2385
  }
2366
2386
  const lines = ["# Recommendations", ""];
@@ -2379,12 +2399,13 @@ function formatRecommendations(result, format) {
2379
2399
  }
2380
2400
  return lines.join("\n");
2381
2401
  }
2382
- var ExitCode;
2402
+ var EMPTY_SCAFFOLD_HEADING, ExitCode;
2383
2403
  var init_output_formatter = __esm({
2384
2404
  "src/core/output-formatter.ts"() {
2385
2405
  "use strict";
2386
2406
  init_esm_shims();
2387
2407
  init_queries();
2408
+ EMPTY_SCAFFOLD_HEADING = "## Getting Started";
2388
2409
  ExitCode = {
2389
2410
  OK: 0,
2390
2411
  USER_ERROR: 1,
@@ -3596,11 +3617,11 @@ var init_session_types = __esm({
3596
3617
  stealReason: z9.string().optional(),
3597
3618
  // Recipe overrides (maxTicketsPerSession: 0 = no limit)
3598
3619
  config: z9.object({
3599
- maxTicketsPerSession: z9.number().min(0).default(0),
3600
- handoverInterval: z9.number().min(0).default(5),
3620
+ maxTicketsPerSession: z9.number().min(0).default(5),
3621
+ handoverInterval: z9.number().min(0).default(3),
3601
3622
  compactThreshold: z9.string().default("high"),
3602
3623
  reviewBackends: z9.array(z9.string()).default(["codex", "agent"])
3603
- }).default({ maxTicketsPerSession: 0, compactThreshold: "high", reviewBackends: ["codex", "agent"], handoverInterval: 5 }),
3624
+ }).default({ maxTicketsPerSession: 5, compactThreshold: "high", reviewBackends: ["codex", "agent"], handoverInterval: 3 }),
3604
3625
  // T-123: Issue sweep tracking
3605
3626
  issueSweepState: z9.object({
3606
3627
  remaining: z9.array(z9.string()),
@@ -3721,10 +3742,10 @@ function createSession(root, recipe, workspaceId, configOverrides) {
3721
3742
  startedAt: now,
3722
3743
  guideCallCount: 0,
3723
3744
  config: {
3724
- maxTicketsPerSession: configOverrides?.maxTicketsPerSession ?? 0,
3745
+ maxTicketsPerSession: configOverrides?.maxTicketsPerSession ?? 5,
3725
3746
  compactThreshold: configOverrides?.compactThreshold ?? "high",
3726
3747
  reviewBackends: configOverrides?.reviewBackends ?? ["codex", "agent"],
3727
- handoverInterval: configOverrides?.handoverInterval ?? 5
3748
+ handoverInterval: configOverrides?.handoverInterval ?? 3
3728
3749
  }
3729
3750
  };
3730
3751
  writeSessionSync(dir, state);
@@ -4884,7 +4905,7 @@ function sortByPhaseAndOrder(tickets, phaseIndex) {
4884
4905
  init_output_formatter();
4885
4906
  function handleRecommend(ctx, count) {
4886
4907
  const result = recommend(ctx.state, count);
4887
- return { output: formatRecommendations(result, ctx.format) };
4908
+ return { output: formatRecommendations(result, ctx.state, ctx.format) };
4888
4909
  }
4889
4910
 
4890
4911
  // src/cli/commands/snapshot.ts
@@ -7734,7 +7755,7 @@ ${ticket.description}` : "",
7734
7755
  });
7735
7756
  const topCandidate = nextResult.kind === "found" ? nextResult.candidates[0] : null;
7736
7757
  const maxTickets = updated.config.maxTicketsPerSession;
7737
- const interval = updated.config.handoverInterval ?? 5;
7758
+ const interval = updated.config.handoverInterval ?? 3;
7738
7759
  const sessionDesc = maxTickets > 0 ? `Work continuously until all tickets are done or you reach ${maxTickets} tickets.` : "Work continuously until all tickets are done.";
7739
7760
  const checkpointDesc = interval > 0 ? ` A checkpoint handover will be saved every ${interval} tickets.` : "";
7740
7761
  const instruction = [
@@ -9374,7 +9395,7 @@ async function ensureGitignoreEntries(gitignorePath, entries) {
9374
9395
  // src/mcp/index.ts
9375
9396
  var ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
9376
9397
  var CONFIG_PATH2 = ".story/config.json";
9377
- var version = "0.1.28";
9398
+ var version = "0.1.30";
9378
9399
  function tryDiscoverRoot() {
9379
9400
  const envRoot = process.env[ENV_VAR2];
9380
9401
  if (envRoot) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anthropologies/claudestory",
3
- "version": "0.1.28",
3
+ "version": "0.1.30",
4
4
  "license": "UNLICENSED",
5
5
  "description": "Cross-session context persistence for AI coding projects. Tracks tickets, issues, roadmap, and handovers so every session builds on the last.",
6
6
  "keywords": [
@@ -199,6 +199,14 @@ Call these in order:
199
199
  5. **Lessons learned** — call `claudestory_lesson_digest` MCP tool
200
200
  6. **Recent commits** — run `git log --oneline -10`
201
201
 
202
+ ## Step 2b: Empty Scaffold Check
203
+
204
+ After `claudestory_status` returns, check in order:
205
+
206
+ 1. **Integrity guard** — if the response starts with "Warning:" and contains "item(s) skipped due to data integrity issues", this is NOT an empty scaffold. Tell the user to run `claudestory validate`. Continue Step 2/3 normally.
207
+ 2. **Scaffold detection** — check BOTH: output contains "## Getting Started" AND shows `Tickets: 0/0 complete` + `Handovers: 0`. If met AND the project has code indicators (git history, package manifest, source files), route to the AI-Assisted Setup Flow (section 1b above) instead of Step 3. Skip remaining Step 2 calls and Step 3.
208
+ 3. **Empty without code** — if scaffold detected but no code indicators (truly empty directory), continue to Step 3 which will show: "Your project is set up but has no tickets yet. Would you like me to help you create your first phase and tickets?"
209
+
202
210
  ## Step 3: Present Summary
203
211
 
204
212
  After loading context, present a concise summary: