@gobing-ai/spur 0.4.31 → 0.4.33

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.
@@ -7,7 +7,7 @@
7
7
  "plugins": [
8
8
  {
9
9
  "name": "sp",
10
- "version": "0.4.31",
10
+ "version": "0.4.33",
11
11
  "source": "./plugins/sp"
12
12
  }
13
13
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/spur",
3
- "version": "0.4.31",
3
+ "version": "0.4.33",
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",
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "sp",
3
- "version": "0.4.31",
3
+ "version": "0.4.33",
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
  }
@@ -72,11 +72,16 @@ mode; `batch-create` surfaces per-child `warnings` rows (`[{"wbs", "warnings"}]`
72
72
  when non-empty) in `--json` and prints `<wbs>: <warning>` lines to stderr in human mode;
73
73
  `update --section` section writes carried this advisory already.
74
74
 
75
+ When an advisory fires, split the task instead of growing it: decompose into smaller tasks before
76
+ pipeline execution (execution-workflow.md, "Split oversized tasks before pipeline execution").
77
+ `create --requirements/--plan` and `batch-create` body fields exist to author the split tasks'
78
+ content up front — not to accumulate more into one oversized task.
79
+
75
80
  ## Verb map
76
81
 
77
82
  | Verb | Purpose | Key flags |
78
83
  | ---- | ------- | --------- |
79
- | `create <title>` | Allocate a new task (race-safe WBS) | `--feature <id>` `--no-reopen` `--parent <wbs>` `--template <variant>` `--dedupe-within <s>` `--allow-duplicate-name` `--skip-ready` `--agent <selector>` `--folder` `--json` |
84
+ | `create <title>` | Allocate a new task (race-safe WBS) | `--feature <id>` `--no-reopen` `--parent <wbs>` `--template <variant>` `--requirements <text>` `--plan <text>` `--dedupe-within <s>` `--allow-duplicate-name` `--skip-ready` `--agent <selector>` `--folder` `--json` |
80
85
  | `show <wbs>` | Print one task's frontmatter + body; section-filtered or summary mode (1325) | `--section <name>` `--sections <csv>` `--summary` `--folder` `--json` |
81
86
  | `update <wbs> [status]` | Lifecycle transition, section replace, **or** frontmatter set | `--section <name> --from-file <path>` `--assignee <spec-id>` (exclusive with `--section`) `--feature <id>` `--no-reopen` `--priority <p>` `--ac-numbering task-local` `--ac-altitude <a>` `--estimate-hours <n>` `--no-lifecycle` `--force-done` `--reason <text>` `--provenance-bypass` `--verdict-dir <path>` `--folder` `--json` |
82
87
  | `deps <wbs> <op> [values...]` | Mutate `dependencies[]` frontmatter array (ops: `set`, `add`, `remove`, `clear`) | `--folder` `--json` |
package/spur.js CHANGED
@@ -44082,6 +44082,7 @@ async function messageRollup(db, sel, opts) {
44082
44082
  return db.queryAll(`WITH filtered AS (
44083
44083
  SELECT m.source, m.session_id, m.model, m.ts, m.input_tokens, m.output_tokens,
44084
44084
  m.cache_read_tokens, m.cache_write_tokens, m.cost_usd, m.role, m.duration_ms,
44085
+ m.disposition,
44085
44086
  ROW_NUMBER() OVER (PARTITION BY m.request_id ORDER BY m.rowid DESC) AS rn,
44086
44087
  m.request_id
44087
44088
  FROM history_message m
@@ -44097,7 +44098,7 @@ async function messageRollup(db, sel, opts) {
44097
44098
  'unknown'
44098
44099
  ) AS model,
44099
44100
  ts, input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
44100
- cost_usd, role, duration_ms
44101
+ cost_usd, role, duration_ms, disposition
44101
44102
  FROM filtered WHERE request_id IS NULL OR rn = 1
44102
44103
  )
44103
44104
  SELECT source, model, DATE(ts) AS day,
@@ -44108,9 +44109,12 @@ async function messageRollup(db, sel, opts) {
44108
44109
  SUM(cache_write_tokens) AS cacheWriteTokens,
44109
44110
  SUM(cost_usd) AS costUsd,
44110
44111
  SUM(CASE WHEN input_tokens IS NOT NULL OR output_tokens IS NOT NULL THEN 1 ELSE 0 END) AS recordsWithUsage,
44111
- SUM(CASE WHEN role = 'assistant' THEN duration_ms END) AS assistantDurationMs,
44112
- SUM(CASE WHEN role = 'assistant' THEN duration_ms IS NULL END) AS assistantDurationUnmeasured,
44113
- SUM(CASE WHEN role = 'assistant' AND duration_ms > 0 THEN 1 ELSE 0 END) AS assistantDurationSamples
44112
+ -- 1328: latency aggregates exclude collapsed poll rows \u2014 the duration
44113
+ -- backfill hands poll duplicates derived durations, so executor-latency
44114
+ -- means/samples stay unpolluted (1324 R3's rollup-chain residue).
44115
+ SUM(CASE WHEN role = 'assistant' AND disposition <> 'poll-duplicate' THEN duration_ms END) AS assistantDurationMs,
44116
+ SUM(CASE WHEN role = 'assistant' AND duration_ms IS NULL AND disposition <> 'poll-duplicate' THEN 1 ELSE 0 END) AS assistantDurationUnmeasured,
44117
+ SUM(CASE WHEN role = 'assistant' AND duration_ms > 0 AND disposition <> 'poll-duplicate' THEN 1 ELSE 0 END) AS assistantDurationSamples
44114
44118
  FROM selected
44115
44119
  GROUP BY source, model, DATE(ts)`, ...params, ...wm.params);
44116
44120
  }
