pi-gsd 2.0.22 → 2.0.23

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 (52) hide show
  1. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-codebase-mapper.md +13 -13
  2. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-debugger.md +37 -37
  3. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-executor.md +8 -8
  4. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-nyquist-auditor.md +31 -31
  5. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-phase-researcher.md +85 -85
  6. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-plan-checker.md +32 -32
  7. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-planner.md +108 -108
  8. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-project-researcher.md +76 -76
  9. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-research-synthesizer.md +16 -16
  10. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-roadmapper.md +39 -39
  11. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-ui-auditor.md +24 -24
  12. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-ui-checker.md +21 -21
  13. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-ui-researcher.md +35 -35
  14. package/.gsd/harnesses/pi/get-shit-done/agents/gsd-verifier.md +30 -30
  15. package/.gsd/harnesses/pi/get-shit-done/references/continuation-format.md +11 -11
  16. package/.gsd/harnesses/pi/get-shit-done/references/ui-brand.md +1 -1
  17. package/.gsd/harnesses/pi/get-shit-done/templates/DEBUG.md +4 -4
  18. package/.gsd/harnesses/pi/get-shit-done/templates/UAT.md +1 -1
  19. package/.gsd/harnesses/pi/get-shit-done/workflows/add-phase.md +36 -68
  20. package/.gsd/harnesses/pi/get-shit-done/workflows/audit-milestone.md +3 -3
  21. package/.gsd/harnesses/pi/get-shit-done/workflows/complete-milestone.md +1 -1
  22. package/.gsd/harnesses/pi/get-shit-done/workflows/discuss-phase-assumptions.md +1 -1
  23. package/.gsd/harnesses/pi/get-shit-done/workflows/discuss-phase.md +2 -2
  24. package/.gsd/harnesses/pi/get-shit-done/workflows/discuss-phase.md.bak +2 -2
  25. package/.gsd/harnesses/pi/get-shit-done/workflows/execute-phase.md +2 -2
  26. package/.gsd/harnesses/pi/get-shit-done/workflows/execute-phase.md.bak +2 -2
  27. package/.gsd/harnesses/pi/get-shit-done/workflows/execute-plan.md +1 -1
  28. package/.gsd/harnesses/pi/get-shit-done/workflows/help.md +5 -5
  29. package/.gsd/harnesses/pi/get-shit-done/workflows/insert-phase.md +1 -1
  30. package/.gsd/harnesses/pi/get-shit-done/workflows/map-codebase.md +1 -1
  31. package/.gsd/harnesses/pi/get-shit-done/workflows/new-milestone.md +1 -1
  32. package/.gsd/harnesses/pi/get-shit-done/workflows/new-milestone.md.bak +1 -1
  33. package/.gsd/harnesses/pi/get-shit-done/workflows/new-project.md +2 -2
  34. package/.gsd/harnesses/pi/get-shit-done/workflows/new-project.md.bak +2 -2
  35. package/.gsd/harnesses/pi/get-shit-done/workflows/plan-milestone-gaps.md +1 -1
  36. package/.gsd/harnesses/pi/get-shit-done/workflows/plan-phase.md +1 -1
  37. package/.gsd/harnesses/pi/get-shit-done/workflows/plan-phase.md.bak +1 -1
  38. package/.gsd/harnesses/pi/get-shit-done/workflows/progress.md +10 -10
  39. package/.gsd/harnesses/pi/get-shit-done/workflows/resume-project.md +2 -2
  40. package/.gsd/harnesses/pi/get-shit-done/workflows/settings.md +1 -1
  41. package/.gsd/harnesses/pi/get-shit-done/workflows/transition.md +3 -3
  42. package/.gsd/harnesses/pi/get-shit-done/workflows/ui-phase.md +1 -1
  43. package/.gsd/harnesses/pi/get-shit-done/workflows/ui-review.md +1 -1
  44. package/.gsd/harnesses/pi/get-shit-done/workflows/validate-phase.md +1 -1
  45. package/.gsd/harnesses/pi/get-shit-done/workflows/verify-work.md +1 -1
  46. package/.gsd/harnesses/pi/hooks/gsd-context-monitor.js +102 -102
  47. package/.gsd/harnesses/pi/hooks/gsd-statusline.js +2 -2
  48. package/.gsd/harnesses/pi/hooks/gsd-workflow-guard.js +65 -65
  49. package/README.md +1 -1
  50. package/dist/pi-gsd-hooks.js +33 -6
  51. package/dist/pi-gsd-tools.js +87 -76
  52. package/package.json +1 -1
