@gobing-ai/spur 0.3.84 → 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 (61) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.global.yaml +11 -6
  3. package/config/workflows/idea-pipeline.yaml +1 -1
  4. package/config/workflows/wrapup-pipeline.yaml +1 -1
  5. package/package.json +1 -1
  6. package/plugins/sp/agents/super-planner.md +2 -1
  7. package/plugins/sp/plugin.json +1 -1
  8. package/plugins/sp/scripts/surface-drift-inventory.ts +1 -1
  9. package/spur.js +244 -18
  10. package/web/_astro/BoardApp.CHenHFia.js +1 -0
  11. package/web/_astro/{BoardApp.D4mBUGLT.js → BoardApp.yW425dRZ.js} +60 -60
  12. package/web/_astro/{TaskDetail.DhCWaOPR.js → TaskDetail.CGwJAinW.js} +1 -1
  13. package/web/_astro/{arc.D3X0nfq-.js → arc.B-qNXzSO.js} +1 -1
  14. package/web/_astro/{architectureDiagram-3BPJPVTR.BQDoiGme.js → architectureDiagram-3BPJPVTR.Bdg-xiji.js} +1 -1
  15. package/web/_astro/{blockDiagram-GPEHLZMM.BjHekBbM.js → blockDiagram-GPEHLZMM.DFArg3Kp.js} +1 -1
  16. package/web/_astro/{c4Diagram-AAUBKEIU.BvHt171l.js → c4Diagram-AAUBKEIU.BkOLcjyx.js} +1 -1
  17. package/web/_astro/channel.CbDHK5UQ.js +1 -0
  18. package/web/_astro/{chunk-2J33WTMH.CwxGXlDR.js → chunk-2J33WTMH.DN3c20V4.js} +1 -1
  19. package/web/_astro/{chunk-4BX2VUAB.Dia1YtwU.js → chunk-4BX2VUAB.B64K0Ttd.js} +1 -1
  20. package/web/_astro/{chunk-55IACEB6.Oz9epCXa.js → chunk-55IACEB6.C-AJvNwa.js} +1 -1
  21. package/web/_astro/{chunk-727SXJPM.DKFYJjYJ.js → chunk-727SXJPM.DMkr46uS.js} +1 -1
  22. package/web/_astro/{chunk-AQP2D5EJ.AD7w5oVz.js → chunk-AQP2D5EJ.4_WGbI64.js} +1 -1
  23. package/web/_astro/{chunk-FMBD7UC4.D0OqTj52.js → chunk-FMBD7UC4.cbFW_lVE.js} +1 -1
  24. package/web/_astro/{chunk-ND2GUHAM.CU5ppR7j.js → chunk-ND2GUHAM.CDW_-2O1.js} +1 -1
  25. package/web/_astro/{chunk-QZHKN3VN.Bu1rFztw.js → chunk-QZHKN3VN.BoYOt_yg.js} +1 -1
  26. package/web/_astro/{classDiagram-4FO5ZUOK.C0Pgm5yE.js → classDiagram-4FO5ZUOK.BNT6XpHQ.js} +1 -1
  27. package/web/_astro/{classDiagram-v2-Q7XG4LA2.C0Pgm5yE.js → classDiagram-v2-Q7XG4LA2.BNT6XpHQ.js} +1 -1
  28. package/web/_astro/{cose-bilkent-S5V4N54A.C0ecLb1e.js → cose-bilkent-S5V4N54A.CI1DQxR3.js} +1 -1
  29. package/web/_astro/{cynefin-OW5HDTMX.CpStVmhO.js → cynefin-OW5HDTMX.A8ARH61p.js} +1 -1
  30. package/web/_astro/{dagre-BM42HDAG.BPWN3W9f.js → dagre-BM42HDAG.u28ZmqwR.js} +1 -1
  31. package/web/_astro/{diagram-2AECGRRQ.DnXbEot4.js → diagram-2AECGRRQ.Dx8JcrGc.js} +1 -1
  32. package/web/_astro/{diagram-5GNKFQAL.BYJsgUiI.js → diagram-5GNKFQAL.BXJUG9GY.js} +1 -1
  33. package/web/_astro/{diagram-KO2AKTUF.CU4Bqprx.js → diagram-KO2AKTUF.BtlMuWqW.js} +1 -1
  34. package/web/_astro/{diagram-LMA3HP47.BQLOANIu.js → diagram-LMA3HP47.C4T7qaAy.js} +1 -1
  35. package/web/_astro/{diagram-OG6HWLK6.Cn1UVhqj.js → diagram-OG6HWLK6.D5HECE10.js} +1 -1
  36. package/web/_astro/{erDiagram-TEJ5UH35.C8FdZyvA.js → erDiagram-TEJ5UH35.B_hMR3yw.js} +1 -1
  37. package/web/_astro/{flowDiagram-I6XJVG4X.QSZCR8Uo.js → flowDiagram-I6XJVG4X.BAmpcYl3.js} +1 -1
  38. package/web/_astro/{ganttDiagram-6RSMTGT7.O1Py31MY.js → ganttDiagram-6RSMTGT7.DV1bSWK-.js} +1 -1
  39. package/web/_astro/{gitGraphDiagram-PVQCEYII.DoeJRBfY.js → gitGraphDiagram-PVQCEYII.DxYKkKrJ.js} +1 -1
  40. package/web/_astro/{index.TPF1FerP.css → index.CcU5weKX.css} +1 -1
  41. package/web/_astro/{infoDiagram-5YYISTIA.9npLJecW.js → infoDiagram-5YYISTIA.BAWY2xMb.js} +1 -1
  42. package/web/_astro/{ishikawaDiagram-YF4QCWOH.DXanvOr3.js → ishikawaDiagram-YF4QCWOH.CZssRn6V.js} +1 -1
  43. package/web/_astro/{journeyDiagram-JHISSGLW.DxbVjxAv.js → journeyDiagram-JHISSGLW.C--muARd.js} +1 -1
  44. package/web/_astro/{kanban-definition-UN3LZRKU.BDk6_iq0.js → kanban-definition-UN3LZRKU.D_QCK4et.js} +1 -1
  45. package/web/_astro/{linear.C9cCDjT3.js → linear.DzrmTtZ0.js} +1 -1
  46. package/web/_astro/{mermaid.core.CpHRe5nH.js → mermaid.core.Da03W3iu.js} +4 -4
  47. package/web/_astro/{mindmap-definition-RKZ34NQL.D-6C85bn.js → mindmap-definition-RKZ34NQL.6cy-8hR_.js} +1 -1
  48. package/web/_astro/{pieDiagram-4H26LBE5.B3Oxodty.js → pieDiagram-4H26LBE5.C5rS1pdU.js} +1 -1
  49. package/web/_astro/{quadrantDiagram-W4KKPZXB.QZ_9Hshu.js → quadrantDiagram-W4KKPZXB.w56GZZ6Q.js} +1 -1
  50. package/web/_astro/{requirementDiagram-4Y6WPE33.BimvuK53.js → requirementDiagram-4Y6WPE33.CipX3Pwu.js} +1 -1
  51. package/web/_astro/{sankeyDiagram-5OEKKPKP.D5Kkpx2C.js → sankeyDiagram-5OEKKPKP.C0VVzgJm.js} +1 -1
  52. package/web/_astro/{sequenceDiagram-3UESZ5HK.B4YR6fAE.js → sequenceDiagram-3UESZ5HK.BX2dUUbF.js} +1 -1
  53. package/web/_astro/{stateDiagram-AJRCARHV.Bt2wkZok.js → stateDiagram-AJRCARHV.ypCdgODQ.js} +1 -1
  54. package/web/_astro/{stateDiagram-v2-BHNVJYJU.YZ4X7dfQ.js → stateDiagram-v2-BHNVJYJU.In0baEtg.js} +1 -1
  55. package/web/_astro/{timeline-definition-PNZ67QCA.DmiTBL8w.js → timeline-definition-PNZ67QCA.CJN4Vkvl.js} +1 -1
  56. package/web/_astro/{vennDiagram-CIIHVFJN.KNJV40mf.js → vennDiagram-CIIHVFJN.Cf8KkIPY.js} +1 -1
  57. package/web/_astro/{wardleyDiagram-YWT4CUSO.BVAaKgWH.js → wardleyDiagram-YWT4CUSO.DCZBo9xw.js} +1 -1
  58. package/web/_astro/{xychartDiagram-2RQKCTM6.D5Jx7zB2.js → xychartDiagram-2RQKCTM6.Cm4v_MiJ.js} +1 -1
  59. package/web/index.html +2 -2
  60. package/web/_astro/BoardApp.DFPYpI5O.js +0 -1
  61. package/web/_astro/channel.BlQwxyoI.js +0 -1
