tribunal-kit 4.3.1 → 4.4.1

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 (67) hide show
  1. package/.agent/agents/api-architect.md +66 -66
  2. package/.agent/agents/db-latency-auditor.md +216 -216
  3. package/.agent/agents/precedence-reviewer.md +250 -250
  4. package/.agent/agents/resilience-reviewer.md +88 -88
  5. package/.agent/agents/schema-reviewer.md +67 -67
  6. package/.agent/agents/throughput-optimizer.md +299 -299
  7. package/.agent/agents/ui-ux-auditor.md +292 -292
  8. package/.agent/agents/vitals-reviewer.md +223 -223
  9. package/.agent/scripts/_colors.js +18 -18
  10. package/.agent/scripts/_utils.js +42 -42
  11. package/.agent/scripts/append_flow.js +72 -72
  12. package/.agent/scripts/auto_preview.js +197 -197
  13. package/.agent/scripts/bundle_analyzer.js +290 -290
  14. package/.agent/scripts/case_law_manager.js +17 -6
  15. package/.agent/scripts/checklist.js +266 -266
  16. package/.agent/scripts/colors.js +17 -17
  17. package/.agent/scripts/compress_skills.js +141 -141
  18. package/.agent/scripts/consolidate_skills.js +149 -149
  19. package/.agent/scripts/context_broker.js +611 -609
  20. package/.agent/scripts/deep_compress.js +150 -150
  21. package/.agent/scripts/dependency_analyzer.js +272 -272
  22. package/.agent/scripts/graph_builder.js +151 -37
  23. package/.agent/scripts/graph_visualizer.js +384 -0
  24. package/.agent/scripts/inner_loop_validator.js +451 -465
  25. package/.agent/scripts/lint_runner.js +187 -187
  26. package/.agent/scripts/minify_context.js +100 -100
  27. package/.agent/scripts/mutation_runner.js +280 -0
  28. package/.agent/scripts/patch_skills_meta.js +156 -156
  29. package/.agent/scripts/patch_skills_output.js +244 -244
  30. package/.agent/scripts/schema_validator.js +297 -297
  31. package/.agent/scripts/security_scan.js +303 -303
  32. package/.agent/scripts/session_manager.js +276 -276
  33. package/.agent/scripts/skill_evolution.js +644 -644
  34. package/.agent/scripts/skill_integrator.js +313 -313
  35. package/.agent/scripts/strengthen_skills.js +193 -193
  36. package/.agent/scripts/strip_tribunal.js +47 -47
  37. package/.agent/scripts/swarm_dispatcher.js +360 -360
  38. package/.agent/scripts/test_runner.js +193 -193
  39. package/.agent/scripts/utils.js +32 -32
  40. package/.agent/scripts/verify_all.js +257 -256
  41. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +1 -1
  42. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +1 -1
  43. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +1 -1
  44. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +1 -1
  45. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +1 -1
  46. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +1 -1
  47. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +1 -1
  48. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +1 -1
  49. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +1 -1
  50. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +1 -1
  51. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +1 -1
  52. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +1 -1
  53. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +1 -1
  54. package/.agent/skills/doc.md +1 -1
  55. package/.agent/skills/knowledge-graph/SKILL.md +32 -16
  56. package/.agent/skills/testing-patterns/SKILL.md +19 -2
  57. package/.agent/skills/ui-ux-pro-max/SKILL.md +480 -43
  58. package/.agent/workflows/generate.md +183 -183
  59. package/.agent/workflows/tribunal-speed.md +183 -183
  60. package/README.md +1 -1
  61. package/bin/tribunal-kit.js +134 -17
  62. package/package.json +6 -3
  63. package/scripts/changelog.js +167 -167
  64. package/scripts/sync-version.js +81 -81
  65. package/.agent/scripts/__pycache__/_colors.cpython-311.pyc +0 -0
  66. package/.agent/scripts/__pycache__/_utils.cpython-311.pyc +0 -0
  67. package/.agent/scripts/__pycache__/case_law_manager.cpython-311.pyc +0 -0
