chati-dev 2.0.9 → 2.1.2

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 (134) hide show
  1. package/README.md +167 -176
  2. package/assets/logo - co/314/201pia.png +0 -0
  3. package/assets/logo - co/314/201pia.svg" +42 -0
  4. package/bin/chati.js +3 -3
  5. package/framework/agents/{planning → discover}/brief.md +24 -2
  6. package/framework/agents/{planning → discover}/brownfield-wu.md +1 -1
  7. package/framework/agents/{planning → discover}/greenfield-wu.md +1 -1
  8. package/framework/agents/{planning → plan}/architect.md +1 -1
  9. package/framework/agents/{planning → plan}/detail.md +1 -1
  10. package/framework/agents/{planning → plan}/phases.md +1 -1
  11. package/framework/agents/{planning → plan}/tasks.md +1 -1
  12. package/framework/agents/{planning → plan}/ux.md +1 -1
  13. package/framework/agents/quality/qa-implementation.md +53 -4
  14. package/framework/agents/quality/qa-planning.md +53 -2
  15. package/framework/config.yaml +3 -3
  16. package/framework/constitution.md +4 -4
  17. package/framework/context/quality.md +1 -1
  18. package/framework/context/root.md +5 -4
  19. package/framework/data/entity-registry.yaml +35 -11
  20. package/framework/domains/agents/brief.yaml +14 -0
  21. package/framework/domains/agents/qa-implementation.yaml +16 -0
  22. package/framework/domains/agents/qa-planning.yaml +16 -0
  23. package/framework/domains/constitution.yaml +6 -2
  24. package/framework/domains/workflows/brownfield-discovery.yaml +5 -4
  25. package/framework/domains/workflows/quick-flow.yaml +23 -0
  26. package/framework/hooks/mode-governance.js +14 -2
  27. package/framework/hooks/model-governance.js +5 -1
  28. package/framework/hooks/prism-engine.js +1 -1
  29. package/framework/hooks/session-digest.js +6 -2
  30. package/framework/hooks/settings.json +11 -0
  31. package/framework/i18n/en.yaml +3 -3
  32. package/framework/i18n/es.yaml +3 -3
  33. package/framework/i18n/fr.yaml +3 -3
  34. package/framework/i18n/pt.yaml +3 -3
  35. package/framework/intelligence/context-engine.md +2 -2
  36. package/framework/intelligence/decision-engine.md +2 -2
  37. package/framework/manifest.json +5 -0
  38. package/framework/manifest.sig +1 -0
  39. package/framework/orchestrator/chati.md +68 -20
  40. package/framework/patterns/elicitation-library.yaml +592 -0
  41. package/framework/patterns/elicitation.md +2 -0
  42. package/framework/quality-gates/planning-gate.md +1 -1
  43. package/framework/schemas/session.schema.json +36 -3
  44. package/framework/tasks/architect-api-design.md +1 -1
  45. package/framework/tasks/architect-consolidate.md +1 -1
  46. package/framework/tasks/architect-db-design.md +1 -1
  47. package/framework/tasks/architect-design.md +1 -1
  48. package/framework/tasks/architect-security-review.md +1 -1
  49. package/framework/tasks/architect-stack-selection.md +1 -1
  50. package/framework/tasks/brief-consolidate.md +1 -1
  51. package/framework/tasks/brief-constraint-identify.md +1 -1
  52. package/framework/tasks/brief-extract-requirements.md +1 -1
  53. package/framework/tasks/brief-stakeholder-map.md +1 -1
  54. package/framework/tasks/brief-validate-completeness.md +1 -1
  55. package/framework/tasks/brownfield-wu-architecture-map.md +1 -1
  56. package/framework/tasks/brownfield-wu-deep-discovery.md +1 -1
  57. package/framework/tasks/brownfield-wu-dependency-scan.md +1 -1
  58. package/framework/tasks/brownfield-wu-migration-plan.md +1 -1
  59. package/framework/tasks/brownfield-wu-report.md +1 -1
  60. package/framework/tasks/brownfield-wu-risk-assess.md +1 -1
  61. package/framework/tasks/detail-acceptance-criteria.md +1 -1
  62. package/framework/tasks/detail-consolidate.md +1 -1
  63. package/framework/tasks/detail-edge-case-analysis.md +1 -1
  64. package/framework/tasks/detail-expand-prd.md +1 -1
  65. package/framework/tasks/detail-nfr-extraction.md +1 -1
  66. package/framework/tasks/greenfield-wu-analyze-empty.md +1 -1
  67. package/framework/tasks/greenfield-wu-report.md +1 -1
  68. package/framework/tasks/greenfield-wu-scaffold-detection.md +1 -1
  69. package/framework/tasks/greenfield-wu-tech-stack-assess.md +1 -1
  70. package/framework/tasks/orchestrator-deviation.md +2 -2
  71. package/framework/tasks/orchestrator-escalate.md +1 -1
  72. package/framework/tasks/orchestrator-handoff.md +3 -3
  73. package/framework/tasks/orchestrator-mode-switch.md +16 -5
  74. package/framework/tasks/orchestrator-preview.md +181 -0
  75. package/framework/tasks/orchestrator-resume.md +2 -2
  76. package/framework/tasks/orchestrator-route.md +5 -4
  77. package/framework/tasks/orchestrator-status.md +4 -4
  78. package/framework/tasks/orchestrator-suggest-mode.md +7 -7
  79. package/framework/tasks/phases-breakdown.md +1 -1
  80. package/framework/tasks/phases-dependency-mapping.md +1 -1
  81. package/framework/tasks/phases-mvp-scoping.md +1 -1
  82. package/framework/tasks/qa-impl-consolidate.md +2 -2
  83. package/framework/tasks/qa-impl-verdict.md +2 -2
  84. package/framework/tasks/qa-planning-consolidate.md +1 -1
  85. package/framework/tasks/qa-planning-coverage-plan.md +2 -2
  86. package/framework/tasks/qa-planning-gate-define.md +1 -1
  87. package/framework/tasks/qa-planning-risk-matrix.md +1 -1
  88. package/framework/tasks/qa-planning-test-strategy.md +1 -1
  89. package/framework/tasks/tasks-acceptance-write.md +1 -1
  90. package/framework/tasks/tasks-consolidate.md +1 -1
  91. package/framework/tasks/tasks-decompose.md +1 -1
  92. package/framework/tasks/tasks-estimate.md +1 -1
  93. package/framework/tasks/ux-a11y-check.md +1 -1
  94. package/framework/tasks/ux-component-map.md +1 -1
  95. package/framework/tasks/ux-consolidate.md +1 -1
  96. package/framework/tasks/ux-user-flow.md +1 -1
  97. package/framework/tasks/ux-wireframe.md +1 -1
  98. package/framework/templates/quick-brief-tmpl.yaml +90 -0
  99. package/framework/workflows/brownfield-discovery.yaml +7 -7
  100. package/framework/workflows/brownfield-fullstack.yaml +24 -10
  101. package/framework/workflows/brownfield-service.yaml +41 -5
  102. package/framework/workflows/brownfield-ui.yaml +41 -5
  103. package/framework/workflows/greenfield-fullstack.yaml +24 -10
  104. package/framework/workflows/quick-flow.yaml +125 -0
  105. package/package.json +2 -2
  106. package/scripts/bundle-framework.js +1 -1
  107. package/scripts/populate-entity-registry.js +1 -1
  108. package/scripts/semantic-lint.js +2 -2
  109. package/scripts/validate-agents.js +2 -1
  110. package/scripts/validate-package.js +1 -1
  111. package/scripts/validate-tasks.js +1 -1
  112. package/src/autonomy/progress-reporter.js +26 -19
  113. package/src/context/engine.js +1 -1
  114. package/src/context/layers/l1-global.js +17 -5
  115. package/src/dashboard/layout.js +2 -1
  116. package/src/gates/g1-planning-complete.js +10 -10
  117. package/src/installer/core.js +14 -12
  118. package/src/installer/templates.js +1 -1
  119. package/src/installer/validator.js +8 -8
  120. package/src/intelligence/registry-manager.js +4 -4
  121. package/src/memory/session-digest.js +1 -1
  122. package/src/orchestrator/agent-selector.js +11 -11
  123. package/src/orchestrator/intent-classifier.js +12 -8
  124. package/src/orchestrator/pipeline-manager.js +100 -3
  125. package/src/orchestrator/pipeline-state.js +1 -1
  126. package/src/orchestrator/session-manager.js +13 -4
  127. package/src/preview/detector.js +238 -0
  128. package/src/preview/index.js +20 -0
  129. package/src/preview/launcher.js +235 -0
  130. package/src/preview/log-buffer.js +103 -0
  131. package/src/tasks/executor.js +1 -1
  132. package/src/tasks/loader.js +1 -1
  133. package/src/terminal/prompt-builder.js +10 -10
  134. package/src/wizard/i18n.js +3 -3