@@ -39,121 +39,121 @@ const stdinTimeout = setTimeout(() => process.exit(0), 10000);
39
39
  process.stdin.setEncoding('utf8');
40
40
  process.stdin.on('data', chunk => input += chunk);
41
41
  process.stdin.on('end', () => {
42
- clearTimeout(stdinTimeout);
43
- try {
44
- const data = JSON.parse(input);
45
- const sessionId = data.session_id;
42
+ clearTimeout(stdinTimeout);
43
+ try {
44
+ const data = JSON.parse(input);
45
+ const sessionId = data.session_id;
46
46
 
47
- if (!sessionId) {
48
- process.exit(0);
49
- }
47
+ if (!sessionId) {
48
+ process.exit(0);
49
+ }
50
50
 
51
- // Check if context warnings are disabled via config
52
- const cwd = data.cwd || process.cwd();
53
- const configPath = path.join(cwd, '.planning', 'config.json');
54
- if (fs.existsSync(configPath)) {
55
- try {
56
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
57
- if (config.hooks?.context_warnings === false) {
58
- process.exit(0);
51
+ // Check if context warnings are disabled via config
52
+ const cwd = data.cwd || process.cwd();
53
+ const configPath = path.join(cwd, '.planning', 'config.json');
54
+ if (fs.existsSync(configPath)) {
55
+ try {
56
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
57
+ if (config.hooks?.context_warnings === false) {
58
+ process.exit(0);
59
+ }
60
+ } catch (e) {
61
+ // Ignore config parse errors
62
+ }
59
63
  }
60
- } catch (e) {
61
- // Ignore config parse errors
62
- }
63
- }
64
64
 
65
- const tmpDir = os.tmpdir();
66
- const metricsPath = path.join(tmpDir, `claude-ctx-${sessionId}.json`);
65
+ const tmpDir = os.tmpdir();
66
+ const metricsPath = path.join(tmpDir, `claude-ctx-${sessionId}.json`);
67
67
 
68
- // If no metrics file, this is a subagent or fresh session -- exit silently
69
- if (!fs.existsSync(metricsPath)) {
70
- process.exit(0);
71
- }
68
+ // If no metrics file, this is a subagent or fresh session -- exit silently
69
+ if (!fs.existsSync(metricsPath)) {
70
+ process.exit(0);
71
+ }
72
72
 
73
- const metrics = JSON.parse(fs.readFileSync(metricsPath, 'utf8'));
74
- const now = Math.floor(Date.now() / 1000);
73
+ const metrics = JSON.parse(fs.readFileSync(metricsPath, 'utf8'));
74
+ const now = Math.floor(Date.now() / 1000);
75
75
 
76
- // Ignore stale metrics
77
- if (metrics.timestamp && (now - metrics.timestamp) > STALE_SECONDS) {
78
- process.exit(0);
79
- }
76
+ // Ignore stale metrics
77
+ if (metrics.timestamp && (now - metrics.timestamp) > STALE_SECONDS) {
78
+ process.exit(0);
79
+ }
80
80
 
81
- const remaining = metrics.remaining_percentage;
82
- const usedPct = metrics.used_pct;
81
+ const remaining = metrics.remaining_percentage;
82
+ const usedPct = metrics.used_pct;
83
83
 
84
- // No warning needed
85
- if (remaining > WARNING_THRESHOLD) {
86
- process.exit(0);
87
- }
84
+ // No warning needed
85
+ if (remaining > WARNING_THRESHOLD) {
86
+ process.exit(0);
87
+ }
88
88
 
89
- // Debounce: check if we warned recently
90
- const warnPath = path.join(tmpDir, `claude-ctx-${sessionId}-warned.json`);
91
- let warnData = { callsSinceWarn: 0, lastLevel: null };
92
- let firstWarn = true;
93
-
94
- if (fs.existsSync(warnPath)) {
95
- try {
96
- warnData = JSON.parse(fs.readFileSync(warnPath, 'utf8'));
97
- firstWarn = false;
98
- } catch (e) {
99
- // Corrupted file, reset
100
- }
101
- }
89
+ // Debounce: check if we warned recently
90
+ const warnPath = path.join(tmpDir, `claude-ctx-${sessionId}-warned.json`);
91
+ let warnData = { callsSinceWarn: 0, lastLevel: null };
92
+ let firstWarn = true;
93
+
94
+ if (fs.existsSync(warnPath)) {
95
+ try {
96
+ warnData = JSON.parse(fs.readFileSync(warnPath, 'utf8'));
97
+ firstWarn = false;
98
+ } catch (e) {
99
+ // Corrupted file, reset
100
+ }
101
+ }
102
102
 
103
- warnData.callsSinceWarn = (warnData.callsSinceWarn || 0) + 1;
103
+ warnData.callsSinceWarn = (warnData.callsSinceWarn || 0) + 1;
104
104
 
105
- const isCritical = remaining <= CRITICAL_THRESHOLD;
106
- const currentLevel = isCritical ? 'critical' : 'warning';
105
+ const isCritical = remaining <= CRITICAL_THRESHOLD;
106
+ const currentLevel = isCritical ? 'critical' : 'warning';
107
107
 
108
- // Emit immediately on first warning, then debounce subsequent ones
109
- // Severity escalation (WARNING -> CRITICAL) bypasses debounce
110
- const severityEscalated = currentLevel === 'critical' && warnData.lastLevel === 'warning';
111
- if (!firstWarn && warnData.callsSinceWarn < DEBOUNCE_CALLS && !severityEscalated) {
112
- // Update counter and exit without warning
113
- fs.writeFileSync(warnPath, JSON.stringify(warnData));
114
- process.exit(0);
115
- }
108
+ // Emit immediately on first warning, then debounce subsequent ones
109
+ // Severity escalation (WARNING -> CRITICAL) bypasses debounce
110
+ const severityEscalated = currentLevel === 'critical' && warnData.lastLevel === 'warning';
111
+ if (!firstWarn && warnData.callsSinceWarn < DEBOUNCE_CALLS && !severityEscalated) {
112
+ // Update counter and exit without warning
113
+ fs.writeFileSync(warnPath, JSON.stringify(warnData));
114
+ process.exit(0);
115
+ }
116
116
 
117
- // Reset debounce counter
118
- warnData.callsSinceWarn = 0;
119
- warnData.lastLevel = currentLevel;
120
- fs.writeFileSync(warnPath, JSON.stringify(warnData));
121
-
122
- // Detect if GSD is active (has .planning/STATE.md in working directory)
123
- const isGsdActive = fs.existsSync(path.join(cwd, '.planning', 'STATE.md'));
124
-
125
- // Build advisory warning message (never use imperative commands that
126
- // override user preferences - see #884)
127
- let message;
128
- if (isCritical) {
129
- message = isGsdActive
130
- ? `CONTEXT CRITICAL: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
131
- 'Context is nearly exhausted. Do NOT start new complex work or write handoff files - ' +
132
- 'GSD state is already tracked in STATE.md. Inform the user so they can run ' +
133
- '/gsd:pause-work at the next natural stopping point.'
134
- : `CONTEXT CRITICAL: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
135
- 'Context is nearly exhausted. Inform the user that context is low and ask how they ' +
136
- 'want to proceed. Do NOT autonomously save state or write handoff files unless the user asks.';
137
- } else {
138
- message = isGsdActive
139
- ? `CONTEXT WARNING: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
140
- 'Context is getting limited. Avoid starting new complex work. If not between ' +
141
- 'defined plan steps, inform the user so they can prepare to pause.'
142
- : `CONTEXT WARNING: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
143
- 'Be aware that context is getting limited. Avoid unnecessary exploration or ' +
144
- 'starting new complex work.';
145
- }
117
+ // Reset debounce counter
118
+ warnData.callsSinceWarn = 0;
119
+ warnData.lastLevel = currentLevel;
120
+ fs.writeFileSync(warnPath, JSON.stringify(warnData));
121
+
122
+ // Detect if GSD is active (has .planning/STATE.md in working directory)
123
+ const isGsdActive = fs.existsSync(path.join(cwd, '.planning', 'STATE.md'));
124
+
125
+ // Build advisory warning message (never use imperative commands that
126
+ // override user preferences - see #884)
127
+ let message;
128
+ if (isCritical) {
129
+ message = isGsdActive
130
+ ? `CONTEXT CRITICAL: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
131
+ 'Context is nearly exhausted. Do NOT start new complex work or write handoff files - ' +
132
+ 'GSD state is already tracked in STATE.md. Inform the user so they can run ' +
133
+ '/gsd-pause-work at the next natural stopping point.'
134
+ : `CONTEXT CRITICAL: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
135
+ 'Context is nearly exhausted. Inform the user that context is low and ask how they ' +
136
+ 'want to proceed. Do NOT autonomously save state or write handoff files unless the user asks.';
137
+ } else {
138
+ message = isGsdActive
139
+ ? `CONTEXT WARNING: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
140
+ 'Context is getting limited. Avoid starting new complex work. If not between ' +
141
+ 'defined plan steps, inform the user so they can prepare to pause.'
142
+ : `CONTEXT WARNING: Usage at ${usedPct}%. Remaining: ${remaining}%. ` +
143
+ 'Be aware that context is getting limited. Avoid unnecessary exploration or ' +
144
+ 'starting new complex work.';
145
+ }
146
146
 
147
- const output = {
148
- hookSpecificOutput: {
149
- hookEventName: process.env.GEMINI_API_KEY ? "AfterTool" : "PostToolUse",
150
- additionalContext: message
151
- }
152
- };
153
-
154
- process.stdout.write(JSON.stringify(output));
155
- } catch (e) {
156
- // Silent fail -- never block tool execution
157
- process.exit(0);
158
- }
147
+ const output = {
148
+ hookSpecificOutput: {
149
+ hookEventName: process.env.GEMINI_API_KEY ? "AfterTool" : "PostToolUse",
150
+ additionalContext: message
151
+ }
152
+ };
153
+
154
+ process.stdout.write(JSON.stringify(output));
155
+ } catch (e) {
156
+ // Silent fail -- never block tool execution
157
+ process.exit(0);
158
+ }
159
159
  });
@@ -105,10 +105,10 @@ process.stdin.on('end', () => {
105
105
  try {
106
106
  const cache = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
107
107
  if (cache.update_available) {
108
- gsdUpdate = '\x1b[33m⬆ /gsd:update\x1b[0m │ ';
108
+ gsdUpdate = '\x1b[33m⬆ /gsd-update\x1b[0m │ ';
109
109
  }
110
110
  if (cache.stale_hooks && cache.stale_hooks.length > 0) {
111
- gsdUpdate += '\x1b[31m⚠ stale hooks - run /gsd:update\x1b[0m │ ';
111
+ gsdUpdate += '\x1b[31m⚠ stale hooks - run /gsd-update\x1b[0m │ ';
112
112
  }
113
113
  } catch (e) { }
114
114
  }
@@ -5,10 +5,10 @@
5
5
  // Do NOT hardcode harness-specific paths here. See HOOKS_ARCHITECTURE.md.
6
6
  // GSD Workflow Guard - PreToolUse hook
7
7
  // Detects when Claude attempts file edits outside a GSD workflow context
8
- // (no active /gsd: command or Task subagent) and injects an advisory warning.
8
+ // (no active /gsd- command or Task subagent) and injects an advisory warning.
9
9
  //
10
10
  // This is a SOFT guard - it advises, not blocks. The edit still proceeds.
11
- // The warning nudges Claude to use /gsd:quick or /gsd:fast instead of
11
+ // The warning nudges Claude to use /gsd-quick or /gsd-fast instead of
12
12
  // making direct edits that bypass state tracking.
13
13
  //
14
14
  // Enable via config: hooks.workflow_guard: true (default: false)
@@ -22,76 +22,76 @@ const stdinTimeout = setTimeout(() => process.exit(0), 3000);
22
22
  process.stdin.setEncoding('utf8');
23
23
  process.stdin.on('data', chunk => input += chunk);
24
24
  process.stdin.on('end', () => {
25
- clearTimeout(stdinTimeout);
26
- try {
27
- const data = JSON.parse(input);
28
- const toolName = data.tool_name;
25
+ clearTimeout(stdinTimeout);
26
+ try {
27
+ const data = JSON.parse(input);
28
+ const toolName = data.tool_name;
29
29
 
30
- // Only guard Write and Edit tool calls
31
- if (toolName !== 'Write' && toolName !== 'Edit') {
32
- process.exit(0);
33
- }
30
+ // Only guard Write and Edit tool calls
31
+ if (toolName !== 'Write' && toolName !== 'Edit') {
32
+ process.exit(0);
33
+ }
34
34
 
35
- // Check if we're inside a GSD workflow (Task subagent or /gsd: command)
36
- // Subagents have a session_id that differs from the parent
37
- // and typically have a description field set by the orchestrator
38
- if (data.tool_input?.is_subagent || data.session_type === 'task') {
39
- process.exit(0);
40
- }
35
+ // Check if we're inside a GSD workflow (Task subagent or /gsd- command)
36
+ // Subagents have a session_id that differs from the parent
37
+ // and typically have a description field set by the orchestrator
38
+ if (data.tool_input?.is_subagent || data.session_type === 'task') {
39
+ process.exit(0);
40
+ }
41
41
 
42
- // Check the file being edited
43
- const filePath = data.tool_input?.file_path || data.tool_input?.path || '';
42
+ // Check the file being edited
43
+ const filePath = data.tool_input?.file_path || data.tool_input?.path || '';
44
44
 
45
- // Allow edits to .planning/ files (GSD state management)
46
- if (filePath.includes('.planning/') || filePath.includes('.planning\\')) {
47
- process.exit(0);
48
- }
45
+ // Allow edits to .planning/ files (GSD state management)
46
+ if (filePath.includes('.planning/') || filePath.includes('.planning\\')) {
47
+ process.exit(0);
48
+ }
49
49
 
50
- // Allow edits to common config/docs files that don't need GSD tracking
51
- const allowedPatterns = [
52
- /\.gitignore$/,
53
- /\.env/,
54
- /CLAUDE\.md$/,
55
- /AGENTS\.md$/,
56
- /GEMINI\.md$/,
57
- /settings\.json$/,
58
- ];
59
- if (allowedPatterns.some(p => p.test(filePath))) {
60
- process.exit(0);
61
- }
50
+ // Allow edits to common config/docs files that don't need GSD tracking
51
+ const allowedPatterns = [
52
+ /\.gitignore$/,
53
+ /\.env/,
54
+ /CLAUDE\.md$/,
55
+ /AGENTS\.md$/,
56
+ /GEMINI\.md$/,
57
+ /settings\.json$/,
58
+ ];
59
+ if (allowedPatterns.some(p => p.test(filePath))) {
60
+ process.exit(0);
61
+ }
62
62
 
63
- // Check if workflow guard is enabled
64
- const cwd = data.cwd || process.cwd();
65
- const configPath = path.join(cwd, '.planning', 'config.json');
66
- if (fs.existsSync(configPath)) {
67
- try {
68
- const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
69
- if (!config.hooks?.workflow_guard) {
70
- process.exit(0); // Guard disabled (default)
63
+ // Check if workflow guard is enabled
64
+ const cwd = data.cwd || process.cwd();
65
+ const configPath = path.join(cwd, '.planning', 'config.json');
66
+ if (fs.existsSync(configPath)) {
67
+ try {
68
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
69
+ if (!config.hooks?.workflow_guard) {
70
+ process.exit(0); // Guard disabled (default)
71
+ }
72
+ } catch (e) {
73
+ process.exit(0);
74
+ }
75
+ } else {
76
+ process.exit(0); // No GSD project - don't guard
71
77
  }
72
- } catch (e) {
73
- process.exit(0);
74
- }
75
- } else {
76
- process.exit(0); // No GSD project - don't guard
77
- }
78
78
 
79
- // If we get here: GSD project, guard enabled, file edit outside .planning/,
80
- // not in a subagent context. Inject advisory warning.
81
- const output = {
82
- hookSpecificOutput: {
83
- hookEventName: "PreToolUse",
84
- additionalContext: `⚠️ WORKFLOW ADVISORY: You're editing ${path.basename(filePath)} directly without a GSD command. ` +
85
- 'This edit will not be tracked in STATE.md or produce a SUMMARY.md. ' +
86
- 'Consider using /gsd:fast for trivial fixes or /gsd:quick for larger changes ' +
87
- 'to maintain project state tracking. ' +
88
- 'If this is intentional (e.g., user explicitly asked for a direct edit), proceed normally.'
89
- }
90
- };
79
+ // If we get here: GSD project, guard enabled, file edit outside .planning/,
80
+ // not in a subagent context. Inject advisory warning.
81
+ const output = {
82
+ hookSpecificOutput: {
83
+ hookEventName: "PreToolUse",
84
+ additionalContext: `⚠️ WORKFLOW ADVISORY: You're editing ${path.basename(filePath)} directly without a GSD command. ` +
85
+ 'This edit will not be tracked in STATE.md or produce a SUMMARY.md. ' +
86
+ 'Consider using /gsd-fast for trivial fixes or /gsd-quick for larger changes ' +
87
+ 'to maintain project state tracking. ' +
88
+ 'If this is intentional (e.g., user explicitly asked for a direct edit), proceed normally.'
89
+ }
90
+ };
91
91
 
92
- process.stdout.write(JSON.stringify(output));
93
- } catch (e) {
94
- // Silent fail - never block tool execution
95
- process.exit(0);
96
- }
92
+ process.stdout.write(JSON.stringify(output));
93
+ } catch (e) {
94
+ // Silent fail - never block tool execution
95
+ process.exit(0);
96
+ }
97
97
  });
