forge-server 0.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.
Files changed (412) hide show
  1. package/.claude/hooks/worktree-create.sh +64 -0
  2. package/.claude/hooks/worktree-remove.sh +57 -0
  3. package/.claude/settings.local.json +29 -0
  4. package/.forge/knowledge/conventions.yaml +1 -0
  5. package/.forge/knowledge/decisions.yaml +1 -0
  6. package/.forge/knowledge/gotchas.yaml +1 -0
  7. package/.forge/knowledge/patterns.yaml +1 -0
  8. package/.forge/manifest.yaml +6 -0
  9. package/CLAUDE.md +144 -0
  10. package/bin/setup-forge.sh +132 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +553 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/context/codebase.d.ts +57 -0
  16. package/dist/context/codebase.d.ts.map +1 -0
  17. package/dist/context/codebase.js +301 -0
  18. package/dist/context/codebase.js.map +1 -0
  19. package/dist/context/injector.d.ts +147 -0
  20. package/dist/context/injector.d.ts.map +1 -0
  21. package/dist/context/injector.js +533 -0
  22. package/dist/context/injector.js.map +1 -0
  23. package/dist/context/memory.d.ts +32 -0
  24. package/dist/context/memory.d.ts.map +1 -0
  25. package/dist/context/memory.js +140 -0
  26. package/dist/context/memory.js.map +1 -0
  27. package/dist/context/session-index.d.ts +54 -0
  28. package/dist/context/session-index.d.ts.map +1 -0
  29. package/dist/context/session-index.js +265 -0
  30. package/dist/context/session-index.js.map +1 -0
  31. package/dist/context/session.d.ts +42 -0
  32. package/dist/context/session.d.ts.map +1 -0
  33. package/dist/context/session.js +121 -0
  34. package/dist/context/session.js.map +1 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +37 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/ingestion/chunker.d.ts +19 -0
  40. package/dist/ingestion/chunker.d.ts.map +1 -0
  41. package/dist/ingestion/chunker.js +189 -0
  42. package/dist/ingestion/chunker.js.map +1 -0
  43. package/dist/ingestion/embedder.d.ts +45 -0
  44. package/dist/ingestion/embedder.d.ts.map +1 -0
  45. package/dist/ingestion/embedder.js +152 -0
  46. package/dist/ingestion/embedder.js.map +1 -0
  47. package/dist/ingestion/git-analyzer.d.ts +77 -0
  48. package/dist/ingestion/git-analyzer.d.ts.map +1 -0
  49. package/dist/ingestion/git-analyzer.js +437 -0
  50. package/dist/ingestion/git-analyzer.js.map +1 -0
  51. package/dist/ingestion/indexer.d.ts +79 -0
  52. package/dist/ingestion/indexer.d.ts.map +1 -0
  53. package/dist/ingestion/indexer.js +766 -0
  54. package/dist/ingestion/indexer.js.map +1 -0
  55. package/dist/ingestion/markdown-chunker.d.ts +19 -0
  56. package/dist/ingestion/markdown-chunker.d.ts.map +1 -0
  57. package/dist/ingestion/markdown-chunker.js +243 -0
  58. package/dist/ingestion/markdown-chunker.js.map +1 -0
  59. package/dist/ingestion/markdown-knowledge.d.ts +21 -0
  60. package/dist/ingestion/markdown-knowledge.d.ts.map +1 -0
  61. package/dist/ingestion/markdown-knowledge.js +129 -0
  62. package/dist/ingestion/markdown-knowledge.js.map +1 -0
  63. package/dist/ingestion/parser.d.ts +20 -0
  64. package/dist/ingestion/parser.d.ts.map +1 -0
  65. package/dist/ingestion/parser.js +429 -0
  66. package/dist/ingestion/parser.js.map +1 -0
  67. package/dist/ingestion/watcher.d.ts +28 -0
  68. package/dist/ingestion/watcher.d.ts.map +1 -0
  69. package/dist/ingestion/watcher.js +147 -0
  70. package/dist/ingestion/watcher.js.map +1 -0
  71. package/dist/knowledge/hydrator.d.ts +37 -0
  72. package/dist/knowledge/hydrator.d.ts.map +1 -0
  73. package/dist/knowledge/hydrator.js +220 -0
  74. package/dist/knowledge/hydrator.js.map +1 -0
  75. package/dist/knowledge/registry.d.ts +129 -0
  76. package/dist/knowledge/registry.d.ts.map +1 -0
  77. package/dist/knowledge/registry.js +361 -0
  78. package/dist/knowledge/registry.js.map +1 -0
  79. package/dist/knowledge/search.d.ts +114 -0
  80. package/dist/knowledge/search.d.ts.map +1 -0
  81. package/dist/knowledge/search.js +428 -0
  82. package/dist/knowledge/search.js.map +1 -0
  83. package/dist/knowledge/store.d.ts +76 -0
  84. package/dist/knowledge/store.d.ts.map +1 -0
  85. package/dist/knowledge/store.js +230 -0
  86. package/dist/knowledge/store.js.map +1 -0
  87. package/dist/learning/confidence.d.ts +30 -0
  88. package/dist/learning/confidence.d.ts.map +1 -0
  89. package/dist/learning/confidence.js +165 -0
  90. package/dist/learning/confidence.js.map +1 -0
  91. package/dist/learning/patterns.d.ts +52 -0
  92. package/dist/learning/patterns.d.ts.map +1 -0
  93. package/dist/learning/patterns.js +290 -0
  94. package/dist/learning/patterns.js.map +1 -0
  95. package/dist/learning/trajectory.d.ts +55 -0
  96. package/dist/learning/trajectory.d.ts.map +1 -0
  97. package/dist/learning/trajectory.js +200 -0
  98. package/dist/learning/trajectory.js.map +1 -0
  99. package/dist/memory/memory-compat.d.ts +100 -0
  100. package/dist/memory/memory-compat.d.ts.map +1 -0
  101. package/dist/memory/memory-compat.js +146 -0
  102. package/dist/memory/memory-compat.js.map +1 -0
  103. package/dist/memory/observation-store.d.ts +57 -0
  104. package/dist/memory/observation-store.d.ts.map +1 -0
  105. package/dist/memory/observation-store.js +154 -0
  106. package/dist/memory/observation-store.js.map +1 -0
  107. package/dist/memory/session-tracker.d.ts +81 -0
  108. package/dist/memory/session-tracker.d.ts.map +1 -0
  109. package/dist/memory/session-tracker.js +262 -0
  110. package/dist/memory/session-tracker.js.map +1 -0
  111. package/dist/pipeline/engine.d.ts +179 -0
  112. package/dist/pipeline/engine.d.ts.map +1 -0
  113. package/dist/pipeline/engine.js +691 -0
  114. package/dist/pipeline/engine.js.map +1 -0
  115. package/dist/pipeline/events.d.ts +54 -0
  116. package/dist/pipeline/events.d.ts.map +1 -0
  117. package/dist/pipeline/events.js +157 -0
  118. package/dist/pipeline/events.js.map +1 -0
  119. package/dist/pipeline/parallel.d.ts +83 -0
  120. package/dist/pipeline/parallel.d.ts.map +1 -0
  121. package/dist/pipeline/parallel.js +277 -0
  122. package/dist/pipeline/parallel.js.map +1 -0
  123. package/dist/pipeline/state-machine.d.ts +65 -0
  124. package/dist/pipeline/state-machine.d.ts.map +1 -0
  125. package/dist/pipeline/state-machine.js +176 -0
  126. package/dist/pipeline/state-machine.js.map +1 -0
  127. package/dist/query/graph-queries.d.ts +84 -0
  128. package/dist/query/graph-queries.d.ts.map +1 -0
  129. package/dist/query/graph-queries.js +216 -0
  130. package/dist/query/graph-queries.js.map +1 -0
  131. package/dist/query/hybrid-search.d.ts +34 -0
  132. package/dist/query/hybrid-search.d.ts.map +1 -0
  133. package/dist/query/hybrid-search.js +263 -0
  134. package/dist/query/hybrid-search.js.map +1 -0
  135. package/dist/query/intent-detector.d.ts +35 -0
  136. package/dist/query/intent-detector.d.ts.map +1 -0
  137. package/dist/query/intent-detector.js +115 -0
  138. package/dist/query/intent-detector.js.map +1 -0
  139. package/dist/query/ranking.d.ts +57 -0
  140. package/dist/query/ranking.d.ts.map +1 -0
  141. package/dist/query/ranking.js +109 -0
  142. package/dist/query/ranking.js.map +1 -0
  143. package/dist/server.d.ts +3 -0
  144. package/dist/server.d.ts.map +1 -0
  145. package/dist/server.js +291 -0
  146. package/dist/server.js.map +1 -0
  147. package/dist/storage/falkordb-store.d.ts +73 -0
  148. package/dist/storage/falkordb-store.d.ts.map +1 -0
  149. package/dist/storage/falkordb-store.js +346 -0
  150. package/dist/storage/falkordb-store.js.map +1 -0
  151. package/dist/storage/file-cache.d.ts +32 -0
  152. package/dist/storage/file-cache.d.ts.map +1 -0
  153. package/dist/storage/file-cache.js +115 -0
  154. package/dist/storage/file-cache.js.map +1 -0
  155. package/dist/storage/interfaces.d.ts +151 -0
  156. package/dist/storage/interfaces.d.ts.map +1 -0
  157. package/dist/storage/interfaces.js +7 -0
  158. package/dist/storage/interfaces.js.map +1 -0
  159. package/dist/storage/qdrant-store.d.ts +110 -0
  160. package/dist/storage/qdrant-store.d.ts.map +1 -0
  161. package/dist/storage/qdrant-store.js +467 -0
  162. package/dist/storage/qdrant-store.js.map +1 -0
  163. package/dist/storage/schema.d.ts +4 -0
  164. package/dist/storage/schema.d.ts.map +1 -0
  165. package/dist/storage/schema.js +136 -0
  166. package/dist/storage/schema.js.map +1 -0
  167. package/dist/storage/sqlite.d.ts +35 -0
  168. package/dist/storage/sqlite.d.ts.map +1 -0
  169. package/dist/storage/sqlite.js +132 -0
  170. package/dist/storage/sqlite.js.map +1 -0
  171. package/dist/tools/collaboration-tools.d.ts +111 -0
  172. package/dist/tools/collaboration-tools.d.ts.map +1 -0
  173. package/dist/tools/collaboration-tools.js +174 -0
  174. package/dist/tools/collaboration-tools.js.map +1 -0
  175. package/dist/tools/context-tools.d.ts +293 -0
  176. package/dist/tools/context-tools.d.ts.map +1 -0
  177. package/dist/tools/context-tools.js +437 -0
  178. package/dist/tools/context-tools.js.map +1 -0
  179. package/dist/tools/graph-tools.d.ts +129 -0
  180. package/dist/tools/graph-tools.d.ts.map +1 -0
  181. package/dist/tools/graph-tools.js +237 -0
  182. package/dist/tools/graph-tools.js.map +1 -0
  183. package/dist/tools/ingestion-tools.d.ts +96 -0
  184. package/dist/tools/ingestion-tools.d.ts.map +1 -0
  185. package/dist/tools/ingestion-tools.js +90 -0
  186. package/dist/tools/ingestion-tools.js.map +1 -0
  187. package/dist/tools/learning-tools.d.ts +168 -0
  188. package/dist/tools/learning-tools.d.ts.map +1 -0
  189. package/dist/tools/learning-tools.js +158 -0
  190. package/dist/tools/learning-tools.js.map +1 -0
  191. package/dist/tools/memory-tools.d.ts +183 -0
  192. package/dist/tools/memory-tools.d.ts.map +1 -0
  193. package/dist/tools/memory-tools.js +197 -0
  194. package/dist/tools/memory-tools.js.map +1 -0
  195. package/dist/tools/phase-tools.d.ts +954 -0
  196. package/dist/tools/phase-tools.d.ts.map +1 -0
  197. package/dist/tools/phase-tools.js +1215 -0
  198. package/dist/tools/phase-tools.js.map +1 -0
  199. package/dist/tools/pipeline-tools.d.ts +140 -0
  200. package/dist/tools/pipeline-tools.d.ts.map +1 -0
  201. package/dist/tools/pipeline-tools.js +162 -0
  202. package/dist/tools/pipeline-tools.js.map +1 -0
  203. package/dist/tools/registration-tools.d.ts +220 -0
  204. package/dist/tools/registration-tools.d.ts.map +1 -0
  205. package/dist/tools/registration-tools.js +391 -0
  206. package/dist/tools/registration-tools.js.map +1 -0
  207. package/dist/util/circuit-breaker.d.ts +75 -0
  208. package/dist/util/circuit-breaker.d.ts.map +1 -0
  209. package/dist/util/circuit-breaker.js +159 -0
  210. package/dist/util/circuit-breaker.js.map +1 -0
  211. package/dist/util/config.d.ts +23 -0
  212. package/dist/util/config.d.ts.map +1 -0
  213. package/dist/util/config.js +164 -0
  214. package/dist/util/config.js.map +1 -0
  215. package/dist/util/logger.d.ts +13 -0
  216. package/dist/util/logger.d.ts.map +1 -0
  217. package/dist/util/logger.js +45 -0
  218. package/dist/util/logger.js.map +1 -0
  219. package/dist/util/token-counter.d.ts +24 -0
  220. package/dist/util/token-counter.d.ts.map +1 -0
  221. package/dist/util/token-counter.js +48 -0
  222. package/dist/util/token-counter.js.map +1 -0
  223. package/dist/util/types.d.ts +525 -0
  224. package/dist/util/types.d.ts.map +1 -0
  225. package/dist/util/types.js +5 -0
  226. package/dist/util/types.js.map +1 -0
  227. package/docker-compose.yml +20 -0
  228. package/docs/plans/2026-02-27-swarm-coordination/architecture.md +203 -0
  229. package/docs/plans/2026-02-27-swarm-coordination/vision.md +57 -0
  230. package/docs/plans/completed/2026-02-26-forge-plugin-bundling/architecture.md +1 -0
  231. package/docs/plans/completed/2026-02-26-forge-plugin-bundling/vision.md +300 -0
  232. package/docs/plans/completed/2026-02-27-forge-swarm-learning/architecture.md +480 -0
  233. package/docs/plans/completed/2026-02-27-forge-swarm-learning/verification-checklist.md +462 -0
  234. package/docs/plans/completed/2026-02-27-git-history-atlassian/git-jira-plan.md +181 -0
  235. package/package.json +39 -0
  236. package/plugin/.claude-plugin/plugin.json +8 -0
  237. package/plugin/.mcp.json +15 -0
  238. package/plugin/README.md +134 -0
  239. package/plugin/agents/architect.md +367 -0
  240. package/plugin/agents/backend-specialist.md +263 -0
  241. package/plugin/agents/brainstormer.md +122 -0
  242. package/plugin/agents/data-specialist.md +266 -0
  243. package/plugin/agents/designer.md +408 -0
  244. package/plugin/agents/frontend-specialist.md +241 -0
  245. package/plugin/agents/inspector.md +406 -0
  246. package/plugin/agents/knowledge-keeper.md +443 -0
  247. package/plugin/agents/platform-engineer.md +326 -0
  248. package/plugin/agents/product-manager.md +268 -0
  249. package/plugin/agents/product-owner.md +438 -0
  250. package/plugin/agents/pulse-checker.md +73 -0
  251. package/plugin/agents/qa-strategist.md +500 -0
  252. package/plugin/agents/self-improver.md +310 -0
  253. package/plugin/agents/strategist.md +360 -0
  254. package/plugin/agents/supervisor.md +380 -0
  255. package/plugin/commands/brainstorm.md +25 -0
  256. package/plugin/commands/forge.md +88 -0
  257. package/plugin/docs/atlassian-integration.md +110 -0
  258. package/plugin/docs/workflow.md +126 -0
  259. package/plugin/skills/agent-development/.skillfish.json +10 -0
  260. package/plugin/skills/agent-development/SKILL.md +415 -0
  261. package/plugin/skills/agent-development/examples/agent-creation-prompt.md +238 -0
  262. package/plugin/skills/agent-development/examples/complete-agent-examples.md +427 -0
  263. package/plugin/skills/agent-development/references/agent-creation-system-prompt.md +207 -0
  264. package/plugin/skills/agent-development/references/system-prompt-design.md +411 -0
  265. package/plugin/skills/agent-development/references/triggering-examples.md +491 -0
  266. package/plugin/skills/agent-development/scripts/validate-agent.sh +217 -0
  267. package/plugin/skills/agent-handoff/SKILL.md +335 -0
  268. package/plugin/skills/anti-stub/SKILL.md +317 -0
  269. package/plugin/skills/brainstorm/SKILL.md +31 -0
  270. package/plugin/skills/debugging/SKILL.md +276 -0
  271. package/plugin/skills/fix/SKILL.md +62 -0
  272. package/plugin/skills/frontend-design/.skillfish.json +10 -0
  273. package/plugin/skills/frontend-design/SKILL.md +42 -0
  274. package/plugin/skills/gotchas/SKILL.md +61 -0
  275. package/plugin/skills/graph-orchestrator/SKILL.md +38 -0
  276. package/plugin/skills/history/SKILL.md +58 -0
  277. package/plugin/skills/impact/SKILL.md +59 -0
  278. package/plugin/skills/implementation-execution/SKILL.md +291 -0
  279. package/plugin/skills/index-repo/SKILL.md +55 -0
  280. package/plugin/skills/interviewing/SKILL.md +225 -0
  281. package/plugin/skills/knowledge-curation/SKILL.md +393 -0
  282. package/plugin/skills/learn/SKILL.md +69 -0
  283. package/plugin/skills/mcp-integration/.skillfish.json +10 -0
  284. package/plugin/skills/mcp-integration/SKILL.md +554 -0
  285. package/plugin/skills/mcp-integration/examples/http-server.json +20 -0
  286. package/plugin/skills/mcp-integration/examples/sse-server.json +19 -0
  287. package/plugin/skills/mcp-integration/examples/stdio-server.json +26 -0
  288. package/plugin/skills/mcp-integration/references/authentication.md +549 -0
  289. package/plugin/skills/mcp-integration/references/server-types.md +536 -0
  290. package/plugin/skills/mcp-integration/references/tool-usage.md +538 -0
  291. package/plugin/skills/nestjs/.skillfish.json +10 -0
  292. package/plugin/skills/nestjs/SKILL.md +669 -0
  293. package/plugin/skills/nestjs/drizzle-reference.md +1894 -0
  294. package/plugin/skills/nestjs/reference.md +1447 -0
  295. package/plugin/skills/nestjs/workflow-optimization.md +229 -0
  296. package/plugin/skills/parallel-dispatch/SKILL.md +308 -0
  297. package/plugin/skills/project-discovery/SKILL.md +304 -0
  298. package/plugin/skills/search/SKILL.md +56 -0
  299. package/plugin/skills/security-audit/SKILL.md +362 -0
  300. package/plugin/skills/skill-development/.skillfish.json +10 -0
  301. package/plugin/skills/skill-development/SKILL.md +637 -0
  302. package/plugin/skills/skill-development/references/skill-creator-original.md +209 -0
  303. package/plugin/skills/tdd/SKILL.md +273 -0
  304. package/plugin/skills/terminal-presentation/SKILL.md +395 -0
  305. package/plugin/skills/test-strategy/SKILL.md +365 -0
  306. package/plugin/skills/verification-protocol/SKILL.md +256 -0
  307. package/plugin/skills/visual-explainer/CHANGELOG.md +97 -0
  308. package/plugin/skills/visual-explainer/LICENSE +21 -0
  309. package/plugin/skills/visual-explainer/README.md +137 -0
  310. package/plugin/skills/visual-explainer/SKILL.md +352 -0
  311. package/plugin/skills/visual-explainer/banner.png +0 -0
  312. package/plugin/skills/visual-explainer/package.json +11 -0
  313. package/plugin/skills/visual-explainer/prompts/diff-review.md +68 -0
  314. package/plugin/skills/visual-explainer/prompts/fact-check.md +63 -0
  315. package/plugin/skills/visual-explainer/prompts/generate-slides.md +18 -0
  316. package/plugin/skills/visual-explainer/prompts/generate-web-diagram.md +10 -0
  317. package/plugin/skills/visual-explainer/prompts/plan-review.md +86 -0
  318. package/plugin/skills/visual-explainer/prompts/project-recap.md +61 -0
  319. package/plugin/skills/visual-explainer/references/css-patterns.md +1188 -0
  320. package/plugin/skills/visual-explainer/references/libraries.md +470 -0
  321. package/plugin/skills/visual-explainer/references/responsive-nav.md +212 -0
  322. package/plugin/skills/visual-explainer/references/slide-patterns.md +1403 -0
  323. package/plugin/skills/visual-explainer/templates/architecture.html +596 -0
  324. package/plugin/skills/visual-explainer/templates/data-table.html +540 -0
  325. package/plugin/skills/visual-explainer/templates/mermaid-flowchart.html +435 -0
  326. package/plugin/skills/visual-explainer/templates/slide-deck.html +913 -0
  327. package/src/cli.ts +655 -0
  328. package/src/context/.gitkeep +0 -0
  329. package/src/context/codebase.ts +393 -0
  330. package/src/context/injector.ts +797 -0
  331. package/src/context/memory.ts +187 -0
  332. package/src/context/session-index.ts +327 -0
  333. package/src/context/session.ts +152 -0
  334. package/src/index.ts +47 -0
  335. package/src/ingestion/.gitkeep +0 -0
  336. package/src/ingestion/chunker.ts +277 -0
  337. package/src/ingestion/embedder.ts +167 -0
  338. package/src/ingestion/git-analyzer.ts +545 -0
  339. package/src/ingestion/indexer.ts +984 -0
  340. package/src/ingestion/markdown-chunker.ts +337 -0
  341. package/src/ingestion/markdown-knowledge.ts +175 -0
  342. package/src/ingestion/parser.ts +475 -0
  343. package/src/ingestion/watcher.ts +182 -0
  344. package/src/knowledge/.gitkeep +0 -0
  345. package/src/knowledge/hydrator.ts +246 -0
  346. package/src/knowledge/registry.ts +463 -0
  347. package/src/knowledge/search.ts +565 -0
  348. package/src/knowledge/store.ts +262 -0
  349. package/src/learning/.gitkeep +0 -0
  350. package/src/learning/confidence.ts +193 -0
  351. package/src/learning/patterns.ts +360 -0
  352. package/src/learning/trajectory.ts +268 -0
  353. package/src/memory/.gitkeep +0 -0
  354. package/src/memory/memory-compat.ts +233 -0
  355. package/src/memory/observation-store.ts +224 -0
  356. package/src/memory/session-tracker.ts +332 -0
  357. package/src/pipeline/.gitkeep +0 -0
  358. package/src/pipeline/engine.ts +1139 -0
  359. package/src/pipeline/events.ts +253 -0
  360. package/src/pipeline/parallel.ts +394 -0
  361. package/src/pipeline/state-machine.ts +199 -0
  362. package/src/query/.gitkeep +0 -0
  363. package/src/query/graph-queries.ts +262 -0
  364. package/src/query/hybrid-search.ts +337 -0
  365. package/src/query/intent-detector.ts +131 -0
  366. package/src/query/ranking.ts +161 -0
  367. package/src/server.ts +352 -0
  368. package/src/storage/.gitkeep +0 -0
  369. package/src/storage/falkordb-store.ts +388 -0
  370. package/src/storage/file-cache.ts +141 -0
  371. package/src/storage/interfaces.ts +201 -0
  372. package/src/storage/qdrant-store.ts +557 -0
  373. package/src/storage/schema.ts +139 -0
  374. package/src/storage/sqlite.ts +168 -0
  375. package/src/tools/.gitkeep +0 -0
  376. package/src/tools/collaboration-tools.ts +208 -0
  377. package/src/tools/context-tools.ts +493 -0
  378. package/src/tools/graph-tools.ts +295 -0
  379. package/src/tools/ingestion-tools.ts +122 -0
  380. package/src/tools/learning-tools.ts +181 -0
  381. package/src/tools/memory-tools.ts +234 -0
  382. package/src/tools/phase-tools.ts +1452 -0
  383. package/src/tools/pipeline-tools.ts +188 -0
  384. package/src/tools/registration-tools.ts +450 -0
  385. package/src/util/.gitkeep +0 -0
  386. package/src/util/circuit-breaker.ts +193 -0
  387. package/src/util/config.ts +177 -0
  388. package/src/util/logger.ts +53 -0
  389. package/src/util/token-counter.ts +52 -0
  390. package/src/util/types.ts +710 -0
  391. package/tests/context/.gitkeep +0 -0
  392. package/tests/integration/.gitkeep +0 -0
  393. package/tests/knowledge/.gitkeep +0 -0
  394. package/tests/learning/.gitkeep +0 -0
  395. package/tests/pipeline/.gitkeep +0 -0
  396. package/tests/tools/.gitkeep +0 -0
  397. package/tsconfig.json +21 -0
  398. package/vitest.config.ts +10 -0
  399. package/vscode-extension/.vscodeignore +7 -0
  400. package/vscode-extension/README.md +43 -0
  401. package/vscode-extension/out/edge-collector.js +274 -0
  402. package/vscode-extension/out/edge-collector.js.map +1 -0
  403. package/vscode-extension/out/extension.js +264 -0
  404. package/vscode-extension/out/extension.js.map +1 -0
  405. package/vscode-extension/out/forge-client.js +318 -0
  406. package/vscode-extension/out/forge-client.js.map +1 -0
  407. package/vscode-extension/package-lock.json +59 -0
  408. package/vscode-extension/package.json +71 -0
  409. package/vscode-extension/src/edge-collector.ts +320 -0
  410. package/vscode-extension/src/extension.ts +269 -0
  411. package/vscode-extension/src/forge-client.ts +364 -0
  412. package/vscode-extension/tsconfig.json +19 -0
