@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,692 @@
1
+ /**
2
+ * Scanner — Codebase static analysis module
3
+ *
4
+ * Extracted from templates.js (Phase 3 refactor).
5
+ * Handles: file walking, language detection, framework detection, package manager detection.
6
+ */
7
+
8
+ const path = require('path');
9
+ const fs = require('fs-extra');
10
+
11
+ // ============================================================
12
+ // CONSTANTS
13
+ // ============================================================
14
+
15
+ /**
16
+ * Extension → language/type mapping
17
+ */
18
+ const EXT_MAP = {
19
+ // JavaScript / TypeScript
20
+ '.js': 'JavaScript', '.mjs': 'JavaScript', '.cjs': 'JavaScript',
21
+ '.ts': 'TypeScript', '.tsx': 'TypeScript',
22
+ '.jsx': 'JavaScript (React)',
23
+ // Python
24
+ '.py': 'Python',
25
+ // Ruby
26
+ '.rb': 'Ruby',
27
+ // Go
28
+ '.go': 'Go',
29
+ // JVM
30
+ '.java': 'Java', '.kt': 'Kotlin',
31
+ // .NET
32
+ '.cs': 'C#', '.fs': 'F#',
33
+ // Systems
34
+ '.cpp': 'C++', '.c': 'C', '.rs': 'Rust', '.swift': 'Swift',
35
+ // PHP
36
+ '.php': 'PHP',
37
+ // Dart/Flutter
38
+ '.dart': 'Dart',
39
+ // Web frameworks
40
+ '.vue': 'Vue', '.svelte': 'Svelte',
41
+ // Config / Data
42
+ '.json': 'JSON', '.yaml': 'YAML', '.yml': 'YAML', '.toml': 'TOML',
43
+ '.sql': 'SQL', '.graphql': 'GraphQL',
44
+ // Shell
45
+ '.sh': 'Shell', '.bash': 'Shell',
46
+ // Web
47
+ '.html': 'HTML', '.css': 'CSS', '.scss': 'SCSS', '.sass': 'SASS',
48
+ // Docs
49
+ '.md': 'Markdown',
50
+ };
51
+
52
+ /** Directories to skip when scanning */
53
+ const SKIP_DIRS = new Set([
54
+ 'node_modules', '.git', '.next', '.nuxt', 'dist', 'build',
55
+ 'out', 'coverage', '.cache', 'vendor', '__pycache__', '.venv',
56
+ 'venv', 'env', 'target', 'bin', 'obj', '.v-flow',
57
+ // Flutter/Dart
58
+ '.dart_tool', '.fvm', '.pub-cache',
59
+ // iOS/macOS
60
+ 'Pods', '.symlinks',
61
+ // Android
62
+ '.gradle',
63
+ ]);
64
+
65
+ /** Framework detection patterns: { dep or file → label } */
66
+ const FRAMEWORK_PATTERNS = [
67
+ // JS/TS
68
+ { dep: 'next', label: 'Next.js' },
69
+ { dep: 'nuxt', label: 'Nuxt.js' },
70
+ { dep: 'react', label: 'React' },
71
+ { dep: 'vue', label: 'Vue.js' },
72
+ { dep: 'svelte', label: 'Svelte' },
73
+ { dep: 'express', label: 'Express.js' },
74
+ { dep: 'fastify', label: 'Fastify' },
75
+ { dep: 'nestjs/core', label: 'NestJS' },
76
+ { dep: '@nestjs/core', label: 'NestJS' },
77
+ { dep: 'hono', label: 'Hono' },
78
+ { dep: 'koa', label: 'Koa' },
79
+ { dep: 'remix', label: 'Remix' },
80
+ { dep: 'astro', label: 'Astro' },
81
+ // Python
82
+ { dep: 'django', label: 'Django' },
83
+ { dep: 'flask', label: 'Flask' },
84
+ { dep: 'fastapi', label: 'FastAPI' },
85
+ // Java/Kotlin
86
+ { dep: 'spring-boot', label: 'Spring Boot' },
87
+ // Dart/Flutter
88
+ { dep: 'flutter', label: 'Flutter' },
89
+ { dep: 'riverpod', label: 'Riverpod' },
90
+ { dep: 'bloc', label: 'BLoC' },
91
+ { dep: 'provider', label: 'Provider' },
92
+ { dep: 'get', label: 'GetX' },
93
+ // .NET
94
+ { dep: 'Microsoft.AspNetCore', label: 'ASP.NET Core' },
95
+ { dep: 'Microsoft.EntityFrameworkCore', label: 'Entity Framework' },
96
+ // Rust
97
+ { dep: 'actix-web', label: 'Actix Web' },
98
+ { dep: 'axum', label: 'Axum' },
99
+ { dep: 'rocket', label: 'Rocket' },
100
+ { dep: 'tokio', label: 'Tokio' },
101
+ // Go
102
+ { dep: 'gin-gonic/gin', label: 'Gin' },
103
+ { dep: 'gorilla/mux', label: 'Gorilla Mux' },
104
+ { dep: 'labstack/echo', label: 'Echo' },
105
+ { dep: 'gofiber/fiber', label: 'Fiber' },
106
+ // Testing
107
+ { dep: 'jest', label: 'Jest' },
108
+ { dep: 'vitest', label: 'Vitest' },
109
+ { dep: 'pytest', label: 'pytest' },
110
+ { dep: 'mocha', label: 'Mocha' },
111
+ { dep: 'cypress', label: 'Cypress' },
112
+ { dep: 'playwright', label: 'Playwright' },
113
+ { dep: 'flutter_test', label: 'flutter_test' },
114
+ { dep: 'mockito', label: 'Mockito' },
115
+ { dep: 'xunit', label: 'xUnit' },
116
+ { dep: 'nunit', label: 'NUnit' },
117
+ // DB / ORM
118
+ { dep: 'prisma', label: 'Prisma' },
119
+ { dep: 'typeorm', label: 'TypeORM' },
120
+ { dep: 'sequelize', label: 'Sequelize' },
121
+ { dep: 'mongoose', label: 'Mongoose' },
122
+ { dep: 'sqlalchemy', label: 'SQLAlchemy' },
123
+ { dep: 'drizzle-orm', label: 'Drizzle' },
124
+ // Infrastructure
125
+ { dep: 'docker', label: 'Docker', fileOnly: true },
126
+ { dep: 'terraform', label: 'Terraform', fileOnly: true },
127
+ ];
128
+
129
+ /** Languages considered "code" (for primary language ranking) */
130
+ const CODE_LANGUAGES = [
131
+ 'JavaScript', 'TypeScript', 'JavaScript (React)', 'Python', 'Ruby', 'Go',
132
+ 'Java', 'Kotlin', 'C#', 'F#', 'PHP', 'Rust', 'Swift', 'Dart',
133
+ 'C++', 'C', 'Vue', 'Svelte',
134
+ ];
135
+
136
+ /** Testing framework labels (used to separate from regular frameworks) */
137
+ const TESTING_LABELS = [
138
+ 'Jest', 'Vitest', 'Mocha', 'Cypress', 'Playwright', 'pytest',
139
+ 'flutter_test', 'Mockito', 'xUnit', 'NUnit',
140
+ ];
141
+
142
+ // ============================================================
143
+ // FILE WALKING
144
+ // ============================================================
145
+
146
+ /**
147
+ * Walk directory recursively, collecting file stats.
148
+ * @returns {{ extCounts, totalFiles, testFiles, configFiles, entrypoints }}
149
+ */
150
+ async function walkDir(dir, depth = 0, acc = null) {
151
+ if (!acc) {
152
+ acc = { extCounts: {}, totalFiles: 0, testFiles: [], configFiles: [], entrypoints: [] };
153
+ }
154
+ if (depth > 8) return acc;
155
+
156
+ let entries;
157
+ try {
158
+ entries = await fs.readdir(dir, { withFileTypes: true });
159
+ } catch {
160
+ return acc;
161
+ }
162
+
163
+ for (const entry of entries) {
164
+ if (SKIP_DIRS.has(entry.name)) continue;
165
+ const fullPath = path.join(dir, entry.name);
166
+
167
+ if (entry.isDirectory()) {
168
+ await walkDir(fullPath, depth + 1, acc);
169
+ } else if (entry.isFile()) {
170
+ acc.totalFiles++;
171
+ const ext = path.extname(entry.name).toLowerCase();
172
+ if (EXT_MAP[ext]) {
173
+ acc.extCounts[EXT_MAP[ext]] = (acc.extCounts[EXT_MAP[ext]] || 0) + 1;
174
+ }
175
+
176
+ const name = entry.name.toLowerCase();
177
+
178
+ // Detect test files
179
+ if (isTestFile(name)) {
180
+ acc.testFiles.push(path.relative(dir, fullPath));
181
+ }
182
+
183
+ // Detect config files
184
+ if (isConfigFile(name)) {
185
+ acc.configFiles.push(entry.name);
186
+ }
187
+
188
+ // Detect likely entrypoints
189
+ if (isEntrypoint(name) && depth <= 3) {
190
+ acc.entrypoints.push(entry.name);
191
+ }
192
+ }
193
+ }
194
+ return acc;
195
+ }
196
+
197
+ /** Check if filename looks like a test file */
198
+ function isTestFile(name) {
199
+ return (
200
+ name.includes('.test.') ||
201
+ name.includes('.spec.') ||
202
+ name.startsWith('test_') ||
203
+ name.endsWith('_test.js') ||
204
+ name.endsWith('_test.ts') ||
205
+ name.endsWith('_test.dart') ||
206
+ name.endsWith('_test.go') ||
207
+ name.endsWith('_test.py') ||
208
+ name.endsWith('_test.rs')
209
+ );
210
+ }
211
+
212
+ /** Check if filename is a project config file */
213
+ function isConfigFile(name) {
214
+ const configNames = [
215
+ 'package.json', 'pom.xml', 'build.gradle', 'build.gradle.kts',
216
+ 'requirements.txt', 'pyproject.toml', 'cargo.toml',
217
+ 'go.mod', 'go.sum',
218
+ 'pubspec.yaml',
219
+ 'dockerfile', 'docker-compose.yml', 'docker-compose.yaml',
220
+ '.csproj', '.sln', '.fsproj',
221
+ 'gemfile', 'mix.exs',
222
+ ];
223
+ return configNames.includes(name) || name.endsWith('.csproj') || name.endsWith('.sln');
224
+ }
225
+
226
+ /** Check if filename is a likely entrypoint */
227
+ function isEntrypoint(name) {
228
+ const entryNames = [
229
+ 'main.js', 'main.ts', 'index.js', 'index.ts',
230
+ 'app.js', 'app.ts', 'server.js', 'server.ts',
231
+ 'main.py', 'app.py', 'main.go',
232
+ 'main.java', 'main.dart', 'main.rs',
233
+ 'program.cs',
234
+ ];
235
+ return entryNames.includes(name);
236
+ }
237
+
238
+ // ============================================================
239
+ // DEPENDENCY READERS
240
+ // ============================================================
241
+
242
+ /**
243
+ * Read package.json and return { name, version, deps, devDeps, scripts }
244
+ */
245
+ async function readPackageJson(projectRoot) {
246
+ const pkgPath = path.join(projectRoot, 'package.json');
247
+ if (!fs.existsSync(pkgPath)) return null;
248
+ try {
249
+ const pkg = await fs.readJson(pkgPath);
250
+ return {
251
+ name: pkg.name || '',
252
+ version: pkg.version || '',
253
+ deps: Object.keys(pkg.dependencies || {}),
254
+ devDeps: Object.keys(pkg.devDependencies || {}),
255
+ scripts: Object.keys(pkg.scripts || {}),
256
+ };
257
+ } catch {
258
+ return null;
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Read requirements.txt (Python)
264
+ */
265
+ async function readRequirementsTxt(projectRoot) {
266
+ const reqPath = path.join(projectRoot, 'requirements.txt');
267
+ if (!fs.existsSync(reqPath)) return [];
268
+ try {
269
+ const content = await fs.readFile(reqPath, 'utf8');
270
+ return content.split('\n')
271
+ .map(l => l.split('==')[0].split('>=')[0].split('~=')[0].trim().toLowerCase())
272
+ .filter(Boolean);
273
+ } catch {
274
+ return [];
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Read pubspec.yaml (Flutter/Dart) — simple regex-based parser
280
+ */
281
+ async function readPubspecYaml(projectRoot) {
282
+ const pubspecPath = path.join(projectRoot, 'pubspec.yaml');
283
+ if (!fs.existsSync(pubspecPath)) return null;
284
+ try {
285
+ const content = await fs.readFile(pubspecPath, 'utf8');
286
+ const nameMatch = content.match(/^name:\s*(.+)$/m);
287
+ const versionMatch = content.match(/^version:\s*(.+)$/m);
288
+
289
+ // Extract dependency names (lines under dependencies: / dev_dependencies:)
290
+ const deps = [];
291
+ const depSections = content.match(/(?:dependencies|dev_dependencies):\s*\n((?: {2}.+\n)*)/g) || [];
292
+ for (const section of depSections) {
293
+ const depLines = section.match(/^\s{2}(\w[\w_-]*?):/gm) || [];
294
+ for (const line of depLines) {
295
+ const depName = line.trim().replace(':', '');
296
+ deps.push(depName);
297
+ }
298
+ }
299
+
300
+ return {
301
+ name: nameMatch ? nameMatch[1].trim() : '',
302
+ version: versionMatch ? versionMatch[1].trim() : '',
303
+ deps,
304
+ };
305
+ } catch {
306
+ return null;
307
+ }
308
+ }
309
+
310
+ /**
311
+ * Read Cargo.toml (Rust) — simple regex-based parser
312
+ */
313
+ async function readCargoToml(projectRoot) {
314
+ const cargoPath = path.join(projectRoot, 'Cargo.toml');
315
+ if (!fs.existsSync(cargoPath)) return null;
316
+ try {
317
+ const content = await fs.readFile(cargoPath, 'utf8');
318
+ const nameMatch = content.match(/^name\s*=\s*"(.+)"/m);
319
+
320
+ const deps = [];
321
+ const depSection = content.match(/\[dependencies\]([\s\S]*?)(?:\[|$)/);
322
+ if (depSection) {
323
+ const depLines = depSection[1].match(/^(\w[\w_-]*?)\s*=/gm) || [];
324
+ for (const line of depLines) {
325
+ deps.push(line.replace(/\s*=.*/, '').trim());
326
+ }
327
+ }
328
+
329
+ return {
330
+ name: nameMatch ? nameMatch[1] : '',
331
+ deps,
332
+ };
333
+ } catch {
334
+ return null;
335
+ }
336
+ }
337
+
338
+ /**
339
+ * Read go.mod (Go) — extract module name and dependencies
340
+ */
341
+ async function readGoMod(projectRoot) {
342
+ const goModPath = path.join(projectRoot, 'go.mod');
343
+ if (!fs.existsSync(goModPath)) return null;
344
+ try {
345
+ const content = await fs.readFile(goModPath, 'utf8');
346
+ const moduleMatch = content.match(/^module\s+(.+)$/m);
347
+
348
+ const deps = [];
349
+ const requireBlock = content.match(/require\s*\(([\s\S]*?)\)/);
350
+ if (requireBlock) {
351
+ const depLines = requireBlock[1].match(/^\s*(\S+)/gm) || [];
352
+ for (const line of depLines) {
353
+ deps.push(line.trim());
354
+ }
355
+ }
356
+
357
+ return {
358
+ name: moduleMatch ? moduleMatch[1].trim() : '',
359
+ deps,
360
+ };
361
+ } catch {
362
+ return null;
363
+ }
364
+ }
365
+
366
+ // ============================================================
367
+ // FRAMEWORK DETECTION
368
+ // ============================================================
369
+
370
+ /**
371
+ * Detect frameworks from deps array
372
+ */
373
+ function detectFrameworks(deps) {
374
+ const detected = [];
375
+ const allDeps = deps.join(' ').toLowerCase();
376
+ for (const { dep, label } of FRAMEWORK_PATTERNS) {
377
+ if (allDeps.includes(dep.toLowerCase())) {
378
+ detected.push(label);
379
+ }
380
+ }
381
+ return [...new Set(detected)];
382
+ }
383
+
384
+ /**
385
+ * Detect frameworks from config files (Dockerfile, CI configs, etc.)
386
+ */
387
+ function detectFromConfigFiles(configFiles) {
388
+ const detected = [];
389
+ const names = configFiles.map(f => f.toLowerCase());
390
+
391
+ if (names.some(n => n === 'dockerfile' || n.startsWith('docker-compose'))) {
392
+ detected.push('Docker');
393
+ }
394
+ if (names.some(n => n === '.github/workflows' || n.endsWith('.yml'))) {
395
+ // GitHub Actions detected separately
396
+ }
397
+
398
+ return detected;
399
+ }
400
+
401
+ // ============================================================
402
+ // PACKAGE MANAGER DETECTION
403
+ // ============================================================
404
+
405
+ /**
406
+ * Detect package manager from lock files and config files
407
+ */
408
+ function detectPackageManager(projectRoot) {
409
+ const checks = [
410
+ { file: 'pnpm-lock.yaml', manager: 'pnpm' },
411
+ { file: 'yarn.lock', manager: 'yarn' },
412
+ { file: 'bun.lockb', manager: 'bun' },
413
+ { file: 'package-lock.json', manager: 'npm' },
414
+ { file: 'pubspec.lock', manager: 'pub' },
415
+ { file: 'requirements.txt', manager: 'pip' },
416
+ { file: 'Pipfile.lock', manager: 'pipenv' },
417
+ { file: 'poetry.lock', manager: 'poetry' },
418
+ { file: 'go.sum', manager: 'go modules' },
419
+ { file: 'go.mod', manager: 'go modules' },
420
+ { file: 'pom.xml', manager: 'Maven' },
421
+ { file: 'build.gradle', manager: 'Gradle' },
422
+ { file: 'build.gradle.kts', manager: 'Gradle' },
423
+ { file: 'Cargo.lock', manager: 'Cargo' },
424
+ { file: 'Cargo.toml', manager: 'Cargo' },
425
+ { file: 'Gemfile.lock', manager: 'Bundler' },
426
+ { file: 'mix.lock', manager: 'Mix' },
427
+ ];
428
+
429
+ for (const { file, manager } of checks) {
430
+ if (fs.existsSync(path.join(projectRoot, file))) {
431
+ return manager;
432
+ }
433
+ }
434
+
435
+ return 'unknown';
436
+ }
437
+
438
+ // ============================================================
439
+ // MAIN SCANNER
440
+ // ============================================================
441
+
442
+ /**
443
+ * Main scanner — runs all static analysis and returns a summary object
444
+ */
445
+ async function scanCodebase(projectRoot) {
446
+ const today = new Date().toISOString().split('T')[0];
447
+ const result = {
448
+ scannedAt: today,
449
+ projectName: path.basename(projectRoot),
450
+ languages: [],
451
+ frameworks: [],
452
+ testingTools: [],
453
+ hasTests: false,
454
+ testFileCount: 0,
455
+ totalFiles: 0,
456
+ entrypoints: [],
457
+ configFiles: [],
458
+ packageManager: 'unknown',
459
+ rawExtCounts: {},
460
+ };
461
+
462
+ // Walk directory
463
+ const walk = await walkDir(projectRoot);
464
+ result.totalFiles = walk.totalFiles;
465
+ result.testFileCount = walk.testFiles.length;
466
+ result.hasTests = walk.testFiles.length > 0;
467
+ result.entrypoints = [...new Set(walk.entrypoints)].slice(0, 5);
468
+ result.configFiles = walk.configFiles;
469
+ result.rawExtCounts = walk.extCounts;
470
+
471
+ // Top languages (exclude non-code types)
472
+ result.languages = Object.entries(walk.extCounts)
473
+ .filter(([lang]) => CODE_LANGUAGES.includes(lang))
474
+ .sort((a, b) => b[1] - a[1])
475
+ .map(([lang]) => lang)
476
+ .slice(0, 5);
477
+
478
+ // Package manager detection
479
+ result.packageManager = detectPackageManager(projectRoot);
480
+
481
+ // Framework detection from package.json (Node.js)
482
+ const pkg = await readPackageJson(projectRoot);
483
+ if (pkg) {
484
+ const allDeps = [...pkg.deps, ...pkg.devDeps];
485
+ const allFrameworks = detectFrameworks(allDeps);
486
+ result.testingTools = allFrameworks.filter(f => TESTING_LABELS.includes(f));
487
+ result.frameworks = allFrameworks.filter(f => !TESTING_LABELS.includes(f));
488
+ }
489
+
490
+ // Python deps
491
+ const pyDeps = await readRequirementsTxt(projectRoot);
492
+ if (pyDeps.length > 0) {
493
+ const pyFrameworks = detectFrameworks(pyDeps);
494
+ result.frameworks.push(...pyFrameworks.filter(f => !result.frameworks.includes(f)));
495
+ }
496
+
497
+ // Dart/Flutter deps
498
+ const pubspec = await readPubspecYaml(projectRoot);
499
+ if (pubspec) {
500
+ if (pubspec.name && !pkg) {
501
+ result.projectName = pubspec.name;
502
+ }
503
+ const dartFrameworks = detectFrameworks(pubspec.deps);
504
+ const dartTesting = dartFrameworks.filter(f => TESTING_LABELS.includes(f));
505
+ const dartRegular = dartFrameworks.filter(f => !TESTING_LABELS.includes(f));
506
+ result.frameworks.push(...dartRegular.filter(f => !result.frameworks.includes(f)));
507
+ result.testingTools.push(...dartTesting.filter(f => !result.testingTools.includes(f)));
508
+ }
509
+
510
+ // Rust deps
511
+ const cargo = await readCargoToml(projectRoot);
512
+ if (cargo) {
513
+ if (cargo.name && !pkg && !pubspec) {
514
+ result.projectName = cargo.name;
515
+ }
516
+ const rustFrameworks = detectFrameworks(cargo.deps);
517
+ result.frameworks.push(...rustFrameworks.filter(f => !result.frameworks.includes(f)));
518
+ }
519
+
520
+ // Go deps
521
+ const goMod = await readGoMod(projectRoot);
522
+ if (goMod) {
523
+ if (goMod.name && !pkg && !pubspec && !cargo) {
524
+ result.projectName = path.basename(goMod.name);
525
+ }
526
+ const goFrameworks = detectFrameworks(goMod.deps);
527
+ result.frameworks.push(...goFrameworks.filter(f => !result.frameworks.includes(f)));
528
+ }
529
+
530
+ // Config-file-based detection (Docker, etc.)
531
+ const configFrameworks = detectFromConfigFiles(walk.configFiles);
532
+ result.frameworks.push(...configFrameworks.filter(f => !result.frameworks.includes(f)));
533
+
534
+ return result;
535
+ }
536
+
537
+ // ============================================================
538
+ // CONTEXT.MD GENERATOR
539
+ // ============================================================
540
+
541
+ /**
542
+ * Generate context.md content from scan result
543
+ */
544
+ function generateContextMd(scan) {
545
+ const langList = scan.languages.length > 0 ? scan.languages.join(', ') : '*(chưa phát hiện — tự điền)*';
546
+ const frameworkList = scan.frameworks.length > 0 ? scan.frameworks.join(', ') : '*(chưa phát hiện — tự điền)*';
547
+ const testingList = scan.testingTools.length > 0 ? scan.testingTools.join(', ') : '*(không tìm thấy)*';
548
+ const hasTests = scan.hasTests ? `✅ Có (${scan.testFileCount} test files phát hiện)` : '❌ Không tìm thấy test files';
549
+ const entrypointList = scan.entrypoints.length > 0
550
+ ? scan.entrypoints.map(e => `| \`${e}\` | *(tự điền mô tả)* |`).join('\n')
551
+ : '| *(tự xác định)* | |';
552
+
553
+ return `# context.md — Codebase Understanding Report
554
+
555
+ > **Phase**: U.0 (Understand)
556
+ > **Dự án**: ${scan.projectName}
557
+ > **Ngày tạo**: ${scan.scannedAt} (auto-generated by v-flow init)
558
+ > **Cập nhật lần cuối**: ${scan.scannedAt}
559
+ > **Người review**: *(Tech Lead cần review và bổ sung)*
560
+
561
+ ---
562
+
563
+ ## ⚠️ Nguyên tắc của file này
564
+
565
+ > **Ghi thực trạng** — không phải "should be". Không lý tưởng hóa.
566
+ > File này là **input bắt buộc** cho mọi Agent trước khi làm feature trên dự án này.
567
+ > Các mục đánh dấu \`*(tự điền)*\` cần được Tech Lead bổ sung thủ công.
568
+
569
+ ---
570
+
571
+ ## 1. Tech Stack Thực Tế
572
+
573
+ *🤖 Auto-detected (${scan.scannedAt}) — ${scan.totalFiles} files scanned*
574
+
575
+ | Hạng mục | Công nghệ |
576
+ |----------|-----------|
577
+ | **Ngôn ngữ chính** | ${langList} |
578
+ | **Framework** | ${frameworkList} |
579
+ | **Package manager** | ${scan.packageManager} |
580
+ | **Testing** | ${testingList} |
581
+ | **Database** | *(tự điền)* |
582
+ | **Cache** | *(tự điền)* |
583
+ | **CI/CD** | *(tự điền)* |
584
+ | **Deployment** | *(tự điền)* |
585
+
586
+ ---
587
+
588
+ ## 2. Kiến Trúc Thực Tế
589
+
590
+ > ⚠️ Mô tả kiến trúc như nó đang tồn tại, không phải lý tưởng.
591
+
592
+ ### Cấu trúc thư mục chính
593
+
594
+ \`\`\`
595
+ *(Tech Lead điền vào — chạy \`tree -d -L 2\` hoặc xem trong IDE)*
596
+ \`\`\`
597
+
598
+ ### Các Layer / Module chính
599
+
600
+ | Module | Vai trò | File/Folder chính |
601
+ |--------|---------|------------------|
602
+ | *(tự điền)* | | |
603
+
604
+ ---
605
+
606
+ ## 3. Entry Points
607
+
608
+ *🤖 Phát hiện tự động — kiểm tra lại độ chính xác*
609
+
610
+ | Entry Point | File phát hiện |
611
+ |-------------|---------------|
612
+ ${entrypointList}
613
+ | *(bổ sung thêm nếu cần)* | |
614
+
615
+ ---
616
+
617
+ ## 4. 🚫 No-Go Zones
618
+
619
+ > **Đây là luật**: Agent KHÔNG được tự ý sửa các vùng này.
620
+ > *(Tech Lead điền vào — là phần quan trọng nhất cần bổ sung)*
621
+
622
+ | Vùng code | Lý do không nên đụng | Rủi ro |
623
+ |-----------|---------------------|--------|
624
+ | *(tự điền)* | | Cao/Trung/Thấp |
625
+
626
+ ---
627
+
628
+ ## 5. Technical Debt Nổi Bật
629
+
630
+ | Mức độ | Mô tả | Kế hoạch fix |
631
+ |--------|-------|--------------|
632
+ | 🔴 Cao | *(tự điền)* | |
633
+ | 🟡 Trung bình | *(tự điền)* | |
634
+
635
+ ---
636
+
637
+ ## 6. Test Coverage Hiện Tại
638
+
639
+ *🤖 Auto-detected*
640
+
641
+ | Loại | Kết quả |
642
+ |------|---------|
643
+ | Test files phát hiện | ${scan.testFileCount} files |
644
+ | Có test không? | ${hasTests} |
645
+ | Coverage ước tính | *(chạy test để biết chính xác)* |
646
+
647
+ **Vùng không có test** (nguy hiểm nhất):
648
+ - [ ] *(Tech Lead điền vào)*
649
+
650
+ ---
651
+
652
+ ## 7. Patterns Đang Dùng (Dù Không Chính Thức)
653
+
654
+ *(Tech Lead điền vào sau khi review codebase)*
655
+
656
+ - **Naming**: *(xyz)*
657
+ - **Error handling**: *(xyz)*
658
+ - **State management**: *(xyz)*
659
+
660
+ ---
661
+
662
+ ## 8. Lịch Sử Cập Nhật
663
+
664
+ | Ngày | Người cập nhật | Lý do cập nhật |
665
+ |------|---------------|----------------|
666
+ | ${scan.scannedAt} | v-flow init | Tạo mới (U.0 auto-scan) |
667
+ `;
668
+ }
669
+
670
+ module.exports = {
671
+ // Constants (exported for testing)
672
+ EXT_MAP,
673
+ SKIP_DIRS,
674
+ FRAMEWORK_PATTERNS,
675
+ CODE_LANGUAGES,
676
+ TESTING_LABELS,
677
+ // Functions
678
+ walkDir,
679
+ isTestFile,
680
+ isConfigFile,
681
+ isEntrypoint,
682
+ readPackageJson,
683
+ readRequirementsTxt,
684
+ readPubspecYaml,
685
+ readCargoToml,
686
+ readGoMod,
687
+ detectFrameworks,
688
+ detectFromConfigFiles,
689
+ detectPackageManager,
690
+ scanCodebase,
691
+ generateContextMd,
692
+ };