package/README.md CHANGED
@@ -49,7 +49,7 @@ Then start your first project:
49
49
  └─► (next phase or /gsd-complete-milestone)
50
50
  ```
51
51
 
52
- All project state lives in `.planning/` - committed to git, survives `/clear` and context resets.
52
+ All project state lives in `.planning/` - committed to git, survives `/new` and context resets.
53
53
 
54
54
  ---
55
55
 
@@ -933,8 +933,10 @@ function pi_gsd_hooks_default(pi) {
933
933
  }
934
934
  }
935
935
  if (raw === null) {
936
- errors.push("File not found: " + filePath);
937
- return null;
936
+ return `
937
+ > \u26A0\uFE0F **GSD include error:** workflow \`${filePath}\` not found.
938
+ > Run \`/gsd-health\` to diagnose, or reinstall with \`npm install pi-gsd\`.
939
+ `;
938
940
  }
939
941
  let result = raw;
940
942
  if (!selectExpr) return result;
@@ -1028,7 +1030,6 @@ function pi_gsd_hooks_default(pi) {
1028
1030
  }
1029
1031
  if (errors.length > 0) {
1030
1032
  ctx.ui.notify("\u274C GSD include failed:\n" + errors.map((e) => " \u2022 " + e).join("\n"), "error");
1031
- return { messages: [] };
1032
1033
  }
