@gobing-ai/spur 0.3.60 → 0.3.61

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 (92) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.example.yaml +2 -2
  3. package/config/config.global.yaml +11 -7
  4. package/config/corpus-baseline.json +96 -8
  5. package/config/workflow-composition-baseline.json +1 -1
  6. package/config/workflows/wayfinder-resolution.yaml +4 -6
  7. package/package.json +1 -1
  8. package/plugins/README.md +5 -4
  9. package/plugins/sp/README.md +3 -3
  10. package/plugins/sp/agents/super-planner.md +1 -1
  11. package/plugins/sp/commands/dev-pr-review.md +1 -1
  12. package/plugins/sp/commands/dev-wrap.md +2 -2
  13. package/plugins/sp/commands/dev-wrapall.md +2 -2
  14. package/plugins/sp/plugin.json +1 -1
  15. package/plugins/sp/references/roles.md +12 -9
  16. package/plugins/sp/scripts/surface-drift-inventory.ts +51 -17
  17. package/plugins/sp/scripts/validate-commands.ts +11 -4
  18. package/plugins/sp/skills/code-verification/SKILL.md +1 -1
  19. package/plugins/sp/skills/code-verification/references/verdict-schema.md +1 -1
  20. package/plugins/sp/skills/issue-finding/SKILL.md +1 -1
  21. package/plugins/sp/skills/next-router/references/routing-table.md +1 -1
  22. package/plugins/sp/skills/pr-reviewing/SKILL.md +2 -2
  23. package/plugins/sp/skills/spec-decomposition/references/decomposition.md +1 -1
  24. package/plugins/sp/skills/spur-cli/references/init.md +1 -1
  25. package/plugins/sp/skills/spur-cli/references/tasks/l3-guard-cheatsheet.md +1 -1
  26. package/plugins/sp/skills/spur-cli/references/workflows/operations.md +1 -1
  27. package/plugins/sp/skills/spur-cli/references/workflows.md +4 -4
  28. package/plugins/sp/skills/spur-dev/SKILL.md +4 -4
  29. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +4 -4
  30. package/plugins/sp/skills/spur-dev/references/dev-operations.md +9 -9
  31. package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +1 -1
  32. package/plugins/sp/skills/spur-dev/references/execution-batch.md +3 -3
  33. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +1 -1
  34. package/plugins/sp/skills/spur-dev/references/feature-link-helper.md +3 -2
  35. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +1 -1
  36. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +5 -2
  37. package/plugins/sp/skills/spur-dev/references/planning-workflow.md +2 -2
  38. package/schemas/spur-config.schema.json +1 -1
  39. package/spur.js +203 -83
  40. package/web/_astro/BoardApp.BtRfVADq.js +1 -0
  41. package/web/_astro/{BoardApp.BJs3l7O8.js → BoardApp.CBIzcvqi.js} +100 -101
  42. package/web/_astro/{TaskDetail.B0l1uzsB.js → TaskDetail.D2N60cfE.js} +1 -1
  43. package/web/_astro/{arc.DPruHC3P.js → arc.D-EfJJwf.js} +1 -1
  44. package/web/_astro/{architectureDiagram-3BPJPVTR.CuOqAck6.js → architectureDiagram-3BPJPVTR.CgvTTzqp.js} +1 -1
  45. package/web/_astro/{blockDiagram-GPEHLZMM.Cxb7HDIA.js → blockDiagram-GPEHLZMM.CnOohvvn.js} +1 -1
  46. package/web/_astro/{c4Diagram-AAUBKEIU.Dw7wa-0S.js → c4Diagram-AAUBKEIU.BFcBGUy_.js} +1 -1
  47. package/web/_astro/channel.kIu33Gui.js +1 -0
  48. package/web/_astro/{chunk-2J33WTMH.MECQitzo.js → chunk-2J33WTMH.C-6MR-XY.js} +1 -1
  49. package/web/_astro/{chunk-4BX2VUAB.p3ofhIGE.js → chunk-4BX2VUAB.BN4LP5AR.js} +1 -1
  50. package/web/_astro/{chunk-55IACEB6.BmQGsPZP.js → chunk-55IACEB6.tUJ_CTtZ.js} +1 -1
  51. package/web/_astro/{chunk-727SXJPM.CwrK23OA.js → chunk-727SXJPM.BNCk-sxv.js} +1 -1
  52. package/web/_astro/{chunk-AQP2D5EJ.CUKI_QXp.js → chunk-AQP2D5EJ.BkpFIbNa.js} +1 -1
  53. package/web/_astro/{chunk-FMBD7UC4.7AtDgShM.js → chunk-FMBD7UC4.DcSZ87PN.js} +1 -1
  54. package/web/_astro/{chunk-ND2GUHAM.f_YJbTs3.js → chunk-ND2GUHAM.BkLvHFFf.js} +1 -1
  55. package/web/_astro/{chunk-QZHKN3VN.0q7V3gEg.js → chunk-QZHKN3VN.HOdgszax.js} +1 -1
  56. package/web/_astro/{classDiagram-4FO5ZUOK.C_hG3HMK.js → classDiagram-4FO5ZUOK.CWfRiY6b.js} +1 -1
  57. package/web/_astro/{classDiagram-v2-Q7XG4LA2.C_hG3HMK.js → classDiagram-v2-Q7XG4LA2.CWfRiY6b.js} +1 -1
  58. package/web/_astro/{cose-bilkent-S5V4N54A.vsaaaEMy.js → cose-bilkent-S5V4N54A.C6j4PxoN.js} +1 -1
  59. package/web/_astro/{dagre-BM42HDAG.BN7XFqQ4.js → dagre-BM42HDAG.BATZG1II.js} +1 -1
  60. package/web/_astro/{diagram-2AECGRRQ.ED6AfryZ.js → diagram-2AECGRRQ.B0f5yY6x.js} +1 -1
  61. package/web/_astro/{diagram-5GNKFQAL.Dxllzg8R.js → diagram-5GNKFQAL.Bewupqv0.js} +1 -1
  62. package/web/_astro/{diagram-KO2AKTUF.RuWppieD.js → diagram-KO2AKTUF.C23r1tB2.js} +1 -1
  63. package/web/_astro/{diagram-LMA3HP47.CL32Xn6s.js → diagram-LMA3HP47.DUErdBOR.js} +1 -1
  64. package/web/_astro/{diagram-OG6HWLK6.CUneEhqB.js → diagram-OG6HWLK6.BubmwAjb.js} +1 -1
  65. package/web/_astro/{erDiagram-TEJ5UH35.DI-92Lec.js → erDiagram-TEJ5UH35.BE_Qs5mv.js} +1 -1
  66. package/web/_astro/{flowDiagram-I6XJVG4X.69Aqa1FF.js → flowDiagram-I6XJVG4X.COWdbzon.js} +1 -1
  67. package/web/_astro/{ganttDiagram-6RSMTGT7.DdpClZpa.js → ganttDiagram-6RSMTGT7.5EGYC4MK.js} +1 -1
  68. package/web/_astro/{gitGraphDiagram-PVQCEYII.xqWfkvB4.js → gitGraphDiagram-PVQCEYII.bUUV7iEw.js} +1 -1
  69. package/web/_astro/index.Cestp9nh.css +1 -0
  70. package/web/_astro/{infoDiagram-5YYISTIA.B3G5sfKm.js → infoDiagram-5YYISTIA.CbiInFvz.js} +1 -1
  71. package/web/_astro/{ishikawaDiagram-YF4QCWOH.ahVNsoAQ.js → ishikawaDiagram-YF4QCWOH.CB7lrSsJ.js} +1 -1
  72. package/web/_astro/{journeyDiagram-JHISSGLW.PmFHLc-X.js → journeyDiagram-JHISSGLW.rhGZgWt8.js} +1 -1
  73. package/web/_astro/{kanban-definition-UN3LZRKU.Co1qOPTd.js → kanban-definition-UN3LZRKU.DPeZD_lP.js} +1 -1
  74. package/web/_astro/{linear.DoHZ-iRt.js → linear.l60Nyp5b.js} +1 -1
  75. package/web/_astro/{mermaid.core.DNRElYN2.js → mermaid.core.DmtMJcmL.js} +4 -4
  76. package/web/_astro/{mindmap-definition-RKZ34NQL.CdfJZidJ.js → mindmap-definition-RKZ34NQL.Cc47AcCn.js} +1 -1
  77. package/web/_astro/{pieDiagram-4H26LBE5.C1A8dby4.js → pieDiagram-4H26LBE5.B8cN-S9g.js} +1 -1
  78. package/web/_astro/{quadrantDiagram-W4KKPZXB.NmZqjG_c.js → quadrantDiagram-W4KKPZXB.DY1FN89L.js} +1 -1
  79. package/web/_astro/{requirementDiagram-4Y6WPE33.tY36zYtK.js → requirementDiagram-4Y6WPE33.CnM6VbV8.js} +1 -1
  80. package/web/_astro/{sankeyDiagram-5OEKKPKP.rOEGZYjo.js → sankeyDiagram-5OEKKPKP.B9DcGfNV.js} +1 -1
  81. package/web/_astro/{sequenceDiagram-3UESZ5HK.Dpnw3S9J.js → sequenceDiagram-3UESZ5HK.ymxaNlAY.js} +1 -1
  82. package/web/_astro/{stateDiagram-AJRCARHV.DGpvQ3Wz.js → stateDiagram-AJRCARHV.DDOJ3d8P.js} +1 -1
  83. package/web/_astro/{stateDiagram-v2-BHNVJYJU.CbwQdEfu.js → stateDiagram-v2-BHNVJYJU.Ck0Nb_KY.js} +1 -1
  84. package/web/_astro/{timeline-definition-PNZ67QCA.DAYUpUJT.js → timeline-definition-PNZ67QCA.BNOvZwAN.js} +1 -1
  85. package/web/_astro/{vennDiagram-CIIHVFJN.C7JCCd6-.js → vennDiagram-CIIHVFJN.D3k5ivSF.js} +1 -1
  86. package/web/_astro/{wardley-L42UT6IY.BGgwOH5_.js → wardley-L42UT6IY.BylaxVqn.js} +1 -1
  87. package/web/_astro/{wardleyDiagram-YWT4CUSO.oIZqzI2l.js → wardleyDiagram-YWT4CUSO.HLNtFUm9.js} +1 -1
  88. package/web/_astro/{xychartDiagram-2RQKCTM6.DohcJzqm.js → xychartDiagram-2RQKCTM6.B-WooKSU.js} +1 -1
  89. package/web/index.html +2 -2
  90. package/web/_astro/BoardApp.D8InuCYx.js +0 -1
  91. package/web/_astro/channel.XCf1DcsL.js +0 -1
  92. package/web/_astro/index.DJl2RAVZ.css +0 -1
