@longph2102/v-flow 1.5.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 (247) hide show
  1. package/AGENTS.md +265 -0
  2. package/CHANGELOG.md +318 -0
  3. package/LICENSE +21 -0
  4. package/README.md +326 -0
  5. package/agents/ba-agent.md +437 -0
  6. package/agents/ba-critic-agent.md +156 -0
  7. package/agents/ba-to-ptyc-agent.md +112 -0
  8. package/agents/bugfix-analyst-agent.md +221 -0
  9. package/agents/constitute-agent.md +155 -0
  10. package/agents/help-agent.md +168 -0
  11. package/agents/implement-agent.md +220 -0
  12. package/agents/import-ba-docs-agent.md +164 -0
  13. package/agents/master-check-agent.md +228 -0
  14. package/agents/metrics-agent.md +180 -0
  15. package/agents/operations-agent.md +123 -0
  16. package/agents/plan-agent.md +218 -0
  17. package/agents/prototype-agent.md +191 -0
  18. package/agents/retrospective-agent.md +196 -0
  19. package/agents/review-agent.md +210 -0
  20. package/agents/sprint-agent.md +191 -0
  21. package/agents/status-agent.md +186 -0
  22. package/agents/sync-agent.md +201 -0
  23. package/agents/test-agent.md +166 -0
  24. package/agents/understand-agent.md +339 -0
  25. package/cli/commands/check.js +96 -0
  26. package/cli/commands/dev-quiz.js +107 -0
  27. package/cli/commands/doctor.js +348 -0
  28. package/cli/commands/feature.js +259 -0
  29. package/cli/commands/hooks.js +163 -0
  30. package/cli/commands/init.js +189 -0
  31. package/cli/commands/log.js +199 -0
  32. package/cli/commands/plugin.js +230 -0
  33. package/cli/commands/score-card.js +203 -0
  34. package/cli/commands/status.js +269 -0
  35. package/cli/commands/sync.js +59 -0
  36. package/cli/commands/upgrade.js +150 -0
  37. package/cli/commands/validate.js +1259 -0
  38. package/cli/commands/watch.js +151 -0
  39. package/cli/index.js +46 -0
  40. package/cli/lib/ac-test-gate.js +89 -0
  41. package/cli/lib/activity-log.js +209 -0
  42. package/cli/lib/cli-error.js +183 -0
  43. package/cli/lib/constitution-lint.js +561 -0
  44. package/cli/lib/dev-quiz-grade.js +127 -0
  45. package/cli/lib/governance.js +78 -0
  46. package/cli/lib/hook-targets.js +167 -0
  47. package/cli/lib/i18n.js +375 -0
  48. package/cli/lib/knowledge-oracle.js +379 -0
  49. package/cli/lib/logger.js +203 -0
  50. package/cli/lib/module-card-lint.js +304 -0
  51. package/cli/lib/module-card-score.js +223 -0
  52. package/cli/lib/plugins.js +481 -0
  53. package/cli/lib/scanner.js +692 -0
  54. package/cli/lib/sync-core.js +232 -0
  55. package/cli/lib/sync-targets.js +84 -0
  56. package/cli/lib/templates.js +268 -0
  57. package/cli/lib/yaml-parser.js +203 -0
  58. package/commands/v.ba-critic.md +101 -0
  59. package/commands/v.ba-to-ptyc.md +71 -0
  60. package/commands/v.bugfix.md +86 -0
  61. package/commands/v.check.md +131 -0
  62. package/commands/v.constitute.md +87 -0
  63. package/commands/v.constitution.md +84 -0
  64. package/commands/v.fork.md +127 -0
  65. package/commands/v.help.md +73 -0
  66. package/commands/v.hotfix.md +200 -0
  67. package/commands/v.implement.md +92 -0
  68. package/commands/v.import-ba-docs.md +222 -0
  69. package/commands/v.metrics.md +74 -0
  70. package/commands/v.operations.md +70 -0
  71. package/commands/v.plan.md +78 -0
  72. package/commands/v.prototype.md +121 -0
  73. package/commands/v.quickfix.md +169 -0
  74. package/commands/v.retrospective.md +80 -0
  75. package/commands/v.review.md +78 -0
  76. package/commands/v.rewind.md +127 -0
  77. package/commands/v.specify.md +118 -0
  78. package/commands/v.sprint.md +75 -0
  79. package/commands/v.status.md +62 -0
  80. package/commands/v.sync.md +81 -0
  81. package/commands/v.test.md +67 -0
  82. package/commands/v.understand.md +112 -0
  83. package/package.json +65 -0
  84. package/skills/_shared/constitution-reader/SKILL.md +109 -0
  85. package/skills/_shared/constitution-reader/config.json +52 -0
  86. package/skills/_shared/constitution-reader/examples/good/b1-phase-output.md +48 -0
  87. package/skills/_shared/constitution-reader/gotchas.md +46 -0
  88. package/skills/_shared/context-reader/SKILL.md +111 -0
  89. package/skills/_shared/context-reader/config.json +54 -0
  90. package/skills/_shared/context-reader/examples/good/legacy-nodejs-output.md +35 -0
  91. package/skills/_shared/context-reader/gotchas.md +49 -0
  92. package/skills/_shared/ears-notation/SKILL.md +63 -0
  93. package/skills/_shared/ears-notation/config.json +55 -0
  94. package/skills/_shared/ears-notation/examples/good/plan-test-interpretation.md +29 -0
  95. package/skills/_shared/ears-notation/gotchas.md +43 -0
  96. package/skills/check/cross-validator/SKILL.md +206 -0
  97. package/skills/check/cross-validator/config.json +33 -0
  98. package/skills/check/cross-validator/examples/good/validation-report-pass-with-concerns.md +105 -0
  99. package/skills/check/cross-validator/gotchas.md +43 -0
  100. package/skills/implement/constitution-enforcer/SKILL.md +134 -0
  101. package/skills/implement/constitution-enforcer/config.json +16 -0
  102. package/skills/implement/constitution-enforcer/examples/bad/vague-report.md +42 -0
  103. package/skills/implement/constitution-enforcer/examples/good/compliance-report.md +57 -0
  104. package/skills/implement/constitution-enforcer/gotchas.md +26 -0
  105. package/skills/implement/constitution-enforcer/scripts/check-constitution.sh +88 -0
  106. package/skills/implement/no-go-zone-guard/SKILL.md +173 -0
  107. package/skills/implement/no-go-zone-guard/config.json +28 -0
  108. package/skills/implement/no-go-zone-guard/examples/good/adapter-workaround.md +46 -0
  109. package/skills/implement/no-go-zone-guard/gotchas.md +27 -0
  110. package/skills/implement/no-go-zone-guard/scripts/check-nogo-zones.sh +148 -0
  111. package/skills/implement/no-go-zone-guard/scripts/nogo-precommit.sh +96 -0
  112. package/skills/implement/tdd-driver/SKILL.md +159 -0
  113. package/skills/implement/tdd-driver/config.json +33 -0
  114. package/skills/implement/tdd-driver/examples/good/tdd-cycle-product-repo.md +81 -0
  115. package/skills/implement/tdd-driver/gotchas.md +34 -0
  116. package/skills/metrics/metrics-collector/SKILL.md +133 -0
  117. package/skills/metrics/metrics-collector/config.json +16 -0
  118. package/skills/metrics/metrics-collector/examples/bad/incomplete-report.md +48 -0
  119. package/skills/metrics/metrics-collector/examples/good/full-metrics-report.md +101 -0
  120. package/skills/metrics/metrics-collector/gotchas.md +26 -0
  121. package/skills/operations/incident-runbook/SKILL.md +167 -0
  122. package/skills/operations/incident-runbook/config.json +21 -0
  123. package/skills/operations/incident-runbook/examples/bad/vague-incident-report.md +48 -0
  124. package/skills/operations/incident-runbook/examples/good/p1-hotfix-response.md +119 -0
  125. package/skills/operations/incident-runbook/gotchas.md +26 -0
  126. package/skills/plan/architecture-designer/SKILL.md +228 -0
  127. package/skills/plan/architecture-designer/config.json +32 -0
  128. package/skills/plan/architecture-designer/examples/bad/vague-plan.md +62 -0
  129. package/skills/plan/architecture-designer/examples/good/expand-contract-migration.md +56 -0
  130. package/skills/plan/architecture-designer/examples/good/plan-structure.md +58 -0
  131. package/skills/plan/architecture-designer/gotchas.md +45 -0
  132. package/skills/plan/task-breakdown/SKILL.md +208 -0
  133. package/skills/plan/task-breakdown/config.json +26 -0
  134. package/skills/plan/task-breakdown/examples/bad/vague-tasks.md +77 -0
  135. package/skills/plan/task-breakdown/examples/good/spike-clarify-tasks.md +66 -0
  136. package/skills/plan/task-breakdown/examples/good/tasks-login-feature.md +111 -0
  137. package/skills/plan/task-breakdown/gotchas.md +39 -0
  138. package/skills/prototype/LOGIC.md +240 -0
  139. package/skills/prototype/SKILL.md +185 -0
  140. package/skills/prototype/UI.md +407 -0
  141. package/skills/prototype/config.json +104 -0
  142. package/skills/prototype/examples/bad/prototype-notes.md +68 -0
  143. package/skills/prototype/examples/good/prototype-notes-ui.md +109 -0
  144. package/skills/prototype/examples/good/prototype-notes.md +67 -0
  145. package/skills/prototype/gotchas.md +128 -0
  146. package/skills/prototype/scripts/check-flow-state.ps1 +112 -0
  147. package/skills/prototype/scripts/check-flow-state.sh +104 -0
  148. package/skills/prototype/scripts/check-prototype-cleanup.ps1 +124 -0
  149. package/skills/prototype/scripts/check-prototype-cleanup.sh +109 -0
  150. package/skills/prototype/scripts/check-prototype-notes.ps1 +107 -0
  151. package/skills/prototype/scripts/check-prototype-notes.sh +102 -0
  152. package/skills/review/adversarial-reviewer/SKILL.md +137 -0
  153. package/skills/review/adversarial-reviewer/config.json +32 -0
  154. package/skills/review/adversarial-reviewer/examples/good/review-report-template.md +56 -0
  155. package/skills/review/adversarial-reviewer/gotchas.md +46 -0
  156. package/skills/review/adversarial-reviewer/scripts/quick-security-scan.sh +52 -0
  157. package/skills/specify/ba-bpmn-doc-gen/SKILL.md +108 -0
  158. package/skills/specify/ba-bpmn-doc-gen/reference/reference-bpmn-generation.md +528 -0
  159. package/skills/specify/ba-bpmn-doc-gen/reference/reference-drawio-flowchart.md +466 -0
  160. package/skills/specify/ba-critic/SKILL.md +172 -0
  161. package/skills/specify/ba-critic/config.json +32 -0
  162. package/skills/specify/ba-critic/examples/good/critic-report-round1.md +51 -0
  163. package/skills/specify/ba-critic/gotchas.md +40 -0
  164. package/skills/specify/ba-critic/scripts/check-spec-quality.sh +72 -0
  165. package/skills/specify/ba-doc-generator/SKILL.md +102 -0
  166. package/skills/specify/ba-doc-generator/references/template-clevel.md +84 -0
  167. package/skills/specify/ba-doc-generator/references/template-compliance.md +83 -0
  168. package/skills/specify/ba-doc-generator/references/template-dev.md +138 -0
  169. package/skills/specify/ba-doc-generator/references/template-partner.md +167 -0
  170. package/skills/specify/ba-doc-generator/references/template-pm.md +92 -0
  171. package/skills/specify/ba-doc-generator/references/template-review.md +114 -0
  172. package/skills/specify/ba-doc-generator/references/template-tester.md +108 -0
  173. package/skills/specify/ba-doc-generator/references/template-user.md +98 -0
  174. package/skills/specify/bugfix-analyst/SKILL.md +296 -0
  175. package/skills/specify/bugfix-analyst/config.json +41 -0
  176. package/skills/specify/bugfix-analyst/examples/bad/common-mistakes.md +71 -0
  177. package/skills/specify/bugfix-analyst/examples/good/email-validation-bugfix.md +53 -0
  178. package/skills/specify/bugfix-analyst/gotchas.md +51 -0
  179. package/skills/specify/ears-writer/SKILL.md +129 -0
  180. package/skills/specify/ears-writer/config.json +20 -0
  181. package/skills/specify/ears-writer/examples/bad/common-mistakes.md +17 -0
  182. package/skills/specify/ears-writer/examples/good/login-requirements.md +41 -0
  183. package/skills/specify/ears-writer/gotchas.md +43 -0
  184. package/skills/specify/ears-writer/scripts/check-ears-compliance.sh +51 -0
  185. package/skills/test/test-case-generator/SKILL.md +161 -0
  186. package/skills/test/test-case-generator/config.json +33 -0
  187. package/skills/test/test-case-generator/examples/good/test-cases-login.md +104 -0
  188. package/skills/test/test-case-generator/gotchas.md +43 -0
  189. package/skills/understand/ba-docs-scanner/SKILL.md +239 -0
  190. package/skills/understand/ba-docs-scanner/config.json +47 -0
  191. package/skills/understand/ba-docs-scanner/examples/good/work-order-br-extract.md +28 -0
  192. package/skills/understand/ba-docs-scanner/gotchas.md +44 -0
  193. package/skills/understand/ba-docs-scanner/merge-rules.md +47 -0
  194. package/skills/understand/codebase-scanner/SKILL.md +260 -0
  195. package/skills/understand/codebase-scanner/config.json +56 -0
  196. package/skills/understand/codebase-scanner/examples/good/menu-module-output.md +44 -0
  197. package/skills/understand/codebase-scanner/gotchas.md +42 -0
  198. package/skills/understand/codebase-scanner/scripts/scan-project-structure.sh +64 -0
  199. package/templates/DESIGN.md +456 -0
  200. package/templates/agent-command-template.yaml +240 -0
  201. package/templates/agent-config-template.md +170 -0
  202. package/templates/agent-definition-template.md +145 -0
  203. package/templates/agent-metrics-template.md +150 -0
  204. package/templates/api-contract-template.md +72 -0
  205. package/templates/bugfix-report-template.md +195 -0
  206. package/templates/bugfix-spec-template.md +134 -0
  207. package/templates/code-review-report-template.md +119 -0
  208. package/templates/constitution-template.md +234 -0
  209. package/templates/context-template.md +94 -0
  210. package/templates/data-model-template.md +95 -0
  211. package/templates/decision-log-template.md +92 -0
  212. package/templates/flow-state-template.yaml +208 -0
  213. package/templates/github/workflows/v-flow-validate.yml +30 -0
  214. package/templates/knowledge/adr-template.md +70 -0
  215. package/templates/knowledge/api-contract-template.md +140 -0
  216. package/templates/knowledge/domain-glossary.md +29 -0
  217. package/templates/knowledge/golden-tests-readme.md +115 -0
  218. package/templates/knowledge/lessons-learned.md +41 -0
  219. package/templates/knowledge/patterns.md +103 -0
  220. package/templates/module-card/SKILL.md +85 -0
  221. package/templates/module-card/api-specs.md +96 -0
  222. package/templates/module-card/business-quiz.md +119 -0
  223. package/templates/module-card/cross-service.md +125 -0
  224. package/templates/module-card/db.md +85 -0
  225. package/templates/module-card/dev-quiz.md +62 -0
  226. package/templates/module-card/permissions.md +83 -0
  227. package/templates/module-card/state-diagram.md +64 -0
  228. package/templates/module-card/tech-context.md +90 -0
  229. package/templates/module-card/ui-flows.md +91 -0
  230. package/templates/module-card/use-cases.md +142 -0
  231. package/templates/module-template.yaml +161 -0
  232. package/templates/operations-report-template.md +108 -0
  233. package/templates/plan-template.md +308 -0
  234. package/templates/prototype-notes-template.md +116 -0
  235. package/templates/ptyc/PTYC.template.docx +0 -0
  236. package/templates/ptyc/ptyc.meta.example.yaml +44 -0
  237. package/templates/retrospective-report-template.md +136 -0
  238. package/templates/security-review-template.md +84 -0
  239. package/templates/session-template.md +167 -0
  240. package/templates/spec-review-log-template.md +75 -0
  241. package/templates/spec-template.md +229 -0
  242. package/templates/sprint-status-template.md +101 -0
  243. package/templates/tasks-template.md +275 -0
  244. package/templates/test-cases-template.md +124 -0
  245. package/templates/ux-checklist-template.md +79 -0
  246. package/templates/validation-report-template.md +125 -0
  247. package/templates/vflow-config-template.yaml +22 -0