1033
1034
  const extFile2 = typeof __filename !== "undefined" ? __filename : "";
1034
1035
  const pkgRoot2 = (0, import_node_path4.join)((0, import_node_path4.dirname)(extFile2), "..");
@@ -1066,6 +1067,19 @@ function pi_gsd_hooks_default(pi) {
1066
1067
  ],
1067
1068
  shellTimeoutMs: projectSettings.shellTimeoutMs ?? globalSettings.shellTimeoutMs ?? 3e4
1068
1069
  };
1070
+ const preWxpSnapshots = /* @__PURE__ */ new Map();
1071
+ for (let msgIdx = 0; msgIdx < messages.length; msgIdx++) {
1072
+ const msg = messages[msgIdx];
1073
+ if (msg.role !== "user") continue;
1074
+ if (typeof msg.content === "string" && msg.content.includes("<gsd-")) {
1075
+ preWxpSnapshots.set(msgIdx, msg.content);
1076
+ } else if (Array.isArray(msg.content)) {
1077
+ const blocks = msg.content;
1078
+ if (blocks.some((b) => b.type === "text" && b.text?.includes("<gsd-"))) {
1079
+ preWxpSnapshots.set(msgIdx, blocks.map((b) => ({ ...b })));
1080
+ }
1081
+ }
1082
+ }
1069
1083
  try {
1070
1084
  for (let msgIdx = 0; msgIdx < messages.length; msgIdx++) {
1071
1085
  const msg = messages[msgIdx];
@@ -1087,10 +1101,23 @@ function pi_gsd_hooks_default(pi) {
1087
1101
  } catch (wxpErr) {
1088
1102
  if (wxpErr instanceof WxpProcessingError) {
1089
1103
  ctx.ui.notify(wxpErr.message, "error");
1090
- return { messages: [] };
1104
+ for (let msgIdx = 0; msgIdx < messages.length; msgIdx++) {
1105
+ const snap = preWxpSnapshots.get(msgIdx);
1106
+ if (snap === void 0) continue;
1107
+ const msg = messages[msgIdx];
1108
+ if (typeof msg.content === "string") {
1109
+ msg.content = snap;
1110
+ } else if (Array.isArray(msg.content)) {
1111
+ const snapArr = snap;
1112
+ const blocks = msg.content;
1113
+ for (let bi = 0; bi < blocks.length; bi++) {
1114
+ const b = blocks[bi];
1115
+ const sb = snapArr[bi];
1116
+ if (b.type === "text" && sb?.text !== void 0) b.text = sb.text;
1117
+ }
1118
+ }
1119
+ }
1091
1120
  }
1092
- const errMsg = wxpErr instanceof Error ? wxpErr.message : String(wxpErr);
1093
- ctx.ui.notify(`GSD WXP: unexpected context error: ${errMsg}`, "info");
1094
1121
  }
1095
1122
  return { messages };
1096
1123
  });