@mc1global/opencode-jarvis 0.4.0 → 0.6.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 (50) hide show
  1. package/QUICK-GUIDE.md +53 -0
  2. package/README.md +308 -89
  3. package/dist/application/bootstrap/healthcheck-use-cases.d.ts +68 -0
  4. package/dist/application/bootstrap/healthcheck-use-cases.d.ts.map +1 -0
  5. package/dist/application/bootstrap/healthcheck-use-cases.js +303 -0
  6. package/dist/application/bootstrap/healthcheck-use-cases.js.map +1 -0
  7. package/dist/application/bootstrap/index.d.ts +10 -0
  8. package/dist/application/bootstrap/index.d.ts.map +1 -0
  9. package/dist/application/bootstrap/index.js +8 -0
  10. package/dist/application/bootstrap/index.js.map +1 -0
  11. package/dist/application/bootstrap/use-cases.d.ts +55 -0
  12. package/dist/application/bootstrap/use-cases.d.ts.map +1 -0
  13. package/dist/application/bootstrap/use-cases.js +355 -0
  14. package/dist/application/bootstrap/use-cases.js.map +1 -0
  15. package/dist/application/rag/dtos.d.ts +6 -0
  16. package/dist/application/rag/dtos.d.ts.map +1 -1
  17. package/dist/application/rag/use-cases.d.ts +9 -1
  18. package/dist/application/rag/use-cases.d.ts.map +1 -1
  19. package/dist/application/rag/use-cases.js +121 -56
  20. package/dist/application/rag/use-cases.js.map +1 -1
  21. package/dist/domain/rag/repositories.d.ts +42 -0
  22. package/dist/domain/rag/repositories.d.ts.map +1 -1
  23. package/dist/domain/rag/services.d.ts +10 -0
  24. package/dist/domain/rag/services.d.ts.map +1 -1
  25. package/dist/domain/rag/services.js +38 -0
  26. package/dist/domain/rag/services.js.map +1 -1
  27. package/dist/hooks/first-run-guide.d.ts +33 -0
  28. package/dist/hooks/first-run-guide.d.ts.map +1 -0
  29. package/dist/hooks/first-run-guide.js +58 -0
  30. package/dist/hooks/first-run-guide.js.map +1 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +53 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/infrastructure/rag/file-discovery.d.ts.map +1 -1
  35. package/dist/infrastructure/rag/file-discovery.js +1 -0
  36. package/dist/infrastructure/rag/file-discovery.js.map +1 -1
  37. package/dist/infrastructure/rag/file-index-registry.d.ts +18 -0
  38. package/dist/infrastructure/rag/file-index-registry.d.ts.map +1 -0
  39. package/dist/infrastructure/rag/file-index-registry.js +67 -0
  40. package/dist/infrastructure/rag/file-index-registry.js.map +1 -0
  41. package/dist/mcp-server.js +33 -2
  42. package/dist/mcp-server.js.map +1 -1
  43. package/dist/tools/bootstrap-tools.d.ts +15 -0
  44. package/dist/tools/bootstrap-tools.d.ts.map +1 -0
  45. package/dist/tools/bootstrap-tools.js +113 -0
  46. package/dist/tools/bootstrap-tools.js.map +1 -0
  47. package/dist/tools/rag-tools.d.ts.map +1 -1
  48. package/dist/tools/rag-tools.js +9 -2
  49. package/dist/tools/rag-tools.js.map +1 -1
  50. package/package.json +3 -2
