chati-dev 1.0.0

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 (63) hide show
  1. package/assets/logo.txt +6 -0
  2. package/bin/chati.js +175 -0
  3. package/framework/agents/build/dev.md +342 -0
  4. package/framework/agents/clarity/architect.md +263 -0
  5. package/framework/agents/clarity/brief.md +277 -0
  6. package/framework/agents/clarity/brownfield-wu.md +288 -0
  7. package/framework/agents/clarity/detail.md +274 -0
  8. package/framework/agents/clarity/greenfield-wu.md +231 -0
  9. package/framework/agents/clarity/phases.md +272 -0
  10. package/framework/agents/clarity/tasks.md +279 -0
  11. package/framework/agents/clarity/ux.md +293 -0
  12. package/framework/agents/deploy/devops.md +321 -0
  13. package/framework/agents/quality/qa-implementation.md +310 -0
  14. package/framework/agents/quality/qa-planning.md +289 -0
  15. package/framework/config.yaml +8 -0
  16. package/framework/constitution.md +238 -0
  17. package/framework/frameworks/decision-heuristics.yaml +64 -0
  18. package/framework/frameworks/quality-dimensions.yaml +59 -0
  19. package/framework/i18n/en.yaml +78 -0
  20. package/framework/i18n/es.yaml +78 -0
  21. package/framework/i18n/fr.yaml +78 -0
  22. package/framework/i18n/pt.yaml +78 -0
  23. package/framework/intelligence/confidence.yaml +42 -0
  24. package/framework/intelligence/gotchas.yaml +51 -0
  25. package/framework/intelligence/patterns.yaml +32 -0
  26. package/framework/migrations/v1.0-to-v1.1.yaml +48 -0
  27. package/framework/orchestrator/chati.md +333 -0
  28. package/framework/patterns/elicitation.md +137 -0
  29. package/framework/quality-gates/implementation-gate.md +64 -0
  30. package/framework/quality-gates/planning-gate.md +52 -0
  31. package/framework/schemas/config.schema.json +42 -0
  32. package/framework/schemas/session.schema.json +103 -0
  33. package/framework/schemas/task.schema.json +71 -0
  34. package/framework/templates/brownfield-prd-tmpl.yaml +103 -0
  35. package/framework/templates/fullstack-architecture-tmpl.yaml +101 -0
  36. package/framework/templates/prd-tmpl.yaml +94 -0
  37. package/framework/templates/qa-gate-tmpl.yaml +96 -0
  38. package/framework/templates/task-tmpl.yaml +85 -0
  39. package/framework/workflows/brownfield-discovery.yaml +75 -0
  40. package/framework/workflows/brownfield-fullstack.yaml +104 -0
  41. package/framework/workflows/brownfield-service.yaml +81 -0
  42. package/framework/workflows/brownfield-ui.yaml +87 -0
  43. package/framework/workflows/greenfield-fullstack.yaml +108 -0
  44. package/package.json +60 -0
  45. package/scripts/bundle-framework.js +58 -0
  46. package/src/config/ide-configs.js +80 -0
  47. package/src/config/mcp-configs.js +136 -0
  48. package/src/dashboard/data-reader.js +99 -0
  49. package/src/dashboard/layout.js +161 -0
  50. package/src/dashboard/renderer.js +104 -0
  51. package/src/installer/core.js +221 -0
  52. package/src/installer/templates.js +97 -0
  53. package/src/installer/validator.js +114 -0
  54. package/src/upgrade/backup.js +107 -0
  55. package/src/upgrade/checker.js +105 -0
  56. package/src/upgrade/migrator.js +171 -0
  57. package/src/utils/colors.js +18 -0
  58. package/src/utils/detector.js +51 -0
  59. package/src/utils/logger.js +41 -0
  60. package/src/wizard/feedback.js +76 -0
  61. package/src/wizard/i18n.js +168 -0
  62. package/src/wizard/index.js +107 -0
  63. package/src/wizard/questions.js +169 -0