@@ -7,7 +7,7 @@
7
7
  "plugins": [
8
8
  {
9
9
  "name": "sp",
10
- "version": "0.3.84",
10
+ "version": "0.3.85",
11
11
  "source": "./plugins/sp"
12
12
  }
13
13
  ]
@@ -95,7 +95,7 @@ agent:
95
95
  # claude-*-5 via claude, grok-4.6 via grok, gemini-3.x via agy) —
96
96
  # consistently outperforms the same model routed through a third-party CLI,
97
97
  # and belongs at the capable rungs. `portable` models (glm-5.x,
98
- # deepseek-v4-*, …) are provider-agnostic and best carried by omp or pi at
98
+ # deepseek-v4-*, …) are provider-agnostic and best carried by pi at
99
99
  # the cheap/standard rungs. The ladder below mixes both classes on purpose;
100
100
  # measure pairings with the history plane before promoting a portable model
101
101
  # into a capable rung. Live tiers: cheap | standard | capable-1 | capable-2
@@ -106,19 +106,24 @@ agent:
106
106
  # capable-1.
107
107
  #
108
108
  # Because this is the global layer, a project that wants one different model
109
- # writes only `- name: omp` + `model: …` — the agent and tier come from
110
- # here.
109
+ # writes only `- name: pi-dsv4-flash` + `model: …` — the agent and tier come
110
+ # from here.
111
111
  executors:
