trackfw 1.0.4 → 1.1.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 CHANGED
@@ -79,12 +79,14 @@ brew install kgsaran/tap/trackfw
79
79
  go install github.com/kgsaran/trackfw/cmd/trackfw@latest
80
80
  ```
81
81
 
82
- ### npm
82
+ ### npm (pure Node.js — no binary)
83
83
 
84
84
  ```bash
85
85
  npm install -g trackfw
86
86
  ```
87
87
 
88
+ The npm package is pure Node.js — no compiled binary, no postinstall download. Works everywhere Node.js ≥ 18 is installed, including corporate Windows environments where unsigned `.exe` files are blocked by antivirus.
89
+
88
90
  ### pip
89
91
 
90
92
  ```bash
@@ -127,18 +129,25 @@ trackfw status
127
129
  | `trackfw req new "title"` | Create a REQ with guided ADR discovery |
128
130
  | `trackfw req list` | List all REQs with status |
129
131
  | `trackfw roadmap new "title"` | Create a roadmap in `backlog/` |
132
+ | `trackfw roadmap show <name>` | Print a roadmap with its current state |
130
133
  | `trackfw roadmap move <name> <state>` | Move roadmap between states |
131
134
  | `trackfw roadmap list` | List all roadmaps grouped by state |
132
135
  | `trackfw validate` | Check governance consistency (use as CI gate) |
133
136
  | `trackfw status` | Show wip, blocked, REQs waiting on ADRs |
134
- | `trackfw agents` | Install Claude Code subagents |
135
- | `trackfw gemini` | Install Gemini CLI skills and commands |
136
- | `trackfw cursor` | Install Cursor rules |
137
- | `trackfw copilot` | Install GitHub Copilot instructions |
138
- | `trackfw windsurf` | Install Windsurf rules and workflows |
139
- | `trackfw amazonq` | Install Amazon Q Developer rules |
137
+ | `trackfw log [--tail N]` | Show roadmap state transition history |
138
+ | `trackfw plugins list` | List installed plugins |
139
+ | `trackfw plugins add <user/repo>` | Install a plugin from GitHub Releases |
140
+ | `trackfw plugins remove <name>` | Remove an installed plugin |
141
+ | `trackfw agents` | Install Claude Code subagents *(Go binary only)* |
142
+ | `trackfw gemini` | Install Gemini CLI skills and commands *(Go binary only)* |
143
+ | `trackfw cursor` | Install Cursor rules *(Go binary only)* |
144
+ | `trackfw copilot` | Install GitHub Copilot instructions *(Go binary only)* |
145
+ | `trackfw windsurf` | Install Windsurf rules and workflows *(Go binary only)* |
146
+ | `trackfw amazonq` | Install Amazon Q Developer rules *(Go binary only)* |
140
147
  | `trackfw version` | Print version |
141
148
 
149
+ > **Go binary only** commands (`agents`, `gemini`, `cursor`, `copilot`, `windsurf`, `amazonq`) are available when installed via brew, `install.sh`, or `go install`. When using the npm package, AI integrations are installed through `trackfw init`.
150
+
142
151
  ---
143
152
 
144
153
  ## Governance chain
@@ -227,7 +236,7 @@ $ trackfw status
227
236
 
228
237
  ## AI assistant integration
229
238
 
230
- `trackfw init` asks which AI tools your team uses and installs native governance context for each. Commands can also be run independently.
239
+ `trackfw init` asks which AI tools your team uses and installs native governance context for each. When using the Go binary (brew, `install.sh`, `go install`), each integration can also be run as a standalone command.
231
240
 
232
241
  | Command | Installs | Format |
233
242
  |---|---|---|
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trackfw",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
4
4
  "description": "Governed software delivery framework: ADR → REQ → ROADMAP → kanban",
