@comfanion/workflow 4.1.3 → 4.5.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/README.md +1 -2
- package/bin/cli.js +343 -5
- package/package.json +8 -3
- package/src/build-info.json +2 -1
- package/src/opencode/ARCHITECTURE.md +7 -6
- package/src/opencode/FLOW.yaml +157 -102
- package/src/opencode/agents/analyst.md +24 -20
- package/src/opencode/agents/architect.md +104 -39
- package/src/opencode/agents/change-manager.md +112 -250
- package/src/opencode/agents/coder.md +36 -19
- package/src/opencode/agents/crawler.md +180 -97
- package/src/opencode/agents/dev.md +117 -29
- package/src/opencode/agents/pm.md +25 -32
- package/src/opencode/agents/researcher.md +116 -241
- package/src/opencode/commands/architecture.md +1 -1
- package/src/opencode/commands/dev-story.md +1 -5
- package/src/opencode/commands/prd.md +1 -1
- package/src/opencode/commands/unit-docs.md +170 -0
- package/src/opencode/config.yaml +29 -0
- package/src/opencode/opencode.json +5 -0
- package/src/opencode/skills/adr-writing/SKILL.md +122 -159
- package/src/opencode/skills/adr-writing/template.md +130 -0
- package/src/opencode/skills/architecture-design/SKILL.md +113 -107
- package/src/opencode/skills/architecture-design/template.md +212 -0
- package/src/opencode/skills/architecture-validation/SKILL.md +1 -1
- package/src/opencode/skills/changelog/template.md +23 -0
- package/src/opencode/{workflows/dev-story/instructions.md → skills/dev-story/SKILL.md} +2 -2
- package/src/opencode/skills/epic-writing/SKILL.md +116 -264
- package/src/opencode/skills/epic-writing/template.md +119 -0
- package/src/opencode/skills/prd-validation/SKILL.md +1 -1
- package/src/opencode/skills/prd-writing/SKILL.md +79 -43
- package/src/opencode/skills/prd-writing/template.md +147 -0
- package/src/opencode/skills/requirements-gathering/SKILL.md +128 -78
- package/src/opencode/skills/requirements-gathering/template.md +156 -0
- package/src/opencode/skills/story-writing/SKILL.md +106 -464
- package/src/opencode/skills/story-writing/template.md +214 -0
- package/src/opencode/skills/unit-writing/SKILL.md +185 -0
- package/src/opencode/skills/unit-writing/template.md +136 -0
- package/src/opencode/tools/codeindex.ts +255 -0
- package/src/opencode/tools/codesearch.ts +134 -0
- package/src/repo-structure/docs/README.md +5 -5
- package/src/repo-structure/docs/requirements/README.md +1 -1
- package/src/opencode/templates/CHANGELOG.md +0 -82
- package/src/opencode/templates/adr-template.md +0 -115
- package/src/opencode/templates/architecture-template.md +0 -362
- package/src/opencode/templates/epic-template.md +0 -166
- package/src/opencode/templates/prd-template.md +0 -479
- package/src/opencode/templates/requirements-template.md +0 -132
- package/src/opencode/templates/story-template.md +0 -182
- /package/src/opencode/{templates/prd-acceptance-criteria-template.md → skills/acceptance-criteria/template.md} +0 -0
- /package/src/opencode/{templates/change-proposal-template.md → skills/archiving/template-change-proposal.md} +0 -0
- /package/src/opencode/{templates/git-workflow-template.md → skills/coding-standards/template-git.md} +0 -0
- /package/src/opencode/{templates/testing-standards-template.md → skills/coding-standards/template-testing.md} +0 -0
- /package/src/opencode/{templates/jira-cache-template.yaml → skills/jira-integration/template-cache.yaml} +0 -0
- /package/src/opencode/{templates/module-index-template.md → skills/module-documentation/template.md} +0 -0
- /package/src/opencode/{templates/sprint-status-template.yaml → skills/sprint-planning/template.yaml} +0 -0
- /package/src/opencode/{templates/integration-tests-template.md → skills/test-design/template-integration.md} +0 -0
- /package/src/opencode/{templates/module-test-cases-template.md → skills/test-design/template-module.md} +0 -0
package/README.md
CHANGED
|
@@ -96,8 +96,7 @@ npx @comfanion/workflow config
|
|
|
96
96
|
├── config.yaml # Your configuration
|
|
97
97
|
├── FLOW.yaml # Workflow definition (v3.0)
|
|
98
98
|
├── agents/ # Agent personas
|
|
99
|
-
├── skills/ # Knowledge modules (25+)
|
|
100
|
-
├── templates/ # Document templates
|
|
99
|
+
├── skills/ # Knowledge modules with templates (25+)
|
|
101
100
|
├── workflows/ # Workflow instructions
|
|
102
101
|
├── checklists/ # Validation checklists
|
|
103
102
|
└── commands/ # Slash commands
|
package/bin/cli.js
CHANGED
|
@@ -7,11 +7,63 @@ import ora from 'ora';
|
|
|
7
7
|
import fs from 'fs-extra';
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import { fileURLToPath } from 'url';
|
|
10
|
+
import { execSync } from 'child_process';
|
|
10
11
|
|
|
11
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
13
|
const PACKAGE_DIR = path.join(__dirname, '..');
|
|
13
14
|
const OPENCODE_SRC = path.join(PACKAGE_DIR, 'src', 'opencode');
|
|
14
15
|
const REPO_TEMPLATES_SRC = path.join(PACKAGE_DIR, 'src', 'repo-structure');
|
|
16
|
+
const VECTORIZER_SRC = path.join(PACKAGE_DIR, 'src', 'vectorizer');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Install vectorizer module with dependencies
|
|
20
|
+
*/
|
|
21
|
+
async function installVectorizer(opencodeDir) {
|
|
22
|
+
console.log('');
|
|
23
|
+
const spinner = ora('Installing vectorizer & caching...').start();
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const vectorizerDir = path.join(opencodeDir, 'vectorizer');
|
|
27
|
+
|
|
28
|
+
// Copy vectorizer source files
|
|
29
|
+
spinner.text = 'Copying vectorizer files...';
|
|
30
|
+
await fs.ensureDir(vectorizerDir);
|
|
31
|
+
await fs.copy(VECTORIZER_SRC, vectorizerDir);
|
|
32
|
+
|
|
33
|
+
// Run npm install
|
|
34
|
+
spinner.text = 'Installing dependencies (~100MB, may take a minute)...';
|
|
35
|
+
execSync('npm install --no-audit --no-fund', {
|
|
36
|
+
cwd: vectorizerDir,
|
|
37
|
+
stdio: 'pipe',
|
|
38
|
+
timeout: 300000 // 5 min timeout
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Add to .gitignore
|
|
42
|
+
const gitignorePath = path.join(opencodeDir, '.gitignore');
|
|
43
|
+
let gitignore = '';
|
|
44
|
+
try { gitignore = await fs.readFile(gitignorePath, 'utf8'); } catch {}
|
|
45
|
+
if (!gitignore.includes('vectors/')) {
|
|
46
|
+
gitignore += '\n# Vectorizer cache\nvectors/\nvectorizer/node_modules/\n';
|
|
47
|
+
await fs.writeFile(gitignorePath, gitignore);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
spinner.succeed(chalk.green('Vectorizer installed!'));
|
|
51
|
+
|
|
52
|
+
console.log(chalk.cyan('\n🔍 Vectorizer ready:'));
|
|
53
|
+
console.log(`
|
|
54
|
+
Index codebase: ${chalk.cyan('npx opencode-workflow index')}
|
|
55
|
+
Search code: ${chalk.cyan('npx opencode-workflow search "your query"')}
|
|
56
|
+
`);
|
|
57
|
+
|
|
58
|
+
return true;
|
|
59
|
+
|
|
60
|
+
} catch (error) {
|
|
61
|
+
spinner.fail(chalk.yellow('Vectorizer installation failed (optional)'));
|
|
62
|
+
console.log(chalk.gray(` Error: ${error.message}`));
|
|
63
|
+
console.log(chalk.gray(' You can install later with: npx opencode-workflow vectorizer install'));
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
15
67
|
|
|
16
68
|
const program = new Command();
|
|
17
69
|
|
|
@@ -28,6 +80,7 @@ program
|
|
|
28
80
|
.option('--tdd', 'Use TDD methodology')
|
|
29
81
|
.option('--stub', 'Use STUB methodology')
|
|
30
82
|
.option('--full', 'Create full repo structure')
|
|
83
|
+
.option('--vectorizer', 'Install vectorizer for semantic code search')
|
|
31
84
|
.action(async (options) => {
|
|
32
85
|
console.log(chalk.blue.bold('\n🚀 OpenCode Workflow v3.7\n'));
|
|
33
86
|
|
|
@@ -43,6 +96,7 @@ program
|
|
|
43
96
|
jira_url: 'https://your-domain.atlassian.net',
|
|
44
97
|
jira_project: 'PROJ',
|
|
45
98
|
create_repo_structure: false,
|
|
99
|
+
install_vectorizer: false,
|
|
46
100
|
project_name: path.basename(process.cwd())
|
|
47
101
|
};
|
|
48
102
|
|
|
@@ -155,6 +209,12 @@ program
|
|
|
155
209
|
name: 'create_repo_structure',
|
|
156
210
|
message: 'Create full repository structure (README, CONTRIBUTING, .gitignore, docs/)?',
|
|
157
211
|
default: options.full || false
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
type: 'confirm',
|
|
215
|
+
name: 'install_vectorizer',
|
|
216
|
+
message: 'Install vectorizer & caching? (semantic code search, ~100MB)',
|
|
217
|
+
default: false
|
|
158
218
|
}
|
|
159
219
|
]);
|
|
160
220
|
|
|
@@ -165,6 +225,7 @@ program
|
|
|
165
225
|
if (options.stub) config.methodology = 'stub';
|
|
166
226
|
if (options.jira) config.jira_enabled = true;
|
|
167
227
|
if (options.full) config.create_repo_structure = true;
|
|
228
|
+
if (options.vectorizer) config.install_vectorizer = true;
|
|
168
229
|
}
|
|
169
230
|
|
|
170
231
|
const spinner = ora('Initializing OpenCode Workflow...').start();
|
|
@@ -259,7 +320,7 @@ program
|
|
|
259
320
|
// Create CHANGELOG.md if not exists
|
|
260
321
|
const changelogPath = path.join(process.cwd(), 'CHANGELOG.md');
|
|
261
322
|
if (!await fs.pathExists(changelogPath)) {
|
|
262
|
-
const changelogTemplate = path.join(targetDir, '
|
|
323
|
+
const changelogTemplate = path.join(targetDir, 'skills/changelog/template.md');
|
|
263
324
|
if (await fs.pathExists(changelogTemplate)) {
|
|
264
325
|
await fs.copy(changelogTemplate, changelogPath);
|
|
265
326
|
}
|
|
@@ -267,6 +328,11 @@ program
|
|
|
267
328
|
|
|
268
329
|
spinner.succeed(chalk.green('OpenCode Workflow initialized!'));
|
|
269
330
|
|
|
331
|
+
// Install vectorizer if requested
|
|
332
|
+
if (config.install_vectorizer) {
|
|
333
|
+
await installVectorizer(targetDir);
|
|
334
|
+
}
|
|
335
|
+
|
|
270
336
|
// Show summary
|
|
271
337
|
console.log(chalk.yellow('\n📁 Created structure:'));
|
|
272
338
|
console.log(`
|
|
@@ -274,8 +340,7 @@ program
|
|
|
274
340
|
├── config.yaml # Your configuration
|
|
275
341
|
├── FLOW.yaml # Workflow definition
|
|
276
342
|
├── agents/ # Agent personas (analyst, pm, architect, sm, dev)
|
|
277
|
-
├── skills/ # Knowledge modules
|
|
278
|
-
├── templates/ # Document templates
|
|
343
|
+
├── skills/ # Knowledge modules (with templates)
|
|
279
344
|
├── workflows/ # Workflow instructions
|
|
280
345
|
└── checklists/ # Validation checklists
|
|
281
346
|
|
|
@@ -400,7 +465,6 @@ program
|
|
|
400
465
|
{ name: 'FLOW.yaml', path: '.opencode/FLOW.yaml', required: true },
|
|
401
466
|
{ name: 'agents/', path: '.opencode/agents', required: true },
|
|
402
467
|
{ name: 'skills/', path: '.opencode/skills', required: true },
|
|
403
|
-
{ name: 'templates/', path: '.opencode/templates', required: true },
|
|
404
468
|
{ name: 'docs/', path: 'docs', required: true },
|
|
405
469
|
{ name: 'docs/sprint-artifacts/', path: 'docs/sprint-artifacts', required: true },
|
|
406
470
|
{ name: 'docs/requirements/', path: 'docs/requirements', required: true },
|
|
@@ -463,10 +527,38 @@ program
|
|
|
463
527
|
console.log(chalk.gray(' ○ Jira credentials not set'));
|
|
464
528
|
}
|
|
465
529
|
|
|
530
|
+
// Check Vectorizer
|
|
531
|
+
console.log(chalk.cyan('\nVectorizer (semantic search):'));
|
|
532
|
+
const vectorizerInstalled = await fs.pathExists(path.join(process.cwd(), '.opencode', 'vectorizer', 'node_modules'));
|
|
533
|
+
const vectorsExist = await fs.pathExists(path.join(process.cwd(), '.opencode', 'vectors', 'hashes.json'));
|
|
534
|
+
|
|
535
|
+
if (vectorizerInstalled) {
|
|
536
|
+
console.log(chalk.green(' ✅ Installed'));
|
|
537
|
+
if (vectorsExist) {
|
|
538
|
+
try {
|
|
539
|
+
const hashes = await fs.readJSON(path.join(process.cwd(), '.opencode', 'vectors', 'hashes.json'));
|
|
540
|
+
console.log(chalk.green(` ✅ Indexed (${Object.keys(hashes).length} files)`));
|
|
541
|
+
} catch {
|
|
542
|
+
console.log(chalk.gray(' ○ Not indexed yet'));
|
|
543
|
+
}
|
|
544
|
+
} else {
|
|
545
|
+
console.log(chalk.gray(' ○ Not indexed (run: npx opencode-workflow index)'));
|
|
546
|
+
}
|
|
547
|
+
} else {
|
|
548
|
+
console.log(chalk.gray(' ○ Not installed (run: npx opencode-workflow vectorizer install)'));
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// Check LSP env
|
|
552
|
+
if (process.env.OPENCODE_EXPERIMENTAL_LSP_TOOL === 'true' || process.env.OPENCODE_EXPERIMENTAL === 'true') {
|
|
553
|
+
console.log(chalk.green(' ✅ LSP tool enabled'));
|
|
554
|
+
} else {
|
|
555
|
+
console.log(chalk.gray(' ○ LSP tool disabled (set OPENCODE_EXPERIMENTAL_LSP_TOOL=true)'));
|
|
556
|
+
}
|
|
557
|
+
|
|
466
558
|
console.log('');
|
|
467
559
|
|
|
468
560
|
if (hasErrors) {
|
|
469
|
-
console.log(chalk.yellow('💡 Run `npx
|
|
561
|
+
console.log(chalk.yellow('💡 Run `npx opencode-workflow init` to fix missing files.\n'));
|
|
470
562
|
} else {
|
|
471
563
|
console.log(chalk.green.bold('✅ All checks passed!\n'));
|
|
472
564
|
}
|
|
@@ -486,4 +578,250 @@ program
|
|
|
486
578
|
}
|
|
487
579
|
});
|
|
488
580
|
|
|
581
|
+
// =============================================================================
|
|
582
|
+
// VECTORIZER COMMANDS
|
|
583
|
+
// =============================================================================
|
|
584
|
+
|
|
585
|
+
program
|
|
586
|
+
.command('vectorizer')
|
|
587
|
+
.description('Manage vectorizer for semantic code search')
|
|
588
|
+
.argument('<action>', 'install | status | uninstall')
|
|
589
|
+
.action(async (action) => {
|
|
590
|
+
const opencodeDir = path.join(process.cwd(), '.opencode');
|
|
591
|
+
const vectorizerDir = path.join(opencodeDir, 'vectorizer');
|
|
592
|
+
|
|
593
|
+
if (action === 'install') {
|
|
594
|
+
if (!await fs.pathExists(opencodeDir)) {
|
|
595
|
+
console.log(chalk.red('\n❌ .opencode/ not found. Run `init` first.\n'));
|
|
596
|
+
process.exit(1);
|
|
597
|
+
}
|
|
598
|
+
await installVectorizer(opencodeDir);
|
|
599
|
+
|
|
600
|
+
} else if (action === 'status') {
|
|
601
|
+
const installed = await fs.pathExists(path.join(vectorizerDir, 'node_modules'));
|
|
602
|
+
const indexed = await fs.pathExists(path.join(process.cwd(), '.opencode', 'vectors', 'lancedb'));
|
|
603
|
+
|
|
604
|
+
console.log(chalk.blue.bold('\n🔍 Vectorizer Status\n'));
|
|
605
|
+
console.log(installed
|
|
606
|
+
? chalk.green(' ✅ Installed')
|
|
607
|
+
: chalk.gray(' ○ Not installed (run: npx opencode-workflow vectorizer install)'));
|
|
608
|
+
console.log(indexed
|
|
609
|
+
? chalk.green(' ✅ Codebase indexed')
|
|
610
|
+
: chalk.gray(' ○ Not indexed (run: npx opencode-workflow index)'));
|
|
611
|
+
|
|
612
|
+
if (indexed) {
|
|
613
|
+
try {
|
|
614
|
+
const hashes = await fs.readJSON(path.join(process.cwd(), '.opencode', 'vectors', 'hashes.json'));
|
|
615
|
+
console.log(chalk.cyan(` 📁 ${Object.keys(hashes).length} files indexed`));
|
|
616
|
+
} catch {}
|
|
617
|
+
}
|
|
618
|
+
console.log('');
|
|
619
|
+
|
|
620
|
+
} else if (action === 'uninstall') {
|
|
621
|
+
const spinner = ora('Removing vectorizer...').start();
|
|
622
|
+
await fs.remove(vectorizerDir);
|
|
623
|
+
await fs.remove(path.join(process.cwd(), '.opencode', 'vectors'));
|
|
624
|
+
spinner.succeed(chalk.green('Vectorizer removed'));
|
|
625
|
+
|
|
626
|
+
} else {
|
|
627
|
+
console.log(chalk.red(`Unknown action: ${action}`));
|
|
628
|
+
console.log('Available: install, status, uninstall');
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
program
|
|
633
|
+
.command('index')
|
|
634
|
+
.description('Index codebase for semantic search')
|
|
635
|
+
.option('-i, --index <name>', 'Index name: code, docs, config, all, or custom', 'code')
|
|
636
|
+
.option('-p, --pattern <glob>', 'File pattern (overrides preset)')
|
|
637
|
+
.option('--force', 'Re-index all files (ignore cache)')
|
|
638
|
+
.option('--list', 'List all indexes and their stats')
|
|
639
|
+
.action(async (options) => {
|
|
640
|
+
const vectorizerDir = path.join(process.cwd(), '.opencode', 'vectorizer');
|
|
641
|
+
|
|
642
|
+
if (!await fs.pathExists(path.join(vectorizerDir, 'node_modules'))) {
|
|
643
|
+
console.log(chalk.red('\n❌ Vectorizer not installed.'));
|
|
644
|
+
console.log(chalk.yellow('Run: npx opencode-workflow vectorizer install\n'));
|
|
645
|
+
process.exit(1);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const spinner = ora('Initializing indexer...').start();
|
|
649
|
+
|
|
650
|
+
try {
|
|
651
|
+
// Dynamic import of the vectorizer (need file:// URL for ESM)
|
|
652
|
+
const vectorizerPath = path.join(vectorizerDir, 'index.js');
|
|
653
|
+
const { CodebaseIndexer, INDEX_PRESETS } = await import(`file://${vectorizerPath}`);
|
|
654
|
+
|
|
655
|
+
// List all indexes
|
|
656
|
+
if (options.list) {
|
|
657
|
+
spinner.stop();
|
|
658
|
+
const indexer = await new CodebaseIndexer(process.cwd(), 'code').init();
|
|
659
|
+
const allStats = await indexer.getAllStats();
|
|
660
|
+
|
|
661
|
+
console.log(chalk.blue.bold('\n📊 Index Statistics\n'));
|
|
662
|
+
|
|
663
|
+
if (allStats.length === 0) {
|
|
664
|
+
console.log(chalk.gray(' No indexes found. Create one with:'));
|
|
665
|
+
console.log(chalk.cyan(' npx opencode-workflow index --index code'));
|
|
666
|
+
console.log(chalk.cyan(' npx opencode-workflow index --index docs\n'));
|
|
667
|
+
} else {
|
|
668
|
+
for (const stat of allStats) {
|
|
669
|
+
console.log(chalk.cyan(` 📁 ${stat.indexName}`));
|
|
670
|
+
console.log(chalk.gray(` ${stat.description}`));
|
|
671
|
+
console.log(` Files: ${stat.fileCount}, Chunks: ${stat.chunkCount}\n`);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
console.log(chalk.yellow('Available presets:'));
|
|
676
|
+
for (const [name, preset] of Object.entries(INDEX_PRESETS)) {
|
|
677
|
+
console.log(` ${chalk.cyan(name)}: ${preset.description}`);
|
|
678
|
+
console.log(chalk.gray(` Pattern: ${preset.pattern}\n`));
|
|
679
|
+
}
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const indexName = options.index;
|
|
684
|
+
const indexer = await new CodebaseIndexer(process.cwd(), indexName).init();
|
|
685
|
+
|
|
686
|
+
// Get pattern from options or preset
|
|
687
|
+
const preset = INDEX_PRESETS[indexName];
|
|
688
|
+
const pattern = options.pattern || preset?.pattern || '**/*.{js,ts,jsx,tsx,py,go,rs,java,md,yaml,yml}';
|
|
689
|
+
|
|
690
|
+
spinner.text = `Initializing index: ${indexName}...`;
|
|
691
|
+
|
|
692
|
+
if (options.force) {
|
|
693
|
+
spinner.text = `Clearing index: ${indexName}...`;
|
|
694
|
+
await indexer.clear();
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
// Find files to index
|
|
698
|
+
spinner.text = 'Finding files...';
|
|
699
|
+
const { glob } = await import('glob');
|
|
700
|
+
const { default: ignore } = await import('ignore');
|
|
701
|
+
|
|
702
|
+
// Load .gitignore
|
|
703
|
+
let ig = ignore();
|
|
704
|
+
try {
|
|
705
|
+
const gitignore = await fs.readFile(path.join(process.cwd(), '.gitignore'), 'utf8');
|
|
706
|
+
ig = ig.add(gitignore);
|
|
707
|
+
} catch {}
|
|
708
|
+
ig.add(['node_modules', '.git', 'dist', 'build', '.opencode/vectors', '.opencode/vectorizer']);
|
|
709
|
+
|
|
710
|
+
const files = await glob(pattern, { cwd: process.cwd(), nodir: true });
|
|
711
|
+
const filtered = files.filter(f => !ig.ignores(f));
|
|
712
|
+
|
|
713
|
+
spinner.succeed(`Found ${filtered.length} files for index "${indexName}"`);
|
|
714
|
+
|
|
715
|
+
let indexed = 0;
|
|
716
|
+
let skipped = 0;
|
|
717
|
+
|
|
718
|
+
for (const file of filtered) {
|
|
719
|
+
const filePath = path.join(process.cwd(), file);
|
|
720
|
+
spinner.start(`[${indexName}] Indexing: ${file}`);
|
|
721
|
+
|
|
722
|
+
try {
|
|
723
|
+
const wasIndexed = await indexer.indexFile(filePath);
|
|
724
|
+
if (wasIndexed) {
|
|
725
|
+
indexed++;
|
|
726
|
+
} else {
|
|
727
|
+
skipped++;
|
|
728
|
+
}
|
|
729
|
+
} catch (e) {
|
|
730
|
+
spinner.warn(`Skipped ${file}: ${e.message}`);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
spinner.succeed(chalk.green(`Index "${indexName}": ${indexed} indexed, ${skipped} unchanged`));
|
|
735
|
+
|
|
736
|
+
} catch (error) {
|
|
737
|
+
spinner.fail(chalk.red('Indexing failed'));
|
|
738
|
+
console.error(error);
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
program
|
|
743
|
+
.command('search')
|
|
744
|
+
.description('Semantic search in codebase')
|
|
745
|
+
.argument('<query>', 'Search query')
|
|
746
|
+
.option('-i, --index <name>', 'Index to search: code, docs, config, or custom', 'code')
|
|
747
|
+
.option('-n, --limit <number>', 'Number of results', '5')
|
|
748
|
+
.option('-a, --all', 'Search all indexes')
|
|
749
|
+
.action(async (query, options) => {
|
|
750
|
+
const vectorizerDir = path.join(process.cwd(), '.opencode', 'vectorizer');
|
|
751
|
+
|
|
752
|
+
if (!await fs.pathExists(path.join(vectorizerDir, 'node_modules'))) {
|
|
753
|
+
console.log(chalk.red('\n❌ Vectorizer not installed.'));
|
|
754
|
+
console.log(chalk.yellow('Run: npx opencode-workflow vectorizer install\n'));
|
|
755
|
+
process.exit(1);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
const spinner = ora('Searching...').start();
|
|
759
|
+
|
|
760
|
+
try {
|
|
761
|
+
// Dynamic import of the vectorizer (need file:// URL for ESM)
|
|
762
|
+
const vectorizerPath = path.join(vectorizerDir, 'index.js');
|
|
763
|
+
const { CodebaseIndexer } = await import(`file://${vectorizerPath}`);
|
|
764
|
+
|
|
765
|
+
let allResults = [];
|
|
766
|
+
const limit = parseInt(options.limit);
|
|
767
|
+
|
|
768
|
+
if (options.all) {
|
|
769
|
+
// Search all indexes
|
|
770
|
+
const tempIndexer = await new CodebaseIndexer(process.cwd(), 'code').init();
|
|
771
|
+
const indexes = await tempIndexer.listIndexes();
|
|
772
|
+
|
|
773
|
+
if (indexes.length === 0) {
|
|
774
|
+
spinner.stop();
|
|
775
|
+
console.log(chalk.yellow('\nNo indexes found. Run `npx opencode-workflow index` first.\n'));
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
for (const indexName of indexes) {
|
|
780
|
+
spinner.text = `Searching index: ${indexName}...`;
|
|
781
|
+
const indexer = await new CodebaseIndexer(process.cwd(), indexName).init();
|
|
782
|
+
const results = await indexer.search(query, limit);
|
|
783
|
+
allResults.push(...results.map(r => ({ ...r, _index: indexName })));
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
// Sort by distance and take top N
|
|
787
|
+
allResults.sort((a, b) => (a._distance || 0) - (b._distance || 0));
|
|
788
|
+
allResults = allResults.slice(0, limit);
|
|
789
|
+
|
|
790
|
+
} else {
|
|
791
|
+
// Search specific index
|
|
792
|
+
const indexer = await new CodebaseIndexer(process.cwd(), options.index).init();
|
|
793
|
+
const results = await indexer.search(query, limit);
|
|
794
|
+
allResults = results.map(r => ({ ...r, _index: options.index }));
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
spinner.stop();
|
|
798
|
+
|
|
799
|
+
if (allResults.length === 0) {
|
|
800
|
+
const indexInfo = options.all ? 'any index' : `index "${options.index}"`;
|
|
801
|
+
console.log(chalk.yellow(`\nNo results found in ${indexInfo}.`));
|
|
802
|
+
console.log(chalk.gray('Try: npx opencode-workflow index --index code\n'));
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
const searchScope = options.all ? 'all indexes' : `index "${options.index}"`;
|
|
807
|
+
console.log(chalk.blue.bold(`\n🔍 Results for: "${query}" (${searchScope})\n`));
|
|
808
|
+
|
|
809
|
+
for (let i = 0; i < allResults.length; i++) {
|
|
810
|
+
const r = allResults[i];
|
|
811
|
+
const score = r._distance ? (1 - r._distance).toFixed(3) : 'N/A';
|
|
812
|
+
const indexLabel = options.all ? chalk.magenta(`[${r._index}] `) : '';
|
|
813
|
+
console.log(chalk.cyan(`${i + 1}. ${indexLabel}${r.file}`) + chalk.gray(` (score: ${score})`));
|
|
814
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
815
|
+
// Show first 5 lines of content
|
|
816
|
+
const preview = r.content.split('\n').slice(0, 5).join('\n');
|
|
817
|
+
console.log(preview);
|
|
818
|
+
console.log('');
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
} catch (error) {
|
|
822
|
+
spinner.fail(chalk.red('Search failed'));
|
|
823
|
+
console.error(error);
|
|
824
|
+
}
|
|
825
|
+
});
|
|
826
|
+
|
|
489
827
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@comfanion/workflow",
|
|
3
|
-
"version": "4.
|
|
4
|
-
"description": "Initialize OpenCode Workflow system for AI-assisted development",
|
|
3
|
+
"version": "4.5.0",
|
|
4
|
+
"description": "Initialize OpenCode Workflow system for AI-assisted development with semantic code search",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"comfanion-workflow": "./bin/cli.js",
|
|
@@ -25,7 +25,10 @@
|
|
|
25
25
|
"prd",
|
|
26
26
|
"architecture",
|
|
27
27
|
"tdd",
|
|
28
|
-
"agile"
|
|
28
|
+
"agile",
|
|
29
|
+
"rag",
|
|
30
|
+
"vector-search",
|
|
31
|
+
"embeddings"
|
|
29
32
|
],
|
|
30
33
|
"author": "OpenCode Team",
|
|
31
34
|
"license": "MIT",
|
|
@@ -44,6 +47,8 @@
|
|
|
44
47
|
"chalk": "^5.3.0",
|
|
45
48
|
"commander": "^11.0.0",
|
|
46
49
|
"fs-extra": "^11.1.0",
|
|
50
|
+
"glob": "^10.5.0",
|
|
51
|
+
"ignore": "^5.3.0",
|
|
47
52
|
"inquirer": "^9.2.0",
|
|
48
53
|
"ora": "^7.0.0"
|
|
49
54
|
}
|
package/src/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "3.0.0",
|
|
3
|
-
"buildDate": "2026-01-
|
|
3
|
+
"buildDate": "2026-01-24T09:15:13.601Z",
|
|
4
4
|
"files": [
|
|
5
5
|
"config.yaml",
|
|
6
6
|
"FLOW.yaml",
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"workflows",
|
|
12
12
|
"checklists",
|
|
13
13
|
"commands",
|
|
14
|
+
"tools",
|
|
14
15
|
"opencode.json"
|
|
15
16
|
]
|
|
16
17
|
}
|
|
@@ -186,12 +186,13 @@ prd-writing:
|
|
|
186
186
|
│ └── integration-testing/
|
|
187
187
|
│ └── SKILL.md
|
|
188
188
|
│
|
|
189
|
-
└──
|
|
190
|
-
├──
|
|
191
|
-
├──
|
|
192
|
-
|
|
193
|
-
├──
|
|
194
|
-
├──
|
|
189
|
+
└── skills/ # Skills with co-located templates
|
|
190
|
+
├── prd-writing/
|
|
191
|
+
│ ├── SKILL.md
|
|
192
|
+
│ └── template.md
|
|
193
|
+
├── architecture-design/
|
|
194
|
+
│ ├── SKILL.md
|
|
195
|
+
│ └── template.md
|
|
195
196
|
└── ...
|
|
196
197
|
```
|
|
197
198
|
|