112
112
  # cheap rung is optional: with none declared, scribe-role work starts
113
113
  # one rung up at the cheapest standard executor. Add one when
114
114
  # transcript-heavy mechanical commands (changelog/gitmsg/handover) get
115
115
  # noisy on cost.
116
116
  # - name: minimax
117
- # agent: omp
117
+ # agent: pi
118
118
  # model: minimax/MiniMax-M3
119
119
  # tier: cheap
120
- - name: omp
121
- agent: omp
120
+ # Standard rung. An `agent:` here is a live DISPATCH target, not a label:
121
+ # role routing (`agent.default: coder`, `--agent auto`, every workflow step
122
+ # declaring `agent: auto`) lands on the cheapest eligible standard executor,
123
+ # so listing an agent makes Spur delegate work to it. Keep this rung on an
124
+ # agent the supported install targets actually carry.
125
+ - name: pi-dsv4-flash
126
+ agent: pi
122
127
  model: opencode/deepseek-v4-flash
123
128
  tier: standard
124
129
  - name: pi
@@ -33,7 +33,7 @@
33
33
  # lets profile=auto route around the idea-eval taste gate (default "false")
34
34
  # CLI --approve-taste sets both design_approved and idea_approved to true.
35
35
  # spurBin — PATH-independent spur invocation (overridden by CLI at run start)
36
- # agent — agent for agent.run steps (default: omp)
36
+ # agent — agent for agent.run steps (default: auto → `agent.default` in config)
37
37
  #
38
38
  # Reliability (aligned with task-pipeline / ADR-043):
39
39
  # - Soft agent doctor at start → failed via transitions (not raw lifecycle abort)
@@ -31,7 +31,7 @@
31
31
  # merge — set --vars '{"merge":"true"}' to run branch cleanup (irreversible
32
32
  # HITL)
33
33
  # spurBin — PATH-independent spur invocation (overridden by CLI at run start)
34
- # agent — agent for agent.run steps (default: omp)
34
+ # agent — agent for agent.run steps (default: auto → `agent.default` in config)
35
35
  #
36
36
  # Reliability (aligned with task-pipeline / ADR-043):
37
37
  # - Prefer pure slash commands when a command exists; free-form inputs remain
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/spur",
3
- "version": "0.3.84",
3
+ "version": "0.3.85",
4
4
  "description": "Spur CLI — local-first harness for mainstream coding agents: constraint checking, workflow orchestration, agent health, and history analytics. Bun-native; exposes the `spur` command.",