@@ -0,0 +1,355 @@
1
+ /**
2
+ * Bootstrap Application — Use Cases
3
+ *
4
+ * Generates AGENTS.md with config-adaptive content and ensures
5
+ * the full JARVIS project structure exists (.jarvis/, config/, vault).
6
+ *
7
+ * SOLID: SRP — bootstrap orchestration only
8
+ * SOLID: OCP — new sections can be added without modifying existing ones
9
+ * SOLID: DIP — depends on ConfigService abstraction for config reading
10
+ */
11
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
12
+ import { join } from "node:path";
13
+ // ── Vault Skeleton Folders ────────────────────────────────────────────────────
14
+ const VAULT_SKELETON_DIRS = [
15
+ "obsidian-vault",
16
+ "obsidian-vault/09-Dashboards",
17
+ "obsidian-vault/11-Domain-Reference",
18
+ "obsidian-vault/12-Scope-Docs",
19
+ ];
20
+ // ── Default jarvis.yaml Content ───────────────────────────────────────────────
21
+ const DEFAULT_YAML_CONTENT = `# JARVIS Configuration
22
+ # Tunable settings per bounded context. ConfigService deep-merges
23
+ # these overrides over hardcoded defaults.
24
+
25
+ azure-sync:
26
+ organization: ""
27
+ project: ""
28
+ repository: ""
29
+ authMethod: "az-cli"
30
+ defaultSyncDirection: "push"
31
+ autoDetectTemplate: true
32
+ pollEnabled: false
33
+ pollIntervalSecs: 60
34
+
35
+ kanban:
36
+ minTransitionIntervalSecs: 30
37
+ gateTasks:
38
+ doing:
39
+ - description: "Code changes committed to feature branch"
40
+ required: true
41
+ autoVerify: "none"
42
+ - description: "tsc --noEmit passes with zero errors"
43
+ required: true
44
+ autoVerify: "none"
45
+ - description: "All tests pass"
46
+ required: true
47
+ autoVerify: "none"
48
+ review:
49
+ - description: "All acceptance criteria met"
50
+ required: true
51
+ autoVerify: "ac-met"
52
+ - description: "Code reviewed or self-reviewed with evidence"
53
+ required: true
54
+ autoVerify: "none"
55
+ tested:
56
+ - description: "All acceptance criteria met"
57
+ required: true
58
+ autoVerify: "ac-met"
59
+ - description: "Merged to main or PR approved"
60
+ required: true
61
+ autoVerify: "none"
62
+
63
+ mcp-server:
64
+ transport: "stdio"
65
+ enabledTools: null
66
+ `;
67
+ // ── Bootstrap Use Cases ───────────────────────────────────────────────────────
68
+ /**
69
+ * Orchestrates project bootstrap: AGENTS.md generation + directory structure.
70
+ */
71
+ export class BootstrapUseCases {
72
+ /**
73
+ * Bootstrap a project directory.
74
+ * Creates AGENTS.md (if missing), .jarvis/, config/jarvis.yaml, and vault skeleton.
75
+ *
76
+ * @param directory - Project root directory
77
+ * @param config - Config snapshot for adaptive content
78
+ * @param force - If true, regenerate AGENTS.md even if it exists
79
+ */
80
+ bootstrap(directory, config, force = false) {
81
+ const agentsMdPath = join(directory, "AGENTS.md");
82
+ const createdDirs = [];
83
+ // 1. Ensure .jarvis/ exists
84
+ const jarvisDir = join(directory, ".jarvis");
85
+ if (!existsSync(jarvisDir)) {
86
+ mkdirSync(jarvisDir, { recursive: true });
87
+ createdDirs.push(".jarvis/");
88
+ }
89
+ // 2. Ensure config/jarvis.yaml exists
90
+ const configDir = join(directory, "config");
91
+ const configPath = join(configDir, "jarvis.yaml");
92
+ let configCreated = false;
93
+ if (!existsSync(configPath)) {
94
+ mkdirSync(configDir, { recursive: true });
95
+ writeFileSync(configPath, DEFAULT_YAML_CONTENT, "utf-8");
96
+ configCreated = true;
97
+ createdDirs.push("config/jarvis.yaml");
98
+ }
99
+ // 3. Ensure vault skeleton exists
100
+ for (const relDir of VAULT_SKELETON_DIRS) {
101
+ const absDir = join(directory, relDir);
102
+ if (!existsSync(absDir)) {
103
+ mkdirSync(absDir, { recursive: true });
104
+ createdDirs.push(relDir + "/");
105
+ }
106
+ }
107
+ // 4. Generate AGENTS.md
108
+ const agentsMdExists = existsSync(agentsMdPath);
109
+ let agentsMdCreated = false;
110
+ if (!agentsMdExists || force) {
111
+ const content = generateAgentsMd(config);
112
+ writeFileSync(agentsMdPath, content, "utf-8");
113
+ agentsMdCreated = true;
114
+ }
115
+ return {
116
+ agentsMdCreated,
117
+ agentsMdPath,
118
+ directoriesCreated: createdDirs,
119
+ configCreated,
120
+ };
121
+ }
122
+ /**
123
+ * Build a BootstrapConfig from an AzureSyncConfig (or defaults).
124
+ */
125
+ buildConfig(azureSyncConfig) {
126
+ const hasAzure = Boolean(azureSyncConfig?.organization && azureSyncConfig?.project);
127
+ return {
128
+ azureSyncEnabled: hasAzure,
129
+ azureOrg: azureSyncConfig?.organization ?? "",
130
+ azureProject: azureSyncConfig?.project ?? "",
131
+ pipelineEnabled: true,
132
+ containerUseEnabled: true,
133
+ };
134
+ }
135
+ }
136
+ // ── AGENTS.md Template Generator ──────────────────────────────────────────────
137
+ /**
138
+ * Generate config-adaptive AGENTS.md content.
139
+ * Sections for Azure DevOps, Dagger, and container-use only appear when enabled.
140
+ */
141
+ export function generateAgentsMd(config) {
142
+ const sections = [];
143
+ sections.push(SECTION_HEADER);
144
+ sections.push(SECTION_KANBAN_LIFECYCLE);
145
+ sections.push(SECTION_GUARDRAILS);
146
+ sections.push(SECTION_GOVERNANCE);
147
+ if (config.containerUseEnabled) {
148
+ sections.push(SECTION_CONTAINER_USE);
149
+ }
150
+ if (config.azureSyncEnabled) {
151
+ sections.push(generateAzureSyncSection(config));
152
+ }
153
+ if (config.pipelineEnabled) {
154
+ sections.push(SECTION_PIPELINE_DAGGER);
155
+ }
156
+ sections.push(SECTION_VAULT);
157
+ sections.push(SECTION_GIT_STRATEGY);
158
+ sections.push(SECTION_CONFIG_SYSTEM);
159
+ sections.push(SECTION_TOOL_QUICK_REFERENCE);
160
+ sections.push(SECTION_SKILLS_AND_COMMANDS);
161
+ sections.push(SECTION_RAG_ORACLE);
162
+ return sections.join("\n\n") + "\n";
163
+ }
164
+ // ── Static Sections ───────────────────────────────────────────────────────────
165
+ const SECTION_HEADER = `# AGENTS.md — JARVIS Plugin Operational Guide
166
+
167
+ This file instructs agentic coding agents operating in this repository.
168
+ JARVIS is an OpenCode plugin implementing a multi-agent development framework
169
+ with kanban governance, context memory, and CI/CD pipeline support.`;
170
+ const SECTION_KANBAN_LIFECYCLE = `## Mandatory Workflow — Kanban Lifecycle
171
+
172
+ ALL non-trivial work (>15 minutes) MUST follow the full kanban pipeline.
173
+ Do NOT skip steps. Do NOT commit directly to \`main\`.
174
+
175
+ \`\`\`
176
+ backlog -> grooming -> ready -> doing -> review -> tested -> done
177
+ \`\`\`
178
+
179
+ ### Before coding anything:
180
+
181
+ 1. **Create or pick a card** — \`kanban-card-create\`, assign to epic and sprint
182
+ 2. **Groom it** — Write a scope doc in \`obsidian-vault/12-Scope-Docs/\`, define
183
+ acceptance criteria (min 2), estimate story points (Fibonacci), assign epic
184
+ 3. **Move to ready** — Card cannot leave grooming without: scope doc, AC, points, epic
185
+ 4. **Assign agent + sprint** — Then move to \`doing\`
186
+ 5. **Start a session** — \`context-session-start\` before any card work
187
+
188
+ ### Grooming rules — CRITICAL:
189
+
190
+ Grooming defines **problem, value, and boundaries**. It does NOT prescribe
191
+ implementation details (no class names, method signatures, field names, code
192
+ snippets). The implementing agent decides those during the \`doing\` phase.
193
+
194
+ ### After coding:
195
+
196
+ 6. **Run tests + linter** — All must pass, zero errors
197
+ 7. **Commit** — Imperative mood, reference card: \`Add X (CD-XXX)\` or \`Fix Y (CD-XXX)\`
198
+ 8. **Move card** — \`doing -> review -> tested -> done\`, meet all AC and gate tasks first
199
+ 9. **Update vault** — Dashboard + domain reference doc at end of every phase`;
200
+ const SECTION_GUARDRAILS = `## Guardrails — Hard Rules
201
+
202
+ ### NEVER:
203
+ - Use fallback/mock behavior to mask errors — errors must throw with proper handling
204
+ - Use a "simpler approach" to bypass the real solution
205
+ - Read \`.csv\` directly — use \`data-csv-query\` tool
206
+ - Read \`.yaml/.yml\` directly — use \`data-yaml-get\` tool
207
+ - Read \`.env\`, \`.pem\`, \`.key\`, credentials files — blocked by governance
208
+ - Run \`git push --force\` on main/master
209
+ - Run \`sqlite3\` CLI — use repository methods
210
+ - Create \`TODO.md\`, \`PLAN.md\`, or \`ROADMAP.md\` — use kanban tools
211
+ - Bypass the kanban pipeline for trackable work
212
+ - Push to git without explicit user approval
213
+ - Start the app from the agent session — ask the user to do it in another terminal
214
+
215
+ ### ALWAYS:
216
+ - Search RAG (\`rag-search\` / \`rag-snippet\`) before creating or modifying files
217
+ - Use specialized tools instead of raw file operations
218
+ - Get full codebase context before making changes — ask if unclear
219
+ - Validate card status transitions through the kanban domain
220
+ - Track context with sessions, TODOs, and notes`;
221
+ const SECTION_GOVERNANCE = `## Governance Policies (Runtime Enforced)
222
+
223
+ Governance policies are evaluated by the \`tool.execute.before\` hook on every
224
+ tool invocation. Violations with \`error\` severity throw and block execution.
225
+ These cover: CSV/YAML/env file access, force push, direct SQLite CLI,
226
+ destructive shell ops, credential file reads.
227
+
228
+ Use \`governance-validate\` to pre-check if an operation is allowed.
229
+ Use \`governance-policies\` to list all active policies.`;
230
+ const SECTION_CONTAINER_USE = `## Container-Use Environments
231
+
232
+ Container-use environments are isolated containers with their own git worktree
233
+ branches. Use them for all file, code, and shell operations.
234
+
235
+ Workflow:
236
+ 1. \`env-init\` — Detect project stack
237
+ 2. \`env-create\` — Spin up container from git ref
238
+ 3. Work inside container via \`container-use_environment_*\` tools
239
+ 4. \`env-merge\` — Merge branch back to main
240
+ 5. \`env-delete\` — Clean up container + worktree
241
+
242
+ IMPORTANT: Always inform the user how to view your work using
243
+ \`container-use log <env_id>\` and \`container-use checkout <env_id>\`.
244
+
245
+ Do NOT install or use the git CLI with \`environment_run_cmd\`. All environment
246
+ tools handle git operations automatically.`;
247
+ const SECTION_PIPELINE_DAGGER = `## CI/CD Pipelines (Dagger)
248
+
249
+ Pipeline BC uses Dagger for CI/CD gate execution. Pipelines are created from
250
+ stack templates (typescript-lib, python-app, etc.) and run gates in containers.
251
+
252
+ Workflow:
253
+ 1. \`pipeline-init\` — Create pipeline from stack template
254
+ 2. \`pipeline-activate\` — Make pipeline runnable
255
+ 3. \`pipeline-run\` — Execute gates via Dagger
256
+ 4. \`pipeline-gate\` — Manual pass/fail/skip/retry for gates
257
+ 5. \`pipeline-status\` — View pipeline state and gate results
258
+
259
+ Templates available via \`pipeline-template\` tool.
260
+ Scaffold Dagger modules via \`pipeline-scaffold\` tool.`;
261
+ const SECTION_VAULT = `## Obsidian Vault
262
+
263
+ Documentation lives in \`obsidian-vault/\`. Use structured lists, NOT markdown
264
+ tables (token efficiency). Update at end of every phase:
265
+
266
+ - \`09-Dashboards/\` — Project dashboards and metrics
267
+ - \`11-Domain-Reference/\` — Per-context architecture docs
268
+ - \`12-Scope-Docs/CD-XXX-*.md\` — Grooming scope documents
269
+
270
+ Use \`vault-manage\` tool for smart vault operations (inspect, read, write, create,
271
+ search, table-read, table-update).`;
272
+ const SECTION_GIT_STRATEGY = `## Git Strategy
273
+
274
+ - Work on feature branches (never commit directly to \`main\`)
275
+ - Commit messages: imperative verb + summary + card reference
276
+ - \`Add feature X (CD-001)\`
277
+ - \`Fix bug Y (CD-015)\`
278
+ - Merge via PR or controlled merge
279
+ - Never amend pushed commits without explicit request
280
+ - Never push without explicit user approval`;
281
+ const SECTION_CONFIG_SYSTEM = `## Configuration System
282
+
283
+ \`config/jarvis.yaml\` holds tunable settings per bounded context. The ConfigService
284
+ deep-merges YAML overrides over hardcoded defaults. Access config via the
285
+ appropriate getter methods — never read YAML files directly.`;
286
+ const SECTION_TOOL_QUICK_REFERENCE = `## Tool Quick Reference (112 tools)
287
+
288
+ JARVIS provides 112 tools across 11 bounded contexts. Key tool groups:
289
+
290
+ - **Context Memory** (8): \`context-session-start\`, \`context-session-finish\`, \`context-todo-add\`, \`context-note-add\`, \`context-search\`...
291
+ - **Kanban** (31): \`kanban-card-create\`, \`kanban-card-move\`, \`kanban-board-view\`, \`kanban-sprint-create\`, \`kanban-epic-create\`...
292
+ - **Governance** (2): \`governance-validate\`, \`governance-policies\`
293
+ - **Token Metrics** (7): \`metrics-summary\`, \`metrics-agent-report\`, \`metrics-card-report\`...
294
+ - **Vault** (11): \`vault-manage\` (smart facade), \`vault-doc-create\`, \`vault-doc-search\`...
295
+ - **RAG** (8): \`rag-search\`, \`rag-snippet\`, \`rag-index\`, \`rag-oracle-search\`, \`rag-oracle-index\`...
296
+ - **Data** (3): \`data-yaml-get\`, \`data-json-get\`, \`data-csv-query\`
297
+ - **Agent** (8): \`agent-sessions\`, \`agent-send\`, \`agent-spawn\`, \`agent-dispatch\`...
298
+ - **Pipeline** (10): \`pipeline-init\`, \`pipeline-run\`, \`pipeline-status\`...
299
+ - **Environment** (6): \`env-create\`, \`env-status\`, \`env-merge\`, \`env-delete\`...
300
+ - **Workspace** (4): \`workspace-info\`, \`workspace-list\`, \`workspace-update\`, \`workspace-link\`
301
+ - **Bootstrap** (2): \`jarvis-bootstrap\`, \`jarvis-healthcheck\`
302
+
303
+ For detailed workflows, load the appropriate skill (see below).`;
304
+ const SECTION_SKILLS_AND_COMMANDS = `## Available Skills
305
+
306
+ Load skills via the \`skill()\` tool to get detailed workflow instructions:
307
+
308
+ - **\`jarvis-kanban\`** — Full kanban lifecycle: card creation, grooming, sprint management, gate tasks
309
+ - **\`jarvis-azure-sync\`** — Azure DevOps sync: push/pull/bidi-sync, PR creation, poll events
310
+ - **\`jarvis-onboarding\`** — Setup, healthcheck, RAG indexing, tool group reference
311
+
312
+ ### Available Commands
313
+
314
+ Use these slash commands for common operations:
315
+
316
+ - **\`/healthcheck\`** — Run diagnostics and get actionable fix guidance
317
+ - **\`/sync-azure\`** — Execute Azure DevOps sync workflow
318
+ - **\`/start-card CD-XXX\`** — Start working on a specific card (handles full lifecycle)
319
+
320
+ When you encounter an unfamiliar workflow, ALWAYS load the relevant skill first.`;
321
+ const SECTION_RAG_ORACLE = `## Knowledge Search — RAG & ORACLE
322
+
323
+ Before creating or modifying code, ALWAYS search first:
324
+
325
+ - **\`rag-search\`** / **\`rag-snippet\`** — Search the codebase semantically
326
+ - **\`rag-oracle-search\`** — Search curated framework documentation (governance, workflows, patterns)
327
+
328
+ When you don't know how to do something, search ORACLE:
329
+ \`\`\`
330
+ rag-oracle-search query="how to sync azure devops"
331
+ rag-oracle-search query="kanban grooming requirements" domain="workflows"
332
+ \`\`\`
333
+
334
+ ORACLE domains: \`governance\`, \`workflows\`, \`patterns\`, \`quick_reference\`.`;
335
+ // ── Dynamic Sections ──────────────────────────────────────────────────────────
336
+ function generateAzureSyncSection(config) {
337
+ return `## Azure DevOps Integration
338
+
339
+ This project is connected to Azure DevOps (${config.azureOrg}/${config.azureProject}).
340
+
341
+ Available tools:
342
+ - \`azure-sync-discover\` — Detect project template, list work items + iterations
343
+ - \`azure-sync-push\` — Push cards/epics/sprints to Azure DevOps
344
+ - \`azure-sync-pull\` — Pull work items from Azure DevOps
345
+ - \`azure-sync-sync\` — Bidirectional sync (push then pull)
346
+ - \`azure-sync-status\` — Show sync status between local and Azure
347
+ - \`azure-sync-config\` — Show configuration + test connectivity
348
+ - \`azure-sync-events\` — List/acknowledge/dismiss detected change events
349
+ - \`azure-pr-create\` — Create Pull Request in Azure Repos
350
+ - \`azure-pr-list\` — List Pull Requests
351
+
352
+ Poll service can be enabled in \`config/jarvis.yaml\` under \`azure-sync.pollEnabled\`
353
+ to automatically detect changes made by team members in Azure DevOps.`;
354
+ }
355
+ //# sourceMappingURL=use-cases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-cases.js","sourceRoot":"","sources":["../../../src/application/bootstrap/use-cases.ts"],"names":[],"mappings":"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"}
@@ -38,6 +38,12 @@ export interface RagIndexResponse {
38
38
  readonly chunksCreated: number;
39
39
  readonly excludedFiles: number;
40
40
  readonly totalSizeMb: number;
41
+ /** Number of files skipped (unchanged since last index). */
42
+ readonly filesSkipped: number;
43
+ /** Number of files re-indexed (modified since last index). */
44
+ readonly filesUpdated: number;
45
+ /** Number of files removed from index (deleted from disk). */
46
+ readonly filesRemoved: number;
41
47
  }
