@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/config/rules/boundary/env-var-hygiene.yaml +13 -5
- package/config/rules/boundary/sp-plugin-standalone.yaml +37 -0
- package/package.json +9 -9
- package/plugins/sp/hooks/agent-hint.ts +1 -1
- package/plugins/sp/hooks/careful-guard.ts +1 -1
- package/plugins/sp/hooks/context-post-tool.ts +1 -1
- package/plugins/sp/hooks/context-session-start.ts +1 -1
- package/plugins/sp/hooks/context-session-stop.ts +1 -1
- package/plugins/sp/hooks/pi/guard-extension.ts +1 -1
- package/plugins/sp/hooks/task-write-guard.ts +1 -1
- package/plugins/sp/lib/env.ts +46 -0
- package/plugins/sp/lib/idea-handoff.generated.mjs +157 -150
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +1 -1
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +1 -1
- package/plugins/sp/scripts/feature-sync-bounded.mjs +1 -1
- package/plugins/sp/scripts/feature-sync-bounded.ts +1 -1
- package/plugins/sp/scripts/idea-handoff.mjs +1 -1
- package/plugins/sp/scripts/idea-handoff.ts +1 -1
- package/plugins/sp/scripts/inline-run-setup.ts +1 -1
- package/plugins/sp/scripts/pr-reviewing.mjs +1 -1
- package/plugins/sp/scripts/pr-reviewing.ts +1 -1
- package/plugins/sp/scripts/quality-gate.mjs +1 -1
- package/plugins/sp/scripts/quality-gate.ts +1 -1
- package/plugins/sp/scripts/script-contract-check.ts +58 -0
- package/plugins/sp/scripts/task-evidence-precheck.ts +1 -1
- package/plugins/sp/scripts/task-size-precheck.ts +1 -1
- package/plugins/sp/scripts/verify-answer-lint.ts +1 -1
- package/plugins/sp/scripts/workflow-step-profile.mjs +1 -1
- package/plugins/sp/scripts/workflow-step-profile.ts +1 -1
- package/plugins/sp/scripts/wrapup-steps.mjs +1 -1
- package/plugins/sp/scripts/wrapup-steps.ts +1 -1
- package/plugins/sp/skills/spur-cli/references/workflows.md +8 -3
- package/spur.js +541 -246
|
@@ -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
|
|
13
|
-
#
|
|
14
|
-
#
|
|
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.
|
|
21
|
-
#
|
|
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.
|
|
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.
|
|
57
|
-
"@gobing-ai/ts-ai-runner": "^0.
|
|
58
|
-
"@gobing-ai/ts-dual-workflow-engine": "^0.
|
|
59
|
-
"@gobing-ai/ts-infra": "^0.
|
|
60
|
-
"@gobing-ai/ts-llm-jsonl-importer": "^0.
|
|
61
|
-
"@gobing-ai/ts-rule-engine": "^0.
|
|
62
|
-
"@gobing-ai/ts-runtime": "^0.
|
|
63
|
-
"@gobing-ai/ts-utils": "^0.
|
|
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",
|
|
@@ -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 '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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 '
|
|
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';
|
|
@@ -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
|
+
}
|