cligr 1.0.7 → 1.0.8

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 (32) hide show
  1. package/.claude/worktrees/agent-ac25cfb2/.claude/settings.local.json +30 -0
  2. package/.claude/worktrees/agent-ac25cfb2/README.md +65 -0
  3. package/.claude/worktrees/agent-ac25cfb2/docs/plans/2026-02-13-named-params-support.md +391 -0
  4. package/.claude/worktrees/agent-ac25cfb2/docs/plans/2026-02-25-named-items-design.md +164 -0
  5. package/.claude/worktrees/agent-ac25cfb2/docs/plans/2026-02-25-named-items-implementation.md +460 -0
  6. package/.claude/worktrees/agent-ac25cfb2/package-lock.json +554 -0
  7. package/.claude/worktrees/agent-ac25cfb2/package.json +27 -0
  8. package/.claude/worktrees/agent-ac25cfb2/scripts/build.js +20 -0
  9. package/.claude/worktrees/agent-ac25cfb2/scripts/test.js +168 -0
  10. package/.claude/worktrees/agent-ac25cfb2/src/commands/config.ts +121 -0
  11. package/.claude/worktrees/agent-ac25cfb2/src/commands/groups.ts +68 -0
  12. package/.claude/worktrees/agent-ac25cfb2/src/commands/ls.ts +25 -0
  13. package/.claude/worktrees/agent-ac25cfb2/src/commands/up.ts +49 -0
  14. package/.claude/worktrees/agent-ac25cfb2/src/config/loader.ts +148 -0
  15. package/.claude/worktrees/agent-ac25cfb2/src/config/types.ts +26 -0
  16. package/.claude/worktrees/agent-ac25cfb2/src/index.ts +97 -0
  17. package/.claude/worktrees/agent-ac25cfb2/src/process/manager.ts +270 -0
  18. package/.claude/worktrees/agent-ac25cfb2/src/process/pid-store.ts +203 -0
  19. package/.claude/worktrees/agent-ac25cfb2/src/process/template.ts +87 -0
  20. package/.claude/worktrees/agent-ac25cfb2/tests/integration/blocking-processes-fixed.test.ts +255 -0
  21. package/.claude/worktrees/agent-ac25cfb2/tests/integration/blocking-processes.test.ts +497 -0
  22. package/.claude/worktrees/agent-ac25cfb2/tests/integration/commands.test.ts +648 -0
  23. package/.claude/worktrees/agent-ac25cfb2/tests/integration/config-loader.test.ts +426 -0
  24. package/.claude/worktrees/agent-ac25cfb2/tests/integration/process-manager.test.ts +394 -0
  25. package/.claude/worktrees/agent-ac25cfb2/tests/integration/template-expander.test.ts +454 -0
  26. package/.claude/worktrees/agent-ac25cfb2/tsconfig.json +15 -0
  27. package/.claude/worktrees/agent-ac25cfb2/usage.md +9 -0
  28. package/dist/index.js +82 -25
  29. package/docs/superpowers/plans/2026-04-13-improve-web-ui-console.md +256 -0
  30. package/docs/superpowers/specs/2026-04-13-improve-web-ui-console-design.md +38 -0
  31. package/package.json +1 -1
  32. package/src/commands/serve.ts +64 -7
