@anthropologies/claudestory 0.1.13 → 0.1.15

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
@@ -262,7 +262,12 @@ var init_config = __esm({
262
262
  type: z6.string(),
263
263
  language: z6.string(),
264
264
  features: FeaturesSchema,
265
- recipe: z6.string().optional()
265
+ recipe: z6.string().optional(),
266
+ recipeOverrides: z6.object({
267
+ maxTicketsPerSession: z6.number().min(0).optional(),
268
+ compactThreshold: z6.string().optional(),
269
+ reviewBackends: z6.array(z6.string()).optional()
270
+ }).optional()
266
271
  }).passthrough();
267
272
  }
268
273
  });
@@ -4491,9 +4496,9 @@ var init_session_types = __esm({
4491
4496
  supersededBy: z8.string().optional(),
4492
4497
  supersededSession: z8.string().optional(),
4493
4498
  stealReason: z8.string().optional(),
4494
- // Recipe overrides
4499
+ // Recipe overrides (maxTicketsPerSession: 0 = no limit)
4495
4500
  config: z8.object({
4496
- maxTicketsPerSession: z8.number().default(3),
4501
+ maxTicketsPerSession: z8.number().min(0).default(3),
4497
4502
  compactThreshold: z8.string().default("high"),
4498
4503
  reviewBackends: z8.array(z8.string()).default(["codex", "agent"])
4499
4504
  }).default({ maxTicketsPerSession: 3, compactThreshold: "high", reviewBackends: ["codex", "agent"] })
@@ -4527,7 +4532,7 @@ function statePath(dir) {
4527
4532
  function eventsPath(dir) {
4528
4533
  return join6(dir, "events.log");
4529
4534
  }
4530
- function createSession(root, recipe, workspaceId) {
4535
+ function createSession(root, recipe, workspaceId, configOverrides) {
4531
4536
  const id = randomUUID();
4532
4537
  const dir = sessionDir(root, id);
4533
4538
  mkdirSync(dir, { recursive: true });
@@ -4563,9 +4568,9 @@ function createSession(root, recipe, workspaceId) {
4563
4568
  startedAt: now,
4564
4569
  guideCallCount: 0,
4565
4570
  config: {
4566
- maxTicketsPerSession: 3,
4567
- compactThreshold: "high",
4568
- reviewBackends: ["codex", "agent"]
4571
+ maxTicketsPerSession: configOverrides?.maxTicketsPerSession ?? 3,
4572
+ compactThreshold: configOverrides?.compactThreshold ?? "high",
4573
+ reviewBackends: configOverrides?.reviewBackends ?? ["codex", "agent"]
4569
4574
  }
4570
4575
  };
4571
4576
  writeSessionSync(dir, state);
@@ -4973,8 +4978,21 @@ async function handleStart(root, args) {
4973
4978
  writeSessionSync(stale.dir, { ...stale.state, status: "superseded" });
4974
4979
  }
4975
4980
  const wsId = deriveWorkspaceId(root);
4976
- const recipe = "coding";
4977
- const session = createSession(root, recipe, wsId);
4981
+ let recipe = "coding";
4982
+ let sessionConfig = {};
4983
+ try {
4984
+ const { state: projectState } = await loadProject(root);
4985
+ const projectConfig = projectState.config;
4986
+ if (typeof projectConfig.recipe === "string") recipe = projectConfig.recipe;
4987
+ if (projectConfig.recipeOverrides && typeof projectConfig.recipeOverrides === "object") {
4988
+ const overrides = projectConfig.recipeOverrides;
4989
+ if (typeof overrides.maxTicketsPerSession === "number") sessionConfig.maxTicketsPerSession = overrides.maxTicketsPerSession;
4990
+ if (typeof overrides.compactThreshold === "string") sessionConfig.compactThreshold = overrides.compactThreshold;
4991
+ if (Array.isArray(overrides.reviewBackends)) sessionConfig.reviewBackends = overrides.reviewBackends;
4992
+ }
4993
+ } catch {
4994
+ }
4995
+ const session = createSession(root, recipe, wsId, sessionConfig);
4978
4996
  const dir = sessionDir(root, session.sessionId);
4979
4997
  try {
4980
4998
  const headResult = await gitHead(root);
@@ -5579,7 +5597,7 @@ async function handleReportComplete(root, dir, state, report) {
5579
5597
  if (pressure === "critical") {
5580
5598
  nextState = "HANDOVER";
5581
5599
  advice = "compact-now";
5582
- } else if (ticketsDone >= maxTickets) {
5600
+ } else if (maxTickets > 0 && ticketsDone >= maxTickets) {
5583
5601
  nextState = "HANDOVER";
5584
5602
  } else if (pressure === "high") {
5585
5603
  advice = "consider-compact";
@@ -6817,7 +6835,7 @@ var init_mcp = __esm({
6817
6835
  init_init();
6818
6836
  ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
6819
6837
  CONFIG_PATH2 = ".story/config.json";
6820
- version = "0.1.13";
6838
+ version = "0.1.15";
6821
6839
  main().catch((err) => {
6822
6840
  process.stderr.write(`Fatal: ${err instanceof Error ? err.message : String(err)}
6823
6841
  `);
@@ -7696,10 +7714,95 @@ var init_hook_status = __esm({
7696
7714
  }
7697
7715
  });
7698
7716
 
7717
+ // src/cli/commands/config-update.ts
7718
+ var config_update_exports = {};
7719
+ __export(config_update_exports, {
7720
+ handleConfigSetOverrides: () => handleConfigSetOverrides
7721
+ });
7722
+ import { readFileSync as readFileSync4 } from "fs";
7723
+ import { join as join15 } from "path";
7724
+ async function handleConfigSetOverrides(root, format, options) {
7725
+ const { json: jsonArg, clear } = options;
7726
+ if (!clear && !jsonArg) {
7727
+ return {
7728
+ output: format === "json" ? JSON.stringify({ version: 1, error: "Provide --json or --clear" }) : "Error: Provide --json or --clear",
7729
+ errorCode: "invalid_input"
7730
+ };
7731
+ }
7732
+ let parsedOverrides = {};
7733
+ if (jsonArg) {
7734
+ try {
7735
+ parsedOverrides = JSON.parse(jsonArg);
7736
+ if (typeof parsedOverrides !== "object" || parsedOverrides === null || Array.isArray(parsedOverrides)) {
7737
+ return {
7738
+ output: format === "json" ? JSON.stringify({ version: 1, error: "Invalid JSON: expected an object" }) : "Error: Invalid JSON: expected an object",
7739
+ errorCode: "invalid_input"
7740
+ };
7741
+ }
7742
+ } catch {
7743
+ return {
7744
+ output: format === "json" ? JSON.stringify({ version: 1, error: "Invalid JSON syntax" }) : "Error: Invalid JSON syntax",
7745
+ errorCode: "invalid_input"
7746
+ };
7747
+ }
7748
+ }
7749
+ let resultOverrides = null;
7750
+ await withProjectLock(root, { strict: false }, async () => {
7751
+ const configPath = join15(root, ".story", "config.json");
7752
+ const rawContent = readFileSync4(configPath, "utf-8");
7753
+ const raw = JSON.parse(rawContent);
7754
+ if (clear) {
7755
+ delete raw.recipeOverrides;
7756
+ } else {
7757
+ const existing = raw.recipeOverrides ?? {};
7758
+ const merged = { ...existing, ...parsedOverrides };
7759
+ for (const [k, v] of Object.entries(merged)) {
7760
+ if (v === null) delete merged[k];
7761
+ }
7762
+ if (Object.keys(merged).length === 0) {
7763
+ delete raw.recipeOverrides;
7764
+ } else {
7765
+ raw.recipeOverrides = merged;
7766
+ }
7767
+ }
7768
+ const validated = ConfigSchema.safeParse(raw);
7769
+ if (!validated.success) {
7770
+ const message = validated.error.issues.map((i) => i.message).join("; ");
7771
+ throw new ProjectLoaderError(
7772
+ "invalid_input",
7773
+ `Invalid config after merge: ${message}`
7774
+ );
7775
+ }
7776
+ await guardPath(configPath, root);
7777
+ await atomicWrite(configPath, JSON.stringify(raw, null, 2) + "\n");
7778
+ resultOverrides = raw.recipeOverrides ?? null;
7779
+ });
7780
+ const data = { recipeOverrides: resultOverrides };
7781
+ if (format === "json") {
7782
+ return { output: JSON.stringify({ version: 1, data }, null, 2) };
7783
+ }
7784
+ if (resultOverrides === null) {
7785
+ return { output: "Recipe overrides cleared (using recipe defaults)." };
7786
+ }
7787
+ const lines = Object.entries(resultOverrides).map(([k, v]) => ` ${k}: ${JSON.stringify(v)}`);
7788
+ return { output: `Recipe overrides updated:
7789
+ ${lines.join("\n")}` };
7790
+ }
7791
+ var init_config_update = __esm({
7792
+ "src/cli/commands/config-update.ts"() {
7793
+ "use strict";
7794
+ init_esm_shims();
7795
+ init_project_loader();
7796
+ init_config();
7797
+ init_errors();
7798
+ }
7799
+ });
7800
+
7699
7801
  // src/cli/register.ts
7700
7802
  var register_exports = {};
7701
7803
  __export(register_exports, {
7702
7804
  registerBlockerCommand: () => registerBlockerCommand,
7805
+ registerConfigCommand: () => registerConfigCommand,
7703
7806
  registerExportCommand: () => registerExportCommand,
7704
7807
  registerHandoverCommand: () => registerHandoverCommand,
7705
7808
  registerHookStatusCommand: () => registerHookStatusCommand,
@@ -9265,6 +9368,51 @@ function registerHookStatusCommand(yargs) {
9265
9368
  }
9266
9369
  );
9267
9370
  }
9371
+ function registerConfigCommand(yargs) {
9372
+ return yargs.command(
9373
+ "config",
9374
+ "Manage project configuration",
9375
+ (y) => y.command(
9376
+ "set-overrides",
9377
+ "Set or clear recipe overrides in config.json",
9378
+ (y2) => y2.option("json", {
9379
+ type: "string",
9380
+ describe: "JSON object to merge into recipeOverrides"
9381
+ }).option("clear", {
9382
+ type: "boolean",
9383
+ describe: "Remove recipeOverrides entirely (reset to defaults)"
9384
+ }).option("format", {
9385
+ choices: ["json", "md"],
9386
+ default: "md",
9387
+ describe: "Output format"
9388
+ }),
9389
+ async (argv) => {
9390
+ const { handleConfigSetOverrides: handleConfigSetOverrides2 } = await Promise.resolve().then(() => (init_config_update(), config_update_exports));
9391
+ const { writeOutput: writeOutput2 } = await Promise.resolve().then(() => (init_run(), run_exports));
9392
+ const format = argv.format;
9393
+ try {
9394
+ const result = await handleConfigSetOverrides2(
9395
+ process.cwd(),
9396
+ format,
9397
+ { json: argv.json, clear: argv.clear === true }
9398
+ );
9399
+ writeOutput2(result.output);
9400
+ if (result.errorCode) process.exitCode = 1;
9401
+ } catch (err) {
9402
+ const { formatError: formatError2, ExitCode: ExitCode2 } = await Promise.resolve().then(() => (init_output_formatter(), output_formatter_exports));
9403
+ const { ProjectLoaderError: ProjectLoaderError2 } = await Promise.resolve().then(() => (init_errors(), errors_exports));
9404
+ if (err instanceof ProjectLoaderError2) {
9405
+ writeOutput2(formatError2(err.code, err.message, format));
9406
+ } else {
9407
+ const message = err instanceof Error ? err.message : String(err);
9408
+ writeOutput2(formatError2("io_error", message, format));
9409
+ }
9410
+ process.exitCode = ExitCode2.USER_ERROR;
9411
+ }
9412
+ }
9413
+ ).demandCommand(1, "Specify a config subcommand. Available: set-overrides")
9414
+ );
9415
+ }
9268
9416
  var init_register = __esm({
9269
9417
  "src/cli/register.ts"() {
9270
9418
  "use strict";
@@ -9319,9 +9467,10 @@ async function runCli() {
9319
9467
  registerReferenceCommand: registerReferenceCommand2,
9320
9468
  registerSelftestCommand: registerSelftestCommand2,
9321
9469
  registerSetupSkillCommand: registerSetupSkillCommand2,
9322
- registerHookStatusCommand: registerHookStatusCommand2
9470
+ registerHookStatusCommand: registerHookStatusCommand2,
9471
+ registerConfigCommand: registerConfigCommand2
9323
9472
  } = await Promise.resolve().then(() => (init_register(), register_exports));
9324
- const version2 = "0.1.13";
9473
+ const version2 = "0.1.15";
9325
9474
  class HandledError extends Error {
9326
9475
  constructor() {
9327
9476
  super("HANDLED_ERROR");
@@ -9360,6 +9509,7 @@ async function runCli() {
9360
9509
  cli = registerSelftestCommand2(cli);
9361
9510
  cli = registerSetupSkillCommand2(cli);
9362
9511
  cli = registerHookStatusCommand2(cli);
9512
+ cli = registerConfigCommand2(cli);
9363
9513
  function handleUnexpectedError(err) {
9364
9514
  if (err instanceof HandledError) return;
9365
9515
  const message = err instanceof Error ? err.message : String(err);
package/dist/index.d.ts CHANGED
@@ -341,6 +341,19 @@ declare const ConfigSchema: z.ZodObject<{
341
341
  reviews: z.ZodBoolean;
342
342
  }, z.ZodTypeAny, "passthrough">>;
343
343
  recipe: z.ZodOptional<z.ZodString>;
344
+ recipeOverrides: z.ZodOptional<z.ZodObject<{
345
+ maxTicketsPerSession: z.ZodOptional<z.ZodNumber>;
346
+ compactThreshold: z.ZodOptional<z.ZodString>;
347
+ reviewBackends: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
348
+ }, "strip", z.ZodTypeAny, {
349
+ maxTicketsPerSession?: number | undefined;
350
+ compactThreshold?: string | undefined;
351
+ reviewBackends?: string[] | undefined;
352
+ }, {
353
+ maxTicketsPerSession?: number | undefined;
354
+ compactThreshold?: string | undefined;
355
+ reviewBackends?: string[] | undefined;
356
+ }>>;
344
357
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
345
358
  version: z.ZodNumber;
346
359
  schemaVersion: z.ZodOptional<z.ZodNumber>;
@@ -367,6 +380,19 @@ declare const ConfigSchema: z.ZodObject<{
367
380
  reviews: z.ZodBoolean;
368
381
  }, z.ZodTypeAny, "passthrough">>;
369
382
  recipe: z.ZodOptional<z.ZodString>;
383
+ recipeOverrides: z.ZodOptional<z.ZodObject<{
384
+ maxTicketsPerSession: z.ZodOptional<z.ZodNumber>;
385
+ compactThreshold: z.ZodOptional<z.ZodString>;
386
+ reviewBackends: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
387
+ }, "strip", z.ZodTypeAny, {
388
+ maxTicketsPerSession?: number | undefined;
389
+ compactThreshold?: string | undefined;
390
+ reviewBackends?: string[] | undefined;
391
+ }, {
392
+ maxTicketsPerSession?: number | undefined;
393
+ compactThreshold?: string | undefined;
394
+ reviewBackends?: string[] | undefined;
395
+ }>>;
370
396
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
371
397
  version: z.ZodNumber;
372
398
  schemaVersion: z.ZodOptional<z.ZodNumber>;
@@ -393,6 +419,19 @@ declare const ConfigSchema: z.ZodObject<{
393
419
  reviews: z.ZodBoolean;
394
420
  }, z.ZodTypeAny, "passthrough">>;
395
421
  recipe: z.ZodOptional<z.ZodString>;
422
+ recipeOverrides: z.ZodOptional<z.ZodObject<{
423
+ maxTicketsPerSession: z.ZodOptional<z.ZodNumber>;
424
+ compactThreshold: z.ZodOptional<z.ZodString>;
425
+ reviewBackends: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
426
+ }, "strip", z.ZodTypeAny, {
427
+ maxTicketsPerSession?: number | undefined;
428
+ compactThreshold?: string | undefined;
429
+ reviewBackends?: string[] | undefined;
430
+ }, {
431
+ maxTicketsPerSession?: number | undefined;
432
+ compactThreshold?: string | undefined;
433
+ reviewBackends?: string[] | undefined;
434
+ }>>;
396
435
  }, z.ZodTypeAny, "passthrough">>;
397
436
  type Config = z.infer<typeof ConfigSchema>;
398
437
 
@@ -853,6 +892,19 @@ declare const SnapshotV1Schema: z.ZodObject<{
853
892
  reviews: z.ZodBoolean;
854
893
  }, z.ZodTypeAny, "passthrough">>;
855
894
  recipe: z.ZodOptional<z.ZodString>;
895
+ recipeOverrides: z.ZodOptional<z.ZodObject<{
896
+ maxTicketsPerSession: z.ZodOptional<z.ZodNumber>;
897
+ compactThreshold: z.ZodOptional<z.ZodString>;
898
+ reviewBackends: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
899
+ }, "strip", z.ZodTypeAny, {
900
+ maxTicketsPerSession?: number | undefined;
901
+ compactThreshold?: string | undefined;
902
+ reviewBackends?: string[] | undefined;
903
+ }, {
904
+ maxTicketsPerSession?: number | undefined;
905
+ compactThreshold?: string | undefined;
906
+ reviewBackends?: string[] | undefined;
907
+ }>>;
856
908
  }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
857
909
  version: z.ZodNumber;
858
910
  schemaVersion: z.ZodOptional<z.ZodNumber>;
@@ -879,6 +931,19 @@ declare const SnapshotV1Schema: z.ZodObject<{
879
931
  reviews: z.ZodBoolean;
880
932
  }, z.ZodTypeAny, "passthrough">>;
881
933
  recipe: z.ZodOptional<z.ZodString>;
934
+ recipeOverrides: z.ZodOptional<z.ZodObject<{
935
+ maxTicketsPerSession: z.ZodOptional<z.ZodNumber>;
936
+ compactThreshold: z.ZodOptional<z.ZodString>;
937
+ reviewBackends: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
938
+ }, "strip", z.ZodTypeAny, {
939
+ maxTicketsPerSession?: number | undefined;
940
+ compactThreshold?: string | undefined;
941
+ reviewBackends?: string[] | undefined;
942
+ }, {
943
+ maxTicketsPerSession?: number | undefined;
944
+ compactThreshold?: string | undefined;
945
+ reviewBackends?: string[] | undefined;
946
+ }>>;
882
947
  }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
883
948
  version: z.ZodNumber;
884
949
  schemaVersion: z.ZodOptional<z.ZodNumber>;
@@ -905,6 +970,19 @@ declare const SnapshotV1Schema: z.ZodObject<{
905
970
  reviews: z.ZodBoolean;
906
971
  }, z.ZodTypeAny, "passthrough">>;
907
972
  recipe: z.ZodOptional<z.ZodString>;
973
+ recipeOverrides: z.ZodOptional<z.ZodObject<{
974
+ maxTicketsPerSession: z.ZodOptional<z.ZodNumber>;
975
+ compactThreshold: z.ZodOptional<z.ZodString>;
976
+ reviewBackends: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
977
+ }, "strip", z.ZodTypeAny, {
978
+ maxTicketsPerSession?: number | undefined;
979
+ compactThreshold?: string | undefined;
980
+ reviewBackends?: string[] | undefined;
981
+ }, {
982
+ maxTicketsPerSession?: number | undefined;
983
+ compactThreshold?: string | undefined;
984
+ reviewBackends?: string[] | undefined;
985
+ }>>;
908
986
  }, z.ZodTypeAny, "passthrough">>;
909
987
  roadmap: z.ZodObject<{
910
988
  title: z.ZodString;
@@ -1185,6 +1263,11 @@ declare const SnapshotV1Schema: z.ZodObject<{
1185
1263
  };
1186
1264
  schemaVersion?: number | undefined;
1187
1265
  recipe?: string | undefined;
1266
+ recipeOverrides?: {
1267
+ maxTicketsPerSession?: number | undefined;
1268
+ compactThreshold?: string | undefined;
1269
+ reviewBackends?: string[] | undefined;
1270
+ } | undefined;
1188
1271
  } & {
1189
1272
  [k: string]: unknown;
1190
1273
  };
@@ -1277,6 +1360,11 @@ declare const SnapshotV1Schema: z.ZodObject<{
1277
1360
  };
1278
1361
  schemaVersion?: number | undefined;
1279
1362
  recipe?: string | undefined;
1363
+ recipeOverrides?: {
1364
+ maxTicketsPerSession?: number | undefined;
1365
+ compactThreshold?: string | undefined;
1366
+ reviewBackends?: string[] | undefined;
1367
+ } | undefined;
1280
1368
  } & {
1281
1369
  [k: string]: unknown;
1282
1370
  };
package/dist/index.js CHANGED
@@ -129,7 +129,12 @@ var ConfigSchema = z6.object({
129
129
  type: z6.string(),
130
130
  language: z6.string(),
131
131
  features: FeaturesSchema,
132
- recipe: z6.string().optional()
132
+ recipe: z6.string().optional(),
133
+ recipeOverrides: z6.object({
134
+ maxTicketsPerSession: z6.number().min(0).optional(),
135
+ compactThreshold: z6.string().optional(),
136
+ reviewBackends: z6.array(z6.string()).optional()
137
+ }).optional()
133
138
  }).passthrough();
134
139
 
135
140
  // src/core/project-state.ts
package/dist/mcp.js CHANGED
@@ -204,7 +204,12 @@ var init_config = __esm({
204
204
  type: z6.string(),
205
205
  language: z6.string(),
206
206
  features: FeaturesSchema,
207
- recipe: z6.string().optional()
207
+ recipe: z6.string().optional(),
208
+ recipeOverrides: z6.object({
209
+ maxTicketsPerSession: z6.number().min(0).optional(),
210
+ compactThreshold: z6.string().optional(),
211
+ reviewBackends: z6.array(z6.string()).optional()
212
+ }).optional()
208
213
  }).passthrough();
209
214
  }
210
215
  });
@@ -4025,9 +4030,9 @@ var SessionStateSchema = z8.object({
4025
4030
  supersededBy: z8.string().optional(),
4026
4031
  supersededSession: z8.string().optional(),
4027
4032
  stealReason: z8.string().optional(),
4028
- // Recipe overrides
4033
+ // Recipe overrides (maxTicketsPerSession: 0 = no limit)
4029
4034
  config: z8.object({
4030
- maxTicketsPerSession: z8.number().default(3),
4035
+ maxTicketsPerSession: z8.number().min(0).default(3),
4031
4036
  compactThreshold: z8.string().default("high"),
4032
4037
  reviewBackends: z8.array(z8.string()).default(["codex", "agent"])
4033
4038
  }).default({ maxTicketsPerSession: 3, compactThreshold: "high", reviewBackends: ["codex", "agent"] })
@@ -4062,7 +4067,7 @@ function statePath(dir) {
4062
4067
  function eventsPath(dir) {
4063
4068
  return join6(dir, "events.log");
4064
4069
  }
4065
- function createSession(root, recipe, workspaceId) {
4070
+ function createSession(root, recipe, workspaceId, configOverrides) {
4066
4071
  const id = randomUUID();
4067
4072
  const dir = sessionDir(root, id);
4068
4073
  mkdirSync(dir, { recursive: true });
@@ -4098,9 +4103,9 @@ function createSession(root, recipe, workspaceId) {
4098
4103
  startedAt: now,
4099
4104
  guideCallCount: 0,
4100
4105
  config: {
4101
- maxTicketsPerSession: 3,
4102
- compactThreshold: "high",
4103
- reviewBackends: ["codex", "agent"]
4106
+ maxTicketsPerSession: configOverrides?.maxTicketsPerSession ?? 3,
4107
+ compactThreshold: configOverrides?.compactThreshold ?? "high",
4108
+ reviewBackends: configOverrides?.reviewBackends ?? ["codex", "agent"]
4104
4109
  }
4105
4110
  };
4106
4111
  writeSessionSync(dir, state);
@@ -4475,8 +4480,21 @@ async function handleStart(root, args) {
4475
4480
  writeSessionSync(stale.dir, { ...stale.state, status: "superseded" });
4476
4481
  }
4477
4482
  const wsId = deriveWorkspaceId(root);
4478
- const recipe = "coding";
4479
- const session = createSession(root, recipe, wsId);
4483
+ let recipe = "coding";
4484
+ let sessionConfig = {};
4485
+ try {
4486
+ const { state: projectState } = await loadProject(root);
4487
+ const projectConfig = projectState.config;
4488
+ if (typeof projectConfig.recipe === "string") recipe = projectConfig.recipe;
4489
+ if (projectConfig.recipeOverrides && typeof projectConfig.recipeOverrides === "object") {
4490
+ const overrides = projectConfig.recipeOverrides;
4491
+ if (typeof overrides.maxTicketsPerSession === "number") sessionConfig.maxTicketsPerSession = overrides.maxTicketsPerSession;
4492
+ if (typeof overrides.compactThreshold === "string") sessionConfig.compactThreshold = overrides.compactThreshold;
4493
+ if (Array.isArray(overrides.reviewBackends)) sessionConfig.reviewBackends = overrides.reviewBackends;
4494
+ }
4495
+ } catch {
4496
+ }
4497
+ const session = createSession(root, recipe, wsId, sessionConfig);
4480
4498
  const dir = sessionDir(root, session.sessionId);
4481
4499
  try {
4482
4500
  const headResult = await gitHead(root);
@@ -5081,7 +5099,7 @@ async function handleReportComplete(root, dir, state, report) {
5081
5099
  if (pressure === "critical") {
5082
5100
  nextState = "HANDOVER";
5083
5101
  advice = "compact-now";
5084
- } else if (ticketsDone >= maxTickets) {
5102
+ } else if (maxTickets > 0 && ticketsDone >= maxTickets) {
5085
5103
  nextState = "HANDOVER";
5086
5104
  } else if (pressure === "high") {
5087
5105
  advice = "consider-compact";
@@ -6016,7 +6034,7 @@ async function ensureGitignoreEntries(gitignorePath, entries) {
6016
6034
  // src/mcp/index.ts
6017
6035
  var ENV_VAR2 = "CLAUDESTORY_PROJECT_ROOT";
6018
6036
  var CONFIG_PATH2 = ".story/config.json";
6019
- var version = "0.1.13";
6037
+ var version = "0.1.15";
6020
6038
  function tryDiscoverRoot() {
6021
6039
  const envRoot = process.env[ENV_VAR2];
6022
6040
  if (envRoot) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anthropologies/claudestory",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
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": [