@gobing-ai/spur 0.3.85 → 0.3.86

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 (106) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/pipeline-budgets.json +0 -25
  3. package/config/plugin-scripts.json +0 -5
  4. package/config/rules/boundary/env-var-hygiene.yaml +41 -0
  5. package/config/transition-shims.json +1 -1
  6. package/config/workflow-candidates.json +41 -0
  7. package/config/workflows/feature-lifecycle.yaml +4 -2
  8. package/config/workflows/feature-verification.yaml +70 -0
  9. package/config/workflows/idea-pipeline.yaml +24 -12
  10. package/config/workflows/task-pipeline.yaml +43 -12
  11. package/config/workflows/wrapup-pipeline.yaml +70 -7
  12. package/package.json +9 -9
  13. package/plugins/sp/README.md +0 -3
  14. package/plugins/sp/hooks/agent-hint.ts +5 -4
  15. package/plugins/sp/hooks/careful-guard.ts +3 -1
  16. package/plugins/sp/hooks/context-post-tool.ts +2 -1
  17. package/plugins/sp/hooks/context-session-start.ts +4 -3
  18. package/plugins/sp/hooks/context-session-stop.ts +2 -1
  19. package/plugins/sp/hooks/pi/guard-extension.ts +4 -3
  20. package/plugins/sp/hooks/task-write-guard.ts +4 -3
  21. package/plugins/sp/lib/idea-handoff.generated.mjs +260 -260
  22. package/plugins/sp/plugin.json +1 -1
  23. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +13 -4
  24. package/plugins/sp/scripts/daily-summary/daily-summary.ts +6 -4
  25. package/plugins/sp/scripts/feature-sync-bounded.mjs +10 -2
  26. package/plugins/sp/scripts/feature-sync-bounded.ts +2 -1
  27. package/plugins/sp/scripts/idea-handoff.mjs +6 -1
  28. package/plugins/sp/scripts/idea-handoff.ts +4 -1
  29. package/plugins/sp/scripts/inline-pipeline-parity-check.ts +1 -1
  30. package/plugins/sp/scripts/inline-run-setup.ts +185 -2
  31. package/plugins/sp/scripts/pr-reviewing.mjs +8 -1
  32. package/plugins/sp/scripts/pr-reviewing.ts +2 -1
  33. package/plugins/sp/scripts/quality-gate.mjs +8 -1
  34. package/plugins/sp/scripts/quality-gate.ts +2 -1
  35. package/plugins/sp/scripts/surface-drift-inventory.ts +1 -4
  36. package/plugins/sp/scripts/task-evidence-precheck.ts +2 -1
  37. package/plugins/sp/scripts/task-size-precheck.ts +6 -4
  38. package/plugins/sp/scripts/verify-answer-lint.ts +2 -1
  39. package/plugins/sp/scripts/workflow-step-profile.mjs +10 -2
  40. package/plugins/sp/scripts/workflow-step-profile.ts +2 -1
  41. package/plugins/sp/scripts/wrapup-steps.mjs +8 -1
  42. package/plugins/sp/scripts/wrapup-steps.ts +2 -1
  43. package/plugins/sp/skills/spur-cli/references/workflows.md +8 -7
  44. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +4 -5
  45. package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +1 -1
  46. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +2 -2
  47. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +59 -1
  48. package/schemas/state-machine-workflow.schema.json +5 -0
  49. package/schemas/transition-flow-workflow.schema.json +5 -0
  50. package/spur.js +20988 -20652
  51. package/web/_astro/BoardApp.CJiqp5pS.js +1 -0
  52. package/web/_astro/{BoardApp.yW425dRZ.js → BoardApp.yBBcFXWP.js} +4 -4
  53. package/web/_astro/{TaskDetail.CGwJAinW.js → TaskDetail.DqFJbRFc.js} +1 -1
  54. package/web/_astro/{arc.B-qNXzSO.js → arc.DL-BpHoi.js} +1 -1
  55. package/web/_astro/{architectureDiagram-3BPJPVTR.Bdg-xiji.js → architectureDiagram-3BPJPVTR.9IdQYyDq.js} +1 -1
  56. package/web/_astro/{blockDiagram-GPEHLZMM.DFArg3Kp.js → blockDiagram-GPEHLZMM.BKsFCqTl.js} +1 -1
  57. package/web/_astro/{c4Diagram-AAUBKEIU.BkOLcjyx.js → c4Diagram-AAUBKEIU.DhwI0dh1.js} +1 -1
  58. package/web/_astro/channel.CX5453qQ.js +1 -0
  59. package/web/_astro/{chunk-2J33WTMH.DN3c20V4.js → chunk-2J33WTMH.B3QVmQ9S.js} +1 -1
  60. package/web/_astro/{chunk-4BX2VUAB.B64K0Ttd.js → chunk-4BX2VUAB.DBSuqs9F.js} +1 -1
  61. package/web/_astro/{chunk-55IACEB6.C-AJvNwa.js → chunk-55IACEB6.BSYTWAYD.js} +1 -1
  62. package/web/_astro/{chunk-727SXJPM.DMkr46uS.js → chunk-727SXJPM.cWVuxXfS.js} +1 -1
  63. package/web/_astro/{chunk-AQP2D5EJ.4_WGbI64.js → chunk-AQP2D5EJ.DpU_Ob3d.js} +1 -1
  64. package/web/_astro/{chunk-FMBD7UC4.cbFW_lVE.js → chunk-FMBD7UC4.BykFkyji.js} +1 -1
  65. package/web/_astro/{chunk-ND2GUHAM.CDW_-2O1.js → chunk-ND2GUHAM.DwgHlMdY.js} +1 -1
  66. package/web/_astro/{chunk-QZHKN3VN.BoYOt_yg.js → chunk-QZHKN3VN.CusXUGWM.js} +1 -1
  67. package/web/_astro/{classDiagram-4FO5ZUOK.BNT6XpHQ.js → classDiagram-4FO5ZUOK.fx0ObzkN.js} +1 -1
  68. package/web/_astro/{classDiagram-v2-Q7XG4LA2.BNT6XpHQ.js → classDiagram-v2-Q7XG4LA2.fx0ObzkN.js} +1 -1
  69. package/web/_astro/{cose-bilkent-S5V4N54A.CI1DQxR3.js → cose-bilkent-S5V4N54A.Z4HgOlsd.js} +1 -1
  70. package/web/_astro/{cynefin-OW5HDTMX.A8ARH61p.js → cynefin-OW5HDTMX.B5dIZHJu.js} +1 -1
  71. package/web/_astro/{dagre-BM42HDAG.u28ZmqwR.js → dagre-BM42HDAG.DT70Q_Yw.js} +1 -1
  72. package/web/_astro/{diagram-2AECGRRQ.Dx8JcrGc.js → diagram-2AECGRRQ.DqHA3XBF.js} +1 -1
  73. package/web/_astro/{diagram-5GNKFQAL.BXJUG9GY.js → diagram-5GNKFQAL.BmCem957.js} +1 -1
  74. package/web/_astro/{diagram-KO2AKTUF.BtlMuWqW.js → diagram-KO2AKTUF.sn0-hrE0.js} +1 -1
  75. package/web/_astro/{diagram-LMA3HP47.C4T7qaAy.js → diagram-LMA3HP47.BSHe9tVc.js} +1 -1
  76. package/web/_astro/{diagram-OG6HWLK6.D5HECE10.js → diagram-OG6HWLK6.DHIc-86k.js} +1 -1
  77. package/web/_astro/{erDiagram-TEJ5UH35.B_hMR3yw.js → erDiagram-TEJ5UH35.Bxayrs7v.js} +1 -1
  78. package/web/_astro/{flowDiagram-I6XJVG4X.BAmpcYl3.js → flowDiagram-I6XJVG4X.BkzoE_5I.js} +1 -1
  79. package/web/_astro/{ganttDiagram-6RSMTGT7.DV1bSWK-.js → ganttDiagram-6RSMTGT7.okT6CvTo.js} +1 -1
  80. package/web/_astro/{gitGraphDiagram-PVQCEYII.DxYKkKrJ.js → gitGraphDiagram-PVQCEYII.CJuYbhC7.js} +1 -1
  81. package/web/_astro/{infoDiagram-5YYISTIA.BAWY2xMb.js → infoDiagram-5YYISTIA.RqLy7nBo.js} +1 -1
  82. package/web/_astro/{ishikawaDiagram-YF4QCWOH.CZssRn6V.js → ishikawaDiagram-YF4QCWOH.BwIcoagw.js} +1 -1
  83. package/web/_astro/{journeyDiagram-JHISSGLW.C--muARd.js → journeyDiagram-JHISSGLW.UB1VbWtH.js} +1 -1
  84. package/web/_astro/{kanban-definition-UN3LZRKU.D_QCK4et.js → kanban-definition-UN3LZRKU.AaxMKpTk.js} +1 -1
  85. package/web/_astro/{linear.DzrmTtZ0.js → linear.Nv_xOUjP.js} +1 -1
  86. package/web/_astro/{mermaid.core.Da03W3iu.js → mermaid.core.Bc4LqQgX.js} +4 -4
  87. package/web/_astro/{mindmap-definition-RKZ34NQL.6cy-8hR_.js → mindmap-definition-RKZ34NQL.oKUvU_qi.js} +1 -1
  88. package/web/_astro/{pieDiagram-4H26LBE5.C5rS1pdU.js → pieDiagram-4H26LBE5.DQk0oo03.js} +1 -1
  89. package/web/_astro/{quadrantDiagram-W4KKPZXB.w56GZZ6Q.js → quadrantDiagram-W4KKPZXB.BdDjESDa.js} +1 -1
  90. package/web/_astro/{requirementDiagram-4Y6WPE33.CipX3Pwu.js → requirementDiagram-4Y6WPE33.C2u9hUeH.js} +1 -1
  91. package/web/_astro/{sankeyDiagram-5OEKKPKP.C0VVzgJm.js → sankeyDiagram-5OEKKPKP.CDEoiJST.js} +1 -1
  92. package/web/_astro/{sequenceDiagram-3UESZ5HK.BX2dUUbF.js → sequenceDiagram-3UESZ5HK.D_hT_GAT.js} +1 -1
  93. package/web/_astro/{stateDiagram-AJRCARHV.ypCdgODQ.js → stateDiagram-AJRCARHV.DI8RYG0b.js} +1 -1
  94. package/web/_astro/{stateDiagram-v2-BHNVJYJU.In0baEtg.js → stateDiagram-v2-BHNVJYJU.Bkxz4DnP.js} +1 -1
  95. package/web/_astro/{timeline-definition-PNZ67QCA.CJN4Vkvl.js → timeline-definition-PNZ67QCA.DSY-kH3-.js} +1 -1
  96. package/web/_astro/{vennDiagram-CIIHVFJN.Cf8KkIPY.js → vennDiagram-CIIHVFJN.CpaDtuGr.js} +1 -1
  97. package/web/_astro/{wardleyDiagram-YWT4CUSO.DCZBo9xw.js → wardleyDiagram-YWT4CUSO.DujQWvo8.js} +1 -1
  98. package/web/_astro/{xychartDiagram-2RQKCTM6.Cm4v_MiJ.js → xychartDiagram-2RQKCTM6.DcM5Y4b9.js} +1 -1
  99. package/web/index.html +1 -1
  100. package/config/workflows/basic.yaml +0 -146
  101. package/config/workflows/docs-pipeline.yaml +0 -350
  102. package/config/workflows/feature-dev.yaml +0 -288
  103. package/plugins/sp/scripts/feature-dev-precheck.mjs +0 -146
  104. package/plugins/sp/scripts/feature-dev-precheck.ts +0 -238
  105. package/web/_astro/BoardApp.CHenHFia.js +0 -1
  106. package/web/_astro/channel.CbDHK5UQ.js +0 -1
