@mc1global/opencode-jarvis 0.4.0 → 0.7.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/QUICK-GUIDE.md +53 -0
- package/README.md +308 -89
- package/dist/application/bootstrap/healthcheck-use-cases.d.ts +68 -0
- package/dist/application/bootstrap/healthcheck-use-cases.d.ts.map +1 -0
- package/dist/application/bootstrap/healthcheck-use-cases.js +303 -0
- package/dist/application/bootstrap/healthcheck-use-cases.js.map +1 -0
- package/dist/application/bootstrap/index.d.ts +12 -0
- package/dist/application/bootstrap/index.d.ts.map +1 -0
- package/dist/application/bootstrap/index.js +9 -0
- package/dist/application/bootstrap/index.js.map +1 -0
- package/dist/application/bootstrap/terminal-commands-use-cases.d.ts +72 -0
- package/dist/application/bootstrap/terminal-commands-use-cases.d.ts.map +1 -0
- package/dist/application/bootstrap/terminal-commands-use-cases.js +132 -0
- package/dist/application/bootstrap/terminal-commands-use-cases.js.map +1 -0
- package/dist/application/bootstrap/use-cases.d.ts +55 -0
- package/dist/application/bootstrap/use-cases.d.ts.map +1 -0
- package/dist/application/bootstrap/use-cases.js +355 -0
- package/dist/application/bootstrap/use-cases.js.map +1 -0
- package/dist/application/rag/dtos.d.ts +6 -0
- package/dist/application/rag/dtos.d.ts.map +1 -1
- package/dist/application/rag/use-cases.d.ts +9 -1
- package/dist/application/rag/use-cases.d.ts.map +1 -1
- package/dist/application/rag/use-cases.js +121 -56
- package/dist/application/rag/use-cases.js.map +1 -1
- package/dist/domain/rag/repositories.d.ts +42 -0
- package/dist/domain/rag/repositories.d.ts.map +1 -1
- package/dist/domain/rag/services.d.ts +10 -0
- package/dist/domain/rag/services.d.ts.map +1 -1
- package/dist/domain/rag/services.js +38 -0
- package/dist/domain/rag/services.js.map +1 -1
- package/dist/hooks/first-run-guide.d.ts +33 -0
- package/dist/hooks/first-run-guide.d.ts.map +1 -0
- package/dist/hooks/first-run-guide.js +58 -0
- package/dist/hooks/first-run-guide.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +62 -1
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/rag/file-discovery.d.ts.map +1 -1
- package/dist/infrastructure/rag/file-discovery.js +1 -0
- package/dist/infrastructure/rag/file-discovery.js.map +1 -1
- package/dist/infrastructure/rag/file-index-registry.d.ts +18 -0
- package/dist/infrastructure/rag/file-index-registry.d.ts.map +1 -0
- package/dist/infrastructure/rag/file-index-registry.js +67 -0
- package/dist/infrastructure/rag/file-index-registry.js.map +1 -0
- package/dist/mcp-server.js +41 -3
- package/dist/mcp-server.js.map +1 -1
- package/dist/tools/bootstrap-tools.d.ts +17 -0
- package/dist/tools/bootstrap-tools.d.ts.map +1 -0
- package/dist/tools/bootstrap-tools.js +171 -0
- package/dist/tools/bootstrap-tools.js.map +1 -0
- package/dist/tools/rag-tools.d.ts.map +1 -1
- package/dist/tools/rag-tools.js +9 -2
- package/dist/tools/rag-tools.js.map +1 -1
- 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":"AAAA;;;;;;;;GAQG;AAIH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,SAAS,uBAAuB,EAAE,CAAC;IACrD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAID,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;
|
|
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,
|
|
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
|
-
|
|
65
|
-
for (const file of
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
|
|
98
|
-
|
|
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:
|
|
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 {
|