@gobing-ai/spur 0.3.88 → 0.3.90

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 (35) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/rules/boundary/env-var-hygiene.yaml +13 -5
  3. package/config/rules/boundary/sp-plugin-standalone.yaml +37 -0
  4. package/package.json +9 -9
  5. package/plugins/sp/hooks/agent-hint.ts +1 -1
  6. package/plugins/sp/hooks/careful-guard.ts +1 -1
  7. package/plugins/sp/hooks/context-post-tool.ts +1 -1
  8. package/plugins/sp/hooks/context-session-start.ts +1 -1
  9. package/plugins/sp/hooks/context-session-stop.ts +1 -1
  10. package/plugins/sp/hooks/pi/guard-extension.ts +1 -1
  11. package/plugins/sp/hooks/task-write-guard.ts +1 -1
  12. package/plugins/sp/lib/env.ts +46 -0
  13. package/plugins/sp/lib/idea-handoff.generated.mjs +157 -150
  14. package/plugins/sp/plugin.json +1 -1
  15. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +1 -1
  16. package/plugins/sp/scripts/daily-summary/daily-summary.ts +1 -1
  17. package/plugins/sp/scripts/feature-sync-bounded.mjs +1 -1
  18. package/plugins/sp/scripts/feature-sync-bounded.ts +1 -1
  19. package/plugins/sp/scripts/idea-handoff.mjs +1 -1
  20. package/plugins/sp/scripts/idea-handoff.ts +1 -1
  21. package/plugins/sp/scripts/inline-run-setup.ts +1 -1
  22. package/plugins/sp/scripts/pr-reviewing.mjs +1 -1
  23. package/plugins/sp/scripts/pr-reviewing.ts +1 -1
  24. package/plugins/sp/scripts/quality-gate.mjs +1 -1
  25. package/plugins/sp/scripts/quality-gate.ts +1 -1
  26. package/plugins/sp/scripts/script-contract-check.ts +58 -0
  27. package/plugins/sp/scripts/task-evidence-precheck.ts +1 -1
  28. package/plugins/sp/scripts/task-size-precheck.ts +1 -1
  29. package/plugins/sp/scripts/verify-answer-lint.ts +1 -1
  30. package/plugins/sp/scripts/workflow-step-profile.mjs +1 -1
  31. package/plugins/sp/scripts/workflow-step-profile.ts +1 -1
  32. package/plugins/sp/scripts/wrapup-steps.mjs +1 -1
  33. package/plugins/sp/scripts/wrapup-steps.ts +1 -1
  34. package/plugins/sp/skills/spur-cli/references/workflows.md +8 -3
  35. package/spur.js +541 -246
@@ -7,7 +7,7 @@
7
7
  "plugins": [
8
8
  {
9
9
  "name": "sp",
10
- "version": "0.3.88",
10
+ "version": "0.3.90",
11
11
  "source": "./plugins/sp"
12
12
  }
13
13
  ]
@@ -9,16 +9,18 @@ $schema: "@gobing-ai/spur/schemas/rule-file.schema.json"
9
9
  # Env flows into the app as typed config or injected `env` records (ADR-027). Runtime
10
10
  # options belong in `.spur/config.yaml` `bootstrap.options` via `getAppOptions`;
11
11
  # dev-only knobs belong in consts.
12
- # - Plugin scripts/hooks import the same functions from `@gobing-ai/ts-utils`; the
13
- # `superskill script convert` `.mjs` twins bundle them in (self-contained on any
14
- # install target).
12
+ # - Plugin scripts/hooks import `plugins/sp/lib/env.ts` — the vendored standalone
13
+ # gateway — because `superskill install` bundles them on targets with no
14
+ # node_modules, where any `@gobing-ai/*` import fails ("Bundle failed", task
15
+ # 0669). It mirrors the upstream semantics and is the one exempt file.
16
+ # The `superskill script convert` `.mjs` twins remain self-contained by inlining.
15
17
  # - `apps/web` uses `import.meta.env` (Vite browser plane) — not process env, never matches.
16
18
  # - Which variables exist and who consumes them is documented in `.env.example`.
17
19
  # (The former per-var lease table moved there; the rule now enforces the funnel,
18
20
  # not a file-by-var inventory.)
19
21
  #
20
- # Scope: apps/**, packages/**, scripts/**, plugins/sp/** — src AND tests. No exclusions:
21
- # even the gateway package itself is free of direct access.
22
+ # Scope: apps/**, packages/**, scripts/**, plugins/sp/** — src AND tests. One exclusion:
23
+ # the plugin's vendored gateway, whose sole job is direct `process.env` access.
22
24
  include:
23
25
  - "apps/**/src/**/*.{ts,tsx}"
24
26
  - "apps/**/tests/**/*.{ts,tsx}"
@@ -26,6 +28,12 @@ include:
26
28
  - "packages/**/tests/**/*.{ts,tsx}"
27
29
  - "scripts/**/*.ts"
28
30
  - "plugins/sp/**/*.ts"
