cligr 1.0.7 → 1.0.9

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 (35) 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 +103 -46
  29. package/docs/superpowers/specs/2026-04-13-improve-web-ui-console-design.md +38 -0
  30. package/package.json +1 -1
  31. package/src/commands/groups.ts +1 -1
  32. package/src/commands/ls.ts +1 -1
  33. package/src/commands/serve.ts +65 -8
  34. package/src/config/loader.ts +6 -2
  35. package/src/config/types.ts +1 -1
@@ -0,0 +1,460 @@
1
+ # Named Items Implementation Plan
2
+
3
+ > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
4
+
5
+ **Goal:** Replace array-based items with named object format for better service identification in logs, PID files, and ls output.
6
+
7
+ **Architecture:** Add `ItemEntry` type to normalize named items, update config loader to validate and normalize, pass ItemEntry through template expander to process manager.
8
+
9
+ **Tech Stack:** TypeScript, js-yaml, Node.js
10
+
11
+ ---
12
+
13
+ ## Task 1: Update Types
14
+
15
+ **Files:**
16
+ - Modify: `src/config/types.ts:5-10`
17
+
18
+ **Step 1: Add ItemEntry interface**
19
+
20
+ Add after `ToolConfig` interface (around line 4):
21
+
22
+ ```typescript
23
+ export interface ItemEntry {
24
+ name: string; // the key from config (e.g., "nginxService1")
25
+ value: string; // the value string (e.g., "nginx,8080")
26
+ }
27
+ ```
28
+
29
+ **Step 2: Update GroupConfig items type**
30
+
31
+ Change line 9 from:
32
+ ```typescript
33
+ items: string[];
34
+ ```
35
+
36
+ To:
37
+ ```typescript
38
+ items: Record<string, string>;
39
+ ```
40
+
41
+ **Step 3: Verify types compile**
42
+
43
+ Run: `npm run typecheck`
44
+ Expected: No errors
45
+
46
+ **Step 4: Commit**
47
+
48
+ ```bash
49
+ git add src/config/types.ts
50
+ git commit -m "feat(types): add ItemEntry and update GroupConfig items to named format"
51
+ ```
52
+
53
+ ---
54
+
55
+ ## Task 2: Update Config Loader - Validation
56
+
57
+ **Files:**
58
+ - Modify: `src/config/loader.ts:60-72`
59
+
60
+ **Step 1: Update validate method to validate items**
61
+
62
+ Replace the `validate` method (lines 60-72) with:
63
+
64
+ ```typescript
65
+ private validate(config: unknown): CliGrConfig {
66
+ if (!config || typeof config !== 'object') {
67
+ throw new ConfigError('Config must be an object');
68
+ }
69
+
70
+ const cfg = config as Record<string, unknown>;
71
+
72
+ if (!cfg.groups || typeof cfg.groups !== 'object') {
73
+ throw new ConfigError('Config must have a "groups" object');
74
+ }
75
+
76
+ // Validate each group's items
77
+ for (const [groupName, group] of Object.entries(cfg.groups as Record<string, unknown>)) {
78
+ if (group && typeof group === 'object') {
79
+ const groupObj = group as Record<string, unknown>;
80
+ this.validateItems(groupObj.items, groupName);
81
+ }
82
+ }
83
+
84
+ return cfg as unknown as CliGrConfig;
85
+ }
86
+
87
+ private validateItems(items: unknown, groupName: string): void {
88
+ if (!items || typeof items !== 'object' || Array.isArray(items)) {
89
+ throw new ConfigError(
90
+ `Group "${groupName}": items must be an object with named entries, e.g.:\n` +
91
+ ' items:\n' +
92
+ ' serviceName: "value1,value2"'
93
+ );
94
+ }
95
+
96
+ const seenNames = new Set<string>();
97
+
98
+ for (const [name, value] of Object.entries(items as Record<string, unknown>)) {
99
+ if (typeof value !== 'string') {
100
+ throw new ConfigError(`Group "${groupName}": item "${name}" must have a string value`);
101
+ }
102
+
103
+ if (seenNames.has(name)) {
104
+ throw new ConfigError(
105
+ `Group "${groupName}": duplicate item name "${name}". ` +
106
+ `Item names must be unique within a group.`
107
+ );
108
+ }
109
+ seenNames.add(name);
110
+ }
111
+ }
112
+ ```
113
+
114
+ **Step 2: Add ItemEntry import**
115
+
116
+ Add `ItemEntry` to the import at line 5:
117
+ ```typescript
118
+ import type { CliGrConfig, GroupConfig, ToolConfig, ItemEntry } from './types.js';
119
+ ```
120
+
121
+ **Step 3: Verify types compile**
122
+
123
+ Run: `npm run typecheck`
124
+ Expected: No errors
125
+
126
+ **Step 4: Commit**
127
+
128
+ ```bash
129
+ git add src/config/loader.ts
130
+ git commit -m "feat(loader): add validation for named items format"
131
+ ```
132
+
133
+ ---
134
+
135
+ ## Task 3: Update Config Loader - Normalization
136
+
137
+ **Files:**
138
+ - Modify: `src/config/loader.ts:74-100`
139
+
140
+ **Step 1: Add normalizeItems method**
141
+
142
+ Add after `validateItems` method:
143
+
144
+ ```typescript
145
+ private normalizeItems(items: Record<string, string>): ItemEntry[] {
146
+ return Object.entries(items).map(([name, value]) => ({
147
+ name,
148
+ value
149
+ }));
150
+ }
151
+ ```
152
+
153
+ **Step 2: Update getGroup return type and implementation**
154
+
155
+ Replace the `getGroup` method (lines 74-100) with:
156
+
157
+ ```typescript
158
+ getGroup(name: string): { config: GroupConfig; items: ItemEntry[]; tool: string | null; toolTemplate: string | null; params: Record<string, string> } {
159
+ const config = this.load();
160
+ const group = config.groups[name];
161
+
162
+ if (!group) {
163
+ const available = Object.keys(config.groups).join(', ');
164
+ throw new ConfigError(`Unknown group: ${name}. Available: ${available}`);
165
+ }
166
+
167
+ // Normalize items to ItemEntry[]
168
+ const items = this.normalizeItems(group.items);
169
+
170
+ // Resolve tool
171
+ let toolTemplate: string | null = null;
172
+ let tool: string | null = null;
173
+
174
+ if (config.tools && config.tools[group.tool]) {
175
+ toolTemplate = config.tools[group.tool].cmd;
176
+ tool = group.tool;
177
+ } else {
178
+ tool = null;
179
+ toolTemplate = null;
180
+ }
181
+
182
+ const params = group.params || {};
183
+
184
+ return { config: group, items, tool, toolTemplate, params };
185
+ }
186
+ ```
187
+
188
+ **Step 3: Verify types compile**
189
+
190
+ Run: `npm run typecheck`
191
+ Expected: Errors in ls.ts and up.ts (they need updates)
192
+
193
+ **Step 4: Commit**
194
+
195
+ ```bash
196
+ git add src/config/loader.ts
197
+ git commit -m "feat(loader): normalize items to ItemEntry[]"
198
+ ```
199
+
200
+ ---
201
+
202
+ ## Task 4: Update TemplateExpander
203
+
204
+ **Files:**
205
+ - Modify: `src/process/template.ts:26-44`
206
+
207
+ **Step 1: Add ItemEntry import**
208
+
209
+ Add at top of file:
210
+ ```typescript
211
+ import type { ProcessItem, ItemEntry } from '../config/types.js';
212
+ ```
213
+
214
+ **Step 2: Update expand method signature and implementation**
215
+
216
+ Replace the `expand` method (lines 26-44) with:
217
+
218
+ ```typescript
219
+ static expand(template: string, item: ItemEntry, index: number, params: Record<string, string> = {}): ProcessItem {
220
+ const args = item.value.split(',').map(s => s.trim());
221
+
222
+ // Use explicit name from ItemEntry
223
+ const name = item.name;
224
+
225
+ // Replace $1, $2, $3 etc. with args (positional params)
226
+ let fullCmd = template;
227
+ for (let i = args.length - 1; i >= 0; i--) {
228
+ const placeholder = `$${i + 1}`;
229
+ fullCmd = fullCmd.replaceAll(placeholder, args[i]);
230
+ }
231
+
232
+ // Replace named params ($name, $env, etc.) AFTER positional params
233
+ fullCmd = this.expandNamedParams(fullCmd, params);
234
+
235
+ return { name, args, fullCmd };
236
+ }
237
+ ```
238
+
239
+ **Step 3: Update parseItem method signature and implementation**
240
+
241
+ Replace the `parseItem` method (lines 54-97) with:
242
+
243
+ ```typescript
244
+ static parseItem(
245
+ tool: string | null,
246
+ toolTemplate: string | null,
247
+ item: ItemEntry,
248
+ index: number,
249
+ params: Record<string, string> = {}
250
+ ): ProcessItem {
251
+ if (toolTemplate) {
252
+ // Use registered tool template
253
+ const result = this.expand(toolTemplate, item, index, params);
254
+
255
+ // If there are more args than placeholders in the template, append them
256
+ const placeholdersInTemplate = (toolTemplate.match(/\$\d+/g) || []);
257
+ let maxPlaceholder = 0;
258
+ for (const p of placeholdersInTemplate) {
259
+ const num = parseInt(p.substring(1), 10);
260
+ if (num > maxPlaceholder) maxPlaceholder = num;
261
+ }
262
+
263
+ if (maxPlaceholder > 0 && result.args.length > maxPlaceholder) {
264
+ const remainingArgs = result.args.slice(maxPlaceholder);
265
+ result.fullCmd = `${result.fullCmd} ${remainingArgs.join(' ')}`;
266
+ }
267
+
268
+ return result;
269
+ } else {
270
+ // Direct executable - use tool as command prefix
271
+ const args = item.value.split(',').map(s => s.trim());
272
+ const name = item.name;
273
+ const fullCmd = tool ? `${tool} ${item.value}` : item.value;
274
+ return { name, args, fullCmd };
275
+ }
276
+ }
277
+ ```
278
+
279
+ **Step 4: Verify types compile**
280
+
281
+ Run: `npm run typecheck`
282
+ Expected: Errors in up.ts only
283
+
284
+ **Step 5: Commit**
285
+
286
+ ```bash
287
+ git add src/process/template.ts
288
+ git commit -m "feat(template): accept ItemEntry instead of raw string"
289
+ ```
290
+
291
+ ---
292
+
293
+ ## Task 5: Update ls Command
294
+
295
+ **Files:**
296
+ - Modify: `src/commands/ls.ts:14-17`
297
+
298
+ **Step 1: Update ls to use normalized items**
299
+
300
+ Replace the `lsCommand` function with:
301
+
302
+ ```typescript
303
+ export async function lsCommand(groupName: string): Promise<number> {
304
+ const loader = new ConfigLoader();
305
+
306
+ try {
307
+ const { config, items } = loader.getGroup(groupName);
308
+
309
+ console.log(`\nGroup: ${groupName}`);
310
+ console.log(`Tool: ${config.tool}`);
311
+ console.log(`Restart: ${config.restart}`);
312
+ console.log('\nItems:');
313
+
314
+ for (const item of items) {
315
+ console.log(` ${item.name}: ${item.value}`);
316
+ }
317
+
318
+ console.log('');
319
+
320
+ return 0;
321
+ } catch (err) {
322
+ console.error((err as Error).message);
323
+ return 1;
324
+ }
325
+ }
326
+ ```
327
+
328
+ **Step 2: Verify types compile**
329
+
330
+ Run: `npm run typecheck`
331
+ Expected: No errors
332
+
333
+ **Step 3: Commit**
334
+
335
+ ```bash
336
+ git add src/commands/ls.ts
337
+ git commit -m "feat(ls): display named items in name: value format"
338
+ ```
339
+
340
+ ---
341
+
342
+ ## Task 6: Update up Command
343
+
344
+ **Files:**
345
+ - Modify: `src/commands/up.ts:19-21`
346
+
347
+ **Step 1: Update up to use normalized items**
348
+
349
+ Replace line 16 with:
350
+ ```typescript
351
+ const { config, items, tool, toolTemplate, params } = loader.getGroup(groupName);
352
+ ```
353
+
354
+ Replace lines 19-21 with:
355
+ ```typescript
356
+ // Build process items
357
+ const processItems = items.map((item, index) =>
358
+ TemplateExpander.parseItem(tool, toolTemplate, item, index, params)
359
+ );
360
+
361
+ // Spawn all processes
362
+ manager.spawnGroup(groupName, processItems, config.restart);
363
+ ```
364
+
365
+ **Step 2: Verify types compile**
366
+
367
+ Run: `npm run typecheck`
368
+ Expected: No errors
369
+
370
+ **Step 3: Commit**
371
+
372
+ ```bash
373
+ git add src/commands/up.ts
374
+ git commit -m "feat(up): pass ItemEntry to template expander"
375
+ ```
376
+
377
+ ---
378
+
379
+ ## Task 7: Update Tests
380
+
381
+ **Files:**
382
+ - Modify: `tests/integration/process-manager.test.ts`
383
+
384
+ **Step 1: Run existing tests to identify failures**
385
+
386
+ Run: `npm test`
387
+ Expected: Some tests may fail due to config format change
388
+
389
+ **Step 2: Check test file for config usage**
390
+
391
+ Read the test file and identify any inline configs that use array format for items.
392
+
393
+ **Step 3: Update test configs to named format**
394
+
395
+ Change any test configs from:
396
+ ```yaml
397
+ items:
398
+ - "nginx,8080"
399
+ ```
400
+
401
+ To:
402
+ ```yaml
403
+ items:
404
+ nginxService: "nginx,8080"
405
+ ```
406
+
407
+ **Step 4: Run tests to verify**
408
+
409
+ Run: `npm test`
410
+ Expected: All tests pass
411
+
412
+ **Step 5: Commit**
413
+
414
+ ```bash
415
+ git add tests/integration/process-manager.test.ts
416
+ git commit -m "test: update tests to use named items format"
417
+ ```
418
+
419
+ ---
420
+
421
+ ## Task 8: Final Verification
422
+
423
+ **Step 1: Run full typecheck**
424
+
425
+ Run: `npm run typecheck`
426
+ Expected: No errors
427
+
428
+ **Step 2: Run all tests**
429
+
430
+ Run: `npm test`
431
+ Expected: All tests pass
432
+
433
+ **Step 3: Manual smoke test**
434
+
435
+ Create a test config file with named items and verify:
436
+ - `cligr groups` lists groups
437
+ - `cligr ls <group>` shows named items correctly
438
+ - `cligr up <group>` starts processes with correct names in logs
439
+
440
+ **Step 4: Final commit (if any fixes needed)**
441
+
442
+ ```bash
443
+ git add -A
444
+ git commit -m "fix: final cleanup for named items feature"
445
+ ```
446
+
447
+ ---
448
+
449
+ ## Summary
450
+
451
+ | Task | Description | Files |
452
+ |------|-------------|-------|
453
+ | 1 | Update types | `src/config/types.ts` |
454
+ | 2 | Add validation | `src/config/loader.ts` |
455
+ | 3 | Add normalization | `src/config/loader.ts` |
456
+ | 4 | Update template expander | `src/process/template.ts` |
457
+ | 5 | Update ls command | `src/commands/ls.ts` |
458
+ | 6 | Update up command | `src/commands/up.ts` |
459
+ | 7 | Update tests | `tests/integration/process-manager.test.ts` |
460
+ | 8 | Final verification | - |