opencode-pilot 0.1.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/.devcontainer/devcontainer.json +16 -0
- package/.github/workflows/ci.yml +67 -0
- package/.releaserc.cjs +28 -0
- package/AGENTS.md +71 -0
- package/CONTRIBUTING.md +102 -0
- package/LICENSE +21 -0
- package/README.md +72 -0
- package/bin/opencode-pilot +809 -0
- package/dist/opencode-ntfy.tar.gz +0 -0
- package/examples/config.yaml +73 -0
- package/examples/templates/default.md +7 -0
- package/examples/templates/devcontainer.md +7 -0
- package/examples/templates/review-feedback.md +7 -0
- package/examples/templates/review.md +15 -0
- package/install.sh +246 -0
- package/package.json +40 -0
- package/plugin/config.js +76 -0
- package/plugin/index.js +260 -0
- package/plugin/logger.js +125 -0
- package/plugin/notifier.js +110 -0
- package/service/actions.js +334 -0
- package/service/io.opencode.ntfy.plist +29 -0
- package/service/logger.js +82 -0
- package/service/poll-service.js +246 -0
- package/service/poller.js +339 -0
- package/service/readiness.js +234 -0
- package/service/repo-config.js +222 -0
- package/service/server.js +1523 -0
- package/service/utils.js +21 -0
- package/test/run_tests.bash +34 -0
- package/test/test_actions.bash +263 -0
- package/test/test_cli.bash +161 -0
- package/test/test_config.bash +438 -0
- package/test/test_helper.bash +140 -0
- package/test/test_logger.bash +401 -0
- package/test/test_notifier.bash +310 -0
- package/test/test_plist.bash +125 -0
- package/test/test_plugin.bash +952 -0
- package/test/test_poll_service.bash +179 -0
- package/test/test_poller.bash +120 -0
- package/test/test_readiness.bash +313 -0
- package/test/test_repo_config.bash +406 -0
- package/test/test_service.bash +1342 -0
- package/test/unit/actions.test.js +235 -0
- package/test/unit/config.test.js +86 -0
- package/test/unit/paths.test.js +77 -0
- package/test/unit/poll-service.test.js +142 -0
- package/test/unit/poller.test.js +347 -0
- package/test/unit/repo-config.test.js +441 -0
- package/test/unit/utils.test.js +53 -0
package/service/utils.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* utils.js - Shared utility functions
|
|
3
|
+
*
|
|
4
|
+
* Common helpers used across service modules.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get a nested field value from an object using dot notation
|
|
9
|
+
* @param {object} obj - Object to traverse
|
|
10
|
+
* @param {string} path - Dot-separated path (e.g., "repository.full_name")
|
|
11
|
+
* @returns {*} Value at path, or undefined if not found
|
|
12
|
+
*/
|
|
13
|
+
export function getNestedValue(obj, path) {
|
|
14
|
+
const parts = path.split(".");
|
|
15
|
+
let value = obj;
|
|
16
|
+
for (const part of parts) {
|
|
17
|
+
if (value === null || value === undefined) return undefined;
|
|
18
|
+
value = value[part];
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Run all tests for opencode-ntfy
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
|
|
10
|
+
echo "========================================"
|
|
11
|
+
echo " opencode-ntfy test suite"
|
|
12
|
+
echo "========================================"
|
|
13
|
+
echo ""
|
|
14
|
+
|
|
15
|
+
FAILED=0
|
|
16
|
+
|
|
17
|
+
for test_file in "$SCRIPT_DIR"/test_*.bash; do
|
|
18
|
+
if [[ -f "$test_file" ]] && [[ "$test_file" != *"test_helper.bash"* ]]; then
|
|
19
|
+
echo "----------------------------------------"
|
|
20
|
+
if ! bash "$test_file"; then
|
|
21
|
+
FAILED=1
|
|
22
|
+
fi
|
|
23
|
+
echo ""
|
|
24
|
+
fi
|
|
25
|
+
done
|
|
26
|
+
|
|
27
|
+
echo "========================================"
|
|
28
|
+
if [[ $FAILED -eq 0 ]]; then
|
|
29
|
+
echo " All test suites passed!"
|
|
30
|
+
else
|
|
31
|
+
echo " Some tests failed!"
|
|
32
|
+
exit 1
|
|
33
|
+
fi
|
|
34
|
+
echo "========================================"
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Tests for actions.js - Action system for starting sessions
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
source "$SCRIPT_DIR/test_helper.bash"
|
|
10
|
+
|
|
11
|
+
SERVICE_DIR="$(dirname "$SCRIPT_DIR")/service"
|
|
12
|
+
|
|
13
|
+
echo "Testing actions.js module..."
|
|
14
|
+
echo ""
|
|
15
|
+
|
|
16
|
+
# =============================================================================
|
|
17
|
+
# File Structure Tests
|
|
18
|
+
# =============================================================================
|
|
19
|
+
|
|
20
|
+
test_actions_file_exists() {
|
|
21
|
+
assert_file_exists "$SERVICE_DIR/actions.js"
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
test_actions_js_syntax() {
|
|
25
|
+
if ! command -v node &>/dev/null; then
|
|
26
|
+
echo "SKIP: node not available"
|
|
27
|
+
return 0
|
|
28
|
+
fi
|
|
29
|
+
node --check "$SERVICE_DIR/actions.js" 2>&1 || {
|
|
30
|
+
echo "actions.js has syntax errors"
|
|
31
|
+
return 1
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
# =============================================================================
|
|
36
|
+
# Export Tests
|
|
37
|
+
# =============================================================================
|
|
38
|
+
|
|
39
|
+
test_actions_exports_execute_action() {
|
|
40
|
+
grep -q "export.*function executeAction\|export.*executeAction" "$SERVICE_DIR/actions.js" || {
|
|
41
|
+
echo "executeAction export not found"
|
|
42
|
+
return 1
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
test_actions_exports_build_command() {
|
|
47
|
+
grep -q "export.*function buildCommand\|export.*buildCommand" "$SERVICE_DIR/actions.js" || {
|
|
48
|
+
echo "buildCommand export not found"
|
|
49
|
+
return 1
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# =============================================================================
|
|
54
|
+
# Implementation Tests
|
|
55
|
+
# =============================================================================
|
|
56
|
+
|
|
57
|
+
test_actions_uses_opencode_run() {
|
|
58
|
+
grep -q "opencode.*run" "$SERVICE_DIR/actions.js" || {
|
|
59
|
+
echo "opencode run command not found"
|
|
60
|
+
return 1
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
test_actions_prompt_template() {
|
|
65
|
+
grep -q "prompt_template\|promptTemplate" "$SERVICE_DIR/actions.js" || {
|
|
66
|
+
echo "Prompt template support not found"
|
|
67
|
+
return 1
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
test_actions_calls_opencode() {
|
|
72
|
+
grep -q "opencode" "$SERVICE_DIR/actions.js" || {
|
|
73
|
+
echo "OpenCode invocation not found"
|
|
74
|
+
return 1
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
test_actions_builds_prompt_from_item() {
|
|
79
|
+
grep -q "title\|body" "$SERVICE_DIR/actions.js" || {
|
|
80
|
+
echo "Prompt building from item not found"
|
|
81
|
+
return 1
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# =============================================================================
|
|
86
|
+
# Functional Tests
|
|
87
|
+
# =============================================================================
|
|
88
|
+
|
|
89
|
+
test_actions_build_local_command() {
|
|
90
|
+
if ! command -v node &>/dev/null; then
|
|
91
|
+
echo "SKIP: node not available"
|
|
92
|
+
return 0
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
local result
|
|
96
|
+
result=$(node --experimental-vm-modules -e "
|
|
97
|
+
import { buildCommand } from './service/actions.js';
|
|
98
|
+
|
|
99
|
+
const item = {
|
|
100
|
+
title: 'Fix bug #123',
|
|
101
|
+
html_url: 'https://github.com/org/repo/issues/123',
|
|
102
|
+
number: 123
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const config = {
|
|
106
|
+
repo_path: '~/code/myrepo',
|
|
107
|
+
session: { name_template: 'issue-{number}' }
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const cmd = buildCommand(item, config);
|
|
111
|
+
|
|
112
|
+
if (!cmd.includes('opencode run')) {
|
|
113
|
+
console.log('FAIL: Local command should include opencode run');
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
// Check for cwd pattern (cd ~/code/myrepo && ...)
|
|
117
|
+
if (!cmd.includes('cd') || !cmd.includes('myrepo')) {
|
|
118
|
+
console.log('FAIL: Command should include cd to repo path, got: ' + cmd);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
console.log('PASS');
|
|
122
|
+
" 2>&1) || {
|
|
123
|
+
echo "Functional test failed: $result"
|
|
124
|
+
return 1
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if ! echo "$result" | grep -q "PASS"; then
|
|
128
|
+
echo "$result"
|
|
129
|
+
return 1
|
|
130
|
+
fi
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
test_actions_prompt_template_expansion() {
|
|
134
|
+
if ! command -v node &>/dev/null; then
|
|
135
|
+
echo "SKIP: node not available"
|
|
136
|
+
return 0
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
local result
|
|
140
|
+
result=$(node --experimental-vm-modules -e "
|
|
141
|
+
import { buildCommand } from './service/actions.js';
|
|
142
|
+
|
|
143
|
+
const item = {
|
|
144
|
+
title: 'Fix bug',
|
|
145
|
+
body: 'Details here',
|
|
146
|
+
number: 456
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Config with custom prompt template (e.g., for devcontainer)
|
|
150
|
+
const config = {
|
|
151
|
+
repo_path: '~/code/myrepo',
|
|
152
|
+
session: {
|
|
153
|
+
name_template: 'issue-{number}',
|
|
154
|
+
prompt_template: '/devcontainer issue-{number}\n\n{title}\n\n{body}'
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const cmd = buildCommand(item, config);
|
|
159
|
+
|
|
160
|
+
// Should include the expanded template
|
|
161
|
+
if (!cmd.includes('/devcontainer issue-456')) {
|
|
162
|
+
console.log('FAIL: Command should include expanded prompt template');
|
|
163
|
+
console.log('Got: ' + cmd);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
if (!cmd.includes('Fix bug')) {
|
|
167
|
+
console.log('FAIL: Command should include title from template');
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
console.log('PASS');
|
|
171
|
+
" 2>&1) || {
|
|
172
|
+
echo "Functional test failed: $result"
|
|
173
|
+
return 1
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if ! echo "$result" | grep -q "PASS"; then
|
|
177
|
+
echo "$result"
|
|
178
|
+
return 1
|
|
179
|
+
fi
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
test_actions_session_name_template() {
|
|
183
|
+
if ! command -v node &>/dev/null; then
|
|
184
|
+
echo "SKIP: node not available"
|
|
185
|
+
return 0
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
local result
|
|
189
|
+
result=$(node --experimental-vm-modules -e "
|
|
190
|
+
import { buildSessionName } from './service/actions.js';
|
|
191
|
+
|
|
192
|
+
const item = {
|
|
193
|
+
number: 42,
|
|
194
|
+
repo_key: 'myorg/backend',
|
|
195
|
+
repo_short: 'backend'
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const template = 'issue-{repo_short}-{number}';
|
|
199
|
+
const name = buildSessionName(template, item);
|
|
200
|
+
|
|
201
|
+
if (name !== 'issue-backend-42') {
|
|
202
|
+
console.log('FAIL: Expected issue-backend-42, got ' + name);
|
|
203
|
+
process.exit(1);
|
|
204
|
+
}
|
|
205
|
+
console.log('PASS');
|
|
206
|
+
" 2>&1) || {
|
|
207
|
+
echo "Functional test failed: $result"
|
|
208
|
+
return 1
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if ! echo "$result" | grep -q "PASS"; then
|
|
212
|
+
echo "$result"
|
|
213
|
+
return 1
|
|
214
|
+
fi
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
# =============================================================================
|
|
218
|
+
# Run Tests
|
|
219
|
+
# =============================================================================
|
|
220
|
+
|
|
221
|
+
echo "File Structure Tests:"
|
|
222
|
+
|
|
223
|
+
for test_func in \
|
|
224
|
+
test_actions_file_exists \
|
|
225
|
+
test_actions_js_syntax
|
|
226
|
+
do
|
|
227
|
+
run_test "${test_func#test_}" "$test_func"
|
|
228
|
+
done
|
|
229
|
+
|
|
230
|
+
echo ""
|
|
231
|
+
echo "Export Tests:"
|
|
232
|
+
|
|
233
|
+
for test_func in \
|
|
234
|
+
test_actions_exports_execute_action \
|
|
235
|
+
test_actions_exports_build_command
|
|
236
|
+
do
|
|
237
|
+
run_test "${test_func#test_}" "$test_func"
|
|
238
|
+
done
|
|
239
|
+
|
|
240
|
+
echo ""
|
|
241
|
+
echo "Implementation Tests:"
|
|
242
|
+
|
|
243
|
+
for test_func in \
|
|
244
|
+
test_actions_uses_opencode_run \
|
|
245
|
+
test_actions_prompt_template \
|
|
246
|
+
test_actions_calls_opencode \
|
|
247
|
+
test_actions_builds_prompt_from_item
|
|
248
|
+
do
|
|
249
|
+
run_test "${test_func#test_}" "$test_func"
|
|
250
|
+
done
|
|
251
|
+
|
|
252
|
+
echo ""
|
|
253
|
+
echo "Functional Tests:"
|
|
254
|
+
|
|
255
|
+
for test_func in \
|
|
256
|
+
test_actions_build_local_command \
|
|
257
|
+
test_actions_prompt_template_expansion \
|
|
258
|
+
test_actions_session_name_template
|
|
259
|
+
do
|
|
260
|
+
run_test "${test_func#test_}" "$test_func"
|
|
261
|
+
done
|
|
262
|
+
|
|
263
|
+
print_summary
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Tests for bin/opencode-pilot CLI
|
|
4
|
+
#
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
9
|
+
source "$SCRIPT_DIR/test_helper.bash"
|
|
10
|
+
|
|
11
|
+
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
12
|
+
CLI_PATH="$PROJECT_DIR/bin/opencode-pilot"
|
|
13
|
+
|
|
14
|
+
echo "Testing opencode-pilot CLI..."
|
|
15
|
+
echo ""
|
|
16
|
+
|
|
17
|
+
# =============================================================================
|
|
18
|
+
# File Structure Tests
|
|
19
|
+
# =============================================================================
|
|
20
|
+
|
|
21
|
+
test_cli_file_exists() {
|
|
22
|
+
assert_file_exists "$CLI_PATH"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
test_cli_is_executable() {
|
|
26
|
+
[[ -x "$CLI_PATH" ]] || {
|
|
27
|
+
echo "CLI is not executable"
|
|
28
|
+
return 1
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
test_cli_js_syntax() {
|
|
33
|
+
if ! command -v node &>/dev/null; then
|
|
34
|
+
echo "SKIP: node not available"
|
|
35
|
+
return 0
|
|
36
|
+
fi
|
|
37
|
+
node --check "$CLI_PATH" 2>&1 || {
|
|
38
|
+
echo "CLI has syntax errors"
|
|
39
|
+
return 1
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# =============================================================================
|
|
44
|
+
# Help Command Tests
|
|
45
|
+
# =============================================================================
|
|
46
|
+
|
|
47
|
+
test_cli_help_shows_usage() {
|
|
48
|
+
"$CLI_PATH" help 2>&1 | grep -q "Usage:" || {
|
|
49
|
+
echo "help command should show Usage"
|
|
50
|
+
return 1
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
test_cli_help_shows_setup_command() {
|
|
55
|
+
"$CLI_PATH" help 2>&1 | grep -q "setup" || {
|
|
56
|
+
echo "help command should show setup command"
|
|
57
|
+
return 1
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
test_cli_help_shows_status_command() {
|
|
62
|
+
"$CLI_PATH" help 2>&1 | grep -q "status" || {
|
|
63
|
+
echo "help command should show status command"
|
|
64
|
+
return 1
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
test_cli_default_shows_help() {
|
|
69
|
+
"$CLI_PATH" 2>&1 | grep -q "Usage:" || {
|
|
70
|
+
echo "default should show usage"
|
|
71
|
+
return 1
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
test_cli_unknown_command_shows_error() {
|
|
76
|
+
local output
|
|
77
|
+
output=$("$CLI_PATH" unknowncommand 2>&1) || true
|
|
78
|
+
|
|
79
|
+
echo "$output" | grep -q "Unknown command" || {
|
|
80
|
+
echo "unknown command should show error"
|
|
81
|
+
echo "Output: $output"
|
|
82
|
+
return 1
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# =============================================================================
|
|
87
|
+
# Status Command Tests
|
|
88
|
+
# =============================================================================
|
|
89
|
+
|
|
90
|
+
test_cli_status_shows_plugin_info() {
|
|
91
|
+
local output
|
|
92
|
+
output=$("$CLI_PATH" status 2>&1) || true
|
|
93
|
+
|
|
94
|
+
echo "$output" | grep -qi "plugin" || {
|
|
95
|
+
echo "status should show plugin info"
|
|
96
|
+
echo "Output: $output"
|
|
97
|
+
return 1
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
test_cli_status_shows_notification_config() {
|
|
102
|
+
local output
|
|
103
|
+
output=$("$CLI_PATH" status 2>&1) || true
|
|
104
|
+
|
|
105
|
+
echo "$output" | grep -qi "notification\|topic" || {
|
|
106
|
+
echo "status should show notification config"
|
|
107
|
+
echo "Output: $output"
|
|
108
|
+
return 1
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
test_cli_status_shows_polling_config() {
|
|
113
|
+
local output
|
|
114
|
+
output=$("$CLI_PATH" status 2>&1) || true
|
|
115
|
+
|
|
116
|
+
echo "$output" | grep -qi "polling\|repos.yaml" || {
|
|
117
|
+
echo "status should show polling config"
|
|
118
|
+
echo "Output: $output"
|
|
119
|
+
return 1
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# =============================================================================
|
|
124
|
+
# Run Tests
|
|
125
|
+
# =============================================================================
|
|
126
|
+
|
|
127
|
+
echo "File Structure Tests:"
|
|
128
|
+
|
|
129
|
+
for test_func in \
|
|
130
|
+
test_cli_file_exists \
|
|
131
|
+
test_cli_is_executable \
|
|
132
|
+
test_cli_js_syntax
|
|
133
|
+
do
|
|
134
|
+
run_test "${test_func#test_}" "$test_func"
|
|
135
|
+
done
|
|
136
|
+
|
|
137
|
+
echo ""
|
|
138
|
+
echo "Help Command Tests:"
|
|
139
|
+
|
|
140
|
+
for test_func in \
|
|
141
|
+
test_cli_help_shows_usage \
|
|
142
|
+
test_cli_help_shows_setup_command \
|
|
143
|
+
test_cli_help_shows_status_command \
|
|
144
|
+
test_cli_default_shows_help \
|
|
145
|
+
test_cli_unknown_command_shows_error
|
|
146
|
+
do
|
|
147
|
+
run_test "${test_func#test_}" "$test_func"
|
|
148
|
+
done
|
|
149
|
+
|
|
150
|
+
echo ""
|
|
151
|
+
echo "Status Command Tests:"
|
|
152
|
+
|
|
153
|
+
for test_func in \
|
|
154
|
+
test_cli_status_shows_plugin_info \
|
|
155
|
+
test_cli_status_shows_notification_config \
|
|
156
|
+
test_cli_status_shows_polling_config
|
|
157
|
+
do
|
|
158
|
+
run_test "${test_func#test_}" "$test_func"
|
|
159
|
+
done
|
|
160
|
+
|
|
161
|
+
print_summary
|