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.
- package/.claude/hooks/worktree-create.sh +64 -0
- package/.claude/hooks/worktree-remove.sh +57 -0
- package/.claude/settings.local.json +29 -0
- package/.forge/knowledge/conventions.yaml +1 -0
- package/.forge/knowledge/decisions.yaml +1 -0
- package/.forge/knowledge/gotchas.yaml +1 -0
- package/.forge/knowledge/patterns.yaml +1 -0
- package/.forge/manifest.yaml +6 -0
- package/CLAUDE.md +144 -0
- package/bin/setup-forge.sh +132 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +553 -0
- package/dist/cli.js.map +1 -0
- package/dist/context/codebase.d.ts +57 -0
- package/dist/context/codebase.d.ts.map +1 -0
- package/dist/context/codebase.js +301 -0
- package/dist/context/codebase.js.map +1 -0
- package/dist/context/injector.d.ts +147 -0
- package/dist/context/injector.d.ts.map +1 -0
- package/dist/context/injector.js +533 -0
- package/dist/context/injector.js.map +1 -0
- package/dist/context/memory.d.ts +32 -0
- package/dist/context/memory.d.ts.map +1 -0
- package/dist/context/memory.js +140 -0
- package/dist/context/memory.js.map +1 -0
- package/dist/context/session-index.d.ts +54 -0
- package/dist/context/session-index.d.ts.map +1 -0
- package/dist/context/session-index.js +265 -0
- package/dist/context/session-index.js.map +1 -0
- package/dist/context/session.d.ts +42 -0
- package/dist/context/session.d.ts.map +1 -0
- package/dist/context/session.js +121 -0
- package/dist/context/session.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/ingestion/chunker.d.ts +19 -0
- package/dist/ingestion/chunker.d.ts.map +1 -0
- package/dist/ingestion/chunker.js +189 -0
- package/dist/ingestion/chunker.js.map +1 -0
- package/dist/ingestion/embedder.d.ts +45 -0
- package/dist/ingestion/embedder.d.ts.map +1 -0
- package/dist/ingestion/embedder.js +152 -0
- package/dist/ingestion/embedder.js.map +1 -0
- package/dist/ingestion/git-analyzer.d.ts +77 -0
- package/dist/ingestion/git-analyzer.d.ts.map +1 -0
- package/dist/ingestion/git-analyzer.js +437 -0
- package/dist/ingestion/git-analyzer.js.map +1 -0
- package/dist/ingestion/indexer.d.ts +79 -0
- package/dist/ingestion/indexer.d.ts.map +1 -0
- package/dist/ingestion/indexer.js +766 -0
- package/dist/ingestion/indexer.js.map +1 -0
- package/dist/ingestion/markdown-chunker.d.ts +19 -0
- package/dist/ingestion/markdown-chunker.d.ts.map +1 -0
- package/dist/ingestion/markdown-chunker.js +243 -0
- package/dist/ingestion/markdown-chunker.js.map +1 -0
- package/dist/ingestion/markdown-knowledge.d.ts +21 -0
- package/dist/ingestion/markdown-knowledge.d.ts.map +1 -0
- package/dist/ingestion/markdown-knowledge.js +129 -0
- package/dist/ingestion/markdown-knowledge.js.map +1 -0
- package/dist/ingestion/parser.d.ts +20 -0
- package/dist/ingestion/parser.d.ts.map +1 -0
- package/dist/ingestion/parser.js +429 -0
- package/dist/ingestion/parser.js.map +1 -0
- package/dist/ingestion/watcher.d.ts +28 -0
- package/dist/ingestion/watcher.d.ts.map +1 -0
- package/dist/ingestion/watcher.js +147 -0
- package/dist/ingestion/watcher.js.map +1 -0
- package/dist/knowledge/hydrator.d.ts +37 -0
- package/dist/knowledge/hydrator.d.ts.map +1 -0
- package/dist/knowledge/hydrator.js +220 -0
- package/dist/knowledge/hydrator.js.map +1 -0
- package/dist/knowledge/registry.d.ts +129 -0
- package/dist/knowledge/registry.d.ts.map +1 -0
- package/dist/knowledge/registry.js +361 -0
- package/dist/knowledge/registry.js.map +1 -0
- package/dist/knowledge/search.d.ts +114 -0
- package/dist/knowledge/search.d.ts.map +1 -0
- package/dist/knowledge/search.js +428 -0
- package/dist/knowledge/search.js.map +1 -0
- package/dist/knowledge/store.d.ts +76 -0
- package/dist/knowledge/store.d.ts.map +1 -0
- package/dist/knowledge/store.js +230 -0
- package/dist/knowledge/store.js.map +1 -0
- package/dist/learning/confidence.d.ts +30 -0
- package/dist/learning/confidence.d.ts.map +1 -0
- package/dist/learning/confidence.js +165 -0
- package/dist/learning/confidence.js.map +1 -0
- package/dist/learning/patterns.d.ts +52 -0
- package/dist/learning/patterns.d.ts.map +1 -0
- package/dist/learning/patterns.js +290 -0
- package/dist/learning/patterns.js.map +1 -0
- package/dist/learning/trajectory.d.ts +55 -0
- package/dist/learning/trajectory.d.ts.map +1 -0
- package/dist/learning/trajectory.js +200 -0
- package/dist/learning/trajectory.js.map +1 -0
- package/dist/memory/memory-compat.d.ts +100 -0
- package/dist/memory/memory-compat.d.ts.map +1 -0
- package/dist/memory/memory-compat.js +146 -0
- package/dist/memory/memory-compat.js.map +1 -0
- package/dist/memory/observation-store.d.ts +57 -0
- package/dist/memory/observation-store.d.ts.map +1 -0
- package/dist/memory/observation-store.js +154 -0
- package/dist/memory/observation-store.js.map +1 -0
- package/dist/memory/session-tracker.d.ts +81 -0
- package/dist/memory/session-tracker.d.ts.map +1 -0
- package/dist/memory/session-tracker.js +262 -0
- package/dist/memory/session-tracker.js.map +1 -0
- package/dist/pipeline/engine.d.ts +179 -0
- package/dist/pipeline/engine.d.ts.map +1 -0
- package/dist/pipeline/engine.js +691 -0
- package/dist/pipeline/engine.js.map +1 -0
- package/dist/pipeline/events.d.ts +54 -0
- package/dist/pipeline/events.d.ts.map +1 -0
- package/dist/pipeline/events.js +157 -0
- package/dist/pipeline/events.js.map +1 -0
- package/dist/pipeline/parallel.d.ts +83 -0
- package/dist/pipeline/parallel.d.ts.map +1 -0
- package/dist/pipeline/parallel.js +277 -0
- package/dist/pipeline/parallel.js.map +1 -0
- package/dist/pipeline/state-machine.d.ts +65 -0
- package/dist/pipeline/state-machine.d.ts.map +1 -0
- package/dist/pipeline/state-machine.js +176 -0
- package/dist/pipeline/state-machine.js.map +1 -0
- package/dist/query/graph-queries.d.ts +84 -0
- package/dist/query/graph-queries.d.ts.map +1 -0
- package/dist/query/graph-queries.js +216 -0
- package/dist/query/graph-queries.js.map +1 -0
- package/dist/query/hybrid-search.d.ts +34 -0
- package/dist/query/hybrid-search.d.ts.map +1 -0
- package/dist/query/hybrid-search.js +263 -0
- package/dist/query/hybrid-search.js.map +1 -0
- package/dist/query/intent-detector.d.ts +35 -0
- package/dist/query/intent-detector.d.ts.map +1 -0
- package/dist/query/intent-detector.js +115 -0
- package/dist/query/intent-detector.js.map +1 -0
- package/dist/query/ranking.d.ts +57 -0
- package/dist/query/ranking.d.ts.map +1 -0
- package/dist/query/ranking.js +109 -0
- package/dist/query/ranking.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +291 -0
- package/dist/server.js.map +1 -0
- package/dist/storage/falkordb-store.d.ts +73 -0
- package/dist/storage/falkordb-store.d.ts.map +1 -0
- package/dist/storage/falkordb-store.js +346 -0
- package/dist/storage/falkordb-store.js.map +1 -0
- package/dist/storage/file-cache.d.ts +32 -0
- package/dist/storage/file-cache.d.ts.map +1 -0
- package/dist/storage/file-cache.js +115 -0
- package/dist/storage/file-cache.js.map +1 -0
- package/dist/storage/interfaces.d.ts +151 -0
- package/dist/storage/interfaces.d.ts.map +1 -0
- package/dist/storage/interfaces.js +7 -0
- package/dist/storage/interfaces.js.map +1 -0
- package/dist/storage/qdrant-store.d.ts +110 -0
- package/dist/storage/qdrant-store.d.ts.map +1 -0
- package/dist/storage/qdrant-store.js +467 -0
- package/dist/storage/qdrant-store.js.map +1 -0
- package/dist/storage/schema.d.ts +4 -0
- package/dist/storage/schema.d.ts.map +1 -0
- package/dist/storage/schema.js +136 -0
- package/dist/storage/schema.js.map +1 -0
- package/dist/storage/sqlite.d.ts +35 -0
- package/dist/storage/sqlite.d.ts.map +1 -0
- package/dist/storage/sqlite.js +132 -0
- package/dist/storage/sqlite.js.map +1 -0
- package/dist/tools/collaboration-tools.d.ts +111 -0
- package/dist/tools/collaboration-tools.d.ts.map +1 -0
- package/dist/tools/collaboration-tools.js +174 -0
- package/dist/tools/collaboration-tools.js.map +1 -0
- package/dist/tools/context-tools.d.ts +293 -0
- package/dist/tools/context-tools.d.ts.map +1 -0
- package/dist/tools/context-tools.js +437 -0
- package/dist/tools/context-tools.js.map +1 -0
- package/dist/tools/graph-tools.d.ts +129 -0
- package/dist/tools/graph-tools.d.ts.map +1 -0
- package/dist/tools/graph-tools.js +237 -0
- package/dist/tools/graph-tools.js.map +1 -0
- package/dist/tools/ingestion-tools.d.ts +96 -0
- package/dist/tools/ingestion-tools.d.ts.map +1 -0
- package/dist/tools/ingestion-tools.js +90 -0
- package/dist/tools/ingestion-tools.js.map +1 -0
- package/dist/tools/learning-tools.d.ts +168 -0
- package/dist/tools/learning-tools.d.ts.map +1 -0
- package/dist/tools/learning-tools.js +158 -0
- package/dist/tools/learning-tools.js.map +1 -0
- package/dist/tools/memory-tools.d.ts +183 -0
- package/dist/tools/memory-tools.d.ts.map +1 -0
- package/dist/tools/memory-tools.js +197 -0
- package/dist/tools/memory-tools.js.map +1 -0
- package/dist/tools/phase-tools.d.ts +954 -0
- package/dist/tools/phase-tools.d.ts.map +1 -0
- package/dist/tools/phase-tools.js +1215 -0
- package/dist/tools/phase-tools.js.map +1 -0
- package/dist/tools/pipeline-tools.d.ts +140 -0
- package/dist/tools/pipeline-tools.d.ts.map +1 -0
- package/dist/tools/pipeline-tools.js +162 -0
- package/dist/tools/pipeline-tools.js.map +1 -0
- package/dist/tools/registration-tools.d.ts +220 -0
- package/dist/tools/registration-tools.d.ts.map +1 -0
- package/dist/tools/registration-tools.js +391 -0
- package/dist/tools/registration-tools.js.map +1 -0
- package/dist/util/circuit-breaker.d.ts +75 -0
- package/dist/util/circuit-breaker.d.ts.map +1 -0
- package/dist/util/circuit-breaker.js +159 -0
- package/dist/util/circuit-breaker.js.map +1 -0
- package/dist/util/config.d.ts +23 -0
- package/dist/util/config.d.ts.map +1 -0
- package/dist/util/config.js +164 -0
- package/dist/util/config.js.map +1 -0
- package/dist/util/logger.d.ts +13 -0
- package/dist/util/logger.d.ts.map +1 -0
- package/dist/util/logger.js +45 -0
- package/dist/util/logger.js.map +1 -0
- package/dist/util/token-counter.d.ts +24 -0
- package/dist/util/token-counter.d.ts.map +1 -0
- package/dist/util/token-counter.js +48 -0
- package/dist/util/token-counter.js.map +1 -0
- package/dist/util/types.d.ts +525 -0
- package/dist/util/types.d.ts.map +1 -0
- package/dist/util/types.js +5 -0
- package/dist/util/types.js.map +1 -0
- package/docker-compose.yml +20 -0
- package/docs/plans/2026-02-27-swarm-coordination/architecture.md +203 -0
- package/docs/plans/2026-02-27-swarm-coordination/vision.md +57 -0
- package/docs/plans/completed/2026-02-26-forge-plugin-bundling/architecture.md +1 -0
- package/docs/plans/completed/2026-02-26-forge-plugin-bundling/vision.md +300 -0
- package/docs/plans/completed/2026-02-27-forge-swarm-learning/architecture.md +480 -0
- package/docs/plans/completed/2026-02-27-forge-swarm-learning/verification-checklist.md +462 -0
- package/docs/plans/completed/2026-02-27-git-history-atlassian/git-jira-plan.md +181 -0
- package/package.json +39 -0
- package/plugin/.claude-plugin/plugin.json +8 -0
- package/plugin/.mcp.json +15 -0
- package/plugin/README.md +134 -0
- package/plugin/agents/architect.md +367 -0
- package/plugin/agents/backend-specialist.md +263 -0
- package/plugin/agents/brainstormer.md +122 -0
- package/plugin/agents/data-specialist.md +266 -0
- package/plugin/agents/designer.md +408 -0
- package/plugin/agents/frontend-specialist.md +241 -0
- package/plugin/agents/inspector.md +406 -0
- package/plugin/agents/knowledge-keeper.md +443 -0
- package/plugin/agents/platform-engineer.md +326 -0
- package/plugin/agents/product-manager.md +268 -0
- package/plugin/agents/product-owner.md +438 -0
- package/plugin/agents/pulse-checker.md +73 -0
- package/plugin/agents/qa-strategist.md +500 -0
- package/plugin/agents/self-improver.md +310 -0
- package/plugin/agents/strategist.md +360 -0
- package/plugin/agents/supervisor.md +380 -0
- package/plugin/commands/brainstorm.md +25 -0
- package/plugin/commands/forge.md +88 -0
- package/plugin/docs/atlassian-integration.md +110 -0
- package/plugin/docs/workflow.md +126 -0
- package/plugin/skills/agent-development/.skillfish.json +10 -0
- package/plugin/skills/agent-development/SKILL.md +415 -0
- package/plugin/skills/agent-development/examples/agent-creation-prompt.md +238 -0
- package/plugin/skills/agent-development/examples/complete-agent-examples.md +427 -0
- package/plugin/skills/agent-development/references/agent-creation-system-prompt.md +207 -0
- package/plugin/skills/agent-development/references/system-prompt-design.md +411 -0
- package/plugin/skills/agent-development/references/triggering-examples.md +491 -0
- package/plugin/skills/agent-development/scripts/validate-agent.sh +217 -0
- package/plugin/skills/agent-handoff/SKILL.md +335 -0
- package/plugin/skills/anti-stub/SKILL.md +317 -0
- package/plugin/skills/brainstorm/SKILL.md +31 -0
- package/plugin/skills/debugging/SKILL.md +276 -0
- package/plugin/skills/fix/SKILL.md +62 -0
- package/plugin/skills/frontend-design/.skillfish.json +10 -0
- package/plugin/skills/frontend-design/SKILL.md +42 -0
- package/plugin/skills/gotchas/SKILL.md +61 -0
- package/plugin/skills/graph-orchestrator/SKILL.md +38 -0
- package/plugin/skills/history/SKILL.md +58 -0
- package/plugin/skills/impact/SKILL.md +59 -0
- package/plugin/skills/implementation-execution/SKILL.md +291 -0
- package/plugin/skills/index-repo/SKILL.md +55 -0
- package/plugin/skills/interviewing/SKILL.md +225 -0
- package/plugin/skills/knowledge-curation/SKILL.md +393 -0
- package/plugin/skills/learn/SKILL.md +69 -0
- package/plugin/skills/mcp-integration/.skillfish.json +10 -0
- package/plugin/skills/mcp-integration/SKILL.md +554 -0
- package/plugin/skills/mcp-integration/examples/http-server.json +20 -0
- package/plugin/skills/mcp-integration/examples/sse-server.json +19 -0
- package/plugin/skills/mcp-integration/examples/stdio-server.json +26 -0
- package/plugin/skills/mcp-integration/references/authentication.md +549 -0
- package/plugin/skills/mcp-integration/references/server-types.md +536 -0
- package/plugin/skills/mcp-integration/references/tool-usage.md +538 -0
- package/plugin/skills/nestjs/.skillfish.json +10 -0
- package/plugin/skills/nestjs/SKILL.md +669 -0
- package/plugin/skills/nestjs/drizzle-reference.md +1894 -0
- package/plugin/skills/nestjs/reference.md +1447 -0
- package/plugin/skills/nestjs/workflow-optimization.md +229 -0
- package/plugin/skills/parallel-dispatch/SKILL.md +308 -0
- package/plugin/skills/project-discovery/SKILL.md +304 -0
- package/plugin/skills/search/SKILL.md +56 -0
- package/plugin/skills/security-audit/SKILL.md +362 -0
- package/plugin/skills/skill-development/.skillfish.json +10 -0
- package/plugin/skills/skill-development/SKILL.md +637 -0
- package/plugin/skills/skill-development/references/skill-creator-original.md +209 -0
- package/plugin/skills/tdd/SKILL.md +273 -0
- package/plugin/skills/terminal-presentation/SKILL.md +395 -0
- package/plugin/skills/test-strategy/SKILL.md +365 -0
- package/plugin/skills/verification-protocol/SKILL.md +256 -0
- package/plugin/skills/visual-explainer/CHANGELOG.md +97 -0
- package/plugin/skills/visual-explainer/LICENSE +21 -0
- package/plugin/skills/visual-explainer/README.md +137 -0
- package/plugin/skills/visual-explainer/SKILL.md +352 -0
- package/plugin/skills/visual-explainer/banner.png +0 -0
- package/plugin/skills/visual-explainer/package.json +11 -0
- package/plugin/skills/visual-explainer/prompts/diff-review.md +68 -0
- package/plugin/skills/visual-explainer/prompts/fact-check.md +63 -0
- package/plugin/skills/visual-explainer/prompts/generate-slides.md +18 -0
- package/plugin/skills/visual-explainer/prompts/generate-web-diagram.md +10 -0
- package/plugin/skills/visual-explainer/prompts/plan-review.md +86 -0
- package/plugin/skills/visual-explainer/prompts/project-recap.md +61 -0
- package/plugin/skills/visual-explainer/references/css-patterns.md +1188 -0
- package/plugin/skills/visual-explainer/references/libraries.md +470 -0
- package/plugin/skills/visual-explainer/references/responsive-nav.md +212 -0
- package/plugin/skills/visual-explainer/references/slide-patterns.md +1403 -0
- package/plugin/skills/visual-explainer/templates/architecture.html +596 -0
- package/plugin/skills/visual-explainer/templates/data-table.html +540 -0
- package/plugin/skills/visual-explainer/templates/mermaid-flowchart.html +435 -0
- package/plugin/skills/visual-explainer/templates/slide-deck.html +913 -0
- package/src/cli.ts +655 -0
- package/src/context/.gitkeep +0 -0
- package/src/context/codebase.ts +393 -0
- package/src/context/injector.ts +797 -0
- package/src/context/memory.ts +187 -0
- package/src/context/session-index.ts +327 -0
- package/src/context/session.ts +152 -0
- package/src/index.ts +47 -0
- package/src/ingestion/.gitkeep +0 -0
- package/src/ingestion/chunker.ts +277 -0
- package/src/ingestion/embedder.ts +167 -0
- package/src/ingestion/git-analyzer.ts +545 -0
- package/src/ingestion/indexer.ts +984 -0
- package/src/ingestion/markdown-chunker.ts +337 -0
- package/src/ingestion/markdown-knowledge.ts +175 -0
- package/src/ingestion/parser.ts +475 -0
- package/src/ingestion/watcher.ts +182 -0
- package/src/knowledge/.gitkeep +0 -0
- package/src/knowledge/hydrator.ts +246 -0
- package/src/knowledge/registry.ts +463 -0
- package/src/knowledge/search.ts +565 -0
- package/src/knowledge/store.ts +262 -0
- package/src/learning/.gitkeep +0 -0
- package/src/learning/confidence.ts +193 -0
- package/src/learning/patterns.ts +360 -0
- package/src/learning/trajectory.ts +268 -0
- package/src/memory/.gitkeep +0 -0
- package/src/memory/memory-compat.ts +233 -0
- package/src/memory/observation-store.ts +224 -0
- package/src/memory/session-tracker.ts +332 -0
- package/src/pipeline/.gitkeep +0 -0
- package/src/pipeline/engine.ts +1139 -0
- package/src/pipeline/events.ts +253 -0
- package/src/pipeline/parallel.ts +394 -0
- package/src/pipeline/state-machine.ts +199 -0
- package/src/query/.gitkeep +0 -0
- package/src/query/graph-queries.ts +262 -0
- package/src/query/hybrid-search.ts +337 -0
- package/src/query/intent-detector.ts +131 -0
- package/src/query/ranking.ts +161 -0
- package/src/server.ts +352 -0
- package/src/storage/.gitkeep +0 -0
- package/src/storage/falkordb-store.ts +388 -0
- package/src/storage/file-cache.ts +141 -0
- package/src/storage/interfaces.ts +201 -0
- package/src/storage/qdrant-store.ts +557 -0
- package/src/storage/schema.ts +139 -0
- package/src/storage/sqlite.ts +168 -0
- package/src/tools/.gitkeep +0 -0
- package/src/tools/collaboration-tools.ts +208 -0
- package/src/tools/context-tools.ts +493 -0
- package/src/tools/graph-tools.ts +295 -0
- package/src/tools/ingestion-tools.ts +122 -0
- package/src/tools/learning-tools.ts +181 -0
- package/src/tools/memory-tools.ts +234 -0
- package/src/tools/phase-tools.ts +1452 -0
- package/src/tools/pipeline-tools.ts +188 -0
- package/src/tools/registration-tools.ts +450 -0
- package/src/util/.gitkeep +0 -0
- package/src/util/circuit-breaker.ts +193 -0
- package/src/util/config.ts +177 -0
- package/src/util/logger.ts +53 -0
- package/src/util/token-counter.ts +52 -0
- package/src/util/types.ts +710 -0
- package/tests/context/.gitkeep +0 -0
- package/tests/integration/.gitkeep +0 -0
- package/tests/knowledge/.gitkeep +0 -0
- package/tests/learning/.gitkeep +0 -0
- package/tests/pipeline/.gitkeep +0 -0
- package/tests/tools/.gitkeep +0 -0
- package/tsconfig.json +21 -0
- package/vitest.config.ts +10 -0
- package/vscode-extension/.vscodeignore +7 -0
- package/vscode-extension/README.md +43 -0
- package/vscode-extension/out/edge-collector.js +274 -0
- package/vscode-extension/out/edge-collector.js.map +1 -0
- package/vscode-extension/out/extension.js +264 -0
- package/vscode-extension/out/extension.js.map +1 -0
- package/vscode-extension/out/forge-client.js +318 -0
- package/vscode-extension/out/forge-client.js.map +1 -0
- package/vscode-extension/package-lock.json +59 -0
- package/vscode-extension/package.json +71 -0
- package/vscode-extension/src/edge-collector.ts +320 -0
- package/vscode-extension/src/extension.ts +269 -0
- package/vscode-extension/src/forge-client.ts +364 -0
- 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
|