@@ -0,0 +1,235 @@
1
+ /**
2
+ * @fileoverview Preview server lifecycle management.
3
+ * Spawns dev servers, finds available ports, health-checks, and opens browsers.
4
+ * NEVER kills existing processes — only manages servers it spawns.
5
+ */
6
+
7
+ import net from 'net';
8
+ import http from 'http';
9
+ import { spawn } from 'child_process';
10
+ import { platform } from 'os';
11
+ import { LogBuffer } from './log-buffer.js';
12
+
13
+ /**
14
+ * Test if a port is available (not in use by another process).
15
+ * Uses net.createServer to probe — never kills existing services.
16
+ *
17
+ * @param {number} port
18
+ * @returns {Promise<boolean>}
19
+ */
20
+ function isPortAvailable(port) {
21
+ return new Promise((resolve) => {
22
+ const server = net.createServer();
23
+ server.once('error', () => resolve(false));
24
+ server.once('listening', () => {
25
+ server.close(() => resolve(true));
26
+ });
27
+ server.listen(port);
28
+ });
29
+ }
30
+
31
+ /**
32
+ * Find an available port starting from a given number.
33
+ * Scans sequentially — NEVER kills existing processes on occupied ports.
34
+ *
35
+ * @param {number} [startPort=3000] - Port to start scanning from
36
+ * @param {number} [maxAttempts=20] - Maximum ports to try
37
+ * @returns {Promise<number>} Available port number
38
+ * @throws {Error} If no port is available within range
39
+ */
40
+ export async function findAvailablePort(startPort = 3000, maxAttempts = 20) {
41
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
42
+ const port = startPort + attempt;
43
+ const available = await isPortAvailable(port);
44
+ if (available) return port;
45
+ }
46
+
47
+ throw new Error(
48
+ `No available port found in range ${startPort}-${startPort + maxAttempts - 1}`,
49
+ );
50
+ }
51
+
52
+ /**
53
+ * Wait for an HTTP server to respond on the given URL.
54
+ * Polls with exponential backoff until the server is ready.
55
+ *
56
+ * @param {string} url - URL to check (e.g., "http://localhost:3000")
57
+ * @param {number} [timeout=30000] - Max wait time in ms
58
+ * @returns {Promise<void>}
59
+ * @throws {Error} If server doesn't respond within timeout
60
+ */
61
+ export function waitForServer(url, timeout = 30000) {
62
+ const startTime = Date.now();
63
+ let delay = 500;
64
+
65
+ return new Promise((resolve, reject) => {
66
+ function attempt() {
67
+ if (Date.now() - startTime > timeout) {
68
+ reject(new Error(`Server at ${url} did not respond within ${timeout}ms`));
69
+ return;
70
+ }
71
+
72
+ const req = http.get(url, (res) => {
73
+ // Any response (even 404) means server is up
74
+ res.resume();
75
+ resolve();
76
+ });
77
+
78
+ req.on('error', () => {
79
+ // Server not ready yet — retry with backoff
80
+ delay = Math.min(delay * 1.5, 3000);
81
+ setTimeout(attempt, delay);
82
+ });
83
+
84
+ req.setTimeout(2000, () => {
85
+ req.destroy();
86
+ setTimeout(attempt, delay);
87
+ });
88
+ }
89
+
90
+ attempt();
91
+ });
92
+ }
93
+
94
+ /**
95
+ * Open a URL in the system's default browser.
96
+ *
97
+ * @param {string} url - URL to open
98
+ * @returns {void}
99
+ */
100
+ export function openBrowser(url) {
101
+ const os = platform();
102
+ let command;
103
+ let args;
104
+
105
+ if (os === 'darwin') {
106
+ command = 'open';
107
+ args = [url];
108
+ } else if (os === 'win32') {
109
+ command = 'cmd';
110
+ args = ['/c', 'start', url];
111
+ } else {
112
+ command = 'xdg-open';
113
+ args = [url];
114
+ }
115
+
116
+ const proc = spawn(command, args, { stdio: 'ignore', detached: true });
117
+ proc.unref();
118
+ }
119
+
120
+ /**
121
+ * Gracefully kill a child process.
122
+ * Sends SIGTERM, waits 3s, then SIGKILL if still alive.
123
+ *
124
+ * @param {import('child_process').ChildProcess} proc
125
+ * @returns {Promise<void>}
126
+ */
127
+ function killProcess(proc) {
128
+ return new Promise((resolve) => {
129
+ if (!proc || proc.killed || proc.exitCode !== null) {
130
+ resolve();
131
+ return;
132
+ }
133
+
134
+ const forceKillTimer = setTimeout(() => {
135
+ try {
136
+ proc.kill('SIGKILL');
137
+ } catch {
138
+ // Already dead
139
+ }
140
+ resolve();
141
+ }, 3000);
142
+
143
+ proc.once('exit', () => {
144
+ clearTimeout(forceKillTimer);
145
+ resolve();
146
+ });
147
+
148
+ try {
149
+ proc.kill('SIGTERM');
150
+ } catch {
151
+ clearTimeout(forceKillTimer);
152
+ resolve();
153
+ }
154
+ });
155
+ }
156
+
157
+ /**
158
+ * Launch a preview server for the project.
159
+ *
160
+ * @param {string} projectDir - Absolute path to project root
161
+ * @param {object} options
162
+ * @param {string} options.command - Command to run (e.g., "npm")
163
+ * @param {string[]} options.args - Command arguments (e.g., ["run", "dev"])
164
+ * @param {number} options.port - Port to use
165
+ * @param {string} [options.framework='unknown'] - Detected framework name
166
+ * @param {boolean} [options.openBrowser=true] - Open browser after server is ready
167
+ * @param {number} [options.timeout=30000] - Health check timeout in ms
168
+ * @returns {Promise<{ url: string, port: number, pid: number, framework: string, logs: LogBuffer, kill: () => Promise<void> }>}
169
+ */
170
+ export async function launchPreview(projectDir, options) {
171
+ const {
172
+ command,
173
+ args,
174
+ port,
175
+ framework = 'unknown',
176
+ openBrowser: shouldOpenBrowser = true,
177
+ timeout = 30000,
178
+ } = options;
179
+
180
+ const logs = new LogBuffer();
181
+ const url = `http://localhost:${port}`;
182
+
183
+ // Spawn the dev server
184
+ const proc = spawn(command, args, {
185
+ cwd: projectDir,
186
+ env: { ...process.env, PORT: String(port) },
187
+ stdio: ['ignore', 'pipe', 'pipe'],
188
+ });
189
+
190
+ // Capture stdout
191
+ if (proc.stdout) {
192
+ proc.stdout.on('data', (data) => {
193
+ for (const line of data.toString().split('\n')) {
194
+ if (line.trim()) logs.append(line, 'stdout');
195
+ }
196
+ });
197
+ }
198
+
199
+ // Capture stderr
200
+ if (proc.stderr) {
201
+ proc.stderr.on('data', (data) => {
202
+ for (const line of data.toString().split('\n')) {
203
+ if (line.trim()) logs.append(line, 'stderr');
204
+ }
205
+ });
206
+ }
207
+
208
+ // Handle spawn errors
209
+ const spawnError = await new Promise((resolve) => {
210
+ proc.once('error', (err) => resolve(err));
211
+ // Give it a moment to fail or succeed
212
+ setTimeout(() => resolve(null), 500);
213
+ });
214
+
215
+ if (spawnError) {
216
+ throw new Error(`Failed to start dev server: ${spawnError.message}`);
217
+ }
218
+
219
+ // Wait for the server to be ready
220
+ await waitForServer(url, timeout);
221
+
222
+ // Open browser
223
+ if (shouldOpenBrowser) {
224
+ openBrowser(url);
225
+ }
226
+
227
+ return {
228
+ url,
229
+ port,
230
+ pid: proc.pid,
231
+ framework,
232
+ logs,
233
+ kill: () => killProcess(proc),
234
+ };
235
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * @fileoverview Circular log buffer for capturing server output.
3
+ * Used by the preview launcher to collect stdout/stderr from dev servers.
4
+ * Provides filtered views (errors, recent) for Dev agent context.
5
+ */
6
+
7
+ /**
8
+ * Circular buffer that captures server logs with metadata.
9
+ * Automatically evicts oldest entries when capacity is reached.
10
+ */
11
+ export class LogBuffer {
12
+ #lines = [];
13
+ #maxLines;
14
+
15
+ /**
16
+ * @param {number} [maxLines=200] - Maximum lines to retain
17
+ */
18
+ constructor(maxLines = 200) {
19
+ this.#maxLines = maxLines;
20
+ }
21
+
22
+ /**
23
+ * Append a log line.
24
+ * @param {string} line - Raw output line
25
+ * @param {'stdout'|'stderr'} [stream='stdout'] - Source stream
26
+ */
27
+ append(line, stream = 'stdout') {
28
+ if (typeof line !== 'string') return;
29
+
30
+ this.#lines.push({
31
+ line: line.trim(),
32
+ stream,
33
+ timestamp: Date.now(),
34
+ });
35
+
36
+ // Evict oldest when over capacity
37
+ if (this.#lines.length > this.#maxLines) {
38
+ this.#lines.shift();
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Get all buffered lines.
44
+ * @returns {Array<{ line: string, stream: string, timestamp: number }>}
45
+ */
46
+ getAll() {
47
+ return [...this.#lines];
48
+ }
49
+
50
+ /**
51
+ * Get lines that look like errors or warnings.
52
+ * Includes all stderr lines + stdout lines matching error patterns.
53
+ * @returns {Array<{ line: string, stream: string, timestamp: number }>}
54
+ */
55
+ getErrors() {
56
+ return this.#lines.filter(
57
+ (l) =>
58
+ l.stream === 'stderr' ||
59
+ /(error|warn(ing)?|exception|failed|fatal|panic|unhandled)/i.test(l.line),
60
+ );
61
+ }
62
+
63
+ /**
64
+ * Get the N most recent lines.
65
+ * @param {number} [n=50] - Number of lines
66
+ * @returns {Array<{ line: string, stream: string, timestamp: number }>}
67
+ */
68
+ getRecent(n = 50) {
69
+ return this.#lines.slice(-n);
70
+ }
71
+
72
+ /**
73
+ * Get total number of buffered lines.
74
+ * @returns {number}
75
+ */
76
+ get size() {
77
+ return this.#lines.length;
78
+ }
79
+
80
+ /**
81
+ * Clear all buffered lines.
82
+ */
83
+ clear() {
84
+ this.#lines = [];
85
+ }
86
+
87
+ /**
88
+ * Format logs as structured context for the Dev agent.
89
+ * Includes error summary + recent output for debugging.
90
+ * @returns {{ totalLines: number, errorCount: number, errors: string[], recentOutput: string[] }}
91
+ */
92
+ toContext() {
93
+ const errors = this.getErrors();
94
+ const recent = this.getRecent(50);
95
+
96
+ return {
97
+ totalLines: this.#lines.length,
98
+ errorCount: errors.length,
99
+ errors: errors.map((e) => e.line),
100
+ recentOutput: recent.map((l) => `[${l.stream}] ${l.line}`),
101
+ };
102
+ }
103
+ }
@@ -186,7 +186,7 @@ function sanitizeSessionState(state) {
186
186
  if (!state) return null;
187
187
 
188
188
  return {
189
- mode: state.mode || 'planning',
189
+ mode: state.mode || 'discover',
190
190
  current_agent: state.current_agent || null,
191
191
  pipeline_position: state.pipeline_position || null,
192
192
  completed_agents: state.completed_agents || [],
@@ -59,7 +59,7 @@ export function parseTaskContent(content, source = 'unknown') {
59
59
  id: meta.id,
60
60
  agent: meta.agent,
61
61
  trigger: meta.trigger || 'orchestrator',
62
- phase: meta.phase || 'planning',
62
+ phase: meta.phase || 'plan',
63
63
  requires_input: meta.requires_input === true || meta.requires_input === 'true',
64
64
  parallelizable: meta.parallelizable === true || meta.parallelizable === 'true',
65
65
  outputs: parseArray(meta.outputs),
@@ -37,14 +37,14 @@ const AGENT_MODELS = {
37
37
  * Map of agent names to their definition file paths (relative to project root).
38
38
  */
39
39
  export const AGENT_FILE_MAP = {
40
- 'greenfield-wu': 'chati.dev/agents/planning/greenfield-wu.md',
41
- 'brownfield-wu': 'chati.dev/agents/planning/brownfield-wu.md',
42
- brief: 'chati.dev/agents/planning/brief.md',
43
- detail: 'chati.dev/agents/planning/detail.md',
44
- architect: 'chati.dev/agents/planning/architect.md',
45
- ux: 'chati.dev/agents/planning/ux.md',
46
- phases: 'chati.dev/agents/planning/phases.md',
47
- tasks: 'chati.dev/agents/planning/tasks.md',
40
+ 'greenfield-wu': 'chati.dev/agents/discover/greenfield-wu.md',
41
+ 'brownfield-wu': 'chati.dev/agents/discover/brownfield-wu.md',
42
+ brief: 'chati.dev/agents/discover/brief.md',
43
+ detail: 'chati.dev/agents/plan/detail.md',
44
+ architect: 'chati.dev/agents/plan/architect.md',
45
+ ux: 'chati.dev/agents/plan/ux.md',
46
+ phases: 'chati.dev/agents/plan/phases.md',
47
+ tasks: 'chati.dev/agents/plan/tasks.md',
48
48
  'qa-planning': 'chati.dev/agents/quality/qa-planning.md',
49
49
  dev: 'chati.dev/agents/build/dev.md',
50
50
  'qa-implementation': 'chati.dev/agents/quality/qa-implementation.md',
@@ -158,7 +158,7 @@ function buildPrismSection(config) {
158
158
  const result = runPrism({
159
159
  domainsDir,
160
160
  remainingPercent: 100, // Spawned terminals are always FRESH
161
- mode: state.project?.state || 'planning',
161
+ mode: state.project?.state || 'discover',
162
162
  agent: config.agent,
163
163
  workflow: config.workflow || null,
164
164
  pipelinePosition: config.agent,
@@ -261,7 +261,7 @@ function buildSessionSection(config) {
261
261
  '',
262
262
  `- **Project**: ${project.name || '(unnamed)'}`,
263
263
  `- **Type**: ${project.type || 'greenfield'}`,
264
- `- **Mode**: ${project.state || 'planning'}`,
264
+ `- **Mode**: ${project.state || 'discover'}`,
265
265
  `- **Language**: ${state.language || 'en'} (interaction) / English (artifacts)`,
266
266
  `- **User Level**: ${state.user_level || 'auto'}`,
267
267
  `- **Execution Mode**: ${state.execution_mode || 'autonomous'}`,
@@ -21,9 +21,9 @@ const FALLBACK_EN = {
21
21
  ides_label: 'IDEs',
22
22
  mcps_label: 'MCPs',
23
23
  will_install: 'Will install:',
24
- agents_count: '13 agent definitions (PLANNING, BUILD, DEPLOY phases)',
25
- workflows_count: '5 workflow blueprints',
26
- templates_count: '5 templates (PRD, Brownfield PRD, Architecture, Task, QA Gate)',
24
+ agents_count: '13 agent definitions (DISCOVER, PLAN, BUILD, DEPLOY phases)',
25
+ workflows_count: '6 workflow blueprints',
26
+ templates_count: '6 templates (PRD, Brownfield PRD, Architecture, Task, QA Gate, Quick Brief)',
27
27
  constitution: 'Constitution (17 Articles + Preamble)',
28
28
  session_mgmt: 'Session management system',
29
29
  quality_gates: 'Quality gates (4-tier validation)',