bluera-knowledge 0.11.6 → 0.11.8
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/.claude/settings.local.json.example +5 -0
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +27 -0
- package/README.md +17 -1
- package/dist/index.js +63 -17
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/scripts/validate-npm-release.sh +209 -0
- package/src/cli/commands/plugin-api.test.ts +105 -43
- package/src/cli/commands/plugin-api.ts +42 -8
- package/src/plugin/commands.ts +42 -17
- package/src/scripts/validate-npm-release.test.ts +55 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bluera-knowledge",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.8",
|
|
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,209 @@
|
|
|
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" 2>&1 | tee -a "$LOG_FILE"; then
|
|
72
|
+
pass "$name"
|
|
73
|
+
return 0
|
|
74
|
+
else
|
|
75
|
+
fail "$name (exit code: ${PIPESTATUS[0]})"
|
|
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
|
+
# Capture output while also showing it on terminal via tee
|
|
90
|
+
if output=$(eval "$cmd" 2>&1 | tee -a "$LOG_FILE"); then
|
|
91
|
+
if echo "$output" | grep -q "$expected"; then
|
|
92
|
+
pass "$name"
|
|
93
|
+
return 0
|
|
94
|
+
else
|
|
95
|
+
fail "$name (output missing: $expected)"
|
|
96
|
+
return 1
|
|
97
|
+
fi
|
|
98
|
+
else
|
|
99
|
+
fail "$name (command failed)"
|
|
100
|
+
return 1
|
|
101
|
+
fi
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
# Cleanup function
|
|
105
|
+
cleanup() {
|
|
106
|
+
log_header "Cleanup"
|
|
107
|
+
|
|
108
|
+
# Delete test store if it exists
|
|
109
|
+
log "Deleting test store: $TEST_STORE"
|
|
110
|
+
bluera-knowledge store delete "$TEST_STORE" --force -d "$DATA_DIR" 2>/dev/null || true
|
|
111
|
+
|
|
112
|
+
# Remove test folder
|
|
113
|
+
log "Removing test folder: $TEST_FOLDER"
|
|
114
|
+
rm -rf "$TEST_FOLDER"
|
|
115
|
+
|
|
116
|
+
# Remove data directory
|
|
117
|
+
log "Removing data directory: $DATA_DIR"
|
|
118
|
+
rm -rf "$DATA_DIR"
|
|
119
|
+
|
|
120
|
+
log "Cleanup complete"
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# Set trap for cleanup on exit
|
|
124
|
+
trap cleanup EXIT
|
|
125
|
+
|
|
126
|
+
# Start validation
|
|
127
|
+
log_header "NPM Module Validation - $TIMESTAMP"
|
|
128
|
+
log "Log file: $LOG_FILE"
|
|
129
|
+
log "Test store: $TEST_STORE"
|
|
130
|
+
log "Test folder: $TEST_FOLDER"
|
|
131
|
+
log "Data directory: $DATA_DIR"
|
|
132
|
+
|
|
133
|
+
# Install latest from npm
|
|
134
|
+
log_header "Installing Latest from npm"
|
|
135
|
+
log "Installing bluera-knowledge@latest globally..."
|
|
136
|
+
if npm install -g bluera-knowledge@latest >> "$LOG_FILE" 2>&1; then
|
|
137
|
+
pass "npm install -g bluera-knowledge@latest"
|
|
138
|
+
else
|
|
139
|
+
fail "npm install -g bluera-knowledge@latest"
|
|
140
|
+
log "ERROR: Failed to install package. Aborting."
|
|
141
|
+
exit 1
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
# Verify installation
|
|
145
|
+
log_header "Verifying Installation"
|
|
146
|
+
|
|
147
|
+
run_test_contains "bluera-knowledge --version" "0." "bluera-knowledge --version"
|
|
148
|
+
|
|
149
|
+
run_test_contains "bluera-knowledge --help" "CLI tool for managing knowledge stores" "bluera-knowledge --help"
|
|
150
|
+
|
|
151
|
+
# Test stores list (should work even if empty)
|
|
152
|
+
log_header "Testing Store Operations"
|
|
153
|
+
|
|
154
|
+
run_test "bluera-knowledge stores (initial list)" "bluera-knowledge stores -d '$DATA_DIR' -f json"
|
|
155
|
+
|
|
156
|
+
# Create a store via add-folder
|
|
157
|
+
log_header "Testing add-folder"
|
|
158
|
+
|
|
159
|
+
run_test "bluera-knowledge add-folder" "bluera-knowledge add-folder '$TEST_FOLDER' --name '$TEST_STORE' -d '$DATA_DIR'"
|
|
160
|
+
|
|
161
|
+
# Verify store was created
|
|
162
|
+
run_test_contains "Store appears in list" "$TEST_STORE" "bluera-knowledge stores -d '$DATA_DIR'"
|
|
163
|
+
|
|
164
|
+
# Test store info
|
|
165
|
+
log_header "Testing store info"
|
|
166
|
+
|
|
167
|
+
run_test_contains "bluera-knowledge store info" "$TEST_STORE" "bluera-knowledge store info '$TEST_STORE' -d '$DATA_DIR'"
|
|
168
|
+
|
|
169
|
+
# Test search (may return no results, but should not error)
|
|
170
|
+
log_header "Testing search"
|
|
171
|
+
|
|
172
|
+
run_test "bluera-knowledge search" "bluera-knowledge search 'test content' --store '$TEST_STORE' -d '$DATA_DIR' -f json"
|
|
173
|
+
|
|
174
|
+
# Test index command (re-index)
|
|
175
|
+
log_header "Testing index"
|
|
176
|
+
|
|
177
|
+
run_test "bluera-knowledge index" "bluera-knowledge index '$TEST_STORE' -d '$DATA_DIR'"
|
|
178
|
+
|
|
179
|
+
# Test search again after re-indexing
|
|
180
|
+
run_test "bluera-knowledge search (after reindex)" "bluera-knowledge search 'validation' --store '$TEST_STORE' -d '$DATA_DIR' -f json"
|
|
181
|
+
|
|
182
|
+
# Test store delete
|
|
183
|
+
log_header "Testing store delete"
|
|
184
|
+
|
|
185
|
+
run_test "bluera-knowledge store delete" "bluera-knowledge store delete '$TEST_STORE' --force -d '$DATA_DIR'"
|
|
186
|
+
|
|
187
|
+
# Verify store was deleted
|
|
188
|
+
log "Verifying store was deleted..."
|
|
189
|
+
if bluera-knowledge stores -d "$DATA_DIR" -f json 2>&1 | grep -q "$TEST_STORE"; then
|
|
190
|
+
fail "Store still exists after delete"
|
|
191
|
+
else
|
|
192
|
+
pass "Store successfully deleted"
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
# Summary
|
|
196
|
+
log_header "Validation Summary"
|
|
197
|
+
log "Tests run: $TESTS_RUN"
|
|
198
|
+
log "Tests passed: $TESTS_PASSED"
|
|
199
|
+
log "Tests failed: $TESTS_FAILED"
|
|
200
|
+
log ""
|
|
201
|
+
log "Log file: $LOG_FILE"
|
|
202
|
+
|
|
203
|
+
if [ "$TESTS_FAILED" -gt 0 ]; then
|
|
204
|
+
log "VALIDATION FAILED"
|
|
205
|
+
exit 1
|
|
206
|
+
else
|
|
207
|
+
log "VALIDATION PASSED"
|
|
208
|
+
exit 0
|
|
209
|
+
fi
|
|
@@ -38,16 +38,25 @@ describe('Plugin API Commands - Execution Tests', () => {
|
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
describe('add-repo command', () => {
|
|
41
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
56
|
+
export function createStoresCommand(getOptions: () => GlobalOptions): Command {
|
|
37
57
|
return new Command('stores').description('List all indexed library stores').action(async () => {
|
|
38
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
}
|
package/src/plugin/commands.ts
CHANGED
|
@@ -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(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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(
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
147
|
-
|
|
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
|
-
|
|
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(
|
|
211
|
+
const services = await createServices(options.config, options.dataDir, projectRoot);
|
|
187
212
|
const analyzer = new DependencyUsageAnalyzer();
|
|
188
213
|
const resolver = new RepoUrlResolver();
|
|
189
214
|
|