package/src/cli.ts ADDED
@@ -0,0 +1,655 @@
1
+ #!/usr/bin/env node
2
+ // cli.ts — B20
3
+ // Thin CLI for dk-forge-server operations.
4
+ //
5
+ // Commands:
6
+ // forge init --name <name> --stack <tags...> [--sharing team|private|public] [--org <org>] [--path <path>]
7
+ // forge register [--path <path>]
8
+ // forge status [--project <id>] [--all]
9
+ // forge knowledge list|search|migrate|sync [--query <text>] [--category gotcha|pattern|decision|convention]
10
+ // forge repos list|info|remove [--id <repo_id>]
11
+ //
12
+ // For init/register: operates directly on filesystem + SQLite, no server needed.
13
+ // For status/knowledge/repos: reads SQLite directly (no server connection required).
14
+
15
+ import { resolve, join } from 'node:path';
16
+ import { existsSync } from 'node:fs';
17
+ import { homedir } from 'node:os';
18
+ import { PipelineDB } from './storage/sqlite.js';
19
+ import { RepoRegistry } from './knowledge/registry.js';
20
+ import { KnowledgeYamlStore } from './knowledge/store.js';
21
+ import { QdrantVectorStore } from './storage/qdrant-store.js';
22
+ import { FalkorDBGraphStore } from './storage/falkordb-store.js';
23
+ import { LRUFileContentCache } from './storage/file-cache.js';
24
+ import { fullIndex } from './ingestion/indexer.js';
25
+ import type { RepoConfig } from './util/types.js';
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Argument parsing
29
+ // ---------------------------------------------------------------------------
30
+
31
+ interface ParsedArgs {
32
+ command: string;
33
+ subcommand: string;
34
+ flags: Record<string, string | string[] | boolean | true>;
35
+ positional: string[];
36
+ }
37
+
38
+ function parseArgs(argv: string[]): ParsedArgs {
39
+ // argv[0] = 'node', argv[1] = 'cli.js', argv[2] = command...
40
+ const args = argv.slice(2);
41
+ const command = args[0] ?? '';
42
+ const subcommand = args[1]?.startsWith('--') ? '' : (args[1] ?? '');
43
+ const startIdx = subcommand ? 2 : 1;
44
+
45
+ const flags: Record<string, string | string[] | boolean | true> = {};
46
+ const positional: string[] = [];
47
+
48
+ let i = startIdx;
49
+ while (i < args.length) {
50
+ const arg = args[i]!;
51
+ if (arg.startsWith('--')) {
52
+ const key = arg.slice(2);
53
+ const next = args[i + 1];
54
+
55
+ if (!next || next.startsWith('--')) {
56
+ flags[key] = true;
57
+ i += 1;
58
+ } else if (key === 'stack') {
59
+ // --stack can have multiple values before the next flag
60
+ const values: string[] = [];
61
+ let j = i + 1;
62
+ while (j < args.length && !args[j]!.startsWith('--')) {
63
+ values.push(args[j]!);
64
+ j++;
65
+ }
66
+ flags[key] = values;
67
+ i = j;
68
+ } else {
69
+ flags[key] = next;
70
+ i += 2;
71
+ }
72
+ } else {
73
+ positional.push(arg);
74
+ i += 1;
75
+ }
76
+ }
77
+
78
+ return { command, subcommand, flags, positional };
79
+ }
80
+
81
+ function getFlag(flags: Record<string, unknown>, key: string): string | undefined {
82
+ const val = flags[key];
83
+ return typeof val === 'string' ? val : undefined;
84
+ }
85
+
86
+ function getFlagArray(flags: Record<string, unknown>, key: string): string[] {
87
+ const val = flags[key];
88
+ if (Array.isArray(val)) return val as string[];
89
+ if (typeof val === 'string') return [val];
90
+ return [];
91
+ }
92
+
93
+ function getFlagBool(flags: Record<string, unknown>, key: string): boolean {
94
+ return flags[key] === true;
95
+ }
96
+
97
+ // ---------------------------------------------------------------------------
98
+ // Database helper — opens the shared pipeline.db
99
+ // ---------------------------------------------------------------------------
100
+
101
+ function openDB(): PipelineDB {
102
+ const dbPath = process.env['FORGE_DB_PATH'] ?? join(homedir(), '.forge', 'pipeline.db');
103
+ const db = new PipelineDB(dbPath);
104
+ db.initialize();
105
+ return db;
106
+ }
107
+
108
+ // ---------------------------------------------------------------------------
109
+ // Output helpers
110
+ // ---------------------------------------------------------------------------
111
+
112
+ function print(obj: unknown): void {
113
+ console.log(JSON.stringify(obj, null, 2));
114
+ }
115
+
116
+ function printError(message: string, exitCode = 1): never {
117
+ console.error(`Error: ${message}`);
118
+ process.exit(exitCode);
119
+ }
120
+
121
+ function printHelp(): void {
122
+ console.log(`
123
+ dk-forge-server CLI
124
+
125
+ Usage:
126
+ forge init --name <name> --stack <tag...> [--sharing team|private|public] [--org <org>] [--path <dir>]
127
+ forge register [--path <dir>]
128
+ forge index [--path <dir>] [--repo-id <id>] [--force]
129
+ forge status [--project <id>] [--all]
130
+ forge knowledge list [--category gotcha|pattern|decision|convention] [--path <dir>]
131
+ forge knowledge search --query <text> [--category gotcha|pattern|decision|convention] [--path <dir>]
132
+ forge repos list
133
+ forge repos info --id <repo_id>
134
+ forge repos remove --id <repo_id>
135
+
136
+ Options:
137
+ --name Project name for forge init
138
+ --stack Space-separated stack tags (nestjs drizzle react postgresql)
139
+ --sharing Sharing mode: team | private | public (default: private)
140
+ --org Organization identifier
141
+ --path Target directory (default: current directory)
142
+ --repo-id Repo ID (for index command)
143
+ --force Force re-index even if files haven't changed
144
+ --project Project ID for status command
145
+ --all Show all projects (for status command)
146
+ --query Search query (for knowledge search)
147
+ --category Knowledge category filter
148
+ --id Repo ID (for repos info/remove)
149
+
150
+ Examples:
151
+ forge init --name my-app --stack nestjs drizzle --sharing team --org acme
152
+ forge register
153
+ forge index --path /path/to/repo
154
+ forge status --all
155
+ forge knowledge list --category gotcha
156
+ forge knowledge search --query "NestJS circular dependency"
157
+ forge repos list
158
+ `.trim());
159
+ }
160
+
161
+ // ---------------------------------------------------------------------------
162
+ // Commands
163
+ // ---------------------------------------------------------------------------
164
+
165
+ async function cmdInit(flags: Record<string, unknown>): Promise<void> {
166
+ const name = getFlag(flags, 'name');
167
+ if (!name) printError('--name is required for init');
168
+
169
+ const stack = getFlagArray(flags, 'stack');
170
+ if (stack.length === 0) printError('--stack requires at least one tag (e.g., --stack nestjs drizzle)');
171
+
172
+ const sharing = (getFlag(flags, 'sharing') ?? 'private') as 'team' | 'private' | 'public';
173
+ const org = getFlag(flags, 'org');
174
+ const targetPath = resolve(getFlag(flags, 'path') ?? process.cwd());
175
+
176
+ const db = openDB();
177
+ const registry = new RepoRegistry(db);
178
+
179
+ try {
180
+ const manifest = { name, stack, sharing, org, repo_id_override: null as string | null };
181
+ const { forgePath, filesCreated } = registry.initForge(targetPath, manifest);
182
+
183
+ print({
184
+ success: true,
185
+ forge_dir: forgePath,
186
+ manifest_path: join(forgePath, 'manifest.yaml'),
187
+ knowledge_dir: join(forgePath, 'knowledge'),
188
+ files_created: filesCreated,
189
+ message: `Initialized forge in ${forgePath}. Run 'forge register' to register this repo.`,
190
+ });
191
+ } catch (err) {
192
+ printError(String(err));
193
+ } finally {
194
+ db.close();
195
+ }
196
+ }
197
+
198
+ async function cmdRegister(flags: Record<string, unknown>): Promise<void> {
199
+ const targetPath = resolve(getFlag(flags, 'path') ?? process.cwd());
200
+ const forgePath = join(targetPath, '.forge');
201
+
202
+ if (!existsSync(join(forgePath, 'manifest.yaml'))) {
203
+ printError(
204
+ `No .forge/manifest.yaml found at ${forgePath}.\n` +
205
+ `Run 'forge init --name <name> --stack <tags...>' first.`,
206
+ );
207
+ }
208
+
209
+ const db = openDB();
210
+ const registry = new RepoRegistry(db);
211
+
212
+ try {
213
+ const result = await registry.register(targetPath);
214
+ print({
215
+ success: true,
216
+ ...result,
217
+ message: result.newly_registered
218
+ ? `Registered repo '${result.name}' (id: ${result.repo_id})`
219
+ : `Updated repo '${result.name}' (id: ${result.repo_id}) — last_seen_at refreshed`,
220
+ });
221
+ } catch (err) {
222
+ printError(String(err));
223
+ } finally {
224
+ db.close();
225
+ }
226
+ }
227
+
228
+ function cmdStatus(flags: Record<string, unknown>): void {
229
+ const db = openDB();
230
+
231
+ try {
232
+ const projectId = getFlag(flags, 'project');
233
+ const showAll = getFlagBool(flags, 'all');
234
+
235
+ if (projectId) {
236
+ // Show single project
237
+ const row = db.get<{
238
+ id: string;
239
+ name: string;
240
+ current_phase: string;
241
+ status: string;
242
+ tier: string;
243
+ created_at: number;
244
+ updated_at: number;
245
+ }>(`SELECT * FROM projects WHERE id = ?`, [projectId]);
246
+
247
+ if (!row) {
248
+ printError(`Project not found: ${projectId}`);
249
+ }
250
+
251
+ print({
252
+ project_id: row.id,
253
+ name: row.name,
254
+ current_phase: row.current_phase,
255
+ status: row.status,
256
+ tier: row.tier,
257
+ created_at: new Date(row.created_at).toISOString(),
258
+ updated_at: new Date(row.updated_at).toISOString(),
259
+ });
260
+ } else {
261
+ // Show all active projects (or all if --all)
262
+ const whereClause = showAll ? '' : `WHERE status = 'active'`;
263
+ const rows = db.all<{
264
+ id: string;
265
+ name: string;
266
+ current_phase: string;
267
+ status: string;
268
+ tier: string;
269
+ created_at: number;
270
+ updated_at: number;
271
+ }>(`SELECT id, name, current_phase, status, tier, created_at, updated_at FROM projects ${whereClause} ORDER BY updated_at DESC`);
272
+
273
+ if (rows.length === 0) {
274
+ print({ projects: [], message: showAll ? 'No projects found.' : 'No active projects. Use --all to see all projects.' });
275
+ return;
276
+ }
277
+
278
+ print({
279
+ projects: rows.map(r => ({
280
+ project_id: r.id,
281
+ name: r.name,
282
+ current_phase: r.current_phase,
283
+ status: r.status,
284
+ tier: r.tier,
285
+ updated_at: new Date(r.updated_at).toISOString(),
286
+ })),
287
+ count: rows.length,
288
+ });
289
+ }
290
+ } finally {
291
+ db.close();
292
+ }
293
+ }
294
+
295
+ function cmdKnowledge(subcommand: string, flags: Record<string, unknown>): void {
296
+ const targetPath = resolve(getFlag(flags, 'path') ?? process.cwd());
297
+ const forgePath = join(targetPath, '.forge');
298
+
299
+ if (!existsSync(forgePath)) {
300
+ printError(`No .forge/ directory found at ${targetPath}. Run 'forge init' first.`);
301
+ }
302
+
303
+ const yamlStore = new KnowledgeYamlStore(forgePath);
304
+ const category = getFlag(flags, 'category') as 'gotcha' | 'pattern' | 'decision' | 'convention' | undefined;
305
+
306
+ switch (subcommand) {
307
+ case 'list': {
308
+ const items = category ? yamlStore.readCategory(category) : yamlStore.readAll();
309
+ print({
310
+ category: category ?? 'all',
311
+ count: items.length,
312
+ items: items.map(i => ({
313
+ id: i.id,
314
+ title: i.title,
315
+ confidence: i.confidence,
316
+ stack_tags: i.stack_tags,
317
+ source: i.source,
318
+ })),
319
+ });
320
+ break;
321
+ }
322
+
323
+ case 'search': {
324
+ const query = getFlag(flags, 'query');
325
+ if (!query) printError('--query is required for knowledge search');
326
+
327
+ const queryLower = query.toLowerCase();
328
+ const items = category ? yamlStore.readCategory(category) : yamlStore.readAll();
329
+
330
+ // Simple text search over title + content
331
+ const matches = items.filter(i =>
332
+ i.title.toLowerCase().includes(queryLower) ||
333
+ i.content.toLowerCase().includes(queryLower),
334
+ );
335
+
336
+ print({
337
+ query,
338
+ category: category ?? 'all',
339
+ count: matches.length,
340
+ items: matches.map(i => ({
341
+ id: i.id,
342
+ title: i.title,
343
+ content: i.content.slice(0, 300),
344
+ confidence: i.confidence,
345
+ stack_tags: i.stack_tags,
346
+ })),
347
+ note: 'This is local YAML text search. For semantic search, use forge.search_knowledge via MCP.',
348
+ });
349
+ break;
350
+ }
351
+
352
+ case 'sync': {
353
+ // Re-sync all knowledge items by re-initializing missing files
354
+ yamlStore.initialize();
355
+ const all = yamlStore.readAll();
356
+ print({
357
+ success: true,
358
+ message: `Knowledge files verified/created in ${forgePath}/knowledge/`,
359
+ total_items: all.length,
360
+ });
361
+ break;
362
+ }
363
+
364
+ case 'migrate': {
365
+ // Placeholder: migration from CLAUDE.md is handled by a migration module
366
+ print({
367
+ success: false,
368
+ message: 'Knowledge migration from CLAUDE.md is not yet implemented in the CLI. Use forge.register to sync YAML to Qdrant.',
369
+ });
370
+ break;
371
+ }
372
+
373
+ default:
374
+ printError(`Unknown knowledge subcommand: '${subcommand}'. Available: list, search, sync, migrate`);
375
+ }
376
+ }
377
+
378
+ function cmdRepos(subcommand: string, flags: Record<string, unknown>): void {
379
+ const db = openDB();
380
+
381
+ try {
382
+ switch (subcommand) {
383
+ case 'list': {
384
+ const rows = db.all<{
385
+ id: string;
386
+ name: string;
387
+ path: string;
388
+ remote: string | null;
389
+ stack: string;
390
+ sharing: string;
391
+ last_seen_at: number;
392
+ }>(`SELECT id, name, path, remote, stack, sharing, last_seen_at FROM repos ORDER BY last_seen_at DESC`);
393
+
394
+ print({
395
+ count: rows.length,
396
+ repos: rows.map(r => {
397
+ let stack: string[] = [];
398
+ try { stack = JSON.parse(r.stack) as string[]; } catch {}
399
+ return {
400
+ id: r.id,
401
+ name: r.name,
402
+ path: r.path,
403
+ remote: r.remote,
404
+ stack,
405
+ sharing: r.sharing,
406
+ last_seen: new Date(r.last_seen_at).toISOString(),
407
+ };
408
+ }),
409
+ });
410
+ break;
411
+ }
412
+
413
+ case 'info': {
414
+ const id = getFlag(flags, 'id');
415
+ if (!id) printError('--id is required for repos info');
416
+
417
+ const row = db.get<{
418
+ id: string;
419
+ name: string;
420
+ path: string;
421
+ remote: string | null;
422
+ stack: string;
423
+ sharing: string;
424
+ org: string | null;
425
+ registered_at: number;
426
+ last_seen_at: number;
427
+ manifest_hash: string | null;
428
+ }>(`SELECT * FROM repos WHERE id = ?`, [id]);
429
+
430
+ if (!row) printError(`Repo not found: ${id}`);
431
+
432
+ let stack: string[] = [];
433
+ try { stack = JSON.parse(row.stack) as string[]; } catch {}
434
+
435
+ // Count projects for this repo
436
+ const projectCount = db.get<{ cnt: number }>(
437
+ `SELECT COUNT(*) as cnt FROM projects WHERE repo_id = ?`,
438
+ [id],
439
+ );
440
+
441
+ print({
442
+ id: row.id,
443
+ name: row.name,
444
+ path: row.path,
445
+ remote: row.remote,
446
+ stack,
447
+ sharing: row.sharing,
448
+ org: row.org,
449
+ registered_at: new Date(row.registered_at).toISOString(),
450
+ last_seen_at: new Date(row.last_seen_at).toISOString(),
451
+ manifest_hash: row.manifest_hash,
452
+ project_count: projectCount?.cnt ?? 0,
453
+ });
454
+ break;
455
+ }
456
+
457
+ case 'remove': {
458
+ const id = getFlag(flags, 'id');
459
+ if (!id) printError('--id is required for repos remove');
460
+
461
+ const existing = db.get<{ id: string }>(`SELECT id FROM repos WHERE id = ?`, [id]);
462
+ if (!existing) printError(`Repo not found: ${id}`);
463
+
464
+ db.run(`DELETE FROM repos WHERE id = ?`, [id]);
465
+
466
+ print({
467
+ success: true,
468
+ message: `Repo ${id} removed from registry. Note: Qdrant knowledge items are NOT removed. Restart the server to clear them.`,
469
+ });
470
+ break;
471
+ }
472
+
473
+ default:
474
+ printError(`Unknown repos subcommand: '${subcommand}'. Available: list, info, remove`);
475
+ }
476
+ } finally {
477
+ db.close();
478
+ }
479
+ }
480
+
481
+ // ---------------------------------------------------------------------------
482
+ // Index command
483
+ // ---------------------------------------------------------------------------
484
+
485
+ async function cmdIndex(flags: Record<string, unknown>): Promise<void> {
486
+ const db = openDB();
487
+ const registry = new RepoRegistry(db);
488
+
489
+ try {
490
+ // Resolve repo: by --repo-id or by --path
491
+ const repoId = getFlag(flags, 'repo-id');
492
+ const targetPath = resolve(getFlag(flags, 'path') ?? process.cwd());
493
+ const force = getFlagBool(flags, 'force');
494
+
495
+ let repo;
496
+ if (repoId) {
497
+ repo = registry.getRepo(repoId);
498
+ if (!repo) printError(`Repo not found: ${repoId}. Run 'forge register' first.`);
499
+ } else {
500
+ repo = registry.findRepoByPath(targetPath);
501
+ if (!repo) {
502
+ printError(
503
+ `No registered repo found at ${targetPath}. Run 'forge register' first.`,
504
+ );
505
+ }
506
+ }
507
+
508
+ console.log(`Indexing repo '${repo.name}' (id: ${repo.id}) at ${repo.path}...`);
509
+
510
+ // Connect to Qdrant
511
+ const qdrantUrl = process.env['QDRANT_URL'] ?? 'http://localhost:6333';
512
+ const vectorStore = new QdrantVectorStore(qdrantUrl);
513
+ const qdrantHealthy = await vectorStore.isHealthy();
514
+ if (!qdrantHealthy) {
515
+ printError(`Qdrant not reachable at ${qdrantUrl}. Start Qdrant first.`);
516
+ }
517
+ await vectorStore.ensureCollections(384);
518
+
519
+ // Connect to FalkorDB (graceful degradation)
520
+ const falkorHost = process.env['FALKORDB_HOST'] ?? 'localhost';
521
+ const falkorPort = parseInt(process.env['FALKORDB_PORT'] ?? '6380', 10);
522
+ let graphStore: FalkorDBGraphStore | null = null;
523
+ try {
524
+ const falkor = new FalkorDBGraphStore(`redis://${falkorHost}:${falkorPort}`);
525
+ await falkor.connect();
526
+ const falkorHealthy = await falkor.isHealthy();
527
+ if (falkorHealthy) {
528
+ graphStore = falkor;
529
+ console.log('FalkorDB connected');
530
+ } else {
531
+ console.log('FalkorDB unavailable — indexing without graph store');
532
+ try { await falkor.disconnect(); } catch {}
533
+ }
534
+ } catch {
535
+ console.log('FalkorDB unavailable — indexing without graph store');
536
+ }
537
+
538
+ // Create a no-op graph store if FalkorDB is unavailable
539
+ const effectiveGraphStore = graphStore ?? createNoOpGraphStore();
540
+
541
+ // File cache
542
+ const fileCache = new LRUFileContentCache();
543
+
544
+ // Build RepoConfig
545
+ const repoConfig: RepoConfig = {
546
+ id: repo.id,
547
+ path: repo.path,
548
+ ownership: 'owned',
549
+ watch: false,
550
+ languages: repo.stack,
551
+ };
552
+
553
+ // Run full index
554
+ const result = await fullIndex({
555
+ repoConfig,
556
+ graphStore: effectiveGraphStore,
557
+ vectorStore,
558
+ fileCache,
559
+ allRepos: [repoConfig],
560
+ });
561
+
562
+ print({
563
+ success: true,
564
+ repo_id: result.repoId,
565
+ files_processed: result.filesProcessed,
566
+ files_skipped: result.filesSkipped,
567
+ files_errored: result.filesErrored,
568
+ chunks_created: result.chunksCreated,
569
+ knowledge_items_extracted: result.knowledgeItemsExtracted,
570
+ duration_ms: result.durationMs,
571
+ errors: result.errors.length > 0 ? result.errors.slice(0, 10) : undefined,
572
+ });
573
+
574
+ // Cleanup
575
+ if (graphStore) {
576
+ try { await graphStore.disconnect(); } catch {}
577
+ }
578
+ } finally {
579
+ db.close();
580
+ }
581
+ }
582
+
583
+ /**
584
+ * Minimal no-op graph store for when FalkorDB is unavailable.
585
+ * Allows indexing to proceed with vector-only storage.
586
+ */
587
+ function createNoOpGraphStore(): FalkorDBGraphStore {
588
+ return {
589
+ connect: async () => {},
590
+ disconnect: async () => {},
591
+ isHealthy: async () => false,
592
+ query: async () => ({ nodes: [], edges: [], raw: [] }),
593
+ upsertNode: async () => {},
594
+ upsertEdge: async () => {},
595
+ deleteFile: async () => {},
596
+ deleteRepo: async () => {},
597
+ getCounts: async () => ({ totalNodes: 0, totalEdges: 0, byLabel: {} }),
598
+ ensureIndexes: async () => {},
599
+ } as unknown as FalkorDBGraphStore;
600
+ }
601
+
602
+ // ---------------------------------------------------------------------------
603
+ // Main entry point
604
+ // ---------------------------------------------------------------------------
605
+
606
+ async function main(): Promise<void> {
607
+ const { command, subcommand, flags } = parseArgs(process.argv);
608
+
609
+ if (!command || command === '--help' || command === 'help' || command === '-h') {
610
+ printHelp();
611
+ process.exit(0);
612
+ }
613
+
614
+ switch (command) {
615
+ case 'init':
616
+ await cmdInit(flags);
617
+ break;
618
+
619
+ case 'register':
620
+ await cmdRegister(flags);
621
+ break;
622
+
623
+ case 'index':
624
+ await cmdIndex(flags);
625
+ break;
626
+
627
+ case 'status':
628
+ cmdStatus(flags);
629
+ break;
630
+
631
+ case 'knowledge':
632
+ if (!subcommand) {
633
+ printError("knowledge requires a subcommand: list, search, sync, or migrate");
634
+ }
635
+ cmdKnowledge(subcommand, flags);
636
+ break;
637
+
638
+ case 'repos':
639
+ if (!subcommand) {
640
+ printError("repos requires a subcommand: list, info, or remove");
641
+ }
642
+ cmdRepos(subcommand, flags);
643
+ break;
644
+
645
+ default:
646
+ console.error(`Unknown command: '${command}'`);
647
+ printHelp();
648
+ process.exit(1);
649
+ }
650
+ }
651
+
652
+ main().catch((err) => {
653
+ console.error(`forge CLI error: ${String(err)}`);
654
+ process.exit(1);
655
+ });
File without changes