trackfw 1.0.4 → 2.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.
- package/README.md +17 -8
- package/package.json +1 -1
- package/src/commands/adr.js +9 -8
- package/src/commands/discover.js +359 -0
- package/src/commands/index.js +1 -0
- package/src/commands/init.js +50 -19
- package/src/commands/log.js +5 -4
- package/src/commands/plugins.js +11 -10
- package/src/commands/req.js +11 -10
- package/src/commands/roadmap.js +6 -5
- package/src/commands/status.js +2 -1
- package/src/commands/validate.js +5 -4
- package/src/config/index.js +92 -0
- package/src/generators/adr.js +6 -5
- package/src/generators/init.js +66 -0
- package/src/generators/req.js +3 -2
- package/src/generators/roadmap.js +150 -57
- package/src/i18n/index.js +53 -0
- package/src/i18n/locales/en-US.json +122 -0
- package/src/i18n/locales/es-ES.json +122 -0
- package/src/i18n/locales/pt-BR.json +122 -0
- package/src/validator/index.js +196 -97
|
@@ -1,40 +1,80 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const fs = require('fs')
|
|
3
3
|
const path = require('path')
|
|
4
|
+
const config = require('../config')
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
const STATE_ORDER = ['wip', 'backlog', 'blocked', 'done', 'abandoned']
|
|
7
|
+
|
|
8
|
+
// stateDir retorna o caminho do diretório para um estado válido no modo flat, ou null se inválido.
|
|
9
|
+
function stateDir(state) {
|
|
10
|
+
const cfg = config.load()
|
|
11
|
+
const valid = ['backlog', 'wip', 'blocked', 'done', 'abandoned']
|
|
12
|
+
if (!valid.includes(state)) return null
|
|
13
|
+
return cfg.roadmapDir + '/' + state
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
// agentStateDir retorna o diretório para um agente+estado em modo by_agent.
|
|
17
|
+
// agent=null usa o primeiro agente configurado (ou "default" se lista vazia).
|
|
18
|
+
function agentStateDir(agent, state) {
|
|
19
|
+
const cfg = config.load()
|
|
20
|
+
const valid = ['backlog', 'wip', 'blocked', 'done', 'abandoned']
|
|
21
|
+
if (!valid.includes(state)) return null
|
|
22
|
+
if (!agent) {
|
|
23
|
+
agent = cfg.agents && cfg.agents.length > 0 ? cfg.agents[0] : 'default'
|
|
24
|
+
}
|
|
25
|
+
return cfg.roadmapDir + '/' + agent + '/' + state
|
|
26
|
+
}
|
|
14
27
|
|
|
15
|
-
|
|
28
|
+
// logPath retorna o caminho do arquivo de log de transições.
|
|
29
|
+
function logPath() {
|
|
30
|
+
return config.load().roadmapDir + '/.trackfw-log'
|
|
31
|
+
}
|
|
16
32
|
|
|
17
33
|
/**
|
|
18
|
-
* listRoadmaps — lista roadmaps agrupados por estado (
|
|
34
|
+
* listRoadmaps — lista roadmaps agrupados por estado (e por agente em modo by_agent).
|
|
19
35
|
* Se nenhum encontrado imprime mensagem orientando o usuário.
|
|
20
36
|
*/
|
|
21
37
|
function listRoadmaps() {
|
|
38
|
+
const cfg = config.load()
|
|
22
39
|
let found = false
|
|
23
40
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
41
|
+
if (cfg.roadmapNamespacing === config.NAMESPACING_BY_AGENT) {
|
|
42
|
+
let agents = cfg.agents || []
|
|
43
|
+
if (agents.length === 0) {
|
|
44
|
+
try {
|
|
45
|
+
agents = fs.readdirSync(cfg.roadmapDir).filter(f => {
|
|
46
|
+
try { return fs.statSync(path.join(cfg.roadmapDir, f)).isDirectory() } catch (_) { return false }
|
|
47
|
+
})
|
|
48
|
+
} catch (_) { agents = [] }
|
|
31
49
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
50
|
+
for (const agent of agents) {
|
|
51
|
+
for (const state of STATE_ORDER) {
|
|
52
|
+
const dir = cfg.roadmapDir + '/' + agent + '/' + state
|
|
53
|
+
let files = []
|
|
54
|
+
try {
|
|
55
|
+
files = fs.readdirSync(dir).filter(f => {
|
|
56
|
+
try { return !fs.statSync(path.join(dir, f)).isDirectory() && f.endsWith('.md') } catch (_) { return false }
|
|
57
|
+
})
|
|
58
|
+
} catch (_) { continue }
|
|
59
|
+
if (files.length === 0) continue
|
|
60
|
+
found = true
|
|
61
|
+
console.log(`[${agent}/${state}]`)
|
|
62
|
+
for (const f of files) console.log(` ${f}`)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} else {
|
|
66
|
+
for (const state of STATE_ORDER) {
|
|
67
|
+
const dir = cfg.roadmapDir + '/' + state
|
|
68
|
+
let files = []
|
|
69
|
+
try {
|
|
70
|
+
files = fs.readdirSync(dir).filter(f => {
|
|
71
|
+
try { return !fs.statSync(path.join(dir, f)).isDirectory() && f.endsWith('.md') } catch (_) { return false }
|
|
72
|
+
})
|
|
73
|
+
} catch (_) { continue }
|
|
74
|
+
if (files.length === 0) continue
|
|
75
|
+
found = true
|
|
76
|
+
console.log(`[${state}]`)
|
|
77
|
+
for (const f of files) console.log(` ${f}`)
|
|
38
78
|
}
|
|
39
79
|
}
|
|
40
80
|
|
|
@@ -44,8 +84,8 @@ function listRoadmaps() {
|
|
|
44
84
|
}
|
|
45
85
|
|
|
46
86
|
/**
|
|
47
|
-
* showRoadmap — busca
|
|
48
|
-
*
|
|
87
|
+
* showRoadmap — busca <roadmapDir>/ESTADO/NOME*.md (partial match, flat) ou
|
|
88
|
+
* <roadmapDir>/AGENTE/ESTADO/NOME*.md (by_agent), imprime cabeçalho + conteúdo.
|
|
49
89
|
*/
|
|
50
90
|
function showRoadmap(name) {
|
|
51
91
|
const matches = findRoadmapMatches(name)
|
|
@@ -58,9 +98,7 @@ function showRoadmap(name) {
|
|
|
58
98
|
|
|
59
99
|
if (matches.length > 1) {
|
|
60
100
|
console.log('Multiple roadmaps found — be more specific:')
|
|
61
|
-
for (const m of matches) {
|
|
62
|
-
console.log(` ${m}`)
|
|
63
|
-
}
|
|
101
|
+
for (const m of matches) console.log(` ${m}`)
|
|
64
102
|
console.error(`ambiguous match for "${name}"`)
|
|
65
103
|
process.exitCode = 1
|
|
66
104
|
return
|
|
@@ -78,12 +116,12 @@ function showRoadmap(name) {
|
|
|
78
116
|
|
|
79
117
|
/**
|
|
80
118
|
* moveRoadmap — move arquivo para diretório do estado alvo.
|
|
81
|
-
*
|
|
82
|
-
* move com fs.renameSync, chama appendTransitionLog, imprime confirmação.
|
|
119
|
+
* Em modo by_agent, mantém o agente na hierarquia.
|
|
83
120
|
*/
|
|
84
121
|
function moveRoadmap(name, state) {
|
|
85
|
-
const
|
|
86
|
-
|
|
122
|
+
const cfg = config.load()
|
|
123
|
+
const valid = ['backlog', 'wip', 'blocked', 'done', 'abandoned']
|
|
124
|
+
if (!valid.includes(state)) {
|
|
87
125
|
console.error(`invalid state "${state}" — valid states: backlog, wip, blocked, done, abandoned`)
|
|
88
126
|
process.exitCode = 1
|
|
89
127
|
return
|
|
@@ -97,9 +135,7 @@ function moveRoadmap(name, state) {
|
|
|
97
135
|
}
|
|
98
136
|
if (matches.length > 1) {
|
|
99
137
|
console.log('Multiple roadmaps found — be more specific:')
|
|
100
|
-
for (const m of matches) {
|
|
101
|
-
console.log(` ${m}`)
|
|
102
|
-
}
|
|
138
|
+
for (const m of matches) console.log(` ${m}`)
|
|
103
139
|
console.error(`ambiguous match for "${name}"`)
|
|
104
140
|
process.exitCode = 1
|
|
105
141
|
return
|
|
@@ -107,22 +143,41 @@ function moveRoadmap(name, state) {
|
|
|
107
143
|
|
|
108
144
|
const src = matches[0]
|
|
109
145
|
const basename = path.basename(src)
|
|
110
|
-
|
|
146
|
+
let targetDir, fromState, logBasename
|
|
147
|
+
|
|
148
|
+
if (cfg.roadmapNamespacing === config.NAMESPACING_BY_AGENT) {
|
|
149
|
+
const agentDir = path.dirname(path.dirname(src))
|
|
150
|
+
const agent = path.basename(agentDir)
|
|
151
|
+
fromState = path.basename(path.dirname(src))
|
|
152
|
+
targetDir = agentStateDir(agent, state)
|
|
153
|
+
if (!targetDir) {
|
|
154
|
+
console.error(`invalid state "${state}"`)
|
|
155
|
+
process.exitCode = 1
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
logBasename = agent + '/' + basename
|
|
159
|
+
} else {
|
|
160
|
+
fromState = path.basename(path.dirname(src))
|
|
161
|
+
targetDir = stateDir(state)
|
|
162
|
+
if (!targetDir) {
|
|
163
|
+
console.error(`invalid state "${state}"`)
|
|
164
|
+
process.exitCode = 1
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
logBasename = basename
|
|
168
|
+
}
|
|
111
169
|
|
|
112
|
-
try {
|
|
113
|
-
fs.mkdirSync(targetDir, { recursive: true })
|
|
114
|
-
} catch (_) {}
|
|
170
|
+
try { fs.mkdirSync(targetDir, { recursive: true }) } catch (_) {}
|
|
115
171
|
|
|
116
172
|
const dst = path.join(targetDir, basename)
|
|
117
173
|
fs.renameSync(src, dst)
|
|
118
174
|
|
|
119
|
-
appendTransitionLog(
|
|
175
|
+
appendTransitionLog(logBasename, fromState, state)
|
|
120
176
|
console.log(`✓ moved ${basename} → ${targetDir}`)
|
|
121
177
|
}
|
|
122
178
|
|
|
123
179
|
/**
|
|
124
|
-
* appendTransitionLog — append em
|
|
125
|
-
* Formato: `YYYY-MM-DD HH:mm <basename padded to 50> <fromState> → <toState>\n`
|
|
180
|
+
* appendTransitionLog — append em <roadmapDir>/.trackfw-log.
|
|
126
181
|
*/
|
|
127
182
|
function appendTransitionLog(basename, fromState, toState) {
|
|
128
183
|
const now = new Date()
|
|
@@ -135,24 +190,39 @@ function appendTransitionLog(basename, fromState, toState) {
|
|
|
135
190
|
const line = `${timestamp} ${basename.padEnd(50)} ${fromState} → ${toState}\n`
|
|
136
191
|
|
|
137
192
|
try {
|
|
138
|
-
|
|
139
|
-
fs.
|
|
193
|
+
const lp = logPath()
|
|
194
|
+
fs.mkdirSync(path.dirname(lp), { recursive: true })
|
|
195
|
+
fs.appendFileSync(lp, line, 'utf8')
|
|
140
196
|
} catch (_) {}
|
|
141
197
|
}
|
|
142
198
|
|
|
143
199
|
/**
|
|
144
|
-
* newRoadmap — cria roadmap em
|
|
200
|
+
* newRoadmap — cria roadmap em <roadmapDir>/backlog/ROADMAP-YYYY-MM-DD-<slug>.md.
|
|
201
|
+
* Em modo by_agent, usa o primeiro agente configurado.
|
|
145
202
|
*/
|
|
146
203
|
function newRoadmap(title, reqPath) {
|
|
204
|
+
const cfg = config.load()
|
|
147
205
|
const now = new Date()
|
|
148
206
|
const yyyy = now.getFullYear()
|
|
149
207
|
const mm = String(now.getMonth() + 1).padStart(2, '0')
|
|
150
208
|
const dd = String(now.getDate()).padStart(2, '0')
|
|
151
209
|
const date = `${yyyy}-${mm}-${dd}`
|
|
152
210
|
const slug = toSlug(title)
|
|
153
|
-
const filename = `docs/roadmaps/backlog/ROADMAP-${date}-${slug}.md`
|
|
154
211
|
|
|
155
|
-
|
|
212
|
+
let backlogDir
|
|
213
|
+
if (cfg.roadmapNamespacing === config.NAMESPACING_BY_AGENT) {
|
|
214
|
+
backlogDir = agentStateDir(null, 'backlog')
|
|
215
|
+
if (!backlogDir) {
|
|
216
|
+
console.error('cannot resolve backlog dir in by_agent mode')
|
|
217
|
+
process.exitCode = 1
|
|
218
|
+
return
|
|
219
|
+
}
|
|
220
|
+
} else {
|
|
221
|
+
backlogDir = cfg.roadmapDir + '/backlog'
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const filename = `${backlogDir}/ROADMAP-${date}-${slug}.md`
|
|
225
|
+
fs.mkdirSync(backlogDir, { recursive: true })
|
|
156
226
|
|
|
157
227
|
const body = `# Roadmap: ${title}
|
|
158
228
|
|
|
@@ -183,21 +253,43 @@ REQ: ${reqPath || ''}
|
|
|
183
253
|
|
|
184
254
|
/**
|
|
185
255
|
* findRoadmapMatches — retorna array de paths que contêm `name` (case-insensitive) em qualquer estado.
|
|
256
|
+
* Suporta modo flat (1 nível) e by_agent (2 níveis).
|
|
186
257
|
*/
|
|
187
258
|
function findRoadmapMatches(name) {
|
|
259
|
+
const cfg = config.load()
|
|
188
260
|
const matches = []
|
|
189
261
|
const nameLower = name.toLowerCase()
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
let
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
262
|
+
|
|
263
|
+
if (cfg.roadmapNamespacing === config.NAMESPACING_BY_AGENT) {
|
|
264
|
+
let agents = cfg.agents || []
|
|
265
|
+
if (agents.length === 0) {
|
|
266
|
+
try {
|
|
267
|
+
agents = fs.readdirSync(cfg.roadmapDir).filter(f => {
|
|
268
|
+
try { return fs.statSync(path.join(cfg.roadmapDir, f)).isDirectory() } catch (_) { return false }
|
|
269
|
+
})
|
|
270
|
+
} catch (_) { agents = ['default'] }
|
|
271
|
+
}
|
|
272
|
+
for (const agent of agents) {
|
|
273
|
+
for (const state of STATE_ORDER) {
|
|
274
|
+
const dir = cfg.roadmapDir + '/' + agent + '/' + state
|
|
275
|
+
let files = []
|
|
276
|
+
try { files = fs.readdirSync(dir) } catch (_) { continue }
|
|
277
|
+
for (const f of files) {
|
|
278
|
+
if (f.toLowerCase().includes(nameLower) && f.endsWith('.md')) {
|
|
279
|
+
matches.push(path.join(dir, f))
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
197
283
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
284
|
+
} else {
|
|
285
|
+
for (const state of STATE_ORDER) {
|
|
286
|
+
const dir = cfg.roadmapDir + '/' + state
|
|
287
|
+
let files = []
|
|
288
|
+
try { files = fs.readdirSync(dir) } catch (_) { continue }
|
|
289
|
+
for (const f of files) {
|
|
290
|
+
if (f.toLowerCase().includes(nameLower) && f.endsWith('.md')) {
|
|
291
|
+
matches.push(path.join(dir, f))
|
|
292
|
+
}
|
|
201
293
|
}
|
|
202
294
|
}
|
|
203
295
|
}
|
|
@@ -215,10 +307,11 @@ function toSlug(s) {
|
|
|
215
307
|
}
|
|
216
308
|
|
|
217
309
|
module.exports = {
|
|
218
|
-
VALID_STATES,
|
|
219
310
|
listRoadmaps,
|
|
220
311
|
showRoadmap,
|
|
221
312
|
moveRoadmap,
|
|
222
313
|
appendTransitionLog,
|
|
223
314
|
newRoadmap,
|
|
315
|
+
stateDir,
|
|
316
|
+
agentStateDir,
|
|
224
317
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
|
|
5
|
+
function detectLocale() {
|
|
6
|
+
const raw = process.env.LANG || process.env.LC_ALL || process.env.LANGUAGE || ''
|
|
7
|
+
// Mapeia pt_BR.UTF-8 → pt-BR, es_ES.UTF-8 → es-ES, en_US.UTF-8 → en-US
|
|
8
|
+
const map = { pt: 'pt-BR', es: 'es-ES' }
|
|
9
|
+
const code = raw.split('.')[0].replace('_', '-') // pt-BR
|
|
10
|
+
const lang = code.split('-')[0] // pt
|
|
11
|
+
if (map[lang]) return map[lang]
|
|
12
|
+
// Fallback para Windows: usar Intl
|
|
13
|
+
try {
|
|
14
|
+
const loc = Intl.DateTimeFormat().resolvedOptions().locale
|
|
15
|
+
const l = loc.split('-')[0]
|
|
16
|
+
if (map[l]) return map[l]
|
|
17
|
+
} catch (_) {}
|
|
18
|
+
return 'en-US'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let _locale = null
|
|
22
|
+
let _messages = null
|
|
23
|
+
|
|
24
|
+
function load() {
|
|
25
|
+
if (_messages) return
|
|
26
|
+
_locale = detectLocale()
|
|
27
|
+
const filePath = path.join(__dirname, 'locales', `${_locale}.json`)
|
|
28
|
+
const fallback = path.join(__dirname, 'locales', 'en-US.json')
|
|
29
|
+
try {
|
|
30
|
+
_messages = JSON.parse(fs.readFileSync(fs.existsSync(filePath) ? filePath : fallback, 'utf8'))
|
|
31
|
+
} catch (_) {
|
|
32
|
+
_messages = {}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function t(key, vars = {}) {
|
|
37
|
+
load()
|
|
38
|
+
const keys = key.split('.')
|
|
39
|
+
let val = _messages
|
|
40
|
+
for (const k of keys) {
|
|
41
|
+
val = val?.[k]
|
|
42
|
+
if (val === undefined) break
|
|
43
|
+
}
|
|
44
|
+
if (typeof val !== 'string') return key
|
|
45
|
+
return val.replace(/\{\{(\w+)\}\}/g, (_, k) => vars[k] ?? `{{${k}}}`)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function locale() {
|
|
49
|
+
load()
|
|
50
|
+
return _locale
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = { t, locale }
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"init": {
|
|
3
|
+
"description": "Initialize trackfw governance in the current project",
|
|
4
|
+
"prompt": {
|
|
5
|
+
"projectName": "Project name?",
|
|
6
|
+
"projectType": "Project type?",
|
|
7
|
+
"frontendStack": "Frontend stack?",
|
|
8
|
+
"pkgManager": "Package manager?",
|
|
9
|
+
"backendLang": "Backend language?",
|
|
10
|
+
"backendFramework": "Backend framework?",
|
|
11
|
+
"gitHooks": "Git hooks?",
|
|
12
|
+
"ci": "CI system?",
|
|
13
|
+
"aiTools": "Which AI assistants do you use?",
|
|
14
|
+
"projectType_fullstack": "Full-stack (frontend + backend)",
|
|
15
|
+
"projectType_frontend": "Frontend only",
|
|
16
|
+
"projectType_backend": "Backend only",
|
|
17
|
+
"projectType_governance": "Governance only (no build stack)"
|
|
18
|
+
},
|
|
19
|
+
"success": "✓ trackfw initialized — run 'trackfw status' to see your governance state."
|
|
20
|
+
},
|
|
21
|
+
"adr": {
|
|
22
|
+
"description": "Manage Architecture Decision Records",
|
|
23
|
+
"new": {
|
|
24
|
+
"description": "Create a new Architecture Decision Record",
|
|
25
|
+
"prompt": {
|
|
26
|
+
"title": "ADR title?",
|
|
27
|
+
"status": "Initial status?",
|
|
28
|
+
"context": "Context (what motivates this decision)?",
|
|
29
|
+
"decision": "Decision (what was decided)?",
|
|
30
|
+
"consequences": "Consequences (positive and negative)?",
|
|
31
|
+
"alternatives": "Alternatives considered?"
|
|
32
|
+
},
|
|
33
|
+
"created": "✓ ADR created: {{path}}"
|
|
34
|
+
},
|
|
35
|
+
"list": {
|
|
36
|
+
"description": "List all ADRs with status",
|
|
37
|
+
"empty": "No ADRs found in docs/adr/"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"req": {
|
|
41
|
+
"description": "Manage Requirements",
|
|
42
|
+
"new": {
|
|
43
|
+
"description": "Create a new requirement",
|
|
44
|
+
"prompt": {
|
|
45
|
+
"title": "Project requirement",
|
|
46
|
+
"motivation": "Motivation (why is this needed)?",
|
|
47
|
+
"criteria": "Acceptance Criteria (one per line)",
|
|
48
|
+
"domainQuestion_authentication": "How will users authenticate?",
|
|
49
|
+
"domainQuestion_ui": "Is there an existing UI framework or design system?",
|
|
50
|
+
"domainQuestion_persistence": "Which database engine will be used?",
|
|
51
|
+
"domainQuestion_api": "Which API protocol will be used?",
|
|
52
|
+
"domainQuestion_deploy": "What is the deployment target?",
|
|
53
|
+
"domainQuestion_events": "Which event broker will be used?"
|
|
54
|
+
},
|
|
55
|
+
"detectedDomains": "Detected domains: {{domains}}",
|
|
56
|
+
"created": "✓ REQ created: {{path}}",
|
|
57
|
+
"adrDraftsCreated": "ADR drafts created:",
|
|
58
|
+
"resolveADRs": "Resolve these ADRs (set Status: Accepted) before creating a roadmap.",
|
|
59
|
+
"adrWarning": "warning: could not create ADR draft for {{slug}}: {{message}}"
|
|
60
|
+
},
|
|
61
|
+
"list": {
|
|
62
|
+
"description": "List all REQs with status",
|
|
63
|
+
"empty": "No REQs found in docs/req/"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"roadmap": {
|
|
67
|
+
"description": "Manage Roadmaps",
|
|
68
|
+
"list": {
|
|
69
|
+
"description": "List all roadmaps grouped by state",
|
|
70
|
+
"empty": "No roadmaps found."
|
|
71
|
+
},
|
|
72
|
+
"show": {
|
|
73
|
+
"description": "Show a roadmap by name (partial match)",
|
|
74
|
+
"notFound": "Roadmap not found: {{name}}"
|
|
75
|
+
},
|
|
76
|
+
"move": {
|
|
77
|
+
"description": "Move a roadmap between states (backlog|wip|blocked|done|abandoned)",
|
|
78
|
+
"success": "✓ Moved {{name}} → {{state}}",
|
|
79
|
+
"notFound": "Roadmap not found: {{name}}"
|
|
80
|
+
},
|
|
81
|
+
"new": {
|
|
82
|
+
"description": "Create a new roadmap from a REQ",
|
|
83
|
+
"created": "✓ Roadmap created: {{path}}"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"validate": {
|
|
87
|
+
"description": "Validate governance rules (use as CI gate)",
|
|
88
|
+
"ok": "✓ No violations found.",
|
|
89
|
+
"violations": "✗ Violations ({{count}}):",
|
|
90
|
+
"warnings": "⚠ Warnings ({{count}}):"
|
|
91
|
+
},
|
|
92
|
+
"status": {
|
|
93
|
+
"description": "Show project governance status"
|
|
94
|
+
},
|
|
95
|
+
"log": {
|
|
96
|
+
"description": "Show roadmap state transition history",
|
|
97
|
+
"empty": "No transitions recorded yet.",
|
|
98
|
+
"tail": "Number of recent transitions to show",
|
|
99
|
+
"header": "── trackfw log ─────────────────────────"
|
|
100
|
+
},
|
|
101
|
+
"plugins": {
|
|
102
|
+
"description": "Manage trackfw plugins",
|
|
103
|
+
"list": {
|
|
104
|
+
"description": "List installed plugins",
|
|
105
|
+
"empty": "No plugins installed. Use `trackfw plugins add <user/repo>` to install one."
|
|
106
|
+
},
|
|
107
|
+
"add": {
|
|
108
|
+
"description": "Install a plugin from GitHub Releases (user/repo or user/repo@tag)",
|
|
109
|
+
"installing": "Installing plugin from {{repo}}...",
|
|
110
|
+
"success": "Plugin \"{{name}}\" installed successfully."
|
|
111
|
+
},
|
|
112
|
+
"remove": {
|
|
113
|
+
"description": "Remove an installed plugin",
|
|
114
|
+
"success": "Plugin \"{{name}}\" removed."
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"errors": {
|
|
118
|
+
"notFound": "Not found: {{path}}",
|
|
119
|
+
"downloadFailed": "download failed: HTTP {{status}} for {{url}}",
|
|
120
|
+
"pluginNotFound": "plugin \"{{name}}\" not found"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
{
|
|
2
|
+
"init": {
|
|
3
|
+
"description": "Inicializa la gobernanza trackfw en el proyecto actual",
|
|
4
|
+
"prompt": {
|
|
5
|
+
"projectName": "¿Nombre del proyecto?",
|
|
6
|
+
"projectType": "¿Tipo de proyecto?",
|
|
7
|
+
"frontendStack": "¿Stack de frontend?",
|
|
8
|
+
"pkgManager": "¿Gestor de paquetes?",
|
|
9
|
+
"backendLang": "¿Lenguaje de backend?",
|
|
10
|
+
"backendFramework": "¿Framework de backend?",
|
|
11
|
+
"gitHooks": "¿Git hooks?",
|
|
12
|
+
"ci": "¿Sistema de CI?",
|
|
13
|
+
"aiTools": "¿Qué asistentes de IA usas?",
|
|
14
|
+
"projectType_fullstack": "Full-stack (frontend + backend)",
|
|
15
|
+
"projectType_frontend": "Solo frontend",
|
|
16
|
+
"projectType_backend": "Solo backend",
|
|
17
|
+
"projectType_governance": "Solo gobernanza (sin stack de build)"
|
|
18
|
+
},
|
|
19
|
+
"success": "✓ trackfw inicializado — ejecuta 'trackfw status' para ver el estado de gobernanza."
|
|
20
|
+
},
|
|
21
|
+
"adr": {
|
|
22
|
+
"description": "Gestionar Architecture Decision Records",
|
|
23
|
+
"new": {
|
|
24
|
+
"description": "Crear un nuevo Architecture Decision Record",
|
|
25
|
+
"prompt": {
|
|
26
|
+
"title": "¿Título del ADR?",
|
|
27
|
+
"status": "¿Estado inicial?",
|
|
28
|
+
"context": "¿Contexto (qué motiva esta decisión)?",
|
|
29
|
+
"decision": "¿Decisión (qué fue decidido)?",
|
|
30
|
+
"consequences": "¿Consecuencias (positivas y negativas)?",
|
|
31
|
+
"alternatives": "¿Alternativas consideradas?"
|
|
32
|
+
},
|
|
33
|
+
"created": "✓ ADR creado: {{path}}"
|
|
34
|
+
},
|
|
35
|
+
"list": {
|
|
36
|
+
"description": "Listar todos los ADRs con estado",
|
|
37
|
+
"empty": "No se encontraron ADRs en docs/adr/"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"req": {
|
|
41
|
+
"description": "Gestionar Requisitos",
|
|
42
|
+
"new": {
|
|
43
|
+
"description": "Crear un nuevo requisito",
|
|
44
|
+
"prompt": {
|
|
45
|
+
"title": "Requisito del proyecto",
|
|
46
|
+
"motivation": "¿Motivación (por qué lo necesitamos)?",
|
|
47
|
+
"criteria": "Criterios de aceptación (uno por línea)",
|
|
48
|
+
"domainQuestion_authentication": "¿Cómo se autenticarán los usuarios?",
|
|
49
|
+
"domainQuestion_ui": "¿Existe un framework de UI o design system ya elegido?",
|
|
50
|
+
"domainQuestion_persistence": "¿Qué motor de base de datos se usará?",
|
|
51
|
+
"domainQuestion_api": "¿Qué protocolo de API se usará?",
|
|
52
|
+
"domainQuestion_deploy": "¿Cuál es el destino de despliegue?",
|
|
53
|
+
"domainQuestion_events": "¿Qué message broker se usará?"
|
|
54
|
+
},
|
|
55
|
+
"detectedDomains": "Dominios detectados: {{domains}}",
|
|
56
|
+
"created": "✓ REQ creado: {{path}}",
|
|
57
|
+
"adrDraftsCreated": "ADR borradores creados:",
|
|
58
|
+
"resolveADRs": "Resuelve estos ADRs (establece Status: Accepted) antes de crear un roadmap.",
|
|
59
|
+
"adrWarning": "advertencia: no se pudo crear ADR borrador para {{slug}}: {{message}}"
|
|
60
|
+
},
|
|
61
|
+
"list": {
|
|
62
|
+
"description": "Listar todos los REQs con estado",
|
|
63
|
+
"empty": "No se encontraron REQs en docs/req/"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"roadmap": {
|
|
67
|
+
"description": "Gestionar Roadmaps",
|
|
68
|
+
"list": {
|
|
69
|
+
"description": "Listar todos los roadmaps agrupados por estado",
|
|
70
|
+
"empty": "No se encontraron roadmaps."
|
|
71
|
+
},
|
|
72
|
+
"show": {
|
|
73
|
+
"description": "Mostrar roadmap por nombre (coincidencia parcial)",
|
|
74
|
+
"notFound": "Roadmap no encontrado: {{name}}"
|
|
75
|
+
},
|
|
76
|
+
"move": {
|
|
77
|
+
"description": "Mover un roadmap entre estados (backlog|wip|blocked|done|abandoned)",
|
|
78
|
+
"success": "✓ Movido {{name}} → {{state}}",
|
|
79
|
+
"notFound": "Roadmap no encontrado: {{name}}"
|
|
80
|
+
},
|
|
81
|
+
"new": {
|
|
82
|
+
"description": "Crear un nuevo roadmap desde un REQ",
|
|
83
|
+
"created": "✓ Roadmap creado: {{path}}"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
"validate": {
|
|
87
|
+
"description": "Validar reglas de gobernanza (úsalo como gate de CI)",
|
|
88
|
+
"ok": "✓ No se encontraron violaciones.",
|
|
89
|
+
"violations": "✗ Violaciones ({{count}}):",
|
|
90
|
+
"warnings": "⚠ Avisos ({{count}}):"
|
|
91
|
+
},
|
|
92
|
+
"status": {
|
|
93
|
+
"description": "Mostrar el estado actual de gobernanza del proyecto"
|
|
94
|
+
},
|
|
95
|
+
"log": {
|
|
96
|
+
"description": "Mostrar historial de transiciones de estado de los roadmaps",
|
|
97
|
+
"empty": "Aún no hay transiciones registradas.",
|
|
98
|
+
"tail": "Número de transiciones recientes a mostrar",
|
|
99
|
+
"header": "── trackfw log ─────────────────────────"
|
|
100
|
+
},
|
|
101
|
+
"plugins": {
|
|
102
|
+
"description": "Gestionar plugins de trackfw",
|
|
103
|
+
"list": {
|
|
104
|
+
"description": "Listar plugins instalados",
|
|
105
|
+
"empty": "No hay plugins instalados. Usa `trackfw plugins add <user/repo>` para instalar uno."
|
|
106
|
+
},
|
|
107
|
+
"add": {
|
|
108
|
+
"description": "Instalar un plugin desde GitHub Releases (user/repo o user/repo@tag)",
|
|
109
|
+
"installing": "Instalando plugin desde {{repo}}...",
|
|
110
|
+
"success": "Plugin \"{{name}}\" instalado correctamente."
|
|
111
|
+
},
|
|
112
|
+
"remove": {
|
|
113
|
+
"description": "Eliminar un plugin instalado",
|
|
114
|
+
"success": "Plugin \"{{name}}\" eliminado."
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
"errors": {
|
|
118
|
+
"notFound": "No encontrado: {{path}}",
|
|
119
|
+
"downloadFailed": "fallo en la descarga: HTTP {{status}} para {{url}}",
|
|
120
|
+
"pluginNotFound": "plugin \"{{name}}\" no encontrado"
|
|
121
|
+
}
|
|
122
|
+
}
|