@@ -109457,6 +109461,7 @@ class AgentRunExecutor {
109457
109461
  let result;
109458
109462
  let invocation;
109459
109463
  let dispatchStartedAt = Date.now();
109464
+ let lifecycleStarted = false;
109460
109465
  const getDb = this.ctx.getDb;
109461
109466
  let sessionObserver;
109462
109467
  if (getDb !== undefined) {
@@ -109634,7 +109639,8 @@ class AgentRunExecutor {
109634
109639
  ...sessionId !== undefined ? { sessionId } : {},
109635
109640
  ...translated !== undefined && prompt !== undefined ? { translatedFrom: traceSafePrompt(prompt) } : {}
109636
109641
  };
109637
- if (attempt === 0) {
109642
+ if (!lifecycleStarted) {
109643
+ lifecycleStarted = true;
109638
109644
  lifecycle.start({
109639
109645
  agent,
109640
109646
  ...model !== undefined ? { model } : {},
@@ -128869,7 +128875,9 @@ ${templateBackground}`;
128869
128875
  featureId: params.featureId,
128870
128876
  parentWbs: params.parentWbs,
128871
128877
  template: params.template,
128872
- explicitStatus: params.status
128878
+ explicitStatus: params.status,
128879
+ requirements: params.requirements,
128880
+ plan: params.plan
128873
128881
  });
128874
128882
  this.validateCandidateOrThrow(params.title, shape);
128875
128883
  let featureReopened;
@@ -146695,6 +146703,14 @@ function readWorkspaceGraph(cwd) {
146695
146703
  } catch {
146696
146704
  return { byName: new Map, dependents: new Map };
146697
146705
  }
146706
+ const unsupported = globs.find((glob) => glob.includes("*") && (!glob.endsWith("/*") || glob === "/*"));
146707
+ if (unsupported !== undefined) {
146708
+ return {
146709
+ byName: new Map,
146710
+ dependents: new Map,
146711
+ unsupported: `workspaces glob ${JSON.stringify(unsupported)} is not a supported shape (only \`dir/*\` globs and literal dirs expand)`
146712
+ };
146713
+ }
146698
146714
  const dirs = new Set;
146699
146715
  for (const glob of globs) {
146700
146716
  if (glob.endsWith("/*")) {
@@ -146779,7 +146795,10 @@ function lightScope(changedFiles, exists = existsSync23, graph) {
146779
146795
  }
146780
146796
  }
146781
146797
  let fallback;
146782
- if (graph !== undefined) {
146798
+ if (graph?.unsupported !== undefined) {
146799
+ fallback = `${graph.unsupported} \u2014 full suite required`;
146800
+ }
146801
+ if (fallback === undefined && graph !== undefined) {
146783
146802
  for (const file of changedFiles) {
146784
146803
  if (file.startsWith("plugins/sp/"))
146785
146804
  continue;
@@ -159910,7 +159929,7 @@ ${result.wbs} (${result.status}): ${result.pass ? "PASS" : "FAIL"}`);
159910
159929
  init_src4();
159911
159930
  init_src2();
159912
159931
  function registerTaskCreateCommand(task, context) {
159913
- task.command("create").summary("Create a new task with race-safe WBS allocation.").argument("<title>", "Task title").option(...SHARED_OPTIONS.featureTrace).option("--parent <wbs>", "Parent WBS for sub-task grouping").option("--template <variant>", `Template variant (${TASK_VARIANTS.join("|")})`).option("--no-reopen", "Do not reopen a 'verifying' parent feature to 'active'").option(...SHARED_OPTIONS.folderTasks).option("--dedupe-within <seconds>", "Override the default dedup window (seconds). Guard is on (300s) by default when --feature is set.", Number).option("--allow-duplicate-name", "Disable the dedup guard entirely (creates anyway)").option("--skip-ready", "Skip ready preparation (backlog capture only, no model dispatch)").option("--agent <selector>", "Agent used for ready preparation (defaults to the configured agent)").option(...SHARED_OPTIONS.json).option(...SHARED_OPTIONS.jsonEnvelope).action(async (title, options) => {
159932
+ task.command("create").summary("Create a new task with race-safe WBS allocation.").argument("<title>", "Task title").option(...SHARED_OPTIONS.featureTrace).option("--parent <wbs>", "Parent WBS for sub-task grouping").option("--template <variant>", `Template variant (${TASK_VARIANTS.join("|")})`).option("--requirements <text>", "Pre-filled Requirements body (normalized like batch items)").option("--plan <text>", "Pre-filled Plan body (ordered checklist)").option("--no-reopen", "Do not reopen a 'verifying' parent feature to 'active'").option(...SHARED_OPTIONS.folderTasks).option("--dedupe-within <seconds>", "Override the default dedup window (seconds). Guard is on (300s) by default when --feature is set.", Number).option("--allow-duplicate-name", "Disable the dedup guard entirely (creates anyway)").option("--skip-ready", "Skip ready preparation (backlog capture only, no model dispatch)").option("--agent <selector>", "Agent used for ready preparation (defaults to the configured agent)").option(...SHARED_OPTIONS.json).option(...SHARED_OPTIONS.jsonEnvelope).action(async (title, options) => {
159914
159933
  if (options.template !== undefined && !TASK_VARIANTS.includes(options.template)) {
159915
159934
  writeCreateJsonError(context, options, "invalid-usage", `Unknown template variant "${options.template}". Valid: ${TASK_VARIANTS.join(", ")}`);
159916
159935
  context.setExitCode(2);
@@ -159933,6 +159952,8 @@ function registerTaskCreateCommand(task, context) {
159933
159952
  featureId: options.feature,
159934
159953
  parentWbs: options.parent,
159935
159954
  template: options.template,
159955
+ requirements: options.requirements,
159956
+ plan: options.plan,
159936
159957
  noReopen: options.reopen === false,
159937
159958
  dedupeWithinSec: options.allowDuplicateName ? null : options.dedupeWithin
159938
159959
  });
@@ -160766,6 +160787,11 @@ function parseCsvSections(csv) {
160766
160787
  function registerTaskShowCommand(task, context) {
160767
160788
  task.command("show").alias("get").summary("Show a task by WBS.").argument("<wbs>", "Task WBS number").option(...SHARED_OPTIONS.folderTasks).option(...SHARED_OPTIONS.sectionShowFilter, collectSection, []).option("--sections <csv>", 'Comma-separated section names to show (e.g. "Background,Plan"); combined with --section').option("--summary", "Print frontmatter metadata plus a section index (name, heading, byte count) and total file size \u2014 no section bodies", false).option(...SHARED_OPTIONS.json).option(...SHARED_OPTIONS.jsonEnvelope).action(async (wbs, options) => {
160768
160789
  const requested = [...options.section ?? [], ...parseCsvSections(options.sections)];
160790
+ if (options.sections !== undefined && parseCsvSections(options.sections).length === 0) {
160791
+ writeJsonError(context.output, options, `--sections "${options.sections}" carries no usable section names`, "VALIDATION_FAILED");
160792
+ context.setExitCode(2);
160793
+ return;
160794
+ }
160769
160795
  if (options.summary === true && requested.length > 0) {
160770
160796
  writeJsonError(context.output, options, "--summary cannot be combined with --section/--sections", "VALIDATION_FAILED");
160771
160797
  context.setExitCode(2);
@@ -166039,7 +166065,7 @@ init_src2();
166039
166065
  var CLI_CONFIG = {
166040
166066
  binaryName: "spur",
166041
166067
  binaryLabel: "spur",
166042
- binaryVersion: "0.4.31",
166068
+ binaryVersion: "0.4.33",
166043
166069
  configDir: ".spur",
166044
166070
  configFile: ".spur/config.yaml",
166045
166071
  databaseFile: ".spur/spur.db"
@@ -173778,7 +173804,7 @@ init_dist5();
173778
173804
  import { homedir as homedir17 } from "os";
173779
173805
  import { basename as basename23, join as join95, relative as relative19, resolve as resolve37 } from "path";
173780
173806
  // ../server/package.json
173781
- var version4 = "0.4.31";
173807
+ var version4 = "0.4.33";
173782
173808
 
173783
173809
  // ../server/src/modules/health/index.ts
173784
173810
  var startedAt = Date.now();