@@ -620,14 +620,11 @@ pipeline owns one lifecycle phase:
620
620
 
621
621
  | Workflow | Phase | Entry command |
622
622
  | --------------------------- | --------------------------------- | --------------------------------- |
623
- | `basic.yaml` | Generic implement/check/fix | direct `spur workflow run` |
624
623
  | `feature-lifecycle.yaml` | Feature status FSM | `spur feature update` |
625
624
  | `task-lifecycle.yaml` | Task status FSM | `spur task update` |
626
625
  | `task-pipeline.yaml` | Single-task execution | `/sp:dev-run` |
627
626
  | `idea-pipeline.yaml` | Idea/planning → feature + tasks | `/sp:dev-idea`, `/sp:dev-plan` |
628
- | `feature-dev.yaml` | Feature umbrella execution | `/sp:dev-runall --feature` |
629
627
  | `wrapup-pipeline.yaml` | Post-execution wrap-up | `/sp:dev-wrap`, `/sp:dev-wrapall` |
630
- | `docs-pipeline.yaml` | Docs-only task execution | `/sp:dev-run --mode implement` |
631
628
  | `wayfinder-resolution.yaml` | Wayfinder ticket resolution loop | `spur workflow run` (free-form) |
632
629
 
633
630
  ### Lifecycle operations
