@gobing-ai/spur 0.3.83 → 0.3.85

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.
Files changed (70) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.global.yaml +11 -6
  3. package/config/rules/strict/runtime-boundaries.yaml +1 -0
  4. package/config/workflows/idea-pipeline.yaml +1 -1
  5. package/config/workflows/wrapup-pipeline.yaml +1 -1
  6. package/package.json +1 -1
  7. package/plugins/sp/agents/super-planner.md +2 -1
  8. package/plugins/sp/lib/idea-handoff.generated.mjs +1 -1
  9. package/plugins/sp/plugin.json +1 -1
  10. package/plugins/sp/scripts/surface-drift-inventory.ts +1 -1
  11. package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +37 -0
  12. package/plugins/sp/skills/parallel-execution/references/fan-out-patterns.md +55 -1
  13. package/plugins/sp/skills/spec-decomposition/references/decomposition.md +12 -0
  14. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +4 -1
  15. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +60 -4
  16. package/schemas/task-batch.schema.json +5 -0
  17. package/spur.js +1013 -332
  18. package/web/_astro/BoardApp.CHenHFia.js +1 -0
  19. package/web/_astro/{BoardApp.D8bM9pKL.js → BoardApp.yW425dRZ.js} +56 -56
  20. package/web/_astro/{TaskDetail.BPRqgVUE.js → TaskDetail.CGwJAinW.js} +1 -1
  21. package/web/_astro/{arc.BPrPES3z.js → arc.B-qNXzSO.js} +1 -1
  22. package/web/_astro/{architectureDiagram-3BPJPVTR.qX_7q02P.js → architectureDiagram-3BPJPVTR.Bdg-xiji.js} +1 -1
  23. package/web/_astro/{blockDiagram-GPEHLZMM.CUZfj5V7.js → blockDiagram-GPEHLZMM.DFArg3Kp.js} +1 -1
  24. package/web/_astro/{c4Diagram-AAUBKEIU.CTaOr8hH.js → c4Diagram-AAUBKEIU.BkOLcjyx.js} +1 -1
  25. package/web/_astro/channel.CbDHK5UQ.js +1 -0
  26. package/web/_astro/{chunk-2J33WTMH.Dt-9wf3h.js → chunk-2J33WTMH.DN3c20V4.js} +1 -1
  27. package/web/_astro/{chunk-4BX2VUAB.CTC2sdoN.js → chunk-4BX2VUAB.B64K0Ttd.js} +1 -1
  28. package/web/_astro/{chunk-55IACEB6.DQcxt2_g.js → chunk-55IACEB6.C-AJvNwa.js} +1 -1
  29. package/web/_astro/{chunk-727SXJPM.DXFPSn-a.js → chunk-727SXJPM.DMkr46uS.js} +1 -1
  30. package/web/_astro/{chunk-AQP2D5EJ.BCx3U4bT.js → chunk-AQP2D5EJ.4_WGbI64.js} +1 -1
  31. package/web/_astro/{chunk-FMBD7UC4.DL2tJkdO.js → chunk-FMBD7UC4.cbFW_lVE.js} +1 -1
  32. package/web/_astro/{chunk-ND2GUHAM.DZyflMro.js → chunk-ND2GUHAM.CDW_-2O1.js} +1 -1
  33. package/web/_astro/{chunk-QZHKN3VN.CUI2mT09.js → chunk-QZHKN3VN.BoYOt_yg.js} +1 -1
  34. package/web/_astro/{classDiagram-4FO5ZUOK.g4rX4Fr1.js → classDiagram-4FO5ZUOK.BNT6XpHQ.js} +1 -1
  35. package/web/_astro/{classDiagram-v2-Q7XG4LA2.g4rX4Fr1.js → classDiagram-v2-Q7XG4LA2.BNT6XpHQ.js} +1 -1
  36. package/web/_astro/{cose-bilkent-S5V4N54A.CzWJLqp0.js → cose-bilkent-S5V4N54A.CI1DQxR3.js} +1 -1
  37. package/web/_astro/{cynefin-OW5HDTMX.WgsvQeCR.js → cynefin-OW5HDTMX.A8ARH61p.js} +1 -1
  38. package/web/_astro/{dagre-BM42HDAG.Dzv6ngql.js → dagre-BM42HDAG.u28ZmqwR.js} +1 -1
  39. package/web/_astro/{diagram-2AECGRRQ.CpJ4a9rU.js → diagram-2AECGRRQ.Dx8JcrGc.js} +1 -1
  40. package/web/_astro/{diagram-5GNKFQAL.CzPlF_dq.js → diagram-5GNKFQAL.BXJUG9GY.js} +1 -1
  41. package/web/_astro/{diagram-KO2AKTUF.TAkZNTcQ.js → diagram-KO2AKTUF.BtlMuWqW.js} +1 -1
  42. package/web/_astro/{diagram-LMA3HP47.uCjoKSag.js → diagram-LMA3HP47.C4T7qaAy.js} +1 -1
  43. package/web/_astro/{diagram-OG6HWLK6.eMplIjoK.js → diagram-OG6HWLK6.D5HECE10.js} +1 -1
  44. package/web/_astro/{erDiagram-TEJ5UH35.Bf7zoXGz.js → erDiagram-TEJ5UH35.B_hMR3yw.js} +1 -1
  45. package/web/_astro/{flowDiagram-I6XJVG4X.B_bHj3gN.js → flowDiagram-I6XJVG4X.BAmpcYl3.js} +1 -1
  46. package/web/_astro/{ganttDiagram-6RSMTGT7.BasrHRMj.js → ganttDiagram-6RSMTGT7.DV1bSWK-.js} +1 -1
  47. package/web/_astro/{gitGraphDiagram-PVQCEYII.C6iphq1x.js → gitGraphDiagram-PVQCEYII.DxYKkKrJ.js} +1 -1
  48. package/web/_astro/index.CcU5weKX.css +1 -0
  49. package/web/_astro/{infoDiagram-5YYISTIA.HXmDMhW4.js → infoDiagram-5YYISTIA.BAWY2xMb.js} +1 -1
  50. package/web/_astro/{ishikawaDiagram-YF4QCWOH.BSmW8NiU.js → ishikawaDiagram-YF4QCWOH.CZssRn6V.js} +1 -1
  51. package/web/_astro/{journeyDiagram-JHISSGLW.DEQow5fo.js → journeyDiagram-JHISSGLW.C--muARd.js} +1 -1
  52. package/web/_astro/{kanban-definition-UN3LZRKU.IVm9cTdc.js → kanban-definition-UN3LZRKU.D_QCK4et.js} +1 -1
  53. package/web/_astro/{linear.CrsM73_9.js → linear.DzrmTtZ0.js} +1 -1
  54. package/web/_astro/{mermaid.core.CfBeDJls.js → mermaid.core.Da03W3iu.js} +4 -4
  55. package/web/_astro/{mindmap-definition-RKZ34NQL.C3j60Y-0.js → mindmap-definition-RKZ34NQL.6cy-8hR_.js} +1 -1
  56. package/web/_astro/{pieDiagram-4H26LBE5.B-aCMeEA.js → pieDiagram-4H26LBE5.C5rS1pdU.js} +1 -1
  57. package/web/_astro/{quadrantDiagram-W4KKPZXB.Cib965yq.js → quadrantDiagram-W4KKPZXB.w56GZZ6Q.js} +1 -1
  58. package/web/_astro/{requirementDiagram-4Y6WPE33.D61cS4O-.js → requirementDiagram-4Y6WPE33.CipX3Pwu.js} +1 -1
  59. package/web/_astro/{sankeyDiagram-5OEKKPKP.GKF2qVPy.js → sankeyDiagram-5OEKKPKP.C0VVzgJm.js} +1 -1
  60. package/web/_astro/{sequenceDiagram-3UESZ5HK.DZnq8F2h.js → sequenceDiagram-3UESZ5HK.BX2dUUbF.js} +1 -1
  61. package/web/_astro/{stateDiagram-AJRCARHV.DXUFmdgM.js → stateDiagram-AJRCARHV.ypCdgODQ.js} +1 -1
  62. package/web/_astro/{stateDiagram-v2-BHNVJYJU.BtHmhLEz.js → stateDiagram-v2-BHNVJYJU.In0baEtg.js} +1 -1
  63. package/web/_astro/{timeline-definition-PNZ67QCA.Cy-WW2ln.js → timeline-definition-PNZ67QCA.CJN4Vkvl.js} +1 -1
  64. package/web/_astro/{vennDiagram-CIIHVFJN.SLp5b9KI.js → vennDiagram-CIIHVFJN.Cf8KkIPY.js} +1 -1
  65. package/web/_astro/{wardleyDiagram-YWT4CUSO.Bww45mWV.js → wardleyDiagram-YWT4CUSO.DCZBo9xw.js} +1 -1
  66. package/web/_astro/{xychartDiagram-2RQKCTM6.DR4swI6a.js → xychartDiagram-2RQKCTM6.Cm4v_MiJ.js} +1 -1
  67. package/web/index.html +2 -2
  68. package/web/_astro/BoardApp.D-WlxiN2.js +0 -1
  69. package/web/_astro/channel.DGZaFHZx.js +0 -1
  70. package/web/_astro/index.DayyIngm.css +0 -1
package/spur.js CHANGED
@@ -39503,6 +39503,35 @@ var init_projects = __esm(() => {
39503
39503
  });
39504
39504
  });
39505
39505
 
39506
+ // ../../packages/config/src/slash-commands.ts
39507
+ import { homedir as homedir2 } from "os";
39508
+ import { join as join2 } from "path";
39509
+ function getSlashCommandsFilePath() {
39510
+ if (process.env.SPUR_SLASH_COMMANDS_FILE) {
39511
+ return process.env.SPUR_SLASH_COMMANDS_FILE;
39512
+ }
39513
+ return join2(homedir2(), ".config", "spur", "slash_commands.json");
39514
+ }
39515
+ var slashCommandCategorySchema, slashCommandItemSchema, slashCommandsFileSchema;
39516
+ var init_slash_commands = __esm(() => {
39517
+ init_zod();
39518
+ slashCommandCategorySchema = exports_external.enum(["git", "dev", "sys", "harness"]);
39519
+ slashCommandItemSchema = exports_external.object({
39520
+ name: exports_external.string().min(1),
39521
+ description: exports_external.string().min(1),
39522
+ category: slashCommandCategorySchema.default("dev"),
39523
+ argumentHint: exports_external.string().optional(),
39524
+ tags: exports_external.array(exports_external.string()).default([]),
39525
+ role: exports_external.string().optional(),
39526
+ source: exports_external.string().optional()
39527
+ });
39528
+ slashCommandsFileSchema = exports_external.object({
39529
+ version: exports_external.number().int().default(1),
39530
+ updatedAt: exports_external.string().optional(),
39531
+ commands: exports_external.array(slashCommandItemSchema).default([])
39532
+ });
39533
+ });
39534
+
39506
39535
  // ../../packages/config/src/index.ts