5
5
  "keywords": [
6
6
  "cli",
@@ -3,26 +3,27 @@
3
3
  const { Command } = require('commander')
4
4
  const { input } = require('@inquirer/prompts')
5
5
  const generators = require('../generators/adr')
6
+ const { t } = require('../i18n')
6
7
 
7
8
  const cmd = new Command('adr')
8
- cmd.description('Manage Architecture Decision Records')
9
+ cmd.description(t('adr.description'))
9
10
 
10
11
  cmd.command('new <title>')
11
- .description('Create a new ADR')
12
+ .description(t('adr.new.description'))
12
13
  .action(async (title) => {
13
14
  const content = { title }
14
15
  // wizard interativo se TTY
15
16
  if (process.stdin.isTTY) {
16
- content.context = await input({ message: 'Context (what motivates this decision)?', default: '' })
17
- content.decision = await input({ message: 'Decision (what was decided)?', default: '' })
18
- content.consequences = await input({ message: 'Consequences (positive and negative)?', default: '' })
19
- content.alternatives = await input({ message: 'Alternatives considered?', default: '' })
17
+ content.context = await input({ message: t('adr.new.prompt.context'), default: '' })
18
+ content.decision = await input({ message: t('adr.new.prompt.decision'), default: '' })
19
+ content.consequences = await input({ message: t('adr.new.prompt.consequences'), default: '' })
20
+ content.alternatives = await input({ message: t('adr.new.prompt.alternatives'), default: '' })
20
21
  }
21
22
  await generators.newADR(content)
22
23
  })
23
24
 
24
25
  cmd.command('list')
25
- .description('List all ADRs in docs/adr/')
26
+ .description(t('adr.list.description'))
26
27
  .action(async () => {
27
28
  await generators.listADRs('docs/adr')
28
29
  })
@@ -1,8 +1,9 @@
1
1
  'use strict'
2
2
  const { Command } = require('commander')
3
+ const { t } = require('../i18n')
3
4
 
4
5
  const cmd = new Command('init')
5
- cmd.description('Initialize trackfw governance in the current project')
6
+ cmd.description(t('init.description'))
6
7
  cmd.action(async () => {
7
8
  const path = require('path')
8
9
  const generators = require('../generators/init')
@@ -19,27 +20,27 @@ cmd.action(async () => {
19
20
  ci: 'none',
20
21
  }
21
22
  await generators.scaffold(cfg)
22
- console.log("\n✓ trackfw initialized — run 'trackfw status' to see your governance state.")
23
+ console.log(`\n${t('init.success')}`)
23
24
  return
24
25
  }
25
26
 
26
27
  const { input, select, checkbox } = require('@inquirer/prompts')
27
28
 
28
- let projectName, projectType, frontend, pkgManager, backend, hooks, ci, aiTools
29
+ let projectName, projectType, frontend, pkgManager, backend, backendFramework, hooks, ci, aiTools
29
30
 
30
31
  try {
31
32
  projectName = await input({
32
- message: 'Project name?',
33
+ message: t('init.prompt.projectName'),
33
34
  default: path.basename(process.cwd()),
34
35
  })
35
36
 
36
37
  projectType = await select({
37
- message: 'Project type?',
38
+ message: t('init.prompt.projectType'),
38
39
  choices: [
39
- { name: 'Full-stack (frontend + backend)', value: 'fullstack' },
40
- { name: 'Frontend only', value: 'frontend' },
41
- { name: 'Backend only', value: 'backend' },
42
- { name: 'Governance only (no build stack)', value: 'governance' },
40
+ { name: t('init.prompt.projectType_fullstack'), value: 'fullstack' },
41
+ { name: t('init.prompt.projectType_frontend'), value: 'frontend' },
42
+ { name: t('init.prompt.projectType_backend'), value: 'backend' },
43
+ { name: t('init.prompt.projectType_governance'), value: 'governance' },
43
44
  ],
44
45
  })
45
46
 
@@ -47,7 +48,7 @@ cmd.action(async () => {
47
48
  pkgManager = ''
48
49
  if (projectType === 'fullstack' || projectType === 'frontend') {
49
50
  frontend = await select({
50
- message: 'Frontend stack?',
51
+ message: t('init.prompt.frontendStack'),
51
52
  choices: [
52
53
  { name: 'React / Next.js', value: 'react' },
53
54
  { name: 'Vue', value: 'vue' },
@@ -55,7 +56,7 @@ cmd.action(async () => {
55
56
  ],
56
57
  })
57
58
  pkgManager = await select({
58
- message: 'Package manager?',
59
+ message: t('init.prompt.pkgManager'),
59
60
  choices: [
60
61
  { name: 'npm', value: 'npm' },
61
62
  { name: 'pnpm', value: 'pnpm' },
@@ -66,20 +67,50 @@ cmd.action(async () => {
66
67
  }
67
68
 
68
69
  backend = ''
70
+ let backendFramework = ''
69
71
  if (projectType === 'fullstack' || projectType === 'backend') {
70
72
  backend = await select({
71
- message: 'Backend stack?',
73
+ message: t('init.prompt.backendLang'),
72
74
  choices: [
73
75
  { name: 'Go', value: 'go' },
74
- { name: 'Java / Spring Boot', value: 'java' },
76
+ { name: 'Java', value: 'java' },
75
77
  { name: 'Node.js', value: 'node' },
76
78
  { name: 'Python', value: 'python' },
77
79
  ],
78
80
  })
81
+
82
+ const frameworkChoices = {
83
+ go: [
84
+ { name: 'Gin', value: 'gin' },
85
+ { name: 'Echo', value: 'echo' },
86
+ { name: 'Fiber', value: 'fiber' },
87
+ { name: 'Standard library (net/http)', value: 'stdlib' },
88
+ ],
89
+ java: [
90
+ { name: 'Spring Boot', value: 'spring-boot' },
91
+ { name: 'Quarkus', value: 'quarkus' },
92
+ { name: 'Micronaut', value: 'micronaut' },
93
+ ],
94
+ node: [
95
+ { name: 'Express', value: 'express' },
96
+ { name: 'Fastify', value: 'fastify' },
97
+ { name: 'NestJS', value: 'nestjs' },
98
+ { name: 'Koa', value: 'koa' },
99
+ ],
100
+ python: [
101
+ { name: 'FastAPI', value: 'fastapi' },
102
+ { name: 'Django', value: 'django' },
103
+ { name: 'Flask', value: 'flask' },
104
+ ],
105
+ }
106
+ backendFramework = await select({
107
+ message: t('init.prompt.backendFramework'),
108
+ choices: frameworkChoices[backend] || [],
109
+ })
79
110
  }
80
111
 
81
112
  hooks = await select({
82
- message: 'Git hooks?',
113
+ message: t('init.prompt.gitHooks'),
83
114
  choices: [
84
115
  { name: 'husky', value: 'husky' },
85
116
  { name: 'lefthook', value: 'lefthook' },
@@ -88,7 +119,7 @@ cmd.action(async () => {
88
119
  })
89
120
 
90
121
  ci = await select({
91
- message: 'CI system?',
122
+ message: t('init.prompt.ci'),
92
123
  choices: [
93
124
  { name: 'GitHub Actions', value: 'github-actions' },
94
125
  { name: 'GitLab CI', value: 'gitlab-ci' },
@@ -97,7 +128,7 @@ cmd.action(async () => {
97
128
  })
98
129
 
99
130
  aiTools = await checkbox({
100
- message: 'Which AI assistants do you use?',
131
+ message: t('init.prompt.aiTools'),
101
132
  choices: [
102
133
  { name: 'Claude Code', value: 'claude' },
103
134
  { name: 'Gemini CLI', value: 'gemini' },
@@ -119,11 +150,11 @@ cmd.action(async () => {
119
150
  ci: 'none',
120
151
  }
121
152
  await generators.scaffold(cfg)
122
- console.log("\n✓ trackfw initialized — run 'trackfw status' to see your governance state.")
153
+ console.log(`\n${t('init.success')}`)
123
154
  return
124
155
  }
125
156
 
126
- const cfg = { projectName, projectType, frontend, backend, pkgManager, hooks, ci }
157
+ const cfg = { projectName, projectType, frontend, backend, backendFramework, pkgManager, hooks, ci }
127
158
  await generators.scaffold(cfg)
128
159
 
129
160
  for (const tool of (aiTools || [])) {
@@ -137,7 +168,7 @@ cmd.action(async () => {
137
168
  }
138
169
  }
139
170
 
140
- console.log("\n✓ trackfw initialized — run 'trackfw status' to see your governance state.")
171
+ console.log(`\n${t('init.success')}`)
141
172
  })
142
173
 
143
174
  module.exports = cmd
@@ -2,16 +2,17 @@
2
2
  const { Command } = require('commander')
3
3
  const fs = require('fs')
4
4
  const path = require('path')
5
+ const { t } = require('../i18n')
5
6
 
6
7
  const cmd = new Command('log')
7
- cmd.description('Show roadmap state transition history')
8
- cmd.option('--tail <n>', 'Number of recent transitions to show', '20')
8
+ cmd.description(t('log.description'))
9
+ cmd.option('--tail <n>', t('log.tail'), '20')
9
10
  cmd.action(async (opts) => {
10
11
  const tail = parseInt(opts.tail, 10)
11
12
  const logPath = path.join('docs', 'roadmaps', '.trackfw-log')
12
13
 
13
14
  if (!fs.existsSync(logPath)) {
14
- console.log('No transitions recorded yet.')
15
+ console.log(t('log.empty'))
15
16
  return
16
17
  }
17
18
 
@@ -22,7 +23,7 @@ cmd.action(async (opts) => {
22
23
  const start = Math.max(0, lines.length - tail)
23
24
  const visible = lines.slice(start)
24
25
 
25
- console.log('── trackfw log ─────────────────────────')
26
+ console.log(t('log.header'))
26
27
  visible.forEach(l => console.log(l))
27
28
  })
28
29
 
@@ -3,6 +3,7 @@ const { Command } = require('commander')
3
3
  const os = require('os')
4
4
  const path = require('path')
5
5
  const fs = require('fs')
6
+ const { t } = require('../i18n')
6
7
 
7
8
  function pluginsDir() {
8
9
  return path.join(os.homedir(), '.trackfw', 'plugins')
@@ -40,7 +41,7 @@ async function installPlugin(repo) {
40
41
  : `https://github.com/${base}/releases/download/${tag}/${assetName}`
41
42
 
42
43
  const res = await fetch(url)
43
- if (!res.ok) throw new Error(`download failed: HTTP ${res.status} for ${url}`)
44
+ if (!res.ok) throw new Error(t('errors.downloadFailed', { status: res.status, url }))
44
45
 
45
46
  const dir = pluginsDir()
46
47
  fs.mkdirSync(dir, { recursive: true })
@@ -49,32 +50,32 @@ async function installPlugin(repo) {
49
50
 
50
51
  function removePlugin(name) {
51
52
  const filePath = path.join(pluginsDir(), name)
52
- if (!fs.existsSync(filePath)) throw new Error(`plugin "${name}" not found`)
53
+ if (!fs.existsSync(filePath)) throw new Error(t('errors.pluginNotFound', { name }))
53
54
  fs.unlinkSync(filePath)
54
55
  }
55
56
 
56
57
  const cmd = new Command('plugins')
57
- cmd.description('Manage trackfw plugins')
58
+ cmd.description(t('plugins.description'))
58
59
 
59
60
  cmd.command('list')
60
- .description('List installed plugins')
61
+ .description(t('plugins.list.description'))
61
62
  .action(() => {
62
63
  const plugins = listPlugins()
63
64
  if (plugins.length === 0) {
64
- console.log('No plugins installed. Use `trackfw plugins add <user/repo>` to install one.')
65
+ console.log(t('plugins.list.empty'))
65
66
  return
66
67
  }
67
68
  plugins.forEach(p => console.log(p))
68
69
  })
69
70
 
70
71
  cmd.command('add <repo>')
71
- .description('Install a plugin from GitHub Releases (user/repo or user/repo@tag)')
72
+ .description(t('plugins.add.description'))
72
73
  .action(async (repo) => {
73
74
  try {
74
- console.log(`Installing plugin from ${repo}...`)
75
+ console.log(t('plugins.add.installing', { repo }))
75
76
  await installPlugin(repo)
76
77
  const name = repo.split('@')[0].split('/').pop()
77
- console.log(`Plugin "${name}" installed successfully.`)
78
+ console.log(t('plugins.add.success', { name }))
78
79
  } catch (err) {
79
80
  console.error(`Error: ${err.message}`)
80
81
  process.exit(1)
@@ -82,11 +83,11 @@ cmd.command('add <repo>')
82
83
  })
83
84
 
84
85
  cmd.command('remove <name>')
85
- .description('Remove an installed plugin')
86
+ .description(t('plugins.remove.description'))
86
87
  .action((name) => {
87
88
  try {
88
89
  removePlugin(name)
89
- console.log(`Plugin "${name}" removed.`)
90
+ console.log(t('plugins.remove.success', { name }))
90
91
  } catch (err) {
91
92
  console.error(`Error: ${err.message}`)
92
93
  process.exit(1)
@@ -1,12 +1,13 @@
1
1
  'use strict'
2
2
  const { Command } = require('commander')
3
3
  const { listREQs } = require('../generators/req')
4
+ const { t } = require('../i18n')
4
5
 
5
6
  const cmd = new Command('req')
6
- cmd.description('Manage Requirements')
7
+ cmd.description(t('req.description'))
7
8
 
8
9
  cmd.command('new <title>')
9
- .description('Create a new REQ')
10
+ .description(t('req.new.description'))
10
11
  .action(async (title) => {
11
12
  const { input, select } = require('@inquirer/prompts')
12
13
  const generators = require('../generators/req')
@@ -16,14 +17,14 @@ cmd.command('new <title>')
16
17
 
17
18
  if (process.stdin.isTTY) {
18
19
  // Form 1 — título + motivação
19
- content.title = await input({ message: 'Project requirement', default: title })
20
- content.motivation = await input({ message: 'Motivation (why is this needed?)', default: '' })
20
+ content.title = await input({ message: t('req.new.prompt.title'), default: title })
21
+ content.motivation = await input({ message: t('req.new.prompt.motivation'), default: '' })
21
22
 
22
23
  // Detectar domínios com base em título + motivação
23
24
  const probes = generators.detectDomains(content.title + ' ' + content.motivation)
24
25
 
25
26
  // Form 2 — critérios de aceite
26
- content.criteria = await input({ message: 'Acceptance Criteria (one per line)', default: '- [ ]\n- [ ]' })
27
+ content.criteria = await input({ message: t('req.new.prompt.criteria'), default: '- [ ]\n- [ ]' })
27
28
 
28
29
  // Perguntas dinâmicas por probe
29
30
  const generatedADRs = []
@@ -42,7 +43,7 @@ cmd.command('new <title>')
42
43
  const basename = await adrGenerators.newADRDraft(answer)
43
44
  if (basename) generatedADRs.push(basename)
44
45
  } catch (e) {
45
- console.warn(`warning: could not create ADR draft for ${answer}: ${e.message}`)
46
+ console.warn(t('req.new.adrWarning', { slug: answer, message: e.message }))
46
47
  }
47
48
  }
48
49
  }
@@ -54,14 +55,14 @@ cmd.command('new <title>')
54
55
  await generators.newREQ(content)
55
56
 
56
57
  if (content.dependsOnADRs.length > 0) {
57
- console.log('\nADR drafts created:')
58
+ console.log(`\n${t('req.new.adrDraftsCreated')}`)
58
59
  content.dependsOnADRs.forEach(adr => console.log(` -> ${adr}`))
59
- console.log('\nResolve these ADRs (set Status: Accepted) before creating a roadmap.')
60
+ console.log(`\n${t('req.new.resolveADRs')}`)
60
61
  }
61
62
  })
62
63
 
63
64
  cmd.command('list')
64
- .description('List all REQs in docs/req/')
65
+ .description(t('req.list.description'))
65
66
  .action(async () => {
66
67
  listREQs('docs/req')
67
68
  })
@@ -1,12 +1,13 @@
1
1
  'use strict'
2
2
  const { Command } = require('commander')
3
3
  const { listRoadmaps, showRoadmap, moveRoadmap, newRoadmap } = require('../generators/roadmap')
4
+ const { t } = require('../i18n')
4
5
 
5
6
  const cmd = new Command('roadmap')
6
- cmd.description('Manage Roadmaps')
7
+ cmd.description(t('roadmap.description'))
7
8
 
8
9
  cmd.command('new')
9
- .description('Create a new roadmap from a REQ')
10
+ .description(t('roadmap.new.description'))
10
11
  .option('-t, --title <title>', 'Roadmap title')
11
12
  .option('-r, --req <path>', 'Path to the linked REQ')
12
13
  .action(async (opts) => {
@@ -16,19 +17,19 @@ cmd.command('new')
16
17
  })
17
18
 
18
19
  cmd.command('list')
19
- .description('List all roadmaps grouped by state')
20
+ .description(t('roadmap.list.description'))
20
21
  .action(async () => {
21
22
  listRoadmaps()
22
23
  })
23
24
 
24
25
  cmd.command('show <name>')
25
- .description('Show a roadmap by name (partial match)')
26
+ .description(t('roadmap.show.description'))
26
27
  .action(async (name) => {
27
28
  showRoadmap(name)
28
29
  })
29
30
 
30
31
  cmd.command('move <name> <state>')
31
- .description('Move a roadmap between states (backlog|wip|blocked|done|abandoned)')
32
+ .description(t('roadmap.move.description'))
32
33
  .action(async (name, state) => {
33
34
  moveRoadmap(name, state)
34
35
  })
@@ -1,9 +1,10 @@
1
1
  'use strict'
2
2
  const { Command } = require('commander')
3
3
  const { getStatus } = require('../validator')
4
+ const { t } = require('../i18n')
4
5
 
5
6
  const cmd = new Command('status')
6
- cmd.description('Show project governance status')
7
+ cmd.description(t('status.description'))
7
8
  cmd.action(async () => {
8
9
  console.log(await getStatus())
9
10
  })
@@ -1,24 +1,25 @@
1
1
  'use strict'
2
2
  const { Command } = require('commander')
3
3
  const { validate } = require('../validator')
4
+ const { t } = require('../i18n')
4
5
 
5
6
  const cmd = new Command('validate')
6
- cmd.description('Validate governance rules')
7
+ cmd.description(t('validate.description'))
7
8
  cmd.action(async () => {
8
9
  const { violations, warnings } = await validate()
9
10
 
10
11
  if (violations.length === 0 && warnings.length === 0) {
11
- console.log('✓ No violations found.')
12
+ console.log(t('validate.ok'))
12
13
  return
13
14
  }
14
15
 
15
16
  if (violations.length > 0) {
16
- console.log(`\n✗ Violations (${violations.length}):`)
17
+ console.log(`\n${t('validate.violations', { count: violations.length })}`)
17
18
  violations.forEach(v => console.log(` • ${v}`))
18
19
  }
19
20
 
20
21
  if (warnings.length > 0) {
21
- console.log(`\n⚠ Warnings (${warnings.length}):`)
22
+ console.log(`\n${t('validate.warnings', { count: warnings.length })}`)
22
23
  warnings.forEach(w => console.log(` • ${w}`))
23
24
  }
24
25
 
@@ -29,6 +29,7 @@ async function scaffold(cfg) {
29
29
  generateCIWorkflow(cfg)
30
30
  generateGitHooks(cfg)
31
31
  generateClaudeMD(cfg)
32
+ if (cfg.backend === 'java') generatePomXml(cfg)
32
33
  generateClaudeCommands()
33
34
  }
34
35
 
@@ -43,6 +44,7 @@ function writeTrackfwConfig(cfg) {
43
44
 
44
45
  frontend: ${cfg.frontend || ''}
45
46
  backend: ${cfg.backend || ''}
47
+ backend_framework: ${cfg.backendFramework || ''}
46
48
  pkg_manager: ${cfg.pkgManager || ''}
47
49
  hooks: ${cfg.hooks || ''}
48
50
  ci: ${cfg.ci || ''}
@@ -193,6 +195,63 @@ function generateLefthookHook() {
193
195
  console.log(' ✓ lefthook.yml')
194
196
  }
195
197
 
198
+ // ---------------------------------------------------------------------------
199
+ // pom.xml (Java / Spring Boot)
200
+ // ---------------------------------------------------------------------------
201
+
202
+ function generatePomXml(cfg) {
203
+ const slug = cfg.projectName
204
+ ? cfg.projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '')
205
+ : 'my-app'
206
+ const name = cfg.projectName || 'My App'
207
+ const content = `<?xml version="1.0" encoding="UTF-8"?>
208
+ <project xmlns="http://maven.apache.org/POM/4.0.0"
209
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
210
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
211
+ <modelVersion>4.0.0</modelVersion>
212
+ <parent>
213
+ <groupId>org.springframework.boot</groupId>
214
+ <artifactId>spring-boot-starter-parent</artifactId>
215
+ <version>3.3.0</version>
216
+ <relativePath/>
217
+ </parent>
218
+ <groupId>com.example</groupId>
219
+ <artifactId>${slug}</artifactId>
220
+ <version>0.0.1-SNAPSHOT</version>
221
+ <name>${name}</name>
222
+ <description>${name} — generated by trackfw</description>
223
+ <properties>
224
+ <java.version>21</java.version>
225
+ </properties>
226
+ <dependencies>
227
+ <dependency>
228
+ <groupId>org.springframework.boot</groupId>
229
+ <artifactId>spring-boot-starter-web</artifactId>
230
+ </dependency>
231
+ <dependency>
232
+ <groupId>org.springframework.boot</groupId>
233
+ <artifactId>spring-boot-starter-actuator</artifactId>
234
+ </dependency>
235
+ <dependency>
236
+ <groupId>org.springframework.boot</groupId>
237
+ <artifactId>spring-boot-starter-test</artifactId>
238
+ <scope>test</scope>
239
+ </dependency>
240
+ </dependencies>
241
+ <build>
242
+ <plugins>
243
+ <plugin>
244
+ <groupId>org.springframework.boot</groupId>
245
+ <artifactId>spring-boot-maven-plugin</artifactId>
246
+ </plugin>
247
+ </plugins>
248
+ </build>
249
+ </project>
250
+ `
251
+ fs.writeFileSync('pom.xml', content, 'utf8')
252
+ console.log(' ✓ pom.xml')
253
+ }
254
+
196
255
  // ---------------------------------------------------------------------------
197
256
  // CLAUDE.md
198
257
  // ---------------------------------------------------------------------------
@@ -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
+ }
@@ -0,0 +1,122 @@
1
+ {
2
+ "init": {
3
+ "description": "Inicializa a governança trackfw no projeto atual",
4
+ "prompt": {
5
+ "projectName": "Nome do projeto?",
6
+ "projectType": "Tipo de projeto?",
7
+ "frontendStack": "Stack de frontend?",
8
+ "pkgManager": "Gerenciador de pacotes?",
9
+ "backendLang": "Linguagem de backend?",
10
+ "backendFramework": "Framework de backend?",
11
+ "gitHooks": "Git hooks?",
12
+ "ci": "Sistema de CI?",
13
+ "aiTools": "Quais assistentes de IA você usa?",
14
+ "projectType_fullstack": "Full-stack (frontend + backend)",
15
+ "projectType_frontend": "Somente frontend",
16
+ "projectType_backend": "Somente backend",
17
+ "projectType_governance": "Somente governança (sem stack de build)"
18
+ },
19
+ "success": "✓ trackfw inicializado — execute 'trackfw status' para ver o estado de governança."
20
+ },
21
+ "adr": {
22
+ "description": "Gerenciar Architecture Decision Records",
23
+ "new": {
24
+ "description": "Criar um novo Architecture Decision Record",
25
+ "prompt": {
26
+ "title": "Título do ADR?",
27
+ "status": "Status inicial?",
28
+ "context": "Contexto (o que motiva esta decisão)?",
29
+ "decision": "Decisão (o que foi decidido)?",
30
+ "consequences": "Consequências (positivas e negativas)?",
31
+ "alternatives": "Alternativas consideradas?"
32
+ },
33
+ "created": "✓ ADR criado: {{path}}"
34
+ },
35
+ "list": {
36
+ "description": "Listar todos os ADRs com status",
37
+ "empty": "Nenhum ADR encontrado em docs/adr/"
38
+ }
39
+ },
40
+ "req": {
41
+ "description": "Gerenciar Requisitos",
42
+ "new": {
43
+ "description": "Criar um novo requisito",
44
+ "prompt": {
45
+ "title": "Requisito do projeto",
46
+ "motivation": "Motivação (por que isso é necessário)?",
47
+ "criteria": "Critérios de aceite (um por linha)",
48
+ "domainQuestion_authentication": "Como os usuários serão autenticados?",
49
+ "domainQuestion_ui": "Existe um framework de UI ou design system já escolhido?",
50
+ "domainQuestion_persistence": "Qual engine de banco de dados será usada?",
51
+ "domainQuestion_api": "Qual protocolo de API será usado?",
52
+ "domainQuestion_deploy": "Qual é o destino de deploy?",
53
+ "domainQuestion_events": "Qual message broker será usado?"
54
+ },
55
+ "detectedDomains": "Domínios detectados: {{domains}}",
56
+ "created": "✓ REQ criado: {{path}}",
57
+ "adrDraftsCreated": "ADR drafts criados:",
58
+ "resolveADRs": "Resolva estes ADRs (defina Status: Accepted) antes de criar um roadmap.",
59
+ "adrWarning": "aviso: não foi possível criar ADR draft para {{slug}}: {{message}}"
60
+ },
61
+ "list": {
62
+ "description": "Listar todos os REQs com status",
63
+ "empty": "Nenhum REQ encontrado em docs/req/"
64
+ }
65
+ },
66
+ "roadmap": {
67
+ "description": "Gerenciar Roadmaps",
68
+ "list": {
69
+ "description": "Listar todos os roadmaps agrupados por estado",
70
+ "empty": "Nenhum roadmap encontrado."
71
+ },
72
+ "show": {
73
+ "description": "Exibir roadmap pelo nome (correspondência parcial)",
74
+ "notFound": "Roadmap não encontrado: {{name}}"
75
+ },
76
+ "move": {
77
+ "description": "Mover um roadmap entre estados (backlog|wip|blocked|done|abandoned)",
78
+ "success": "✓ Movido {{name}} → {{state}}",
79
+ "notFound": "Roadmap não encontrado: {{name}}"
80
+ },
81
+ "new": {
82
+ "description": "Criar um novo roadmap a partir de uma REQ",
83
+ "created": "✓ Roadmap criado: {{path}}"
84
+ }
85
+ },
86
+ "validate": {
87
+ "description": "Validar regras de governança (use como gate de CI)",
88
+ "ok": "✓ Nenhuma violação encontrada.",
89
+ "violations": "✗ Violações ({{count}}):",
90
+ "warnings": "⚠ Avisos ({{count}}):"
91
+ },
92
+ "status": {
93
+ "description": "Exibir o estado atual de governança do projeto"
94
+ },
95
+ "log": {
96
+ "description": "Exibir histórico de transições de estado dos roadmaps",
97
+ "empty": "Nenhuma transição registrada ainda.",
98
+ "tail": "Número de transições recentes a exibir",
99
+ "header": "── trackfw log ─────────────────────────"
100
+ },
101
+ "plugins": {
102
+ "description": "Gerenciar plugins do trackfw",
103
+ "list": {
104
+ "description": "Listar plugins instalados",
105
+ "empty": "Nenhum plugin instalado. Use `trackfw plugins add <user/repo>` para instalar um."
106
+ },
107
+ "add": {
108
+ "description": "Instalar um plugin do GitHub Releases (user/repo ou user/repo@tag)",
109
+ "installing": "Instalando plugin de {{repo}}...",
110
+ "success": "Plugin \"{{name}}\" instalado com sucesso."
111
+ },
112
+ "remove": {
113
+ "description": "Remover um plugin instalado",
114
+ "success": "Plugin \"{{name}}\" removido."
115
+ }
116
+ },
117
+ "errors": {
118
+ "notFound": "Não encontrado: {{path}}",
119
+ "downloadFailed": "falha no download: HTTP {{status}} para {{url}}",
120
+ "pluginNotFound": "plugin \"{{name}}\" não encontrado"
121
+ }
122
+ }