ninja-terminals 2.2.5 → 2.2.6
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.md +11 -0
- package/package.json +1 -1
- package/prompts/orchestrator-lite.md +14 -0
- package/server.js +29 -2
package/CLAUDE.md
CHANGED
|
@@ -38,6 +38,17 @@ These status lines are CRITICAL — the orchestrator parses them to know your st
|
|
|
38
38
|
- If a build breaks, fix it yourself — don't wait for the orchestrator
|
|
39
39
|
- If you hit a permissions wall, report BLOCKED with specifics
|
|
40
40
|
|
|
41
|
+
### Stuck Terminal Recovery
|
|
42
|
+
If you notice your terminal is stuck (no response to input, commands hang), or if you see another terminal is stuck:
|
|
43
|
+
1. **Report it**: `STATUS: ERROR:STUCK — terminal unresponsive after [what happened]`
|
|
44
|
+
2. **The orchestrator will**: Refresh the page or restart the terminal
|
|
45
|
+
3. **After restart**: You will lose context. Wait for re-orientation from the orchestrator.
|
|
46
|
+
|
|
47
|
+
Common causes of stuck terminals:
|
|
48
|
+
- Permission errors (chmod, file access)
|
|
49
|
+
- Tool failures that leave PTY in bad state
|
|
50
|
+
- Context window overflow
|
|
51
|
+
|
|
41
52
|
### Completeness
|
|
42
53
|
- Don't report DONE for partial work. If you were asked to "build and test", both must be done.
|
|
43
54
|
- Include evidence with DONE: test output, screenshot path, API response, URL, file path
|
package/package.json
CHANGED
|
@@ -110,8 +110,22 @@ When done: STATUS: DONE — [template name and test result]
|
|
|
110
110
|
| `done` | Read output. Verify the claim. Assign next task. |
|
|
111
111
|
| `blocked` | Read what it needs. Provide it, or reassign. |
|
|
112
112
|
| `error` | Read the error. Send fix instructions or restart. |
|
|
113
|
+
| `stuck` | Terminal is unresponsive. **Refresh the page** or `POST /api/terminals/:id/restart`. |
|
|
113
114
|
| `compacting` | Wait, then re-orient fully with context summary. |
|
|
114
115
|
|
|
116
|
+
### Stuck Terminal Recovery
|
|
117
|
+
Terminals can get stuck after tool errors (permission denied, failed commands, etc.). Signs of a stuck terminal:
|
|
118
|
+
- No output for 2+ minutes while status shows "working"
|
|
119
|
+
- Input commands have no effect
|
|
120
|
+
- Status shows `stuck`
|
|
121
|
+
|
|
122
|
+
**Recovery steps:**
|
|
123
|
+
1. **First try**: Refresh the Ninja Terminals page (Cmd+R / Ctrl+R)
|
|
124
|
+
2. **If that fails**: `POST /api/terminals/:id/restart` to restart just that terminal
|
|
125
|
+
3. **Last resort**: Kill and respawn: `DELETE /api/terminals/:id` then `POST /api/terminals/spawn`
|
|
126
|
+
|
|
127
|
+
After recovery, re-dispatch the task with full context — the terminal lost its memory.
|
|
128
|
+
|
|
115
129
|
### Context Preservation
|
|
116
130
|
- Terminals WILL compact during long tasks and lose memory
|
|
117
131
|
- You MUST re-orient them: what they were doing, what's completed, what's next, critical context
|
package/server.js
CHANGED
|
@@ -219,14 +219,41 @@ function spawnTerminal(label, scope = [], cwd = null, tier = 'pro') {
|
|
|
219
219
|
|
|
220
220
|
// ── Status Detection Loop (2s) ──────────────────────────────
|
|
221
221
|
|
|
222
|
+
// Track last output time for stuck detection
|
|
223
|
+
const lastOutputTime = new Map();
|
|
224
|
+
const STUCK_THRESHOLD_MS = 120000; // 2 minutes with no output = stuck
|
|
225
|
+
|
|
222
226
|
setInterval(() => {
|
|
223
|
-
for (const [, terminal] of terminals) {
|
|
227
|
+
for (const [id, terminal] of terminals) {
|
|
224
228
|
if (terminal.status === 'exited') continue;
|
|
225
229
|
const prev = terminal.status;
|
|
226
230
|
const recentLines = terminal.lineBuffer.last(50);
|
|
227
231
|
const newStatus = detectStatus(recentLines);
|
|
228
232
|
|
|
229
|
-
|
|
233
|
+
// Track output activity for stuck detection
|
|
234
|
+
const currentOutput = terminal.lineBuffer.last(5).join('');
|
|
235
|
+
const lastOutput = lastOutputTime.get(id);
|
|
236
|
+
if (!lastOutput || lastOutput.output !== currentOutput) {
|
|
237
|
+
lastOutputTime.set(id, { output: currentOutput, time: Date.now() });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Check for stuck terminal (no output for 2+ minutes while "working")
|
|
241
|
+
const lastActivity = lastOutputTime.get(id);
|
|
242
|
+
if (lastActivity && terminal.status === 'working') {
|
|
243
|
+
const stuckTime = Date.now() - lastActivity.time;
|
|
244
|
+
if (stuckTime > STUCK_THRESHOLD_MS) {
|
|
245
|
+
terminal.status = 'stuck';
|
|
246
|
+
sse.broadcast('terminal_stuck', {
|
|
247
|
+
terminal: terminal.label,
|
|
248
|
+
id: terminal.id,
|
|
249
|
+
stuckFor: Math.round(stuckTime / 1000),
|
|
250
|
+
suggestion: 'Refresh page or POST /api/terminals/:id/restart',
|
|
251
|
+
});
|
|
252
|
+
console.log(`Terminal ${id} (${terminal.label}) appears stuck - no output for ${Math.round(stuckTime / 1000)}s`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (newStatus !== prev && newStatus !== 'stuck') {
|
|
230
257
|
terminal.status = newStatus;
|
|
231
258
|
sse.broadcast('status_change', {
|
|
232
259
|
terminal: terminal.label,
|