@@ -0,0 +1,256 @@
1
+ # Improve Web UI Console Logging Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Enhance the web UI console panel in `cligr serve` with timestamps, system event logging, color-coded output, and a clear button.
6
+
7
+ **Architecture:** Modify the embedded HTML/CSS/JS inside `src/commands/serve.ts`'s `serveHtml()` function. All changes are client-side: timestamps are generated in `appendLog`, system events are rendered when `status` SSE events arrive, and a clear button empties the log container.
8
+
9
+ **Tech Stack:** TypeScript, vanilla HTML/CSS/JS embedded in a string.
10
+
11
+ ---
12
+
13
+ ### Task 1: Add console header layout and clear button
14
+
15
+ **Files:**
16
+ - Modify: `src/commands/serve.ts:232-234`
17
+
18
+ - [ ] **Step 1: Update `.main h2` CSS to support a flex header with button**
19
+
20
+ Replace:
21
+ ```css
22
+ .main h2 { font-size: 1rem; margin: 0; padding: 0.5rem 1rem; border-bottom: 1px solid #ccc; background: #f0f0f0; }
23
+ ```
24
+
25
+ With:
26
+ ```css
27
+ .main-header { display: flex; align-items: center; justify-content: space-between; font-size: 1rem; margin: 0; padding: 0.5rem 1rem; border-bottom: 1px solid #ccc; background: #f0f0f0; }
28
+ .main-header h2 { font-size: 1rem; margin: 0; }
29
+ .clear-btn { font-size: 0.8rem; padding: 0.25rem 0.6rem; cursor: pointer; }
30
+ ```
31
+
32
+ - [ ] **Step 2: Replace the `<h2>Console</h2>` markup with the new header**
33
+
34
+ Replace:
35
+ ```html
36
+ <h2>Console</h2>
37
+ ```
38
+
39
+ With:
40
+ ```html
41
+ <div class="main-header">
42
+ <h2>Console</h2>
43
+ <button class="clear-btn" id="clearBtn">Clear</button>
44
+ </div>
45
+ ```
46
+
47
+ - [ ] **Step 3: Add clear button click handler in the script section**
48
+
49
+ Add after:
50
+ ```javascript
51
+ const groupsEl = document.getElementById('groups');
52
+ const logsEl = document.getElementById('logs');
53
+ const resizer = document.getElementById('resizer');
54
+ let autoScroll = true;
55
+ ```
56
+
57
+ ```javascript
58
+ const clearBtn = document.getElementById('clearBtn');
59
+ clearBtn.addEventListener('click', () => {
60
+ logsEl.innerHTML = '';
61
+ });
62
+ ```
63
+
64
+ - [ ] **Step 4: Typecheck**
65
+
66
+ Run: `npm run typecheck`
67
+ Expected: No errors.
68
+
69
+ - [ ] **Step 5: Commit**
70
+
71
+ ```bash
72
+ git add src/commands/serve.ts
73
+ git commit -m "feat(serve): add clear button to console header"
74
+ ```
75
+
76
+ ---
77
+
78
+ ### Task 2: Add timestamp and color-coded log styling
79
+
80
+ **Files:**
81
+ - Modify: `src/commands/serve.ts:238-239` and `src/commands/serve.ts:336-342`
82
+
83
+ - [ ] **Step 1: Add CSS classes for log styling**
84
+
85
+ Replace:
86
+ ```css
87
+ .logs { flex: 1; background: #111; color: #0f0; font-family: monospace; font-size: 0.85rem; overflow-y: auto; padding: 0.75rem; white-space: pre-wrap; }
88
+ .error { color: #f55; }
89
+ ```
90
+
91
+ With:
92
+ ```css
93
+ .logs { flex: 1; background: #111; color: #0f0; font-family: monospace; font-size: 0.85rem; overflow-y: auto; padding: 0.75rem; white-space: pre-wrap; }
94
+ .log-line { margin: 0.1rem 0; }
95
+ .log-time { color: #888; }
96
+ .log-system { color: #6cf; }
97
+ .log-error { color: #f55; }
98
+ ```
99
+
100
+ - [ ] **Step 2: Rewrite `appendLog` to include timestamps and structured styling**
101
+
102
+ Replace:
103
+ ```javascript
104
+ function appendLog(line, isError) {
105
+ const span = document.createElement('div');
106
+ span.textContent = line;
107
+ if (isError) span.className = 'error';
108
+ logsEl.appendChild(span);
109
+ if (autoScroll) logsEl.scrollTop = logsEl.scrollHeight;
110
+ }
111
+ ```
112
+
113
+ With:
114
+ ```javascript
115
+ function appendLog(line, isError) {
116
+ const div = document.createElement('div');
117
+ div.className = 'log-line';
118
+
119
+ const time = document.createElement('span');
120
+ time.className = 'log-time';
121
+ const now = new Date();
122
+ const hh = String(now.getHours()).padStart(2, '0');
123
+ const mm = String(now.getMinutes()).padStart(2, '0');
124
+ const ss = String(now.getSeconds()).padStart(2, '0');
125
+ time.textContent = `[${hh}:${mm}:${ss}] `;
126
+ div.appendChild(time);
127
+
128
+ const content = document.createElement('span');
129
+ content.textContent = line;
130
+ if (isError) content.className = 'log-error';
131
+ div.appendChild(content);
132
+
133
+ logsEl.appendChild(div);
134
+ if (autoScroll) logsEl.scrollTop = logsEl.scrollHeight;
135
+ }
136
+ ```
137
+
138
+ - [ ] **Step 3: Typecheck**
139
+
140
+ Run: `npm run typecheck`
141
+ Expected: No errors.
142
+
143
+ - [ ] **Step 4: Commit**
144
+
145
+ ```bash
146
+ git add src/commands/serve.ts
147
+ git commit -m "feat(serve): add timestamps and color-coded log styling"
148
+ ```
149
+
150
+ ---
151
+
152
+ ### Task 3: Log system events to the console
153
+
154
+ **Files:**
155
+ - Modify: `src/commands/serve.ts:344-354`
156
+
157
+ - [ ] **Step 1: Update the `status` SSE handler to append system messages**
158
+
159
+ Replace:
160
+ ```javascript
161
+ evtSource.addEventListener('status', (e) => {
162
+ fetchGroups();
163
+ });
164
+ ```
165
+
166
+ With:
167
+ ```javascript
168
+ evtSource.addEventListener('status', (e) => {
169
+ const data = JSON.parse(e.data);
170
+ if (data.type === 'group-started') {
171
+ appendLog(`[system] Group "${data.groupName}" started`, false);
172
+ const groupTime = document.querySelector('.log-line:last-child span:last-child');
173
+ if (groupTime) groupTime.className = 'log-system';
174
+ } else if (data.type === 'group-stopped') {
175
+ appendLog(`[system] Group "${data.groupName}" stopped`, false);
176
+ const groupTime = document.querySelector('.log-line:last-child span:last-child');
177
+ if (groupTime) groupTime.className = 'log-system';
178
+ } else if (data.type === 'item-restarted') {
179
+ appendLog(`[system] Item "${data.groupName}/${data.itemName}" restarted`, false);
180
+ const groupTime = document.querySelector('.log-line:last-child span:last-child');
181
+ if (groupTime) groupTime.className = 'log-system';
182
+ }
183
+ fetchGroups();
184
+ });
185
+ ```
186
+
187
+ Wait — the above uses `querySelector` hackily. Better: introduce an `appendSystemLog(message)` helper.
188
+
189
+ Instead, replace the whole block with:
190
+
191
+ ```javascript
192
+ function appendSystemLog(message) {
193
+ const div = document.createElement('div');
194
+ div.className = 'log-line';
195
+
196
+ const time = document.createElement('span');
197
+ time.className = 'log-time';
198
+ const now = new Date();
199
+ const hh = String(now.getHours()).padStart(2, '0');
200
+ const mm = String(now.getMinutes()).padStart(2, '0');
201
+ const ss = String(now.getSeconds()).padStart(2, '0');
202
+ time.textContent = `[${hh}:${mm}:${ss}] `;
203
+ div.appendChild(time);
204
+
205
+ const content = document.createElement('span');
206
+ content.className = 'log-system';
207
+ content.textContent = message;
208
+ div.appendChild(content);
209
+
210
+ logsEl.appendChild(div);
211
+ if (autoScroll) logsEl.scrollTop = logsEl.scrollHeight;
212
+ }
213
+
214
+ evtSource.addEventListener('status', (e) => {
215
+ const data = JSON.parse(e.data);
216
+ if (data.type === 'group-started') {
217
+ appendSystemLog(`[system] Group "${data.groupName}" started`);
218
+ } else if (data.type === 'group-stopped') {
219
+ appendSystemLog(`[system] Group "${data.groupName}" stopped`);
220
+ } else if (data.type === 'item-restarted') {
221
+ appendSystemLog(`[system] Item "${data.groupName}/${data.itemName}" restarted`);
222
+ }
223
+ fetchGroups();
224
+ });
225
+ ```
226
+
227
+ - [ ] **Step 2: Typecheck**
228
+
229
+ Run: `npm run typecheck`
230
+ Expected: No errors.
231
+
232
+ - [ ] **Step 3: Build**
233
+
234
+ Run: `npm run build`
235
+ Expected: Build completes successfully.
236
+
237
+ - [ ] **Step 4: Commit**
238
+
239
+ ```bash
240
+ git add src/commands/serve.ts
241
+ git commit -m "feat(serve): log system events in web UI console"
242
+ ```
243
+
244
+ ---
245
+
246
+ ## Self-Review
247
+
248
+ **Spec coverage:**
249
+ - Timestamps: Task 2 (`appendLog` adds `[HH:MM:SS]`).
250
+ - System events in console: Task 3 (`appendSystemLog` for `group-started`, `group-stopped`, `item-restarted`).
251
+ - Color coding: Task 2 (`.log-time`, `.log-system`, `.log-error`) and Task 3 (uses `.log-system`).
252
+ - Clear button: Task 1 (Clear button + click handler).
253
+
254
+ **Placeholder scan:** None found. Every step has exact code, file paths, and commands.
255
+
256
+ **Type consistency:** `appendLog` signature unchanged (`line, isError`). `appendSystemLog` is a new helper. All DOM APIs are standard.
@@ -0,0 +1,38 @@
1
+ # Improve Web UI Console Logging
2
+
3
+ ## Goal
4
+ Enhance the web UI console panel in `cligr serve` with timestamps, system event logging, color-coded output, and a clear button.
5
+
6
+ ## Changes
7
+
8
+ ### 1. Timestamps
9
+ - Every log line in the console gets an `[HH:MM:SS]` prefix.
10
+ - Generated client-side in the `appendLog` function using `new Date().toLocaleTimeString()`.
11
+
12
+ ### 2. System Events in Console
13
+ - When the `status` SSE event fires, log human-readable messages to the console:
14
+ - `group-started` → `[system] Group "<name>" started`
15
+ - `group-stopped` → `[system] Group "<name>" stopped`
16
+ - `item-restarted` → `[system] Item "<group/item>" restarted`
17
+
18
+ ### 3. Color Coding (CSS Classes)
19
+ - `.log-time` — muted gray timestamp
20
+ - `.log-system` — blue/purple for system events
21
+ - `.log-error` — red for stderr lines
22
+ - Process stdout stays default green (existing `#0f0` on `#111`)
23
+
24
+ ### 4. Clear Button
25
+ - A "Clear" button is added to the console header next to the "Console" title.
26
+ - Clicking it empties the `#logs` element.
27
+
28
+ ## Scope
29
+ - Only `src/commands/serve.ts` is modified (specifically the `serveHtml()` function).
30
+ - No backend or SSE protocol changes.
31
+ - No new dependencies.
32
+
33
+ ## Success Criteria
34
+ - Console lines show `[HH:MM:SS]` prefix.
35
+ - Toggling a group on/off logs a system message in the console.
36
+ - Restarting an item logs a system message in the console.
37
+ - The Clear button removes all visible log lines.
38
+ - Existing process output behavior remains unchanged.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cligr",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "cligr": "dist/index.js"
@@ -230,13 +230,18 @@ function serveHtml(): string {
230
230
  .resizer { width: 6px; background: #e0e0e0; cursor: col-resize; flex-shrink: 0; }
231
231
  .resizer:hover { background: #bbb; }
232
232
  .main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
233
- .main h2 { font-size: 1rem; margin: 0; padding: 0.5rem 1rem; border-bottom: 1px solid #ccc; background: #f0f0f0; }
233
+ .main-header { display: flex; align-items: center; justify-content: space-between; font-size: 1rem; padding: 0.5rem 1rem; border-bottom: 1px solid #ccc; background: #f0f0f0; }
234
+ .main-header h2 { font-size: 1rem; margin: 0; }
235
+ .clear-btn { font-size: 0.8rem; padding: 0.25rem 0.6rem; cursor: pointer; }
234
236
  .group { border: 1px solid #ccc; border-radius: 6px; padding: 0.75rem; margin: 0 0 0.75rem 0; background: #fff; }
235
237
  .group-header { display: flex; align-items: center; gap: 0.5rem; font-weight: bold; font-size: 1rem; }
236
238
  .items { margin: 0.5rem 0 0 1.25rem; }
237
239
  .item { display: flex; align-items: center; gap: 0.4rem; margin: 0.2rem 0; font-size: 0.9rem; }
238
240
  .logs { flex: 1; background: #111; color: #0f0; font-family: monospace; font-size: 0.85rem; overflow-y: auto; padding: 0.75rem; white-space: pre-wrap; }
239
- .error { color: #f55; }
241
+ .log-line { margin: 0.1rem 0; }
242
+ .log-time { color: #888; }
243
+ .log-system { color: #6cf; }
244
+ .log-error { color: #f55; }
240
245
  </style>
241
246
  </head>
242
247
  <body>
@@ -245,7 +250,10 @@ function serveHtml(): string {
245
250
  <div class="sidebar" id="groups"></div>
246
251
  <div class="resizer" id="resizer"></div>
247
252
  <div class="main">
248
- <h2>Console</h2>
253
+ <div class="main-header">
254
+ <h2>Console</h2>
255
+ <button class="clear-btn" id="clearBtn">Clear</button>
256
+ </div>
249
257
  <div class="logs" id="logs"></div>
250
258
  </div>
251
259
  </div>
@@ -256,6 +264,11 @@ function serveHtml(): string {
256
264
  const resizer = document.getElementById('resizer');
257
265
  let autoScroll = true;
258
266
 
267
+ const clearBtn = document.getElementById('clearBtn');
268
+ clearBtn.addEventListener('click', () => {
269
+ logsEl.innerHTML = '';
270
+ });
271
+
259
272
  resizer.addEventListener('mousedown', (e) => {
260
273
  e.preventDefault();
261
274
  document.body.style.cursor = 'col-resize';
@@ -333,16 +346,60 @@ function serveHtml(): string {
333
346
  autoScroll = logsEl.scrollTop + logsEl.clientHeight >= logsEl.scrollHeight - 10;
334
347
  });
335
348
 
349
+ function formatTime() {
350
+ const now = new Date();
351
+ const hh = String(now.getHours()).padStart(2, '0');
352
+ const mm = String(now.getMinutes()).padStart(2, '0');
353
+ const ss = String(now.getSeconds()).padStart(2, '0');
354
+ return \`[\${hh}:\${mm}:\${ss}] \`;
355
+ }
356
+
336
357
  function appendLog(line, isError) {
337
- const span = document.createElement('div');
338
- span.textContent = line;
339
- if (isError) span.className = 'error';
340
- logsEl.appendChild(span);
358
+ const div = document.createElement('div');
359
+ div.className = 'log-line';
360
+
361
+ const time = document.createElement('span');
362
+ time.className = 'log-time';
363
+ time.textContent = formatTime();
364
+ div.appendChild(time);
365
+
366
+ const content = document.createElement('span');
367
+ content.textContent = line;
368
+ if (isError) content.className = 'log-error';
369
+ div.appendChild(content);
370
+
371
+ logsEl.appendChild(div);
372
+ if (autoScroll) logsEl.scrollTop = logsEl.scrollHeight;
373
+ }
374
+
375
+ function appendSystemLog(message) {
376
+ const div = document.createElement('div');
377
+ div.className = 'log-line';
378
+
379
+ const time = document.createElement('span');
380
+ time.className = 'log-time';
381
+ time.textContent = formatTime();
382
+ div.appendChild(time);
383
+
384
+ const content = document.createElement('span');
385
+ content.className = 'log-system';
386
+ content.textContent = message;
387
+ div.appendChild(content);
388
+
389
+ logsEl.appendChild(div);
341
390
  if (autoScroll) logsEl.scrollTop = logsEl.scrollHeight;
342
391
  }
343
392
 
344
393
  const evtSource = new EventSource('/api/events');
345
394
  evtSource.addEventListener('status', (e) => {
395
+ const data = JSON.parse(e.data);
396
+ if (data.type === 'group-started') {
397
+ appendSystemLog(\`[system] Group "\${data.groupName}" started\`);
398
+ } else if (data.type === 'group-stopped') {
399
+ appendSystemLog(\`[system] Group "\${data.groupName}" stopped\`);
400
+ } else if (data.type === 'item-restarted') {
401
+ appendSystemLog(\`[system] Item "\${data.groupName}/\${data.itemName}" restarted\`);
402
+ }
346
403
  fetchGroups();
347
404
  });
348
405
  evtSource.addEventListener('log', (e) => {