sdd-toolkit 1.5.0 → 1.6.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.
- package/README.md +139 -155
- package/README.pt.md +135 -0
- package/definitions/{dev.coder.yaml → coder.yaml} +15 -22
- package/definitions/feature.yaml +74 -0
- package/definitions/log.yaml +61 -0
- package/definitions/project.yaml +108 -0
- package/definitions/requirements.yaml +71 -0
- package/definitions/review.yaml +88 -0
- package/definitions/sdd.yaml +30 -0
- package/package.json +1 -1
- package/src/commands/view.js +18 -0
- package/src/index.js +335 -284
- package/src/lib/agents.js +1 -1
- package/src/lib/dashboard.js +188 -0
- package/src/lib/docs.js +2 -1
- package/src/lib/messages.js +57 -9
- package/src/lib/transformers.js +332 -246
- package/src/scripts/archive.js +5 -5
- package/src/scripts/reset.js +4 -4
- package/src/scripts/status.js +7 -7
- package/templates/milestones.md +1 -1
- package/definitions/dev.auditor.yaml +0 -66
- package/definitions/dev.feature.yaml +0 -105
- package/definitions/dev.log.yaml +0 -90
- package/definitions/dev.milestone.yaml +0 -75
- package/definitions/dev.ops.yaml +0 -51
- package/definitions/dev.project.yaml +0 -106
- package/definitions/dev.requirements.yaml +0 -91
- package/definitions/dev.review.yaml +0 -61
- package/definitions/dev.tasks.yaml +0 -84
package/src/lib/agents.js
CHANGED
|
@@ -16,7 +16,7 @@ const pc = require('picocolors');
|
|
|
16
16
|
*/
|
|
17
17
|
async function loadAgents(options = {}) {
|
|
18
18
|
const definitionsDir = path.join(__dirname, '..', '..', 'definitions');
|
|
19
|
-
const localDefinitionsDir = path.join(process.cwd(), 'sdd-toolkit', 'agents');
|
|
19
|
+
const localDefinitionsDir = path.join(process.cwd(), '.sdd-toolkit', 'agents');
|
|
20
20
|
|
|
21
21
|
// Check default directory
|
|
22
22
|
try {
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
const fs = require('fs/promises');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const pc = require('picocolors');
|
|
4
|
+
const { t } = require('./i18n');
|
|
5
|
+
|
|
6
|
+
const DOCS_DIR = '.sdd-toolkit';
|
|
7
|
+
const PROJECT_FILE = path.join(DOCS_DIR, 'project.md');
|
|
8
|
+
const TASKS_FILE = path.join(DOCS_DIR, 'task.md');
|
|
9
|
+
const LOGS_DIR = path.join(DOCS_DIR, 'logs', 'executions');
|
|
10
|
+
const PACKAGE_FILE = 'package.json';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Extracts metadata from package.json (priority) or project.md
|
|
14
|
+
*/
|
|
15
|
+
async function getProjectMetadata() {
|
|
16
|
+
let meta = { title: 'Project', version: '0.0.0', status: 'Active' };
|
|
17
|
+
|
|
18
|
+
// Try reading package.json first (Single Source of Truth for Version)
|
|
19
|
+
try {
|
|
20
|
+
const pkgContent = await fs.readFile(PACKAGE_FILE, 'utf8');
|
|
21
|
+
const pkg = JSON.parse(pkgContent);
|
|
22
|
+
if (pkg.name) meta.title = pkg.name;
|
|
23
|
+
if (pkg.version) meta.version = pkg.version;
|
|
24
|
+
} catch (e) {
|
|
25
|
+
// Ignore if no package.json
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Try reading project.md for status or title fallback
|
|
29
|
+
try {
|
|
30
|
+
const content = await fs.readFile(PROJECT_FILE, 'utf8');
|
|
31
|
+
|
|
32
|
+
// Only override title if package.json didn't provide one
|
|
33
|
+
if (meta.title === 'Project') {
|
|
34
|
+
const titleMatch = content.match(/title:\s*(.+)/);
|
|
35
|
+
if (titleMatch) meta.title = titleMatch[1].trim();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const statusMatch = content.match(/status:\s*(.+)/);
|
|
39
|
+
if (statusMatch) meta.status = statusMatch[1].trim();
|
|
40
|
+
|
|
41
|
+
} catch (e) {
|
|
42
|
+
// Ignore errors
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return meta;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Parses task.md to calculate progress metrics
|
|
50
|
+
*/
|
|
51
|
+
async function getTaskProgress() {
|
|
52
|
+
try {
|
|
53
|
+
const content = await fs.readFile(TASKS_FILE, 'utf8');
|
|
54
|
+
|
|
55
|
+
// Extract Milestone Name
|
|
56
|
+
const milestoneMatch = content.match(/# Execution Backlog:\s*(.+)/);
|
|
57
|
+
const milestone = milestoneMatch ? milestoneMatch[1].trim() : 'General Tasks';
|
|
58
|
+
|
|
59
|
+
// Count Checkboxes
|
|
60
|
+
const total = (content.match(/- \[ \]/g) || []).length + (content.match(/- \[x\]/g) || []).length;
|
|
61
|
+
const done = (content.match(/- \[x\]/g) || []).length;
|
|
62
|
+
const pending = total - done;
|
|
63
|
+
const percent = total > 0 ? Math.round((done / total) * 100) : 0;
|
|
64
|
+
|
|
65
|
+
return { milestone, total, done, pending, percent };
|
|
66
|
+
} catch (e) {
|
|
67
|
+
return { milestone: 'No active plan', total: 0, done: 0, pending: 0, percent: 0 };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Retrieves the N most recent execution logs
|
|
73
|
+
*/
|
|
74
|
+
async function getRecentLogs(limit = 5) {
|
|
75
|
+
try {
|
|
76
|
+
// Ensure dir exists to avoid crash
|
|
77
|
+
await fs.access(LOGS_DIR);
|
|
78
|
+
|
|
79
|
+
const files = await fs.readdir(LOGS_DIR);
|
|
80
|
+
|
|
81
|
+
// Read stats for sorting
|
|
82
|
+
const fileStats = await Promise.all(
|
|
83
|
+
files
|
|
84
|
+
.filter(f => f.endsWith('.md'))
|
|
85
|
+
.map(async (file) => {
|
|
86
|
+
const filePath = path.join(LOGS_DIR, file);
|
|
87
|
+
const stats = await fs.stat(filePath);
|
|
88
|
+
return { file, mtime: stats.mtime, filePath };
|
|
89
|
+
})
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
// Sort by time desc
|
|
93
|
+
const sorted = fileStats.sort((a, b) => b.mtime - a.mtime).slice(0, limit);
|
|
94
|
+
|
|
95
|
+
// Read content to get status/task ID if possible (simplified for now)
|
|
96
|
+
const logs = sorted.map(item => {
|
|
97
|
+
const name = item.file.replace('.md', '');
|
|
98
|
+
return {
|
|
99
|
+
name,
|
|
100
|
+
date: item.mtime.toLocaleDateString(),
|
|
101
|
+
status: 'Completed'
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
return logs;
|
|
106
|
+
} catch (e) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* UI Component: Progress Bar
|
|
113
|
+
* [██████░░░░] 60%
|
|
114
|
+
*/
|
|
115
|
+
function drawProgressBar(percent, width = 20) {
|
|
116
|
+
const filled = Math.round((percent / 100) * width);
|
|
117
|
+
const empty = width - filled;
|
|
118
|
+
|
|
119
|
+
const bar = pc.green('█'.repeat(filled)) + pc.gray('░'.repeat(empty));
|
|
120
|
+
return `[${bar}] ${pc.bold(percent + '%')}`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* UI Component: Box
|
|
125
|
+
*/
|
|
126
|
+
function drawBox(lines) {
|
|
127
|
+
const maxWidth = Math.max(60, ...lines.map(l => l.replace(/\x1b\[[0-9;]*m/g, '').length));
|
|
128
|
+
const borderTop = '╭' + '─'.repeat(maxWidth + 2) + '╮';
|
|
129
|
+
const borderBottom = '╰' + '─'.repeat(maxWidth + 2) + '╯';
|
|
130
|
+
|
|
131
|
+
console.log(pc.gray(borderTop));
|
|
132
|
+
lines.forEach(line => {
|
|
133
|
+
const len = line.replace(/\x1b\[[0-9;]*m/g, '').length;
|
|
134
|
+
const padding = ' '.repeat(maxWidth - len);
|
|
135
|
+
console.log(pc.gray('│ ') + line + padding + pc.gray(' │'));
|
|
136
|
+
});
|
|
137
|
+
console.log(pc.gray(borderBottom));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Main Render Function
|
|
142
|
+
*/
|
|
143
|
+
async function renderDashboard() {
|
|
144
|
+
console.clear();
|
|
145
|
+
|
|
146
|
+
// Fetch Data Parallel
|
|
147
|
+
const [meta, progress, logs] = await Promise.all([
|
|
148
|
+
getProjectMetadata(),
|
|
149
|
+
getTaskProgress(),
|
|
150
|
+
getRecentLogs(5)
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
// Header (using i18n keys)
|
|
154
|
+
drawBox([
|
|
155
|
+
`${pc.magenta(t('DASHBOARD.TITLE'))} ${pc.bold(meta.title)} (v${meta.version})`,
|
|
156
|
+
`${pc.cyan(t('DASHBOARD.PHASE'))} ${progress.milestone}`,
|
|
157
|
+
`${pc.yellow(t('DASHBOARD.STATUS'))} ${meta.status}`
|
|
158
|
+
]);
|
|
159
|
+
|
|
160
|
+
console.log(''); // Spacing
|
|
161
|
+
|
|
162
|
+
// Progress Section
|
|
163
|
+
console.log(pc.bold(t('DASHBOARD.OVERALL')));
|
|
164
|
+
console.log(drawProgressBar(progress.percent, 40));
|
|
165
|
+
console.log(`${pc.green('✅ ' + progress.done + ' ' + t('DASHBOARD.COMPLETED'))} | ${pc.red('⭕ ' + progress.pending + ' ' + t('DASHBOARD.PENDING'))}`);
|
|
166
|
+
|
|
167
|
+
console.log(''); // Spacing
|
|
168
|
+
|
|
169
|
+
// Recent Logs Section
|
|
170
|
+
if (logs.length > 0) {
|
|
171
|
+
console.log(pc.bold(t('DASHBOARD.RECENT')));
|
|
172
|
+
logs.forEach(log => {
|
|
173
|
+
console.log(`${pc.gray('•')} ${pc.dim(`[${log.date}]`)} ${log.name}`);
|
|
174
|
+
});
|
|
175
|
+
} else {
|
|
176
|
+
console.log(pc.gray(t('DASHBOARD.NO_ACTIVITY')));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.log(''); // Spacing
|
|
180
|
+
console.log(pc.bgMagenta(pc.black(t('DASHBOARD.ACTION'))) + ' ' + t('DASHBOARD.HINT') + ' ' + pc.cyan('/dev:coder <Task_ID>') + ' ' + t('DASHBOARD.HINT_SUFFIX'));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = {
|
|
184
|
+
getProjectMetadata,
|
|
185
|
+
getTaskProgress,
|
|
186
|
+
getRecentLogs,
|
|
187
|
+
renderDashboard
|
|
188
|
+
};
|
package/src/lib/docs.js
CHANGED
|
@@ -6,10 +6,11 @@ const path = require('path');
|
|
|
6
6
|
* Implements Smart Scaffolding: Only creates missing files/folders.
|
|
7
7
|
*/
|
|
8
8
|
function generateWorkflowGuide(baseDir) {
|
|
9
|
-
const docsDir = path.join(baseDir, '
|
|
9
|
+
const docsDir = path.join(baseDir, '.sdd-toolkit');
|
|
10
10
|
|
|
11
11
|
// 1. Define folder structure based on new logging architecture
|
|
12
12
|
const folders = [
|
|
13
|
+
path.join(docsDir, 'features'),
|
|
13
14
|
path.join(docsDir, 'logs'),
|
|
14
15
|
path.join(docsDir, 'logs', 'executions'),
|
|
15
16
|
path.join(docsDir, 'logs', 'reviews'),
|
package/src/lib/messages.js
CHANGED
|
@@ -20,8 +20,8 @@ const EN = {
|
|
|
20
20
|
},
|
|
21
21
|
SCAFFOLD: {
|
|
22
22
|
LOADING: 'Checking workspace structure...',
|
|
23
|
-
SUCCESS: '✔ Folder structure (
|
|
24
|
-
ALREADY_EXISTS: '✔ Folder structure (
|
|
23
|
+
SUCCESS: '✔ Folder structure (.sdd-toolkit/) created.',
|
|
24
|
+
ALREADY_EXISTS: '✔ Folder structure (.sdd-toolkit/) verified.',
|
|
25
25
|
ERROR: 'Failed to verify workspace structure.',
|
|
26
26
|
},
|
|
27
27
|
SETUP: {
|
|
@@ -58,6 +58,22 @@ const EN = {
|
|
|
58
58
|
EN: 'Always reply in English unless told otherwise.',
|
|
59
59
|
PT_BR: 'Responda sempre em Português (Brasil), a menos que solicitado o contrário.',
|
|
60
60
|
ES: 'Responda siempre en Español, a menos que se solicite lo contrario.',
|
|
61
|
+
},
|
|
62
|
+
DASHBOARD: {
|
|
63
|
+
TITLE: '🚀 PROJECT:',
|
|
64
|
+
PHASE: '📅 PHASE:',
|
|
65
|
+
STATUS: '📡 STATUS:',
|
|
66
|
+
OVERALL: 'OVERALL STATUS',
|
|
67
|
+
COMPLETED: 'Completed',
|
|
68
|
+
PENDING: 'Pending',
|
|
69
|
+
RECENT: 'RECENT ACTIVITY',
|
|
70
|
+
NO_ACTIVITY: 'No recent activity recorded.',
|
|
71
|
+
ACTION: ' 👉 ACTION: ',
|
|
72
|
+
HINT: 'Use',
|
|
73
|
+
HINT_SUFFIX: 'to work.',
|
|
74
|
+
LOADING: 'Loading Dashboard...',
|
|
75
|
+
SUCCESS: 'Dashboard updated.',
|
|
76
|
+
ERROR: 'Error loading dashboard.'
|
|
61
77
|
}
|
|
62
78
|
};
|
|
63
79
|
|
|
@@ -78,8 +94,8 @@ const PT_BR = {
|
|
|
78
94
|
},
|
|
79
95
|
SCAFFOLD: {
|
|
80
96
|
LOADING: 'Verificando estrutura do workspace...',
|
|
81
|
-
SUCCESS: '✔ Estrutura de pastas (
|
|
82
|
-
ALREADY_EXISTS: '✔ Estrutura de pastas (
|
|
97
|
+
SUCCESS: '✔ Estrutura de pastas (.sdd-toolkit/) criada.',
|
|
98
|
+
ALREADY_EXISTS: '✔ Estrutura de pastas (.sdd-toolkit/) verificada.',
|
|
83
99
|
ERROR: 'Falha ao verificar estrutura do workspace.',
|
|
84
100
|
},
|
|
85
101
|
SETUP: {
|
|
@@ -115,7 +131,23 @@ const PT_BR = {
|
|
|
115
131
|
LANGUAGE_RULES: {
|
|
116
132
|
EN: 'Always reply in English unless told otherwise.',
|
|
117
133
|
PT_BR: 'Responda sempre em Português (Brasil), a menos que solicitado o contrário.',
|
|
118
|
-
ES: 'Responda
|
|
134
|
+
ES: 'Responda sempre en Español, a menos que se solicite lo contrario.',
|
|
135
|
+
},
|
|
136
|
+
DASHBOARD: {
|
|
137
|
+
TITLE: '🚀 PROJETO:',
|
|
138
|
+
PHASE: '📅 FASE:',
|
|
139
|
+
STATUS: '📡 STATUS:',
|
|
140
|
+
OVERALL: 'STATUS GERAL',
|
|
141
|
+
COMPLETED: 'Concluídas',
|
|
142
|
+
PENDING: 'Pendentes',
|
|
143
|
+
RECENT: 'ATIVIDADE RECENTE',
|
|
144
|
+
NO_ACTIVITY: 'Nenhuma atividade recente registrada.',
|
|
145
|
+
ACTION: ' 👉 AÇÃO: ',
|
|
146
|
+
HINT: 'Use',
|
|
147
|
+
HINT_SUFFIX: 'para trabalhar.',
|
|
148
|
+
LOADING: 'Carregando Dashboard...',
|
|
149
|
+
SUCCESS: 'Dashboard atualizado.',
|
|
150
|
+
ERROR: 'Erro ao carregar dashboard.'
|
|
119
151
|
}
|
|
120
152
|
};
|
|
121
153
|
|
|
@@ -136,8 +168,8 @@ const ES = {
|
|
|
136
168
|
},
|
|
137
169
|
SCAFFOLD: {
|
|
138
170
|
LOADING: 'Verificando estructura del espacio de trabajo...',
|
|
139
|
-
SUCCESS: '✔ Estructura de carpetas (
|
|
140
|
-
ALREADY_EXISTS: '✔ Estructura de carpetas (
|
|
171
|
+
SUCCESS: '✔ Estructura de carpetas (.sdd-toolkit/) creada.',
|
|
172
|
+
ALREADY_EXISTS: '✔ Estructura de carpetas (.sdd-toolkit/) verificada.',
|
|
141
173
|
ERROR: 'Fallo al verificar estructura del espacio de trabajo.',
|
|
142
174
|
},
|
|
143
175
|
SETUP: {
|
|
@@ -172,8 +204,24 @@ const ES = {
|
|
|
172
204
|
},
|
|
173
205
|
LANGUAGE_RULES: {
|
|
174
206
|
EN: 'Always reply in English unless told otherwise.',
|
|
175
|
-
PT_BR: 'Responda
|
|
207
|
+
PT_BR: 'Responda siempre em Português (Brasil), a menos que solicitado o contrário.',
|
|
176
208
|
ES: 'Responda siempre en Español, a menos que se solicite lo contrario.',
|
|
209
|
+
},
|
|
210
|
+
DASHBOARD: {
|
|
211
|
+
TITLE: '🚀 PROYECTO:',
|
|
212
|
+
PHASE: '📅 FASE:',
|
|
213
|
+
STATUS: '📡 ESTADO:',
|
|
214
|
+
OVERALL: 'ESTADO GENERAL',
|
|
215
|
+
COMPLETED: 'Completadas',
|
|
216
|
+
PENDING: 'Pendientes',
|
|
217
|
+
RECENT: 'ACTIVIDAD RECIENTE',
|
|
218
|
+
NO_ACTIVITY: 'No hay actividad reciente registrada.',
|
|
219
|
+
ACTION: ' 👉 ACCIÓN: ',
|
|
220
|
+
HINT: 'Use',
|
|
221
|
+
HINT_SUFFIX: 'para trabajar.',
|
|
222
|
+
LOADING: 'Cargando Dashboard...',
|
|
223
|
+
SUCCESS: 'Dashboard actualizado.',
|
|
224
|
+
ERROR: 'Error al cargar dashboard.'
|
|
177
225
|
}
|
|
178
226
|
};
|
|
179
227
|
|
|
@@ -183,4 +231,4 @@ const TRANSLATIONS = {
|
|
|
183
231
|
es: ES
|
|
184
232
|
};
|
|
185
233
|
|
|
186
|
-
module.exports = { TRANSLATIONS, MESSAGES: EN };
|
|
234
|
+
module.exports = { TRANSLATIONS, MESSAGES: EN };
|