@@ -0,0 +1,136 @@
1
+ /**
2
+ * MCP Registry and Configuration
3
+ * Defines available MCPs and their agent dependencies
4
+ */
5
+ export const MCP_CONFIGS = {
6
+ browser: {
7
+ name: 'Browser (Playwright)',
8
+ description: 'Web automation and testing',
9
+ defaultSelected: true,
10
+ requiresEnv: [],
11
+ claudeConfig: {
12
+ command: 'npx',
13
+ args: ['-y', '@playwright/mcp'],
14
+ },
15
+ },
16
+ context7: {
17
+ name: 'Context7',
18
+ description: 'Library documentation search',
19
+ defaultSelected: true,
20
+ requiresEnv: [],
21
+ claudeConfig: {
22
+ command: 'npx',
23
+ args: ['-y', '@context7/mcp'],
24
+ },
25
+ },
26
+ exa: {
27
+ name: 'Exa',
28
+ description: 'Advanced web search',
29
+ defaultSelected: true,
30
+ requiresEnv: ['EXA_API_KEY'],
31
+ claudeConfig: {
32
+ command: 'npx',
33
+ args: ['-y', '@exa/mcp'],
34
+ env: { EXA_API_KEY: '${EXA_API_KEY}' },
35
+ },
36
+ },
37
+ 'desktop-commander': {
38
+ name: 'Desktop Commander',
39
+ description: 'File system access',
40
+ defaultSelected: false,
41
+ requiresEnv: [],
42
+ claudeConfig: {
43
+ command: 'npx',
44
+ args: ['-y', '@anthropic/desktop-commander-mcp'],
45
+ },
46
+ },
47
+ 'sequential-thinking': {
48
+ name: 'Sequential Thinking',
49
+ description: 'Step-by-step reasoning',
50
+ defaultSelected: false,
51
+ requiresEnv: [],
52
+ claudeConfig: {
53
+ command: 'npx',
54
+ args: ['-y', '@anthropic/sequential-thinking-mcp'],
55
+ },
56
+ },
57
+ github: {
58
+ name: 'GitHub',
59
+ description: 'GitHub API integration',
60
+ defaultSelected: false,
61
+ requiresEnv: ['GITHUB_TOKEN'],
62
+ claudeConfig: {
63
+ command: 'npx',
64
+ args: ['-y', '@anthropic/github-mcp'],
65
+ env: { GITHUB_TOKEN: '${GITHUB_TOKEN}' },
66
+ },
67
+ },
68
+ };
69
+
70
+ /**
71
+ * Agent-MCP dependency matrix
72
+ */
73
+ export const AGENT_MCP_DEPS = {
74
+ orchestrator: { required: [], optional: ['git'] },
75
+ 'greenfield-wu': { required: [], optional: [] },
76
+ 'brownfield-wu': { required: ['git'], optional: ['browser'] },
77
+ brief: { required: [], optional: [] },
78
+ detail: { required: [], optional: ['exa'] },
79
+ architect: { required: ['context7'], optional: ['exa', 'git'] },
80
+ ux: { required: [], optional: ['browser'] },
81
+ phases: { required: [], optional: [] },
82
+ tasks: { required: [], optional: [] },
83
+ 'qa-planning': { required: [], optional: [] },
84
+ dev: { required: ['context7', 'git'], optional: ['browser', 'coderabbit'] },
85
+ 'qa-implementation':{ required: ['git'], optional: ['browser', 'coderabbit'] },
86
+ devops: { required: ['git', 'github'], optional: [] },
87
+ };
88
+
89
+ /**
90
+ * Get list of MCPs for selection prompt
91
+ */
92
+ export function getMCPChoices() {
93
+ return Object.entries(MCP_CONFIGS).map(([key, config]) => ({
94
+ value: key,
95
+ label: config.name,
96
+ hint: config.description,
97
+ initialValue: config.defaultSelected,
98
+ }));
99
+ }
100
+
101
+ /**
102
+ * Check MCP warnings for selected project type and MCPs
103
+ */
104
+ export function getMCPWarnings(projectType, selectedMCPs) {
105
+ const warnings = [];
106
+
107
+ if (projectType === 'brownfield' && !selectedMCPs.includes('git')) {
108
+ warnings.push({
109
+ level: 'critical',
110
+ message: "You selected Brownfield but did not enable 'git' MCP. The brownfield-wu agent requires git for codebase analysis.",
111
+ });
112
+ }
113
+
114
+ return warnings;
115
+ }
116
+
117
+ /**
118
+ * Generate MCP config for Claude Code (.claude/mcp.json)
119
+ */
120
+ export function generateClaudeMCPConfig(selectedMCPs) {
121
+ const mcpServers = {};
122
+ for (const mcpKey of selectedMCPs) {
123
+ const config = MCP_CONFIGS[mcpKey];
124
+ if (config?.claudeConfig) {
125
+ const entry = {
126
+ command: config.claudeConfig.command,
127
+ args: config.claudeConfig.args,
128
+ };
129
+ if (config.claudeConfig.env) {
130
+ entry.env = config.claudeConfig.env;
131
+ }
132
+ mcpServers[mcpKey] = entry;
133
+ }
134
+ }
135
+ return { mcpServers };
136
+ }
@@ -0,0 +1,99 @@
1
+ import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
2
+ import { join } from 'path';
3
+ import yaml from 'js-yaml';
4
+
5
+ /**
6
+ * Read all dashboard data from project directory
7
+ */
8
+ export function readDashboardData(targetDir) {
9
+ const data = {
10
+ session: null,
11
+ config: null,
12
+ agentScores: {},
13
+ taskProgress: null,
14
+ validationResults: null,
15
+ recentActivity: [],
16
+ blockers: [],
17
+ gotchas: [],
18
+ };
19
+
20
+ // Read session.yaml
21
+ const sessionPath = join(targetDir, '.chati', 'session.yaml');
22
+ if (existsSync(sessionPath)) {
23
+ try {
24
+ data.session = yaml.load(readFileSync(sessionPath, 'utf-8'));
25
+ } catch {
26
+ data.session = null;
27
+ }
28
+ }
29
+
30
+ // Read config.yaml
31
+ const configPath = join(targetDir, 'chati.dev', 'config.yaml');
32
+ if (existsSync(configPath)) {
33
+ try {
34
+ data.config = yaml.load(readFileSync(configPath, 'utf-8'));
35
+ } catch {
36
+ data.config = null;
37
+ }
38
+ }
39
+
40
+ // Extract agent scores
41
+ if (data.session?.agents) {
42
+ for (const [name, info] of Object.entries(data.session.agents)) {
43
+ data.agentScores[name] = {
44
+ status: info.status || 'pending',
45
+ score: info.score || 0,
46
+ completedAt: info.completed_at || null,
47
+ };
48
+ }
49
+ }
50
+
51
+ // Extract blockers from backlog (high priority items)
52
+ if (data.session?.backlog) {
53
+ data.blockers = data.session.backlog.filter(
54
+ item => item.priority === 'high' && item.status !== 'done'
55
+ );
56
+ }
57
+
58
+ // Read task progress from artifacts
59
+ const tasksDir = join(targetDir, 'chati.dev', 'artifacts', '6-Tasks');
60
+ if (existsSync(tasksDir)) {
61
+ try {
62
+ const files = readdirSync(tasksDir).filter(f => f.endsWith('.yaml') || f.endsWith('.md'));
63
+ data.taskProgress = { totalFiles: files.length };
64
+ } catch {
65
+ // Ignore
66
+ }
67
+ }
68
+
69
+ // Read gotchas
70
+ const gotchasPath = join(targetDir, 'chati.dev', 'intelligence', 'gotchas.yaml');
71
+ if (existsSync(gotchasPath)) {
72
+ try {
73
+ const gotchasData = yaml.load(readFileSync(gotchasPath, 'utf-8'));
74
+ data.gotchas = gotchasData?.gotchas || [];
75
+ } catch {
76
+ // Ignore
77
+ }
78
+ }
79
+
80
+ // Build recent activity from agent completion timestamps
81
+ if (data.session?.agents) {
82
+ for (const [name, info] of Object.entries(data.session.agents)) {
83
+ if (info.completed_at) {
84
+ data.recentActivity.push({
85
+ agent: name,
86
+ score: info.score,
87
+ completedAt: info.completed_at,
88
+ });
89
+ }
90
+ }
91
+ data.recentActivity.sort((a, b) => {
92
+ if (!a.completedAt) return 1;
93
+ if (!b.completedAt) return -1;
94
+ return new Date(b.completedAt) - new Date(a.completedAt);
95
+ });
96
+ }
97
+
98
+ return data;
99
+ }
@@ -0,0 +1,161 @@
1
+ import { brand, dim, success, yellow, green, gray, bold, red } from '../utils/colors.js';
2
+
3
+ /**
4
+ * Dashboard layout definition
5
+ * Formats data into the TUI display
6
+ */
7
+
8
+ /**
9
+ * Format agent score with color
10
+ */
11
+ function formatScore(score, status) {
12
+ if (status === 'pending') return gray('--');
13
+ if (status === 'in_progress') return yellow('...');
14
+ if (score >= 95) return green(String(score));
15
+ if (score >= 80) return yellow(String(score));
16
+ return red(String(score));
17
+ }
18
+
19
+ /**
20
+ * Format project state with label
21
+ */
22
+ function formatState(state) {
23
+ const stateMap = {
24
+ clarity: brand('CLARITY'),
25
+ build: yellow('BUILD'),
26
+ validate: bold('VALIDATE'),
27
+ deploy: green('DEPLOY'),
28
+ completed: success('COMPLETED'),
29
+ };
30
+ return stateMap[state] || gray(state || 'unknown');
31
+ }
32
+
33
+ /**
34
+ * Format execution mode
35
+ */
36
+ function formatMode(mode) {
37
+ if (mode === 'autonomous') return yellow('Ralph Wiggum');
38
+ return dim('Interactive');
39
+ }
40
+
41
+ /**
42
+ * Build the header section
43
+ */
44
+ export function buildHeader(data) {
45
+ const version = data.config?.version || '?.?.?';
46
+ const line = '─'.repeat(59);
47
+ return [
48
+ brand(`┌${line}┐`),
49
+ brand(`│ chati.dev Dashboard`) + ' '.repeat(59 - 21 - version.length - 2) + dim(`v${version}`) + brand(' │'),
50
+ brand(`├${line}┤`),
51
+ ];
52
+ }
53
+
54
+ /**
55
+ * Build project info section
56
+ */
57
+ export function buildProjectInfo(data) {
58
+ const session = data.session || {};
59
+ const project = session.project || {};
60
+
61
+ const name = project.name || 'unknown';
62
+ const type = project.type === 'brownfield' ? 'Brownfield' : 'Greenfield';
63
+ const state = formatState(project.state);
64
+ const mode = formatMode(session.execution_mode);
65
+ const lang = session.language || 'en';
66
+ const ide = (session.ides || [])[0] || 'unknown';
67
+
68
+ return [
69
+ brand('│') + ` ${dim('Project:')} ${name}` + ' '.repeat(Math.max(1, 30 - name.length)) + `${dim('Type:')} ${type}` + ' '.repeat(Math.max(1, 18 - type.length)) + brand('│'),
70
+ brand('│') + ` ${dim('Phase:')} ${state}` + ' '.repeat(Math.max(1, 28)) + `${dim('Mode:')} ${mode}` + ' '.repeat(Math.max(1, 10)) + brand('│'),
71
+ brand('│') + ` ${dim('Language:')} ${lang}` + ' '.repeat(Math.max(1, 30 - lang.length)) + `${dim('IDE:')} ${ide}` + ' '.repeat(Math.max(1, 18 - ide.length)) + brand('│'),
72
+ ];
73
+ }
74
+
75
+ /**
76
+ * Build CLARITY section with agent scores
77
+ */
78
+ export function buildClaritySection(data) {
79
+ const scores = data.agentScores || {};
80
+
81
+ const clarityAgents = [
82
+ ['WU', scores['greenfield-wu'] || scores['brownfield-wu'] || { status: 'pending', score: 0 }],
83
+ ['Brief', scores['brief'] || { status: 'pending', score: 0 }],
84
+ ['Detail', scores['detail'] || { status: 'pending', score: 0 }],
85
+ ['Arch', scores['architect'] || { status: 'pending', score: 0 }],
86
+ ['UX', scores['ux'] || { status: 'pending', score: 0 }],
87
+ ['Phases', scores['phases'] || { status: 'pending', score: 0 }],
88
+ ['Tasks', scores['tasks'] || { status: 'pending', score: 0 }],
89
+ ['QA-P', scores['qa-planning'] || { status: 'pending', score: 0 }],
90
+ ];
91
+
92
+ const row1 = clarityAgents.slice(0, 4)
93
+ .map(([name, info]) => `${dim(name.padEnd(8))} ${formatScore(info.score, info.status)}`)
94
+ .join(' ');
95
+
96
+ const row2 = clarityAgents.slice(4)
97
+ .map(([name, info]) => `${dim(name.padEnd(8))} ${formatScore(info.score, info.status)}`)
98
+ .join(' ');
99
+
100
+ return [
101
+ brand('│') + ` ${brand('── CLARITY')} ${'─'.repeat(46)}` + brand('│'),
102
+ brand('│') + ` │ ${row1}` + ' '.repeat(5) + brand('│'),
103
+ brand('│') + ` │ ${row2}` + ' '.repeat(5) + brand('│'),
104
+ brand('│') + ` ${'─'.repeat(57)}` + brand('│'),
105
+ ];
106
+ }
107
+
108
+ /**
109
+ * Build BUILD section
110
+ */
111
+ export function buildBuildSection(data) {
112
+ const devInfo = data.agentScores?.dev || { status: 'pending', score: 0 };
113
+ const status = devInfo.status === 'pending' ? gray('Waiting') :
114
+ devInfo.status === 'in_progress' ? yellow('In Progress') :
115
+ green('Complete');
116
+
117
+ return [
118
+ brand('│') + ` ${brand('── BUILD')} ${'─'.repeat(48)}` + brand('│'),
119
+ brand('│') + ` │ ${dim('Dev Agent:')} ${status}` + ' '.repeat(38) + brand('│'),
120
+ brand('│') + ` ${'─'.repeat(57)}` + brand('│'),
121
+ ];
122
+ }
123
+
124
+ /**
125
+ * Build VALIDATE section
126
+ */
127
+ export function buildValidateSection(data) {
128
+ const qaInfo = data.agentScores?.['qa-implementation'] || { status: 'pending', score: 0 };
129
+ const tests = qaInfo.status === 'completed' ? green('Pass') : gray('--');
130
+
131
+ return [
132
+ brand('│') + ` ${brand('── VALIDATE')} ${'─'.repeat(45)}` + brand('│'),
133
+ brand('│') + ` │ ${dim('Tests:')} ${tests} ${dim('SAST:')} ${gray('--')} ${dim('DAST:')} ${gray('--')} ${dim('DS Audit:')} ${gray('--')}` + ' '.repeat(5) + brand('│'),
134
+ brand('│') + ` ${'─'.repeat(57)}` + brand('│'),
135
+ ];
136
+ }
137
+
138
+ /**
139
+ * Build footer with recent activity, blockers, gotchas
140
+ */
141
+ export function buildFooter(data) {
142
+ const recent = data.recentActivity?.[0];
143
+ const recentText = recent
144
+ ? `${recent.agent} completed (score: ${recent.score})`
145
+ : 'No recent activity';
146
+
147
+ const blockerCount = data.blockers?.length || 0;
148
+ const blockerText = blockerCount > 0 ? red(`${blockerCount} active`) : green('None');
149
+
150
+ const gotchaCount = data.gotchas?.length || 0;
151
+ const gotchaText = gotchaCount > 0 ? `${gotchaCount} patterns learned` : 'None yet';
152
+
153
+ const line = '─'.repeat(59);
154
+
155
+ return [
156
+ brand('│') + ` ${dim('Recent:')} ${recentText}` + ' '.repeat(Math.max(1, 40 - recentText.length)) + brand('│'),
157
+ brand('│') + ` ${dim('Blockers:')} ${blockerText}` + ' '.repeat(42) + brand('│'),
158
+ brand('│') + ` ${dim('Gotchas:')} ${gotchaText}` + ' '.repeat(Math.max(1, 40 - gotchaText.length)) + brand('│'),
159
+ brand(`└${line}┘`),
160
+ ];
161
+ }
@@ -0,0 +1,104 @@
1
+ import { readDashboardData } from './data-reader.js';
2
+ import {
3
+ buildHeader,
4
+ buildProjectInfo,
5
+ buildClaritySection,
6
+ buildBuildSection,
7
+ buildValidateSection,
8
+ buildFooter,
9
+ } from './layout.js';
10
+ import { dim } from '../utils/colors.js';
11
+
12
+ /**
13
+ * Render dashboard once (static mode)
14
+ */
15
+ export function renderDashboard(targetDir) {
16
+ const data = readDashboardData(targetDir);
17
+
18
+ if (!data.session) {
19
+ console.log('No chati.dev session found. Run `npx chati-dev init` first.');
20
+ return;
21
+ }
22
+
23
+ const lines = [
24
+ ...buildHeader(data),
25
+ ...buildProjectInfo(data),
26
+ '',
27
+ ...buildClaritySection(data),
28
+ '',
29
+ ...buildBuildSection(data),
30
+ '',
31
+ ...buildValidateSection(data),
32
+ '',
33
+ ...buildFooter(data),
34
+ ];
35
+
36
+ console.log();
37
+ for (const line of lines) {
38
+ console.log(line);
39
+ }
40
+ console.log();
41
+ }
42
+
43
+ /**
44
+ * Render dashboard in watch mode (auto-refresh)
45
+ */
46
+ export function renderDashboardWatch(targetDir, intervalMs = 5000) {
47
+ // Initial render
48
+ console.clear();
49
+ renderDashboard(targetDir);
50
+ console.log(dim(` Auto-refreshing every ${intervalMs / 1000}s. Press Ctrl+C to exit.`));
51
+
52
+ // Set up interval
53
+ const timer = setInterval(() => {
54
+ console.clear();
55
+ renderDashboard(targetDir);
56
+ console.log(dim(` Auto-refreshing every ${intervalMs / 1000}s. Press Ctrl+C to exit.`));
57
+ console.log(dim(` Last update: ${new Date().toLocaleTimeString()}`));
58
+ }, intervalMs);
59
+
60
+ // Handle exit
61
+ process.on('SIGINT', () => {
62
+ clearInterval(timer);
63
+ console.log();
64
+ process.exit(0);
65
+ });
66
+
67
+ return timer;
68
+ }
69
+
70
+ /**
71
+ * Render plain text fallback (for terminals without TUI support)
72
+ */
73
+ export function renderPlainDashboard(targetDir) {
74
+ const data = readDashboardData(targetDir);
75
+
76
+ if (!data.session) {
77
+ console.log('No chati.dev session found. Run `npx chati-dev init` first.');
78
+ return;
79
+ }
80
+
81
+ const session = data.session;
82
+ const project = session.project || {};
83
+
84
+ console.log();
85
+ console.log('=== chati.dev Dashboard ===');
86
+ console.log();
87
+ console.log(`Project: ${project.name || 'unknown'}`);
88
+ console.log(`Type: ${project.type || 'unknown'}`);
89
+ console.log(`Phase: ${project.state || 'unknown'}`);
90
+ console.log(`Mode: ${session.execution_mode || 'interactive'}`);
91
+ console.log(`Language: ${session.language || 'en'}`);
92
+ console.log();
93
+
94
+ console.log('--- Agent Scores ---');
95
+ for (const [name, info] of Object.entries(data.agentScores)) {
96
+ const status = info.status === 'pending' ? '--' : info.status === 'in_progress' ? '...' : String(info.score);
97
+ console.log(` ${name.padEnd(20)} ${status}`);
98
+ }
99
+
100
+ console.log();
101
+ console.log(`Blockers: ${data.blockers.length || 'None'}`);
102
+ console.log(`Gotchas: ${data.gotchas.length} patterns`);
103
+ console.log();
104
+ }