@@ -0,0 +1,259 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+ const chalk = require('chalk');
4
+ const { isInitialized, getVFlowDir, copyTemplate, listFeatures } = require('../lib/templates');
5
+ const { withErrorHandler, requireInit } = require('../lib/cli-error');
6
+ const { readFlowState } = require('../lib/yaml-parser');
7
+
8
+ module.exports = function (program) {
9
+ const feature = program
10
+ .command('feature')
11
+ .description('Manage V Flow features');
12
+
13
+ // ── v-flow feature create <name> ──────────────────────────────────────
14
+ feature
15
+ .command('create')
16
+ .description('Create a new feature with scaffolded artifacts')
17
+ .argument('<name>', 'Feature name (e.g. "user-login" or "001-user-login")')
18
+ .option('--type <type>', 'Project type: greenfield or legacy', 'greenfield')
19
+ .option('--no-session', 'Skip creating _session.md')
20
+ .action(withErrorHandler(async (name, options) => {
21
+ const projectRoot = process.cwd();
22
+ requireInit(projectRoot, isInitialized);
23
+
24
+ const mfDir = getVFlowDir(projectRoot);
25
+ const featuresDir = path.join(mfDir, 'features');
26
+
27
+ // Auto-prefix with next number if name doesn't start with digits
28
+ let featureDirName = name;
29
+ if (!/^\d{3}/.test(name)) {
30
+ const existing = await listFeatures(projectRoot);
31
+ const nextNum = existing.length > 0
32
+ ? Math.max(...existing.map(f => parseInt(f.split('-')[0]) || 0)) + 1
33
+ : 1;
34
+ featureDirName = `${String(nextNum).padStart(3, '0')}-${name}`;
35
+ }
36
+
37
+ const featureDir = path.join(featuresDir, featureDirName);
38
+
39
+ // Check if feature already exists
40
+ if (fs.existsSync(featureDir)) {
41
+ console.log(chalk.yellow(`\n⚠️ Feature "${featureDirName}" already exists.`));
42
+ console.log(chalk.dim(` Path: ${featureDir}\n`));
43
+ return;
44
+ }
45
+
46
+ console.log(chalk.cyan.bold(`\n🆕 Creating feature: ${featureDirName}\n`));
47
+
48
+ // Create feature directory
49
+ await fs.ensureDir(featureDir);
50
+ console.log(chalk.green(` ✅ Directory created: .v-flow/features/${featureDirName}/`));
51
+
52
+ const today = new Date().toISOString().split('T')[0];
53
+ const featureDisplayName = featureDirName.replace(/^\d+-/, '').replace(/-/g, ' ');
54
+ const featureId = featureDirName.split('-')[0];
55
+
56
+ // 1. Create spec.md from template
57
+ const specDest = path.join(featureDir, 'spec.md');
58
+ try {
59
+ await copyTemplate('spec-template.md', specDest, {
60
+ 'TÊN FEATURE': featureDisplayName,
61
+ 'YYYY-MM-DD': today,
62
+ });
63
+ console.log(chalk.green(' ✅ spec.md (specification template)'));
64
+ } catch {
65
+ // Create minimal spec if template not found
66
+ await fs.writeFile(specDest, `# Specification — ${featureDisplayName}\n\n> 📌 **Single Source of Truth** — Spec thắng khi có conflict với plan/tasks/tests.\n> Feature: ${featureDisplayName}\n\n## 1. Tổng quan\n\n*(BA Agent sẽ điền khi chạy /v.specify)*\n\n## 7. Status\n\n| Thông tin | Giá trị |\n|-----------|--------|\n| **Spec Version** | v1.0 |\n| **Trạng thái** | Draft |\n| **Ngày tạo** | ${today} |\n`, 'utf8');
67
+ console.log(chalk.green(' ✅ spec.md (minimal template)'));
68
+ }
69
+
70
+ // 2. Create .flow-state.yaml
71
+ const flowStateDest = path.join(featureDir, '.flow-state.yaml');
72
+ const flowStateContent = `# V Flow State — ${featureDirName}
73
+ # Tạo tự động bởi v-flow feature create
74
+ # ⚠️ KHÔNG sửa tay — dùng /v.status để xem
75
+
76
+ # ── IDENTITY ──
77
+ feature_id: "${featureId}"
78
+ feature_name: "${featureDirName}"
79
+ created_at: "${today}"
80
+ last_updated: "${today}"
81
+ project_type: "${options.type}"
82
+ schema_version: "2.0" # khớp SCHEMA_VERSION trong cli/commands/validate.js — feature mới phải theo schema hiện hành
83
+ pipeline_type: "full" # full | quickfix | hotfix — drive "lite" profile của validator
84
+
85
+ # ── PIPELINE STATE ──
86
+ current_phase: "S.1"
87
+ phase_status: "pending"
88
+
89
+ phase_history:
90
+ - phase: "S.1"
91
+ entered_at: "${today}"
92
+ exited_at: null
93
+ reason: "feature created via CLI"
94
+
95
+ # ── ARTIFACTS ──
96
+ artifacts:
97
+ spec:
98
+ exists: true
99
+ version: "v1.0"
100
+ status: "draft"
101
+ human_approved: false
102
+ plan:
103
+ exists: false
104
+ synced_to_spec: null
105
+ human_approved: false
106
+ tasks:
107
+ exists: false
108
+ synced_to_spec: null
109
+ total: 0
110
+ completed: 0
111
+ test_cases:
112
+ exists: false
113
+ synced_to_spec: null
114
+ total: 0
115
+ human_approved: false
116
+ validation_report:
117
+ exists: false
118
+ verdict: null
119
+ concerns_count: 0
120
+ code_review:
121
+ exists: false
122
+ verdict: null
123
+
124
+ # ── ROUTING ──
125
+ routing:
126
+ after_master_check:
127
+ last_verdict: null
128
+ route_decision: null
129
+ after_review:
130
+ last_verdict: null
131
+ route_decision: null
132
+
133
+ # ── HUMAN CHECKPOINTS ──
134
+ human_checkpoints:
135
+ spec_approved: false
136
+ plan_approved: false
137
+ master_check_approved: false
138
+ implementation_approved: false
139
+ review_approved: false
140
+
141
+ awaiting_checkpoint: "spec_approved"
142
+
143
+ # ── CONCERNS ──
144
+ active_concerns: []
145
+
146
+ # ── BLOCKED ──
147
+ blocked: false
148
+ blocked_reason: null
149
+
150
+ # ── METADATA ──
151
+ spec_version_latest: "v1.0"
152
+ session_file: ".v-flow/features/${featureDirName}/_session.md"
153
+ notes: ""
154
+ `;
155
+ await fs.writeFile(flowStateDest, flowStateContent, 'utf8');
156
+ console.log(chalk.green(' ✅ .flow-state.yaml (pipeline state)'));
157
+
158
+ // 3. Create _session.md
159
+ if (options.session !== false) {
160
+ const sessionDest = path.join(featureDir, '_session.md');
161
+ try {
162
+ await copyTemplate('session-template.md', sessionDest);
163
+ // Replace placeholders
164
+ let content = await fs.readFile(sessionDest, 'utf8');
165
+ content = content
166
+ .replace(/\[Feature Name\]/g, featureDisplayName)
167
+ .replace(/NNN-feature-name/g, featureDirName)
168
+ .replace(/NNN/g, featureId);
169
+ await fs.writeFile(sessionDest, content, 'utf8');
170
+ console.log(chalk.green(' ✅ _session.md (working memory)'));
171
+ } catch {
172
+ await fs.writeFile(sessionDest, `# Session Memory — ${featureDisplayName}\n\n> Working memory cho feature này.\n\n## 📋 Tóm Tắt Nhanh\n\n\`\`\`\nPhase hiện tại : S.1\nBắt đầu lúc : ${today}\nSpec version : v1.0\n\`\`\`\n\n## 🧠 Key Decisions\n\n*(Chưa có)*\n\n## ❓ Open Questions\n\n*(Chưa có)*\n\n## ⚡ Execution Trace\n\n*(Agent sẽ append khi hoàn thành mỗi phase)*\n`, 'utf8');
173
+ console.log(chalk.green(' ✅ _session.md (minimal template)'));
174
+ }
175
+ }
176
+
177
+ // Summary
178
+ console.log(chalk.cyan.bold(`\n🎉 Feature "${featureDirName}" created!\n`));
179
+ console.log(chalk.white('Files created:'));
180
+ console.log(chalk.dim(` 📂 .v-flow/features/${featureDirName}/`));
181
+ console.log(chalk.dim(` ├── spec.md ← Write spec here (or run /v.specify)`));
182
+ console.log(chalk.dim(` ├── .flow-state.yaml ← Pipeline state (auto-managed)`));
183
+ if (options.session !== false) {
184
+ console.log(chalk.dim(` └── _session.md ← Working memory`));
185
+ }
186
+ console.log('');
187
+ console.log(chalk.white('Next steps:'));
188
+ console.log(chalk.yellow(` 1. Run /v.specify to generate spec from requirements`));
189
+ console.log(chalk.dim(` 2. Or edit spec.md manually, then run /v.plan`));
190
+ console.log('');
191
+ }));
192
+
193
+ // ── v-flow feature list ───────────────────────────────────────────────
194
+ feature
195
+ .command('list')
196
+ .alias('ls')
197
+ .description('List all features and their current phase')
198
+ .action(withErrorHandler(async () => {
199
+ const projectRoot = process.cwd();
200
+ requireInit(projectRoot, isInitialized);
201
+
202
+ const features = await listFeatures(projectRoot);
203
+
204
+ if (features.length === 0) {
205
+ console.log(chalk.yellow('\nNo features found.'));
206
+ console.log(chalk.dim('Create one: v-flow feature create <name>\n'));
207
+ return;
208
+ }
209
+
210
+ console.log(chalk.cyan.bold(`\n📋 Features (${features.length})\n`));
211
+
212
+ const mfDir = getVFlowDir(projectRoot);
213
+
214
+ for (const feature of features) {
215
+ const featureDir = path.join(mfDir, 'features', feature);
216
+ const flowStatePath = path.join(featureDir, '.flow-state.yaml');
217
+
218
+ let phase = '?';
219
+ let status = '';
220
+
221
+ if (fs.existsSync(flowStatePath)) {
222
+ const flowState = await readFlowState(featureDir);
223
+ if (flowState) {
224
+ if (flowState.currentPhase) phase = flowState.currentPhase;
225
+ if (flowState.phaseStatus) status = flowState.phaseStatus;
226
+ }
227
+ } else {
228
+ // Infer from existing files
229
+ const hasSpec = fs.existsSync(path.join(featureDir, 'spec.md'));
230
+ const hasPlan = fs.existsSync(path.join(featureDir, 'plan.md'));
231
+ const hasTests = fs.existsSync(path.join(featureDir, 'test-cases.md'));
232
+ const hasValidation = fs.existsSync(path.join(featureDir, 'validation-report.md'));
233
+ if (!hasSpec) phase = 'S.1';
234
+ else if (!hasPlan) phase = 'B.0a';
235
+ else if (!hasTests) phase = 'B.0b';
236
+ else if (!hasValidation) phase = 'R.0';
237
+ else phase = 'B.1';
238
+ }
239
+
240
+ const statusIcon = {
241
+ 'pending': '⏳',
242
+ 'in_progress': '🔄',
243
+ 'awaiting_human': '👤',
244
+ 'done': '✅',
245
+ 'blocked': '🚫',
246
+ }[status] || '📂';
247
+
248
+ const phaseColor = phase.startsWith('S') ? chalk.blue
249
+ : phase.startsWith('B') ? chalk.yellow
250
+ : phase.startsWith('R') ? chalk.magenta
251
+ : phase.startsWith('M') ? chalk.green
252
+ : chalk.cyan;
253
+
254
+ console.log(` ${statusIcon} ${chalk.white.bold(feature)} ${phaseColor(`[${phase}]`)} ${chalk.dim(status)}`);
255
+ }
256
+
257
+ console.log('');
258
+ }));
259
+ };
@@ -0,0 +1,163 @@
1
+ /**
2
+ * v-flow hooks — install / inspect / remove activity-logging lifecycle hooks
3
+ * for the supported AI coding tools.
4
+ *
5
+ * Subcommands:
6
+ * v-flow hooks install [--target <key|all>] — write `v-flow log --hook` hooks
7
+ * v-flow hooks status — show which tools have it installed
8
+ * v-flow hooks uninstall [--target <key|all>] — remove v-flow's hooks (keep user's)
9
+ *
10
+ * Each tool's hook calls `v-flow log --hook <provider>`, which logs every agent
11
+ * tool-use / session boundary into .v-flow/logs/. Configs are merged, never
12
+ * clobbered, and the operation is idempotent.
13
+ */
14
+
15
+ const path = require('path');
16
+ const fs = require('fs-extra');
17
+ const chalk = require('chalk');
18
+ const { withErrorHandler, ValidationError, printOk, printWarn, printError, printDim } = require('../lib/cli-error');
19
+ const { isInitialized } = require('../lib/templates');
20
+ const { PROVIDERS, getProvider, mergeConfig, removeConfig, isInstalled } = require('../lib/hook-targets');
21
+
22
+ module.exports = function (program) {
23
+ const hooksCmd = program
24
+ .command('hooks')
25
+ .description('🪝 Activity-logging hooks for AI tools (claude, cursor, copilot, codex, antigravity)');
26
+
27
+ // ── v-flow hooks install ──
28
+ hooksCmd
29
+ .command('install')
30
+ .description('Install v-flow logging hooks into AI tool configs')
31
+ .option('--target <key>', 'Only this tool (claude|cursor|copilot|codex|antigravity); default all')
32
+ .option('--abs', 'Use an absolute node + CLI path instead of `v-flow` (avoids PATH issues in GUI IDEs)')
33
+ .action(
34
+ withErrorHandler(async (options) => {
35
+ const projectRoot = process.cwd();
36
+ const targets = resolveTargets(options.target);
37
+ const bin = options.abs
38
+ ? `"${process.execPath}" "${path.resolve(__dirname, '..', 'index.js')}"`
39
+ : 'v-flow';
40
+
41
+ console.log(chalk.cyan.bold('\n🪝 Installing activity-logging hooks\n'));
42
+
43
+ let installed = 0;
44
+ for (const provider of targets) {
45
+ const filePath = path.join(projectRoot, ...provider.file);
46
+ const rel = path.relative(projectRoot, filePath) || filePath;
47
+
48
+ const existing = await readJsonSafe(filePath);
49
+ if (existing === PARSE_ERROR) {
50
+ printError(`${provider.label}: ${rel} is not valid JSON — skipped (won't clobber)`);
51
+ continue;
52
+ }
53
+
54
+ const merged = mergeConfig(provider, existing || {}, { bin });
55
+ await fs.ensureDir(path.dirname(filePath));
56
+ await fs.writeJson(filePath, merged, { spaces: 2 });
57
+ printOk(`${provider.label} → ${rel}`);
58
+ installed++;
59
+ }
60
+
61
+ console.log('');
62
+ printDim(`Installed for ${installed}/${targets.length} tool(s). Hooks call: ${bin} log --hook <provider>`);
63
+ if (options.abs) {
64
+ printWarn('Absolute paths are machine-specific — re-run `v-flow hooks install --abs` on each machine; do not commit these configs.');
65
+ } else {
66
+ printDim('Tip: `v-flow` must be on PATH for the hook to run. GUI IDEs may not inherit your shell PATH — use --abs if hooks do not fire.');
67
+ }
68
+ if (!isInitialized(projectRoot)) {
69
+ printWarn('No .v-flow/ here yet — hook entries will be logged only after `v-flow init` (or set VFLOW_LOG_DIR).');
70
+ }
71
+ console.log('');
72
+ })
73
+ );
74
+
75
+ // ── v-flow hooks status ──
76
+ hooksCmd
77
+ .command('status')
78
+ .description('Show which AI tools have v-flow logging hooks installed')
79
+ .action(
80
+ withErrorHandler(async () => {
81
+ const projectRoot = process.cwd();
82
+ console.log(chalk.cyan.bold('\n🪝 Hook status\n'));
83
+
84
+ for (const provider of PROVIDERS) {
85
+ const filePath = path.join(projectRoot, ...provider.file);
86
+ const rel = path.relative(projectRoot, filePath) || filePath;
87
+ const existing = await readJsonSafe(filePath);
88
+
89
+ let mark;
90
+ if (existing === PARSE_ERROR) {
91
+ mark = chalk.red('invalid JSON');
92
+ } else if (existing && isInstalled(provider, existing)) {
93
+ mark = chalk.green('✅ installed');
94
+ } else if (existing) {
95
+ mark = chalk.yellow('⬜ config exists, no v-flow hook');
96
+ } else {
97
+ mark = chalk.dim('— not configured');
98
+ }
99
+ console.log(` ${chalk.white(provider.label.padEnd(16))} ${mark} ${chalk.dim(rel)}`);
100
+ }
101
+ console.log('');
102
+ })
103
+ );
104
+
105
+ // ── v-flow hooks uninstall ──
106
+ hooksCmd
107
+ .command('uninstall')
108
+ .description("Remove v-flow's logging hooks (leaves your own hooks intact)")
109
+ .option('--target <key>', 'Only this tool (claude|cursor|copilot|codex|antigravity); default all')
110
+ .action(
111
+ withErrorHandler(async (options) => {
112
+ const projectRoot = process.cwd();
113
+ const targets = resolveTargets(options.target);
114
+
115
+ console.log(chalk.cyan.bold('\n🪝 Removing activity-logging hooks\n'));
116
+
117
+ for (const provider of targets) {
118
+ const filePath = path.join(projectRoot, ...provider.file);
119
+ const rel = path.relative(projectRoot, filePath) || filePath;
120
+ const existing = await readJsonSafe(filePath);
121
+
122
+ if (existing === PARSE_ERROR) {
123
+ printError(`${provider.label}: ${rel} is not valid JSON — skipped`);
124
+ continue;
125
+ }
126
+ if (!existing || !isInstalled(provider, existing)) {
127
+ printDim(`${provider.label}: nothing to remove`);
128
+ continue;
129
+ }
130
+ const stripped = removeConfig(provider, existing);
131
+ await fs.writeJson(filePath, stripped, { spaces: 2 });
132
+ printOk(`${provider.label} → removed from ${rel}`);
133
+ }
134
+ console.log('');
135
+ })
136
+ );
137
+ };
138
+
139
+ // ── helpers ──────────────────────────────────────────────────────────────
140
+
141
+ const PARSE_ERROR = Symbol('parse-error');
142
+
143
+ function resolveTargets(targetKey) {
144
+ if (!targetKey || targetKey === 'all') return PROVIDERS;
145
+ const provider = getProvider(targetKey);
146
+ if (!provider) {
147
+ throw new ValidationError(
148
+ `Unknown target: ${targetKey}`,
149
+ `Valid: ${PROVIDERS.map((p) => p.key).join(', ')}, all`
150
+ );
151
+ }
152
+ return [provider];
153
+ }
154
+
155
+ /** Read JSON if present; null if missing; PARSE_ERROR sentinel if unparseable. */
156
+ async function readJsonSafe(filePath) {
157
+ if (!(await fs.pathExists(filePath))) return null;
158
+ try {
159
+ return await fs.readJson(filePath);
160
+ } catch (_) {
161
+ return PARSE_ERROR;
162
+ }
163
+ }
@@ -0,0 +1,189 @@
1
+ const path = require('path');
2
+ const chalk = require('chalk');
3
+ const {
4
+ isInitialized,
5
+ scaffoldDirectories,
6
+ copyTemplate,
7
+ getVFlowDir,
8
+ scanCodebase,
9
+ generateContextMd,
10
+ } = require('../lib/templates');
11
+ const { runSync, syncVFlowAssets, selectTargets } = require('../lib/sync-core');
12
+ const { withErrorHandler } = require('../lib/cli-error');
13
+ const { createT } = require('../lib/i18n');
14
+
15
+ module.exports = function (program) {
16
+ program
17
+ .command('init')
18
+ .description('Initialize V Flow in the current project')
19
+ .argument('[project-name]', 'Project name (optional)', '')
20
+ .option('--ai <agents>', 'AI agents to setup: antigravity, copilot, cursor, claude, all (default: all)', 'all')
21
+ .option('--skip-ai', 'Skip AI agent file setup')
22
+ .option('--force', 'Force reinitialize (overwrite existing files)')
23
+ .action(withErrorHandler(async (projectName, options) => {
24
+ const t = createT();
25
+ const projectRoot = process.cwd();
26
+ const sourceRoot = path.resolve(__dirname, '../..');
27
+
28
+ console.log(chalk.cyan.bold(`\n${t('init.title')}\n`));
29
+
30
+ // Check if already initialized
31
+ if (isInitialized(projectRoot) && !options.force) {
32
+ console.log(chalk.yellow(t('init.already_exists')));
33
+ console.log(chalk.dim(`${t('init.already_exists_hint') }\n`));
34
+ }
35
+
36
+ // Step 1: Scaffold directories
37
+ console.log(chalk.white(t('init.creating_dirs')));
38
+ await scaffoldDirectories(projectRoot);
39
+ console.log(chalk.green(` ${t('init.dir_created')}`));
40
+
41
+ // Step 2: Copy constitution template
42
+ const mfDir = getVFlowDir(projectRoot);
43
+ const constitutionDest = path.join(mfDir, 'constitution.md');
44
+ const fs = require('fs-extra');
45
+
46
+ if (!fs.existsSync(constitutionDest) || options.force) {
47
+ const today = new Date().toISOString().split('T')[0];
48
+ await copyTemplate('constitution-template.md', constitutionDest, {
49
+ 'TÊN DỰ ÁN': projectName || path.basename(projectRoot),
50
+ 'YYYY-MM-DD': today,
51
+ });
52
+ console.log(chalk.green(` ${t('init.constitution_created')}`));
53
+ } else {
54
+ console.log(chalk.dim(` ${t('init.constitution_exists')}`));
55
+ }
56
+
57
+ // Step 3: Copy agent-config template
58
+ const agentConfigDest = path.join(mfDir, 'agent-config.md');
59
+ if (!fs.existsSync(agentConfigDest) || options.force) {
60
+ await copyTemplate('agent-config-template.md', agentConfigDest);
61
+ console.log(chalk.green(` ${t('init.agent_config_created')}`));
62
+ } else {
63
+ console.log(chalk.dim(` ${t('init.agent_config_exists')}`));
64
+ }
65
+
66
+ // Step 4: Scan codebase & create context.md
67
+ const contextDest = path.join(mfDir, 'context.md');
68
+ if (!fs.existsSync(contextDest) || options.force) {
69
+ process.stdout.write(chalk.white(`\n${t('init.scanning')}`));
70
+ try {
71
+ const scan = await scanCodebase(projectRoot);
72
+ const contextContent = generateContextMd(scan);
73
+ await fs.writeFile(contextDest, contextContent, 'utf8');
74
+ process.stdout.write(` ${ chalk.green('✅\n')}`);
75
+ console.log(chalk.green(` ${t('init.scan_complete', { count: scan.totalFiles })}`));
76
+ if (scan.languages.length > 0) {
77
+ console.log(chalk.dim(t('init.scan_languages', { languages: scan.languages.join(', ') })));
78
+ }
79
+ if (scan.frameworks.length > 0) {
80
+ console.log(chalk.dim(t('init.scan_frameworks', { frameworks: scan.frameworks.join(', ') })));
81
+ }
82
+ console.log(chalk.yellow(` ${t('init.scan_warning')}`));
83
+ } catch (err) {
84
+ process.stdout.write(` ${ chalk.yellow(`${t('init.scan_failed')}\n`)}`);
85
+ await copyTemplate('context-template.md', contextDest);
86
+ console.log(chalk.dim(` ${t('init.scan_template')}`));
87
+ }
88
+ } else {
89
+ console.log(chalk.dim(` ${t('init.context_exists')}`));
90
+ }
91
+
92
+ // Step 5: Seed Knowledge Base
93
+ const knowledgeDir = path.join(mfDir, 'knowledge');
94
+ const knowledgeTemplateDir = path.join(sourceRoot, 'templates', 'knowledge');
95
+
96
+ const knowledgeFiles = [
97
+ { template: 'domain-glossary.md', dest: 'domain-glossary.md' },
98
+ { template: 'patterns.md', dest: 'patterns/v-flow-patterns.md' },
99
+ { template: 'lessons-learned.md', dest: 'lessons-learned/v-flow-lessons.md' },
100
+ ];
101
+
102
+ let kbCreated = 0;
103
+ for (const kf of knowledgeFiles) {
104
+ const destPath = path.join(knowledgeDir, kf.dest);
105
+ const srcPath = path.join(knowledgeTemplateDir, kf.template);
106
+ if (!fs.existsSync(destPath) || options.force) {
107
+ await fs.ensureDir(path.dirname(destPath));
108
+ if (fs.existsSync(srcPath)) {
109
+ await fs.copy(srcPath, destPath);
110
+ }
111
+ kbCreated++;
112
+ }
113
+ }
114
+ if (kbCreated > 0) {
115
+ console.log(chalk.green(` ${t('init.knowledge_seeded', { count: kbCreated })}`));
116
+ } else {
117
+ console.log(chalk.dim(` ${t('init.knowledge_exists')}`));
118
+ }
119
+
120
+ // Step 7: Copy DESIGN.md into .v-flow/DESIGN.md
121
+ const designDest = path.join(mfDir, 'DESIGN.md');
122
+ if (!fs.existsSync(designDest) || options.force) {
123
+ await copyTemplate('DESIGN.md', designDest);
124
+ console.log(chalk.green(` ✅ Design system: .v-flow/DESIGN.md`));
125
+ } else {
126
+ console.log(chalk.dim(` .v-flow/DESIGN.md already exists`));
127
+ }
128
+
129
+ // Step 8: Scaffold CI gate — runs `v-flow validate --strict` on PRs so
130
+ // spec/plan/tasks can't drift. Without this the validator gates nothing.
131
+ const ciDest = path.join(projectRoot, '.github', 'workflows', 'v-flow-validate.yml');
132
+ if (!fs.existsSync(ciDest) || options.force) {
133
+ await fs.ensureDir(path.dirname(ciDest));
134
+ await copyTemplate('github/workflows/v-flow-validate.yml', ciDest);
135
+ console.log(chalk.green(' ✅ CI gate: .github/workflows/v-flow-validate.yml'));
136
+ } else {
137
+ console.log(chalk.dim(' .github/workflows/v-flow-validate.yml already exists'));
138
+ }
139
+
140
+ // Step 6: Sync AI agent files using shared sync-core
141
+ if (!options.skipAi) {
142
+ console.log(chalk.white(`\n${t('init.syncing')}`));
143
+
144
+ const ai = options.ai.toLowerCase();
145
+
146
+ // Use the shared sync engine — same logic as `v-flow sync --apply`
147
+ const stats = await runSync({
148
+ projectRoot,
149
+ sourceRoot,
150
+ dryRun: false,
151
+ verbose: false,
152
+ target: ai,
153
+ silent: true,
154
+ });
155
+
156
+ // Per-target summary — derived from the same registry runSync walks,
157
+ // so a new vendor shows up here automatically (no edits needed).
158
+ for (const tgt of selectTargets(ai)) {
159
+ const cmdDir = path.join(projectRoot, ...tgt.commands.dir);
160
+ const ext = tgt.commands.ext || '.md';
161
+ const cmdCount = fs.existsSync(cmdDir)
162
+ ? (await fs.readdir(cmdDir)).filter(f => f.endsWith(ext)).length
163
+ : 0;
164
+ const globals = (tgt.globals || []).map(g => path.join(...g.dest)).join(', ');
165
+ console.log(chalk.green(` ✅ ${tgt.label}: ${globals}`));
166
+ console.log(chalk.green(` ${path.join(...tgt.commands.dir)}/ (${cmdCount} slash commands) + agents + skills`));
167
+ }
168
+
169
+ console.log(chalk.green(' ✅ Templates: .v-flow/templates/ (all templates synced)'));
170
+ console.log(chalk.dim(`\n ${t('init.sync_stats', { added: stats.added, synced: stats.synced, skipped: stats.skipped })}`));
171
+ } else {
172
+ // --skip-ai bỏ qua file AI-tool, NHƯNG vẫn cài runtime assets (.v-flow/templates + skills)
173
+ // để agent path `.v-flow/...` resolve được (chúng là framework runtime, không phải AI files).
174
+ const assets = await syncVFlowAssets({ projectRoot, sourceRoot, dryRun: false });
175
+ console.log(chalk.green(` ✅ Runtime assets → .v-flow/ (${assets.templates} templates + ${assets.skills} skill files; AI agent files skipped)`));
176
+ }
177
+
178
+ // Summary
179
+ console.log(chalk.cyan.bold(`\n${t('init.complete')}\n`));
180
+ console.log(chalk.white(t('init.next_steps')));
181
+ console.log(chalk.yellow(` ${t('init.review_context')}`));
182
+ console.log(chalk.dim(` ${t('init.edit_constitution')}`));
183
+ console.log(chalk.dim(` ${t('init.edit_agent_config')}`));
184
+ console.log(chalk.dim(` ${t('init.import_ba_docs')}`));
185
+ console.log(chalk.dim(` ${t('init.start_feature')}`));
186
+ console.log(chalk.dim(` ${t('init.sync_hint')}`));
187
+ console.log('');
188
+ }));
189
+ };