@coralai/sps-cli 0.24.4 → 0.24.5

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 (66) hide show
  1. package/dist/core/config.d.ts.map +1 -1
  2. package/dist/core/config.js +3 -70
  3. package/dist/core/config.js.map +1 -1
  4. package/dist/core/config.test.d.ts +2 -0
  5. package/dist/core/config.test.d.ts.map +1 -0
  6. package/dist/core/config.test.js +363 -0
  7. package/dist/core/config.test.js.map +1 -0
  8. package/dist/core/lock.d.ts.map +1 -1
  9. package/dist/core/lock.js +8 -5
  10. package/dist/core/lock.js.map +1 -1
  11. package/dist/core/lock.test.d.ts +2 -0
  12. package/dist/core/lock.test.d.ts.map +1 -0
  13. package/dist/core/lock.test.js +103 -0
  14. package/dist/core/lock.test.js.map +1 -0
  15. package/dist/core/queue.test.d.ts +2 -0
  16. package/dist/core/queue.test.d.ts.map +1 -0
  17. package/dist/core/queue.test.js +99 -0
  18. package/dist/core/queue.test.js.map +1 -0
  19. package/dist/core/shellEnv.d.ts +20 -0
  20. package/dist/core/shellEnv.d.ts.map +1 -0
  21. package/dist/core/shellEnv.js +78 -0
  22. package/dist/core/shellEnv.js.map +1 -0
  23. package/dist/core/shellEnv.test.d.ts +2 -0
  24. package/dist/core/shellEnv.test.d.ts.map +1 -0
  25. package/dist/core/shellEnv.test.js +101 -0
  26. package/dist/core/shellEnv.test.js.map +1 -0
  27. package/dist/core/state.test.d.ts +2 -0
  28. package/dist/core/state.test.d.ts.map +1 -0
  29. package/dist/core/state.test.js +322 -0
  30. package/dist/core/state.test.js.map +1 -0
  31. package/dist/manager/completion-judge.test.d.ts +2 -0
  32. package/dist/manager/completion-judge.test.d.ts.map +1 -0
  33. package/dist/manager/completion-judge.test.js +215 -0
  34. package/dist/manager/completion-judge.test.js.map +1 -0
  35. package/dist/manager/integration-queue.test.d.ts +2 -0
  36. package/dist/manager/integration-queue.test.d.ts.map +1 -0
  37. package/dist/manager/integration-queue.test.js +195 -0
  38. package/dist/manager/integration-queue.test.js.map +1 -0
  39. package/dist/manager/resource-limiter.test.d.ts +2 -0
  40. package/dist/manager/resource-limiter.test.d.ts.map +1 -0
  41. package/dist/manager/resource-limiter.test.js +103 -0
  42. package/dist/manager/resource-limiter.test.js.map +1 -0
  43. package/dist/manager/supervisor.d.ts +0 -1
  44. package/dist/manager/supervisor.d.ts.map +1 -1
  45. package/dist/manager/supervisor.js +5 -40
  46. package/dist/manager/supervisor.js.map +1 -1
  47. package/dist/manager/supervisor.test.d.ts +2 -0
  48. package/dist/manager/supervisor.test.d.ts.map +1 -0
  49. package/dist/manager/supervisor.test.js +245 -0
  50. package/dist/manager/supervisor.test.js.map +1 -0
  51. package/dist/manager/worker-manager-impl.d.ts.map +1 -1
  52. package/dist/manager/worker-manager-impl.js +7 -0
  53. package/dist/manager/worker-manager-impl.js.map +1 -1
  54. package/dist/manager/worker-manager-impl.test.d.ts +2 -0
  55. package/dist/manager/worker-manager-impl.test.d.ts.map +1 -0
  56. package/dist/manager/worker-manager-impl.test.js +528 -0
  57. package/dist/manager/worker-manager-impl.test.js.map +1 -0
  58. package/dist/providers/outputParser.test.d.ts +2 -0
  59. package/dist/providers/outputParser.test.d.ts.map +1 -0
  60. package/dist/providers/outputParser.test.js +203 -0
  61. package/dist/providers/outputParser.test.js.map +1 -0
  62. package/dist/test-setup.d.ts +2 -0
  63. package/dist/test-setup.d.ts.map +1 -0
  64. package/dist/test-setup.js +18 -0
  65. package/dist/test-setup.js.map +1 -0
  66. package/package.json +7 -2