39507
39536
  function folderPathWithDefault(defaultPath) {
39508
39537
  return exports_external.preprocess((v) => v == null ? undefined : v, exports_external.string().default(defaultPath));
@@ -39608,6 +39637,7 @@ var init_src = __esm(() => {
39608
39637
  init_zod();
39609
39638
  init_finding_codes();
39610
39639
  init_projects();
39640
+ init_slash_commands();
39611
39641
  SPUR_ENV_VARS = {
39612
39642
  nodeEnv: "NODE_ENV",
39613
39643
  port: "PORT",
@@ -39835,14 +39865,14 @@ var init_src = __esm(() => {
39835
39865
 
39836
39866
  // ../../packages/config/src/bundled-config.ts
39837
39867
  import { existsSync as existsSync3, readdirSync as readdirSync3, statSync as statSync4 } from "fs";
39838
- import { dirname as dirname3, join as join2 } from "path";
39868
+ import { dirname as dirname3, join as join3 } from "path";
39839
39869
  function bundledConfigRoot() {
39840
39870
  if (cachedRoot !== undefined)
39841
39871
  return cachedRoot;
39842
39872
  let dir = import.meta.dirname;
39843
39873
  while (true) {
39844
39874
  for (const name of BUNDLED_CONFIG_DIRS) {
39845
- const candidate = join2(dir, name);
39875
+ const candidate = join3(dir, name);
39846
39876
  if (isBundledConfigDir(candidate)) {
39847
39877
  cachedRoot = candidate;
39848
39878
  return cachedRoot;
@@ -39857,7 +39887,7 @@ function bundledConfigRoot() {
39857
39887
  return cachedRoot;
39858
39888
  }
39859
39889
  function isBundledConfigDir(candidate) {
39860
- return existsSync3(candidate) && statSync4(candidate).isDirectory() && existsSync3(join2(candidate, "rules")) && existsSync3(join2(candidate, "workflows"));
39890
+ return existsSync3(candidate) && statSync4(candidate).isDirectory() && existsSync3(join3(candidate, "rules")) && existsSync3(join3(candidate, "workflows"));
39861
39891
  }
39862
39892
  function listBundledConfigFiles() {
39863
39893
  const root = bundledConfigRoot();
@@ -39868,7 +39898,7 @@ function listBundledConfigFiles() {
39868
39898
  function walk(dir, relPrefix, filter = /\.(ya?ml|json)$/i) {
39869
39899
  const acc = [];
39870
39900
  for (const entry of readdirSync3(dir)) {
39871
- const abs = join2(dir, entry);
39901
+ const abs = join3(dir, entry);
39872
39902
  const rel = relPrefix.length > 0 ? `${relPrefix}/${entry}` : entry;
39873
39903
  if (statSync4(abs).isDirectory()) {
39874
39904
  acc.push(...walk(abs, rel, filter));
@@ -39906,7 +39936,7 @@ var init_template_renderer = () => {};
39906
39936
 
39907
39937
  // ../../packages/config/src/executor-update.ts
39908
39938
  import { chmod, lstat, open, readFile, rename, stat, unlink } from "fs/promises";
39909
- import { basename, dirname as dirname4, join as join3 } from "path";
39939
+ import { basename, dirname as dirname4, join as join4 } from "path";
39910
39940
  async function setProjectExecutorDisabled(projectRoot, executorName, disabled) {
39911
39941
  if (executorName.length === 0) {
39912
39942
  throw new ExecutorUpdateError("INVALID_CONFIG", "executor name must be a non-empty string");
@@ -39914,7 +39944,7 @@ async function setProjectExecutorDisabled(projectRoot, executorName, disabled) {
39914
39944
  if (typeof disabled !== "boolean") {
39915
39945
  throw new ExecutorUpdateError("INVALID_CONFIG", `disabled must be a boolean, received ${typeof disabled}`);
39916
39946
  }
39917
- const configPath = join3(projectRoot, ".spur", "config.yaml");
39947
+ const configPath = join4(projectRoot, ".spur", "config.yaml");
39918
39948
  let original;
39919
39949
  try {
39920
39950
  original = await stat(configPath);
@@ -39969,7 +39999,7 @@ async function setProjectExecutorDisabled(projectRoot, executorName, disabled) {
39969
39999
  }
39970
40000
  target.set("disabled", disabled);
39971
40001
  const serialized = String(doc2);
39972
- const tmpPath = join3(dirname4(configPath), `.${basename(configPath)}.tmp-${process.pid}`);
40002
+ const tmpPath = join4(dirname4(configPath), `.${basename(configPath)}.tmp-${process.pid}`);
39973
40003
  try {
39974
40004
  const latest = await stat(configPath);
39975
40005
  if (latest.mtimeMs !== baseline.mtimeMs || latest.size !== baseline.size || latest.ino !== baseline.ino) {
@@ -40057,8 +40087,8 @@ var init_executor_update = __esm(() => {
40057
40087
 
40058
40088
  // ../../packages/config/src/loader.ts
40059
40089
  import { existsSync as existsSync4, statSync as statSync5 } from "fs";
40060
- import { homedir as homedir2 } from "os";
40061
- import { dirname as dirname5, join as join4, resolve } from "path";
40090
+ import { homedir as homedir3 } from "os";
40091
+ import { dirname as dirname5, join as join5, resolve } from "path";
40062
40092
  import { fileURLToPath as fileURLToPath3 } from "url";
40063
40093
  function embeddedSchemasId(embeddedSchemas) {
40064
40094
  if (embeddedSchemas === undefined)
@@ -40081,7 +40111,7 @@ function cacheKey(configPath, opts, validateJsonSchema2) {
40081
40111
  }
40082
40112
  function resolveSchemaSpecifier(specifier, manifestSpecifier) {
40083
40113
  if (specifier === manifestSpecifier) {
40084
- const workspaceManifest = join4(LOADER_DIR, "..", "..", "..", "apps", "cli", "package.json");
40114
+ const workspaceManifest = join5(LOADER_DIR, "..", "..", "..", "apps", "cli", "package.json");
40085
40115
  if (existsSync4(workspaceManifest))
40086
40116
  return workspaceManifest;
40087
40117
  }
@@ -40096,7 +40126,7 @@ function resolveConfigLayers(cwd) {
40096
40126
  const layers = {};
40097
40127
  const skipProject = cwd === undefined && process.env.SPUR_SKIP_PROJECT_CONFIG === "true";
40098
40128
  if (!skipProject) {
40099
- const projectConfig = join4(cwd ?? process.cwd(), SPUR_CONFIG_DIR, SPUR_CONFIG_FILE);
40129
+ const projectConfig = join5(cwd ?? process.cwd(), SPUR_CONFIG_DIR, SPUR_CONFIG_FILE);
40100
40130
  if (existsSync4(projectConfig))
40101
40131
  layers.project = projectConfig;
40102
40132
  }
@@ -40169,9 +40199,9 @@ function assertNoGlobalFleetSection(globalRaw, layerPath, projectFile) {
40169
40199
  throw new Error(`agent.fleet is not supported in the global config (${layerPath ?? SPUR_CONFIG_FILE}). ` + `Declare the project fleet under agent.fleet in ${projectFile} \u2014 the flock belongs to a project, not to the machine.`);
40170
40200
  }
40171
40201
  function assertNoLegacyFleetFile(projectRoot, projectConfigPath) {
40172
- const candidates = new Set([join4(projectRoot, SPUR_CONFIG_DIR, "fleet.json")]);
40202
+ const candidates = new Set([join5(projectRoot, SPUR_CONFIG_DIR, "fleet.json")]);
40173
40203
  if (projectConfigPath !== undefined)
40174
- candidates.add(join4(dirname5(projectConfigPath), "fleet.json"));
40204
+ candidates.add(join5(dirname5(projectConfigPath), "fleet.json"));
40175
40205
  for (const legacyFile of candidates) {
40176
40206
  if (!existsSync4(legacyFile))
40177
40207
  continue;
@@ -40179,7 +40209,7 @@ function assertNoLegacyFleetFile(projectRoot, projectConfigPath) {
40179
40209
  }
40180
40210
  }
40181
40211
  function projectConfigReplacement(layers) {
40182
- return layers.project ?? join4(SPUR_CONFIG_DIR, SPUR_CONFIG_FILE);
40212
+ return layers.project ?? join5(SPUR_CONFIG_DIR, SPUR_CONFIG_FILE);
40183
40213
  }
40184
40214
  function schemaValidationContext(opts) {
40185
40215
  const embeddedSchemas = opts?.embeddedSchemas;
@@ -40434,7 +40464,7 @@ async function resolvePlanningFolders(fs2) {
40434
40464
  return promise2;
40435
40465
  }
40436
40466
  async function resolvePlanningFoldersUncached(fs2) {
40437
- const configPath = fs2.resolve(join4(SPUR_CONFIG_DIR, SPUR_CONFIG_FILE));
40467
+ const configPath = fs2.resolve(join5(SPUR_CONFIG_DIR, SPUR_CONFIG_FILE));
40438
40468
  if (!await fs2.exists(configPath))
40439
40469
  return defaultPlanningFolders();
40440
40470
  try {
@@ -40473,7 +40503,7 @@ var init_loader = __esm(() => {
40473
40503
  init_template_renderer();
40474
40504
  init_executor_update();
40475
40505
  init_projects();
40476
- GLOBAL_CONFIG_FILE = join4(homedir2(), ".config", "spur", "config.yaml");
40506
+ GLOBAL_CONFIG_FILE = join5(homedir3(), ".config", "spur", "config.yaml");
40477
40507
  LOADER_DIR = dirname5(fileURLToPath3(import.meta.url));
40478
40508
  embeddedSchemasIds = new WeakMap;
40479
40509
  spurConfigCache = new Map;
@@ -68003,7 +68033,7 @@ var init_schema3 = __esm(() => {
68003
68033
  });
68004
68034
 
68005
68035
  // ../../packages/domain/src/migrations.ts
68006
- import { join as join6 } from "path";
68036
+ import { join as join7 } from "path";
68007
68037
  async function allMigrationsJournaled(adapter, migrations) {
68008
68038
  if (migrations.length === 0)
68009
68039
  return true;
@@ -68116,7 +68146,7 @@ async function loadSqlMigrations(folder) {
68116
68146
  for (const entry of entries) {
68117
68147
  migrations.push({
68118
68148
  id: entry.replace(/\.sql$/, ""),
68119
- sql: await fs3.readFile(join6(folder, entry))
68149
+ sql: await fs3.readFile(join7(folder, entry))
68120
68150
  });
68121
68151
  }
68122
68152
  return migrations.length > 0 ? migrations : CLI_MIGRATIONS;
@@ -70803,7 +70833,7 @@ var init_envelope = __esm(() => {
70803
70833
 
70804
70834
  // ../../packages/domain/src/retention.ts
70805
70835
  import { readdirSync as readdirSync5, rmSync as rmSync3, statfsSync, statSync as statSync6 } from "fs";
70806
- import { join as join7 } from "path";
70836
+ import { join as join8 } from "path";
70807
70837
  async function estimateReclaimableBytes(db2) {
70808
70838
  try {
70809
70839
  const rows = await db2.queryAll("SELECT pageno, pagetype, ncell, payload, unused FROM dbstat ORDER BY pageno");
@@ -70871,7 +70901,7 @@ async function runRetention(db2, cwd, now = new Date) {
70871
70901
  result.queueJobs = await purgeQueueJobs(db2, now);
70872
70902
  result.ledgerRows = await purgeLedger(db2, now);
70873
70903
  result.backupFiles = pruneBackups(cwd, now);
70874
- result.compaction = await compactDatabase(db2, { dbPath: join7(cwd, ".spur", "spur.db"), now });
70904
+ result.compaction = await compactDatabase(db2, { dbPath: join8(cwd, ".spur", "spur.db"), now });
70875
70905
  return result;
70876
70906
  }
70877
70907
  async function purgeRuleEvalRuns(db2, now) {
@@ -70884,7 +70914,7 @@ async function purgeLedger(db2, now) {
70884
70914
  return purgeRows(db2, `DELETE FROM history_import_ledger WHERE imported_at < ?`, new Date(now.getTime() - LEDGER_RETENTION_DAYS * DAY_MS).toISOString());
70885
70915
  }
70886
70916
  function pruneBackups(cwd, now) {
70887
- const dir = join7(cwd, ".spur", "backups");
70917
+ const dir = join8(cwd, ".spur", "backups");
70888
70918
  let entries;
70889
70919
  try {
70890
70920
  entries = readdirSync5(dir);
@@ -70894,7 +70924,7 @@ function pruneBackups(cwd, now) {
70894
70924
  const cutoffMs = now.getTime() - BACKUP_RETENTION_DAYS * DAY_MS;
70895
70925
  let reclaimed = 0;
70896
70926
  for (const name of entries) {
70897
- const path9 = join7(dir, name);
70927
+ const path9 = join8(dir, name);
70898
70928
  try {
70899
70929
  const stat2 = statSync6(path9);
70900
70930
  if (!stat2.isFile() || stat2.mtimeMs >= cutoffMs)
@@ -70979,12 +71009,12 @@ var init_maintenance = __esm(() => {
70979
71009
 
70980
71010
  // ../../packages/domain/src/planning/locks.ts
70981
71011
  import { closeSync, fsyncSync, mkdirSync as mkdirSync4, openSync, readFileSync as readFileSync7, renameSync as renameSync3, unlinkSync, writeSync } from "fs";
70982
- import { dirname as dirname8, join as join8 } from "path";
71012
+ import { dirname as dirname8, join as join9 } from "path";
70983
71013
  function lockPathForEntity(folder, entityId) {
70984
- return join8(folder, `.${entityId}.lock`);
71014
+ return join9(folder, `.${entityId}.lock`);
70985
71015
  }
70986
71016
  function createLockPath(folder) {
70987
- return join8(folder, ".create.lock");
71017
+ return join9(folder, ".create.lock");
70988
71018
  }
70989
71019
  function parseLockContent(raw) {
70990
71020
  const str = typeof raw === "string" ? raw : raw.toString();
@@ -71089,7 +71119,7 @@ function fsyncPath(path9, swallowErrors = false) {
71089
71119
  function atomicWrite(dest, content, entityId, fs3, projectName = "spur") {
71090
71120
  const dir = dirname8(dest);
71091
71121
  const rand = Math.random().toString(36).slice(2, 10);
71092
- const tempPath = join8(dir, `.${projectName}.${entityId}.${rand}.tmp`);
71122
+ const tempPath = join9(dir, `.${projectName}.${entityId}.${rand}.tmp`);
71093
71123
  fs3.writeFile(tempPath, content);
71094
71124
  fsyncPath(tempPath);
71095
71125
  renameSync3(tempPath, dest);
@@ -71098,7 +71128,7 @@ function atomicWrite(dest, content, entityId, fs3, projectName = "spur") {
71098
71128
  async function atomicWriteAsync(dest, content, entityId, fs3, projectName = "spur") {
71099
71129
  const dir = dirname8(dest);
71100
71130
  const rand = Math.random().toString(36).slice(2, 10);
71101
- const tempPath = join8(dir, `.${projectName}.${entityId}.${rand}.tmp`);
71131
+ const tempPath = join9(dir, `.${projectName}.${entityId}.${rand}.tmp`);
71102
71132
  await fs3.writeFile(tempPath, content);
71103
71133
  fsyncPath(tempPath);
71104
71134
  renameSync3(tempPath, dest);
@@ -71113,7 +71143,7 @@ function cleanupTempFiles(folder, entityId, fs3, projectName = "spur") {
71113
71143
  let count3 = 0;
71114
71144
  for (const entry of entries) {
71115
71145
  if (entry.startsWith(prefix) && entry.endsWith(suffix)) {
71116
- fs3.deleteFile(join8(folder, entry));
71146
+ fs3.deleteFile(join9(folder, entry));
71117
71147
  count3++;
71118
71148
  }
71119
71149
  }
@@ -71694,6 +71724,7 @@ var init_schema6 = __esm(() => {
71694
71724
  done_forced: exports_external.preprocess((v) => typeof v === "string" ? v === "true" : v, exports_external.boolean().optional()),
71695
71725
  done_reason: exports_external.string().optional(),
71696
71726
  feature_link_declined: exports_external.preprocess((v) => typeof v === "string" ? v === "true" : v, exports_external.boolean().optional()),
71727
+ estimate_hours: exports_external.preprocess((v) => typeof v === "string" && v.trim() !== "" ? Number(v) : v, exports_external.number().positive().optional()),
71697
71728
  created_at: isoDateString,
71698
71729
  updated_at: isoDateString
71699
71730
  });
@@ -71721,7 +71752,8 @@ var init_schema6 = __esm(() => {
71721
71752
  parent_wbs: wbsString,
71722
71753
  priority: exports_external.enum(PRIORITIES).optional(),
71723
71754
  tags: exports_external.array(exports_external.string()).optional(),
71724
- template: exports_external.enum(TASK_VARIANTS).optional()
71755
+ template: exports_external.enum(TASK_VARIANTS).optional(),
71756
+ estimate_hours: exports_external.number().positive().optional()
71725
71757
  }).strict();
71726
71758
  taskBatchSchema = exports_external.array(taskBatchItemSchema).min(1, "batch must contain at least one task");
71727
71759
  });
@@ -73046,7 +73078,7 @@ var init_escalation_packet = __esm(() => {
73046
73078
 
73047
73079
  // ../../packages/app/src/observability/escalation-packet-sink.ts
73048
73080
  import { randomUUID } from "crypto";
73049
- import { join as join9 } from "path";
73081
+ import { join as join10 } from "path";
73050
73082
  function parseDryRunProbeMetadata(metadataJson) {
73051
73083
  if (metadataJson === "")
73052
73084
  return false;
@@ -73093,7 +73125,7 @@ class EscalationPacketSink {
73093
73125
  }
73094
73126
  }
73095
73127
  async readPacket(runId) {
73096
- const path9 = join9(this.options.cwd, ".spur", "run", `${runId}-escalation.json`);
73128
+ const path9 = join10(this.options.cwd, ".spur", "run", `${runId}-escalation.json`);
73097
73129
  if (!await this.options.fs.exists(path9))
73098
73130
  return;
73099
73131
  return JSON.parse(await this.options.fs.readFile(path9));
@@ -73161,7 +73193,7 @@ class EscalationPacketSink {
73161
73193
  const identity6 = await this.resolveIdentity(wbs);
73162
73194
  const now2 = (this.options.now ?? (() => new Date().toISOString()))();
73163
73195
  const packet = buildEscalationPacket({ ...packetInput, identity: identity6, now: now2 });
73164
- const path9 = join9(this.options.cwd, ".spur", "run", `${input.runId}-escalation.json`);
73196
+ const path9 = join10(this.options.cwd, ".spur", "run", `${input.runId}-escalation.json`);
73165
73197
  if (!await this.options.fs.exists(path9)) {
73166
73198
  await this.options.fs.writeFile(path9, `${JSON.stringify(packet, null, 2)}
73167
73199
  `);
@@ -73452,7 +73484,7 @@ var init_step_reporter = () => {};
73452
73484
 
73453
73485
  // ../../packages/app/src/observability/workflow-run-log-sink.ts
73454
73486
  import { closeSync as closeSync2, mkdirSync as mkdirSync5, openSync as openSync2, writeSync as writeSync2 } from "fs";
73455
- import { join as join10 } from "path";
73487
+ import { join as join11 } from "path";
73456
73488
 
73457
73489
  class WorkflowRunLogSink {
73458
73490
  filePath;
@@ -73468,7 +73500,7 @@ class WorkflowRunLogSink {
73468
73500
  bus;
73469
73501
  handlers;
73470
73502
  constructor(options) {
73471
- this.filePath = join10(options.dir, `${options.runId}.log`);
73503
+ this.filePath = join11(options.dir, `${options.runId}.log`);
73472
73504
  this.maxBytes = options.maxBytes ?? DEFAULT_RUN_LOG_MAX_BYTES;
73473
73505
  this.maxLines = options.maxLines;
73474
73506
  this.planPreview = options.planPreview;
@@ -73923,7 +73955,7 @@ var init_failure_classification = __esm(() => {
73923
73955
  });
73924
73956
 
73925
73957
  // ../../packages/app/src/services/run-session-observer.ts
73926
- import { homedir as homedir3 } from "os";
73958
+ import { homedir as homedir4 } from "os";
73927
73959
  import { basename as basename2, isAbsolute as isAbsolute2, resolve as resolve3 } from "path";
73928
73960
 
73929
73961
  class RunSessionObserver {
@@ -73935,7 +73967,7 @@ class RunSessionObserver {
73935
73967
  now;
73936
73968
  constructor(options) {
73937
73969
  this.options = options;
73938
- this.home = options.home ?? homedir3();
73970
+ this.home = options.home ?? homedir4();
73939
73971
  this.cwd = options.cwd ?? process.cwd();
73940
73972
  this.now = options.now ?? Date.now;
73941
73973
  }
@@ -74147,7 +74179,7 @@ var init_run_session_observer = __esm(() => {
74147
74179
 
74148
74180
  // ../../packages/app/src/services/agent-service.ts
74149
74181
  import { createHash as createHash8 } from "crypto";
74150
- import { homedir as homedir4 } from "os";
74182
+ import { homedir as homedir5 } from "os";
74151
74183
  import { isatty as isatty3 } from "tty";
74152
74184
 
74153
74185
  class AgentService {
@@ -74565,7 +74597,7 @@ class AgentService {
74565
74597
  getDb,
74566
74598
  output: this.ctx.output,
74567
74599
  registry: this.sessionRootRegistry,
74568
- home: homedir4(),
74600
+ home: homedir5(),
74569
74601
  cwd: cwd || this.ctx.cwd,
74570
74602
  json: jsonOutput
74571
74603
  }));
@@ -75669,15 +75701,15 @@ var init_agent_service = __esm(() => {
75669
75701
  // ../../packages/app/src/services/project-registry.ts
75670
75702
  import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync8, realpathSync as realpathSync3, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
75671
75703
  import { connect, createServer } from "net";
75672
- import { homedir as homedir5 } from "os";
75673
- import { dirname as dirname9, join as join11, resolve as resolve4 } from "path";
75704
+ import { homedir as homedir6 } from "os";
75705
+ import { dirname as dirname9, join as join12, resolve as resolve4 } from "path";
75674
75706
  function normalizeProjectPath(pathInput) {
75675
75707
  const trimmed = pathInput.trim();
75676
75708
  let expanded = trimmed;
75677
75709
  if (trimmed === "~") {
75678
- expanded = homedir5();
75710
+ expanded = homedir6();
75679
75711
  } else if (trimmed.startsWith("~/")) {
75680
- expanded = join11(homedir5(), trimmed.slice(2));
75712
+ expanded = join12(homedir6(), trimmed.slice(2));
75681
75713
  } else {
75682
75714
  expanded = resolve4(trimmed);
75683
75715
  }
@@ -75978,7 +76010,7 @@ var init_project_registry = __esm(() => {
75978
76010
  });
75979
76011
 
75980
76012
  // ../../packages/app/src/services/fleet-service.ts
75981
- import { basename as basename3, dirname as dirname10, join as join12 } from "path";
76013
+ import { basename as basename3, dirname as dirname10, join as join13 } from "path";
75982
76014
  function resolveMemberExecutor(params) {
75983
76015
  const { member, index: index2, label, agentConfig, roles, roster } = params;
75984
76016
  if (member.executor !== undefined) {
@@ -76188,7 +76220,7 @@ class FleetService {
76188
76220
  }
76189
76221
  const slug = await this.projectSlug(normalized);
76190
76222
  const config4 = await this.effectiveConfig();
76191
- const configDir = join12(normalized, ".spur", "agents");
76223
+ const configDir = join13(normalized, ".spur", "agents");
76192
76224
  const specs = await loadAgentSpecs(configDir);
76193
76225
  const projection = materializeRoster({
76194
76226
  slug,
@@ -76242,7 +76274,7 @@ class FleetService {
76242
76274
  }
76243
76275
  async assertLaunchGroundTruth(projectPath) {
76244
76276
  const expected = normalizeProjectPath(projectPath);
76245
- const expectedStorageRoot = join12(expected, ".spur");
76277
+ const expectedStorageRoot = join13(expected, ".spur");
76246
76278
  const cwd = normalizeProjectPath(process.cwd());
76247
76279
  const storageRoot = normalizeProjectPath(this.fs.resolve(".spur"));
76248
76280
  const databasePath = this.ctx.openDb ? await new ProjectClaimDao(await this.ctx.openDb(expected)).databasePath() : "";
@@ -76350,7 +76382,7 @@ var init_task_locator = __esm(() => {
76350
76382
  });
76351
76383
 
76352
76384
  // ../../packages/app/src/services/agent-coordination-service.ts
76353
- import { join as join13 } from "path";
76385
+ import { join as join14 } from "path";
76354
76386
 
76355
76387
  class AgentCoordinationService {
76356
76388
  ctx;
@@ -76358,7 +76390,7 @@ class AgentCoordinationService {
76358
76390
  orchestratorPromise;
76359
76391
  constructor(ctx) {
76360
76392
  this.ctx = ctx;
76361
- this.configDir = join13(ctx.cwd, ".spur", "agents");
76393
+ this.configDir = join14(ctx.cwd, ".spur", "agents");
76362
76394
  }
76363
76395
  async sendMessage(fromId, toId, body, replyTo, requestKey) {
76364
76396
  validateAgentId(toId);
@@ -76624,7 +76656,7 @@ class AgentCoordinationService {
76624
76656
  async resolveTaskFile(taskId) {
76625
76657
  const fs3 = this.ctx.fs;
76626
76658
  const { foldersConfig } = await resolvePlanningFolders(fs3);
76627
- const dirs = [...new Set([foldersConfig.active_folder, ...Object.keys(foldersConfig.folders)])].map((dir) => join13(this.ctx.cwd, dir));
76659
+ const dirs = [...new Set([foldersConfig.active_folder, ...Object.keys(foldersConfig.folders)])].map((dir) => join14(this.ctx.cwd, dir));
76628
76660
  return await TaskLocator.forDirs(fs3, dirs).findPathByWbs(taskId);
76629
76661
  }
76630
76662
  }
@@ -76976,7 +77008,7 @@ var init_agent_quota_updates = __esm(() => {
76976
77008
  });
76977
77009
 
76978
77010
  // ../../packages/app/src/services/anchor-qualifier.ts
76979
- import { basename as basename4, join as join14 } from "path";
77011
+ import { basename as basename4, join as join15 } from "path";
76980
77012
  async function resolveRepoRoot(projectRoot, hintDir) {
76981
77013
  if (projectRoot)
76982
77014
  return projectRoot;
@@ -77105,7 +77137,7 @@ async function qualifyAnchors(fs3, opts) {
77105
77137
  } catch {
77106
77138
  continue;
77107
77139
  }
77108
- const mdFiles = entries.filter((name) => name.endsWith(".md") && name !== "kanban.md").map((name) => join14(dir, name)).sort();
77140
+ const mdFiles = entries.filter((name) => name.endsWith(".md") && name !== "kanban.md").map((name) => join15(dir, name)).sort();
77109
77141
  for (const filePath of mdFiles) {
77110
77142
  let raw;
77111
77143
  try {
@@ -78327,7 +78359,7 @@ __export(exports_feature_check, {
78327
78359
  FeatureCheckService: () => FeatureCheckService,
78328
78360
  DEFAULT_FEATURE_MATRIX: () => DEFAULT_FEATURE_MATRIX
78329
78361
  });
78330
- import { dirname as dirname11, join as join15 } from "path";
78362
+ import { dirname as dirname11, join as join16 } from "path";
78331
78363
  function isGroupFeature(fm) {
78332
78364
  const tags = fm.tags;
78333
78365
  return Array.isArray(tags) && tags.includes("group") ? "group" : "standard";
@@ -78425,9 +78457,9 @@ function decodeVerdictRows(source, sectionName, required2) {
78425
78457
  function defaultVerdictRunDir(tasksDir) {
78426
78458
  const norm = tasksDir.replace(/\\/g, "/");
78427
78459
  if (/\/docs\/tasks\d*$/.test(norm) || /\/docs\/tasks$/.test(norm)) {
78428
- return join15(dirname11(dirname11(tasksDir)), ".spur", "run");
78460
+ return join16(dirname11(dirname11(tasksDir)), ".spur", "run");
78429
78461
  }
78430
- return join15(dirname11(tasksDir), ".spur", "run");
78462
+ return join16(dirname11(tasksDir), ".spur", "run");
78431
78463
  }
78432
78464
  function rowMatchesScenario(id, sc) {
78433
78465
  const stripped = id.replace(/^\[[^\]]*\]\s*/, "").replace(/^Scenario:\s*/i, "").replace(/^\[[^\]]*\]\s*/, "").trim();
@@ -78557,7 +78589,7 @@ var init_feature_check = __esm(() => {
78557
78589
  await this.checkOneActiveGoal(fm, featureId2, options.featuresDir, findings, options.asStatus);
78558
78590
  await this.checkChildrenLimit(featureId2, options.featuresDir, findings);
78559
78591
  }
78560
- const dogfoodDir = options?.dogfoodDir ?? (options?.featuresDir ? join15(dirname11(options.featuresDir), "dogfood") : undefined);
78592
+ const dogfoodDir = options?.dogfoodDir ?? (options?.featuresDir ? join16(dirname11(options.featuresDir), "dogfood") : undefined);
78561
78593
  const primaryTasksDir = options?.tasksDir ?? options?.tasksDirs?.[0];
78562
78594
  const runDir = options?.runDir ?? (primaryTasksDir ? defaultVerdictRunDir(primaryTasksDir) : undefined);
78563
78595
  const taskScanDirs = options?.tasksDirs && options.tasksDirs.length > 0 ? options.tasksDirs : options?.tasksDir ? [options.tasksDir] : [];
@@ -78988,7 +79020,7 @@ var init_feature_check = __esm(() => {
78988
79020
  });
78989
79021
 
78990
79022
  // ../../packages/app/src/services/task-check.ts
78991
- import { basename as basename5, dirname as dirname12, join as join16 } from "path";
79023
+ import { basename as basename5, dirname as dirname12, join as join17 } from "path";
78992
79024
  function isPlaceholderBody(body) {
78993
79025
  const stripped = body.replace(/<!--[\s\S]*?-->/g, "").replace(/^\s*>\s*TBD\s*$/gim, "").trim();
78994
79026
  return stripped.length === 0;
@@ -79248,7 +79280,7 @@ var init_task_check = __esm(() => {
79248
79280
  this.runL2(doc2, entry, findings, raw);
79249
79281
  this.runL3(doc2, entry, effectiveStatus, findings, isTransitionTarget);
79250
79282
  const tasksDir = dirname12(filePath);
79251
- const featuresDir = join16(dirname12(tasksDir), "features");
79283
+ const featuresDir = join17(dirname12(tasksDir), "features");
79252
79284
  await this.runL4(doc2, fm, effectiveStatus, findings, featuresDir, tasksDir, wbs);
79253
79285
  await this.runL4Rollup(doc2, wbs, effectiveStatus, findings, tasksDir);
79254
79286
  if (effectiveStatus !== "done" && effectiveStatus !== "cancelled") {
@@ -79815,7 +79847,7 @@ var init_task_check = __esm(() => {
79815
79847
  for (const cite of citations) {
79816
79848
  if (reported >= 5)
79817
79849
  break;
79818
- const abs = join16(projectRoot, cite.path);
79850
+ const abs = join17(projectRoot, cite.path);
79819
79851
  let exists2 = false;
79820
79852
  try {
79821
79853
  exists2 = await this.fs.exists(abs);
@@ -79899,7 +79931,7 @@ var init_task_check = __esm(() => {
79899
79931
  continue;
79900
79932
  }
79901
79933
  try {
79902
- const abs = join16(dir, name);
79934
+ const abs = join17(dir, name);
79903
79935
  const stat2 = await this.fs.stat(abs);
79904
79936
  if (stat2 !== null && stat2 !== undefined && stat2.isDirectory?.())
79905
79937
  stack.push(abs);
@@ -79994,7 +80026,7 @@ var init_task_check = __esm(() => {
79994
80026
  }
79995
80027
  async checkVerdictArtifact(wbs, tasksDir, findings) {
79996
80028
  const projectRoot = resolveProjectRootFromTasksDir(tasksDir);
79997
- const runDir = join16(projectRoot, ".spur", "run");
80029
+ const runDir = join17(projectRoot, ".spur", "run");
79998
80030
  const verdictPath = `${runDir}/${wbs}-verdict.json`;
79999
80031
  const outcome = await readVerifyVerdict(this.fs, verdictPath, wbs);
80000
80032
  if (outcome.kind === "missing")
@@ -80039,13 +80071,13 @@ var init_task_check = __esm(() => {
80039
80071
  });
80040
80072
 
80041
80073
  // ../../packages/app/src/services/corpus-check.ts
80042
- import { basename as basename6, dirname as dirname13, join as join17, relative, resolve as resolve5 } from "path";
80074
+ import { basename as basename6, dirname as dirname13, join as join18, relative, resolve as resolve5 } from "path";
80043
80075
  async function loadTaskMatrix(projectRoot) {
80044
80076
  const fs3 = createNodeFileSystem3(projectRoot);
80045
- const candidates = [join17(projectRoot, ".spur", "tasks", "section-matrix.yaml")];
80077
+ const candidates = [join18(projectRoot, ".spur", "tasks", "section-matrix.yaml")];
80046
80078
  const bundledRoot = bundledConfigRoot();
80047
80079
  if (bundledRoot !== null)
80048
- candidates.push(join17(bundledRoot, "tasks", "section-matrix.yaml"));
80080
+ candidates.push(join18(bundledRoot, "tasks", "section-matrix.yaml"));
80049
80081
  let matrixPath;
80050
80082
  for (const candidate of candidates) {
80051
80083
  if (await fs3.exists(candidate)) {
@@ -80088,7 +80120,7 @@ async function structuralSweep(projectRoot) {
80088
80120
  const wbs = fileName.match(/^(\d{4})_.+\.md$/)?.[1];
80089
80121
  if (wbs === undefined)
80090
80122
  continue;
80091
- const result = await taskService.check(join17(tasksDir, fileName), wbs);
80123
+ const result = await taskService.check(join18(tasksDir, fileName), wbs);
80092
80124
  for (const finding of result.findings) {
80093
80125
  findings.push({
80094
80126
  kind: "task",
@@ -80106,7 +80138,7 @@ async function structuralSweep(projectRoot) {
80106
80138
  const id = fileName.match(/^([A-Z][0-9]*)_.+\.md$/)?.[1];
80107
80139
  if (id === undefined)
80108
80140
  continue;
80109
- const result = await featureService.check(join17(featuresDir, fileName), id, {
80141
+ const result = await featureService.check(join18(featuresDir, fileName), id, {
80110
80142
  featuresDir,
80111
80143
  tasksDir: activeTasksDir,
80112
80144
  tasksDirs: taskDirs,
@@ -80135,7 +80167,7 @@ async function duplicateIds(cwd, taskDirs, featuresDir) {
80135
80167
  return [];
80136
80168
  }
80137
80169
  const pattern = kind === "task" ? /^(\d{4})_/ : /^([A-Z][0-9]*)_/;
80138
- return names.map((n2) => ({ m: n2.match(pattern), n: n2 })).filter((x) => x.m !== null).map((x) => ({ id: x.m[1], file: relative(cwd, join17(dir, x.n)), kind }));
80170
+ return names.map((n2) => ({ m: n2.match(pattern), n: n2 })).filter((x) => x.m !== null).map((x) => ({ id: x.m[1], file: relative(cwd, join18(dir, x.n)), kind }));
80139
80171
  };
80140
80172
  const all = [
80141
80173
  ...(await Promise.all(taskDirs.map((dir) => scan(dir, "task")))).flat(),
@@ -80189,7 +80221,7 @@ function resolveProjectRoot(cwd) {
80189
80221
  const fs3 = createNodeFileSystem3(cwd);
80190
80222
  let current = resolve5(cwd);
80191
80223
  while (true) {
80192
- if (fs3.exists(join17(current, ".spur", "config.yaml"))) {
80224
+ if (fs3.exists(join18(current, ".spur", "config.yaml"))) {
80193
80225
  return current;
80194
80226
  }
80195
80227
  const parent = dirname13(current);
@@ -80211,14 +80243,14 @@ function diskReader(cwd) {
80211
80243
  return {
80212
80244
  list: async (dir) => {
80213
80245
  try {
80214
- return (await fs3.readDir(join17(cwd, dir))).map((n2) => `${dir}/${n2}`);
80246
+ return (await fs3.readDir(join18(cwd, dir))).map((n2) => `${dir}/${n2}`);
80215
80247
  } catch {
80216
80248
  return [];
80217
80249
  }
80218
80250
  },
80219
80251
  read: async (path9) => {
80220
80252
  try {
80221
- return await fs3.readFile(join17(cwd, path9));
80253
+ return await fs3.readFile(join18(cwd, path9));
80222
80254
  } catch {
80223
80255
  return null;
80224
80256
  }
@@ -80378,7 +80410,7 @@ var init_corpus_check = __esm(() => {
80378
80410
  });
80379
80411
 
80380
80412
  // ../../packages/app/src/services/corpus-migrator.ts
80381
- import { join as join18, relative as relative2 } from "path";
80413
+ import { join as join19, relative as relative2 } from "path";
80382
80414
  function applyM1(data, flags) {
80383
80415
  const raw = data.status;
80384
80416
  if (typeof raw !== "string")
@@ -80678,7 +80710,7 @@ class CorpusMigrator {
80678
80710
  }
80679
80711
  async discoverFiles() {
80680
80712
  const entries = await this.fs.readDir(this.corpusDir);
80681
- return entries.filter((name) => name.endsWith(".md")).filter((name) => name !== "kanban.md").map((name) => join18(this.corpusDir, name)).sort();
80713
+ return entries.filter((name) => name.endsWith(".md")).filter((name) => name !== "kanban.md").map((name) => join19(this.corpusDir, name)).sort();
80682
80714
  }
80683
80715
  async migrateFile(filePath, dryRun, gitDates = new Map) {
80684
80716
  const flags = [];
@@ -86418,8 +86450,13 @@ function schedulerJobTimeoutEnvName(name) {
86418
86450
  const snake = name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([^A-Za-z0-9]+)/g, "_").toUpperCase().replace(/^_+|_+$/g, "");
86419
86451
  return `SPUR_SCHEDULER_TIMEOUT_${snake}_MS`;
86420
86452
  }
86421
- function resolveSchedulerJobTimeoutMs(name, env, fallbackMs) {
86422
- return normalizeLegacyTimeoutMs(env[schedulerJobTimeoutEnvName(name)], fallbackMs);
86453
+ function resolveSchedulerJobTimeoutMs(name, env, fallbackMs, declaredMs) {
86454
+ const override = env[schedulerJobTimeoutEnvName(name)];
86455
+ if (override !== undefined)
86456
+ return normalizeLegacyTimeoutMs(override, fallbackMs);
86457
+ if (declaredMs !== undefined)
86458
+ return declaredMs;
86459
+ return fallbackMs;
86423
86460
  }
86424
86461
  function outputTail(text4) {
86425
86462
  const trimmed = text4.trim();
@@ -86475,6 +86512,10 @@ async function handleSchedulerCustomJob(deps, job) {
86475
86512
  throw new Error(describeBoundedFailure({ subject: `scheduler job "${payload.name}"`, outcome, outputTail: detail }));
86476
86513
  }
86477
86514
  if (result.exitCode === null) {
86515
+ if (deps.isShuttingDown?.() === true) {
86516
+ deps.onShutdownAbandon?.(payload.name);
86517
+ return;
86518
+ }
86478
86519
  const signalDetail = result.signal === undefined ? "" : ` (${result.signal})`;
86479
86520
  throw new Error(`scheduler job "${payload.name}" terminated before a normal exit${signalDetail} ` + `after ${Math.round(outcome.elapsedMs)}ms${detail && `: ${detail}`}`);
86480
86521
  }
@@ -86897,8 +86938,8 @@ import {
86897
86938
  unlinkSync as unlinkSync2,
86898
86939
  writeFileSync as writeFileSync5
86899
86940
  } from "fs";
86900
- import { homedir as homedir6 } from "os";
86901
- import { dirname as dirname14, isAbsolute as isAbsolute3, join as join19, resolve as resolve6, sep } from "path";
86941
+ import { homedir as homedir7 } from "os";
86942
+ import { dirname as dirname14, isAbsolute as isAbsolute3, join as join20, resolve as resolve6, sep } from "path";
86902
86943
  function parseImporterVersion(version3) {
86903
86944
  if (version3 === undefined) {
86904
86945
  return null;
@@ -86953,7 +86994,7 @@ async function buildRefreshCoverage(db2, selector, fanOut) {
86953
86994
  async function runSessionAugmentedRoots(source, home, cwd, dao2) {
86954
86995
  const roots = getSourceDefinition(source).defaultRoots.map((root) => resolve6(home, root));
86955
86996
  const runRoots = [];
86956
- const runRoot = join19(cwd, ".spur", "run");
86997
+ const runRoot = join20(cwd, ".spur", "run");
86957
86998
  if (!existsSync6(runRoot))
86958
86999
  return { roots, runRoots };
86959
87000
  const sourcesByRun = new Map;
@@ -86963,7 +87004,7 @@ async function runSessionAugmentedRoots(source, home, cwd, dao2) {
86963
87004
  sourcesByRun.set(row.run_id, sources);
86964
87005
  }
86965
87006
  for (const runId of readdirSync6(runRoot)) {
86966
- const sessionsRoot = join19(runRoot, runId, "agent-sessions");
87007
+ const sessionsRoot = join20(runRoot, runId, "agent-sessions");
86967
87008
  if (!existsSync6(sessionsRoot))
86968
87009
  continue;
86969
87010
  const mappedSources = sourcesByRun.get(runId);
@@ -86972,7 +87013,7 @@ async function runSessionAugmentedRoots(source, home, cwd, dao2) {
86972
87013
  const soleMappedSource = mappedSources?.size === 1 && mappedSources.has(source);
86973
87014
  if (!directMatch && !soleMappedSource)
86974
87015
  continue;
86975
- const root = realpathSync4(join19(sessionsRoot, agent));
87016
+ const root = realpathSync4(join20(sessionsRoot, agent));
86976
87017
  roots.push(root);
86977
87018
  runRoots.push({ runId, root });
86978
87019
  }
@@ -86993,7 +87034,7 @@ class HistoryService {
86993
87034
  const startedAt = new Date().toISOString();
86994
87035
  const db2 = await this.ctx.getDb();
86995
87036
  const dao2 = new RunSessionDao(db2);
86996
- const discovery = parsedSource !== "opencode" && opts.file === undefined && opts.root === undefined ? await runSessionAugmentedRoots(parsedSource, this.ctx.historyHome ?? homedir6(), this.ctx.cwd ?? process.cwd(), dao2) : undefined;
87037
+ const discovery = parsedSource !== "opencode" && opts.file === undefined && opts.root === undefined ? await runSessionAugmentedRoots(parsedSource, this.ctx.historyHome ?? homedir7(), this.ctx.cwd ?? process.cwd(), dao2) : undefined;
86997
87038
  const result = parsedSource === "opencode" && opts.file === undefined && opts.root === undefined ? await runOpenCodeImport({
86998
87039
  db: db2,
86999
87040
  sourceDatabase: this.ctx.openCodeSourceDatabase,
@@ -87060,7 +87101,7 @@ class HistoryService {
87060
87101
  }
87061
87102
  async maintain(opts = {}) {
87062
87103
  const db2 = await this.ctx.getDb();
87063
- const dbPath = this.ctx.cwd ? join19(this.ctx.cwd, ".spur", "spur.db") : undefined;
87104
+ const dbPath = this.ctx.cwd ? join20(this.ctx.cwd, ".spur", "spur.db") : undefined;
87064
87105
  return maintainDatabase(db2, {
87065
87106
  ...opts,
87066
87107
  ...dbPath && opts.dbPath === undefined ? { dbPath } : {}
@@ -87568,9 +87609,9 @@ function resolveArtifactPaths(artifact, opts) {
87568
87609
  return { artifactPath: out, sidecarPath: `${out.replace(/\.json$/, "")}.errors.jsonl` };
87569
87610
  }
87570
87611
  const base2 = resolve6(opts.cwd, ".spur", "reports", "history");
87571
- const dateDir = join19(base2, artifact.generatedAt.slice(0, 10));
87612
+ const dateDir = join20(base2, artifact.generatedAt.slice(0, 10));
87572
87613
  const name = `analyze-${selectorDigest(artifact.selector)}`;
87573
- return { artifactPath: join19(dateDir, `${name}.json`), sidecarPath: join19(dateDir, `${name}.errors.jsonl`) };
87614
+ return { artifactPath: join20(dateDir, `${name}.json`), sidecarPath: join20(dateDir, `${name}.errors.jsonl`) };
87574
87615
  }
87575
87616
  function boundCoverage(coverage) {
87576
87617
  const overflow = [];
@@ -87593,7 +87634,7 @@ function boundCoverage(coverage) {
87593
87634
  }
87594
87635
  function updateLatestPointer(opts, artifactPath) {
87595
87636
  const base2 = resolve6(opts.cwd, ".spur", "reports", "history");
87596
- const latest = join19(base2, "latest.json");
87637
+ const latest = join20(base2, "latest.json");
87597
87638
  mkdirSync7(base2, { recursive: true });
87598
87639
  if (existsSync6(latest))
87599
87640
  unlinkSync2(latest);
@@ -87659,7 +87700,7 @@ function formatIssue(issue2) {
87659
87700
  return `${issue2.sourceFile}:${issue2.sourceLine}: ${issue2.reason}`;
87660
87701
  }
87661
87702
  function pruneReports(cwd, retentionDays = REPORT_RETENTION_DAYS, now2 = new Date) {
87662
- const dir = join19(cwd, ".spur", "reports", "history");
87703
+ const dir = join20(cwd, ".spur", "reports", "history");
87663
87704
  let entries;
87664
87705
  try {
87665
87706
  entries = readdirSync6(dir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
@@ -87677,7 +87718,7 @@ function pruneReports(cwd, retentionDays = REPORT_RETENTION_DAYS, now2 = new Dat
87677
87718
  if (entryDate >= cutoff)
87678
87719
  continue;
87679
87720
  try {
87680
- rmSync5(join19(dir, name), { recursive: true, force: true });
87721
+ rmSync5(join20(dir, name), { recursive: true, force: true });
87681
87722
  pruned.push(name);
87682
87723
  } catch {}
87683
87724
  }
@@ -87787,7 +87828,7 @@ function extractResolvedWorkflowFacts(workflow) {
87787
87828
  var init_composition_baseline = () => {};
87788
87829
 
87789
87830
  // ../../packages/app/src/workflow/workflow-resolver.ts
87790
- import { basename as basename8, isAbsolute as isAbsolute4, join as join20, resolve as resolve7 } from "path";
87831
+ import { basename as basename8, isAbsolute as isAbsolute4, join as join21, resolve as resolve7 } from "path";
87791
87832
  function registeredWorkflowPaths(config4) {
87792
87833
  const paths = config4?.workflows?.paths ?? [".spur/workflows/"];
87793
87834
  const bundledRoot = bundledConfigRoot();
@@ -87795,7 +87836,7 @@ function registeredWorkflowPaths(config4) {
87795
87836
  for (const path9 of paths) {
87796
87837
  if (path9.startsWith(BUNDLED_PATH_PREFIX)) {
87797
87838
  if (bundledRoot !== null)
87798
- expanded.push(join20(bundledRoot, path9.slice(BUNDLED_PATH_PREFIX.length)));
87839
+ expanded.push(join21(bundledRoot, path9.slice(BUNDLED_PATH_PREFIX.length)));
87799
87840
  } else {
87800
87841
  expanded.push(path9);
87801
87842
  }
@@ -87807,7 +87848,7 @@ function normalizeLayerPath(cwd, path9) {
87807
87848
  }
87808
87849
  function workflowLayers(opts) {
87809
87850
  const sharedRoot = opts.sharedRoot !== undefined ? opts.sharedRoot : bundledConfigRoot();
87810
- const sharedPath = sharedRoot !== null ? normalizeLayerPath(opts.cwd, join20(sharedRoot, "workflows")) : null;
87851
+ const sharedPath = sharedRoot !== null ? normalizeLayerPath(opts.cwd, join21(sharedRoot, "workflows")) : null;
87811
87852
  const layers = [];
87812
87853
  const seen = new Set;
87813
87854
  const add = (id, path9) => {
@@ -87816,7 +87857,7 @@ function workflowLayers(opts) {
87816
87857
  seen.add(path9);
87817
87858
  layers.push({ id, path: path9 });
87818
87859
  };
87819
- add("project", normalizeLayerPath(opts.cwd, join20(opts.cwd, ".spur", "workflows")));
87860
+ add("project", normalizeLayerPath(opts.cwd, join21(opts.cwd, ".spur", "workflows")));
87820
87861
  for (const entry of opts.registered) {
87821
87862
  if (sharedPath !== null && normalizeLayerPath(opts.cwd, entry) === sharedPath)
87822
87863
  continue;
@@ -87860,7 +87901,7 @@ async function scanWorkflowByName(cwd, name, registered) {
87860
87901
  try {
87861
87902
  const files = (await fs3.readDir(layer.path)).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
87862
87903
  for (const f of files) {
87863
- const abs = join20(layer.path, f);
87904
+ const abs = join21(layer.path, f);
87864
87905
  const wfName = await readWorkflowNameFast(fs3, abs);
87865
87906
  if (wfName === name) {
87866
87907
  return { path: abs, source: layer.id };
@@ -87902,16 +87943,16 @@ function resolveWorkflowFile(cwd, file2) {
87902
87943
  const bundledRoot = bundledConfigRoot();
87903
87944
  if (bundledRoot !== null) {
87904
87945
  const sharedName = isYaml ? base2 : `${base2}.yaml`;
87905
- const sharedPath = join20(bundledRoot, "workflows", sharedName);
87946
+ const sharedPath = join21(bundledRoot, "workflows", sharedName);
87906
87947
  if (fs3.exists(sharedPath)) {
87907
87948
  return { path: sharedPath, source: "shared" };
87908
87949
  }
87909
87950
  if (!isYaml) {
87910
- const sharedPipeline = join20(bundledRoot, "workflows", `${base2}-pipeline.yaml`);
87951
+ const sharedPipeline = join21(bundledRoot, "workflows", `${base2}-pipeline.yaml`);
87911
87952
  if (fs3.exists(sharedPipeline)) {
87912
87953
  return { path: sharedPipeline, source: "shared" };
87913
87954
  }
87914
- const sharedLiteral = join20(bundledRoot, "workflows", base2);
87955
+ const sharedLiteral = join21(bundledRoot, "workflows", base2);
87915
87956
  if (fs3.exists(sharedLiteral)) {
87916
87957
  return { path: sharedLiteral, source: "shared" };
87917
87958
  }
@@ -88315,7 +88356,7 @@ var init_tripwire = __esm(() => {
88315
88356
 
88316
88357
  // ../../packages/app/src/workflow/actions/agent-run.ts
88317
88358
  import { tmpdir } from "os";
88318
- import { dirname as dirname15, isAbsolute as isAbsolute5, join as join21 } from "path";
88359
+ import { dirname as dirname15, isAbsolute as isAbsolute5, join as join22 } from "path";
88319
88360
 
88320
88361
  class AgentRunActionRunner {
88321
88362
  observabilityBus;
@@ -88344,20 +88385,20 @@ class AgentRunActionRunner {
88344
88385
  const priority = asOptionalString(options.priority);
88345
88386
  const compareExecutorWith = asOptionalString(options.compareExecutorWith) ?? "implement";
88346
88387
  const agentLabel = dispatchAgent ?? "<default>";
88347
- const targetAgentDir = dispatchAgent ?? this.agentConfig.default ?? "omp";
88388
+ const targetAgentDir = dispatchAgent ?? this.agentConfig.default ?? "default";
88348
88389
  const prevAgent = freshSession ? undefined : asOptionalString(context4.vars.__agentSessionAgent);
88349
88390
  let sessionDir = freshSession ? undefined : asOptionalString(context4.vars.__agentSessionDir);
88350
88391
  if (affinityOn) {
88351
88392
  if (!sessionDir || prevAgent && prevAgent !== targetAgentDir) {
88352
- sessionDir = join21(cwd, ".spur", "run", context4.runId, "agent-sessions", targetAgentDir);
88393
+ sessionDir = join22(cwd, ".spur", "run", context4.runId, "agent-sessions", targetAgentDir);
88353
88394
  }
88354
88395
  } else {
88355
88396
  if (!sessionDir) {
88356
- sessionDir = join21(cwd, ".spur", "run", context4.runId, "agent-sessions", targetAgentDir);
88397
+ sessionDir = join22(cwd, ".spur", "run", context4.runId, "agent-sessions", targetAgentDir);
88357
88398
  }
88358
88399
  }
88359
88400
  if (freshSession) {
88360
- sessionDir = join21(cwd, ".spur", "run", context4.runId, "agent-sessions", targetAgentDir, `fresh-${context4.stateOrNodeId}`);
88401
+ sessionDir = join22(cwd, ".spur", "run", context4.runId, "agent-sessions", targetAgentDir, `fresh-${context4.stateOrNodeId}`);
88361
88402
  }
88362
88403
  const storedSessionId = freshSession ? undefined : asOptionalString(context4.vars.__agentSessionId);
88363
88404
  let continueFlag = asOptionalBoolean(options.continue);
@@ -88449,7 +88490,7 @@ class AgentRunActionRunner {
88449
88490
  flags.requiresCapabilities = JSON.stringify(requiresCapabilities.requires);
88450
88491
  }
88451
88492
  if (expectFile !== undefined) {
88452
- const target = isAbsolute5(expectFile) ? expectFile : join21(cwd, expectFile);
88493
+ const target = isAbsolute5(expectFile) ? expectFile : join22(cwd, expectFile);
88453
88494
  const fs3 = createNodeFileSystem3(cwd);
88454
88495
  if (await fs3.exists(target)) {
88455
88496
  try {
@@ -88468,7 +88509,7 @@ class AgentRunActionRunner {
88468
88509
  return;
88469
88510
  return `
88470
88511
 
88471
- Write the required artifact to this absolute path: ${join21(cwd, rel)}`;
88512
+ Write the required artifact to this absolute path: ${join22(cwd, rel)}`;
88472
88513
  })();
88473
88514
  const dispatchInput = input === undefined || absoluteArtifactHint === undefined ? input : `${input}${absoluteArtifactHint}`;
88474
88515
  const observer = this.observabilityBus === undefined ? undefined : (event) => {
@@ -88622,13 +88663,13 @@ Write the required artifact to this absolute path: ${join21(cwd, rel)}`;
88622
88663
  }
88623
88664
  }
88624
88665
  if (capture && answerFile !== undefined) {
88625
- const target = isAbsolute5(answerFile) ? answerFile : join21(cwd, answerFile);
88666
+ const target = isAbsolute5(answerFile) ? answerFile : join22(cwd, answerFile);
88626
88667
  const fs3 = createNodeFileSystem3(cwd);
88627
88668
  await fs3.ensureDir(dirname15(target));
88628
88669
  await fs3.writeFile(target, answer);
88629
88670
  }
88630
88671
  if (ok && expectFile !== undefined) {
88631
- const target = isAbsolute5(expectFile) ? expectFile : join21(cwd, expectFile);
88672
+ const target = isAbsolute5(expectFile) ? expectFile : join22(cwd, expectFile);
88632
88673
  const fs3 = createNodeFileSystem3(cwd);
88633
88674
  if (!await fs3.exists(target)) {
88634
88675
  await writePartialWorkArtifact(context4, agentLabel, model, traced, cwd, sessionDir, this.agentConfig.secretValues);
@@ -88673,7 +88714,7 @@ ${traced.stdout ?? ""}`);
88673
88714
  const resolvedAgent = invocation?.agent ?? targetAgentDir;
88674
88715
  let resolvedSessionDir = sessionDir;
88675
88716
  if (ok && affinityOn && resolvedAgent !== targetAgentDir && !context4.vars.__agentSessionDir) {
88676
- resolvedSessionDir = join21(cwd, ".spur", "run", context4.runId, "agent-sessions", resolvedAgent);
88717
+ resolvedSessionDir = join22(cwd, ".spur", "run", context4.runId, "agent-sessions", resolvedAgent);
88677
88718
  }
88678
88719
  let discoveredSessionId = storedSessionId;
88679
88720
  if (ok && affinityOn && resolvedSessionDir && !discoveredSessionId) {
@@ -88681,7 +88722,7 @@ ${traced.stdout ?? ""}`);
88681
88722
  }
88682
88723
  if (ok && affinityOn && resolvedSessionDir) {
88683
88724
  try {
88684
- const sidecarPath = join21(cwd, ".spur", "run", `${context4.runId}-agent-session.json`);
88725
+ const sidecarPath = join22(cwd, ".spur", "run", `${context4.runId}-agent-session.json`);
88685
88726
  const fs3 = createNodeFileSystem3(cwd);
88686
88727
  await fs3.ensureDir(dirname15(sidecarPath));
88687
88728
  await fs3.writeFile(sidecarPath, JSON.stringify({
@@ -88730,7 +88771,7 @@ async function discoverSessionId(sessionDir) {
88730
88771
  let newestFile;
88731
88772
  let newestMtime = 0;
88732
88773
  for (const entry of jsonEntries) {
88733
- const fullPath = join21(sessionDir, entry);
88774
+ const fullPath = join22(sessionDir, entry);
88734
88775
  const st = await fs3.stat(fullPath);
88735
88776
  if (st?.isFile() && st.mtimeMs > newestMtime) {
88736
88777
  newestMtime = st.mtimeMs;
@@ -88826,7 +88867,7 @@ async function writePartialWorkArtifact(context4, agentLabel, model, traced, cwd
88826
88867
  const headerLine = model !== undefined ? `${agentLabel} (model: ${model})` : agentLabel;
88827
88868
  const inv = traced.invocation;
88828
88869
  const argvLine = redactAndBound(inv ? `${inv.command} ${inv.argv.join(" ")}` : "(invocation not captured)", secretValues, PARTIAL_ARTIFACT_TAIL_CHARS);
88829
- const latchedSessionPath = join21(cwd, ".spur", "run", `${context4.runId}-agent-session.json`);
88870
+ const latchedSessionPath = join22(cwd, ".spur", "run", `${context4.runId}-agent-session.json`);
88830
88871
  const resumeContext = [
88831
88872
  "",
88832
88873
  "## resume context",
@@ -88842,9 +88883,9 @@ async function writePartialWorkArtifact(context4, agentLabel, model, traced, cwd
88842
88883
  try {
88843
88884
  const fs4 = createNodeFileSystem3(cwd);
88844
88885
  const locator = TaskLocator.forDirs(fs4, [
88845
- join21(cwd, "docs", "tasks3"),
88846
- join21(cwd, "docs", "tasks2"),
88847
- join21(cwd, "docs", "tasks")
88886
+ join22(cwd, "docs", "tasks3"),
88887
+ join22(cwd, "docs", "tasks2"),
88888
+ join22(cwd, "docs", "tasks")
88848
88889
  ]);
88849
88890
  const hit = await locator.findByWbs(wbs);
88850
88891
  if (hit) {
@@ -88900,7 +88941,7 @@ async function writePartialWorkArtifact(context4, agentLabel, model, traced, cwd
88900
88941
  completedSection
88901
88942
  ].join(`
88902
88943
  `);
88903
- const target = join21(cwd, ".spur", "run", `${context4.runId}-${context4.stateOrNodeId}-partial.md`);
88944
+ const target = join22(cwd, ".spur", "run", `${context4.runId}-${context4.stateOrNodeId}-partial.md`);
88904
88945
  const fs3 = createNodeFileSystem3(cwd);
88905
88946
  await fs3.ensureDir(dirname15(target));
88906
88947
  await fs3.writeFile(target, body);
@@ -88922,7 +88963,7 @@ async function gitDiffStat(cwd) {
88922
88963
  }
88923
88964
  }
88924
88965
  async function createGitWorkingTreeSnapshot(cwd, excludeGlobs = ["docs/tasks3/*", "docs/features/*"]) {
88925
- const indexFile = join21(tmpdir(), `spur-implement-scope-${crypto.randomUUID()}.index`);
88966
+ const indexFile = join22(tmpdir(), `spur-implement-scope-${crypto.randomUUID()}.index`);
88926
88967
  const fs3 = createNodeFileSystem3(cwd);
88927
88968
  let keepIndex = false;
88928
88969
  try {
@@ -89053,9 +89094,9 @@ async function findOutOfScopeChanges(cwd, wbs, changed) {
89053
89094
  try {
89054
89095
  const fs3 = createNodeFileSystem3(cwd);
89055
89096
  const locator = TaskLocator.forDirs(fs3, [
89056
- join21(cwd, "docs", "tasks3"),
89057
- join21(cwd, "docs", "tasks2"),
89058
- join21(cwd, "docs", "tasks")
89097
+ join22(cwd, "docs", "tasks3"),
89098
+ join22(cwd, "docs", "tasks2"),
89099
+ join22(cwd, "docs", "tasks")
89059
89100
  ]);
89060
89101
  const hit = await locator.findByWbs(wbs);
89061
89102
  if (!hit)
@@ -89105,7 +89146,7 @@ var init_child_env = __esm(() => {
89105
89146
  });
89106
89147
 
89107
89148
  // ../../packages/app/src/workflow/actions/run-path.ts
89108
- import { dirname as dirname16, join as join22, normalize, relative as relative3, resolve as resolve8, sep as sep2 } from "path";
89149
+ import { dirname as dirname16, join as join23, normalize, relative as relative3, resolve as resolve8, sep as sep2 } from "path";
89109
89150
  function within(child, ancestor) {
89110
89151
  return child === ancestor || child.startsWith(`${ancestor}${sep2}`);
89111
89152
  }
@@ -89114,7 +89155,7 @@ async function resolveRunArtifactPath(fileSystem, workdir, pathRaw) {
89114
89155
  throw new RunArtifactPathError("physical path confinement requires a FileSystem with realPath support \u2014 refusing to skip confinement (0785 R2)");
89115
89156
  }
89116
89157
  const lexicalTarget = normalize(resolve8(workdir, pathRaw));
89117
- const lexicalRunRoot = normalize(join22(resolve8(workdir), ".spur", "run"));
89158
+ const lexicalRunRoot = normalize(join23(resolve8(workdir), ".spur", "run"));
89118
89159
  if (!lexicalTarget.startsWith(`${lexicalRunRoot}${sep2}`)) {
89119
89160
  throw new RunArtifactPathError(`must resolve beneath .spur/run/ (got ${pathRaw})`);
89120
89161
  }
@@ -89125,7 +89166,7 @@ async function resolveRunArtifactPath(fileSystem, workdir, pathRaw) {
89125
89166
  } catch (error51) {
89126
89167
  throw new RunArtifactPathError(`project workdir could not be canonicalized: ${error51.message}`);
89127
89168
  }
89128
- const canonicalRunRootCandidate = join22(canonicalWorkdir, ".spur", "run");
89169
+ const canonicalRunRootCandidate = join23(canonicalWorkdir, ".spur", "run");
89129
89170
  let canonicalRunRoot = canonicalRunRootCandidate;
89130
89171
  const rootStat = await fileSystem.stat(canonicalRunRootCandidate);
89131
89172
  if (rootStat !== null) {
@@ -89173,7 +89214,7 @@ async function resolveRunArtifactPath(fileSystem, workdir, pathRaw) {
89173
89214
  if (!within(anchor, canonicalWorkdir)) {
89174
89215
  throw new RunArtifactPathError(`ancestor ${probe} escapes the project workdir through a symlink`);
89175
89216
  }
89176
- const reconstructed = normalize(join22(anchor, relative3(probe, lexicalTarget)));
89217
+ const reconstructed = normalize(join23(anchor, relative3(probe, lexicalTarget)));
89177
89218
  if (!reconstructed.startsWith(`${canonicalRunRoot}${sep2}`)) {
89178
89219
  throw new RunArtifactPathError(`path escapes .spur/run/ through a symlink (got ${pathRaw})`);
89179
89220
  }
@@ -89190,7 +89231,7 @@ var init_run_path = __esm(() => {
89190
89231
  });
89191
89232
 
89192
89233
  // ../../packages/app/src/workflow/actions/command-gate.ts
89193
- import { join as join23, normalize as normalize2 } from "path";
89234
+ import { join as join24, normalize as normalize2 } from "path";
89194
89235
  function sleep2(ms) {
89195
89236
  return new Promise((resolve9) => setTimeout(resolve9, ms));
89196
89237
  }
@@ -89262,7 +89303,7 @@ class CommandGateActionRunner {
89262
89303
  const retryOn = Array.isArray(retry.on) ? retry.on : [];
89263
89304
  const timeoutMs = typeof options.timeoutMs === "number" ? options.timeoutMs : undefined;
89264
89305
  const env = childProcessEnv(context4.vars);
89265
- await this.fileSystem.ensureDir(normalize2(join23(normalized, "..")));
89306
+ await this.fileSystem.ensureDir(normalize2(join24(normalized, "..")));
89266
89307
  let lastStdout = "";
89267
89308
  let lastStderr = "";
89268
89309
  let lastExitCode = -1;
@@ -89919,7 +89960,7 @@ var init_http_request = __esm(() => {
89919
89960
  // ../../packages/app/src/workflow/proof-input-fingerprint.ts
89920
89961
  import crypto4 from "crypto";
89921
89962
  import { tmpdir as tmpdir2 } from "os";
89922
- import { dirname as dirname17, join as join24, resolve as resolve10, sep as sep4 } from "path";
89963
+ import { dirname as dirname17, join as join25, resolve as resolve10, sep as sep4 } from "path";
89923
89964
  function taskDocumentShapeError(content) {
89924
89965
  const doc2 = MarkdownDocument.parse(content, "task");
89925
89966
  const heading = CANONICAL_TASK_HEADING_RE.exec(doc2.bodyWithoutFrontmatter);
@@ -89994,7 +90035,7 @@ async function readProofInputContents(fileSystem = createNodeFileSystem3(), work
89994
90035
  };
89995
90036
  }
89996
90037
  async function createGitAlternateTree(cwd, excludeGlobs = DEFAULT_EXCLUDE_GLOBS, executor = new NodeProcessExecutor3, fs3 = createNodeFileSystem3()) {
89997
- const indexFile = join24(tmpdir2(), `spur-proof-fingerprint-${crypto4.randomUUID()}.index`);
90038
+ const indexFile = join25(tmpdir2(), `spur-proof-fingerprint-${crypto4.randomUUID()}.index`);
89998
90039
  try {
89999
90040
  await fs3.ensureDir(dirname17(indexFile));
90000
90041
  const env = Object.fromEntries(Object.entries(process.env).filter((entry) => entry[1] !== undefined));
@@ -90289,7 +90330,7 @@ var init_rule_check = __esm(() => {
90289
90330
  });
90290
90331
 
90291
90332
  // ../../packages/app/src/workflow/actions/run-artifact.ts
90292
- import { join as join25, resolve as resolve11 } from "path";
90333
+ import { join as join26, resolve as resolve11 } from "path";
90293
90334
 
90294
90335
  class RunArtifactActionRunner {
90295
90336
  getDb;
@@ -90567,7 +90608,7 @@ class RunArtifactActionRunner {
90567
90608
  error: `${KIND14}: proof.stages.review.status is "${String(review.status)}" \u2014 a skipped or stale review is not completed evidence (0785 R4)`
90568
90609
  };
90569
90610
  }
90570
- const markerPath = join25(resolve11(workdir), ".spur", "run", `${context4.runId}-review-proof.digest`);
90611
+ const markerPath = join26(resolve11(workdir), ".spur", "run", `${context4.runId}-review-proof.digest`);
90571
90612
  let marker = null;
90572
90613
  try {
90573
90614
  marker = await this.fileSystem.readFile(markerPath);
@@ -90911,7 +90952,7 @@ var init_checkpoint_contract = __esm(() => {
90911
90952
 
90912
90953
  // ../../packages/app/src/services/workflow-service.ts
90913
90954
  import { realpathSync as realpathSync5 } from "fs";
90914
- import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute6, join as join26, resolve as resolve12, sep as sep5 } from "path";
90955
+ import { basename as basename9, dirname as dirname18, isAbsolute as isAbsolute6, join as join27, resolve as resolve12, sep as sep5 } from "path";
90915
90956
  function signalSubprocess(pid) {
90916
90957
  if (!Number.isInteger(pid) || pid <= 1)
90917
90958
  return false;
@@ -91176,7 +91217,7 @@ class WorkflowAppService {
91176
91217
  };
91177
91218
  }
91178
91219
  async cleanRunLogs(retentionDays = 30, dryRun = false) {
91179
- const runDir = join26(this.ctx.cwd, ".spur", "run");
91220
+ const runDir = join27(this.ctx.cwd, ".spur", "run");
91180
91221
  const fs3 = createNodeFileSystem3();
91181
91222
  const cutoffMs = Date.now() - retentionDays * 24 * 60 * 60 * 1000;
91182
91223
  const reclaimed = [];
@@ -91188,7 +91229,7 @@ class WorkflowAppService {
91188
91229
  return { retentionDays, dryRun, reclaimed, failures };
91189
91230
  }
91190
91231
  for (const name of entries) {
91191
- const path9 = join26(runDir, name);
91232
+ const path9 = join27(runDir, name);
91192
91233
  const stat2 = await fs3.stat(path9);
91193
91234
  if (stat2 === null || !stat2.isFile() || stat2.mtimeMs >= cutoffMs)
91194
91235
  continue;
@@ -91207,7 +91248,7 @@ class WorkflowAppService {
91207
91248
  return { retentionDays, dryRun, reclaimed, failures };
91208
91249
  }
91209
91250
  async cleanCheckpoints(retentionDays = 30, dryRun = false) {
91210
- const sessionsDir = join26(this.ctx.cwd, ".spur", "memory", "sessions");
91251
+ const sessionsDir = join27(this.ctx.cwd, ".spur", "memory", "sessions");
91211
91252
  const fs3 = createNodeFileSystem3();
91212
91253
  let sessionsReal;
91213
91254
  try {
@@ -91227,7 +91268,7 @@ class WorkflowAppService {
91227
91268
  }
91228
91269
  const activeRunIds = new Set((await new RunDao(await this.ctx.getDb()).listActiveRuns()).map((r) => r.id));
91229
91270
  for (const name of entries) {
91230
- const path9 = join26(sessionsDir, name);
91271
+ const path9 = join27(sessionsDir, name);
91231
91272
  let resolved;
91232
91273
  try {
91233
91274
  resolved = realpathSync5(path9);
@@ -91427,7 +91468,7 @@ class WorkflowAppService {
91427
91468
  await new RunDao(db2).stampFailureReason(runId, result.reason);
91428
91469
  }
91429
91470
  async validateResumeCheckpointFreshness(runId, row, workdir) {
91430
- const sessionsDir = join26(workdir, ".spur", "memory", "sessions");
91471
+ const sessionsDir = join27(workdir, ".spur", "memory", "sessions");
91431
91472
  const fs3 = createNodeFileSystem3();
91432
91473
  let entries;
91433
91474
  try {
@@ -91461,7 +91502,7 @@ class WorkflowAppService {
91461
91502
  return head;
91462
91503
  };
91463
91504
  for (const name of entries) {
91464
- const filePath = join26(sessionsDir, name);
91505
+ const filePath = join27(sessionsDir, name);
91465
91506
  let raw;
91466
91507
  try {
91467
91508
  raw = await fs3.readFile(filePath);
@@ -91492,7 +91533,7 @@ class WorkflowAppService {
91492
91533
  const staleness = checkpointStaleness(meta3, {
91493
91534
  ...wbs !== undefined ? { taskWbs: wbs } : {},
91494
91535
  ...meta3.sourceCommit === "" ? {} : { sourceCommit: await resolveHead() },
91495
- artifactExists: (p) => fs3.exists(isAbsolute6(p) ? p : join26(workdir, p))
91536
+ artifactExists: (p) => fs3.exists(isAbsolute6(p) ? p : join27(workdir, p))
91496
91537
  });
91497
91538
  if (staleness.stale) {
91498
91539
  throw new Error(`Cannot resume run "${runId}": checkpoint is stale (${staleness.reason}).`);
@@ -92037,8 +92078,8 @@ function resolveOutputLogConfig(config4) {
92037
92078
  };
92038
92079
  }
92039
92080
  async function outputArtifactForRun(cwd, runId) {
92040
- const relative4 = join26(".spur", "run", `${runId}.log`);
92041
- return await fileExists(join26(cwd, relative4)) ? relative4 : undefined;
92081
+ const relative4 = join27(".spur", "run", `${runId}.log`);
92082
+ return await fileExists(join27(cwd, relative4)) ? relative4 : undefined;
92042
92083
  }
92043
92084
  async function scanWorkflowFiles(rootPath, source, loadEntry) {
92044
92085
  const entries = [];
@@ -92236,7 +92277,7 @@ function projectActionTraceResult(resultJson, secretValues = []) {
92236
92277
  async function partialArtifactForAction(cwd, runId, node, ok) {
92237
92278
  if (ok !== false || !TRACE_IDENTIFIER.test(runId) || !TRACE_IDENTIFIER.test(node))
92238
92279
  return;
92239
- const relativePath2 = join26(".spur", "run", `${runId}-${node}-partial.md`);
92280
+ const relativePath2 = join27(".spur", "run", `${runId}-${node}-partial.md`);
92240
92281
  return await fileExists(resolve12(cwd, relativePath2)) ? relativePath2 : undefined;
92241
92282
  }
92242
92283
  var TASK_PIPELINE_WORKFLOW = "task-pipeline", PIPELINE_LINK_KIND = "pipeline", COMPOSITION_CAPS, STRUCTURE_TOKENS, TRACE_IDENTIFIER, TRACE_RESULT_FIELDS, TRACE_INVOCATION_FIELDS;
@@ -92281,7 +92322,7 @@ var init_workflow_service = __esm(() => {
92281
92322
  });
92282
92323
 
92283
92324
  // ../../packages/app/src/services/inline-run-setup.ts
92284
- import { join as join27, resolve as resolve13 } from "path";
92325
+ import { join as join28, resolve as resolve13 } from "path";
92285
92326
  function parseIdentityMetadata(raw) {
92286
92327
  if (raw.trim() === "")
92287
92328
  return {};
@@ -92295,8 +92336,8 @@ function parseIdentityMetadata(raw) {
92295
92336
  }
92296
92337
  }
92297
92338
  async function openInlineRunProjectDb(workdir) {
92298
- const url2 = join27(resolve13(workdir), ".spur", "spur.db");
92299
- mkdirSync8(join27(url2, ".."), { recursive: true });
92339
+ const url2 = join28(resolve13(workdir), ".spur", "spur.db");
92340
+ mkdirSync8(join28(url2, ".."), { recursive: true });
92300
92341
  const adapter = await createMigratedDb({ url: url2 });
92301
92342
  return { adapter, close: () => adapter.close() };
92302
92343
  }
@@ -93248,8 +93289,8 @@ var init_project_start = __esm(() => {
93248
93289
  });
93249
93290
 
93250
93291
  // ../../packages/app/src/services/rule-service.ts
93251
- import { homedir as homedir7 } from "os";
93252
- import { delimiter, join as join28, relative as relative4, resolve as resolve15 } from "path";
93292
+ import { homedir as homedir8 } from "os";
93293
+ import { delimiter, join as join29, relative as relative4, resolve as resolve15 } from "path";
93253
93294
 
93254
93295
  class RuleService {
93255
93296
  context;
@@ -93444,13 +93485,13 @@ ${ruleIds.join(`
93444
93485
  const hasGlobalOverride = globalOverride !== undefined && globalOverride.length > 0;
93445
93486
  layers.push({
93446
93487
  id: "global",
93447
- path: hasGlobalOverride ? resolve15(cwd, globalOverride) : join28(homedir7(), GLOBAL_RULES_DIR),
93488
+ path: hasGlobalOverride ? resolve15(cwd, globalOverride) : join29(homedir8(), GLOBAL_RULES_DIR),
93448
93489
  priority: 10
93449
93490
  });
93450
93491
  if (opts.includeBundled && !hasGlobalOverride) {
93451
93492
  const bundledConfig = bundledConfigRoot();
93452
93493
  if (bundledConfig !== null) {
93453
- layers.push({ id: "bundled-config", path: join28(bundledConfig, "rules"), priority: 15 });
93494
+ layers.push({ id: "bundled-config", path: join29(bundledConfig, "rules"), priority: 15 });
93454
93495
  }
93455
93496
  const bundled = await bundledRulesRoot();
93456
93497
  if (bundled !== null)
@@ -93663,7 +93704,7 @@ ${ruleIds.join(`
93663
93704
  const { fs: fs3 } = this.context;
93664
93705
  for (const root of await this.ruleRoots()) {
93665
93706
  for (const ext of ["yaml", "yml", "json"]) {
93666
- if (await fs3.exists(join28(root, `${name}.${ext}`)))
93707
+ if (await fs3.exists(join29(root, `${name}.${ext}`)))
93667
93708
  return true;
93668
93709
  }
93669
93710
  }
@@ -93699,7 +93740,7 @@ ${ruleIds.join(`
93699
93740
  for (const layer of await this.existingRuleSourceLayers()) {
93700
93741
  const entries = await this.context.fs.readDir(layer.path);
93701
93742
  const named = entries.map((entry) => ({ entry, name: entry.match(/^([\w-]+)\.(?:ya?ml|json)$/i)?.[1] ?? "" })).filter(({ name }) => name.length > 0);
93702
- const stats = await Promise.all(named.map(({ entry }) => this.context.fs.stat(join28(layer.path, entry))));
93743
+ const stats = await Promise.all(named.map(({ entry }) => this.context.fs.stat(join29(layer.path, entry))));
93703
93744
  named.forEach(({ name }, i2) => {
93704
93745
  if (stats[i2]?.isFile())
93705
93746
  candidates.add(name);
@@ -93721,7 +93762,7 @@ ${ruleIds.join(`
93721
93762
  for (const layer of layers) {
93722
93763
  for (const file2 of await this.listRuleFilesInLayer(layer.path)) {
93723
93764
  if (!merged.has(file2))
93724
- merged.set(file2, { absolutePath: join28(layer.path, file2), source: layer.id });
93765
+ merged.set(file2, { absolutePath: join29(layer.path, file2), source: layer.id });
93725
93766
  }
93726
93767
  }
93727
93768
  const files = [];
@@ -93789,7 +93830,7 @@ ${ruleIds.join(`
93789
93830
  for (const entry of entries.sort()) {
93790
93831
  if (entry === "presets")
93791
93832
  continue;
93792
- const stat2 = await this.context.fs.stat(join28(root, entry));
93833
+ const stat2 = await this.context.fs.stat(join29(root, entry));
93793
93834
  if (stat2?.isDirectory())
93794
93835
  dirs.push(entry);
93795
93836
  }
@@ -93797,12 +93838,12 @@ ${ruleIds.join(`
93797
93838
  }
93798
93839
  async listRuleFiles(root, relativeDir) {
93799
93840
  const { fs: fs3 } = this.context;
93800
- const dir = join28(root, relativeDir);
93841
+ const dir = join29(root, relativeDir);
93801
93842
  const entries = await fs3.readDir(dir);
93802
93843
  const files = [];
93803
93844
  for (const entry of entries.sort()) {
93804
- const relativePath2 = relativeDir.length === 0 ? entry : join28(relativeDir, entry);
93805
- const absolutePath = join28(root, relativePath2);
93845
+ const relativePath2 = relativeDir.length === 0 ? entry : join29(relativeDir, entry);
93846
+ const absolutePath = join29(root, relativePath2);
93806
93847
  const stat2 = await fs3.stat(absolutePath);
93807
93848
  if (stat2?.isDirectory()) {
93808
93849
  files.push(...await this.listRuleFiles(root, relativePath2));
@@ -93936,8 +93977,8 @@ var init_rule_service = __esm(() => {
93936
93977
  init_dist10();
93937
93978
  init_agent_execution();
93938
93979
  init_system_event_envelope();
93939
- LOCAL_RULES_DIR = join28(".spur", "rules");
93940
- GLOBAL_RULES_DIR = join28(".config", "spur", "rules");
93980
+ LOCAL_RULES_DIR = join29(".spur", "rules");
93981
+ GLOBAL_RULES_DIR = join29(".config", "spur", "rules");
93941
93982
  SEVERITY_RANK2 = {
93942
93983
  info: 0,
93943
93984
  warning: 1,
@@ -94152,6 +94193,572 @@ var init_run_store_service = __esm(() => {
94152
94193
  };
94153
94194
  });
94154
94195
 
94196
+ // ../../packages/app/src/services/slash-commands-service.ts
94197
+ import { existsSync as existsSync8, mkdirSync as mkdirSync9, readdirSync as readdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync6 } from "fs";
94198
+ import { homedir as homedir9 } from "os";
94199
+ import { basename as basename11, dirname as dirname19, join as join30, resolve as resolve16 } from "path";
94200
+ function getBuiltinClaudeCommands() {
94201
+ return BUILTIN_CLAUDE_COMMANDS.map((cmd) => ({ ...cmd }));
94202
+ }
94203
+ function deduceCommandCategory(nameOrStem) {
94204
+ const lower = nameOrStem.toLowerCase();
94205
+ if (/(git|commit|review|revert|pr-review|diff|branch|stash)/.test(lower)) {
94206
+ return "git";
94207
+ }
94208
+ if (/(rule|workflow|init|harness|spur|plan|run|verify|wrap|refine|parallel|idea)/.test(lower)) {
94209
+ return "harness";
94210
+ }
94211
+ if (/(doctor|cost|compact|clear|terminal|\bbug\b|config|login|logout|sys|status|ps)/.test(lower)) {
94212
+ return "sys";
94213
+ }
94214
+ return "dev";
94215
+ }
94216
+ function findPluginsSpCommandsDir(startDir = process.cwd()) {
94217
+ let current = resolve16(startDir);
94218
+ while (true) {
94219
+ try {
94220
+ const candidate = join30(current, "plugins", "sp", "commands");
94221
+ if (existsSync8(candidate)) {
94222
+ return candidate;
94223
+ }
94224
+ } catch {
94225
+ break;
94226
+ }
94227
+ const parent = dirname19(current);
94228
+ if (parent === current) {
94229
+ break;
94230
+ }
94231
+ current = parent;
94232
+ }
94233
+ return null;
94234
+ }
94235
+ function findProjectClaudeCommandsDir(startDir = process.cwd()) {
94236
+ let current = resolve16(startDir);
94237
+ while (true) {
94238
+ try {
94239
+ const candidate = join30(current, ".claude", "commands");
94240
+ if (existsSync8(candidate)) {
94241
+ return candidate;
94242
+ }
94243
+ } catch {
94244
+ break;
94245
+ }
94246
+ const parent = dirname19(current);
94247
+ if (parent === current) {
94248
+ break;
94249
+ }
94250
+ current = parent;
94251
+ }
94252
+ return null;
94253
+ }
94254
+ function parseMarkdownFrontmatter(rawContent) {
94255
+ const trimmed = rawContent.trimStart();
94256
+ if (!trimmed.startsWith("---")) {
94257
+ return { frontmatter: {}, body: rawContent };
94258
+ }
94259
+ const match = trimmed.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/);
94260
+ if (!match) {
94261
+ return { frontmatter: {}, body: rawContent };
94262
+ }
94263
+ try {
94264
+ const parsed = $parse(match[1] ?? "");
94265
+ return {
94266
+ frontmatter: typeof parsed === "object" && parsed !== null ? parsed : {},
94267
+ body: match[2] ?? ""
94268
+ };
94269
+ } catch {
94270
+ return { frontmatter: {}, body: rawContent };
94271
+ }
94272
+ }
94273
+ function scanMarkdownCommands(dirPath, options = {}) {
94274
+ if (!existsSync8(dirPath)) {
94275
+ return [];
94276
+ }
94277
+ const { prefix = "/", source = "plugin:sp", defaultCategory, pluginId } = options;
94278
+ const items = [];
94279
+ try {
94280
+ const entries = readdirSync7(dirPath, { withFileTypes: true });
94281
+ for (const entry of entries) {
94282
+ if (!entry.isFile() || !entry.name.endsWith(".md")) {
94283
+ continue;
94284
+ }
94285
+ const fullPath = join30(dirPath, entry.name);
94286
+ const stem = basename11(entry.name, ".md");
94287
+ const fileContent = readFileSync10(fullPath, "utf-8");
94288
+ const { frontmatter, body } = parseMarkdownFrontmatter(fileContent);
94289
+ const cleanPrefix = prefix.endsWith(":") ? prefix : prefix.endsWith("/") ? prefix : `${prefix}:`;
94290
+ const name = prefix === "/" ? `/${stem}` : `${cleanPrefix}${stem}`;
94291
+ let description = "";
94292
+ if (typeof frontmatter.description === "string" && frontmatter.description.trim().length > 0) {
94293
+ description = frontmatter.description.trim();
94294
+ } else {
94295
+ const lines = body.split(`
94296
+ `).map((l) => l.trim()).filter((l) => l.length > 0);
94297
+ const firstHeading = lines.find((l) => l.startsWith("# "));
94298
+ const firstLine = lines[0];
94299
+ if (firstHeading) {
94300
+ description = firstHeading.replace(/^#\s+/, "").trim();
94301
+ } else if (firstLine !== undefined) {
94302
+ description = firstLine.replace(/^[#*-\s]+/, "").trim();
94303
+ } else {
94304
+ description = stem.replace(/[-_]/g, " ");
94305
+ }
94306
+ }
94307
+ let category;
94308
+ const fmCategory = typeof frontmatter.category === "string" ? frontmatter.category.toLowerCase() : "";
94309
+ if (fmCategory === "git" || fmCategory === "dev" || fmCategory === "sys" || fmCategory === "harness") {
94310
+ category = fmCategory;
94311
+ } else {
94312
+ category = defaultCategory ?? deduceCommandCategory(stem);
94313
+ }
94314
+ const hintRaw = frontmatter["argument-hint"] ?? frontmatter.argumentHint ?? frontmatter.arguments ?? frontmatter.args;
94315
+ const argumentHint = typeof hintRaw === "string" && hintRaw.trim().length > 0 ? hintRaw.trim() : undefined;
94316
+ const roleRaw = frontmatter.role;
94317
+ const role = typeof roleRaw === "string" && roleRaw.trim().length > 0 ? roleRaw.trim() : undefined;
94318
+ const rawTags = frontmatter.tags ?? frontmatter.tag;
94319
+ const tagSet = new Set;
94320
+ if (Array.isArray(rawTags)) {
94321
+ for (const t of rawTags) {
94322
+ if (typeof t === "string" && t.trim().length > 0) {
94323
+ tagSet.add(t.trim().toLowerCase());
94324
+ }
94325
+ }
94326
+ } else if (typeof rawTags === "string" && rawTags.trim().length > 0) {
94327
+ for (const t of rawTags.split(",")) {
94328
+ if (t.trim().length > 0) {
94329
+ tagSet.add(t.trim().toLowerCase());
94330
+ }
94331
+ }
94332
+ }
94333
+ if (Array.isArray(options.tags)) {
94334
+ for (const t of options.tags) {
94335
+ if (t.trim().length > 0) {
94336
+ tagSet.add(t.trim().toLowerCase());
94337
+ }
94338
+ }
94339
+ }
94340
+ if (category) {
94341
+ tagSet.add(category);
94342
+ }
94343
+ if (pluginId) {
94344
+ tagSet.add(pluginId.toLowerCase());
94345
+ } else if (prefix && prefix !== "/") {
94346
+ const clean = prefix.replace(/^[/:]+|[/:]+$/g, "").toLowerCase();
94347
+ if (clean.length > 0) {
94348
+ tagSet.add(clean);
94349
+ }
94350
+ }
94351
+ for (const part of stem.split(/[-_]/)) {
94352
+ const p = part.trim().toLowerCase();
94353
+ if (p.length > 1 && !tagSet.has(p)) {
94354
+ tagSet.add(p);
94355
+ }
94356
+ }
94357
+ const tags = Array.from(tagSet);
94358
+ items.push({
94359
+ name,
94360
+ description,
94361
+ category,
94362
+ argumentHint,
94363
+ tags,
94364
+ role,
94365
+ source
94366
+ });
94367
+ }
94368
+ } catch {}
94369
+ return items;
94370
+ }
94371
+ function discoverEnabledClaudePlugins(options = {}) {
94372
+ const { projectRoot = process.cwd(), claudeDir = join30(homedir9(), ".claude") } = options;
94373
+ const userSettingsPath = join30(claudeDir, "settings.json");
94374
+ const projectSettingsPath = join30(projectRoot, ".claude", "settings.json");
94375
+ const installedPath = join30(claudeDir, "plugins", "installed_plugins.json");
94376
+ let userSettings = {};
94377
+ if (existsSync8(userSettingsPath)) {
94378
+ try {
94379
+ userSettings = JSON.parse(readFileSync10(userSettingsPath, "utf-8"));
94380
+ } catch {}
94381
+ }
94382
+ let projectSettings = {};
94383
+ if (existsSync8(projectSettingsPath)) {
94384
+ try {
94385
+ projectSettings = JSON.parse(readFileSync10(projectSettingsPath, "utf-8"));
94386
+ } catch {}
94387
+ }
94388
+ const enabledPlugins = {
94389
+ ...userSettings.enabledPlugins ?? {},
94390
+ ...projectSettings.enabledPlugins ?? {}
94391
+ };
94392
+ const extraKnownMarketplaces = {
94393
+ ...userSettings.extraKnownMarketplaces ?? {},
94394
+ ...projectSettings.extraKnownMarketplaces ?? {}
94395
+ };
94396
+ let installedManifest = { plugins: {} };
94397
+ if (existsSync8(installedPath)) {
94398
+ try {
94399
+ installedManifest = JSON.parse(readFileSync10(installedPath, "utf-8"));
94400
+ } catch {}
94401
+ }
94402
+ const installedPlugins = installedManifest.plugins ?? {};
94403
+ const candidateKeys = new Set;
94404
+ for (const [key2, isEnabled] of Object.entries(enabledPlugins)) {
94405
+ if (isEnabled) {
94406
+ candidateKeys.add(key2);
94407
+ }
94408
+ }
94409
+ if (candidateKeys.size === 0) {
94410
+ for (const key2 of Object.keys(installedPlugins)) {
94411
+ if (enabledPlugins[key2] !== false) {
94412
+ candidateKeys.add(key2);
94413
+ }
94414
+ }
94415
+ }
94416
+ const results = [];
94417
+ for (const pluginKey of candidateKeys) {
94418
+ if (enabledPlugins[pluginKey] === false) {
94419
+ continue;
94420
+ }
94421
+ const atIndex = pluginKey.indexOf("@");
94422
+ const pluginName = atIndex !== -1 ? pluginKey.slice(0, atIndex) : pluginKey;
94423
+ const marketplaceName = atIndex !== -1 ? pluginKey.slice(atIndex + 1) : undefined;
94424
+ let resolvedDir = null;
94425
+ const installs = installedPlugins[pluginKey];
94426
+ if (Array.isArray(installs)) {
94427
+ for (const inst of installs) {
94428
+ if (typeof inst.installPath === "string" && existsSync8(inst.installPath)) {
94429
+ resolvedDir = inst.installPath;
94430
+ break;
94431
+ }
94432
+ }
94433
+ }
94434
+ if (!resolvedDir && marketplaceName && extraKnownMarketplaces[marketplaceName]) {
94435
+ const mSrc = extraKnownMarketplaces[marketplaceName]?.source;
94436
+ if (mSrc?.source === "directory" && typeof mSrc.path === "string") {
94437
+ const cand1 = join30(mSrc.path, "plugins", pluginName);
94438
+ const cand2 = join30(mSrc.path, pluginName);
94439
+ if (existsSync8(cand1)) {
94440
+ resolvedDir = cand1;
94441
+ } else if (existsSync8(cand2)) {
94442
+ resolvedDir = cand2;
94443
+ }
94444
+ }
94445
+ }
94446
+ if (!resolvedDir && marketplaceName) {
94447
+ const cacheDir = join30(claudeDir, "plugins", "cache", marketplaceName, pluginName);
94448
+ if (existsSync8(cacheDir)) {
94449
+ try {
94450
+ const entries = readdirSync7(cacheDir, { withFileTypes: true });
94451
+ const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
94452
+ const lastDir = dirs[dirs.length - 1];
94453
+ if (lastDir) {
94454
+ resolvedDir = join30(cacheDir, lastDir);
94455
+ }
94456
+ } catch {}
94457
+ }
94458
+ }
94459
+ if (!resolvedDir && marketplaceName) {
94460
+ const mpDir = join30(claudeDir, "plugins", "marketplaces", marketplaceName, "plugins", pluginName);
94461
+ if (existsSync8(mpDir)) {
94462
+ resolvedDir = mpDir;
94463
+ }
94464
+ }
94465
+ if (!resolvedDir) {
94466
+ const localPlugin = resolve16(projectRoot, "plugins", pluginName);
94467
+ if (existsSync8(localPlugin)) {
94468
+ resolvedDir = localPlugin;
94469
+ }
94470
+ }
94471
+ let commandsDir;
94472
+ if (resolvedDir) {
94473
+ const candidateCommands = join30(resolvedDir, "commands");
94474
+ if (existsSync8(candidateCommands)) {
94475
+ commandsDir = candidateCommands;
94476
+ }
94477
+ }
94478
+ results.push({
94479
+ id: pluginKey,
94480
+ name: pluginName,
94481
+ marketplace: marketplaceName,
94482
+ installPath: resolvedDir ?? undefined,
94483
+ commandsDir
94484
+ });
94485
+ }
94486
+ return results;
94487
+ }
94488
+ function generateSlashCommands(options = {}) {
94489
+ const {
94490
+ projectRoot = process.cwd(),
94491
+ includeBuiltin = true,
94492
+ includePluginSp = true,
94493
+ includeAllPlugins = true,
94494
+ includeProjectClaude = true,
94495
+ includeUserClaude = true,
94496
+ includeUserSpur = true,
94497
+ claudeDir
94498
+ } = options;
94499
+ const commandMap = new Map;
94500
+ if (includeBuiltin) {
94501
+ for (const cmd of getBuiltinClaudeCommands()) {
94502
+ commandMap.set(cmd.name, cmd);
94503
+ }
94504
+ }
94505
+ if (includeAllPlugins) {
94506
+ const plugins = discoverEnabledClaudePlugins({ projectRoot, claudeDir });
94507
+ for (const plugin of plugins) {
94508
+ if (plugin.commandsDir) {
94509
+ const pluginCommands = scanMarkdownCommands(plugin.commandsDir, {
94510
+ prefix: `/${plugin.name}:`,
94511
+ source: `plugin:${plugin.name}`,
94512
+ pluginId: plugin.name,
94513
+ defaultCategory: deduceCommandCategory(plugin.name)
94514
+ });
94515
+ for (const cmd of pluginCommands) {
94516
+ commandMap.set(cmd.name, cmd);
94517
+ }
94518
+ }
94519
+ }
94520
+ }
94521
+ if (includePluginSp) {
94522
+ const spCommandsDir = findPluginsSpCommandsDir(projectRoot) ?? resolve16(projectRoot, "plugins", "sp", "commands");
94523
+ if (existsSync8(spCommandsDir)) {
94524
+ const spCommands = scanMarkdownCommands(spCommandsDir, {
94525
+ prefix: "/sp:",
94526
+ source: "plugin:sp",
94527
+ pluginId: "sp",
94528
+ defaultCategory: "harness"
94529
+ });
94530
+ for (const cmd of spCommands) {
94531
+ commandMap.set(cmd.name, cmd);
94532
+ }
94533
+ }
94534
+ }
94535
+ if (includeProjectClaude) {
94536
+ const projectClaudeDir = findProjectClaudeCommandsDir(projectRoot) ?? resolve16(projectRoot, ".claude", "commands");
94537
+ const projectCommands = scanMarkdownCommands(projectClaudeDir, {
94538
+ prefix: "/",
94539
+ source: "claude:project"
94540
+ });
94541
+ for (const cmd of projectCommands) {
94542
+ commandMap.set(cmd.name, cmd);
94543
+ }
94544
+ }
94545
+ if (includeUserClaude) {
94546
+ const userClaudeDir = join30(homedir9(), ".claude", "commands");
94547
+ const userClaudeCommands = scanMarkdownCommands(userClaudeDir, {
94548
+ prefix: "/",
94549
+ source: "claude:user"
94550
+ });
94551
+ for (const cmd of userClaudeCommands) {
94552
+ commandMap.set(cmd.name, cmd);
94553
+ }
94554
+ }
94555
+ if (includeUserSpur) {
94556
+ const userSpurDir = join30(homedir9(), ".config", "spur", "commands");
94557
+ const userSpurCommands = scanMarkdownCommands(userSpurDir, {
94558
+ prefix: "/sp:",
94559
+ source: "spur:user",
94560
+ pluginId: "sp",
94561
+ defaultCategory: "harness"
94562
+ });
94563
+ for (const cmd of userSpurCommands) {
94564
+ commandMap.set(cmd.name, cmd);
94565
+ }
94566
+ }
94567
+ return Array.from(commandMap.values());
94568
+ }
94569
+ function generateSlashCommandsFile(targetPath = getSlashCommandsFilePath(), options = {}) {
94570
+ const commands = generateSlashCommands(options);
94571
+ const filePayload = {
94572
+ version: 1,
94573
+ updatedAt: new Date().toISOString(),
94574
+ commands
94575
+ };
94576
+ try {
94577
+ const dir = dirname19(targetPath);
94578
+ if (!existsSync8(dir)) {
94579
+ mkdirSync9(dir, { recursive: true });
94580
+ }
94581
+ writeFileSync6(targetPath, JSON.stringify(filePayload, null, 2), "utf-8");
94582
+ } catch {}
94583
+ return {
94584
+ path: targetPath,
94585
+ count: commands.length,
94586
+ commands
94587
+ };
94588
+ }
94589
+ function loadSlashCommands(filePath = getSlashCommandsFilePath(), autoGenerateIfMissing = true, options = {}) {
94590
+ if (!existsSync8(filePath)) {
94591
+ if (autoGenerateIfMissing) {
94592
+ const generated = generateSlashCommandsFile(filePath, options);
94593
+ return generated.commands;
94594
+ }
94595
+ return getBuiltinClaudeCommands();
94596
+ }
94597
+ try {
94598
+ const content = readFileSync10(filePath, "utf-8");
94599
+ const raw = JSON.parse(content);
94600
+ const parsed = slashCommandsFileSchema.safeParse(raw);
94601
+ if (parsed.success && parsed.data.commands.length > 0) {
94602
+ return parsed.data.commands;
94603
+ }
94604
+ } catch {}
94605
+ if (autoGenerateIfMissing) {
94606
+ const generated = generateSlashCommandsFile(filePath, options);
94607
+ return generated.commands;
94608
+ }
94609
+ return getBuiltinClaudeCommands();
94610
+ }
94611
+
94612
+ class SlashCommandsService {
94613
+ filePath;
94614
+ projectRoot;
94615
+ cachedCommands = null;
94616
+ constructor(options = {}) {
94617
+ this.filePath = options.filePath ?? getSlashCommandsFilePath();
94618
+ this.projectRoot = options.projectRoot ?? process.cwd();
94619
+ }
94620
+ getFilePath() {
94621
+ return this.filePath;
94622
+ }
94623
+ getCommands(forceReload = false) {
94624
+ if (!this.cachedCommands || forceReload) {
94625
+ this.cachedCommands = loadSlashCommands(this.filePath, true, {
94626
+ projectRoot: this.projectRoot
94627
+ });
94628
+ }
94629
+ return this.cachedCommands;
94630
+ }
94631
+ generate(targetPath) {
94632
+ const dest = targetPath ?? this.filePath;
94633
+ const result = generateSlashCommandsFile(dest, {
94634
+ projectRoot: this.projectRoot
94635
+ });
94636
+ this.cachedCommands = result.commands;
94637
+ return result;
94638
+ }
94639
+ }
94640
+ var BUILTIN_CLAUDE_COMMANDS;
94641
+ var init_slash_commands_service = __esm(() => {
94642
+ init_src();
94643
+ init_dist2();
94644
+ BUILTIN_CLAUDE_COMMANDS = [
94645
+ {
94646
+ name: "/help",
94647
+ description: "View available commands, tools, and usage hints",
94648
+ category: "sys",
94649
+ argumentHint: "[command]",
94650
+ tags: ["builtin", "sys", "help"],
94651
+ source: "builtin"
94652
+ },
94653
+ {
94654
+ name: "/clear",
94655
+ description: "Clear conversation history and reset context window",
94656
+ category: "sys",
94657
+ tags: ["builtin", "sys", "clear"],
94658
+ source: "builtin"
94659
+ },
94660
+ {
94661
+ name: "/compact",
94662
+ description: "Purge execution trace and condense context window",
94663
+ category: "sys",
94664
+ argumentHint: "[focus]",
94665
+ tags: ["builtin", "sys", "compact", "context"],
94666
+ source: "builtin"
94667
+ },
94668
+ {
94669
+ name: "/cost",
94670
+ description: "Inspect token breakdown and USD expenditure",
94671
+ category: "sys",
94672
+ tags: ["builtin", "sys", "cost", "tokens"],
94673
+ source: "builtin"
94674
+ },
94675
+ {
94676
+ name: "/doctor",
94677
+ description: "Run diagnostic health checks on environment and tools",
94678
+ category: "sys",
94679
+ tags: ["builtin", "sys", "doctor", "health"],
94680
+ source: "builtin"
94681
+ },
94682
+ {
94683
+ name: "/init",
94684
+ description: "Initialize project instructions, settings and guidelines",
94685
+ category: "harness",
94686
+ tags: ["builtin", "harness", "init"],
94687
+ source: "builtin"
94688
+ },
94689
+ {
94690
+ name: "/review",
94691
+ description: "Inspect staged git diff, verify test coverage & lint",
94692
+ category: "git",
94693
+ argumentHint: "[target] [--cached]",
94694
+ tags: ["builtin", "git", "review"],
94695
+ source: "builtin"
94696
+ },
94697
+ {
94698
+ name: "/commit",
94699
+ description: "Draft conventional commit message from staged hunks",
94700
+ category: "git",
94701
+ argumentHint: "[--all] [-m <message>]",
94702
+ tags: ["builtin", "git", "commit"],
94703
+ source: "builtin"
94704
+ },
94705
+ {
94706
+ name: "/terminal",
94707
+ description: "Run isolated bash execution in sandbox mirror",
94708
+ category: "sys",
94709
+ argumentHint: "[command]",
94710
+ tags: ["builtin", "sys", "terminal", "bash"],
94711
+ source: "builtin"
94712
+ },
94713
+ {
94714
+ name: "/revert",
94715
+ description: "Undo last agent file changes or restore git stash",
94716
+ category: "git",
94717
+ argumentHint: "[stash-id | file]",
94718
+ tags: ["builtin", "git", "revert"],
94719
+ source: "builtin"
94720
+ },
94721
+ {
94722
+ name: "/resume",
94723
+ description: "Resume paused background agent run",
94724
+ category: "dev",
94725
+ argumentHint: "[run-id]",
94726
+ tags: ["builtin", "dev", "resume"],
94727
+ source: "builtin"
94728
+ },
94729
+ {
94730
+ name: "/bug",
94731
+ description: "Report a bug or issue with diagnostic transcript",
94732
+ category: "sys",
94733
+ argumentHint: "[description]",
94734
+ tags: ["builtin", "sys", "bug"],
94735
+ source: "builtin"
94736
+ },
94737
+ {
94738
+ name: "/config",
94739
+ description: "View or update local/global configuration settings",
94740
+ category: "sys",
94741
+ argumentHint: "[key] [value]",
94742
+ tags: ["builtin", "sys", "config"],
94743
+ source: "builtin"
94744
+ },
94745
+ {
94746
+ name: "/login",
94747
+ description: "Authenticate account and refresh API credentials",
94748
+ category: "sys",
94749
+ tags: ["builtin", "sys", "login", "auth"],
94750
+ source: "builtin"
94751
+ },
94752
+ {
94753
+ name: "/logout",
94754
+ description: "Sign out of current account and remove stored session",
94755
+ category: "sys",
94756
+ tags: ["builtin", "sys", "logout", "auth"],
94757
+ source: "builtin"
94758
+ }
94759
+ ];
94760
+ });
94761
+
94155
94762
  // ../../packages/app/src/services/write-slot-service.ts
94156
94763
  import { randomUUID as randomUUID2 } from "crypto";
94157
94764
 
@@ -94501,7 +95108,7 @@ var init_strategy_runtime = __esm(() => {
94501
95108
  });
94502
95109
 
94503
95110
  // ../../packages/app/src/services/supervisor-service.ts
94504
- import { dirname as dirname19 } from "path";
95111
+ import { dirname as dirname20 } from "path";
94505
95112
  function defaultWrapperArgv(agentId) {
94506
95113
  return {
94507
95114
  command: process.execPath,
@@ -94561,7 +95168,7 @@ class SupervisorService {
94561
95168
  }
94562
95169
  if (spec.tags.some((tag) => tag.startsWith("fleet:"))) {
94563
95170
  await new FleetService({
94564
- fs: createNodeFileSystem3(dirname19(dirname19(this.configDir)))
95171
+ fs: createNodeFileSystem3(dirname20(dirname20(this.configDir)))
94565
95172
  }).assertLaunchGroundTruth(spec.workspace);
94566
95173
  }
94567
95174
  const { command, args } = this.resolveCommand(spec);
@@ -94651,10 +95258,10 @@ class SupervisorService {
94651
95258
  proc.handle.kill("SIGTERM");
94652
95259
  let timedOut = false;
94653
95260
  const exitPromise = proc.handle.exited;
94654
- const timeout = new Promise((resolve16) => {
95261
+ const timeout = new Promise((resolve17) => {
94655
95262
  setTimeout(() => {
94656
95263
  timedOut = true;
94657
- resolve16();
95264
+ resolve17();
94658
95265
  }, 3000);
94659
95266
  });
94660
95267
  await Promise.race([exitPromise, timeout]);
@@ -95054,7 +95661,7 @@ async function* followSystemEventsAfter(getDb, opts) {
95054
95661
  await sleep3(FOLLOW_POLL_INTERVAL_MS);
95055
95662
  }
95056
95663
  }
95057
- var FOLLOW_POLL_INTERVAL_MS = 100, FOLLOW_BATCH_SIZE = 512, sleep3 = (ms) => new Promise((resolve16) => setTimeout(resolve16, ms));
95664
+ var FOLLOW_POLL_INTERVAL_MS = 100, FOLLOW_BATCH_SIZE = 512, sleep3 = (ms) => new Promise((resolve17) => setTimeout(resolve17, ms));
95058
95665
  var init_system_event_follow = __esm(() => {
95059
95666
  init_src2();
95060
95667
  });
@@ -95302,7 +95909,7 @@ var init_task_readiness = __esm(() => {
95302
95909
  });
95303
95910
 
95304
95911
  // ../../packages/app/src/services/task-scaffold.ts
95305
- import { dirname as dirname20 } from "path";
95912
+ import { dirname as dirname21 } from "path";
95306
95913
 
95307
95914
  class TaskScaffoldService {
95308
95915
  ctx;
@@ -95345,7 +95952,7 @@ class TaskScaffoldService {
95345
95952
  fileExists2 = false;
95346
95953
  }
95347
95954
  const mergeResult = mergeStubs(existingContent, stubs);
95348
- const dir = dirname20(targetFile);
95955
+ const dir = dirname21(targetFile);
95349
95956
  await this.ctx.fs.ensureDir(dir);
95350
95957
  if (mergeResult.created > 0 || !fileExists2) {
95351
95958
  await this.ctx.fs.writeFile(targetFile, mergeResult.content);
@@ -95427,7 +96034,7 @@ var init_task_size_precheck = __esm(() => {
95427
96034
  });
95428
96035
 
95429
96036
  // ../../packages/app/src/services/task-service.ts
95430
- import { dirname as dirname21, isAbsolute as isAbsolute7, join as join29, relative as relative5 } from "path";
96037
+ import { dirname as dirname22, isAbsolute as isAbsolute7, join as join31, relative as relative5 } from "path";
95431
96038
  function renderRosterTable(rows) {
95432
96039
  const header = `| WBS | Sub-task | Status |
95433
96040
  | --- | -------- | ------ |`;
@@ -95567,7 +96174,8 @@ ${templateBackground}`;
95567
96174
  ...input.featureId !== undefined ? { feature_id: input.featureId } : {},
95568
96175
  ...input.parentWbs !== undefined ? { parent_wbs: input.parentWbs } : {},
95569
96176
  ...input.priority !== undefined ? { priority: input.priority } : {},
95570
- ...input.tags !== undefined && input.tags.length > 0 ? { tags: input.tags } : {}
96177
+ ...input.tags !== undefined && input.tags.length > 0 ? { tags: input.tags } : {},
96178
+ ...input.estimateHours !== undefined ? { estimate_hours: input.estimateHours } : {}
95571
96179
  });
95572
96180
  let status;
95573
96181
  if (input.explicitStatus !== undefined) {
@@ -95667,7 +96275,8 @@ ${templateBackground}`;
95667
96275
  done_forced: true,
95668
96276
  done_reason: true,
95669
96277
  ac_numbering: true,
95670
- ac_altitude: true
96278
+ ac_altitude: true,
96279
+ estimate_hours: true
95671
96280
  };
95672
96281
  if (!(key2 in allowed)) {
95673
96282
  throw new Error(`Field "${key2}" is not settable via update; allowed: ${Object.keys(allowed).join(", ")}.`);
@@ -95917,8 +96526,8 @@ ${templateBackground}`;
95917
96526
  const featureId2 = fm.feature_id ?? fm["feature-id"];
95918
96527
  if (!featureId2 || featureId2.length === 0)
95919
96528
  return [];
95920
- const tasksDir = dirname21(taskFilePath);
95921
- const featuresDir = join29(tasksDir, "..", "features");
96529
+ const tasksDir = dirname22(taskFilePath);
96530
+ const featuresDir = join31(tasksDir, "..", "features");
95922
96531
  const featurePath = await (async () => {
95923
96532
  try {
95924
96533
  for (const name of await this.ctx.fs.readDir(featuresDir)) {
@@ -96062,7 +96671,8 @@ ${issues}`);
96062
96671
  plan: item.plan,
96063
96672
  acceptanceCriteria: item.acceptance_criteria,
96064
96673
  priority: item.priority,
96065
- tags: item.tags
96674
+ tags: item.tags,
96675
+ estimateHours: item.estimate_hours
96066
96676
  });
96067
96677
  try {
96068
96678
  this.validateCandidateOrThrow(item.name, shape);
@@ -96229,8 +96839,8 @@ ${block}` : block);
96229
96839
  return { wbs: hit.wbs, filePath: hit.filePath };
96230
96840
  if (opts.strict === true)
96231
96841
  return null;
96232
- const basename11 = filePath.split("/").pop() ?? "";
96233
- const capturedWbs = /^(\d{4})_.+\.md$/.exec(basename11)?.[1];
96842
+ const basename12 = filePath.split("/").pop() ?? "";
96843
+ const capturedWbs = /^(\d{4})_.+\.md$/.exec(basename12)?.[1];
96234
96844
  if (capturedWbs) {
96235
96845
  const taskPath = await this.locator.findPathByWbs(capturedWbs);
96236
96846
  if (taskPath !== null)
@@ -96702,8 +97312,8 @@ var init_task_verdict = __esm(() => {
96702
97312
  });
96703
97313
 
96704
97314
  // ../../packages/app/src/services/token-ledger-service.ts
96705
- import { closeSync as closeSync3, existsSync as existsSync8, fstatSync, openSync as openSync3, readSync } from "fs";
96706
- import { join as join30 } from "path";
97315
+ import { closeSync as closeSync3, existsSync as existsSync9, fstatSync, openSync as openSync3, readSync } from "fs";
97316
+ import { join as join32 } from "path";
96707
97317
  function isSparseToolActivity(events2) {
96708
97318
  return events2.length === 0 || !events2.some((e) => TOOL_ACTIVITY_TYPES.has(e.type));
96709
97319
  }
@@ -96754,7 +97364,7 @@ function parseLedgerLine(line) {
96754
97364
  }
96755
97365
  }
96756
97366
  function tailTokenLedgerFile(filePath, limit, chunkSize = DEFAULT_CHUNK, before) {
96757
- if (!existsSync8(filePath)) {
97367
+ if (!existsSync9(filePath)) {
96758
97368
  return { events: [], truncated: false };
96759
97369
  }
96760
97370
  const target = limit + 1;
@@ -96804,7 +97414,7 @@ class TokenLedgerService {
96804
97414
  ledgerPath;
96805
97415
  chunkSize;
96806
97416
  constructor(options) {
96807
- this.ledgerPath = options.ledgerPath ?? join30(options.cwd, TOKEN_LEDGER_RELATIVE_PATH);
97417
+ this.ledgerPath = options.ledgerPath ?? join32(options.cwd, TOKEN_LEDGER_RELATIVE_PATH);
96808
97418
  this.chunkSize = options.chunkSize ?? DEFAULT_CHUNK;
96809
97419
  }
96810
97420
  get path() {
@@ -96845,12 +97455,12 @@ class TokenLedgerService {
96845
97455
  var TOOL_ACTIVITY_TYPES, TOKEN_LEDGER_DEFAULT_LIMIT = 200, TOKEN_LEDGER_MAX_LIMIT = 1000, TOKEN_LEDGER_RELATIVE_PATH, DEFAULT_CHUNK;
96846
97456
  var init_token_ledger_service = __esm(() => {
96847
97457
  TOOL_ACTIVITY_TYPES = new Set(["read", "write", "bash", "grep", "glob"]);
96848
- TOKEN_LEDGER_RELATIVE_PATH = join30(".spur", "context", "token-ledger.jsonl");
97458
+ TOKEN_LEDGER_RELATIVE_PATH = join32(".spur", "context", "token-ledger.jsonl");
96849
97459
  DEFAULT_CHUNK = 64 * 1024;
96850
97460
  });
96851
97461
 
96852
97462
  // ../../packages/app/src/services/token-ledger-watcher.ts
96853
- import { closeSync as closeSync4, existsSync as existsSync9, fstatSync as fstatSync2, openSync as openSync4, readSync as readSync2, watch } from "fs";
97463
+ import { closeSync as closeSync4, existsSync as existsSync10, fstatSync as fstatSync2, openSync as openSync4, readSync as readSync2, watch } from "fs";
96854
97464
 
96855
97465
  class TokenLedgerWatcher {
96856
97466
  ledgerPath;
@@ -96871,7 +97481,7 @@ class TokenLedgerWatcher {
96871
97481
  this.started = true;
96872
97482
  this.offset = this.currentSize();
96873
97483
  try {
96874
- const target = existsSync9(this.ledgerPath) ? this.ledgerPath : this.parentDir();
97484
+ const target = existsSync10(this.ledgerPath) ? this.ledgerPath : this.parentDir();
96875
97485
  this.watcher = watch(target, () => this.schedulePoll());
96876
97486
  } catch {
96877
97487
  this.watcher = undefined;
@@ -96906,7 +97516,7 @@ class TokenLedgerWatcher {
96906
97516
  return idx > 0 ? this.ledgerPath.slice(0, idx) : ".";
96907
97517
  }
96908
97518
  currentSize() {
96909
- if (!existsSync9(this.ledgerPath))
97519
+ if (!existsSync10(this.ledgerPath))
96910
97520
  return 0;
96911
97521
  try {
96912
97522
  const fd = openSync4(this.ledgerPath, "r");
@@ -96928,7 +97538,7 @@ class TokenLedgerWatcher {
96928
97538
  }, this.debounceMs);
96929
97539
  }
96930
97540
  pollNewBytes() {
96931
- if (!existsSync9(this.ledgerPath)) {
97541
+ if (!existsSync10(this.ledgerPath)) {
96932
97542
  this.offset = 0;
96933
97543
  this.carry = "";
96934
97544
  return;
@@ -96973,7 +97583,7 @@ var init_token_ledger_watcher = __esm(() => {
96973
97583
  });
96974
97584
 
96975
97585
  // ../../packages/app/src/workflow/idea-handoff.ts
96976
- import { resolve as resolve16 } from "path";
97586
+ import { resolve as resolve17 } from "path";
96977
97587
  function processEvidence(r) {
96978
97588
  return `exit=${r.exitCode ?? "null"}${r.signal ? ` signal=${r.signal}` : ""}: ${r.stderr.trim() || "no stderr"}`;
96979
97589
  }
@@ -96983,12 +97593,12 @@ async function finalizeIdeaHandoff(options) {
96983
97593
  const executor = options.processExecutor ?? new NodeProcessExecutor3;
96984
97594
  const spurBin = options.spurBin ?? "spur";
96985
97595
  const { runId, featureId: featureId2 } = options;
96986
- const runDir = resolve16(root, ".spur", "run");
96987
- const batchPath = resolve16(runDir, `${runId}-idea-task-batch.json`);
96988
- const resultPath = resolve16(runDir, `${runId}-idea-batch-create-result.json`);
96989
- const orderPath = resolve16(runDir, `${runId}-idea-task-order.json`);
96990
- const readyPath = resolve16(runDir, `${runId}-idea-ready.json`);
96991
- const reportPath = resolve16(runDir, `${runId}-idea-handoff.md`);
97596
+ const runDir = resolve17(root, ".spur", "run");
97597
+ const batchPath = resolve17(runDir, `${runId}-idea-task-batch.json`);
97598
+ const resultPath = resolve17(runDir, `${runId}-idea-batch-create-result.json`);
97599
+ const orderPath = resolve17(runDir, `${runId}-idea-task-order.json`);
97600
+ const readyPath = resolve17(runDir, `${runId}-idea-ready.json`);
97601
+ const reportPath = resolve17(runDir, `${runId}-idea-handoff.md`);
96992
97602
  const spurSplit = splitLaunchCommand(spurBin, 'idea-handoff "spurBin"');
96993
97603
  if ("error" in spurSplit) {
96994
97604
  return {
@@ -97781,7 +98391,7 @@ var init_progress_follow = __esm(() => {
97781
98391
  });
97782
98392
 
97783
98393
  // ../../packages/app/src/workflow/trace-writer.ts
97784
- import { dirname as dirname22, join as join31 } from "path";
98394
+ import { dirname as dirname23, join as join33 } from "path";
97785
98395
 
97786
98396
  class WorkflowTraceWriter {
97787
98397
  path;
@@ -97789,7 +98399,7 @@ class WorkflowTraceWriter {
97789
98399
  pending = Promise.resolve();
97790
98400
  constructor(cwd, runId) {
97791
98401
  const safeRunId = runId.replace(/[^A-Za-z0-9._-]/g, "_");
97792
- this.path = join31(cwd, ".spur", "workflow", `${safeRunId}.jsonl`);
98402
+ this.path = join33(cwd, ".spur", "workflow", `${safeRunId}.jsonl`);
97793
98403
  }
97794
98404
  attach(bus) {
97795
98405
  bus.on("workflow.run.started", (event) => this.enqueue("workflow.run.started", event));
@@ -97815,7 +98425,7 @@ class WorkflowTraceWriter {
97815
98425
  `;
97816
98426
  this.pending = this.pending.then(async () => {
97817
98427
  const fs3 = createNodeFileSystem3();
97818
- await fs3.ensureDir(dirname22(this.path));
98428
+ await fs3.ensureDir(dirname23(this.path));
97819
98429
  await fs3.appendFile(this.path, line);
97820
98430
  });
97821
98431
  }
@@ -97910,6 +98520,7 @@ __export(exports_src2, {
97910
98520
  setPortProbeForTests: () => setPortProbeForTests,
97911
98521
  setDetachedServeSpawnForTests: () => setDetachedServeSpawnForTests,
97912
98522
  schedulerJobTimeoutEnvName: () => schedulerJobTimeoutEnvName,
98523
+ scanMarkdownCommands: () => scanMarkdownCommands,
97913
98524
  satisfies: () => satisfies,
97914
98525
  runIdeaHandoffCli: () => runIdeaHandoffCli,
97915
98526
  runHistoryReport: () => runHistoryReport,
@@ -97982,6 +98593,7 @@ __export(exports_src2, {
97982
98593
  normalizeLegacyTimeoutMs: () => normalizeLegacyTimeoutMs,
97983
98594
  materializeRoster: () => materializeRoster,
97984
98595
  looksLikeOpaqueId: () => looksLikeOpaqueId,
98596
+ loadSlashCommands: () => loadSlashCommands,
97985
98597
  isSystemEventEnvelopeV2: () => isSystemEventEnvelopeV2,
97986
98598
  isSchedulerCustomActiveConflict: () => isSchedulerCustomActiveConflict,
97987
98599
  isRecordAuthoredReview: () => isRecordAuthoredReview,
@@ -98001,7 +98613,10 @@ __export(exports_src2, {
98001
98613
  handleHistoryRefreshJob: () => handleHistoryRefreshJob,
98002
98614
  gtdStrategy: () => gtdStrategy,
98003
98615
  getLatestSystemEventSequence: () => getLatestSystemEventSequence,
98616
+ getBuiltinClaudeCommands: () => getBuiltinClaudeCommands,
98004
98617
  genericSystemEventCatalogEntry: () => genericSystemEventCatalogEntry,
98618
+ generateSlashCommandsFile: () => generateSlashCommandsFile,
98619
+ generateSlashCommands: () => generateSlashCommands,
98005
98620
  formatNoopMessage: () => formatNoopMessage,
98006
98621
  formatIssue: () => formatIssue,
98007
98622
  formatDenialMessage: () => formatDenialMessage,
@@ -98023,7 +98638,9 @@ __export(exports_src2, {
98023
98638
  enqueueHistoryRefresh: () => enqueueHistoryRefresh,
98024
98639
  encodeRunListCursor: () => encodeRunListCursor,
98025
98640
  drainPendingAgentQuotaUpdates: () => drainPendingAgentQuotaUpdates,
98641
+ discoverEnabledClaudePlugins: () => discoverEnabledClaudePlugins,
98026
98642
  deriveVerdict: () => deriveVerdict,
98643
+ deduceCommandCategory: () => deduceCommandCategory,
98027
98644
  decorateWorkflowEvent: () => decorateWorkflowEvent,
98028
98645
  decodeRunListCursor: () => decodeRunListCursor,
98029
98646
  decisionKindForGate: () => decisionKindForGate,
@@ -98086,6 +98703,7 @@ __export(exports_src2, {
98086
98703
  SystemEventEmitter: () => SystemEventEmitter,
98087
98704
  SupervisorService: () => SupervisorService,
98088
98705
  StrategyRuntime: () => StrategyRuntime,
98706
+ SlashCommandsService: () => SlashCommandsService,
98089
98707
  SectionMutationError: () => SectionMutationError,
98090
98708
  SYSTEM_EVENT_UNCATALOGED_WARN_EVENT: () => SYSTEM_EVENT_UNCATALOGED_WARN_EVENT,
98091
98709
  SYSTEM_EVENT_STREAMED_NAMES: () => SYSTEM_EVENT_STREAMED_NAMES,
@@ -98174,6 +98792,7 @@ __export(exports_src2, {
98174
98792
  CommandGateActionRunner: () => CommandGateActionRunner,
98175
98793
  CHILD_KILL_GRACE_MS: () => CHILD_KILL_GRACE_MS,
98176
98794
  BusPlanningEventEmitter: () => BusPlanningEventEmitter,
98795
+ BUILTIN_CLAUDE_COMMANDS: () => BUILTIN_CLAUDE_COMMANDS,
98177
98796
  AgentService: () => AgentService,
98178
98797
  AgentRunActionRunner: () => AgentRunActionRunner,
98179
98798
  AgentExecutionLifecycle: () => AgentExecutionLifecycle,
@@ -98224,6 +98843,7 @@ var init_src3 = __esm(async () => {
98224
98843
  init_run_session_observer();
98225
98844
  init_run_store_service();
98226
98845
  init_scheduler_custom_job_service();
98846
+ init_slash_commands_service();
98227
98847
  init_strategy_runtime();
98228
98848
  init_supervisor_service();
98229
98849
  init_system_event_catch_all();
@@ -106024,10 +106644,10 @@ init_src2();
106024
106644
  init_dist7();
106025
106645
  init_dist5();
106026
106646
  await init_src3();
106027
- import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
106647
+ import { existsSync as existsSync11, readFileSync as readFileSync11 } from "fs";
106028
106648
  import { rm, writeFile } from "fs/promises";
106029
106649
  import { tmpdir as tmpdir3 } from "os";
106030
- import { dirname as dirname23, join as join32 } from "path";
106650
+ import { dirname as dirname24, join as join34 } from "path";
106031
106651
 
106032
106652
  // src/colors.ts
106033
106653
  var CODES = {
@@ -106992,9 +107612,9 @@ init_src2();
106992
107612
  await init_src3();
106993
107613
 
106994
107614
  // src/workflow/resolve-spur-bin.ts
106995
- import { basename as basename11 } from "path";
107615
+ import { basename as basename12 } from "path";
106996
107616
  function resolveSpurBinFrom(launch) {
106997
- const runtime = basename11(launch.execPath).toLowerCase().replace(/\.exe$/, "");
107617
+ const runtime = basename12(launch.execPath).toLowerCase().replace(/\.exe$/, "");
106998
107618
  const isJsRuntime = runtime === "bun" || runtime === "node";
106999
107619
  if (isJsRuntime && launch.mainModule) {
107000
107620
  return `${launch.execPath} ${launch.mainModule}`;
@@ -107252,7 +107872,7 @@ ${result.content}`);
107252
107872
  'Sections replace, with one exception: `--section "Q&A"` APPENDS a timestamped',
107253
107873
  "`#### Q&A entry \u2014 <ISO>` block. Start the body with `<!-- qa:replace -->` to replace it wholesale."
107254
107874
  ].join(`
107255
- `)).option(...SHARED_OPTIONS.section).option(...SHARED_OPTIONS.fromFile).option(...SHARED_OPTIONS.featureFrontmatter).option(...SHARED_OPTIONS.prioritySet).option("--ac-numbering <mode>", "Set the ac_numbering frontmatter field (task-local) \u2014 opts the task into the Requirements\u2194AC coverage check").option("--ac-altitude <mode>", "Set the ac_altitude frontmatter field. Valid: `graduating` (default; feature-AC subset rule enforced) or `task-local` (skip the DD-09 subset rule \u2014 task scenarios are intentionally not feature ship criteria). Mirrors the L1 schema enum (packages/domain/src/planning/schema.ts:304).").option("--no-lifecycle", "Suppress lifecycle workflow run creation (use during pipeline runs to avoid orphaned lifecycle runs)").option("--assignee <spec-id>", "Set the assignee frontmatter field to an agent spec id").option("--force-done", "Allow transitioning to `done` even when the verify verdict is not PASS; records an override (task 0292). Waives the verdict only \u2014 the FSM path still applies, so from an earlier status walk the hops first: `todo` \u2192 `wip` \u2192 `testing` \u2192 `done` (each hop runs the structural `spur task check`)").option("--reason <text>", "Rationale for a forced-done override (paired with --force-done; persisted as done_reason)").option("--verdict-dir <path>", "Directory holding <wbs>-verdict.json artifacts (default: .spur/run)").option(...SHARED_OPTIONS.folderTasks).option(...SHARED_OPTIONS.json).option(...SHARED_OPTIONS.jsonEnvelope).action(async (wbs, status, options) => {
107875
+ `)).option(...SHARED_OPTIONS.section).option(...SHARED_OPTIONS.fromFile).option(...SHARED_OPTIONS.featureFrontmatter).option(...SHARED_OPTIONS.prioritySet).option("--ac-numbering <mode>", "Set the ac_numbering frontmatter field (task-local) \u2014 opts the task into the Requirements\u2194AC coverage check").option("--ac-altitude <mode>", "Set the ac_altitude frontmatter field. Valid: `graduating` (default; feature-AC subset rule enforced) or `task-local` (skip the DD-09 subset rule \u2014 task scenarios are intentionally not feature ship criteria). Mirrors the L1 schema enum (packages/domain/src/planning/schema.ts:304).").option("--estimate-hours <n>", "Set the estimate_hours frontmatter field (positive number) \u2014 decomposition size estimate; the inline pipeline driver dispatches no subagent at/below its floor").option("--no-lifecycle", "Suppress lifecycle workflow run creation (use during pipeline runs to avoid orphaned lifecycle runs)").option("--assignee <spec-id>", "Set the assignee frontmatter field to an agent spec id").option("--force-done", "Allow transitioning to `done` even when the verify verdict is not PASS; records an override (task 0292). Waives the verdict only \u2014 the FSM path still applies, so from an earlier status walk the hops first: `todo` \u2192 `wip` \u2192 `testing` \u2192 `done` (each hop runs the structural `spur task check`)").option("--reason <text>", "Rationale for a forced-done override (paired with --force-done; persisted as done_reason)").option("--verdict-dir <path>", "Directory holding <wbs>-verdict.json artifacts (default: .spur/run)").option(...SHARED_OPTIONS.folderTasks).option(...SHARED_OPTIONS.json).option(...SHARED_OPTIONS.jsonEnvelope).action(async (wbs, status, options) => {
107256
107876
  const svc = await makeService(context4, options.folder, options.lifecycle === false);
107257
107877
  try {
107258
107878
  if (options.assignee !== undefined) {
@@ -107290,9 +107910,9 @@ ${result.content}`);
107290
107910
  }
107291
107911
  context4.output.write(`Updated section '${options.section}' in task ${result.ref.id}`);
107292
107912
  }
107293
- } else if (options.feature !== undefined || options.priority !== undefined || options.acNumbering !== undefined || options.acAltitude !== undefined) {
107294
- const key2 = options.feature !== undefined ? "feature_id" : options.priority !== undefined ? "priority" : options.acNumbering !== undefined ? "ac_numbering" : "ac_altitude";
107295
- const value = options.feature ?? options.priority ?? options.acNumbering ?? options.acAltitude ?? "";
107913
+ } else if (options.feature !== undefined || options.priority !== undefined || options.acNumbering !== undefined || options.acAltitude !== undefined || options.estimateHours !== undefined) {
107914
+ const key2 = options.feature !== undefined ? "feature_id" : options.priority !== undefined ? "priority" : options.acNumbering !== undefined ? "ac_numbering" : options.acAltitude !== undefined ? "ac_altitude" : "estimate_hours";
107915
+ const value = options.feature ?? options.priority ?? options.acNumbering ?? options.acAltitude ?? options.estimateHours ?? "";
107296
107916
  const result = await svc.updateField(wbs, key2, value);
107297
107917
  if (options.json) {
107298
107918
  context4.output.write(toEnvelopeJson(result, { enveloped: options.jsonEnvelope }));
@@ -107322,7 +107942,7 @@ ${result.content}`);
107322
107942
  let forcedDoneVerdict;
107323
107943
  if (status === "done") {
107324
107944
  const current = await svc.show(wbs);
107325
- const verdictDir = options.verdictDir ?? join32(context4.cwd, ".spur", "run");
107945
+ const verdictDir = options.verdictDir ?? join34(context4.cwd, ".spur", "run");
107326
107946
  const loaded = await readVerdictArtifact(context4.fs, verdictDir, wbs);
107327
107947
  const guardOutcome = evaluateDoneTransition({
107328
107948
  wbs,
@@ -107384,7 +108004,7 @@ ${result.content}`);
107384
108004
  }
107385
108005
  }
107386
108006
  } else {
107387
- context4.output.error("Either <status>, --section/--from-file, or --feature/--priority is required");
108007
+ context4.output.error("Either <status>, --section/--from-file, or a field flag (--feature/--priority/--ac-numbering/--ac-altitude/--estimate-hours) is required");
107388
108008
  context4.setExitCode(2);
107389
108009
  }
107390
108010
  } catch (err) {
@@ -107558,7 +108178,7 @@ ${result.content}`);
107558
108178
  dryRun,
107559
108179
  projectRoot: context4.cwd,
107560
108180
  write: async (filePath, wbs, section, newBody) => {
107561
- const ref = { kind: "task", id: wbs, filePath, folder: dirname23(filePath) };
108181
+ const ref = { kind: "task", id: wbs, filePath, folder: dirname24(filePath) };
107562
108182
  const ws = new PlanningWriteService({ fs: context4.fs, emitter: makePlanningEmitter(context4) });
107563
108183
  await ws.updateSection(ref, section, newBody);
107564
108184
  }
@@ -107632,7 +108252,7 @@ ${result.content}`);
107632
108252
  agentSelector: options.agent,
107633
108253
  cwd: context4.cwd
107634
108254
  });
107635
- tmpPath = join32(tmpdir3(), `spur-ready-batch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`);
108255
+ tmpPath = join34(tmpdir3(), `spur-ready-batch-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`);
107636
108256
  await writeFile(tmpPath, `${JSON.stringify(prepared.items, null, 2)}
107637
108257
  `);
107638
108258
  batchFile = tmpPath;
@@ -108065,15 +108685,15 @@ function loadTemplateBodies(projectRoot, variant) {
108065
108685
  if (cached2 !== undefined)
108066
108686
  return cached2;
108067
108687
  let bodies = {};
108068
- const localPath = join32(projectRoot, ".spur", "tasks", "templates", `${variant}.md`);
108069
- if (existsSync10(localPath)) {
108070
- bodies = extractTemplateBodies(readFileSync10(localPath, "utf8"));
108688
+ const localPath = join34(projectRoot, ".spur", "tasks", "templates", `${variant}.md`);
108689
+ if (existsSync11(localPath)) {
108690
+ bodies = extractTemplateBodies(readFileSync11(localPath, "utf8"));
108071
108691
  } else {
108072
108692
  const root = bundledConfigRoot();
108073
108693
  if (root !== null) {
108074
- const templatePath = join32(root, "templates", "task", `${variant}.md`);
108075
- if (existsSync10(templatePath)) {
108076
- bodies = extractTemplateBodies(readFileSync10(templatePath, "utf8"));
108694
+ const templatePath = join34(root, "templates", "task", `${variant}.md`);
108695
+ if (existsSync11(templatePath)) {
108696
+ bodies = extractTemplateBodies(readFileSync11(templatePath, "utf8"));
108077
108697
  }
108078
108698
  }
108079
108699
  }
@@ -108129,7 +108749,7 @@ async function loadSectionMatrixUncached(projectRoot) {
108129
108749
  }
108130
108750
  const root = bundledConfigRoot();
108131
108751
  if (root !== null) {
108132
- const matrixPath = join32(root, "tasks", "section-matrix.yaml");
108752
+ const matrixPath = join34(root, "tasks", "section-matrix.yaml");
108133
108753
  if (await fs3.exists(matrixPath)) {
108134
108754
  const data = await loadStructuredSpurConfig(matrixPath, {
108135
108755
  validateJsonSchema: true,
@@ -108140,7 +108760,7 @@ async function loadSectionMatrixUncached(projectRoot) {
108140
108760
  }
108141
108761
  throw new Error(`no canonical section-matrix found for task section authority (F92 R1); tried:
108142
108762
  ` + ` - ${localPath}
108143
- ` + (root !== null ? ` - ${join32(root, "tasks", "section-matrix.yaml")}
108763
+ ` + (root !== null ? ` - ${join34(root, "tasks", "section-matrix.yaml")}
108144
108764
  ` : "") + "copy/generate section-matrix.yaml from the canonical build-time matrix asset (repo `config` `tasks` tree) into one of those paths");
108145
108765
  }
108146
108766
 
@@ -108518,15 +109138,15 @@ function parseTimeout(raw) {
108518
109138
  return n3;
108519
109139
  }
108520
109140
  function loopSleep(ms, signal) {
108521
- return new Promise((resolve17) => {
109141
+ return new Promise((resolve18) => {
108522
109142
  if (signal?.aborted) {
108523
- resolve17();
109143
+ resolve18();
108524
109144
  return;
108525
109145
  }
108526
- const timer = setTimeout(resolve17, ms);
109146
+ const timer = setTimeout(resolve18, ms);
108527
109147
  signal?.addEventListener("abort", () => {
108528
109148
  clearTimeout(timer);
108529
- resolve17();
109149
+ resolve18();
108530
109150
  }, { once: true });
108531
109151
  });
108532
109152
  }
@@ -108846,13 +109466,13 @@ function waitFail(context4, options, code, message) {
108846
109466
  // src/release-ops.ts
108847
109467
  init_src();
108848
109468
  init_dist5();
108849
- import { existsSync as existsSync12, readdirSync as readdirSync7, readFileSync as readFileSync11 } from "fs";
108850
- import { join as join34 } from "path";
109469
+ import { existsSync as existsSync13, readdirSync as readdirSync8, readFileSync as readFileSync12 } from "fs";
109470
+ import { join as join36 } from "path";
108851
109471
 
108852
109472
  // src/version-carriers.ts
108853
109473
  init_zod();
108854
- import { existsSync as existsSync11 } from "fs";
108855
- import { join as join33 } from "path";
109474
+ import { existsSync as existsSync12 } from "fs";
109475
+ import { join as join35 } from "path";
108856
109476
  var defaultLiteralProbe = { file: "src/config.ts", identifier: "binaryVersion" };
108857
109477
  var registry2 = new Map;
108858
109478
  function registerCarrierType(type, def) {
@@ -108888,7 +109508,7 @@ function findLiteralProbe(carriers, packageDir) {
108888
109508
  return;
108889
109509
  }
108890
109510
  async function readJson(path9) {
108891
- if (!existsSync11(path9))
109511
+ if (!existsSync12(path9))
108892
109512
  return null;
108893
109513
  try {
108894
109514
  return JSON.parse(await Bun.file(path9).text());
@@ -108904,7 +109524,7 @@ registerCarrierType("plugin-manifest", {
108904
109524
  schema: PluginManifestCarrierSchema,
108905
109525
  async syncRepoWide(carrier, { repoRoot }, version3, staged, output2) {
108906
109526
  for (const rel of carrier.paths) {
108907
- const absPath = join33(repoRoot, rel);
109527
+ const absPath = join35(repoRoot, rel);
108908
109528
  const manifest = await readJson(absPath);
108909
109529
  if (manifest === null) {
108910
109530
  output2.write(` \u26A0 ${rel}: not found or malformed \u2014 skipping`);
@@ -108988,7 +109608,7 @@ async function readJson2(path9) {
108988
109608
  function rootWorkspaceGlobs(repoRoot) {
108989
109609
  let root;
108990
109610
  try {
108991
- root = JSON.parse(readFileSync11(join34(repoRoot, "package.json"), "utf8"));
109611
+ root = JSON.parse(readFileSync12(join36(repoRoot, "package.json"), "utf8"));
108992
109612
  } catch {
108993
109613
  return [];
108994
109614
  }
@@ -109005,11 +109625,11 @@ function workspaceDirs(repoRoot) {
109005
109625
  if (!glob.endsWith("/*"))
109006
109626
  continue;
109007
109627
  const parent = glob.slice(0, -2);
109008
- const parentAbs = join34(repoRoot, parent);
109009
- if (!existsSync12(parentAbs))
109628
+ const parentAbs = join36(repoRoot, parent);
109629
+ if (!existsSync13(parentAbs))
109010
109630
  continue;
109011
- for (const entry of readdirSync7(parentAbs, { withFileTypes: true })) {
109012
- if (entry.isDirectory() && existsSync12(join34(parentAbs, entry.name, "package.json"))) {
109631
+ for (const entry of readdirSync8(parentAbs, { withFileTypes: true })) {
109632
+ if (entry.isDirectory() && existsSync13(join36(parentAbs, entry.name, "package.json"))) {
109013
109633
  dirs.push(`${parent}/${entry.name}`);
109014
109634
  }
109015
109635
  }
@@ -109017,14 +109637,14 @@ function workspaceDirs(repoRoot) {
109017
109637
  return dirs;
109018
109638
  }
109019
109639
  async function releaseContext(repoRoot, bumpVerConfig = resolveBuilderBumpVerConfig(null)) {
109020
- const rootManifest = await readJson2(join34(repoRoot, "package.json"));
109640
+ const rootManifest = await readJson2(join36(repoRoot, "package.json"));
109021
109641
  if (rootManifest === null) {
109022
109642
  throw new Error(`no package.json found at ${repoRoot} \u2014 builder operates on a package workspace root.`);
109023
109643
  }
109024
109644
  const rootName = typeof rootManifest.name === "string" ? rootManifest.name : "root";
109025
109645
  const workspaces2 = [];
109026
109646
  for (const dir of workspaceDirs(repoRoot)) {
109027
- const manifest = await readJson2(join34(repoRoot, dir, "package.json"));
109647
+ const manifest = await readJson2(join36(repoRoot, dir, "package.json"));
109028
109648
  if (manifest === null)
109029
109649
  continue;
109030
109650
  workspaces2.push({
@@ -109039,8 +109659,8 @@ async function releaseContext(repoRoot, bumpVerConfig = resolveBuilderBumpVerCon
109039
109659
  const packages = new Map;
109040
109660
  for (const ws of workspaces2) {
109041
109661
  const probe = findLiteralProbe(versionCarriers, ws.dir) ?? defaultLiteralProbe;
109042
- const literalProbe = join34(ws.dir, probe.file);
109043
- const versionSourceFile = existsSync12(join34(repoRoot, literalProbe)) ? literalProbe : undefined;
109662
+ const literalProbe = join36(ws.dir, probe.file);
109663
+ const versionSourceFile = existsSync13(join36(repoRoot, literalProbe)) ? literalProbe : undefined;
109044
109664
  packages.set(shortName(ws.name), {
109045
109665
  packageDir: ws.dir,
109046
109666
  packageName: ws.name,
@@ -109078,7 +109698,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output2
109078
109698
  const changed = [];
109079
109699
  for (const dir of workspaceDirs(ctx.repoRoot)) {
109080
109700
  const relPath = `${dir}/package.json`;
109081
- const manifest = await readJson2(join34(ctx.repoRoot, relPath));
109701
+ const manifest = await readJson2(join36(ctx.repoRoot, relPath));
109082
109702
  if (manifest === null)
109083
109703
  continue;
109084
109704
  let dirty = false;
@@ -109095,7 +109715,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output2
109095
109715
  }
109096
109716
  }
109097
109717
  if (dirty) {
109098
- await Bun.write(join34(ctx.repoRoot, relPath), `${JSON.stringify(manifest, null, 4)}
109718
+ await Bun.write(join36(ctx.repoRoot, relPath), `${JSON.stringify(manifest, null, 4)}
109099
109719
  `);
109100
109720
  output2.write(` \u21B3 ${relPath}: ${pkgName} workspace pin ${oldVersion} \u2192 ${newVersion}`);
109101
109721
  changed.push(relPath);
@@ -109104,7 +109724,7 @@ async function updateWorkspacePins(ctx, pkgName, oldVersion, newVersion, output2
109104
109724
  return changed;
109105
109725
  }
109106
109726
  async function updateVersionSourceFile(ctx, filePath, previous, next, output2, identifier = "binaryVersion") {
109107
- const absPath = join34(ctx.repoRoot, filePath);
109727
+ const absPath = join36(ctx.repoRoot, filePath);
109108
109728
  const content = await Bun.file(absPath).text();
109109
109729
  const pattern = new RegExp(`(${identifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:\\s*['"])\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z-.]+)?(?:\\+[0-9A-Za-z-.]+)?(['"])`);
109110
109730
  const updated = content.replace(pattern, `$1${next}$2`);
@@ -109122,7 +109742,7 @@ async function syncExtraCarriers(ctx, version3, staged, output2) {
109122
109742
  }
109123
109743
  }
109124
109744
  async function syncMarketplaceAndPlugins(ctx, version3, staged, output2) {
109125
- const marketplacePath = join34(ctx.repoRoot, ".claude-plugin", "marketplace.json");
109745
+ const marketplacePath = join36(ctx.repoRoot, ".claude-plugin", "marketplace.json");
109126
109746
  const marketplaceFile = Bun.file(marketplacePath);
109127
109747
  if (!await marketplaceFile.exists())
109128
109748
  return;
@@ -109147,7 +109767,7 @@ async function syncMarketplaceAndPlugins(ctx, version3, staged, output2) {
109147
109767
  output2.write(`Bumped marketplace plugins to ${version3}`);
109148
109768
  }
109149
109769
  for (const entry of plugins) {
109150
- const pluginJsonPath = join34(ctx.repoRoot, entry.source, "plugin.json");
109770
+ const pluginJsonPath = join36(ctx.repoRoot, entry.source, "plugin.json");
109151
109771
  const pluginJson = await readJson2(pluginJsonPath);
109152
109772
  if (pluginJson === null) {
109153
109773
  output2.write(` \u26A0 plugin.json not found at ${entry.source}/plugin.json \u2014 skipping`);
@@ -109200,7 +109820,7 @@ async function bumpVersion(ctx, config4, version3, options, output2) {
109200
109820
  if (await npmViewVersion(ctx.repoRoot, config4.packageName, version3)) {
109201
109821
  throw new Error(`${config4.packageName}@${version3} is already published on npm. Use a new version.`);
109202
109822
  }
109203
- const manifestPath = join34(ctx.repoRoot, config4.packageDir, "package.json");
109823
+ const manifestPath = join36(ctx.repoRoot, config4.packageDir, "package.json");
109204
109824
  const manifest = await readJson2(manifestPath);
109205
109825
  if (manifest === null)
109206
109826
  throw new Error(`missing or malformed manifest at ${config4.packageDir}/package.json`);
@@ -109218,8 +109838,8 @@ async function bumpVersion(ctx, config4, version3, options, output2) {
109218
109838
  staged.push(...await updateWorkspacePins(ctx, config4.packageName, previous, version3, output2));
109219
109839
  await syncMarketplaceAndPlugins(ctx, version3, staged, output2);
109220
109840
  await syncExtraCarriers(ctx, version3, staged, output2);
109221
- const lockPath = join34(ctx.repoRoot, "bun.lock");
109222
- if (existsSync12(lockPath) && Bun.file(lockPath).size > 0)
109841
+ const lockPath = join36(ctx.repoRoot, "bun.lock");
109842
+ if (existsSync13(lockPath) && Bun.file(lockPath).size > 0)
109223
109843
  staged.push("bun.lock");
109224
109844
  await git2(ctx.repoRoot, ["add", ...staged]);
109225
109845
  const message = `${config4.releaseCommitType}(${config4.releaseCommitScope}): ${config4.releaseCommitSubject(version3)}`;
@@ -109274,7 +109894,7 @@ async function bumpAll(ctx, version3, options, output2) {
109274
109894
  }
109275
109895
  const staged = [];
109276
109896
  for (const config4 of configs) {
109277
- const manifestPath = join34(ctx.repoRoot, config4.packageDir, "package.json");
109897
+ const manifestPath = join36(ctx.repoRoot, config4.packageDir, "package.json");
109278
109898
  const manifest = await readJson2(manifestPath);
109279
109899
  if (manifest === null) {
109280
109900
  throw new Error(`missing or malformed manifest at ${config4.packageDir}/package.json`);
@@ -109294,8 +109914,8 @@ async function bumpAll(ctx, version3, options, output2) {
109294
109914
  }
109295
109915
  await syncMarketplaceAndPlugins(ctx, version3, staged, output2);
109296
109916
  await syncExtraCarriers(ctx, version3, staged, output2);
109297
- const lockPath = join34(ctx.repoRoot, "bun.lock");
109298
- if (existsSync12(lockPath) && Bun.file(lockPath).size > 0)
109917
+ const lockPath = join36(ctx.repoRoot, "bun.lock");
109918
+ if (existsSync13(lockPath) && Bun.file(lockPath).size > 0)
109299
109919
  staged.push("bun.lock");
109300
109920
  await git2(ctx.repoRoot, ["add", ...staged]);
109301
109921
  const shortNames = configs.map((c3) => c3.packageName.split("/").pop() ?? c3.packageName).join(" + ");
@@ -109900,7 +110520,7 @@ import { createRequire } from "module";
109900
110520
  var CLI_CONFIG = {
109901
110521
  binaryName: "spur",
109902
110522
  binaryLabel: "spur",
109903
- binaryVersion: "0.3.83",
110523
+ binaryVersion: "0.3.85",
109904
110524
  configDir: ".spur",
109905
110525
  configFile: ".spur/config.yaml",
109906
110526
  databaseFile: ".spur/spur.db"
@@ -110311,8 +110931,8 @@ init_src();
110311
110931
  init_loader();
110312
110932
  init_src2();
110313
110933
  init_dist10();
110314
- import { homedir as homedir8 } from "os";
110315
- import { join as join35, resolve as resolve17 } from "path";
110934
+ import { homedir as homedir10 } from "os";
110935
+ import { join as join37, resolve as resolve18 } from "path";
110316
110936
 
110317
110937
  // src/config/scaffold-manifest.ts
110318
110938
  var SCAFFOLD_MANIFEST = [
@@ -110341,8 +110961,8 @@ var SCAFFOLD_MANIFEST = [
110341
110961
  ];
110342
110962
 
110343
110963
  // src/commands/init.ts
110344
- var GLOBAL_CONFIG_DIR = join35(".config", "spur");
110345
- var GLOBAL_RULES_DIR2 = join35(GLOBAL_CONFIG_DIR, "rules");
110964
+ var GLOBAL_CONFIG_DIR = join37(".config", "spur");
110965
+ var GLOBAL_RULES_DIR2 = join37(GLOBAL_CONFIG_DIR, "rules");
110346
110966
  var GLOBAL_CONFIG_EXAMPLE = BUNDLED_GLOBAL_CONFIG;
110347
110967
  var GLOBAL_CONFIG_FILE2 = "config.yaml";
110348
110968
  var INDEXED_CONTEXT_MARKER = "## Indexed context";
@@ -110373,7 +110993,7 @@ If \`.spur/context/\` is absent, proceed normally. Never block work on its absen
110373
110993
  `;
110374
110994
  function globalRulesRoot(context4) {
110375
110995
  const override = context4.env.SPUR_GLOBAL_RULES_DIR;
110376
- return override !== undefined && override.length > 0 ? resolve17(context4.cwd, override) : join35(homedir8(), GLOBAL_RULES_DIR2);
110996
+ return override !== undefined && override.length > 0 ? resolve18(context4.cwd, override) : join37(homedir10(), GLOBAL_RULES_DIR2);
110377
110997
  }
110378
110998
  async function writeIfNew(context4, path9, content, force, result) {
110379
110999
  if (!force && await context4.fs.exists(path9)) {
@@ -110390,18 +111010,18 @@ async function seedGlobalRules(context4) {
110390
111010
  const target = globalRulesRoot(context4);
110391
111011
  let written = 0;
110392
111012
  for (const relPath of await listBundledRuleFiles()) {
110393
- const destination = join35(target, relPath);
111013
+ const destination = join37(target, relPath);
110394
111014
  if (await context4.fs.exists(destination))
110395
111015
  continue;
110396
- await context4.fs.ensureDir(join35(target, ...relPath.split("/").slice(0, -1)));
110397
- await context4.fs.writeFile(destination, await context4.fs.readFile(join35(source, relPath)));
111016
+ await context4.fs.ensureDir(join37(target, ...relPath.split("/").slice(0, -1)));
111017
+ await context4.fs.writeFile(destination, await context4.fs.readFile(join37(source, relPath)));
110398
111018
  written += 1;
110399
111019
  }
110400
111020
  return written;
110401
111021
  }
110402
111022
  function resolveGlobalConfigDir(context4) {
110403
111023
  const globalOverride = context4.env.SPUR_GLOBAL_RULES_DIR;
110404
- return globalOverride !== undefined && globalOverride.length > 0 ? resolve17(context4.cwd, globalOverride) : join35(homedir8(), GLOBAL_CONFIG_DIR);
111024
+ return globalOverride !== undefined && globalOverride.length > 0 ? resolve18(context4.cwd, globalOverride) : join37(homedir10(), GLOBAL_CONFIG_DIR);
110405
111025
  }
110406
111026
  async function seedGlobalConfig(context4) {
110407
111027
  const source = bundledConfigRoot();
@@ -110412,15 +111032,15 @@ async function seedGlobalConfig(context4) {
110412
111032
  for (const relPath of listBundledConfigFiles()) {
110413
111033
  if (relPath === GLOBAL_CONFIG_EXAMPLE)
110414
111034
  continue;
110415
- const destination = join35(target, relPath);
111035
+ const destination = join37(target, relPath);
110416
111036
  if (await context4.fs.exists(destination))
110417
111037
  continue;
110418
- await context4.fs.ensureDir(join35(target, ...relPath.split("/").slice(0, -1)));
110419
- await context4.fs.writeFile(destination, await context4.fs.readFile(join35(source, relPath)));
111038
+ await context4.fs.ensureDir(join37(target, ...relPath.split("/").slice(0, -1)));
111039
+ await context4.fs.writeFile(destination, await context4.fs.readFile(join37(source, relPath)));
110420
111040
  written += 1;
110421
111041
  }
110422
- const examplePath = join35(source, GLOBAL_CONFIG_EXAMPLE);
110423
- const globalConfigPath = join35(target, GLOBAL_CONFIG_FILE2);
111042
+ const examplePath = join37(source, GLOBAL_CONFIG_EXAMPLE);
111043
+ const globalConfigPath = join37(target, GLOBAL_CONFIG_FILE2);
110424
111044
  if (await context4.fs.exists(examplePath) && !await context4.fs.exists(globalConfigPath)) {
110425
111045
  await context4.fs.ensureDir(target);
110426
111046
  await context4.fs.writeFile(globalConfigPath, await context4.fs.readFile(examplePath));
@@ -110435,10 +111055,10 @@ function registerInitCommand(program2, context4, options = {}) {
110435
111055
  const minimal = options2.minimal === true;
110436
111056
  const adoptGlobal = options2.adoptGlobalConfig === true;
110437
111057
  const projectName = options2.name ?? "default";
110438
- const configPath = join35(context4.cwd, CLI_CONFIG.configFile);
111058
+ const configPath = join37(context4.cwd, CLI_CONFIG.configFile);
110439
111059
  const result = { created: [], skipped: [] };
110440
111060
  const globalConfigDir = resolveGlobalConfigDir(context4);
110441
- const globalConfigPath = join35(globalConfigDir, GLOBAL_CONFIG_FILE2);
111061
+ const globalConfigPath = join37(globalConfigDir, GLOBAL_CONFIG_FILE2);
110442
111062
  const globalConfigExists = await context4.fs.exists(globalConfigPath);
110443
111063
  let misplacedKeys = [];
110444
111064
  if (globalConfigExists) {
@@ -110451,20 +111071,20 @@ function registerInitCommand(program2, context4, options = {}) {
110451
111071
  const configRoot = bundledConfigRoot();
110452
111072
  if (configRoot !== null && !minimal) {
110453
111073
  for (const relPath of listBundledProjectSeedFiles()) {
110454
- const sourcePath = join35(configRoot, relPath);
111074
+ const sourcePath = join37(configRoot, relPath);
110455
111075
  if (!await context4.fs.exists(sourcePath))
110456
111076
  continue;
110457
- const targetPath = join35(context4.cwd, CLI_CONFIG.configDir, relPath);
110458
- await context4.fs.ensureDir(join35(targetPath, ".."));
111077
+ const targetPath = join37(context4.cwd, CLI_CONFIG.configDir, relPath);
111078
+ await context4.fs.ensureDir(join37(targetPath, ".."));
110459
111079
  await writeIfNew(context4, targetPath, await context4.fs.readFile(sourcePath), false, result);
110460
111080
  }
110461
111081
  for (const entry of SCAFFOLD_MANIFEST) {
110462
- const sourcePath = join35(configRoot, entry.source);
111082
+ const sourcePath = join37(configRoot, entry.source);
110463
111083
  if (!await context4.fs.exists(sourcePath))
110464
111084
  continue;
110465
- const baseDir = entry.root === true ? context4.cwd : join35(context4.cwd, CLI_CONFIG.configDir);
110466
- const targetPath = join35(baseDir, entry.target);
110467
- await context4.fs.ensureDir(join35(targetPath, ".."));
111085
+ const baseDir = entry.root === true ? context4.cwd : join37(context4.cwd, CLI_CONFIG.configDir);
111086
+ const targetPath = join37(baseDir, entry.target);
111087
+ await context4.fs.ensureDir(join37(targetPath, ".."));
110468
111088
  const entryForce = false;
110469
111089
  let body = await context4.fs.readFile(sourcePath);
110470
111090
  if (entry.target === "AGENTS.md") {
@@ -110481,7 +111101,7 @@ function registerInitCommand(program2, context4, options = {}) {
110481
111101
  const backupPath = `${globalConfigPath}.bak-${new Date().toISOString().replace(/[:.]/g, "-")}`;
110482
111102
  await context4.fs.writeFile(backupPath, await context4.fs.readFile(globalConfigPath));
110483
111103
  const bundledRoot = bundledConfigRoot();
110484
- const examplePath = bundledRoot !== null ? join35(bundledRoot, GLOBAL_CONFIG_EXAMPLE) : null;
111104
+ const examplePath = bundledRoot !== null ? join37(bundledRoot, GLOBAL_CONFIG_EXAMPLE) : null;
110485
111105
  if (examplePath !== null && await context4.fs.exists(examplePath)) {
110486
111106
  await context4.fs.writeFile(globalConfigPath, await context4.fs.readFile(examplePath));
110487
111107
  adoptedGlobal = true;
@@ -110551,13 +111171,13 @@ function registerInitCommand(program2, context4, options = {}) {
110551
111171
  ].join(`
110552
111172
  `)}
110553
111173
  `;
110554
- await context4.fs.ensureDir(join35(context4.cwd, CLI_CONFIG.configDir));
111174
+ await context4.fs.ensureDir(join37(context4.cwd, CLI_CONFIG.configDir));
110555
111175
  await context4.fs.writeFile(configPath, configYaml);
110556
111176
  result.created.push(configPath);
110557
- const agentsDir = join35(context4.cwd, CLI_CONFIG.configDir, "agents");
111177
+ const agentsDir = join37(context4.cwd, CLI_CONFIG.configDir, "agents");
110558
111178
  await context4.fs.ensureDir(agentsDir);
110559
- await writeIfNew(context4, join35(agentsDir, ".gitkeep"), "", force, result);
110560
- const gitignorePath = join35(context4.cwd, ".gitignore");
111179
+ await writeIfNew(context4, join37(agentsDir, ".gitkeep"), "", force, result);
111180
+ const gitignorePath = join37(context4.cwd, ".gitignore");
110561
111181
  const contextEntry = ".spur/context/";
110562
111182
  if (await context4.fs.exists(gitignorePath)) {
110563
111183
  const existing = await context4.fs.readFile(gitignorePath);
@@ -110577,20 +111197,20 @@ ${contextEntry}
110577
111197
  const configRoot = bundledConfigRoot();
110578
111198
  if (configRoot !== null) {
110579
111199
  for (const relPath of listBundledProjectSeedFiles()) {
110580
- const sourcePath = join35(configRoot, relPath);
111200
+ const sourcePath = join37(configRoot, relPath);
110581
111201
  if (!await context4.fs.exists(sourcePath))
110582
111202
  continue;
110583
- const targetPath = join35(context4.cwd, CLI_CONFIG.configDir, relPath);
110584
- await context4.fs.ensureDir(join35(targetPath, ".."));
111203
+ const targetPath = join37(context4.cwd, CLI_CONFIG.configDir, relPath);
111204
+ await context4.fs.ensureDir(join37(targetPath, ".."));
110585
111205
  await writeIfNew(context4, targetPath, await context4.fs.readFile(sourcePath), force, result);
110586
111206
  }
110587
111207
  for (const entry of SCAFFOLD_MANIFEST) {
110588
- const sourcePath = join35(configRoot, entry.source);
111208
+ const sourcePath = join37(configRoot, entry.source);
110589
111209
  if (!await context4.fs.exists(sourcePath))
110590
111210
  continue;
110591
- const baseDir = entry.root === true ? context4.cwd : join35(context4.cwd, CLI_CONFIG.configDir);
110592
- const targetPath = join35(baseDir, entry.target);
110593
- await context4.fs.ensureDir(join35(targetPath, ".."));
111211
+ const baseDir = entry.root === true ? context4.cwd : join37(context4.cwd, CLI_CONFIG.configDir);
111212
+ const targetPath = join37(baseDir, entry.target);
111213
+ await context4.fs.ensureDir(join37(targetPath, ".."));
110594
111214
  const entryForce = entry.preserve === true ? false : force;
110595
111215
  let body = await context4.fs.readFile(sourcePath);
110596
111216
  if (entry.target === "AGENTS.md") {
@@ -110603,7 +111223,7 @@ ${contextEntry}
110603
111223
  }
110604
111224
  }
110605
111225
  }
110606
- const agentsMdPath = join35(context4.cwd, "AGENTS.md");
111226
+ const agentsMdPath = join37(context4.cwd, "AGENTS.md");
110607
111227
  if (await context4.fs.exists(agentsMdPath)) {
110608
111228
  const existing = await context4.fs.readFile(agentsMdPath);
110609
111229
  if (!existing.includes(INDEXED_CONTEXT_MARKER)) {
@@ -110644,11 +111264,11 @@ ${contextEntry}
110644
111264
  // src/commands/maintain.ts
110645
111265
  init_src();
110646
111266
  init_src2();
110647
- import { join as join36 } from "path";
111267
+ import { join as join38 } from "path";
110648
111268
  function registerMaintainCommand(program2, context4, options = {}) {
110649
111269
  program2.command("maintain", { hidden: options.hidden === true }).summary("run database maintenance (optimize, WAL checkpoint truncation, optional VACUUM)").description("Run database maintenance: PRAGMA optimize, WAL checkpoint truncation; optionally VACUUM compaction.").option("--vacuum", "Run VACUUM defragmentation and page compaction", false).option(...SHARED_OPTIONS.jsonSupported).option(...SHARED_OPTIONS.jsonEnvelope).action(async (options2) => {
110650
111270
  const db2 = await context4.getDb();
110651
- const dbPath = context4.env.DATABASE_URL ?? join36(context4.cwd, DEFAULT_DATABASE_URL);
111271
+ const dbPath = context4.env.DATABASE_URL ?? join38(context4.cwd, DEFAULT_DATABASE_URL);
110652
111272
  const result = await maintainDatabase(db2, {
110653
111273
  vacuum: options2.vacuum === true,
110654
111274
  dbPath: dbPath.startsWith(":memory:") ? undefined : dbPath
@@ -110971,15 +111591,15 @@ async function runMessageWatch(svc, output2, options, runtime = {}) {
110971
111591
  }
110972
111592
  }
110973
111593
  function defaultSleep(ms, signal) {
110974
- return new Promise((resolve18) => {
111594
+ return new Promise((resolve19) => {
110975
111595
  if (signal?.aborted) {
110976
- resolve18();
111596
+ resolve19();
110977
111597
  return;
110978
111598
  }
110979
- const timer = setTimeout(resolve18, ms);
111599
+ const timer = setTimeout(resolve19, ms);
110980
111600
  signal?.addEventListener("abort", () => {
110981
111601
  clearTimeout(timer);
110982
- resolve18();
111602
+ resolve19();
110983
111603
  }, { once: true });
110984
111604
  });
110985
111605
  }
@@ -111006,15 +111626,15 @@ function parseTimeout2(raw) {
111006
111626
  return n3;
111007
111627
  }
111008
111628
  function sendSleep(ms, signal) {
111009
- return new Promise((resolve18) => {
111629
+ return new Promise((resolve19) => {
111010
111630
  if (signal?.aborted) {
111011
- resolve18();
111631
+ resolve19();
111012
111632
  return;
111013
111633
  }
111014
- const timer = setTimeout(resolve18, ms);
111634
+ const timer = setTimeout(resolve19, ms);
111015
111635
  signal?.addEventListener("abort", () => {
111016
111636
  clearTimeout(timer);
111017
- resolve18();
111637
+ resolve19();
111018
111638
  }, { once: true });
111019
111639
  });
111020
111640
  }
@@ -111072,10 +111692,10 @@ async function waitForPendingDrain(agentService, teamService, pin, timeoutMs, si
111072
111692
 
111073
111693
  // src/commands/migrate.ts
111074
111694
  init_src2();
111075
- import { join as join37 } from "path";
111695
+ import { join as join39 } from "path";
111076
111696
  function registerMigrateCommand(program2, context4, options = {}) {
111077
111697
  program2.command("migrate", { hidden: options.hidden === true }).summary("apply CLI-owned schema migrations").option(...SHARED_OPTIONS.json).option(...SHARED_OPTIONS.jsonEnvelope).action(async (options2) => {
111078
- const migrations = await loadSqlMigrations(join37(context4.cwd, "drizzle")).catch(() => {
111698
+ const migrations = await loadSqlMigrations(join39(context4.cwd, "drizzle")).catch(() => {
111079
111699
  return;
111080
111700
  });
111081
111701
  const applied = await applyCliMigrations(await context4.getDb(), migrations);
@@ -111088,16 +111708,16 @@ function registerMigrateCommand(program2, context4, options = {}) {
111088
111708
  init_src2();
111089
111709
  init_dist5();
111090
111710
  await init_src3();
111091
- import { basename as basename12, dirname as dirname24, join as join38, resolve as resolve18 } from "path";
111711
+ import { basename as basename13, dirname as dirname25, join as join40, resolve as resolve19 } from "path";
111092
111712
  function registerProjectsCommand(program2, context4) {
111093
111713
  const projectsCmd = program2.command("projects").summary("manage the Spur multi-project registry");
111094
111714
  projectsCmd.command("add").argument("<path>", "Project root directory path").option(...SHARED_OPTIONS.nameProjectDisplay).option(...SHARED_OPTIONS.jsonProjectsResponse).option(...SHARED_OPTIONS.jsonEnvelope).action(async (pathArg, options) => {
111095
111715
  try {
111096
- const absolutePath = resolve18(context4.cwd, pathArg);
111716
+ const absolutePath = resolve19(context4.cwd, pathArg);
111097
111717
  if (!await context4.fs.exists(absolutePath)) {
111098
111718
  throw new Error(`Directory does not exist: ${absolutePath}`);
111099
111719
  }
111100
- const name = options.name ?? basename12(absolutePath);
111720
+ const name = options.name ?? basename13(absolutePath);
111101
111721
  const registry3 = new ProjectRegistry;
111102
111722
  const entry = await registry3.upsert({ path: absolutePath, name, port: 0 });
111103
111723
  if (options.json) {
@@ -111146,8 +111766,8 @@ function registerProjectsCommand(program2, context4) {
111146
111766
  running: p.port > 0 ? await isPortLive(p.port) : false
111147
111767
  })));
111148
111768
  const openProjectDb = async (projectPath) => {
111149
- const url2 = join38(projectPath, ".spur", "spur.db");
111150
- await context4.fs.ensureDir(dirname24(url2));
111769
+ const url2 = join40(projectPath, ".spur", "spur.db");
111770
+ await context4.fs.ensureDir(dirname25(url2));
111151
111771
  return createMigratedDb({ url: url2 });
111152
111772
  };
111153
111773
  const fleets = options.fleet ? await Promise.all(projects3.map(async (p) => {
@@ -111589,7 +112209,7 @@ function formatTraceDetail(detail) {
111589
112209
  // src/commands/serve.ts
111590
112210
  init_src();
111591
112211
  import { statSync as statSync7 } from "fs";
111592
- import { join as join42, resolve as resolve19 } from "path";
112212
+ import { join as join44, resolve as resolve20 } from "path";
111593
112213
 
111594
112214
  // ../server/src/index.ts
111595
112215
  init_src();
@@ -111599,11 +112219,11 @@ init_src();
111599
112219
  init_loader();
111600
112220
  init_src2();
111601
112221
  await init_src3();
111602
- import { basename as basename14, dirname as dirname26, isAbsolute as isAbsolute8, join as join41 } from "path";
112222
+ import { basename as basename15, dirname as dirname27, isAbsolute as isAbsolute8, join as join43 } from "path";
111603
112223
  init_dist5();
111604
112224
 
111605
112225
  // ../server/src/bootstrap.ts
111606
- import { join as join39 } from "path";
112226
+ import { join as join41 } from "path";
111607
112227
 
111608
112228
  // ../../node_modules/.bun/radash@12.1.1/node_modules/radash/dist/esm/async.mjs
111609
112229
  var guard = (func, shouldGuard) => {
@@ -111876,8 +112496,8 @@ class EventPublisher {
111876
112496
  if (bufferedEvents.length > 0) {
111877
112497
  return { done: false, value: bufferedEvents.shift() };
111878
112498
  }
111879
- return new Promise((resolve19, reject) => {
111880
- pullResolvers.push([resolve19, reject]);
112499
+ return new Promise((resolve20, reject) => {
112500
+ pullResolvers.push([resolve20, reject]);
111881
112501
  });
111882
112502
  }, async () => {
111883
112503
  unsubscribe();
@@ -116997,6 +117617,55 @@ function mountMiddleware(app, appRt) {
116997
117617
  }
116998
117618
  }
116999
117619
 
117620
+ // ../server/src/modules/commands/index.ts
117621
+ init_src();
117622
+ await init_src3();
117623
+ var commandsModule = {
117624
+ name: "commands",
117625
+ mount(app, ctx) {
117626
+ const projectRoot = ctx?.cwd ?? process.cwd();
117627
+ const filePath = getSlashCommandsFilePath();
117628
+ const service = new SlashCommandsService({ filePath, projectRoot });
117629
+ let cachedCommands = [];
117630
+ try {
117631
+ cachedCommands = service.getCommands();
117632
+ } catch {}
117633
+ const handleList = (c3) => {
117634
+ const reload = c3.req.query("reload") === "true";
117635
+ if (reload || cachedCommands.length === 0) {
117636
+ try {
117637
+ cachedCommands = service.getCommands(reload);
117638
+ } catch {}
117639
+ }
117640
+ return c3.json({
117641
+ commands: cachedCommands,
117642
+ count: cachedCommands.length,
117643
+ filePath: service.getFilePath()
117644
+ });
117645
+ };
117646
+ app.get("/api/commands", handleList);
117647
+ app.get("/api/agent/commands", handleList);
117648
+ app.post("/api/commands/generate", (c3) => {
117649
+ try {
117650
+ const result = service.generate();
117651
+ cachedCommands = result.commands;
117652
+ return c3.json({
117653
+ ok: true,
117654
+ count: result.count,
117655
+ filePath: result.path,
117656
+ commands: result.commands
117657
+ });
117658
+ } catch (err) {
117659
+ return c3.json({
117660
+ ok: false,
117661
+ error: err instanceof Error ? err.message : String(err),
117662
+ filePath: service.getFilePath()
117663
+ }, 500);
117664
+ }
117665
+ });
117666
+ }
117667
+ };
117668
+
117000
117669
  // ../server/src/modules/sse/stream-helpers.ts
117001
117670
  function sendSseKeepalive(closed, controller, encoder) {
117002
117671
  if (closed.current)
@@ -118305,7 +118974,7 @@ var featureModule = {
118305
118974
  // ../server/src/modules/health/index.ts
118306
118975
  init_src2();
118307
118976
  await init_src3();
118308
- import { basename as basename13 } from "path";
118977
+ import { basename as basename14 } from "path";
118309
118978
  var startedAt = Date.now();
118310
118979
  var OPERATOR_AGENT_ID = "board-operator";
118311
118980
  var healthModule = {
@@ -118332,7 +119001,7 @@ var healthModule = {
118332
119001
  return c3.json({ status: "error", db: "unreachable" }, 503);
118333
119002
  });
118334
119003
  app.get("/api/project", (c3) => {
118335
- return c3.json({ name: ctx ? basename13(ctx.cwd) : null, path: ctx ? normalizeProjectPath(ctx.cwd) : null });
119004
+ return c3.json({ name: ctx ? basename14(ctx.cwd) : null, path: ctx ? normalizeProjectPath(ctx.cwd) : null });
118336
119005
  });
118337
119006
  app.get("/api/project/fleet", async (c3) => {
118338
119007
  if (!ctx) {
@@ -119412,7 +120081,8 @@ var builtins = [
119412
120081
  processesModule,
119413
120082
  observabilityModule,
119414
120083
  runsModule,
119415
- historyModule
120084
+ historyModule,
120085
+ commandsModule
119416
120086
  ];
119417
120087
  function registerModules(app, ctx, modules = builtins) {
119418
120088
  for (const mod of modules) {
@@ -121430,7 +122100,7 @@ function createApp(appRt, opts) {
121430
122100
  app.use("*", async (c3, next) => {
121431
122101
  const pathname = c3.req.path === "/" ? "/index.html" : c3.req.path;
121432
122102
  try {
121433
- const file2 = Bun.file(join39(webDistPath, pathname));
122103
+ const file2 = Bun.file(join41(webDistPath, pathname));
121434
122104
  if (await file2.exists()) {
121435
122105
  const headers = new Headers({ "content-type": file2.type });
121436
122106
  return new Response(file2.stream(), { headers });
@@ -121443,7 +122113,7 @@ function createApp(appRt, opts) {
121443
122113
  return c3.json({ error: "Not Found" }, 404);
121444
122114
  }
121445
122115
  try {
121446
- const indexFile = Bun.file(join39(webDistPath, "index.html"));
122116
+ const indexFile = Bun.file(join41(webDistPath, "index.html"));
121447
122117
  if (await indexFile.exists()) {
121448
122118
  return new Response(indexFile.stream(), {
121449
122119
  headers: { "content-type": "text/html; charset=utf-8" }
@@ -121465,7 +122135,7 @@ init_loader();
121465
122135
  init_src2();
121466
122136
  init_dist5();
121467
122137
  await init_src3();
121468
- import { dirname as dirname25, join as join40 } from "path";
122138
+ import { dirname as dirname26, join as join42 } from "path";
121469
122139
  var NOOP_OUTPUT = { write: (_s) => {}, error: (_s) => {} };
121470
122140
 
121471
122141
  class LazyPlanningEventEmitter {
@@ -121501,7 +122171,7 @@ class NotConfiguredError extends Error {
121501
122171
  function createServerContext(appRt, options) {
121502
122172
  const cwd = options.cwd;
121503
122173
  const fs3 = options.fs;
121504
- const dbUrl = options.dbUrl ?? join40(cwd, DEFAULT_DATABASE_URL);
122174
+ const dbUrl = options.dbUrl ?? join42(cwd, DEFAULT_DATABASE_URL);
121505
122175
  const eventsBus = options.eventsBus ?? appRt.events;
121506
122176
  const jobQueueEnabled = options.jobQueueEnabled ?? false;
121507
122177
  const eventProjectContext = systemEventProjectContext(cwd);
@@ -121538,7 +122208,7 @@ function createServerContext(appRt, options) {
121538
122208
  if (!dbPromise) {
121539
122209
  const created = (async () => {
121540
122210
  if (dbUrl !== IN_MEMORY_DATABASE_URL) {
121541
- await fs3.ensureDir(dirname25(dbUrl));
122211
+ await fs3.ensureDir(dirname26(dbUrl));
121542
122212
  }
121543
122213
  return createMigratedDbViaRuntime({ url: dbUrl });
121544
122214
  })();
@@ -121806,7 +122476,7 @@ async function loadServerSectionMatrix(projectRoot) {
121806
122476
  }
121807
122477
  const root = bundledConfigRoot();
121808
122478
  if (root !== null) {
121809
- const matrixPath = join41(root, "tasks", "section-matrix.yaml");
122479
+ const matrixPath = join43(root, "tasks", "section-matrix.yaml");
121810
122480
  if (await nodeFs.exists(matrixPath)) {
121811
122481
  return await loadStructuredSpurConfig(matrixPath, {
121812
122482
  validateJsonSchema: false
@@ -121815,7 +122485,7 @@ async function loadServerSectionMatrix(projectRoot) {
121815
122485
  }
121816
122486
  throw new Error(`no canonical section-matrix found for task creation (F92 R1); tried:
121817
122487
  ` + ` - ${localPath}
121818
- ` + (root !== null ? ` - ${join41(root, "tasks", "section-matrix.yaml")}
122488
+ ` + (root !== null ? ` - ${join43(root, "tasks", "section-matrix.yaml")}
121819
122489
  ` : "") + "copy/generate section-matrix.yaml from the canonical build-time matrix asset (repo `config` `tasks` tree) into one of those paths");
121820
122490
  }
121821
122491
  var SYSTEM_EVENTS_PRUNE_JOB = "system-events-prune";
@@ -121880,7 +122550,7 @@ function registerSchedulerEntries(scheduler3, ctx, jobs = [], options = {}) {
121880
122550
  });
121881
122551
  for (const job of jobs) {
121882
122552
  const schedule = job.cron ?? String(job.intervalMinutes * 60000);
121883
- const jobPolicy = resolveSchedulerJobTimeoutMs(job.name, env, timeoutMs);
122553
+ const jobPolicy = resolveSchedulerJobTimeoutMs(job.name, env, timeoutMs, job.timeoutMs);
121884
122554
  const sweepThresholdMs = jobPolicy === null ? null : jobPolicy + resolveKillGraceMs3(env);
121885
122555
  register(schedule, `${SCHEDULER_CUSTOM_JOB}:${job.name}`, async () => {
121886
122556
  const queue = await ctx.jobQueue();
@@ -122050,15 +122720,15 @@ async function handleFeatureActionJob(ctx, env, payload) {
122050
122720
  await runFeatureActionJob(ctx, env, payload);
122051
122721
  }
122052
122722
  async function resolveWebDistPath(configuredPath, projectRoot = process.cwd()) {
122053
- const candidates = configuredPath && configuredPath.trim() !== "" ? [isAbsolute8(configuredPath) ? configuredPath : join41(projectRoot, configuredPath)] : [
122054
- join41(projectRoot, "dist/web"),
122055
- join41(import.meta.dir, "web"),
122056
- join41(dirname26(process.execPath), "web"),
122057
- join41(dirname26(process.execPath), "../web"),
122058
- join41(import.meta.dir, "../../../dist/web")
122723
+ const candidates = configuredPath && configuredPath.trim() !== "" ? [isAbsolute8(configuredPath) ? configuredPath : join43(projectRoot, configuredPath)] : [
122724
+ join43(projectRoot, "dist/web"),
122725
+ join43(import.meta.dir, "web"),
122726
+ join43(dirname27(process.execPath), "web"),
122727
+ join43(dirname27(process.execPath), "../web"),
122728
+ join43(import.meta.dir, "../../../dist/web")
122059
122729
  ];
122060
122730
  for (const candidate of candidates) {
122061
- if (await Bun.file(join41(candidate, "index.html")).exists()) {
122731
+ if (await Bun.file(join43(candidate, "index.html")).exists()) {
122062
122732
  return candidate;
122063
122733
  }
122064
122734
  }
@@ -122075,7 +122745,7 @@ async function startServer(options, deps = defaultDeps) {
122075
122745
  async start(appRt) {
122076
122746
  const fs3 = deps.createNodeFileSystem(projectRoot);
122077
122747
  if (options.dbUrl && options.dbUrl !== IN_MEMORY_DATABASE_URL) {
122078
- await fs3.ensureDir(dirname26(options.dbUrl));
122748
+ await fs3.ensureDir(dirname27(options.dbUrl));
122079
122749
  }
122080
122750
  const scheduler3 = appRt.config.scheduler.enabled ? appRt.scheduler : undefined;
122081
122751
  const webDistPath = await resolveWebDistPath(options.webDistPath, projectRoot);
@@ -122211,10 +122881,11 @@ async function startServer(options, deps = defaultDeps) {
122211
122881
  });
122212
122882
  registry3.register(SCHEDULER_CUSTOM_JOB, async (job) => {
122213
122883
  await emitQueueJobStarted(ctx, job);
122884
+ const declaredTimeouts = new Map(appRt.config.scheduler.jobs.map((entry) => [entry.name, entry.timeoutMs]));
122214
122885
  return handleSchedulerCustomJob({
122215
122886
  cwd: ctx.cwd,
122216
122887
  executor: childExecutor,
122217
- resolveTimeoutMs: (name) => resolveSchedulerJobTimeoutMs(name, env, schedulerCustomTimeoutMs),
122888
+ resolveTimeoutMs: (name) => resolveSchedulerJobTimeoutMs(name, env, schedulerCustomTimeoutMs, declaredTimeouts.get(name)),
122218
122889
  onDuplicate: (name) => {
122219
122890
  ctx.eventBus().emit("scheduler.job.executed", {
122220
122891
  name: `${SCHEDULER_CUSTOM_JOB}:${name}`,
@@ -122223,10 +122894,20 @@ async function startServer(options, deps = defaultDeps) {
122223
122894
  skipped: true,
122224
122895
  reason: `duplicate execution suppressed; ${name} is already running in this process`
122225
122896
  });
122226
- }
122897
+ },
122898
+ onShutdownAbandon: (name) => {
122899
+ ctx.eventBus().emit("scheduler.job.executed", {
122900
+ name: `${SCHEDULER_CUSTOM_JOB}:${name}`,
122901
+ durationMs: 0,
122902
+ severity: "info",
122903
+ skipped: true,
122904
+ reason: `abandoned by server shutdown; the next tick resumes ${name}`
122905
+ });
122906
+ },
122907
+ isShuttingDown: () => shuttingDown
122227
122908
  }, job);
122228
122909
  });
122229
- const unlimitedJobNames = appRt.config.scheduler.jobs.filter((job) => resolveSchedulerJobTimeoutMs(job.name, env, schedulerCustomTimeoutMs) === null).map((job) => job.name);
122910
+ const unlimitedJobNames = appRt.config.scheduler.jobs.filter((job) => resolveSchedulerJobTimeoutMs(job.name, env, schedulerCustomTimeoutMs, job.timeoutMs) === null).map((job) => job.name);
122230
122911
  const orphanCount = await failOrphanedProcessingJobs(await ctx.getDb(), Date.now(), unlimitedJobNames);
122231
122912
  if (orphanCount > 0) {
122232
122913
  appRt.logger.warn("Swept orphaned processing jobs at startup", {
@@ -122254,7 +122935,7 @@ async function startServer(options, deps = defaultDeps) {
122254
122935
  });
122255
122936
  const projectRegistry = new ProjectRegistry;
122256
122937
  const projectCwd = projectRoot;
122257
- const projectName = basename14(projectCwd);
122938
+ const projectName = basename15(projectCwd);
122258
122939
  try {
122259
122940
  const existingProject = await projectRegistry.getByPath(projectCwd);
122260
122941
  await projectRegistry.upsert({
@@ -122374,10 +123055,10 @@ var SQLITE_BUSY_MESSAGE_CONSTANTS = Object.freeze({
122374
123055
 
122375
123056
  // src/commands/serve.ts
122376
123057
  function resolveServeDbUrl(cwd, env, configuredUrl) {
122377
- return env.DATABASE_URL === undefined ? join42(cwd, DEFAULT_DATABASE_URL) : configuredUrl;
123058
+ return env.DATABASE_URL === undefined ? join44(cwd, DEFAULT_DATABASE_URL) : configuredUrl;
122378
123059
  }
122379
123060
  function resolveServeCwd(cwd) {
122380
- const resolved = resolve19(cwd);
123061
+ const resolved = resolve20(cwd);
122381
123062
  let isDirectory = false;
122382
123063
  try {
122383
123064
  isDirectory = statSync7(resolved).isDirectory();
@@ -122430,7 +123111,7 @@ function registerServeCommand(program2, context4, options = {}) {
122430
123111
  }
122431
123112
 
122432
123113
  // src/commands/status.ts
122433
- import { join as join43 } from "path";
123114
+ import { join as join45 } from "path";
122434
123115
 
122435
123116
  // src/git-context.ts
122436
123117
  init_dist5();
@@ -122478,8 +123159,8 @@ function registerStatusCommand(program2, context4, options = {}) {
122478
123159
  }
122479
123160
  async function runStatusCore(path9, options, context4) {
122480
123161
  const [packageJsonExists, spurConfigExists, git3, agentSpecs] = await Promise.all([
122481
- context4.fs.exists(join43(context4.cwd, "package.json")),
122482
- context4.fs.exists(join43(context4.cwd, ".spur", "config.yaml")),
123162
+ context4.fs.exists(join45(context4.cwd, "package.json")),
123163
+ context4.fs.exists(join45(context4.cwd, ".spur", "config.yaml")),
122483
123164
  gitContext(context4.cwd),
122484
123165
  listAgentSpecIds(context4)
122485
123166
  ]);
@@ -122507,14 +123188,14 @@ async function runStatusCore(path9, options, context4) {
122507
123188
  return status.ok ? 0 : 1;
122508
123189
  }
122509
123190
  async function listAgentSpecIds(context4) {
122510
- const dir = join43(context4.cwd, ".spur", "agents");
123191
+ const dir = join45(context4.cwd, ".spur", "agents");
122511
123192
  if (!await context4.fs.exists(dir))
122512
123193
  return [];
122513
123194
  const entries = await context4.fs.readDir(dir);
122514
123195
  return entries.filter((entry) => entry.endsWith(".yaml") || entry.endsWith(".yml")).map((entry) => entry.replace(/\.ya?ml$/, "")).sort();
122515
123196
  }
122516
123197
  async function readTargetStatus(context4, targetPath) {
122517
- const resolved = join43(context4.cwd, targetPath);
123198
+ const resolved = join45(context4.cwd, targetPath);
122518
123199
  const stat2 = await context4.fs.stat(resolved);
122519
123200
  if (stat2 === null)
122520
123201
  throw new CommandError(`status failed: path does not exist at ${resolved}`);
@@ -122527,7 +123208,7 @@ init_dist7();
122527
123208
  init_dist5();
122528
123209
  await init_src3();
122529
123210
  import { closeSync as closeSync5, fstatSync as fstatSync3, openSync as openSync5, readSync as readSync3 } from "fs";
122530
- import { join as join44 } from "path";
123211
+ import { join as join46 } from "path";
122531
123212
  import { createInterface } from "readline";
122532
123213
  import { setTimeout as sleep5 } from "timers/promises";
122533
123214
 
@@ -122706,9 +123387,9 @@ async function writeWorkflowPlanArtifact(cwd, runId, resolved, secretValues) {
122706
123387
  }))
122707
123388
  });
122708
123389
  const { mkdir, writeFile: writeFile2 } = await import("fs/promises");
122709
- const artifactsDir = join44(cwd, ".spur", "run");
123390
+ const artifactsDir = join46(cwd, ".spur", "run");
122710
123391
  await mkdir(artifactsDir, { recursive: true });
122711
- const artifactPath = join44(artifactsDir, `${runId}-workflow-plan.json`);
123392
+ const artifactPath = join46(artifactsDir, `${runId}-workflow-plan.json`);
122712
123393
  await writeFile2(artifactPath, payload);
122713
123394
  return artifactPath;
122714
123395
  }
@@ -122993,7 +123674,7 @@ Monitor with: spur workflow trace ${runId2} --follow`);
122993
123674
  }
122994
123675
  const runLog = options.log === false ? undefined : new WorkflowRunLogSink({
122995
123676
  bus,
122996
- dir: join44(context4.cwd, ".spur", "run"),
123677
+ dir: join46(context4.cwd, ".spur", "run"),
122997
123678
  runId,
122998
123679
  ...planPreview !== undefined ? { planPreview } : {},
122999
123680
  ...resolveOutputLogConfig(context4.spurConfig ?? null)
@@ -123531,7 +124212,7 @@ function readRunLogChunk(logPath, offset) {
123531
124212
  }
123532
124213
  }
123533
124214
  async function followRunLog(service, runId, dir, pollMs, write, wait = (ms) => sleep5(ms)) {
123534
- const logPath = join44(dir, ".spur", "run", `${runId}.log`);
124215
+ const logPath = join46(dir, ".spur", "run", `${runId}.log`);
123535
124216
  let offset = 0;
123536
124217
  let everRead = false;
123537
124218
  while (true) {
@@ -123594,7 +124275,7 @@ init_src();
123594
124275
  init_src2();
123595
124276
  init_dist5();
123596
124277
  await init_src3();
123597
- import { dirname as dirname27, join as join45, resolve as resolve20 } from "path";
124278
+ import { dirname as dirname28, join as join47, resolve as resolve21 } from "path";
123598
124279
  import { isatty as isatty4 } from "tty";
123599
124280
 
123600
124281
  // ../../node_modules/.bun/@clack+core@1.4.1/node_modules/@clack/core/dist/index.mjs
@@ -124978,7 +125659,7 @@ class DefaultHitlResponder {
124978
125659
  // src/context.ts
124979
125660
  await init_src3();
124980
125661
  function createCliContext(options) {
124981
- const cwd = resolve20(options.cwd ?? process.cwd());
125662
+ const cwd = resolve21(options.cwd ?? process.cwd());
124982
125663
  const env = options.env ?? process.env;
124983
125664
  const fs3 = createNodeFileSystem3(cwd);
124984
125665
  const agentConfig = options.agentConfig ?? options.spurConfig?.agent;
@@ -125031,10 +125712,10 @@ function createCliContext(options) {
125031
125712
  function noopSetExitCode(_code) {}
125032
125713
  async function createMigratedDbAdapter(cwd = process.cwd(), env = process.env, dbUrl) {
125033
125714
  const config4 = buildConfigFromEnv(env);
125034
- const configuredUrl = env.DATABASE_URL === undefined ? join45(cwd, DEFAULT_DATABASE_URL) : config4.database.url;
125715
+ const configuredUrl = env.DATABASE_URL === undefined ? join47(cwd, DEFAULT_DATABASE_URL) : config4.database.url;
125035
125716
  const url2 = dbUrl ?? configuredUrl;
125036
125717
  if (url2 !== IN_MEMORY_DATABASE_URL) {
125037
- await createNodeFileSystem3().ensureDir(dirname27(url2));
125718
+ await createNodeFileSystem3().ensureDir(dirname28(url2));
125038
125719
  }
125039
125720
  return createMigratedDb({ url: url2 });
125040
125721
  }