@@ -1,3 +1,4 @@
1
+ import { getEnvVars } from '@gobing-ai/ts-utils';
1
2
  /**
2
3
  * Shared host-agent identification helpers — the **only** host-agent detection path
3
4
  * in the sp plugin hooks.
@@ -22,11 +23,11 @@
22
23
  * 3. `TERM_PROGRAM` — terminal identifier (e.g. `claude`, `ghostty`)
23
24
  * 4. `SPUR_DEFAULT_AGENT` — configured default agent
24
25
  *
25
- * @param env - environment record (defaults to `process.env`)
26
+ * @param env - environment record (defaults to `getEnvVars()`)
26
27
  * @param fallback - value returned when no candidate matches (defaults to `undefined`)
27
28
  */
28
29
  export function resolveAgentHint(
29
- env: NodeJS.ProcessEnv = process.env,
30
+ env: NodeJS.ProcessEnv = getEnvVars(),
30
31
  fallback: string | undefined = undefined,
31
32
  ): string | undefined {
32
33
  const candidates = [env.SPUR_AGENT, env.CLAUDE_CODE_ENTRYPOINT, env.TERM_PROGRAM, env.SPUR_DEFAULT_AGENT];
@@ -45,11 +46,11 @@ export function resolveAgentHint(
45
46
  * 3. `OPENAI_MODEL` — OpenAI API model
46
47
  * 4. `CLAUDE_MODEL` — Claude Code model setting
47
48
  *
48
- * @param env - environment record (defaults to `process.env`)
49
+ * @param env - environment record (defaults to `getEnvVars()`)
49
50
  * @param fallback - value returned when no candidate matches (defaults to `undefined`)
50
51
  */
51
52
  export function resolveModelHint(
52
- env: NodeJS.ProcessEnv = process.env,
53
+ env: NodeJS.ProcessEnv = getEnvVars(),
53
54
  fallback: string | undefined = undefined,
54
55
  ): string | undefined {
55
56
  const candidates = [env.SPUR_MODEL, env.ANTHROPIC_MODEL, env.OPENAI_MODEL, env.CLAUDE_MODEL];
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env bun
2
+
2
3
  /**
3
4
  * careful-guard — PreToolUse guard for destructive shell commands (task 0215, R3).
4
5
  *
@@ -21,6 +22,7 @@
21
22
  * adapter. This file owns the Claude Code payload/decision shape only.
22
23
  */
23
24
 
25
+ import { getEnvVar } from '@gobing-ai/ts-utils';
24
26
  import { classifyCommand } from './destructive-policy';
25
27
 
26
28
  interface ToolPayload {
@@ -43,7 +45,7 @@ function preToolUseDecision(decision: Decision, reason?: string): never {
43
45
  }
44
46
 
45
47
  async function main(): Promise<void> {
46
- if (process.env.SPUR_CAREFUL === 'off') preToolUseDecision('allow');
48
+ if (getEnvVar('SPUR_CAREFUL') === 'off') preToolUseDecision('allow');
47
49
 
48
50
  const stdinText = await Bun.stdin.text();
49
51
  let payload: ToolPayload;
@@ -23,6 +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
27
 
27
28
  /** Tools recorded by this hook (must match hooks.json PostToolUse matcher). */
28
29
  export const ALLOWED_TOOLS = new Set(['Bash', 'Grep', 'Glob', 'Read', 'Write', 'Edit']);
@@ -411,7 +412,7 @@ export function readContextFreshness(contextDir: string): string | null {
411
412
  // Entrypoint — thin wrapper; logic lives in {@link recordToolUseEvent} for unit coverage.
412
413
  if (import.meta.main) {
413
414
  void (async () => {
414
- const dir = join(process.env.CLAUDE_PROJECT_DIR ?? process.cwd(), '.spur', 'context');
415
+ const dir = join(getEnvVar('CLAUDE_PROJECT_DIR') ?? process.cwd(), '.spur', 'context');
415
416
  try {
416
417
  const stdinText = await Bun.stdin.text();
417
418
  const payload = JSON.parse(stdinText) as ToolPayload;
@@ -15,6 +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
19
  import { resolveAgentHint, resolveModelHint } from './agent-hint';
19
20
  import { checkContextFreshness } from './context-post-tool';
20
21
 
@@ -82,7 +83,7 @@ interface SessionFileBody {
82
83
  * a stale file is only reachable from inside a live agent run, where reusing it is the correct
83
84
  * answer anyway.
84
85
  */
85
- export function resolveActiveSession(dir: string, now: Date, env: NodeJS.ProcessEnv = process.env): string | null {
86
+ export function resolveActiveSession(dir: string, now: Date, env: NodeJS.ProcessEnv = getEnvVars()): string | null {
86
87
  let raw: string;
87
88
  try {
88
89
  raw = readFileSync(join(dir, '.session.json'), 'utf-8');
@@ -123,7 +124,7 @@ export function resolveActiveSession(dir: string, now: Date, env: NodeJS.Process
123
124
  */
124
125
  export function recordSessionStart(
125
126
  dir: string,
126
- env: NodeJS.ProcessEnv = process.env,
127
+ env: NodeJS.ProcessEnv = getEnvVars(),
127
128
  now: () => Date = () => new Date(),
128
129
  ): string | null {
129
130
  try {
@@ -195,7 +196,7 @@ export function recordSessionStart(
195
196
  // Entrypoint — kept minimal so unit coverage focuses on pure helpers above.
196
197
  if (import.meta.main) {
197
198
  try {
198
- recordSessionStart(join(process.env.CLAUDE_PROJECT_DIR ?? process.cwd(), '.spur', 'context'));
199
+ recordSessionStart(join(getEnvVar('CLAUDE_PROJECT_DIR') ?? process.cwd(), '.spur', 'context'));
199
200
  } catch {
200
201
  /* fail-open */
201
202
  }
@@ -17,6 +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
21
 
21
22
  interface LedgerEvent {
22
23
  session: string;
@@ -53,7 +54,7 @@ function exitOk(): never {
53
54
  }
54
55
 
55
56
  async function main(): Promise<void> {
56
- const dir = join(process.env.CLAUDE_PROJECT_DIR ?? process.cwd(), '.spur', 'context');
57
+ const dir = join(getEnvVar('CLAUDE_PROJECT_DIR') ?? process.cwd(), '.spur', 'context');
57
58
 
58
59
  const sessionFile = join(dir, '.session.json');
59
60
  if (!existsSync(sessionFile)) exitOk();
@@ -23,6 +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
27
  import { resolveAgentHint as resolveAgentHintShared, resolveModelHint as resolveModelHintShared } from '../agent-hint';
27
28
  import { classifyCommand } from '../destructive-policy';
28
29
  import { couldBeTaskFile } from '../task-file-policy';
@@ -81,7 +82,7 @@ function resolveSpurTaskOwnership(filePath: string): TaskOwnership {
81
82
  spawnSync(cmd, args, { cwd: process.cwd(), encoding: 'utf-8', timeout: 8000 });
82
83
 
83
84
  // 1. SPUR_BIN env override (may include args) or `spur` on PATH
84
- const envBin = process.env.SPUR_BIN || 'spur';
85
+ const envBin = getEnvVar('SPUR_BIN') || 'spur';
85
86
  const envParts = envBin.split(' ');
86
87
  let res = run(envParts[0] ?? 'spur', [...envParts.slice(1), 'task', 'resolve', filePath, '--strict', '--json']);
87
88
  // Only 0 (owned) / 1 (unowned) are valid spur exit codes; 127 (interpreter
@@ -203,8 +204,8 @@ function initSession(): void {
203
204
  const sessionId = generateSessionId();
204
205
  const session = {
205
206
  session_id: sessionId,
206
- agent: resolveAgentHintShared(process.env, 'pi'),
207
- model: resolveModelHintShared(process.env),
207
+ agent: resolveAgentHintShared(getEnvVars(), 'pi'),
208
+ model: resolveModelHintShared(getEnvVars()),
208
209
  started_at: new Date().toISOString(),
209
210
  };
210
211
  writeFileSync(sessionFilePath(), `${JSON.stringify(session, null, 2)}\n`);
@@ -20,6 +20,7 @@
20
20
  */
21
21
 
22
22
  import { spawnSync } from 'node:child_process';
23
+ import { getEnvVar } from '@gobing-ai/ts-utils';
23
24
  import { couldBeTaskFile } from './task-file-policy';
24
25
 
25
26
  interface ToolPayload {
@@ -40,7 +41,7 @@ function preToolUseDecision(decision: 'allow' | 'deny', reason?: string): never
40
41
 
41
42
  /** Resolve whether a path is owned by a Spur task via `spur task resolve --strict --json`. */
42
43
  function resolveSpurTaskOwnership(filePath: string, cwd: string): TaskOwnership {
43
- const spurBin = process.env.SPUR_BIN || 'spur';
44
+ const spurBin = getEnvVar('SPUR_BIN') || 'spur';
44
45
  const parts = spurBin.split(' ');
45
46
  const cmd = parts[0] ?? 'spur';
46
47
  const args = [...parts.slice(1), 'task', 'resolve', filePath, '--strict', '--json'];
@@ -54,7 +55,7 @@ function resolveSpurTaskOwnership(filePath: string, cwd: string): TaskOwnership
54
55
  }
55
56
 
56
57
  async function main(): Promise<void> {
57
- if (process.env.SPUR_WRITE_GUARD === 'off') preToolUseDecision('allow');
58
+ if (getEnvVar('SPUR_WRITE_GUARD') === 'off') preToolUseDecision('allow');
58
59
 
59
60
  const stdinText = await Bun.stdin.text();
60
61
  let payload: ToolPayload;
@@ -73,7 +74,7 @@ async function main(): Promise<void> {
73
74
  // cannot name a task file — that is every ordinary source edit.
74
75
  if (!couldBeTaskFile(filePath)) preToolUseDecision('allow');
75
76
 
76
- const ownership = resolveSpurTaskOwnership(filePath, process.env.CLAUDE_PROJECT_DIR ?? process.cwd());
77
+ const ownership = resolveSpurTaskOwnership(filePath, getEnvVar('CLAUDE_PROJECT_DIR') ?? process.cwd());
77
78
  if (ownership === 'owned') {
78
79
  preToolUseDecision(
79
80
  'deny',