42
48
  export interface RagSnippetRequest {
43
49
  readonly query: string;
@@ -1 +1 @@
1
- {"version":3,"file":"dtos.d.ts","sourceRoot":"","sources":["../../../src/application/rag/dtos.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dtos.d.ts","sourceRoot":"","sources":["../../../src/application/rag/dtos.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@
10
10
  * DDD: Application Service pattern
11
11
  */
12
12
  import type { Result } from "../../domain/shared/value-objects.js";
13
- import type { EmbeddingProvider, VectorRepository, TextSplitterPort, FileDiscoveryPort } from "../../domain/rag/repositories.js";
13
+ import type { EmbeddingProvider, VectorRepository, TextSplitterPort, FileDiscoveryPort, FileIndexRegistryPort } from "../../domain/rag/repositories.js";
14
14
  import type { RagConfig } from "../../domain/config/value-objects.js";
15
15
  import type { RagSearchRequest, RagSearchResponse, RagIndexRequest, RagIndexResponse, RagSnippetRequest, RagSnippetResponse, RagUpdateRequest, RagUpdateResponse, RagDeleteRequest, RagDeleteResponse, RagStatusRequest, RagStatusResponse } from "./dtos.js";
16
16
  export interface RagUseCasesDeps {
@@ -19,6 +19,8 @@ export interface RagUseCasesDeps {
19
19
  readonly textSplitter: TextSplitterPort;
20
20
  readonly fileDiscovery: FileDiscoveryPort;
21
21
  readonly ragConfig: RagConfig;
22
+ /** Optional: file index registry for incremental indexing. */
23
+ readonly fileIndexRegistry?: FileIndexRegistryPort;
22
24
  }
23
25
  export declare class RagUseCases {
24
26
  private readonly embedding;
@@ -26,9 +28,15 @@ export declare class RagUseCases {
26
28
  private readonly splitter;
27
29
  private readonly files;
28
30
  private readonly ragConfig;
31
+ private readonly registry;
29
32
  constructor(deps: RagUseCasesDeps);
30
33
  search(request: RagSearchRequest): Promise<Result<RagSearchResponse>>;
31
34
  index(request: RagIndexRequest): Promise<Result<RagIndexResponse>>;
35
+ /**
36
+ * Index a single file: read, split, embed, store chunks.
37
+ * @returns Number of chunks created for this file.
38
+ */
39
+ private indexSingleFile;
32
40
  snippet(request: RagSnippetRequest): Promise<Result<RagSnippetResponse>>;
33
41
  update(request: RagUpdateRequest): Promise<Result<RagUpdateResponse>>;
34
42
  delete(request: RagDeleteRequest): Promise<Result<RagDeleteResponse>>;
@@ -1 +1 @@
1
- {"version":3,"file":"use-cases.d.ts","sourceRoot":"","sources":["../../../src/application/rag/use-cases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sCAAsC,CAAC;AAEnE,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,kCAAkC,CAAC;AAO1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAC;AAEtE,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,WAAW,CAAC;AAKnB,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAC9C,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC/B;AAID,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;gBAE1B,IAAI,EAAE,eAAe;IAU3B,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAgCrE,KAAK,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAmGlE,OAAO,CACX,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAoChC,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAkGrE,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAqBrE,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;CAuB5E"}
1
+ {"version":3,"file":"use-cases.d.ts","sourceRoot":"","sources":["../../../src/application/rag/use-cases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,sCAAsC,CAAC;AAEnE,OAAO,KAAK,EACV,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,qBAAqB,EAGtB,MAAM,kCAAkC,CAAC;AAO1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sCAAsC,CAAC;AAEtE,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,WAAW,CAAC;AAKnB,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,iBAAiB,EAAE,iBAAiB,CAAC;IAC9C,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IAC5C,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;IAC9B,8DAA8D;IAC9D,QAAQ,CAAC,iBAAiB,CAAC,EAAE,qBAAqB,CAAC;CACpD;AAID,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoB;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;IAC5C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;gBAEjD,IAAI,EAAE,eAAe;IAW3B,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAgCrE,KAAK,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAuGxE;;;OAGG;YACW,eAAe;IAgEvB,OAAO,CACX,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC;IAoChC,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAkGrE,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAqBrE,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;CAuB5E"}
@@ -1,6 +1,6 @@
1
1
  import { ok, fail } from "../../domain/shared/value-objects.js";
2
+ import { extractSnippet, computeIndexDelta } from "../../domain/rag/services.js";
2
3
  import { EXTENSION_LANGUAGE_MAP, inferDomain, } from "../../domain/rag/value-objects.js";
3
- import { extractSnippet } from "../../domain/rag/services.js";
4
4
  import { extname } from "node:path";
5
5
  // ── Use Cases ─────────────────────────────────────────────────────────
6
6
  export class RagUseCases {
@@ -9,12 +9,14 @@ export class RagUseCases {
9
9
  splitter;
10
10
  files;
11
11
  ragConfig;
12
+ registry;
12
13
  constructor(deps) {
13
14
  this.embedding = deps.embeddingProvider;
14
15
  this.vectors = deps.vectorRepository;
15
16
  this.splitter = deps.textSplitter;
16
17
  this.files = deps.fileDiscovery;
17
18
  this.ragConfig = deps.ragConfig;
19
+ this.registry = deps.fileIndexRegistry;
18
20
  }
19
21
  // ── Search ──────────────────────────────────────────────────────
20
22
  async search(request) {
@@ -52,83 +54,146 @@ export class RagUseCases {
52
54
  const workspace = request.workspace.toLowerCase();
53
55
  // Discover files
54
56
  const analysis = await this.files.discover(request.directory);
55
- // Clear if requested
57
+ // Clear if requested (wipe index + registry)
56
58
  if (request.clear === true) {
57
59
  await this.vectors.clearIndex(workspace);
60
+ if (this.registry !== undefined) {
61
+ await this.registry.clearRecords(workspace);
62
+ }
58
63
  }
59
64
  // Ensure index exists
60
65
  await this.vectors.ensureIndex(workspace);
61
66
  // Create .ragignore if not present
62
67
  await this.files.createRagignore(request.directory, analysis.projectType);
68
+ // Compute incremental delta if registry is available
69
+ let filesToProcess;
70
+ let filesSkipped = 0;
71
+ let filesUpdated = 0;
72
+ let filesRemoved = 0;
73
+ if (this.registry !== undefined && request.clear !== true) {
74
+ const indexed = await this.registry.loadRecords(workspace);
75
+ const delta = computeIndexDelta(analysis.files, indexed);
76
+ // Remove deleted files from vector store
77
+ for (const relPath of delta.toRemove) {
78
+ const record = indexed.find((r) => r.relativePath === relPath);
79
+ if (record !== undefined) {
80
+ await this.vectors.deleteBySource(workspace, record.absolutePath);
81
+ }
82
+ }
83
+ filesRemoved = delta.toRemove.length;
84
+ // Delete existing chunks for modified files (will be re-added)
85
+ for (const file of delta.toUpdate) {
86
+ await this.vectors.deleteBySource(workspace, file.absolutePath);
87
+ }
88
+ filesUpdated = delta.toUpdate.length;
89
+ filesToProcess = [...delta.toAdd, ...delta.toUpdate];
90
+ filesSkipped = delta.unchanged.length;
91
+ }
92
+ else {
93
+ filesToProcess = analysis.files;
94
+ }
95
+ // Process files that need indexing
63
96
  let totalChunks = 0;
64
- // Process each file
65
- for (const file of analysis.files) {
66
- const content = await this.files.readFile(file.absolutePath);
67
- if (content === null || content.trim().length === 0)
68
- continue;
69
- const ext = extname(file.absolutePath).toLowerCase();
70
- const language = EXTENSION_LANGUAGE_MAP.get(ext);
71
- const domain = inferDomain(file.relativePath);
72
- const fileType = ext.startsWith(".") ? ext.slice(1) : ext;
73
- // Route markdown through splitByHeaders when enabled
74
- if (language === "markdown" &&
75
- this.ragConfig.markdownSplitByHeaders) {
76
- const sections = this.splitter.splitByHeaders(content);
77
- if (sections.length === 0)
78
- continue;
79
- const texts = sections.map((s) => enrichWithHeaders(s));
80
- const vectors = await this.embedding.embedBatch(texts);
81
- const chunks = sections.map((_, i) => ({
82
- vector: vectors[i],
83
- metadata: {
84
- source: file.absolutePath,
85
- workspace,
86
- domain,
87
- fileType,
88
- chunkIndex: i,
89
- totalChunks: sections.length,
90
- content: texts[i],
91
- },
92
- }));
93
- await this.vectors.addChunks(workspace, chunks);
94
- totalChunks += chunks.length;
95
- continue;
97
+ const newRecords = [];
98
+ for (const file of filesToProcess) {
99
+ const chunkCount = await this.indexSingleFile(file, workspace);
100
+ totalChunks += chunkCount;
101
+ newRecords.push({
102
+ relativePath: file.relativePath,
103
+ absolutePath: file.absolutePath,
104
+ mtimeMs: file.mtimeMs,
105
+ chunkCount,
106
+ });
107
+ }
108
+ // Update registry with current state
109
+ if (this.registry !== undefined) {
110
+ // Merge: keep unchanged records + add new/updated records
111
+ const indexed = request.clear === true
112
+ ? []
113
+ : await this.registry.loadRecords(workspace);
114
+ const processedPaths = new Set(newRecords.map((r) => r.relativePath));
115
+ const removedPaths = new Set();
116
+ if (this.registry !== undefined && request.clear !== true) {
117
+ const delta = computeIndexDelta(analysis.files, indexed);
118
+ for (const rp of delta.toRemove)
119
+ removedPaths.add(rp);
96
120
  }
97
- // Standard language-aware splitting with config-driven chunk sizes
98
- const chunkEntry = resolveChunkConfig(this.ragConfig, language);
99
- const textChunks = await this.splitter.split(content, language, chunkEntry);
100
- if (textChunks.length === 0)
101
- continue;
102
- const texts = textChunks.map((c) => c.content);
103
- const vectors = await this.embedding.embedBatch(texts);
104
- const chunks = textChunks.map((chunk, i) => ({
105
- vector: vectors[i],
106
- metadata: {
107
- source: file.absolutePath,
108
- workspace,
109
- domain,
110
- fileType,
111
- chunkIndex: chunk.index,
112
- totalChunks: textChunks.length,
113
- content: chunk.content,
114
- },
115
- }));
116
- await this.vectors.addChunks(workspace, chunks);
117
- totalChunks += chunks.length;
121
+ const keptRecords = indexed.filter((r) => !processedPaths.has(r.relativePath) && !removedPaths.has(r.relativePath));
122
+ await this.registry.saveRecords(workspace, [...keptRecords, ...newRecords]);
118
123
  }
119
124
  return ok({
120
125
  workspace,
121
126
  projectType: analysis.projectType,
122
- filesIndexed: analysis.files.length,
127
+ filesIndexed: filesToProcess.length,
123
128
  chunksCreated: totalChunks,
124
129
  excludedFiles: analysis.excludedCount,
125
130
  totalSizeMb: analysis.totalSizeMb,
131
+ filesSkipped,
132
+ filesUpdated,
133
+ filesRemoved,
126
134
  });
127
135
  }
128
136
  catch (error) {
129
137
  return fail(`RAG index failed: ${String(error)}`);
130
138
  }
131
139
  }
140
+ // ── Index Single File (private helper) ─────────────────────────
141
+ /**
142
+ * Index a single file: read, split, embed, store chunks.
143
+ * @returns Number of chunks created for this file.
144
+ */
145
+ async indexSingleFile(file, workspace) {
146
+ const content = await this.files.readFile(file.absolutePath);
147
+ if (content === null || content.trim().length === 0)
148
+ return 0;
149
+ const ext = extname(file.absolutePath).toLowerCase();
150
+ const language = EXTENSION_LANGUAGE_MAP.get(ext);
151
+ const domain = inferDomain(file.relativePath);
152
+ const fileType = ext.startsWith(".") ? ext.slice(1) : ext;
153
+ // Route markdown through splitByHeaders when enabled
154
+ if (language === "markdown" && this.ragConfig.markdownSplitByHeaders) {
155
+ const sections = this.splitter.splitByHeaders(content);
156
+ if (sections.length === 0)
157
+ return 0;
158
+ const texts = sections.map((s) => enrichWithHeaders(s));
159
+ const vectors = await this.embedding.embedBatch(texts);
160
+ const chunks = sections.map((_, i) => ({
161
+ vector: vectors[i],
162
+ metadata: {
163
+ source: file.absolutePath,
164
+ workspace,
165
+ domain,
166
+ fileType,
167
+ chunkIndex: i,
168
+ totalChunks: sections.length,
169
+ content: texts[i],
170
+ },
171
+ }));
172
+ await this.vectors.addChunks(workspace, chunks);
173
+ return chunks.length;
174
+ }
175
+ // Standard language-aware splitting with config-driven chunk sizes
176
+ const chunkEntry = resolveChunkConfig(this.ragConfig, language);
177
+ const textChunks = await this.splitter.split(content, language, chunkEntry);
178
+ if (textChunks.length === 0)
179
+ return 0;
180
+ const texts = textChunks.map((c) => c.content);
181
+ const vectors = await this.embedding.embedBatch(texts);
182
+ const chunks = textChunks.map((chunk, i) => ({
183
+ vector: vectors[i],
184
+ metadata: {
185
+ source: file.absolutePath,
186
+ workspace,
187
+ domain,
188
+ fileType,
189
+ chunkIndex: chunk.index,
190
+ totalChunks: textChunks.length,
191
+ content: chunk.content,
192
+ },
193
+ }));
194
+ await this.vectors.addChunks(workspace, chunks);
195
+ return chunks.length;
196
+ }
132
197
  // ── Snippet ─────────────────────────────────────────────────────
133
198
  async snippet(request) {
134
199
  try {