bluera-knowledge 0.11.6 → 0.11.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bluera-knowledge",
3
- "version": "0.11.6",
3
+ "version": "0.11.7",
4
4
  "description": "CLI tool for managing knowledge stores with semantic search",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,7 +38,8 @@
38
38
  "version:patch": "bun run prerelease && commit-and-tag-version --release-as patch --skip.commit --skip.tag",
39
39
  "release:patch": "commit-and-tag-version --release-as patch && git push --follow-tags",
40
40
  "release:minor": "commit-and-tag-version --release-as minor && git push --follow-tags",
41
- "release:major": "commit-and-tag-version --release-as major && git push --follow-tags"
41
+ "release:major": "commit-and-tag-version --release-as major && git push --follow-tags",
42
+ "validate:npm": "./scripts/validate-npm-release.sh"
42
43
  },
43
44
  "keywords": [
44
45
  "knowledge",
@@ -0,0 +1,211 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # validate-npm-release.sh
4
+ #
5
+ # Post-release validation script for bluera-knowledge npm module.
6
+ # Installs the latest version from npm and exercises all CLI commands.
7
+ #
8
+ # Usage:
9
+ # ./scripts/validate-npm-release.sh
10
+ #
11
+ # Output:
12
+ # Logs written to: <repo>/logs/validation/npm-validation-YYYYMMDD-HHMMSS.log
13
+ # Exit code: 0 if all tests pass, 1 if any fail
14
+ #
15
+
16
+ set -euo pipefail
17
+
18
+ # Configuration
19
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
+ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
21
+ RESULTS_DIR="$REPO_ROOT/logs/validation"
22
+ TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
23
+ LOG_FILE="$RESULTS_DIR/npm-validation-$TIMESTAMP.log"
24
+
25
+ # Test configuration
26
+ TEST_STORE="npm-validation-test-$TIMESTAMP"
27
+ TEST_FOLDER="$(mktemp -d)"
28
+ DATA_DIR="$(mktemp -d)"
29
+
30
+ # Counters
31
+ TESTS_RUN=0
32
+ TESTS_PASSED=0
33
+ TESTS_FAILED=0
34
+
35
+ # Setup
36
+ mkdir -p "$RESULTS_DIR"
37
+ echo "Test content for validation" > "$TEST_FOLDER/test.txt"
38
+ echo "Another test file" > "$TEST_FOLDER/test2.md"
39
+
40
+ # Logging functions
41
+ log() {
42
+ echo "[$(date +%H:%M:%S)] $*" | tee -a "$LOG_FILE"
43
+ }
44
+
45
+ log_header() {
46
+ echo "" | tee -a "$LOG_FILE"
47
+ echo "========================================" | tee -a "$LOG_FILE"
48
+ echo "$*" | tee -a "$LOG_FILE"
49
+ echo "========================================" | tee -a "$LOG_FILE"
50
+ }
51
+
52
+ pass() {
53
+ TESTS_RUN=$((TESTS_RUN + 1))
54
+ TESTS_PASSED=$((TESTS_PASSED + 1))
55
+ log "✓ PASS: $*"
56
+ }
57
+
58
+ fail() {
59
+ TESTS_RUN=$((TESTS_RUN + 1))
60
+ TESTS_FAILED=$((TESTS_FAILED + 1))
61
+ log "✗ FAIL: $*"
62
+ }
63
+
64
+ # Run a command and check exit code
65
+ run_test() {
66
+ local name="$1"
67
+ shift
68
+ local cmd="$*"
69
+
70
+ log "Running: $cmd"
71
+ if eval "$cmd" >> "$LOG_FILE" 2>&1; then
72
+ pass "$name"
73
+ return 0
74
+ else
75
+ fail "$name (exit code: $?)"
76
+ return 1
77
+ fi
78
+ }
79
+
80
+ # Run a command and check output contains expected string
81
+ run_test_contains() {
82
+ local name="$1"
83
+ local expected="$2"
84
+ shift 2
85
+ local cmd="$*"
86
+
87
+ log "Running: $cmd"
88
+ local output
89
+ if output=$(eval "$cmd" 2>&1); then
90
+ if echo "$output" | grep -q "$expected"; then
91
+ pass "$name"
92
+ echo "$output" >> "$LOG_FILE"
93
+ return 0
94
+ else
95
+ fail "$name (output missing: $expected)"
96
+ echo "$output" >> "$LOG_FILE"
97
+ return 1
98
+ fi
99
+ else
100
+ fail "$name (command failed)"
101
+ echo "$output" >> "$LOG_FILE"
102
+ return 1
103
+ fi
104
+ }
105
+
106
+ # Cleanup function
107
+ cleanup() {
108
+ log_header "Cleanup"
109
+
110
+ # Delete test store if it exists
111
+ log "Deleting test store: $TEST_STORE"
112
+ bluera-knowledge store delete "$TEST_STORE" --force -d "$DATA_DIR" 2>/dev/null || true
113
+
114
+ # Remove test folder
115
+ log "Removing test folder: $TEST_FOLDER"
116
+ rm -rf "$TEST_FOLDER"
117
+
118
+ # Remove data directory
119
+ log "Removing data directory: $DATA_DIR"
120
+ rm -rf "$DATA_DIR"
121
+
122
+ log "Cleanup complete"
123
+ }
124
+
125
+ # Set trap for cleanup on exit
126
+ trap cleanup EXIT
127
+
128
+ # Start validation
129
+ log_header "NPM Module Validation - $TIMESTAMP"
130
+ log "Log file: $LOG_FILE"
131
+ log "Test store: $TEST_STORE"
132
+ log "Test folder: $TEST_FOLDER"
133
+ log "Data directory: $DATA_DIR"
134
+
135
+ # Install latest from npm
136
+ log_header "Installing Latest from npm"
137
+ log "Installing bluera-knowledge@latest globally..."
138
+ if npm install -g bluera-knowledge@latest >> "$LOG_FILE" 2>&1; then
139
+ pass "npm install -g bluera-knowledge@latest"
140
+ else
141
+ fail "npm install -g bluera-knowledge@latest"
142
+ log "ERROR: Failed to install package. Aborting."
143
+ exit 1
144
+ fi
145
+
146
+ # Verify installation
147
+ log_header "Verifying Installation"
148
+
149
+ run_test_contains "bluera-knowledge --version" "0." "bluera-knowledge --version"
150
+
151
+ run_test_contains "bluera-knowledge --help" "CLI tool for managing knowledge stores" "bluera-knowledge --help"
152
+
153
+ # Test stores list (should work even if empty)
154
+ log_header "Testing Store Operations"
155
+
156
+ run_test "bluera-knowledge stores (initial list)" "bluera-knowledge stores -d '$DATA_DIR' -f json"
157
+
158
+ # Create a store via add-folder
159
+ log_header "Testing add-folder"
160
+
161
+ run_test "bluera-knowledge add-folder" "bluera-knowledge add-folder '$TEST_FOLDER' --name '$TEST_STORE' -d '$DATA_DIR'"
162
+
163
+ # Verify store was created
164
+ run_test_contains "Store appears in list" "$TEST_STORE" "bluera-knowledge stores -d '$DATA_DIR'"
165
+
166
+ # Test store info
167
+ log_header "Testing store info"
168
+
169
+ run_test_contains "bluera-knowledge store info" "$TEST_STORE" "bluera-knowledge store info '$TEST_STORE' -d '$DATA_DIR'"
170
+
171
+ # Test search (may return no results, but should not error)
172
+ log_header "Testing search"
173
+
174
+ run_test "bluera-knowledge search" "bluera-knowledge search 'test content' --store '$TEST_STORE' -d '$DATA_DIR' -f json"
175
+
176
+ # Test index command (re-index)
177
+ log_header "Testing index"
178
+
179
+ run_test "bluera-knowledge index" "bluera-knowledge index '$TEST_STORE' -d '$DATA_DIR'"
180
+
181
+ # Test search again after re-indexing
182
+ run_test "bluera-knowledge search (after reindex)" "bluera-knowledge search 'validation' --store '$TEST_STORE' -d '$DATA_DIR' -f json"
183
+
184
+ # Test store delete
185
+ log_header "Testing store delete"
186
+
187
+ run_test "bluera-knowledge store delete" "bluera-knowledge store delete '$TEST_STORE' --force -d '$DATA_DIR'"
188
+
189
+ # Verify store was deleted
190
+ log "Verifying store was deleted..."
191
+ if bluera-knowledge stores -d "$DATA_DIR" -f json 2>&1 | grep -q "$TEST_STORE"; then
192
+ fail "Store still exists after delete"
193
+ else
194
+ pass "Store successfully deleted"
195
+ fi
196
+
197
+ # Summary
198
+ log_header "Validation Summary"
199
+ log "Tests run: $TESTS_RUN"
200
+ log "Tests passed: $TESTS_PASSED"
201
+ log "Tests failed: $TESTS_FAILED"
202
+ log ""
203
+ log "Log file: $LOG_FILE"
204
+
205
+ if [ "$TESTS_FAILED" -gt 0 ]; then
206
+ log "VALIDATION FAILED"
207
+ exit 1
208
+ else
209
+ log "VALIDATION PASSED"
210
+ exit 0
211
+ fi
@@ -38,16 +38,25 @@ describe('Plugin API Commands - Execution Tests', () => {
38
38
  });
39
39
 
40
40
  describe('add-repo command', () => {
41
- it('calls handleAddRepo with url', async () => {
41
+ const expectedGlobalOpts = {
42
+ config: undefined,
43
+ dataDir: '/tmp/test',
44
+ projectRoot: undefined,
45
+ format: undefined,
46
+ quiet: false,
47
+ };
48
+
49
+ it('calls handleAddRepo with url and global options', async () => {
42
50
  const { handleAddRepo } = await import('../../plugin/commands.js');
43
51
 
44
52
  const command = createAddRepoCommand(getOptions);
45
53
  const actionHandler = (command as any)._actionHandler;
46
54
  await actionHandler(['https://github.com/user/repo.git']);
47
55
 
48
- expect(handleAddRepo).toHaveBeenCalledWith({
49
- url: 'https://github.com/user/repo.git',
50
- });
56
+ expect(handleAddRepo).toHaveBeenCalledWith(
57
+ { url: 'https://github.com/user/repo.git' },
58
+ expectedGlobalOpts
59
+ );
51
60
  });
52
61
 
53
62
  it('calls handleAddRepo with url and name option', async () => {
@@ -58,10 +67,10 @@ describe('Plugin API Commands - Execution Tests', () => {
58
67
  command.parseOptions(['--name', 'my-repo']);
59
68
  await actionHandler(['https://github.com/user/repo.git']);
60
69
 
61
- expect(handleAddRepo).toHaveBeenCalledWith({
62
- url: 'https://github.com/user/repo.git',
63
- name: 'my-repo',
64
- });
70
+ expect(handleAddRepo).toHaveBeenCalledWith(
71
+ { url: 'https://github.com/user/repo.git', name: 'my-repo' },
72
+ expectedGlobalOpts
73
+ );
65
74
  });
66
75
 
67
76
  it('calls handleAddRepo with url and branch option', async () => {
@@ -72,10 +81,10 @@ describe('Plugin API Commands - Execution Tests', () => {
72
81
  command.parseOptions(['--branch', 'develop']);
73
82
  await actionHandler(['https://github.com/user/repo.git']);
74
83
 
75
- expect(handleAddRepo).toHaveBeenCalledWith({
76
- url: 'https://github.com/user/repo.git',
77
- branch: 'develop',
78
- });
84
+ expect(handleAddRepo).toHaveBeenCalledWith(
85
+ { url: 'https://github.com/user/repo.git', branch: 'develop' },
86
+ expectedGlobalOpts
87
+ );
79
88
  });
80
89
 
81
90
  it('calls handleAddRepo with all options', async () => {
@@ -86,11 +95,10 @@ describe('Plugin API Commands - Execution Tests', () => {
86
95
  command.parseOptions(['--name', 'custom-name', '--branch', 'main']);
87
96
  await actionHandler(['https://github.com/user/repo.git']);
88
97
 
89
- expect(handleAddRepo).toHaveBeenCalledWith({
90
- url: 'https://github.com/user/repo.git',
91
- name: 'custom-name',
92
- branch: 'main',
93
- });
98
+ expect(handleAddRepo).toHaveBeenCalledWith(
99
+ { url: 'https://github.com/user/repo.git', name: 'custom-name', branch: 'main' },
100
+ expectedGlobalOpts
101
+ );
94
102
  });
95
103
 
96
104
  it('handles errors from handleAddRepo', async () => {
@@ -108,16 +116,22 @@ describe('Plugin API Commands - Execution Tests', () => {
108
116
  });
109
117
 
110
118
  describe('add-folder command', () => {
111
- it('calls handleAddFolder with path', async () => {
119
+ const expectedGlobalOpts = {
120
+ config: undefined,
121
+ dataDir: '/tmp/test',
122
+ projectRoot: undefined,
123
+ format: undefined,
124
+ quiet: false,
125
+ };
126
+
127
+ it('calls handleAddFolder with path and global options', async () => {
112
128
  const { handleAddFolder } = await import('../../plugin/commands.js');
113
129
 
114
130
  const command = createAddFolderCommand(getOptions);
115
131
  const actionHandler = (command as any)._actionHandler;
116
132
  await actionHandler(['/path/to/folder']);
117
133
 
118
- expect(handleAddFolder).toHaveBeenCalledWith({
119
- path: '/path/to/folder',
120
- });
134
+ expect(handleAddFolder).toHaveBeenCalledWith({ path: '/path/to/folder' }, expectedGlobalOpts);
121
135
  });
122
136
 
123
137
  it('calls handleAddFolder with path and name option', async () => {
@@ -128,10 +142,10 @@ describe('Plugin API Commands - Execution Tests', () => {
128
142
  command.parseOptions(['--name', 'my-folder']);
129
143
  await actionHandler(['/path/to/folder']);
130
144
 
131
- expect(handleAddFolder).toHaveBeenCalledWith({
132
- path: '/path/to/folder',
133
- name: 'my-folder',
134
- });
145
+ expect(handleAddFolder).toHaveBeenCalledWith(
146
+ { path: '/path/to/folder', name: 'my-folder' },
147
+ expectedGlobalOpts
148
+ );
135
149
  });
136
150
 
137
151
  it('handles relative paths', async () => {
@@ -141,9 +155,7 @@ describe('Plugin API Commands - Execution Tests', () => {
141
155
  const actionHandler = (command as any)._actionHandler;
142
156
  await actionHandler(['./relative/path']);
143
157
 
144
- expect(handleAddFolder).toHaveBeenCalledWith({
145
- path: './relative/path',
146
- });
158
+ expect(handleAddFolder).toHaveBeenCalledWith({ path: './relative/path' }, expectedGlobalOpts);
147
159
  });
148
160
 
149
161
  it('handles paths with spaces', async () => {
@@ -153,9 +165,10 @@ describe('Plugin API Commands - Execution Tests', () => {
153
165
  const actionHandler = (command as any)._actionHandler;
154
166
  await actionHandler(['/path/with spaces/folder']);
155
167
 
156
- expect(handleAddFolder).toHaveBeenCalledWith({
157
- path: '/path/with spaces/folder',
158
- });
168
+ expect(handleAddFolder).toHaveBeenCalledWith(
169
+ { path: '/path/with spaces/folder' },
170
+ expectedGlobalOpts
171
+ );
159
172
  });
160
173
 
161
174
  it('handles errors from handleAddFolder', async () => {
@@ -171,14 +184,20 @@ describe('Plugin API Commands - Execution Tests', () => {
171
184
  });
172
185
 
173
186
  describe('stores command', () => {
174
- it('calls handleStores with no arguments', async () => {
187
+ it('calls handleStores with global options', async () => {
175
188
  const { handleStores } = await import('../../plugin/commands.js');
176
189
 
177
190
  const command = createStoresCommand(getOptions);
178
191
  const actionHandler = (command as any)._actionHandler;
179
192
  await actionHandler([]);
180
193
 
181
- expect(handleStores).toHaveBeenCalledWith();
194
+ expect(handleStores).toHaveBeenCalledWith({
195
+ config: undefined,
196
+ dataDir: '/tmp/test',
197
+ projectRoot: undefined,
198
+ format: undefined,
199
+ quiet: false,
200
+ });
182
201
  });
183
202
 
184
203
  it('calls handleStores exactly once', async () => {
@@ -204,14 +223,20 @@ describe('Plugin API Commands - Execution Tests', () => {
204
223
  });
205
224
 
206
225
  describe('suggest command', () => {
207
- it('calls handleSuggest with no arguments', async () => {
226
+ it('calls handleSuggest with global options', async () => {
208
227
  const { handleSuggest } = await import('../../plugin/commands.js');
209
228
 
210
229
  const command = createSuggestCommand(getOptions);
211
230
  const actionHandler = (command as any)._actionHandler;
212
231
  await actionHandler([]);
213
232
 
214
- expect(handleSuggest).toHaveBeenCalledWith();
233
+ expect(handleSuggest).toHaveBeenCalledWith({
234
+ config: undefined,
235
+ dataDir: '/tmp/test',
236
+ projectRoot: undefined,
237
+ format: undefined,
238
+ quiet: false,
239
+ });
215
240
  });
216
241
 
217
242
  it('calls handleSuggest exactly once', async () => {
@@ -283,28 +308,65 @@ describe('Plugin API Commands - Execution Tests', () => {
283
308
  });
284
309
 
285
310
  describe('global options handling', () => {
286
- it('add-repo ignores global options', async () => {
311
+ it('add-repo passes global options', async () => {
287
312
  const { handleAddRepo } = await import('../../plugin/commands.js');
288
313
 
289
- // Global options should not be passed to handlers
290
314
  const command = createAddRepoCommand(getOptions);
291
315
  const actionHandler = (command as any)._actionHandler;
292
316
  await actionHandler(['https://github.com/user/repo.git']);
293
317
 
294
- expect(handleAddRepo).toHaveBeenCalledWith({
295
- url: 'https://github.com/user/repo.git',
296
- });
318
+ expect(handleAddRepo).toHaveBeenCalledWith(
319
+ { url: 'https://github.com/user/repo.git' },
320
+ {
321
+ config: undefined,
322
+ dataDir: '/tmp/test',
323
+ projectRoot: undefined,
324
+ format: undefined,
325
+ quiet: false,
326
+ }
327
+ );
297
328
  });
298
329
 
299
- it('add-folder ignores global options', async () => {
330
+ it('add-folder passes global options', async () => {
300
331
  const { handleAddFolder } = await import('../../plugin/commands.js');
301
332
 
302
333
  const command = createAddFolderCommand(getOptions);
303
334
  const actionHandler = (command as any)._actionHandler;
304
335
  await actionHandler(['/path']);
305
336
 
306
- expect(handleAddFolder).toHaveBeenCalledWith({
307
- path: '/path',
337
+ expect(handleAddFolder).toHaveBeenCalledWith(
338
+ { path: '/path' },
339
+ {
340
+ config: undefined,
341
+ dataDir: '/tmp/test',
342
+ projectRoot: undefined,
343
+ format: undefined,
344
+ quiet: false,
345
+ }
346
+ );
347
+ });
348
+
349
+ it('stores passes dataDir from global options', async () => {
350
+ const { handleStores } = await import('../../plugin/commands.js');
351
+
352
+ const customGetOptions = (): GlobalOptions => ({
353
+ config: '/custom/config.json',
354
+ dataDir: '/custom/data',
355
+ quiet: true,
356
+ format: 'json',
357
+ projectRoot: '/my/project',
358
+ });
359
+
360
+ const command = createStoresCommand(customGetOptions);
361
+ const actionHandler = (command as any)._actionHandler;
362
+ await actionHandler([]);
363
+
364
+ expect(handleStores).toHaveBeenCalledWith({
365
+ config: '/custom/config.json',
366
+ dataDir: '/custom/data',
367
+ projectRoot: '/my/project',
368
+ format: 'json',
369
+ quiet: true,
308
370
  });
309
371
  });
310
372
  });
@@ -12,37 +12,71 @@ import type { GlobalOptions } from '../program.js';
12
12
  * These commands provide a simpler interface that matches the plugin commands.
13
13
  */
14
14
 
15
- export function createAddRepoCommand(_getOptions: () => GlobalOptions): Command {
15
+ export function createAddRepoCommand(getOptions: () => GlobalOptions): Command {
16
16
  return new Command('add-repo')
17
17
  .description('Clone and index a library source repository')
18
18
  .argument('<url>', 'Git repository URL')
19
19
  .option('--name <name>', 'Store name (defaults to repo name)')
20
20
  .option('--branch <branch>', 'Git branch to clone')
21
21
  .action(async (url: string, options: { name?: string; branch?: string }) => {
22
- await handleAddRepo({ url, ...options });
22
+ const globalOpts = getOptions();
23
+ await handleAddRepo(
24
+ { url, ...options },
25
+ {
26
+ config: globalOpts.config,
27
+ dataDir: globalOpts.dataDir,
28
+ projectRoot: globalOpts.projectRoot,
29
+ format: globalOpts.format,
30
+ quiet: globalOpts.quiet,
31
+ }
32
+ );
23
33
  });
24
34
  }
25
35
 
26
- export function createAddFolderCommand(_getOptions: () => GlobalOptions): Command {
36
+ export function createAddFolderCommand(getOptions: () => GlobalOptions): Command {
27
37
  return new Command('add-folder')
28
38
  .description('Index a local folder of reference material')
29
39
  .argument('<path>', 'Folder path to index')
30
40
  .option('--name <name>', 'Store name (defaults to folder name)')
31
41
  .action(async (path: string, options: { name?: string }) => {
32
- await handleAddFolder({ path, ...options });
42
+ const globalOpts = getOptions();
43
+ await handleAddFolder(
44
+ { path, ...options },
45
+ {
46
+ config: globalOpts.config,
47
+ dataDir: globalOpts.dataDir,
48
+ projectRoot: globalOpts.projectRoot,
49
+ format: globalOpts.format,
50
+ quiet: globalOpts.quiet,
51
+ }
52
+ );
33
53
  });
34
54
  }
35
55
 
36
- export function createStoresCommand(_getOptions: () => GlobalOptions): Command {
56
+ export function createStoresCommand(getOptions: () => GlobalOptions): Command {
37
57
  return new Command('stores').description('List all indexed library stores').action(async () => {
38
- await handleStores();
58
+ const globalOpts = getOptions();
59
+ await handleStores({
60
+ config: globalOpts.config,
61
+ dataDir: globalOpts.dataDir,
62
+ projectRoot: globalOpts.projectRoot,
63
+ format: globalOpts.format,
64
+ quiet: globalOpts.quiet,
65
+ });
39
66
  });
40
67
  }
41
68
 
42
- export function createSuggestCommand(_getOptions: () => GlobalOptions): Command {
69
+ export function createSuggestCommand(getOptions: () => GlobalOptions): Command {
43
70
  return new Command('suggest')
44
71
  .description('Suggest important dependencies to add to knowledge stores')
45
72
  .action(async () => {
46
- await handleSuggest();
73
+ const globalOpts = getOptions();
74
+ await handleSuggest({
75
+ config: globalOpts.config,
76
+ dataDir: globalOpts.dataDir,
77
+ projectRoot: globalOpts.projectRoot,
78
+ format: globalOpts.format,
79
+ quiet: globalOpts.quiet,
80
+ });
47
81
  });
48
82
  }
@@ -4,6 +4,17 @@ import { DependencyUsageAnalyzer } from '../analysis/dependency-usage-analyzer.j
4
4
  import { RepoUrlResolver } from '../analysis/repo-url-resolver.js';
5
5
  import { createServices } from '../services/index.js';
6
6
 
7
+ /**
8
+ * Options passed from CLI global options to plugin command handlers.
9
+ */
10
+ export interface CommandOptions {
11
+ config?: string | undefined;
12
+ dataDir?: string | undefined;
13
+ projectRoot?: string | undefined;
14
+ format?: 'json' | 'table' | 'plain' | undefined;
15
+ quiet?: boolean | undefined;
16
+ }
17
+
7
18
  export async function handleSearch(args: {
8
19
  query: string;
9
20
  stores?: string;
@@ -45,13 +56,19 @@ export async function handleSearch(args: {
45
56
  }
46
57
  }
47
58
 
48
- export async function handleAddRepo(args: {
49
- url: string;
50
- name?: string;
51
- branch?: string;
52
- }): Promise<void> {
53
- // PWD is set by Claude Code to user's project directory
54
- const services = await createServices(undefined, undefined, process.env['PWD']);
59
+ export async function handleAddRepo(
60
+ args: {
61
+ url: string;
62
+ name?: string;
63
+ branch?: string;
64
+ },
65
+ options: CommandOptions = {}
66
+ ): Promise<void> {
67
+ const services = await createServices(
68
+ options.config,
69
+ options.dataDir,
70
+ options.projectRoot ?? process.env['PWD']
71
+ );
55
72
  const storeName = args.name ?? extractRepoName(args.url);
56
73
 
57
74
  console.log(`Cloning ${args.url}...`);
@@ -84,9 +101,15 @@ export async function handleAddRepo(args: {
84
101
  }
85
102
  }
86
103
 
87
- export async function handleAddFolder(args: { path: string; name?: string }): Promise<void> {
88
- // PWD is set by Claude Code to user's project directory
89
- const services = await createServices(undefined, undefined, process.env['PWD']);
104
+ export async function handleAddFolder(
105
+ args: { path: string; name?: string },
106
+ options: CommandOptions = {}
107
+ ): Promise<void> {
108
+ const services = await createServices(
109
+ options.config,
110
+ options.dataDir,
111
+ options.projectRoot ?? process.env['PWD']
112
+ );
90
113
  const { basename } = await import('node:path');
91
114
  const storeName = args.name ?? basename(args.path);
92
115
 
@@ -142,9 +165,12 @@ export async function handleIndex(args: { store: string }): Promise<void> {
142
165
  }
143
166
  }
144
167
 
145
- export async function handleStores(): Promise<void> {
146
- // PWD is set by Claude Code to user's project directory
147
- const services = await createServices(undefined, undefined, process.env['PWD']);
168
+ export async function handleStores(options: CommandOptions = {}): Promise<void> {
169
+ const services = await createServices(
170
+ options.config,
171
+ options.dataDir,
172
+ options.projectRoot ?? process.env['PWD']
173
+ );
148
174
  const stores = await services.store.list();
149
175
 
150
176
  if (stores.length === 0) {
@@ -176,14 +202,13 @@ export async function handleStores(): Promise<void> {
176
202
  }
177
203
  }
178
204
 
179
- export async function handleSuggest(): Promise<void> {
180
- // PWD is set by Claude Code to user's project directory
181
- const projectRoot = process.env['PWD'] ?? process.cwd();
205
+ export async function handleSuggest(options: CommandOptions = {}): Promise<void> {
206
+ const projectRoot = options.projectRoot ?? process.env['PWD'] ?? process.cwd();
182
207
 
183
208
  console.log('Analyzing project dependencies...\n');
184
209
 
185
210
  // Create analyzer instance
186
- const services = await createServices(undefined, undefined, projectRoot);
211
+ const services = await createServices(options.config, options.dataDir, projectRoot);
187
212
  const analyzer = new DependencyUsageAnalyzer();
188
213
  const resolver = new RepoUrlResolver();
189
214