31
+ exclude:
32
+ # plugins/sp/lib/env.ts is the vendored standalone env gateway for plugin
33
+ # hooks/scripts (bundled by superskill onto targets without node_modules);
34
+ # direct process.env access is its entire purpose. Mirrors the upstream
35
+ # ts-utils gateway — keep in sync (verified 2026-09).
36
+ - "plugins/sp/lib/env.ts"
29
37
  rules:
30
38
  - id: env-var-hygiene
31
39
  description: >
@@ -0,0 +1,37 @@
1
+ $schema: "@gobing-ai/spur/schemas/rule-file.schema.json"
2
+ # Plugin standalone contract — bundled plugin surfaces import only node:* or relative paths.
3
+ #
4
+ # WHY: `superskill install` bundles plugins/sp hooks and scripts on targets (and from
5
+ # marketplace snapshots) with NO node_modules. A bare `@gobing-ai/*` value import only
6
+ # converts if the host superskill happens to carry that package in its private dep tree
7
+ # (superskill 0.3.28+ bundles ts-utils/ts-runtime; older versions do not). When it does
8
+ # not: "Error: Bundle failed" and the plugin cannot install — this broke releases
9
+ # 0.3.81–0.3.88 (task 0669; regression commit 84c87a9ac).
10
+ #
11
+ # Contract:
12
+ # - plugins/sp/hooks/**, plugins/sp/scripts/**, plugins/sp/lib/** may value-import
13
+ # only `node:*` builtins and relative paths (vendored helpers live in plugins/sp/lib/).
14
+ # - `import type` / `export type` from any package is fine — erased before bundling
15
+ # (e.g. inline-run-setup.ts's type-only import from @gobing-ai/app).
16
+ # - This rule is the author-time tripwire over DIRECT imports; the transitive closure
17
+ # and the real `superskill install` path are checked by `bun run plugin-smoke`, and
18
+ # script-contract-check Rule 5 (manifest-aware) fails the spur-check gate.
19
+ #
20
+ # Scope: plugins/sp bundled surfaces only. apps/packages follow the normal workspace
21
+ # import rules and are not affected.
22
+ include:
23
+ - "plugins/sp/hooks/**/*.ts"
24
+ - "plugins/sp/scripts/**/*.ts"
25
+ - "plugins/sp/lib/**/*.ts"
26
+ rules:
27
+ - id: sp-plugin-standalone
28
+ description: >
29
+ No value imports of workspace packages (`@gobing-ai/*` or any bare specifier)
30
+ in plugin bundled surfaces — superskill bundles them onto targets with no
31
+ node_modules. Vendor the helper into plugins/sp/lib/ (see lib/env.ts) or use a
32
+ relative import. `import type` is exempt.
33
+ severity: error
34
+ evaluator:
35
+ type: rg
36
+ config:
37
+ pattern: "^import \\{[^}]*\\} from ['\\\"]@gobing-ai/|^import ['\\\"]@gobing-ai/|^import\\(['\\\"]@gobing-ai/|^} from ['\\\"]@gobing-ai/"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/spur",
3
- "version": "0.3.88",
3
+ "version": "0.3.90",
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",
@@ -53,14 +53,14 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "@commander-js/extra-typings": "^14.0.0",
56
- "@gobing-ai/ts-db": "^0.4.69",
57
- "@gobing-ai/ts-ai-runner": "^0.4.69",
58
- "@gobing-ai/ts-dual-workflow-engine": "^0.4.69",
59
- "@gobing-ai/ts-infra": "^0.4.69",
60
- "@gobing-ai/ts-llm-jsonl-importer": "^0.4.69",
61
- "@gobing-ai/ts-rule-engine": "^0.4.69",
62
- "@gobing-ai/ts-runtime": "^0.4.69",
63
- "@gobing-ai/ts-utils": "^0.4.69",
56
+ "@gobing-ai/ts-db": "^0.5.0",
57
+ "@gobing-ai/ts-ai-runner": "^0.5.0",
58
+ "@gobing-ai/ts-dual-workflow-engine": "^0.5.0",
59
+ "@gobing-ai/ts-infra": "^0.5.0",
60
+ "@gobing-ai/ts-llm-jsonl-importer": "^0.5.0",
61
+ "@gobing-ai/ts-rule-engine": "^0.5.0",
62
+ "@gobing-ai/ts-runtime": "^0.5.0",
63
+ "@gobing-ai/ts-utils": "^0.5.0",
64
64
  "@types/bun": "1.3.14",
65
65
  "@types/figlet": "^1.7.0",
66
66
  "@types/node-notifier": "8.0.5",
@@ -1,4 +1,4 @@
1
- import { getEnvVars } from '@gobing-ai/ts-utils';
1
+ import { getEnvVars } from '../lib/env';
2
2
  /**
3
3
  * Shared host-agent identification helpers — the **only** host-agent detection path
4
4
  * in the sp plugin hooks.
@@ -22,7 +22,7 @@
22
22
  * adapter. This file owns the Claude Code payload/decision shape only.
23
23
  */