5
5
  "keywords": [
6
6
  "spur",
@@ -97,7 +97,8 @@ You own the spaces **between** task runs:
97
97
  You explicitly do **NOT** own step-level execution:
98
98
 
99
99
  - How an `agent.run` step (implement/test/review/verify) runs is `vars.agent`'s concern - default
100
- `omp`, pinned in `task-pipeline.yaml`. `--agent <value>` from the command flows into each
100
+ `auto` (`agent.default` in config resolves the executor), pinned in `task-pipeline.yaml`.
101
+ `--agent <value>` from the command flows into each
101
102
  per-task `vars.agent`; you forward it, you do not interpret it.
102
103
  - You never edit the pipeline YAML, never reach into a step, and never decide how a single
103
104
  `agent.run` stage executes. The per-task pipeline is invoked **verbatim**.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sp",
3
- "version": "0.3.84",
3
+ "version": "0.3.85",
4
4
  "description": "Spur — a local-first harness engineering toolkit that wraps mainstream coding agents with constraint checking, workflow orchestration, and history analytics.",
5
5
  "extensions": {
6
6
  "pi": ["./hooks/pi/guard-extension.ts"]
@@ -636,7 +636,7 @@ const JSON_PROBES: string[][] = [
636
636
  ['feature', 'show', 'I3', '--json'],
637
637
  ['rule', 'list', '--json'],
638
638
  ['agent', 'list', '--json'],
639
- ['agent', 'doctor', 'omp', '--json'],
639
+ ['agent', 'doctor', 'pi', '--json'],
640
640
  ['workflow', 'list', '--json'],
641
641
  ['workflow', 'validate', 'basic.yaml', '--json'],
642
642
  ['status', '--json'],
package/spur.js CHANGED
@@ -39521,6 +39521,7 @@ var init_slash_commands = __esm(() => {
39521
39521
  description: exports_external.string().min(1),
39522
39522
  category: slashCommandCategorySchema.default("dev"),
39523
39523
  argumentHint: exports_external.string().optional(),
39524
+ tags: exports_external.array(exports_external.string()).default([]),
39524
39525
  role: exports_external.string().optional(),
39525
39526
  source: exports_external.string().optional()
39526
39527
  });
@@ -86449,8 +86450,13 @@ function schedulerJobTimeoutEnvName(name) {
86449
86450
  const snake = name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([^A-Za-z0-9]+)/g, "_").toUpperCase().replace(/^_+|_+$/g, "");
86450
86451
  return `SPUR_SCHEDULER_TIMEOUT_${snake}_MS`;
86451
86452
  }
86452
- function resolveSchedulerJobTimeoutMs(name, env, fallbackMs) {
86453
- 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;
86454
86460
  }
86455
86461
  function outputTail(text4) {
86456
86462
  const trimmed = text4.trim();
@@ -86506,6 +86512,10 @@ async function handleSchedulerCustomJob(deps, job) {
86506
86512
  throw new Error(describeBoundedFailure({ subject: `scheduler job "${payload.name}"`, outcome, outputTail: detail }));
86507
86513
  }
86508
86514
  if (result.exitCode === null) {
86515
+ if (deps.isShuttingDown?.() === true) {
86516
+ deps.onShutdownAbandon?.(payload.name);
86517
+ return;
86518
+ }
86509
86519
  const signalDetail = result.signal === undefined ? "" : ` (${result.signal})`;
86510
86520
  throw new Error(`scheduler job "${payload.name}" terminated before a normal exit${signalDetail} ` + `after ${Math.round(outcome.elapsedMs)}ms${detail && `: ${detail}`}`);
86511
86521
  }
@@ -88375,7 +88385,7 @@ class AgentRunActionRunner {
88375
88385
  const priority = asOptionalString(options.priority);
88376
88386
  const compareExecutorWith = asOptionalString(options.compareExecutorWith) ?? "implement";
88377
88387
  const agentLabel = dispatchAgent ?? "<default>";
88378
- const targetAgentDir = dispatchAgent ?? this.agentConfig.default ?? "omp";
88388
+ const targetAgentDir = dispatchAgent ?? this.agentConfig.default ?? "default";
88379
88389
  const prevAgent = freshSession ? undefined : asOptionalString(context4.vars.__agentSessionAgent);
88380
88390
  let sessionDir = freshSession ? undefined : asOptionalString(context4.vars.__agentSessionDir);
88381
88391
  if (affinityOn) {
@@ -94264,7 +94274,7 @@ function scanMarkdownCommands(dirPath, options = {}) {
94264
94274
  if (!existsSync8(dirPath)) {
94265
94275
  return [];
94266
94276
  }
94267
- const { prefix = "/", source = "plugin:sp", defaultCategory } = options;
94277
+ const { prefix = "/", source = "plugin:sp", defaultCategory, pluginId } = options;
94268
94278
  const items = [];
94269
94279
  try {
94270
94280
  const entries = readdirSync7(dirPath, { withFileTypes: true });
@@ -94301,15 +94311,56 @@ function scanMarkdownCommands(dirPath, options = {}) {
94301
94311
  } else {
94302
94312
  category = defaultCategory ?? deduceCommandCategory(stem);
94303
94313
  }
94304
- const hintRaw = frontmatter["argument-hint"] ?? frontmatter.argumentHint;
94314
+ const hintRaw = frontmatter["argument-hint"] ?? frontmatter.argumentHint ?? frontmatter.arguments ?? frontmatter.args;
94305
94315
  const argumentHint = typeof hintRaw === "string" && hintRaw.trim().length > 0 ? hintRaw.trim() : undefined;
94306
94316
  const roleRaw = frontmatter.role;
94307
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);
94308
94358
  items.push({
94309
94359
  name,
94310
94360
  description,
94311
94361
  category,
94312
94362
  argumentHint,
94363
+ tags,
94313
94364
  role,
94314
94365
  source
94315
94366
  });
@@ -94317,14 +94368,133 @@ function scanMarkdownCommands(dirPath, options = {}) {
94317
94368
  } catch {}
94318
94369
  return items;
94319
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
+ }
94320
94488
  function generateSlashCommands(options = {}) {
94321
94489
  const {
94322
94490
  projectRoot = process.cwd(),
94323
94491
  includeBuiltin = true,
94324
94492
  includePluginSp = true,
94493
+ includeAllPlugins = true,
94325
94494
  includeProjectClaude = true,
94326
94495
  includeUserClaude = true,
94327
- includeUserSpur = true
94496
+ includeUserSpur = true,
94497
+ claudeDir
94328
94498
  } = options;
94329
94499
  const commandMap = new Map;
94330
94500
  if (includeBuiltin) {
@@ -94332,15 +94502,34 @@ function generateSlashCommands(options = {}) {
94332
94502
  commandMap.set(cmd.name, cmd);
94333
94503
  }
94334
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
+ }
94335
94521
  if (includePluginSp) {
94336
94522
  const spCommandsDir = findPluginsSpCommandsDir(projectRoot) ?? resolve16(projectRoot, "plugins", "sp", "commands");
94337
- const spCommands = scanMarkdownCommands(spCommandsDir, {
94338
- prefix: "/sp:",
94339
- source: "plugin:sp",
94340
- defaultCategory: "harness"
94341
- });
94342
- for (const cmd of spCommands) {
94343
- commandMap.set(cmd.name, cmd);
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
+ }
94344
94533
  }
94345
94534
  }
94346
94535
  if (includeProjectClaude) {
@@ -94368,6 +94557,7 @@ function generateSlashCommands(options = {}) {
94368
94557
  const userSpurCommands = scanMarkdownCommands(userSpurDir, {
94369
94558
  prefix: "/sp:",
94370
94559
  source: "spur:user",
94560
+ pluginId: "sp",
94371
94561
  defaultCategory: "harness"
94372
94562
  });
94373
94563
  for (const cmd of userSpurCommands) {
@@ -94456,90 +94646,114 @@ var init_slash_commands_service = __esm(() => {
94456
94646
  name: "/help",
94457
94647
  description: "View available commands, tools, and usage hints",
94458
94648
  category: "sys",
94649
+ argumentHint: "[command]",
94650
+ tags: ["builtin", "sys", "help"],
94459
94651
  source: "builtin"
94460
94652
  },
94461
94653
  {
94462
94654
  name: "/clear",
94463
94655
  description: "Clear conversation history and reset context window",
94464
94656
  category: "sys",
94657
+ tags: ["builtin", "sys", "clear"],
94465
94658
  source: "builtin"
94466
94659
  },
94467
94660
  {
94468
94661
  name: "/compact",
94469
94662
  description: "Purge execution trace and condense context window",
94470
94663
  category: "sys",
94664
+ argumentHint: "[focus]",
94665
+ tags: ["builtin", "sys", "compact", "context"],
94471
94666
  source: "builtin"
94472
94667
  },
94473
94668
  {
94474
94669
  name: "/cost",
94475
94670
  description: "Inspect token breakdown and USD expenditure",
94476
94671
  category: "sys",
94672
+ tags: ["builtin", "sys", "cost", "tokens"],
94477
94673
  source: "builtin"
94478
94674
  },
94479
94675
  {
94480
94676
  name: "/doctor",
94481
94677
  description: "Run diagnostic health checks on environment and tools",
94482
94678
  category: "sys",
94679
+ tags: ["builtin", "sys", "doctor", "health"],
94483
94680
  source: "builtin"
94484
94681
  },
94485
94682
  {
94486
94683
  name: "/init",
94487
94684
  description: "Initialize project instructions, settings and guidelines",
94488
94685
  category: "harness",
94686
+ tags: ["builtin", "harness", "init"],
94489
94687
  source: "builtin"
94490
94688
  },
94491
94689
  {
94492
94690
  name: "/review",
94493
94691
  description: "Inspect staged git diff, verify test coverage & lint",
94494
94692
  category: "git",
94693
+ argumentHint: "[target] [--cached]",
94694
+ tags: ["builtin", "git", "review"],
94495
94695
  source: "builtin"
94496
94696
  },
94497
94697
  {
94498
94698
  name: "/commit",
94499
94699
  description: "Draft conventional commit message from staged hunks",
94500
94700
  category: "git",
94701
+ argumentHint: "[--all] [-m <message>]",
94702
+ tags: ["builtin", "git", "commit"],
94501
94703
  source: "builtin"
94502
94704
  },
94503
94705
  {
94504
94706
  name: "/terminal",
94505
94707
  description: "Run isolated bash execution in sandbox mirror",
94506
94708
  category: "sys",
94709
+ argumentHint: "[command]",
94710
+ tags: ["builtin", "sys", "terminal", "bash"],
94507
94711
  source: "builtin"
94508
94712
  },
94509
94713
  {
94510
94714
  name: "/revert",
94511
94715
  description: "Undo last agent file changes or restore git stash",
94512
94716
  category: "git",
94717
+ argumentHint: "[stash-id | file]",
94718
+ tags: ["builtin", "git", "revert"],
94513
94719
  source: "builtin"
94514
94720
  },
94515
94721
  {
94516
94722
  name: "/resume",
94517
94723
  description: "Resume paused background agent run",
94518
94724
  category: "dev",
94725
+ argumentHint: "[run-id]",
94726
+ tags: ["builtin", "dev", "resume"],
94519
94727
  source: "builtin"
94520
94728
  },
94521
94729
  {
94522
94730
  name: "/bug",
94523
94731
  description: "Report a bug or issue with diagnostic transcript",
94524
94732
  category: "sys",
94733
+ argumentHint: "[description]",
94734
+ tags: ["builtin", "sys", "bug"],
94525
94735
  source: "builtin"
94526
94736
  },
94527
94737
  {
94528
94738
  name: "/config",
94529
94739
  description: "View or update local/global configuration settings",
94530
94740
  category: "sys",
94741
+ argumentHint: "[key] [value]",
94742
+ tags: ["builtin", "sys", "config"],
94531
94743
  source: "builtin"
94532
94744
  },
94533
94745
  {
94534
94746
  name: "/login",
94535
94747
  description: "Authenticate account and refresh API credentials",
94536
94748
  category: "sys",
94749
+ tags: ["builtin", "sys", "login", "auth"],
94537
94750
  source: "builtin"
94538
94751
  },
94539
94752
  {
94540
94753
  name: "/logout",
94541
94754
  description: "Sign out of current account and remove stored session",
94542
94755
  category: "sys",
94756
+ tags: ["builtin", "sys", "logout", "auth"],
94543
94757
  source: "builtin"
94544
94758
  }
94545
94759
  ];
@@ -98424,6 +98638,7 @@ __export(exports_src2, {
98424
98638
  enqueueHistoryRefresh: () => enqueueHistoryRefresh,
98425
98639
  encodeRunListCursor: () => encodeRunListCursor,
98426
98640
  drainPendingAgentQuotaUpdates: () => drainPendingAgentQuotaUpdates,
98641
+ discoverEnabledClaudePlugins: () => discoverEnabledClaudePlugins,
98427
98642
  deriveVerdict: () => deriveVerdict,
98428
98643
  deduceCommandCategory: () => deduceCommandCategory,
98429
98644
  decorateWorkflowEvent: () => decorateWorkflowEvent,
@@ -110305,7 +110520,7 @@ import { createRequire } from "module";
110305
110520
  var CLI_CONFIG = {
110306
110521
  binaryName: "spur",
110307
110522
  binaryLabel: "spur",
110308
- binaryVersion: "0.3.84",
110523
+ binaryVersion: "0.3.85",
110309
110524
  configDir: ".spur",
110310
110525
  configFile: ".spur/config.yaml",
110311
110526
  databaseFile: ".spur/spur.db"
@@ -122335,7 +122550,7 @@ function registerSchedulerEntries(scheduler3, ctx, jobs = [], options = {}) {
122335
122550
  });
122336
122551
  for (const job of jobs) {
122337
122552
  const schedule = job.cron ?? String(job.intervalMinutes * 60000);
122338
- const jobPolicy = resolveSchedulerJobTimeoutMs(job.name, env, timeoutMs);
122553
+ const jobPolicy = resolveSchedulerJobTimeoutMs(job.name, env, timeoutMs, job.timeoutMs);
122339
122554
  const sweepThresholdMs = jobPolicy === null ? null : jobPolicy + resolveKillGraceMs3(env);
122340
122555
  register(schedule, `${SCHEDULER_CUSTOM_JOB}:${job.name}`, async () => {
122341
122556
  const queue = await ctx.jobQueue();
@@ -122666,10 +122881,11 @@ async function startServer(options, deps = defaultDeps) {
122666
122881
  });
122667
122882
  registry3.register(SCHEDULER_CUSTOM_JOB, async (job) => {
122668
122883
  await emitQueueJobStarted(ctx, job);
122884
+ const declaredTimeouts = new Map(appRt.config.scheduler.jobs.map((entry) => [entry.name, entry.timeoutMs]));
122669
122885
  return handleSchedulerCustomJob({
122670
122886
  cwd: ctx.cwd,
122671
122887
  executor: childExecutor,
122672
- resolveTimeoutMs: (name) => resolveSchedulerJobTimeoutMs(name, env, schedulerCustomTimeoutMs),
122888
+ resolveTimeoutMs: (name) => resolveSchedulerJobTimeoutMs(name, env, schedulerCustomTimeoutMs, declaredTimeouts.get(name)),
122673
122889
  onDuplicate: (name) => {
122674
122890
  ctx.eventBus().emit("scheduler.job.executed", {
122675
122891
  name: `${SCHEDULER_CUSTOM_JOB}:${name}`,
@@ -122678,10 +122894,20 @@ async function startServer(options, deps = defaultDeps) {
122678
122894
  skipped: true,
122679
122895
  reason: `duplicate execution suppressed; ${name} is already running in this process`
122680
122896
  });
122681
- }
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
122682
122908
  }, job);
122683
122909
  });
122684
- 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);
122685
122911
  const orphanCount = await failOrphanedProcessingJobs(await ctx.getDb(), Date.now(), unlimitedJobNames);
122686
122912
  if (orphanCount > 0) {
122687
122913
  appRt.logger.warn("Swept orphaned processing jobs at startup", {
@@ -0,0 +1 @@
1
+ import{d as a}from"./BoardApp.yW425dRZ.js";import"./index.De90oHcH.js";export{a as default};