@@ -0,0 +1,203 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdtempSync, writeFileSync, rmSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { tmpdir } from 'node:os';
5
+ import { tailFile, fileSize, parseClaudeSessionId, parseCodexSessionId, isProcessAlive, extractLastAssistantText, } from './outputParser.js';
6
+ // ─── Helpers ──────────────────────────────────────────────────────
7
+ function makeTempDir() {
8
+ return mkdtempSync(join(tmpdir(), 'sps-parser-test-'));
9
+ }
10
+ // ─── Tests ────────────────────────────────────────────────────────
11
+ describe('tailFile', () => {
12
+ let tempDir;
13
+ beforeEach(() => { tempDir = makeTempDir(); });
14
+ afterEach(() => { rmSync(tempDir, { recursive: true, force: true }); });
15
+ it('returns empty string for nonexistent file', () => {
16
+ expect(tailFile('/tmp/does-not-exist-12345.txt', 10)).toBe('');
17
+ });
18
+ it('returns last N lines', () => {
19
+ const file = join(tempDir, 'log.txt');
20
+ writeFileSync(file, 'line1\nline2\nline3\nline4\nline5\n');
21
+ const result = tailFile(file, 2);
22
+ expect(result).toBe('line5\n'); // last 2 entries include trailing empty
23
+ });
24
+ it('returns entire file when fewer lines than requested', () => {
25
+ const file = join(tempDir, 'short.txt');
26
+ writeFileSync(file, 'only\n');
27
+ const result = tailFile(file, 100);
28
+ expect(result).toContain('only');
29
+ });
30
+ });
31
+ describe('fileSize', () => {
32
+ let tempDir;
33
+ beforeEach(() => { tempDir = makeTempDir(); });
34
+ afterEach(() => { rmSync(tempDir, { recursive: true, force: true }); });
35
+ it('returns 0 for nonexistent file', () => {
36
+ expect(fileSize('/tmp/no-file-12345')).toBe(0);
37
+ });
38
+ it('returns correct size', () => {
39
+ const file = join(tempDir, 'data.bin');
40
+ writeFileSync(file, 'hello world'); // 11 bytes
41
+ expect(fileSize(file)).toBe(11);
42
+ });
43
+ it('returns 0 for empty file', () => {
44
+ const file = join(tempDir, 'empty.txt');
45
+ writeFileSync(file, '');
46
+ expect(fileSize(file)).toBe(0);
47
+ });
48
+ });
49
+ describe('parseClaudeSessionId', () => {
50
+ let tempDir;
51
+ beforeEach(() => { tempDir = makeTempDir(); });
52
+ afterEach(() => { rmSync(tempDir, { recursive: true, force: true }); });
53
+ it('returns null for nonexistent file', () => {
54
+ expect(parseClaudeSessionId('/tmp/no-file-12345')).toBeNull();
55
+ });
56
+ it('extracts session_id from stream-json output', () => {
57
+ const file = join(tempDir, 'output.jsonl');
58
+ writeFileSync(file, [
59
+ '{"type":"system","message":"starting"}',
60
+ '{"type":"assistant","message":{"content":"hello"}}',
61
+ '{"type":"result","result":"done","session_id":"sess-abc-123"}',
62
+ ].join('\n'));
63
+ expect(parseClaudeSessionId(file)).toBe('sess-abc-123');
64
+ });
65
+ it('returns null when no session_id present', () => {
66
+ const file = join(tempDir, 'output.jsonl');
67
+ writeFileSync(file, '{"type":"result","result":"done"}\n');
68
+ expect(parseClaudeSessionId(file)).toBeNull();
69
+ });
70
+ it('skips non-JSON lines gracefully', () => {
71
+ const file = join(tempDir, 'mixed.jsonl');
72
+ writeFileSync(file, [
73
+ 'not json at all',
74
+ '{"session_id":"found-it"}',
75
+ 'another bad line',
76
+ ].join('\n'));
77
+ expect(parseClaudeSessionId(file)).toBe('found-it');
78
+ });
79
+ it('returns first session_id found', () => {
80
+ const file = join(tempDir, 'multi.jsonl');
81
+ writeFileSync(file, [
82
+ '{"session_id":"first"}',
83
+ '{"session_id":"second"}',
84
+ ].join('\n'));
85
+ expect(parseClaudeSessionId(file)).toBe('first');
86
+ });
87
+ });
88
+ describe('parseCodexSessionId', () => {
89
+ let tempDir;
90
+ beforeEach(() => { tempDir = makeTempDir(); });
91
+ afterEach(() => { rmSync(tempDir, { recursive: true, force: true }); });
92
+ it('returns null for nonexistent file', () => {
93
+ expect(parseCodexSessionId('/tmp/no-file-12345')).toBeNull();
94
+ });
95
+ it('extracts conversation_id', () => {
96
+ const file = join(tempDir, 'codex.jsonl');
97
+ writeFileSync(file, '{"conversation_id":"conv-xyz-789"}\n');
98
+ expect(parseCodexSessionId(file)).toBe('conv-xyz-789');
99
+ });
100
+ it('extracts session_id', () => {
101
+ const file = join(tempDir, 'codex.jsonl');
102
+ writeFileSync(file, '{"session_id":"sess-xyz"}\n');
103
+ expect(parseCodexSessionId(file)).toBe('sess-xyz');
104
+ });
105
+ it('extracts id from session_start event', () => {
106
+ const file = join(tempDir, 'codex.jsonl');
107
+ writeFileSync(file, '{"type":"session_start","id":"start-id-123"}\n');
108
+ expect(parseCodexSessionId(file)).toBe('start-id-123');
109
+ });
110
+ it('prefers conversation_id over session_id', () => {
111
+ const file = join(tempDir, 'codex.jsonl');
112
+ writeFileSync(file, '{"conversation_id":"conv","session_id":"sess"}\n');
113
+ expect(parseCodexSessionId(file)).toBe('conv');
114
+ });
115
+ it('returns null when no IDs present', () => {
116
+ const file = join(tempDir, 'codex.jsonl');
117
+ writeFileSync(file, '{"type":"message","text":"hello"}\n');
118
+ expect(parseCodexSessionId(file)).toBeNull();
119
+ });
120
+ });
121
+ describe('isProcessAlive', () => {
122
+ it('returns true for current process', () => {
123
+ expect(isProcessAlive(process.pid)).toBe(true);
124
+ });
125
+ it('returns false for invalid PID', () => {
126
+ expect(isProcessAlive(0)).toBe(false);
127
+ expect(isProcessAlive(-1)).toBe(false);
128
+ });
129
+ it('returns false for non-existent PID', () => {
130
+ // Use a very high PID that almost certainly doesn't exist
131
+ expect(isProcessAlive(4_000_000)).toBe(false);
132
+ });
133
+ });
134
+ describe('extractLastAssistantText', () => {
135
+ let tempDir;
136
+ beforeEach(() => { tempDir = makeTempDir(); });
137
+ afterEach(() => { rmSync(tempDir, { recursive: true, force: true }); });
138
+ it('returns empty string for nonexistent file', () => {
139
+ expect(extractLastAssistantText('/tmp/no-file-12345')).toBe('');
140
+ });
141
+ it('extracts text from assistant message with content array', () => {
142
+ const file = join(tempDir, 'output.jsonl');
143
+ writeFileSync(file, JSON.stringify({
144
+ type: 'assistant',
145
+ message: {
146
+ content: [
147
+ { type: 'text', text: 'I have completed the task.' },
148
+ ],
149
+ },
150
+ }) + '\n');
151
+ expect(extractLastAssistantText(file)).toBe('I have completed the task.');
152
+ });
153
+ it('extracts text from assistant message with string content', () => {
154
+ const file = join(tempDir, 'output.jsonl');
155
+ writeFileSync(file, JSON.stringify({
156
+ type: 'assistant',
157
+ message: { content: 'Done! All tasks finished.' },
158
+ }) + '\n');
159
+ expect(extractLastAssistantText(file)).toBe('Done! All tasks finished.');
160
+ });
161
+ it('extracts from result type', () => {
162
+ const file = join(tempDir, 'output.jsonl');
163
+ writeFileSync(file, JSON.stringify({
164
+ type: 'result',
165
+ result: 'Task completed successfully 🎉',
166
+ }) + '\n');
167
+ expect(extractLastAssistantText(file)).toBe('Task completed successfully 🎉');
168
+ });
169
+ it('returns last assistant text when multiple messages', () => {
170
+ const file = join(tempDir, 'output.jsonl');
171
+ const lines = [
172
+ JSON.stringify({ type: 'assistant', message: { content: 'Starting...' } }),
173
+ JSON.stringify({ type: 'assistant', message: { content: 'Working...' } }),
174
+ JSON.stringify({ type: 'assistant', message: { content: '全部完成' } }),
175
+ ];
176
+ writeFileSync(file, lines.join('\n') + '\n');
177
+ expect(extractLastAssistantText(file)).toBe('全部完成');
178
+ });
179
+ it('accumulates content_block_delta text', () => {
180
+ const file = join(tempDir, 'output.jsonl');
181
+ const lines = [
182
+ JSON.stringify({ type: 'content_block_delta', delta: { text: 'Hello ' } }),
183
+ JSON.stringify({ type: 'content_block_delta', delta: { text: 'world' } }),
184
+ ];
185
+ writeFileSync(file, lines.join('\n') + '\n');
186
+ expect(extractLastAssistantText(file)).toBe('Hello world');
187
+ });
188
+ it('skips non-JSON lines without error', () => {
189
+ const file = join(tempDir, 'mixed.jsonl');
190
+ writeFileSync(file, [
191
+ 'garbage line',
192
+ JSON.stringify({ type: 'assistant', message: { content: 'valid' } }),
193
+ 'more garbage',
194
+ ].join('\n') + '\n');
195
+ expect(extractLastAssistantText(file)).toBe('valid');
196
+ });
197
+ it('returns empty string for empty file', () => {
198
+ const file = join(tempDir, 'empty.jsonl');
199
+ writeFileSync(file, '');
200
+ expect(extractLastAssistantText(file)).toBe('');
201
+ });
202
+ });
203
+ //# sourceMappingURL=outputParser.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outputParser.test.js","sourceRoot":"","sources":["../../src/providers/outputParser.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=test-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-setup.d.ts","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Vitest global setup — suppress stderr log noise during tests.
3
+ *
4
+ * Production modules write [worker-manager], [supervisor], [completion-judge]
5
+ * logs to stderr. This silences them in test output.
6
+ */
7
+ import { vi } from 'vitest';
8
+ const originalWrite = process.stderr.write.bind(process.stderr);
9
+ vi.spyOn(process.stderr, 'write').mockImplementation((chunk, ...rest) => {
10
+ const msg = typeof chunk === 'string' ? chunk : '';
11
+ // Suppress known log prefixes from production code
12
+ if (/^\[(worker-manager|supervisor|completion-judge|integration-queue)\]/.test(msg)) {
13
+ return true;
14
+ }
15
+ // Pass through everything else (test framework output, actual errors)
16
+ return originalWrite(chunk, ...rest);
17
+ });
18
+ //# sourceMappingURL=test-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-setup.js","sourceRoot":"","sources":["../src/test-setup.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE5B,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;AAEhE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,CAAC,KAAU,EAAE,GAAG,IAAW,EAAE,EAAE;IAClF,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACnD,mDAAmD;IACnD,IAAI,qEAAqE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACpF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,sEAAsE;IACtE,OAAQ,aAAqB,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;AAChD,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coralai/sps-cli",
3
- "version": "0.24.4",
3
+ "version": "0.24.5",
4
4
  "description": "SPS CLI — AI-driven development pipeline orchestrator",
5
5
  "type": "module",
6
6
  "main": "dist/main.js",
@@ -11,6 +11,9 @@
11
11
  "build": "tsc && rm -rf ./project-template && cp -r ../project-template ./project-template",
12
12
  "dev": "tsx src/main.ts",
13
13
  "typecheck": "tsc --noEmit",
14
+ "test": "vitest run",
15
+ "test:watch": "vitest",
16
+ "test:coverage": "vitest run --coverage",
14
17
  "prepublishOnly": "npm run build"
15
18
  },
16
19
  "engines": {
@@ -33,8 +36,10 @@
33
36
  "license": "MIT",
34
37
  "devDependencies": {
35
38
  "@types/node": "^25.5.0",
39
+ "@vitest/coverage-v8": "^4.1.2",
36
40
  "tsx": "^4.21.0",
37
- "typescript": "^5.9.3"
41
+ "typescript": "^5.9.3",
42
+ "vitest": "^4.1.2"
38
43
  },
39
44
  "dependencies": {
40
45
  "node-pty": "^1.2.0-beta.12",