24
24
 
25
- import { getEnvVar } from '@gobing-ai/ts-utils';
25
+ import { getEnvVar } from '../lib/env';
26
26
  import { classifyCommand } from './destructive-policy';
27
27
 
28
28
  interface ToolPayload {
@@ -23,7 +23,7 @@
23
23
  import { execSync } from 'node:child_process';
24
24
  import { appendFileSync, existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
25
25
  import { join, resolve, sep } from 'node:path';
26
- import { getEnvVar } from '@gobing-ai/ts-utils';
26
+ import { getEnvVar } from '../lib/env';
27
27
 
28
28
  /** Tools recorded by this hook (must match hooks.json PostToolUse matcher). */
29
29
  export const ALLOWED_TOOLS = new Set(['Bash', 'Grep', 'Glob', 'Read', 'Write', 'Edit']);
@@ -15,7 +15,7 @@
15
15
  import { execSync } from 'node:child_process';
16
16
  import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
17
17
  import { join } from 'node:path';
18
- import { getEnvVar, getEnvVars } from '@gobing-ai/ts-utils';
18
+ import { getEnvVar, getEnvVars } from '../lib/env';
19
19
  import { resolveAgentHint, resolveModelHint } from './agent-hint';
20
20
  import { checkContextFreshness } from './context-post-tool';
21
21
 
@@ -17,7 +17,7 @@
17
17
 
18
18
  import { appendFileSync, existsSync, readFileSync, rmSync } from 'node:fs';
19
19
  import { join } from 'node:path';
20
- import { getEnvVar } from '@gobing-ai/ts-utils';
20
+ import { getEnvVar } from '../lib/env';
21
21
 
22
22
  interface LedgerEvent {
23
23
  session: string;
@@ -23,7 +23,7 @@ import { appendFileSync, existsSync, mkdirSync, readFileSync, realpathSync, rmSy
23
23
  import { homedir } from 'node:os';
24
24
  import { isAbsolute, join, resolve } from 'node:path';
25
25
  import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
26
- import { getEnvVar, getEnvVars } from '@gobing-ai/ts-utils';
26
+ import { getEnvVar, getEnvVars } from '../../lib/env';
27
27
  import { resolveAgentHint as resolveAgentHintShared, resolveModelHint as resolveModelHintShared } from '../agent-hint';
28
28
  import { classifyCommand } from '../destructive-policy';
29
29
  import { couldBeTaskFile } from '../task-file-policy';
@@ -20,7 +20,7 @@
20
20
  */
21
21
 
22
22
  import { spawnSync } from 'node:child_process';
23
- import { getEnvVar } from '@gobing-ai/ts-utils';
23
+ import { getEnvVar } from '../lib/env';
24
24
  import { couldBeTaskFile } from './task-file-policy';
25
25
 
26
26
  interface ToolPayload {
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Vendored env gateway for the sp plugin.
3
+ *
4
+ * WHY this copy exists: hooks and scripts in this plugin are bundled
5
+ * standalone by `superskill install` on targets that have no node_modules —
6
+ * any `@gobing-ai/*` import fails to resolve there and aborts the install
7
+ * ("Bundle failed", task 0669). This file mirrors the semantics of the
8
+ * canonical gateway (`@gobing-ai/ts-utils` dist/env, ts-libs) so the plugin
9
+ * stays self-contained. Keep in sync when the upstream gateway changes.
10
+ *
11
+ * These accessors are node-bun only: on `cloudflare-workers` there is no
12
+ * `process`; inject config explicitly rather than calling them (ADR-008).
13
+ */
14
+
15
+ /**
16
+ * Read one environment variable. Only an unset variable yields `fallback`;
17
+ * an empty string is a set value and is returned as-is.
18
+ */
19
+ export function getEnvVar(name: string, fallback?: string): string | undefined {
20
+ const raw = process.env[name];
21
+ return raw === undefined ? fallback : raw;
22
+ }
23
+
24
+ /**
25
+ * Read the live environment as a record. The returned object IS `process.env`
26
+ * (not a copy): whole-record operations see later mutations, which child-spawn
27
+ * composition relies on. For single variables prefer {@link getEnvVar}.
28
+ */
29
+ export function getEnvVars(): Record<string, string | undefined> {
30
+ return process.env;
31
+ }
32
+
33
+ /**
34
+ * Set one environment variable through the gateway. Passing `undefined` removes
35
+ * the key, so the save/restore idiom (`const prev = getEnvVar(k); …
36
+ * setEnvVar(k, prev)`) restores absence exactly.
37
+ */
38
+ export function setEnvVar(name: string, value: string | undefined): void {
39
+ if (value === undefined) delete process.env[name];
40
+ else process.env[name] = value;
41
+ }
42
+
43
+ /** Remove one environment variable; a no-op when the key is absent. */
44
+ export function removeEnvVar(name: string): void {
45
+ delete process.env[name];
46
+ }