@comfanion/workflow 4.36.43 → 4.36.45
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/bin/cli.js +41 -0
- package/package.json +1 -1
- package/src/build-info.json +2 -2
- package/src/opencode/agents/dev.md +16 -15
- package/src/opencode/package.json +1 -1
- package/src/vectorizer/index.js +21 -4
package/bin/cli.js
CHANGED
|
@@ -124,6 +124,7 @@ program
|
|
|
124
124
|
install_vectorizer: true, // Vectorizer ON by default
|
|
125
125
|
vectorizer_enabled: true,
|
|
126
126
|
vectorizer_auto_index: true,
|
|
127
|
+
vectorizer_model: 'Xenova/bge-small-en-v1.5', // Default: balanced (quality + speed)
|
|
127
128
|
project_name: path.basename(process.cwd())
|
|
128
129
|
};
|
|
129
130
|
|
|
@@ -146,6 +147,7 @@ program
|
|
|
146
147
|
// Parse vectorizer settings
|
|
147
148
|
const vectorizerEnabledMatch = existingContent.match(/vectorizer:[\s\S]*?enabled:\s*(true|false)/);
|
|
148
149
|
const vectorizerAutoIndexMatch = existingContent.match(/vectorizer:[\s\S]*?auto_index:\s*(true|false)/);
|
|
150
|
+
const vectorizerModelMatch = existingContent.match(/vectorizer:[\s\S]*?model:\s*["']?([^"'\n]+)["']?/);
|
|
149
151
|
|
|
150
152
|
if (nameMatch) config.user_name = nameMatch[1];
|
|
151
153
|
if (langMatch) config.communication_language = langMatch[1];
|
|
@@ -155,6 +157,7 @@ program
|
|
|
155
157
|
if (jiraProjMatch) config.jira_project = jiraProjMatch[1];
|
|
156
158
|
if (vectorizerEnabledMatch) config.vectorizer_enabled = vectorizerEnabledMatch[1] === 'true';
|
|
157
159
|
if (vectorizerAutoIndexMatch) config.vectorizer_auto_index = vectorizerAutoIndexMatch[1] === 'true';
|
|
160
|
+
if (vectorizerModelMatch) config.vectorizer_model = vectorizerModelMatch[1].trim();
|
|
158
161
|
|
|
159
162
|
isUpdate = true;
|
|
160
163
|
} catch (e) {
|
|
@@ -249,6 +252,27 @@ program
|
|
|
249
252
|
message: 'Install vectorizer? (semantic code search, ~100MB)',
|
|
250
253
|
default: true
|
|
251
254
|
},
|
|
255
|
+
{
|
|
256
|
+
type: 'list',
|
|
257
|
+
name: 'vectorizer_model',
|
|
258
|
+
message: 'Embedding model for semantic search:',
|
|
259
|
+
when: (answers) => answers.install_vectorizer,
|
|
260
|
+
choices: [
|
|
261
|
+
{
|
|
262
|
+
name: 'MiniLM-L6 (Fast) - ~10 files/10sec, 384 dims, good quality',
|
|
263
|
+
value: 'Xenova/all-MiniLM-L6-v2'
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: 'BGE-small (Balanced) - ~9 files/10sec, 384 dims, better quality',
|
|
267
|
+
value: 'Xenova/bge-small-en-v1.5'
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
name: 'BGE-base (Quality) - ~3 files/10sec, 768 dims, best quality',
|
|
271
|
+
value: 'Xenova/bge-base-en-v1.5'
|
|
272
|
+
}
|
|
273
|
+
],
|
|
274
|
+
default: 'Xenova/bge-small-en-v1.5'
|
|
275
|
+
},
|
|
252
276
|
{
|
|
253
277
|
type: 'confirm',
|
|
254
278
|
name: 'vectorizer_auto_index',
|
|
@@ -449,6 +473,23 @@ program
|
|
|
449
473
|
.replace(/(# Auto-index files.*\n\s+auto_index:)\s*(true|false)/,
|
|
450
474
|
`$1 ${config.vectorizer_auto_index}`);
|
|
451
475
|
|
|
476
|
+
// Add/update vectorizer model
|
|
477
|
+
if (config.vectorizer_model) {
|
|
478
|
+
if (configContent.includes('model:') && configContent.match(/vectorizer:[\s\S]*?model:/)) {
|
|
479
|
+
// Update existing model setting
|
|
480
|
+
configContent = configContent.replace(
|
|
481
|
+
/(vectorizer:[\s\S]*?)model:\s*["']?[^"'\n]+["']?/,
|
|
482
|
+
`$1model: "${config.vectorizer_model}"`
|
|
483
|
+
);
|
|
484
|
+
} else {
|
|
485
|
+
// Add model setting after auto_index
|
|
486
|
+
configContent = configContent.replace(
|
|
487
|
+
/(auto_index:\s*(true|false))/,
|
|
488
|
+
`$1\n \n # Embedding model for semantic search\n # Options: Xenova/all-MiniLM-L6-v2 (fast), Xenova/bge-base-en-v1.5 (quality)\n model: "${config.vectorizer_model}"`
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
452
493
|
await fs.writeFile(configPath, configContent);
|
|
453
494
|
|
|
454
495
|
// Create docs structure (always)
|
package/package.json
CHANGED
package/src/build-info.json
CHANGED
|
@@ -37,13 +37,13 @@ permission:
|
|
|
37
37
|
<step n="3">Greet user by {user_name}, communicate in {communication_language}</step>
|
|
38
38
|
<step n="4">Understand user request and select appropriate skill</step>
|
|
39
39
|
<step n="5">Load .opencode/skills/{skill-name}/SKILL.md and follow instructions</step>
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
<search-first critical="MANDATORY - DO THIS BEFORE GLOB/GREP">
|
|
42
42
|
BEFORE using glob or grep, you MUST call search() first:
|
|
43
43
|
1. search({ query: "your topic", index: "code" }) - for source code patterns
|
|
44
44
|
2. search({ query: "your topic", index: "docs" }) - for documentation
|
|
45
45
|
3. THEN use glob/grep if you need specific files
|
|
46
|
-
|
|
46
|
+
|
|
47
47
|
Example: Looking for similar implementation?
|
|
48
48
|
✅ CORRECT: search({ query: "user repository CRUD", index: "code" })
|
|
49
49
|
❌ WRONG: glob("**/*user*.go") without search first
|
|
@@ -53,27 +53,28 @@ permission:
|
|
|
53
53
|
<r>ALWAYS communicate in {communication_language}</r>
|
|
54
54
|
<r>ALWAYS write technical documentation in ENGLISH (docs/ folder)</r>
|
|
55
55
|
<r>The Story File is the single source of truth</r>
|
|
56
|
+
<r>Prefer Agents development (@coder)</r>
|
|
56
57
|
<r>Tasks/subtasks sequence is authoritative over any model priors</r>
|
|
57
58
|
<r>Follow red-green-refactor: write failing test, make it pass, improve code</r>
|
|
58
59
|
<r>Never implement anything not mapped to a specific task/subtask</r>
|
|
59
60
|
<r>All existing tests must pass 100% before story is ready for review</r>
|
|
60
61
|
<r>NEVER lie about tests being written or passing</r>
|
|
61
|
-
<r>Find and use `**/
|
|
62
|
+
<r>Find and use `**/prd.md`, `**/architecture.md`, `AGENTS.md` and `CLAUDE.md` as source of truth</r>
|
|
62
63
|
<r critical="MANDATORY">🔍 SEARCH FIRST: Call search() BEFORE glob when exploring codebase.
|
|
63
64
|
search({ query: "feature pattern", index: "code" }) → THEN glob if needed</r>
|
|
64
65
|
</rules>
|
|
65
|
-
|
|
66
|
+
|
|
66
67
|
<dev-story-workflow hint="When executing /dev-story command" critical="FOLLOW THIS EXACTLY">
|
|
67
68
|
<!-- PHASE 1: SETUP -->
|
|
68
69
|
<step n="1">READ the entire story file BEFORE any implementation</step>
|
|
69
|
-
<step n="2">Load
|
|
70
|
+
<step n="2">Load **/prd.md`, `**/architecture.md`, `AGENTS.md` and `CLAUDE.md` if available</step>
|
|
70
71
|
<step n="3">CREATE TODO LIST from story tasks using todowrite:
|
|
71
72
|
- Each task becomes a TODO item
|
|
72
73
|
- Set priority based on task order (first = high)
|
|
73
74
|
- All tasks start as "pending"
|
|
74
75
|
</step>
|
|
75
76
|
<step n="4">Mark story status as "in-progress"</step>
|
|
76
|
-
|
|
77
|
+
|
|
77
78
|
<!-- PHASE 2: IMPLEMENTATION LOOP -->
|
|
78
79
|
<step n="5">FOR EACH TASK in order:
|
|
79
80
|
a) Update TODO: mark current task as "in_progress"
|
|
@@ -97,7 +98,7 @@ permission:
|
|
|
97
98
|
<step n="8">Clear TODO list (all done)</step>
|
|
98
99
|
<step n="9">Mark story status as "review"</step>
|
|
99
100
|
</dev-story-workflow>
|
|
100
|
-
|
|
101
|
+
|
|
101
102
|
<todo-usage hint="How to use TODO for tracking">
|
|
102
103
|
<create>
|
|
103
104
|
todowrite([
|
|
@@ -143,7 +144,7 @@ permission:
|
|
|
143
144
|
- Repetitive tasks across files
|
|
144
145
|
- Code following existing patterns
|
|
145
146
|
</subagent>
|
|
146
|
-
|
|
147
|
+
|
|
147
148
|
<delegation-strategy>
|
|
148
149
|
<rule>Prefer delegation to @coder for parallelizable tasks</rule>
|
|
149
150
|
<rule>Keep complex logic and architecture decisions to yourself</rule>
|
|
@@ -174,7 +175,7 @@ permission:
|
|
|
174
175
|
<operation name="goToImplementation">Find implementations of interface. Use: lsp goToImplementation file.ts:10:5</operation>
|
|
175
176
|
<operation name="incomingCalls">Who calls this function? Use: lsp incomingCalls file.ts:10:5</operation>
|
|
176
177
|
<operation name="outgoingCalls">What does this function call? Use: lsp outgoingCalls file.ts:10:5</operation>
|
|
177
|
-
|
|
178
|
+
|
|
178
179
|
<when-to-use>
|
|
179
180
|
- Before modifying: findReferences to see impact
|
|
180
181
|
- Understanding code: hover for types, documentSymbol for structure
|
|
@@ -185,13 +186,13 @@ permission:
|
|
|
185
186
|
|
|
186
187
|
<codesearch-guide hint="Semantic code search with multi-index support">
|
|
187
188
|
<check-first>codeindex({ action: "list" }) → See all available indexes</check-first>
|
|
188
|
-
|
|
189
|
+
|
|
189
190
|
<indexes>
|
|
190
191
|
<index name="code" pattern="*.{js,ts,go,py,java,...}">Source code - functions, classes, logic</index>
|
|
191
192
|
<index name="docs" pattern="*.{md,txt,rst}">Documentation - READMEs, guides, ADRs</index>
|
|
192
193
|
<index name="config" pattern="*.{yaml,json,toml}">Configuration - settings, schemas</index>
|
|
193
194
|
</indexes>
|
|
194
|
-
|
|
195
|
+
|
|
195
196
|
<operations>
|
|
196
197
|
<op name="search code">codesearch({ query: "authentication middleware", index: "code" })</op>
|
|
197
198
|
<op name="search docs">codesearch({ query: "deployment guide", index: "docs" })</op>
|
|
@@ -201,7 +202,7 @@ permission:
|
|
|
201
202
|
<op name="index status">codeindex({ action: "status", index: "code" })</op>
|
|
202
203
|
<op name="reindex">codeindex({ action: "reindex", index: "code" })</op>
|
|
203
204
|
</operations>
|
|
204
|
-
|
|
205
|
+
|
|
205
206
|
<when-to-use>
|
|
206
207
|
<use index="code">
|
|
207
208
|
- BEFORE implementing: find existing patterns "repository pattern for users"
|
|
@@ -224,19 +225,19 @@ permission:
|
|
|
224
225
|
- Cross-cutting concerns: "logging configuration"
|
|
225
226
|
</use>
|
|
226
227
|
</when-to-use>
|
|
227
|
-
|
|
228
|
+
|
|
228
229
|
<examples>
|
|
229
230
|
<example query="repository interface for products" index="code">Finds domain/repository files</example>
|
|
230
231
|
<example query="HTTP request validation" index="code">Finds middleware and handlers</example>
|
|
231
232
|
<example query="how to run tests" index="docs">Finds testing documentation</example>
|
|
232
233
|
<example query="redis connection" index="config">Finds redis configuration</example>
|
|
233
234
|
</examples>
|
|
234
|
-
|
|
235
|
+
|
|
235
236
|
<vs-grep>
|
|
236
237
|
grep: exact text match "UserRepository" → finds only that string
|
|
237
238
|
codesearch: semantic "user storage" → finds UserRepository, UserStore, user_repo.go
|
|
238
239
|
</vs-grep>
|
|
239
|
-
|
|
240
|
+
|
|
240
241
|
<strategy>
|
|
241
242
|
1. codeindex({ action: "list" }) → Check what indexes exist
|
|
242
243
|
2. codesearch({ query: "concept", index: "code" }) → Find relevant code
|
package/src/vectorizer/index.js
CHANGED
|
@@ -45,6 +45,7 @@ const DEFAULT_PRESETS = {
|
|
|
45
45
|
// Will be populated from config.yaml if available
|
|
46
46
|
let INDEX_PRESETS = { ...DEFAULT_PRESETS };
|
|
47
47
|
let GLOBAL_IGNORE = [];
|
|
48
|
+
let EMBEDDING_MODEL = 'Xenova/all-MiniLM-L6-v2'; // Default: fast model
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
51
|
* Load index configuration from config.yaml
|
|
@@ -61,6 +62,13 @@ async function loadConfig(projectRoot) {
|
|
|
61
62
|
|
|
62
63
|
const section = vectorizerMatch[1];
|
|
63
64
|
|
|
65
|
+
// Parse embedding model
|
|
66
|
+
const modelMatch = section.match(/^\s{2}model:\s*["']?([^"'\n]+)["']?/m);
|
|
67
|
+
if (modelMatch) {
|
|
68
|
+
EMBEDDING_MODEL = modelMatch[1].trim();
|
|
69
|
+
if (DEBUG) console.log('[vectorizer] Using model from config:', EMBEDDING_MODEL);
|
|
70
|
+
}
|
|
71
|
+
|
|
64
72
|
// Parse global exclude
|
|
65
73
|
const excludeMatch = section.match(/^\s{2}exclude:\s*\n((?:\s{4}-\s+.+\n?)*)/m);
|
|
66
74
|
if (excludeMatch) {
|
|
@@ -148,11 +156,14 @@ class CodebaseIndexer {
|
|
|
148
156
|
|
|
149
157
|
async loadModel() {
|
|
150
158
|
if (!this.model) {
|
|
151
|
-
if (DEBUG) console.log(
|
|
152
|
-
|
|
159
|
+
if (DEBUG) console.log(`[vectorizer] Loading embedding model: ${EMBEDDING_MODEL}...`);
|
|
160
|
+
// Model options:
|
|
161
|
+
// - Xenova/all-MiniLM-L6-v2: fast, 384 dims, ~10 files/10sec
|
|
162
|
+
// - Xenova/bge-base-en-v1.5: quality, 768 dims, ~3 files/10sec
|
|
163
|
+
this.model = await pipeline('feature-extraction', EMBEDDING_MODEL, {
|
|
153
164
|
progress_callback: DEBUG ? undefined : null // Suppress progress bar unless DEBUG
|
|
154
165
|
});
|
|
155
|
-
if (DEBUG) console.log(
|
|
166
|
+
if (DEBUG) console.log(`[vectorizer] Model loaded: ${EMBEDDING_MODEL}`);
|
|
156
167
|
}
|
|
157
168
|
return this.model;
|
|
158
169
|
}
|
|
@@ -485,6 +496,7 @@ class CodebaseIndexer {
|
|
|
485
496
|
return {
|
|
486
497
|
indexName: this.indexName,
|
|
487
498
|
description: preset?.description || 'Custom index',
|
|
499
|
+
model: EMBEDDING_MODEL,
|
|
488
500
|
fileCount,
|
|
489
501
|
chunkCount
|
|
490
502
|
};
|
|
@@ -553,4 +565,9 @@ class CodebaseIndexer {
|
|
|
553
565
|
}
|
|
554
566
|
}
|
|
555
567
|
|
|
556
|
-
|
|
568
|
+
// Getter for current embedding model (after config loaded)
|
|
569
|
+
function getEmbeddingModel() {
|
|
570
|
+
return EMBEDDING_MODEL;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
export { CodebaseIndexer, INDEX_PRESETS, getEmbeddingModel };
|