@@ -1,72 +1,72 @@
1
- #!/usr/bin/env node
2
- // append_flow.js — append Supreme Court section to AGENT_FLOW.md
3
- const fs = require('fs');
4
- const f = 'AGENT_FLOW.md';
5
-
6
- const appendix = [
7
- '',
8
- '---',
9
- '',
10
- '## Supreme Court Edition — Self-Learning Engine',
11
- '',
12
- 'Tribunal Kit v4.0+ ships two industry-first features that transform the',
13
- 'agent kit from a reactive reviewer into a **persistent engineering authority**.',
14
- '',
15
- '### 1 — Case Law Engine',
16
- '',
17
- 'Every rejected pattern becomes binding legal precedent.',
18
- '',
19
- '| Step | What Happens |',
20
- '|:-----|:-------------|',
21
- '| 1 | Developer rejects AI proposal |',
22
- '| 2 | Runs `case_law_manager.py add-case` |',
23
- '| 3 | diff + tags + reason stored in `.agent/history/case-law/` |',
24
- '| 4 | `precedence-reviewer` queries index on every future `/generate` or `/review` |',
25
- '| 5 | Jaccard tag match score >= 0.4 → PRECEDENCE HOLD |',
26
- '',
27
- '### 2 — Skill Evolution Forge',
28
- '',
29
- 'The agent kit writes its own skills by learning from your commits.',
30
- '',
31
- '| Step | What Happens |',
32
- '|:-----|:-------------|',
33
- '| 1 | Developer commits code different from AI proposal |',
34
- '| 2 | `tribunal-kit learn` (or `skill_evolution.py digest`) |',
35
- '| 3 | Semantic Delta Filter strips trivial noise (70-90% token reduction) |',
36
- '| 4 | Minimal LLM Reflection Prompt (< 500 tokens) |',
37
- '| 5 | YAML idioms merged into `.agent/skills/project-idioms/SKILL.md` |',
38
- '| 6 | All agents inherit these idioms on next activation |',
39
- '',
40
- '### CLI Commands (Supreme Court)',
41
- '',
42
- '| Command | Action |',
43
- '|:--------|:-------|',
44
- '| `tribunal-kit learn` | Run Skill Evolution + Case Law prompt |',
45
- '| `tribunal-kit learn --dry-run` | Preview delta without writing |',
46
- '| `tribunal-kit learn --head` | Diff last commit instead of staged |',
47
- '| `python .agent/scripts/case_law_manager.py add-case` | Record a rejection |',
48
- '| `python .agent/scripts/case_law_manager.py search-cases --query "..."` | Find precedents |',
49
- '| `python .agent/scripts/skill_evolution.py digest` | Run evolution cycle |',
50
- '| `python .agent/scripts/skill_evolution.py status` | Token savings report |',
51
- '',
52
- '### Review Order (Updated)',
53
- '',
54
- '```',
55
- '1. precedence-reviewer <- FIRST (Case Law check, zero LLM tokens)',
56
- '2. logic-reviewer',
57
- '3. security-auditor',
58
- '4. domain-specific reviewers',
59
- '5. Human Gate',
60
- '```',
61
- '',
62
- '### New Reviewer',
63
- '',
64
- '| Reviewer | Activates for | Catches |',
65
- '|:---------|:-------------|:--------|',
66
- '| `precedence-reviewer` | All domains | Violations of previously rejected patterns |',
67
- '',
68
- ].join('\n');
69
-
70
- let existing = fs.readFileSync(f, 'utf8').trimEnd();
71
- fs.writeFileSync(f, existing + appendix + '\n', 'utf8');
72
- console.log('AGENT_FLOW.md updated.');
1
+ #!/usr/bin/env node
2
+ // append_flow.js — append Supreme Court section to AGENT_FLOW.md
3
+ const fs = require('fs');
4
+ const f = 'AGENT_FLOW.md';
5
+
6
+ const appendix = [
7
+ '',
8
+ '---',
9
+ '',
10
+ '## Supreme Court Edition — Self-Learning Engine',
11
+ '',
12
+ 'Tribunal Kit v4.0+ ships two industry-first features that transform the',
13
+ 'agent kit from a reactive reviewer into a **persistent engineering authority**.',
14
+ '',
15
+ '### 1 — Case Law Engine',
16
+ '',
17
+ 'Every rejected pattern becomes binding legal precedent.',
18
+ '',
19
+ '| Step | What Happens |',
20
+ '|:-----|:-------------|',
21
+ '| 1 | Developer rejects AI proposal |',
22
+ '| 2 | Runs `case_law_manager.py add-case` |',
23
+ '| 3 | diff + tags + reason stored in `.agent/history/case-law/` |',
24
+ '| 4 | `precedence-reviewer` queries index on every future `/generate` or `/review` |',
25
+ '| 5 | Jaccard tag match score >= 0.4 → PRECEDENCE HOLD |',
26
+ '',
27
+ '### 2 — Skill Evolution Forge',
28
+ '',
29
+ 'The agent kit writes its own skills by learning from your commits.',
30
+ '',
31
+ '| Step | What Happens |',
32
+ '|:-----|:-------------|',
33
+ '| 1 | Developer commits code different from AI proposal |',
34
+ '| 2 | `tribunal-kit learn` (or `skill_evolution.py digest`) |',
35
+ '| 3 | Semantic Delta Filter strips trivial noise (70-90% token reduction) |',
36
+ '| 4 | Minimal LLM Reflection Prompt (< 500 tokens) |',
37
+ '| 5 | YAML idioms merged into `.agent/skills/project-idioms/SKILL.md` |',
38
+ '| 6 | All agents inherit these idioms on next activation |',
39
+ '',
40
+ '### CLI Commands (Supreme Court)',
41
+ '',
42
+ '| Command | Action |',
43
+ '|:--------|:-------|',
44
+ '| `tribunal-kit learn` | Run Skill Evolution + Case Law prompt |',
45
+ '| `tribunal-kit learn --dry-run` | Preview delta without writing |',
46
+ '| `tribunal-kit learn --head` | Diff last commit instead of staged |',
47
+ '| `python .agent/scripts/case_law_manager.py add-case` | Record a rejection |',
48
+ '| `python .agent/scripts/case_law_manager.py search-cases --query "..."` | Find precedents |',
49
+ '| `python .agent/scripts/skill_evolution.py digest` | Run evolution cycle |',
50
+ '| `python .agent/scripts/skill_evolution.py status` | Token savings report |',
51
+ '',
52
+ '### Review Order (Updated)',
53
+ '',
54
+ '```',
55
+ '1. precedence-reviewer <- FIRST (Case Law check, zero LLM tokens)',
56
+ '2. logic-reviewer',
57
+ '3. security-auditor',
58
+ '4. domain-specific reviewers',
59
+ '5. Human Gate',
60
+ '```',
61
+ '',
62
+ '### New Reviewer',
63
+ '',
64
+ '| Reviewer | Activates for | Catches |',
65
+ '|:---------|:-------------|:--------|',
66
+ '| `precedence-reviewer` | All domains | Violations of previously rejected patterns |',
67
+ '',
68
+ ].join('\n');
69
+
70
+ let existing = fs.readFileSync(f, 'utf8').trimEnd();
71
+ fs.writeFileSync(f, existing + appendix + '\n', 'utf8');
72
+ console.log('AGENT_FLOW.md updated.');
@@ -1,197 +1,197 @@
1
- #!/usr/bin/env node
2
- /**
3
- * auto_preview.js — Start, stop, or check a local development server.
4
- *
5
- * Usage:
6
- * node .agent/scripts/auto_preview.js start
7
- * node .agent/scripts/auto_preview.js stop
8
- * node .agent/scripts/auto_preview.js status
9
- * node .agent/scripts/auto_preview.js restart
10
- */
11
-
12
- 'use strict';
13
-
14
- const fs = require('fs');
15
- const path = require('path');
16
- const { spawn, execSync } = require('child_process');
17
- const net = require('net');
18
-
19
- const PID_FILE = ".preview.pid";
20
- const DEFAULT_PORT = 3000;
21
- const TIMEOUT_SECONDS = 30;
22
-
23
- const { GREEN, RED, YELLOW, BOLD, RESET } = require('./colors.js');
24
-
25
- function findStartCommand() {
26
- const pkgPath = path.resolve("package.json");
27
- if (!fs.existsSync(pkgPath)) return { cmd: [], found: false };
28
-
29
- try {
30
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
31
- const scripts = pkg.scripts || {};
32
- if (scripts.dev) return { cmd: ["npm", "run", "dev"], found: true };
33
- if (scripts.start) return { cmd: ["npm", "run", "start"], found: true };
34
- } catch {
35
- // Ignore
36
- }
37
- return { cmd: [], found: false };
38
- }
39
-
40
- function getPortFromEnv() {
41
- const envPath = path.resolve(".env");
42
- if (fs.existsSync(envPath)) {
43
- try {
44
- const data = fs.readFileSync(envPath, 'utf8');
45
- for (const line of data.split('\n')) {
46
- if (line.trim().startsWith("PORT=")) {
47
- return parseInt(line.split("=")[1].trim(), 10);
48
- }
49
- }
50
- } catch {}
51
- }
52
- return DEFAULT_PORT;
53
- }
54
-
55
- function isPortOpen(port) {
56
- return new Promise(resolve => {
57
- const client = new net.Socket();
58
- client.setTimeout(1000);
59
- client.once('connect', () => {
60
- client.destroy();
61
- resolve(true);
62
- }).once('timeout', () => {
63
- client.destroy();
64
- resolve(false);
65
- }).once('error', () => {
66
- resolve(false);
67
- }).connect(port, 'localhost');
68
- });
69
- }
70
-
71
- function readPid() {
72
- if (fs.existsSync(PID_FILE)) {
73
- try {
74
- return parseInt(fs.readFileSync(PID_FILE, 'utf8').trim(), 10);
75
- } catch {}
76
- }
77
- return null;
78
- }
79
-
80
- function writePid(pid) {
81
- fs.writeFileSync(PID_FILE, String(pid), 'utf8');
82
- }
83
-
84
- function clearPid() {
85
- if (fs.existsSync(PID_FILE)) {
86
- fs.unlinkSync(PID_FILE);
87
- }
88
- }
89
-
90
- async function startServer() {
91
- const port = getPortFromEnv();
92
-
93
- if (await isPortOpen(port)) {
94
- console.log(`${YELLOW}⚠️ Port ${port} is already in use.${RESET}`);
95
- const pid = readPid();
96
- if (pid) console.log(` Known PID: ${pid}`);
97
- return;
98
- }
99
-
100
- const { cmd, found } = findStartCommand();
101
- if (!found) {
102
- console.log(`${RED}❌ No dev/start script found.${RESET}`);
103
- console.log(` This project has no package.json, or its package.json has no 'dev' or 'start' script.`);
104
- console.log(` Add a script to package.json, or start your server manually.`);
105
- return;
106
- }
107
-
108
- console.log(`${BOLD}Starting: ${cmd.join(' ')}${RESET}`);
109
- // Adjust command for windows (npm.cmd instead of npm)
110
- const executable = process.platform === 'win32' ? `${cmd[0]}.cmd` : cmd[0];
111
-
112
- const proc = spawn(executable, cmd.slice(1), {
113
- stdio: 'pipe',
114
- detached: true
115
- });
116
-
117
- // Ignore children stdout inside detached mode to let node exit
118
- proc.stdout.unref();
119
- proc.stderr.unref();
120
- proc.unref();
121
-
122
- writePid(proc.pid);
123
-
124
- process.stdout.write(`Waiting for port ${port}…`);
125
- for (let i = 0; i < TIMEOUT_SECONDS; i++) {
126
- if (await isPortOpen(port)) {
127
- console.log(`\n${GREEN}✅ Server started${RESET}`);
128
- console.log(` URL: http://localhost:${port}`);
129
- console.log(` PID: ${proc.pid}`);
130
- console.log(` Command: ${cmd.join(' ')}`);
131
- console.log(`\nStop with: node .agent/scripts/auto_preview.js stop`);
132
- return;
133
- }
134
- process.stdout.write(".");
135
- await new Promise(r => setTimeout(r, 1000));
136
- }
137
-
138
- console.log(`\n${RED}❌ Server did not start within ${TIMEOUT_SECONDS}s${RESET}`);
139
- try {
140
- process.kill(proc.pid, 'SIGTERM');
141
- } catch {}
142
- clearPid();
143
- }
144
-
145
- function stopServer() {
146
- const pid = readPid();
147
- if (!pid) {
148
- console.log(`${YELLOW}⚠️ No stored server PID found${RESET}`);
149
- return;
150
- }
151
- try {
152
- process.kill(pid, 'SIGTERM');
153
- console.log(`${GREEN}✅ Server stopped (PID ${pid})${RESET}`);
154
- } catch {
155
- console.log(`${YELLOW}Process ${pid} was not running${RESET}`);
156
- } finally {
157
- clearPid();
158
- }
159
- }
160
-
161
- async function showStatus() {
162
- const port = getPortFromEnv();
163
- const pid = readPid();
164
- if (await isPortOpen(port)) {
165
- console.log(`${GREEN}🟢 Running — http://localhost:${port}${RESET}`);
166
- if (pid) console.log(` PID: ${pid}`);
167
- } else {
168
- console.log(`${RED}🔴 Not running on port ${port}${RESET}`);
169
- }
170
- }
171
-
172
- async function main() {
173
- const args = process.argv.slice(2);
174
- const actions = new Set(["start", "stop", "status", "restart"]);
175
-
176
- if (args.length < 1 || !actions.has(args[0])) {
177
- console.log(`Usage: node auto_preview.js [start|stop|status|restart]`);
178
- process.exit(1);
179
- }
180
-
181
- const action = args[0];
182
- if (action === "start") {
183
- await startServer();
184
- } else if (action === "stop") {
185
- stopServer();
186
- } else if (action === "status") {
187
- await showStatus();
188
- } else if (action === "restart") {
189
- stopServer();
190
- await new Promise(r => setTimeout(r, 1000));
191
- await startServer();
192
- }
193
- }
194
-
195
- if (require.main === module) {
196
- main();
197
- }
1
+ #!/usr/bin/env node
2
+ /**
3
+ * auto_preview.js — Start, stop, or check a local development server.
4
+ *
5
+ * Usage:
6
+ * node .agent/scripts/auto_preview.js start
7
+ * node .agent/scripts/auto_preview.js stop
8
+ * node .agent/scripts/auto_preview.js status
9
+ * node .agent/scripts/auto_preview.js restart
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+ const { spawn } = require('child_process');
17
+ const net = require('net');
18
+
19
+ const PID_FILE = ".preview.pid";
20
+ const DEFAULT_PORT = 3000;
21
+ const TIMEOUT_SECONDS = 30;
22
+
23
+ const { GREEN, RED, YELLOW, BOLD, RESET } = require('./colors.js');
24
+
25
+ function findStartCommand() {
26
+ const pkgPath = path.resolve("package.json");
27
+ if (!fs.existsSync(pkgPath)) return { cmd: [], found: false };
28
+
29
+ try {
30
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
31
+ const scripts = pkg.scripts || {};
32
+ if (scripts.dev) return { cmd: ["npm", "run", "dev"], found: true };
33
+ if (scripts.start) return { cmd: ["npm", "run", "start"], found: true };
34
+ } catch {
35
+ // Ignore
36
+ }
37
+ return { cmd: [], found: false };
38
+ }
39
+
40
+ function getPortFromEnv() {
41
+ const envPath = path.resolve(".env");
42
+ if (fs.existsSync(envPath)) {
43
+ try {
44
+ const data = fs.readFileSync(envPath, 'utf8');
45
+ for (const line of data.split('\n')) {
46
+ if (line.trim().startsWith("PORT=")) {
47
+ return parseInt(line.split("=")[1].trim(), 10);
48
+ }
49
+ }
50
+ } catch {}
51
+ }
52
+ return DEFAULT_PORT;
53
+ }
54
+
55
+ function isPortOpen(port) {
56
+ return new Promise(resolve => {
57
+ const client = new net.Socket();
58
+ client.setTimeout(1000);
59
+ client.once('connect', () => {
60
+ client.destroy();
61
+ resolve(true);
62
+ }).once('timeout', () => {
63
+ client.destroy();
64
+ resolve(false);
65
+ }).once('error', () => {
66
+ resolve(false);
67
+ }).connect(port, 'localhost');
68
+ });
69
+ }
70
+
71
+ function readPid() {
72
+ if (fs.existsSync(PID_FILE)) {
73
+ try {
74
+ return parseInt(fs.readFileSync(PID_FILE, 'utf8').trim(), 10);
75
+ } catch {}
76
+ }
77
+ return null;
78
+ }
79
+
80
+ function writePid(pid) {
81
+ fs.writeFileSync(PID_FILE, String(pid), 'utf8');
82
+ }
83
+
84
+ function clearPid() {
85
+ if (fs.existsSync(PID_FILE)) {
86
+ fs.unlinkSync(PID_FILE);
87
+ }
88
+ }
89
+
90
+ async function startServer() {
91
+ const port = getPortFromEnv();
92
+
93
+ if (await isPortOpen(port)) {
94
+ console.log(`${YELLOW}⚠️ Port ${port} is already in use.${RESET}`);
95
+ const pid = readPid();
96
+ if (pid) console.log(` Known PID: ${pid}`);
97
+ return;
98
+ }
99
+
100
+ const { cmd, found } = findStartCommand();
101
+ if (!found) {
102
+ console.log(`${RED}❌ No dev/start script found.${RESET}`);
103
+ console.log(` This project has no package.json, or its package.json has no 'dev' or 'start' script.`);
104
+ console.log(` Add a script to package.json, or start your server manually.`);
105
+ return;
106
+ }
107
+
108
+ console.log(`${BOLD}Starting: ${cmd.join(' ')}${RESET}`);
109
+ // Adjust command for windows (npm.cmd instead of npm)
110
+ const executable = process.platform === 'win32' ? `${cmd[0]}.cmd` : cmd[0];
111
+
112
+ const proc = spawn(executable, cmd.slice(1), {
113
+ stdio: 'pipe',
114
+ detached: true
115
+ });
116
+
117
+ // Ignore children stdout inside detached mode to let node exit
118
+ proc.stdout.unref();
119
+ proc.stderr.unref();
120
+ proc.unref();
121
+
122
+ writePid(proc.pid);
123
+
124
+ process.stdout.write(`Waiting for port ${port}…`);
125
+ for (let i = 0; i < TIMEOUT_SECONDS; i++) {
126
+ if (await isPortOpen(port)) {
127
+ console.log(`\n${GREEN}✅ Server started${RESET}`);
128
+ console.log(` URL: http://localhost:${port}`);
129
+ console.log(` PID: ${proc.pid}`);
130
+ console.log(` Command: ${cmd.join(' ')}`);
131
+ console.log(`\nStop with: node .agent/scripts/auto_preview.js stop`);
132
+ return;
133
+ }
134
+ process.stdout.write(".");
135
+ await new Promise(r => setTimeout(r, 1000));
136
+ }
137
+
138
+ console.log(`\n${RED}❌ Server did not start within ${TIMEOUT_SECONDS}s${RESET}`);
139
+ try {
140
+ process.kill(proc.pid, 'SIGTERM');
141
+ } catch {}
142
+ clearPid();
143
+ }
144
+
145
+ function stopServer() {
146
+ const pid = readPid();
147
+ if (!pid) {
148
+ console.log(`${YELLOW}⚠️ No stored server PID found${RESET}`);
149
+ return;
150
+ }
151
+ try {
152
+ process.kill(pid, 'SIGTERM');
153
+ console.log(`${GREEN}✅ Server stopped (PID ${pid})${RESET}`);
154
+ } catch {
155
+ console.log(`${YELLOW}Process ${pid} was not running${RESET}`);
156
+ } finally {
157
+ clearPid();
158
+ }
159
+ }
160
+
161
+ async function showStatus() {
162
+ const port = getPortFromEnv();
163
+ const pid = readPid();
164
+ if (await isPortOpen(port)) {
165
+ console.log(`${GREEN}🟢 Running — http://localhost:${port}${RESET}`);
166
+ if (pid) console.log(` PID: ${pid}`);
167
+ } else {
168
+ console.log(`${RED}🔴 Not running on port ${port}${RESET}`);
169
+ }
170
+ }
171
+
172
+ async function main() {
173
+ const args = process.argv.slice(2);
174
+ const actions = new Set(["start", "stop", "status", "restart"]);
175
+
176
+ if (args.length < 1 || !actions.has(args[0])) {
177
+ console.log(`Usage: node auto_preview.js [start|stop|status|restart]`);
178
+ process.exit(1);
179
+ }
180
+
181
+ const action = args[0];
182
+ if (action === "start") {
183
+ await startServer();
184
+ } else if (action === "stop") {
185
+ stopServer();
186
+ } else if (action === "status") {
187
+ await showStatus();
188
+ } else if (action === "restart") {
189
+ stopServer();
190
+ await new Promise(r => setTimeout(r, 1000));
191
+ await startServer();
192
+ }
193
+ }
194
+
195
+ if (require.main === module) {
196
+ main();
197
+ }