package/spur.js CHANGED
@@ -37592,6 +37592,18 @@ function resolveHistoryRefreshTrigger(config3) {
37592
37592
  const parsed = HistoryRefreshConfigSchema.parse(config3?.history?.refresh ?? {});
37593
37593
  return { onCompletion: parsed.on_completion, debounceMs: parsed.debounce_ms };
37594
37594
  }
37595
+ function misplacedGlobalKeys(parsed) {
37596
+ const misplaced = [];
37597
+ for (const key of PROJECT_SHAPED_GLOBAL_KEYS) {
37598
+ if (key in parsed)
37599
+ misplaced.push(key);
37600
+ }
37601
+ const agent = parsed.agent;
37602
+ if (typeof agent === "object" && agent !== null && "team" in agent) {
37603
+ misplaced.push("agent.team");
37604
+ }
37605
+ return misplaced;
37606
+ }
37595
37607
  function parseEnvBoolean(value) {
37596
37608
  if (value === undefined || value === "")
37597
37609
  return;
@@ -37620,7 +37632,7 @@ function buildConfigFromEnv(env = process.env) {
37620
37632
  }
37621
37633
  });
37622
37634
  }
37623
- var SPUR_ENV_VARS, SPUR_LOG_LEVELS, DEFAULT_TASKS_DIR = "docs/tasks", DEFAULT_FEATURES_DIR = "docs/features", DEFAULT_DATABASE_URL = ".spur/spur.db", IN_MEMORY_DATABASE_URL = ":memory:", folderConfigSchema, tasksConfigSchema, featuresConfigSchema, EXECUTOR_CAPABILITY_TIERS, executorCapabilityTierSchema, AGENT_ROLE_NAMES, DEFAULT_AGENT_ROLES, AgentExecutorConfigSchema, AgentRoleConfigSchema, AGENT_ID_REGEX, TeamMemberConfigSchema, TeamConfigSchema, AgentOutputConfigSchema, AgentConfigSchema, RulesConfigSchema, WorkflowsConfigSchema, WorkflowConfigSchema, RedactionConfigSchema, HistoryRefreshConfigSchema, HistoryConfigSchema, spurConfigSchema, configSchema2;
37635
+ var SPUR_ENV_VARS, SPUR_LOG_LEVELS, DEFAULT_TASKS_DIR = "docs/tasks", DEFAULT_FEATURES_DIR = "docs/features", DEFAULT_DATABASE_URL = ".spur/spur.db", IN_MEMORY_DATABASE_URL = ":memory:", folderConfigSchema, tasksConfigSchema, featuresConfigSchema, EXECUTOR_CAPABILITY_TIERS, executorCapabilityTierSchema, AGENT_ROLE_NAMES, DEFAULT_AGENT_ROLES, AgentExecutorConfigSchema, AgentRoleConfigSchema, AGENT_ID_REGEX, TeamMemberConfigSchema, TeamConfigSchema, AgentOutputConfigSchema, AgentConfigSchema, RulesConfigSchema, WorkflowsConfigSchema, WorkflowConfigSchema, RedactionConfigSchema, HistoryRefreshConfigSchema, HistoryConfigSchema, spurConfigSchema, PROJECT_SHAPED_GLOBAL_KEYS, configSchema2;
37624
37636
  var init_src = __esm(() => {
37625
37637
  init_zod();
37626
37638
  init_finding_codes();
@@ -37661,7 +37673,7 @@ var init_src = __esm(() => {
37661
37673
  executorCapabilityTierSchema = exports_external.preprocess((value) => {
37662
37674
  if (value === undefined || value === null)
37663
37675
  return;
37664
- return normalizeExecutorTier(value);
37676
+ return typeof value === "string" ? normalizeExecutorTier(value) : value;
37665
37677
  }, exports_external.enum(EXECUTOR_CAPABILITY_TIERS).optional());
37666
37678
  AGENT_ROLE_NAMES = ["scribe", "coder", "reviewer", "planner"];
37667
37679
  DEFAULT_AGENT_ROLES = new Map([
@@ -37858,6 +37870,7 @@ var init_src = __esm(() => {
37858
37870
  tasks: tasksConfigSchema.optional(),
37859
37871
  features: featuresConfigSchema.optional()
37860
37872
  });
37873
+ PROJECT_SHAPED_GLOBAL_KEYS = ["name", "bootstrap", "rules", "redaction", "tasks", "features"];
37861
37874
  configSchema2 = exports_external.object({
37862
37875
  database: exports_external.object({
37863
37876
  url: exports_external.string().default(DEFAULT_DATABASE_URL)
@@ -39523,7 +39536,9 @@ function walk(dir, relPrefix, filter = /\.(ya?ml|json)$/i) {
39523
39536
  function isDroppedFromProjectSeed(rel) {
39524
39537
  if (rel === BUNDLED_GLOBAL_CONFIG)
39525
39538
  return true;
39526
- if (rel.startsWith("templates/task/"))
39539
+ if (rel.startsWith("templates/"))
39540
+ return true;
39541
+ if (rel.startsWith("workflows/"))
39527
39542
  return true;
39528
39543
  if (rel.startsWith("plugins/"))
39529
39544
  return true;
@@ -39676,6 +39691,9 @@ async function readRawYamlLayer(configPath, kind) {
39676
39691
  throw new Error(`Failed to parse ${kind} config ${configPath}: ${error51.message}`);
39677
39692
  }
39678
39693
  }
39694
+ function parseConfigYaml2(text3) {
39695
+ return $parse(text3) ?? {};
39696
+ }
39679
39697
  function isPlainObject6(value) {
39680
39698
  return typeof value === "object" && value !== null && !Array.isArray(value);
39681
39699
  }
@@ -39718,21 +39736,20 @@ function mergeByKeyIdentity(globalItems, projectItems, identityOf, segments) {
39718
39736
  const indexByIdentity = new Map;
39719
39737
  for (const item of globalItems) {
39720
39738
  const identity4 = identityOf(item);
39721
- if (identity4 === undefined || !indexByIdentity.has(identity4)) {
39722
- if (identity4 !== undefined)
39723
- indexByIdentity.set(identity4, merged.length);
39724
- merged.push(item);
39725
- }
39739
+ if (identity4 !== undefined && !indexByIdentity.has(identity4))
39740
+ indexByIdentity.set(identity4, merged.length);
39741
+ merged.push(item);
39726
39742
  }
39743
+ const matchedProjectIdentities = new Set;
39727
39744
  for (const item of projectItems) {
39728
39745
  const identity4 = identityOf(item);
39729
- const existing = identity4 === undefined ? undefined : indexByIdentity.get(identity4);
39746
+ const existing = identity4 === undefined || matchedProjectIdentities.has(identity4) ? undefined : indexByIdentity.get(identity4);
39730
39747
  if (existing === undefined) {
39731
- if (identity4 !== undefined)
39732
- indexByIdentity.set(identity4, merged.length);
39733
39748
  merged.push(item);
39734
39749
  continue;
39735
39750
  }
39751
+ if (identity4 !== undefined)
39752
+ matchedProjectIdentities.add(identity4);
39736
39753
  const base2 = merged[existing];
39737
39754
  merged[existing] = isPlainObject6(base2) && isPlainObject6(item) ? mergeDeep(base2, item, [...segments, existing]) : item;
39738
39755
  }
@@ -84907,10 +84924,19 @@ class WorkflowAppService {
84907
84924
  };
84908
84925
  }
84909
84926
  async validate(file2, opts = {}) {
84910
- const absolute = resolve13(this.ctx.cwd, file2);
84911
- if (!await fileExists(absolute)) {
84912
- return { ok: false, valid: false, file: file2, errors: [`File not found: ${absolute}`] };
84927
+ const resolved = resolveWorkflowFile(this.ctx.cwd, file2);
84928
+ if (resolved.path === null) {
84929
+ const [probedProject, probedBundled] = resolved.probed;
84930
+ return {
84931
+ ok: false,
84932
+ valid: false,
84933
+ file: file2,
84934
+ errors: [
84935
+ `File not found: ${probedProject}${probedBundled !== null ? ` (bundled: ${probedBundled})` : ""}`
84936
+ ]
84937
+ };
84913
84938
  }
84939
+ const absolute = resolved.path;
84914
84940
  try {
84915
84941
  const embedded = this.embeddedSchemaOptions();
84916
84942
  const workflow = await loadWorkflowDef(absolute, {
@@ -84959,7 +84985,12 @@ class WorkflowAppService {
84959
84985
  }
84960
84986
  async run(file2, opts = {}) {
84961
84987
  const eventsBus = this.ctx.events?.();
84962
- const absolute = resolve13(this.ctx.cwd, file2);
84988
+ const resolved = resolveWorkflowFile(this.ctx.cwd, file2);
84989
+ if (resolved.path === null) {
84990
+ const [probedProject, probedBundled] = resolved.probed;
84991
+ throw new Error(`Workflow not found: ${probedProject}${probedBundled !== null ? ` (bundled: ${probedBundled})` : ""}`);
84992
+ }
84993
+ const absolute = resolved.path;
84963
84994
  const embedded = this.embeddedSchemaOptions();
84964
84995
  const workflow = await loadWorkflowDef(absolute, {
84965
84996
  validateSchema: true,
@@ -85019,7 +85050,10 @@ class WorkflowAppService {
85019
85050
  return;
85020
85051
  let workflowName;
85021
85052
  try {
85022
- const def = await loadWorkflowDef(resolve13(this.ctx.cwd, file2), { validateSchema: false });
85053
+ const resolved = resolveWorkflowFile(this.ctx.cwd, file2);
85054
+ if (resolved.path === null)
85055
+ return;
85056
+ const def = await loadWorkflowDef(resolved.path, { validateSchema: false });
85023
85057
  workflowName = def.name;
85024
85058
  } catch {
85025
85059
  return;
@@ -85532,6 +85566,21 @@ async function fileExists(path9) {
85532
85566
  const fs3 = createNodeFileSystem3();
85533
85567
  return await fs3.exists(path9);
85534
85568
  }
85569
+ function resolveWorkflowFile(cwd, file2) {
85570
+ const projectPath = resolve13(cwd, file2);
85571
+ if (existsSync10(projectPath)) {
85572
+ return { path: projectPath, source: "project" };
85573
+ }
85574
+ const bundledRoot = bundledConfigRoot();
85575
+ if (bundledRoot !== null) {
85576
+ const bundledPath = join22(bundledRoot, "workflows", basename7(file2));
85577
+ if (existsSync10(bundledPath)) {
85578
+ return { path: bundledPath, source: "bundled" };
85579
+ }
85580
+ return { path: null, probed: [projectPath, bundledPath] };
85581
+ }
85582
+ return { path: null, probed: [projectPath, null] };
85583
+ }
85535
85584
  async function resolveDefaultAgentVar(cwd, callerVars, warn) {
85536
85585
  const result = {};
85537
85586
  if (callerVars?.implementAgent === undefined && callerVars?.agent !== undefined) {
@@ -86534,6 +86583,7 @@ __export(exports_src2, {
86534
86583
  runHistoryReport: () => runHistoryReport,
86535
86584
  runCorpusCheck: () => runCorpusCheck,
86536
86585
  resolveWorkflowLogRetentionDays: () => resolveWorkflowLogRetentionDays,
86586
+ resolveWorkflowFile: () => resolveWorkflowFile,
86537
86587
  resolveSpurServeCommand: () => resolveSpurServeCommand,
86538
86588
  resolveRetentionQuotas: () => resolveRetentionQuotas,
86539
86589
  resolvePlanningFolders: () => resolvePlanningFolders,
@@ -95200,8 +95250,7 @@ init_loader();
95200
95250
  init_src2();
95201
95251
  await init_src3();
95202
95252
  import { existsSync as existsSync12 } from "fs";
95203
- import { homedir as homedir9 } from "os";
95204
- import { join as join25, resolve as resolve16 } from "path";
95253
+ import { join as join25 } from "path";
95205
95254
 
95206
95255
  // src/workflow/resolve-spur-bin.ts
95207
95256
  import { basename as basename8 } from "path";
@@ -95219,11 +95268,6 @@ function resolveSpurBin() {
95219
95268
  }
95220
95269
 
95221
95270
  // src/workflow/make-lifecycle-adapter.ts
95222
- var GLOBAL_CONFIG_DIR = join25(".config", "spur");
95223
- function globalConfigRoot(context4) {
95224
- const override = context4.env.SPUR_GLOBAL_RULES_DIR;
95225
- return override !== undefined && override.length > 0 ? resolve16(context4.cwd, override) : join25(homedir9(), GLOBAL_CONFIG_DIR);
95226
- }
95227
95271
  function resolveWorkflowPath(context4, profile) {
95228
95272
  const bundledRoot = bundledConfigRoot();
95229
95273
  if (bundledRoot !== null) {
@@ -95234,9 +95278,6 @@ function resolveWorkflowPath(context4, profile) {
95234
95278
  const projectPath = join25(context4.cwd, ".spur", "workflows", `${profile.workflowName}.yaml`);
95235
95279
  if (existsSync12(projectPath))
95236
95280
  return projectPath;
95237
- const globalPath = join25(globalConfigRoot(context4), "workflows", `${profile.workflowName}.yaml`);
95238
- if (existsSync12(globalPath))
95239
- return globalPath;
95240
95281
  return null;
95241
95282
  }
95242
95283
  function makeLifecycleAdapter(context4, profile) {
@@ -95652,7 +95693,7 @@ import { createRequire } from "module";
95652
95693
  var CLI_CONFIG = {
95653
95694
  binaryName: "spur",
95654
95695
  binaryLabel: "spur",
95655
- binaryVersion: "0.3.60",
95696
+ binaryVersion: "0.3.61",
95656
95697
  configDir: ".spur",
95657
95698
  configFile: ".spur/config.yaml",
95658
95699
  databaseFile: ".spur/spur.db"
@@ -95926,24 +95967,17 @@ function formatDailyResult(r) {
95926
95967
  }
95927
95968
 
95928
95969
  // src/commands/init.ts
95970
+ init_src();
95929
95971
  init_loader();
95930
95972
  init_src2();
95931
95973
  init_dist10();
95932
- import { homedir as homedir10 } from "os";
95933
- import { join as join26, resolve as resolve17 } from "path";
95974
+ import { homedir as homedir9 } from "os";
95975
+ import { join as join26, resolve as resolve16 } from "path";
95934
95976
 
95935
95977
  // src/config/scaffold-manifest.ts
95936
95978
  var SCAFFOLD_MANIFEST = [
95937
95979
  { source: "rules/recommended-pre-check.yaml", target: "rules/recommended-pre-check.yaml" },
95938
95980
  { source: "rules/recommended-post-check.yaml", target: "rules/recommended-post-check.yaml" },
95939
- { source: "workflows/basic.yaml", target: "workflows/basic.yaml" },
95940
- { source: "workflows/task-lifecycle.yaml", target: "workflows/task-lifecycle.yaml" },
95941
- { source: "workflows/feature-lifecycle.yaml", target: "workflows/feature-lifecycle.yaml" },
95942
- { source: "workflows/feature-dev.yaml", target: "workflows/feature-dev.yaml" },
95943
- { source: "workflows/task-pipeline.yaml", target: "workflows/task-pipeline.yaml" },
95944
- { source: "workflows/idea-pipeline.yaml", target: "workflows/idea-pipeline.yaml" },
95945
- { source: "workflows/wrapup-pipeline.yaml", target: "workflows/wrapup-pipeline.yaml" },
95946
- { source: "workflows/pr-review.yaml", target: "workflows/pr-review.yaml" },
95947
95981
  { source: "tasks/section-matrix.yaml", target: "tasks/section-matrix.yaml" },
95948
95982
  { source: "templates/task/standard.md", target: "tasks/templates/standard.md" },
95949
95983
  { source: "templates/task/feature-impl.md", target: "tasks/templates/feature-impl.md" },
@@ -95951,9 +95985,6 @@ var SCAFFOLD_MANIFEST = [
95951
95985
  { source: "templates/task/review.md", target: "tasks/templates/review.md" },
95952
95986
  { source: "templates/task/brainstorm.md", target: "tasks/templates/brainstorm.md" },
95953
95987
  { source: "templates/task/meta.md", target: "tasks/templates/meta.md" },
95954
- { source: "templates/feature/default.md", target: "templates/feature/default.md" },
95955
- { source: "templates/bdd/gherkin.md", target: "templates/bdd/gherkin.md" },
95956
- { source: "templates/bdd/checklist.md", target: "templates/bdd/checklist.md" },
95957
95988
  {
95958
95989
  source: "templates/docs/99_PROJECT_CONSTITUTION.md",
95959
95990
  target: "docs/99_PROJECT_CONSTITUTION.md",
@@ -95966,19 +95997,12 @@ var SCAFFOLD_MANIFEST = [
95966
95997
  { source: "templates/docs/03_ARCHITECTURE.md", target: "docs/03_ARCHITECTURE.md", root: true, preserve: true },
95967
95998
  { source: "templates/docs/04_DESIGN.md", target: "docs/04_DESIGN.md", root: true, preserve: true },
95968
95999
  { source: "templates/docs/05_FEATURES.md", target: "docs/05_FEATURES.md", root: true, preserve: true },
95969
- { source: "templates/docs/99_PROJECT_CONSTITUTION.md", target: "templates/docs/99_PROJECT_CONSTITUTION.md" },
95970
- { source: "templates/docs/00_ADR.md", target: "templates/docs/00_ADR.md" },
95971
- { source: "templates/docs/01_PRD.md", target: "templates/docs/01_PRD.md" },
95972
- { source: "templates/docs/02_ROADMAP.md", target: "templates/docs/02_ROADMAP.md" },
95973
- { source: "templates/docs/03_ARCHITECTURE.md", target: "templates/docs/03_ARCHITECTURE.md" },
95974
- { source: "templates/docs/04_DESIGN.md", target: "templates/docs/04_DESIGN.md" },
95975
- { source: "templates/docs/05_FEATURES.md", target: "templates/docs/05_FEATURES.md" },
95976
96000
  { source: "templates/AGENTS.md", target: "AGENTS.md", root: true, preserve: true }
95977
96001
  ];
95978
96002
 
95979
96003
  // src/commands/init.ts
95980
- var GLOBAL_CONFIG_DIR2 = join26(".config", "spur");
95981
- var GLOBAL_RULES_DIR2 = join26(GLOBAL_CONFIG_DIR2, "rules");
96004
+ var GLOBAL_CONFIG_DIR = join26(".config", "spur");
96005
+ var GLOBAL_RULES_DIR2 = join26(GLOBAL_CONFIG_DIR, "rules");
95982
96006
  var GLOBAL_CONFIG_EXAMPLE = BUNDLED_GLOBAL_CONFIG;
95983
96007
  var GLOBAL_CONFIG_FILE2 = "config.yaml";
95984
96008
  var INDEXED_CONTEXT_MARKER = "## Indexed context";
@@ -96009,7 +96033,7 @@ If \`.spur/context/\` is absent, proceed normally. Never block work on its absen
96009
96033
  `;
96010
96034
  function globalRulesRoot(context4) {
96011
96035
  const override = context4.env.SPUR_GLOBAL_RULES_DIR;
96012
- return override !== undefined && override.length > 0 ? resolve17(context4.cwd, override) : join26(homedir10(), GLOBAL_RULES_DIR2);
96036
+ return override !== undefined && override.length > 0 ? resolve16(context4.cwd, override) : join26(homedir9(), GLOBAL_RULES_DIR2);
96013
96037
  }
96014
96038
  async function writeIfNew(context4, path9, content, force, result) {
96015
96039
  if (!force && await context4.fs.exists(path9)) {
@@ -96035,12 +96059,15 @@ async function seedGlobalRules(context4) {
96035
96059
  }
96036
96060
  return written;
96037
96061
  }
96062
+ function resolveGlobalConfigDir(context4) {
96063
+ const globalOverride = context4.env.SPUR_GLOBAL_RULES_DIR;
96064
+ return globalOverride !== undefined && globalOverride.length > 0 ? resolve16(context4.cwd, globalOverride) : join26(homedir9(), GLOBAL_CONFIG_DIR);
96065
+ }
96038
96066
  async function seedGlobalConfig(context4) {
96039
96067
  const source = bundledConfigRoot();
96040
96068
  if (source === null)
96041
96069
  return 0;
96042
- const globalOverride = context4.env.SPUR_GLOBAL_RULES_DIR;
96043
- const target = globalOverride !== undefined && globalOverride.length > 0 ? resolve17(context4.cwd, globalOverride) : join26(homedir10(), GLOBAL_CONFIG_DIR2);
96070
+ const target = resolveGlobalConfigDir(context4);
96044
96071
  let written = 0;
96045
96072
  for (const relPath of listBundledConfigFiles()) {
96046
96073
  if (relPath === GLOBAL_CONFIG_EXAMPLE)
@@ -96062,19 +96089,101 @@ async function seedGlobalConfig(context4) {
96062
96089
  return written;
96063
96090
  }
96064
96091
  function registerInitCommand(program2, context4, options = {}) {
96065
- program2.command("init", { hidden: options.hidden === true }).summary("scaffold a local Spur project").option(...SHARED_OPTIONS.nameProjectInit).option(...SHARED_OPTIONS.forceInitRecreate).option("--minimal", "Only write the minimal .spur scaffold").option(...SHARED_OPTIONS.json).action(async (options2) => {
96092
+ program2.command("init", { hidden: options.hidden === true }).summary("scaffold a local Spur project").option(...SHARED_OPTIONS.nameProjectInit).option(...SHARED_OPTIONS.forceInitRecreate).option("--minimal", "Only write the minimal .spur scaffold").option("--adopt-global-config", "Also rewrite ~/.config/spur/config.yaml from the shipped global default (backed up first)").option(...SHARED_OPTIONS.json).action(async (options2) => {
96066
96093
  const json3 = options2.json === true;
96067
96094
  const force = options2.force === true;
96068
96095
  const minimal = options2.minimal === true;
96096
+ const adoptGlobal = options2.adoptGlobalConfig === true;
96069
96097
  const projectName = options2.name ?? "default";
96070
96098
  const configPath = join26(context4.cwd, CLI_CONFIG.configFile);
96099
+ const result = { created: [], skipped: [] };
96100
+ const globalConfigDir = resolveGlobalConfigDir(context4);
96101
+ const globalConfigPath = join26(globalConfigDir, GLOBAL_CONFIG_FILE2);
96102
+ const globalConfigExists = await context4.fs.exists(globalConfigPath);
96103
+ let misplacedKeys = [];
96104
+ if (globalConfigExists) {
96105
+ try {
96106
+ const raw = await context4.fs.readFile(globalConfigPath);
96107
+ misplacedKeys = misplacedGlobalKeys(parseConfigYaml2(raw));
96108
+ } catch {}
96109
+ }
96071
96110
  if (!force && await context4.fs.exists(configPath)) {
96072
- const message = `Already initialized: ${CLI_CONFIG.configFile}. Use --force to overwrite.`;
96073
- context4.output.write(json3 ? toJson2({ ok: false, reason: "already-initialized", config: CLI_CONFIG.configFile }) : message);
96074
- context4.setExitCode(1);
96111
+ const configRoot = bundledConfigRoot();
96112
+ if (configRoot !== null && !minimal) {
96113
+ for (const relPath of listBundledProjectSeedFiles()) {
96114
+ const sourcePath = join26(configRoot, relPath);
96115
+ if (!await context4.fs.exists(sourcePath))
96116
+ continue;
96117
+ const targetPath = join26(context4.cwd, CLI_CONFIG.configDir, relPath);
96118
+ await context4.fs.ensureDir(join26(targetPath, ".."));
96119
+ await writeIfNew(context4, targetPath, await context4.fs.readFile(sourcePath), false, result);
96120
+ }
96121
+ for (const entry of SCAFFOLD_MANIFEST) {
96122
+ const sourcePath = join26(configRoot, entry.source);
96123
+ if (!await context4.fs.exists(sourcePath))
96124
+ continue;
96125
+ const baseDir = entry.root === true ? context4.cwd : join26(context4.cwd, CLI_CONFIG.configDir);
96126
+ const targetPath = join26(baseDir, entry.target);
96127
+ await context4.fs.ensureDir(join26(targetPath, ".."));
96128
+ const entryForce = false;
96129
+ let body = await context4.fs.readFile(sourcePath);
96130
+ if (entry.target === "AGENTS.md") {
96131
+ body = substituteAgentsMdTemplate(body, projectName);
96132
+ }
96133
+ if (entry.source.startsWith("templates/docs/")) {
96134
+ body = substituteDocTemplateTokens(body);
96135
+ }
96136
+ await writeIfNew(context4, targetPath, body, entryForce, result);
96137
+ }
96138
+ }
96139
+ let adoptedGlobal = false;
96140
+ if (adoptGlobal && globalConfigExists) {
96141
+ const backupPath = `${globalConfigPath}.bak-${new Date().toISOString().replace(/[:.]/g, "-")}`;
96142
+ await context4.fs.writeFile(backupPath, await context4.fs.readFile(globalConfigPath));
96143
+ const bundledRoot = bundledConfigRoot();
96144
+ const examplePath = bundledRoot !== null ? join26(bundledRoot, GLOBAL_CONFIG_EXAMPLE) : null;
96145
+ if (examplePath !== null && await context4.fs.exists(examplePath)) {
96146
+ await context4.fs.writeFile(globalConfigPath, await context4.fs.readFile(examplePath));
96147
+ adoptedGlobal = true;
96148
+ }
96149
+ result.created.push(backupPath);
96150
+ }
96151
+ const rulesSeeded2 = await seedGlobalRules(context4);
96152
+ const configSeeded2 = await seedGlobalConfig(context4);
96153
+ const payload = {
96154
+ ok: true,
96155
+ converged: true,
96156
+ config: CLI_CONFIG.configFile,
96157
+ ...result,
96158
+ globalRulesSeeded: rulesSeeded2,
96159
+ globalConfigSeeded: configSeeded2,
96160
+ ...adoptedGlobal ? { adoptGlobalConfig: adoptedGlobal } : {},
96161
+ ...misplacedKeys.length > 0 ? { misplacedGlobalKeys: misplacedKeys } : {}
96162
+ };
96163
+ if (json3) {
96164
+ context4.output.write(toJson2(payload));
96165
+ } else {
96166
+ context4.output.write("Already initialized \u2014 converged (no project files overwritten)");
96167
+ for (const path9 of result.created)
96168
+ context4.output.write(` \u2713 ${path9}`);
96169
+ for (const path9 of result.skipped)
96170
+ context4.output.write(` - ${path9} (exists)`);
96171
+ if (rulesSeeded2 > 0) {
96172
+ context4.output.write(` \u2713 seeded ${rulesSeeded2} rule file(s) to ~/${GLOBAL_RULES_DIR2}`);
96173
+ }
96174
+ if (configSeeded2 > 0) {
96175
+ context4.output.write(` \u2713 seeded ${configSeeded2} config file(s) to ~/${GLOBAL_CONFIG_DIR}`);
96176
+ }
96177
+ if (adoptedGlobal) {
96178
+ context4.output.write(" \u2713 adopted global config from bundled default (backup written)");
96179
+ }
96180
+ if (misplacedKeys.length > 0) {
96181
+ context4.output.write(` \u26A0 global config carries project-shaped key(s): ${misplacedKeys.join(", ")}`);
96182
+ }
96183
+ }
96184
+ context4.setExitCode(0);
96075
96185
  return;
96076
96186
  }
96077
- const result = { created: [], skipped: [] };
96078
96187
  const configYaml = `${[
96079
96188
  `# Spur project configuration for ${projectName}`,
96080
96189
  `# Generated by \`spur init\``,
@@ -96128,8 +96237,6 @@ ${contextEntry}
96128
96237
  const configRoot = bundledConfigRoot();
96129
96238
  if (configRoot !== null) {
96130
96239
  for (const relPath of listBundledProjectSeedFiles()) {
96131
- if (relPath.startsWith("templates/docs/"))
96132
- continue;
96133
96240
  const sourcePath = join26(configRoot, relPath);
96134
96241
  if (!await context4.fs.exists(sourcePath))
96135
96242
  continue;
@@ -96188,7 +96295,7 @@ ${contextEntry}
96188
96295
  context4.output.write(` \u2713 seeded ${rulesSeeded} rule file(s) to ~/${GLOBAL_RULES_DIR2}`);
96189
96296
  }
96190
96297
  if (configSeeded > 0) {
96191
- context4.output.write(` \u2713 seeded ${configSeeded} config file(s) to ~/${GLOBAL_CONFIG_DIR2}`);
96298
+ context4.output.write(` \u2713 seeded ${configSeeded} config file(s) to ~/${GLOBAL_CONFIG_DIR}`);
96192
96299
  }
96193
96300
  }
96194
96301
  });
@@ -96401,15 +96508,15 @@ async function runMessageWatch(svc, output2, options, runtime = {}) {
96401
96508
  }
96402
96509
  }
96403
96510
  function defaultSleep(ms, signal) {
96404
- return new Promise((resolve18) => {
96511
+ return new Promise((resolve17) => {
96405
96512
  if (signal?.aborted) {
96406
- resolve18();
96513
+ resolve17();
96407
96514
  return;
96408
96515
  }
96409
- const timer = setTimeout(resolve18, ms);
96516
+ const timer = setTimeout(resolve17, ms);
96410
96517
  signal?.addEventListener("abort", () => {
96411
96518
  clearTimeout(timer);
96412
- resolve18();
96519
+ resolve17();
96413
96520
  }, { once: true });
96414
96521
  });
96415
96522
  }
@@ -96436,15 +96543,15 @@ function parseTimeout2(raw) {
96436
96543
  return n3;
96437
96544
  }
96438
96545
  function sendSleep(ms, signal) {
96439
- return new Promise((resolve18) => {
96546
+ return new Promise((resolve17) => {
96440
96547
  if (signal?.aborted) {
96441
- resolve18();
96548
+ resolve17();
96442
96549
  return;
96443
96550
  }
96444
- const timer = setTimeout(resolve18, ms);
96551
+ const timer = setTimeout(resolve17, ms);
96445
96552
  signal?.addEventListener("abort", () => {
96446
96553
  clearTimeout(timer);
96447
- resolve18();
96554
+ resolve17();
96448
96555
  }, { once: true });
96449
96556
  });
96450
96557
  }
@@ -96514,12 +96621,12 @@ function registerMigrateCommand(program2, context4, options = {}) {
96514
96621
  // src/commands/projects.ts
96515
96622
  init_dist5();
96516
96623
  await init_src3();
96517
- import { basename as basename9, resolve as resolve18 } from "path";
96624
+ import { basename as basename9, resolve as resolve17 } from "path";
96518
96625
  function registerProjectsCommand(program2, context4) {
96519
96626
  const projectsCmd = program2.command("projects").summary("manage the Spur multi-project registry");
96520
96627
  projectsCmd.command("add").argument("<path>", "Project root directory path").option(...SHARED_OPTIONS.nameProjectDisplay).option(...SHARED_OPTIONS.jsonProjectsResponse).action(async (pathArg, options) => {
96521
96628
  try {
96522
- const absolutePath = resolve18(context4.cwd, pathArg);
96629
+ const absolutePath = resolve17(context4.cwd, pathArg);
96523
96630
  if (!await context4.fs.exists(absolutePath)) {
96524
96631
  throw new Error(`Directory does not exist: ${absolutePath}`);
96525
96632
  }
@@ -97227,8 +97334,8 @@ class EventPublisher {
97227
97334
  if (bufferedEvents.length > 0) {
97228
97335
  return { done: false, value: bufferedEvents.shift() };
97229
97336
  }
97230
- return new Promise((resolve19, reject) => {
97231
- pullResolvers.push([resolve19, reject]);
97337
+ return new Promise((resolve18, reject) => {
97338
+ pullResolvers.push([resolve18, reject]);
97232
97339
  });
97233
97340
  }, async () => {
97234
97341
  unsubscribe();
@@ -107164,7 +107271,7 @@ var spur_config_schema_default = {
107164
107271
  },
107165
107272
  version: {
107166
107273
  type: "string",
107167
- description: "Config schema version (string, not integer). Current recommended: '1.1' (ADR-033 executor tiers + planning blocks). '1' remains accepted for backward compatibility."
107274
+ description: "Config schema version (string, not integer). Current recommended: '1.2' (config-1.2 layered global/project merge, ADR-033 executor tiers + planning blocks). '1' remains accepted for backward compatibility."
107168
107275
  },
107169
107276
  name: {
107170
107277
  type: "string",
@@ -109196,7 +109303,7 @@ init_dist6();
109196
109303
  init_dist5();
109197
109304
  await init_src3();
109198
109305
  import { closeSync as closeSync5, fstatSync as fstatSync3, openSync as openSync5, readSync as readSync3 } from "fs";
109199
- import { join as join34, resolve as resolve19 } from "path";
109306
+ import { join as join34 } from "path";
109200
109307
  import { createInterface } from "readline";
109201
109308
  import { setTimeout as sleep5 } from "timers/promises";
109202
109309
 
@@ -109506,8 +109613,11 @@ Monitor with: spur workflow trace ${runId2} --follow`);
109506
109613
  let planPreview;
109507
109614
  if (options.plan !== false) {
109508
109615
  try {
109509
- const def = await loadWorkflowDef(resolve19(context4.cwd, file2), { validateSchema: false });
109510
- planPreview = renderRunPlan(def);
109616
+ const resolved = resolveWorkflowFile(context4.cwd, file2);
109617
+ if (resolved.path !== null) {
109618
+ const def = await loadWorkflowDef(resolved.path, { validateSchema: false });
109619
+ planPreview = renderRunPlan(def);
109620
+ }
109511
109621
  } catch {}
109512
109622
  }
109513
109623
  const runLog = options.log === false ? undefined : new WorkflowRunLogSink({
@@ -109562,10 +109672,13 @@ Monitor with: spur workflow trace ${runId2} --follow`);
109562
109672
  const steeringController = options.steer === true ? await (async () => {
109563
109673
  let identity7;
109564
109674
  try {
109565
- const def = await loadWorkflowDef(resolve19(context4.cwd, file2), {
109566
- validateSchema: false
109567
- });
109568
- identity7 = createWorkflowEventIdentity(def);
109675
+ const resolved = resolveWorkflowFile(context4.cwd, file2);
109676
+ if (resolved.path !== null) {
109677
+ const def = await loadWorkflowDef(resolved.path, {
109678
+ validateSchema: false
109679
+ });
109680
+ identity7 = createWorkflowEventIdentity(def);
109681
+ }
109569
109682
  } catch {}
109570
109683
  return new WorkflowSteeringController((ack) => {
109571
109684
  bus.emit("workflow.steering", identity7 === undefined ? ack : decorateWorkflowEvent(identity7, "workflow.steering", ack));
@@ -109732,7 +109845,14 @@ Monitor with: spur workflow trace ${runId2} --follow`);
109732
109845
  }
109733
109846
  });
109734
109847
  workflow.command("show").description("Render a workflow definition as a mermaid FSM diagram.").argument("<file>", "Workflow YAML file").action(async (file2) => {
109735
- const filePath = resolve19(context4.cwd, file2);
109848
+ const resolved = resolveWorkflowFile(context4.cwd, file2);
109849
+ if (resolved.path === null) {
109850
+ const [probedProject, probedBundled] = resolved.probed;
109851
+ context4.output.error(`workflow show: file not found: ${probedProject}${probedBundled !== null ? ` (bundled: ${probedBundled})` : ""}`);
109852
+ context4.setExitCode(1);
109853
+ return;
109854
+ }
109855
+ const filePath = resolved.path;
109736
109856
  let def;
109737
109857
  try {
109738
109858
  def = await loadWorkflowDef(filePath, { validateSchema: true });
@@ -110046,7 +110166,7 @@ init_src();
110046
110166
  init_src2();
110047
110167
  init_dist5();
110048
110168
  await init_src3();
110049
- import { dirname as dirname22, join as join35, resolve as resolve20 } from "path";
110169
+ import { dirname as dirname22, join as join35, resolve as resolve18 } from "path";
110050
110170
  import { isatty as isatty4 } from "tty";
110051
110171
 
110052
110172
  // ../../node_modules/.bun/@clack+core@1.4.1/node_modules/@clack/core/dist/index.mjs
@@ -111461,7 +111581,7 @@ function resolveAgentRoles(agentConfig) {
111461
111581
  return resolved;
111462
111582
  }
111463
111583
  function createCliContext(options) {
111464
- const cwd = resolve20(options.cwd ?? process.cwd());
111584
+ const cwd = resolve18(options.cwd ?? process.cwd());
111465
111585
  const env = options.env ?? process.env;
111466
111586
  const fs3 = createNodeFileSystem3(cwd);
111467
111587
  const agentRoles = options.agentRoles ?? resolveAgentRoles(options.agentConfig);
@@ -0,0 +1 @@
1
+ import{a as t}from"./BoardApp.CBIzcvqi.js";import"./index.A0eX93qW.js";export{t as default};