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.
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-codebase-mapper.md +13 -13
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-debugger.md +37 -37
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-executor.md +8 -8
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-nyquist-auditor.md +31 -31
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-phase-researcher.md +85 -85
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-plan-checker.md +32 -32
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-planner.md +108 -108
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-project-researcher.md +76 -76
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-research-synthesizer.md +16 -16
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-roadmapper.md +39 -39
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-ui-auditor.md +24 -24
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-ui-checker.md +21 -21
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-ui-researcher.md +35 -35
- package/.gsd/harnesses/pi/get-shit-done/agents/gsd-verifier.md +30 -30
- package/.gsd/harnesses/pi/get-shit-done/references/continuation-format.md +11 -11
- package/.gsd/harnesses/pi/get-shit-done/references/ui-brand.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/templates/DEBUG.md +4 -4
- package/.gsd/harnesses/pi/get-shit-done/templates/UAT.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/add-phase.md +36 -68
- package/.gsd/harnesses/pi/get-shit-done/workflows/audit-milestone.md +3 -3
- package/.gsd/harnesses/pi/get-shit-done/workflows/complete-milestone.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/discuss-phase-assumptions.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/discuss-phase.md +2 -2
- package/.gsd/harnesses/pi/get-shit-done/workflows/discuss-phase.md.bak +2 -2
- package/.gsd/harnesses/pi/get-shit-done/workflows/execute-phase.md +2 -2
- package/.gsd/harnesses/pi/get-shit-done/workflows/execute-phase.md.bak +2 -2
- package/.gsd/harnesses/pi/get-shit-done/workflows/execute-plan.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/help.md +5 -5
- package/.gsd/harnesses/pi/get-shit-done/workflows/insert-phase.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/map-codebase.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/new-milestone.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/new-milestone.md.bak +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/new-project.md +2 -2
- package/.gsd/harnesses/pi/get-shit-done/workflows/new-project.md.bak +2 -2
- package/.gsd/harnesses/pi/get-shit-done/workflows/plan-milestone-gaps.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/plan-phase.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/plan-phase.md.bak +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/progress.md +10 -10
- package/.gsd/harnesses/pi/get-shit-done/workflows/resume-project.md +2 -2
- package/.gsd/harnesses/pi/get-shit-done/workflows/settings.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/transition.md +3 -3
- package/.gsd/harnesses/pi/get-shit-done/workflows/ui-phase.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/ui-review.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/validate-phase.md +1 -1
- package/.gsd/harnesses/pi/get-shit-done/workflows/verify-work.md +1 -1
- package/.gsd/harnesses/pi/hooks/gsd-context-monitor.js +102 -102
- package/.gsd/harnesses/pi/hooks/gsd-statusline.js +2 -2
- package/.gsd/harnesses/pi/hooks/gsd-workflow-guard.js +65 -65
- package/README.md +1 -1
- package/dist/pi-gsd-hooks.js +33 -6
- package/dist/pi-gsd-tools.js +87 -76
- 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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
clearTimeout(stdinTimeout);
|
|
43
|
+
try {
|
|
44
|
+
const data = JSON.parse(input);
|
|
45
|
+
const sessionId = data.session_id;
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
if (!sessionId) {
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
66
|
-
|
|
65
|
+
const tmpDir = os.tmpdir();
|
|
66
|
+
const metricsPath = path.join(tmpDir, `claude-ctx-${sessionId}.json`);
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
74
|
-
|
|
73
|
+
const metrics = JSON.parse(fs.readFileSync(metricsPath, 'utf8'));
|
|
74
|
+
const now = Math.floor(Date.now() / 1000);
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
// Ignore stale metrics
|
|
77
|
+
if (metrics.timestamp && (now - metrics.timestamp) > STALE_SECONDS) {
|
|
78
|
+
process.exit(0);
|
|
79
|
+
}
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
const remaining = metrics.remaining_percentage;
|
|
82
|
+
const usedPct = metrics.used_pct;
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
// No warning needed
|
|
85
|
+
if (remaining > WARNING_THRESHOLD) {
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
103
|
+
warnData.callsSinceWarn = (warnData.callsSinceWarn || 0) + 1;
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
const isCritical = remaining <= CRITICAL_THRESHOLD;
|
|
106
|
+
const currentLevel = isCritical ? 'critical' : 'warning';
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
clearTimeout(stdinTimeout);
|
|
26
|
+
try {
|
|
27
|
+
const data = JSON.parse(input);
|
|
28
|
+
const toolName = data.tool_name;
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
// Only guard Write and Edit tool calls
|
|
31
|
+
if (toolName !== 'Write' && toolName !== 'Edit') {
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
43
|
-
|
|
42
|
+
// Check the file being edited
|
|
43
|
+
const filePath = data.tool_input?.file_path || data.tool_input?.path || '';
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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 `/
|
|
52
|
+
All project state lives in `.planning/` - committed to git, survives `/new` and context resets.
|
|
53
53
|
|
|
54
54
|
---
|
|
55
55
|
|
package/dist/pi-gsd-hooks.js
CHANGED
|
@@ -933,8 +933,10 @@ function pi_gsd_hooks_default(pi) {
|
|
|
933
933
|
}
|
|
934
934
|
}
|
|
935
935
|
if (raw === null) {
|
|
936
|
-
|
|
937
|
-
|
|
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
|
-
|
|
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
|
});
|