@cnrai/pave 0.3.35 → 0.3.51
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/LICENSE +21 -0
- package/README.md +21 -218
- package/package.json +32 -35
- package/pave.js +3 -0
- package/sandbox/SandboxRunner.js +1 -0
- package/sandbox/pave-run.js +2 -0
- package/sandbox/permission.js +1 -0
- package/sandbox/utils/yaml.js +1 -0
- package/MARKETPLACE.md +0 -406
- package/build-binary.js +0 -591
- package/build-npm.js +0 -537
- package/build.js +0 -230
- package/check-binary.js +0 -26
- package/deploy.sh +0 -95
- package/index.js +0 -5776
- package/lib/agent-registry.js +0 -1037
- package/lib/args-parser.js +0 -837
- package/lib/blessed-widget-patched.js +0 -93
- package/lib/cli-markdown.js +0 -590
- package/lib/compaction.js +0 -153
- package/lib/duration.js +0 -94
- package/lib/hash.js +0 -22
- package/lib/marketplace.js +0 -866
- package/lib/memory-config.js +0 -166
- package/lib/skill-manager.js +0 -891
- package/lib/soul.js +0 -31
- package/lib/tool-output-formatter.js +0 -180
- package/start-pave.sh +0 -149
- package/status.js +0 -271
- package/test/abort-stream.test.js +0 -445
- package/test/agent-auto-compaction.test.js +0 -552
- package/test/agent-comm-abort.test.js +0 -95
- package/test/agent-comm.test.js +0 -598
- package/test/agent-inbox.test.js +0 -576
- package/test/agent-init.test.js +0 -264
- package/test/agent-interrupt.test.js +0 -314
- package/test/agent-lifecycle.test.js +0 -520
- package/test/agent-log-files.test.js +0 -349
- package/test/agent-mode.manual-test.js +0 -392
- package/test/agent-parsing.test.js +0 -228
- package/test/agent-post-stream-idle.test.js +0 -762
- package/test/agent-registry.test.js +0 -359
- package/test/agent-rm.test.js +0 -442
- package/test/agent-spawn.test.js +0 -933
- package/test/agent-status-api.test.js +0 -624
- package/test/agent-update.test.js +0 -435
- package/test/args-parser.test.js +0 -391
- package/test/auto-compaction-chat.manual-test.js +0 -227
- package/test/auto-compaction.test.js +0 -941
- package/test/build-config.test.js +0 -120
- package/test/build-npm.test.js +0 -388
- package/test/chat-command.test.js +0 -137
- package/test/chat-leading-lines.test.js +0 -159
- package/test/config-flag.test.js +0 -272
- package/test/cursor-drift.test.js +0 -135
- package/test/debug-require.js +0 -23
- package/test/dir-migration.test.js +0 -323
- package/test/duration.test.js +0 -229
- package/test/ghostty-term.test.js +0 -202
- package/test/http500-backoff.test.js +0 -854
- package/test/integration.test.js +0 -86
- package/test/memory-guard-env.test.js +0 -220
- package/test/pr233-fixes.test.js +0 -259
- package/test/run-agent-init.js +0 -297
- package/test/run-all.js +0 -64
- package/test/run-config-flag.js +0 -159
- package/test/run-cursor-drift.js +0 -82
- package/test/run-session-path.js +0 -154
- package/test/run-tests.js +0 -643
- package/test/sandbox-redirect.test.js +0 -202
- package/test/session-path.test.js +0 -132
- package/test/shebang-strip.test.js +0 -241
- package/test/soul-reinject.test.js +0 -1027
- package/test/soul-reread.test.js +0 -281
- package/test/tool-output-formatter.test.js +0 -486
- package/test/tool-output-gating.test.js +0 -143
- package/test/tool-states.test.js +0 -167
- package/test/tools-flag.test.js +0 -65
- package/test/tui-attach.test.js +0 -1255
- package/test/tui-compaction.test.js +0 -354
- package/test/tui-wrap.test.js +0 -568
- package/test-binary.js +0 -52
- package/test-binary2.js +0 -36
package/test/run-agent-init.js
DELETED
|
@@ -1,297 +0,0 @@
|
|
|
1
|
-
// Standalone test runner for 'pave agent init' (issue #109)
|
|
2
|
-
// Tests args-parser changes and source code patterns.
|
|
3
|
-
// Designed for Node 16 sandbox compatibility (no module imports that use # fields).
|
|
4
|
-
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
|
|
8
|
-
let passed = 0;
|
|
9
|
-
let failed = 0;
|
|
10
|
-
|
|
11
|
-
function assert(cond, msg) {
|
|
12
|
-
if (cond) {
|
|
13
|
-
passed++;
|
|
14
|
-
console.log('\u2705 ' + msg);
|
|
15
|
-
} else {
|
|
16
|
-
failed++;
|
|
17
|
-
console.log('\u274C ' + msg);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function _assertEqual(actual, expected, msg) {
|
|
22
|
-
if (actual === expected) {
|
|
23
|
-
passed++;
|
|
24
|
-
console.log('\u2705 ' + msg);
|
|
25
|
-
} else {
|
|
26
|
-
failed++;
|
|
27
|
-
console.log('\u274C ' + msg + ' (expected ' + JSON.stringify(expected) + ', got ' + JSON.stringify(actual) + ')');
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// =============================================
|
|
32
|
-
// Read source files for pattern verification
|
|
33
|
-
// =============================================
|
|
34
|
-
|
|
35
|
-
const argsParserPath = path.join(__dirname, '..', 'lib', 'args-parser.js');
|
|
36
|
-
const indexPath = path.join(__dirname, '..', 'index.js');
|
|
37
|
-
const permissionPath = path.join(__dirname, '..', '..', 'opencode-lite', 'src', 'tools', 'permission.js');
|
|
38
|
-
|
|
39
|
-
const argsParserSrc = fs.readFileSync(argsParserPath, 'utf8');
|
|
40
|
-
const indexSrc = fs.readFileSync(indexPath, 'utf8');
|
|
41
|
-
const permissionSrc = fs.readFileSync(permissionPath, 'utf8');
|
|
42
|
-
|
|
43
|
-
console.log('Agent init tests (issue #109)');
|
|
44
|
-
console.log('========================================');
|
|
45
|
-
console.log('');
|
|
46
|
-
|
|
47
|
-
// =============================================
|
|
48
|
-
// Args-parser source verification
|
|
49
|
-
// =============================================
|
|
50
|
-
|
|
51
|
-
console.log('Args-parser changes:');
|
|
52
|
-
|
|
53
|
-
assert(argsParserSrc.indexOf('agentSubcommand') !== -1,
|
|
54
|
-
'args-parser defines agentSubcommand field');
|
|
55
|
-
|
|
56
|
-
assert(argsParserSrc.indexOf("agentSubcommand: null") !== -1,
|
|
57
|
-
'agentSubcommand defaults to null');
|
|
58
|
-
|
|
59
|
-
assert(argsParserSrc.indexOf("a === 'init'") !== -1,
|
|
60
|
-
'args-parser checks for init token in positional args');
|
|
61
|
-
|
|
62
|
-
assert(argsParserSrc.indexOf("agentSubcommand = 'init'") !== -1,
|
|
63
|
-
'args-parser sets agentSubcommand to init');
|
|
64
|
-
|
|
65
|
-
assert(argsParserSrc.indexOf("a === 'init' && args.agentSubcommand === null && args.commandArgs.length === 0") !== -1,
|
|
66
|
-
'args-parser detects init as first positional token in main loop');
|
|
67
|
-
|
|
68
|
-
assert(argsParserSrc.indexOf("agentSubcommand !== 'init'") !== -1,
|
|
69
|
-
'args-parser skips sleep/SOUL defaults for agent init');
|
|
70
|
-
|
|
71
|
-
assert(argsParserSrc.indexOf("agentSubcommand !== 'init' && args.commandArgs.length > 1") !== -1,
|
|
72
|
-
'args-parser skips SOUL validation/warnings for agent init');
|
|
73
|
-
|
|
74
|
-
assert(argsParserSrc.indexOf('agent init') !== -1,
|
|
75
|
-
'help text mentions agent init');
|
|
76
|
-
|
|
77
|
-
console.log('');
|
|
78
|
-
|
|
79
|
-
// =============================================
|
|
80
|
-
// Permission.js source verification
|
|
81
|
-
// =============================================
|
|
82
|
-
|
|
83
|
-
console.log('Permission system changes:');
|
|
84
|
-
|
|
85
|
-
assert(permissionSrc.indexOf('let PAVE_DIR') !== -1,
|
|
86
|
-
'PAVE_DIR is declared with let (mutable)');
|
|
87
|
-
|
|
88
|
-
assert(permissionSrc.indexOf('let TOKENS_FILE') !== -1,
|
|
89
|
-
'TOKENS_FILE is declared with let (mutable)');
|
|
90
|
-
|
|
91
|
-
assert(permissionSrc.indexOf('let PERMISSION_FILE') !== -1,
|
|
92
|
-
'PERMISSION_FILE is declared with let (mutable)');
|
|
93
|
-
|
|
94
|
-
assert(permissionSrc.indexOf('function setConfigDir(dir)') !== -1,
|
|
95
|
-
'setConfigDir function exists');
|
|
96
|
-
|
|
97
|
-
assert(permissionSrc.indexOf('function getConfigDir()') !== -1,
|
|
98
|
-
'getConfigDir function exists');
|
|
99
|
-
|
|
100
|
-
assert(permissionSrc.indexOf('function createDefaultPermissions()') !== -1,
|
|
101
|
-
'createDefaultPermissions function exists');
|
|
102
|
-
|
|
103
|
-
// Verify setConfigDir updates all three paths
|
|
104
|
-
const setConfigDirStart = permissionSrc.indexOf('function setConfigDir(dir)');
|
|
105
|
-
const setConfigDirEnd = permissionSrc.indexOf('\n/**', setConfigDirStart + 1);
|
|
106
|
-
if (setConfigDirStart !== -1 && setConfigDirEnd !== -1) {
|
|
107
|
-
const body = permissionSrc.substring(setConfigDirStart, setConfigDirEnd);
|
|
108
|
-
assert(body.indexOf('PAVE_DIR =') !== -1, 'setConfigDir updates PAVE_DIR');
|
|
109
|
-
assert(body.indexOf('TOKENS_FILE =') !== -1, 'setConfigDir updates TOKENS_FILE');
|
|
110
|
-
assert(body.indexOf('PERMISSION_FILE =') !== -1, 'setConfigDir updates PERMISSION_FILE');
|
|
111
|
-
assert(body.indexOf('_permissionsLoaded = false') !== -1, 'setConfigDir resets _permissionsLoaded');
|
|
112
|
-
assert(body.indexOf('_privateTokenValues.clear()') !== -1, 'setConfigDir clears private token values');
|
|
113
|
-
assert(body.indexOf('_lastEnvFileMtime = 0') !== -1, 'setConfigDir resets token file mtime');
|
|
114
|
-
assert(body.indexOf('_lastEnvFilePath = null') !== -1, 'setConfigDir resets token file path');
|
|
115
|
-
assert(body.indexOf('permissions.network.clear()') !== -1, 'setConfigDir resets network permissions');
|
|
116
|
-
assert(body.indexOf('permissions.filesystem.read.clear()') !== -1, 'setConfigDir resets filesystem.read permissions');
|
|
117
|
-
assert(body.indexOf('permissions.filesystem.write.clear()') !== -1, 'setConfigDir resets filesystem.write permissions');
|
|
118
|
-
assert(body.indexOf('permissions.modules.clear()') !== -1, 'setConfigDir resets modules permissions');
|
|
119
|
-
assert(body.indexOf('permissions.system.clear()') !== -1, 'setConfigDir resets system permissions');
|
|
120
|
-
assert(body.indexOf('tokenConfigs.clear()') !== -1, 'setConfigDir resets token configs');
|
|
121
|
-
assert(body.indexOf('loadPermissions(true)') !== -1, 'setConfigDir reloads permissions from new directory');
|
|
122
|
-
assert(body.indexOf('_permissionsLoaded = true') !== -1, 'setConfigDir sets _permissionsLoaded to true after reload');
|
|
123
|
-
} else {
|
|
124
|
-
failed++;
|
|
125
|
-
console.log(' FAIL: could not find setConfigDir body');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Verify createDefaultPermissions has correct structure
|
|
129
|
-
assert(permissionSrc.indexOf("version: 4") !== -1 ||
|
|
130
|
-
permissionSrc.indexOf("version: 4,") !== -1,
|
|
131
|
-
'createDefaultPermissions sets version 4');
|
|
132
|
-
|
|
133
|
-
assert(permissionSrc.indexOf("createDefaultPermissions") !== -1 &&
|
|
134
|
-
permissionSrc.substring(permissionSrc.indexOf('module.exports')).indexOf("createDefaultPermissions") !== -1,
|
|
135
|
-
'createDefaultPermissions is exported');
|
|
136
|
-
|
|
137
|
-
// Verify exports
|
|
138
|
-
const exportsSection = permissionSrc.substring(permissionSrc.indexOf('module.exports'));
|
|
139
|
-
assert(exportsSection.indexOf('setConfigDir') !== -1, 'setConfigDir is in exports');
|
|
140
|
-
assert(exportsSection.indexOf('getConfigDir') !== -1, 'getConfigDir is in exports');
|
|
141
|
-
assert(exportsSection.indexOf('createDefaultPermissions') !== -1, 'createDefaultPermissions is in exports');
|
|
142
|
-
|
|
143
|
-
console.log('');
|
|
144
|
-
|
|
145
|
-
// =============================================
|
|
146
|
-
// Index.js source verification
|
|
147
|
-
// =============================================
|
|
148
|
-
|
|
149
|
-
console.log('Index.js changes:');
|
|
150
|
-
|
|
151
|
-
assert(indexSrc.indexOf('Permission.setConfigDir(configPath)') !== -1,
|
|
152
|
-
'index.js calls Permission.setConfigDir in config setup');
|
|
153
|
-
|
|
154
|
-
assert(indexSrc.indexOf('Permission.createDefaultPermissions()') !== -1,
|
|
155
|
-
'index.js calls Permission.createDefaultPermissions in config setup');
|
|
156
|
-
|
|
157
|
-
assert(indexSrc.indexOf('handleAgentInit') !== -1,
|
|
158
|
-
'handleAgentInit function exists');
|
|
159
|
-
|
|
160
|
-
assert(indexSrc.indexOf("agentSubcommand === 'init'") !== -1,
|
|
161
|
-
'agent command checks for init subcommand');
|
|
162
|
-
|
|
163
|
-
// Verify handleAgentInit function exists and has correct structure
|
|
164
|
-
const initMatch = indexSrc.match(/function handleAgentInit\(args\)\s*\{/);
|
|
165
|
-
assert(initMatch !== null, 'handleAgentInit is defined as a function');
|
|
166
|
-
|
|
167
|
-
// Verify handleAgentInit calls setConfigDir and createDefaultPermissions
|
|
168
|
-
const initFuncStart = indexSrc.indexOf('function handleAgentInit(args)');
|
|
169
|
-
const initFuncEnd = indexSrc.indexOf('\n/**', initFuncStart + 1);
|
|
170
|
-
if (initFuncStart !== -1 && initFuncEnd !== -1) {
|
|
171
|
-
const initFuncBody = indexSrc.substring(initFuncStart, initFuncEnd);
|
|
172
|
-
assert(initFuncBody.indexOf('Permission.setConfigDir') !== -1,
|
|
173
|
-
'handleAgentInit calls Permission.setConfigDir');
|
|
174
|
-
assert(initFuncBody.indexOf('Permission.createDefaultPermissions') !== -1 ||
|
|
175
|
-
initFuncBody.indexOf('createDefaultPermissions') !== -1,
|
|
176
|
-
'handleAgentInit calls createDefaultPermissions');
|
|
177
|
-
assert(initFuncBody.indexOf('return 0') !== -1,
|
|
178
|
-
'handleAgentInit returns 0 on success');
|
|
179
|
-
assert(initFuncBody.indexOf('return 1') !== -1,
|
|
180
|
-
'handleAgentInit returns 1 on error');
|
|
181
|
-
assert(initFuncBody.indexOf('pave agent init') !== -1,
|
|
182
|
-
'handleAgentInit shows usage error for invalid args');
|
|
183
|
-
assert(initFuncBody.indexOf('hasCommandArgs') !== -1 || initFuncBody.indexOf('commandArgs') !== -1,
|
|
184
|
-
'handleAgentInit validates no extra command args');
|
|
185
|
-
assert(initFuncBody.indexOf('hasSleepFlag') !== -1 || initFuncBody.indexOf('args.sleep') !== -1,
|
|
186
|
-
'handleAgentInit validates no --sleep flag');
|
|
187
|
-
assert(initFuncBody.indexOf('hasSoulFlag') !== -1 || initFuncBody.indexOf('args.soul') !== -1,
|
|
188
|
-
'handleAgentInit validates no --soul flag');
|
|
189
|
-
assert(initFuncBody.indexOf('hasNameFlag') !== -1 || initFuncBody.indexOf('args.name') !== -1,
|
|
190
|
-
'handleAgentInit validates no --name flag');
|
|
191
|
-
assert(initFuncBody.indexOf('hasReinjectIntervalFlag') !== -1 || initFuncBody.indexOf('args.reinjectInterval') !== -1,
|
|
192
|
-
'handleAgentInit validates no --reinject-interval flag');
|
|
193
|
-
} else {
|
|
194
|
-
failed++;
|
|
195
|
-
console.log(' FAIL: could not extract handleAgentInit function body');
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// Verify help text includes init
|
|
199
|
-
assert(indexSrc.indexOf('pave agent init') !== -1,
|
|
200
|
-
'agent help text mentions pave agent init');
|
|
201
|
-
|
|
202
|
-
console.log('');
|
|
203
|
-
|
|
204
|
-
// =============================================
|
|
205
|
-
// Verify createDefaultPermissions content
|
|
206
|
-
// =============================================
|
|
207
|
-
|
|
208
|
-
console.log('Default permissions content:');
|
|
209
|
-
|
|
210
|
-
// Extract the default permissions object from createDefaultPermissions
|
|
211
|
-
const createFuncStart = permissionSrc.indexOf('function createDefaultPermissions()');
|
|
212
|
-
const createFuncEnd = permissionSrc.indexOf('\n// Default safe', createFuncStart);
|
|
213
|
-
if (createFuncStart !== -1 && createFuncEnd !== -1) {
|
|
214
|
-
const createFuncBody = permissionSrc.substring(createFuncStart, createFuncEnd);
|
|
215
|
-
|
|
216
|
-
assert(createFuncBody.indexOf("network: ['*']") !== -1,
|
|
217
|
-
'default permissions have network: [*]');
|
|
218
|
-
assert(createFuncBody.indexOf("read: ['*']") !== -1,
|
|
219
|
-
'default permissions have filesystem.read: [*]');
|
|
220
|
-
assert(createFuncBody.indexOf("write: ['*']") !== -1,
|
|
221
|
-
'default permissions have filesystem.write: [*]');
|
|
222
|
-
assert(createFuncBody.indexOf("modules: ['*']") !== -1,
|
|
223
|
-
'default permissions have modules: [*]');
|
|
224
|
-
assert(createFuncBody.indexOf("system: ['*']") !== -1,
|
|
225
|
-
'default permissions have system: [*]');
|
|
226
|
-
assert(createFuncBody.indexOf("allowed: ['*']") !== -1,
|
|
227
|
-
'default permissions have skills.allowed: [*]');
|
|
228
|
-
assert(createFuncBody.indexOf("denied: []") !== -1,
|
|
229
|
-
'default permissions have skills.denied: []');
|
|
230
|
-
assert(createFuncBody.indexOf("fs.existsSync(PERMISSION_FILE)") !== -1,
|
|
231
|
-
'createDefaultPermissions checks if file exists before writing');
|
|
232
|
-
assert(createFuncBody.indexOf("0o600") !== -1,
|
|
233
|
-
'permissions file is written with restricted mode 0o600');
|
|
234
|
-
assert(createFuncBody.indexOf("fs.openSync(PERMISSION_FILE, 'wx'") !== -1,
|
|
235
|
-
'createDefaultPermissions uses exclusive write (wx flag) to prevent TOCTOU race');
|
|
236
|
-
assert(createFuncBody.indexOf("EEXIST") !== -1,
|
|
237
|
-
'createDefaultPermissions handles EEXIST from concurrent creation');
|
|
238
|
-
assert(createFuncBody.indexOf("loadPermissions(true)") !== -1,
|
|
239
|
-
'createDefaultPermissions reloads permissions after successful file creation');
|
|
240
|
-
} else {
|
|
241
|
-
failed++;
|
|
242
|
-
console.log(' FAIL: could not extract createDefaultPermissions function body');
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
console.log('');
|
|
246
|
-
|
|
247
|
-
// =============================================
|
|
248
|
-
// Verify auto-bootstrap in config setup
|
|
249
|
-
// =============================================
|
|
250
|
-
|
|
251
|
-
console.log('Auto-bootstrap in config setup:');
|
|
252
|
-
|
|
253
|
-
// Find the config setup block in index.js
|
|
254
|
-
const configSetupStart = indexSrc.indexOf("// Set custom config path if provided");
|
|
255
|
-
const configSetupEnd = indexSrc.indexOf("// Handle skill management commands");
|
|
256
|
-
if (configSetupStart !== -1 && configSetupEnd !== -1) {
|
|
257
|
-
const configSetup = indexSrc.substring(configSetupStart, configSetupEnd);
|
|
258
|
-
|
|
259
|
-
const setPaveHomeIdx = configSetup.indexOf('skillManager.setPaveHome(configPath)');
|
|
260
|
-
const setConfigDirIdx = configSetup.indexOf('Permission.setConfigDir(configPath)');
|
|
261
|
-
const createDefaultIdx = configSetup.indexOf('Permission.createDefaultPermissions()');
|
|
262
|
-
|
|
263
|
-
assert(setPaveHomeIdx !== -1, 'config setup calls skillManager.setPaveHome');
|
|
264
|
-
assert(setConfigDirIdx !== -1, 'config setup calls Permission.setConfigDir');
|
|
265
|
-
assert(createDefaultIdx !== -1, 'config setup calls Permission.createDefaultPermissions');
|
|
266
|
-
|
|
267
|
-
// Verify order: setConfigDir before createDefaultPermissions
|
|
268
|
-
if (setConfigDirIdx !== -1 && createDefaultIdx !== -1) {
|
|
269
|
-
assert(setConfigDirIdx < createDefaultIdx,
|
|
270
|
-
'setConfigDir is called before createDefaultPermissions');
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// Verify agent init skip
|
|
274
|
-
assert(configSetup.indexOf("isAgentInit") !== -1,
|
|
275
|
-
'config setup skips auto-bootstrap for agent init');
|
|
276
|
-
assert(configSetup.indexOf("!isAgentInit") !== -1,
|
|
277
|
-
'auto-bootstrap is conditional on NOT being agent init');
|
|
278
|
-
} else {
|
|
279
|
-
failed++;
|
|
280
|
-
console.log(' FAIL: could not find config setup block');
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// =============================================
|
|
284
|
-
// Summary
|
|
285
|
-
// =============================================
|
|
286
|
-
|
|
287
|
-
console.log('');
|
|
288
|
-
console.log('========================================');
|
|
289
|
-
console.log('Results: ' + passed + ' passed, ' + failed + ' failed, ' + (passed + failed) + ' total');
|
|
290
|
-
console.log('');
|
|
291
|
-
|
|
292
|
-
if (failed > 0) {
|
|
293
|
-
console.log('Some tests FAILED.');
|
|
294
|
-
process.exitCode = 1;
|
|
295
|
-
} else {
|
|
296
|
-
console.log('All tests passed!');
|
|
297
|
-
}
|
package/test/run-all.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Run all tests for the pave package
|
|
4
|
-
* Usage: node test/run-all.js
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const { execSync } = require('child_process');
|
|
8
|
-
const fs = require('fs');
|
|
9
|
-
const path = require('path');
|
|
10
|
-
|
|
11
|
-
const testDir = __dirname;
|
|
12
|
-
const testFiles = fs.readdirSync(testDir)
|
|
13
|
-
.filter((f) => f.endsWith('.test.js'))
|
|
14
|
-
.sort();
|
|
15
|
-
|
|
16
|
-
console.log('🧪 Running PAVE Test Suite\n');
|
|
17
|
-
|
|
18
|
-
let totalTests = 0;
|
|
19
|
-
let passedTests = 0;
|
|
20
|
-
let failedTests = 0;
|
|
21
|
-
|
|
22
|
-
for (const testFile of testFiles) {
|
|
23
|
-
const testPath = path.join(testDir, testFile);
|
|
24
|
-
console.log(`📋 Running ${testFile}...`);
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
const output = execSync(`node "${testPath}"`, {
|
|
28
|
-
encoding: 'utf8',
|
|
29
|
-
cwd: path.join(__dirname, '..'),
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Count test results from output
|
|
33
|
-
const lines = output.split('\n');
|
|
34
|
-
const passed = lines.filter((l) => l.startsWith('✅')).length;
|
|
35
|
-
const failed = lines.filter((l) => l.startsWith('❌')).length;
|
|
36
|
-
|
|
37
|
-
totalTests += passed + failed;
|
|
38
|
-
passedTests += passed;
|
|
39
|
-
failedTests += failed;
|
|
40
|
-
|
|
41
|
-
if (failed > 0) {
|
|
42
|
-
console.log(output);
|
|
43
|
-
} else {
|
|
44
|
-
console.log(` ✅ ${passed} tests passed\n`);
|
|
45
|
-
}
|
|
46
|
-
} catch (error) {
|
|
47
|
-
console.log(` ❌ Test suite failed to run: ${error.message}\n`);
|
|
48
|
-
failedTests++;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
console.log('=' * 60);
|
|
53
|
-
console.log('📊 Final Test Results:');
|
|
54
|
-
console.log(` Total: ${totalTests} tests`);
|
|
55
|
-
console.log(` Passed: ${passedTests} tests`);
|
|
56
|
-
console.log(` Failed: ${failedTests} tests`);
|
|
57
|
-
|
|
58
|
-
if (failedTests > 0) {
|
|
59
|
-
console.log(`\n❌ ${failedTests} test(s) failed. Please review and fix the issues above.`);
|
|
60
|
-
process.exit(1);
|
|
61
|
-
} else {
|
|
62
|
-
console.log(`\n✅ All tests passed! The tool output functionality is working correctly. 🎉`);
|
|
63
|
-
process.exit(0);
|
|
64
|
-
}
|
package/test/run-config-flag.js
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Standalone runner for config-flag behavioral tests (issue #126).
|
|
4
|
-
* Run directly with: node src/packages/pave/test/run-config-flag.js
|
|
5
|
-
*
|
|
6
|
-
* Tests parseArgs() and setPaveHome() behavioral correctness.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
let passed = 0;
|
|
10
|
-
let failed = 0;
|
|
11
|
-
|
|
12
|
-
function assert(cond, msg) {
|
|
13
|
-
if (cond) {
|
|
14
|
-
passed++;
|
|
15
|
-
console.log('\u2705 ' + msg);
|
|
16
|
-
} else {
|
|
17
|
-
failed++;
|
|
18
|
-
console.log('\u274c ' + msg);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const path = require('path');
|
|
23
|
-
const os = require('os');
|
|
24
|
-
|
|
25
|
-
const projDir = path.resolve(__dirname, '../../../..');
|
|
26
|
-
const tmpPave = path.join(os.tmpdir(), '.pave');
|
|
27
|
-
const tmpTestCfg = path.join(os.tmpdir(), 'test-pave-cfg');
|
|
28
|
-
|
|
29
|
-
// ---- 1. parseArgs behavioral tests ----
|
|
30
|
-
|
|
31
|
-
// pave list --config ./.pave
|
|
32
|
-
const r1 = parseArgs(['list', '--config', './.pave']);
|
|
33
|
-
assert(r1.command === 'list', 'parseArgs: list command detected');
|
|
34
|
-
assert(r1.config === './.pave', 'parseArgs: --config value parsed');
|
|
35
|
-
assert(r1.commandArgs.indexOf('./.pave') === -1, 'parseArgs: ./.pave NOT in commandArgs');
|
|
36
|
-
assert(r1.commandArgs.length === 0, 'parseArgs: commandArgs empty for list --config');
|
|
37
|
-
|
|
38
|
-
// pave install gmail --config <tmpdir>/.pave
|
|
39
|
-
const r2 = parseArgs(['install', 'gmail', '--config', tmpPave]);
|
|
40
|
-
assert(r2.config === tmpPave, 'parseArgs: --config value parsed for install');
|
|
41
|
-
assert(r2.commandArgs.indexOf(tmpPave) === -1, 'parseArgs: config path NOT in install commandArgs');
|
|
42
|
-
assert(r2.commandArgs.length === 1 && r2.commandArgs[0] === 'gmail',
|
|
43
|
-
'parseArgs: install commandArgs = [gmail]');
|
|
44
|
-
|
|
45
|
-
// pave remove my-skill --config ./.pave
|
|
46
|
-
const r3 = parseArgs(['remove', 'my-skill', '--config', './.pave']);
|
|
47
|
-
assert(r3.commandArgs.indexOf('./.pave') === -1, 'parseArgs: ./.pave NOT in remove commandArgs');
|
|
48
|
-
assert(r3.commandArgs[0] === 'my-skill', 'parseArgs: remove commandArgs = [my-skill]');
|
|
49
|
-
|
|
50
|
-
// pave search query --category tools --config /custom
|
|
51
|
-
const r4 = parseArgs(['search', 'query', '--category', 'tools', '--config', '/custom']);
|
|
52
|
-
assert(r4.commandArgs.indexOf('/custom') === -1, 'parseArgs: /custom NOT in search commandArgs');
|
|
53
|
-
assert(r4.commandArgs.indexOf('tools') === -1, 'parseArgs: --category value NOT in commandArgs');
|
|
54
|
-
assert(r4.commandArgs[0] === 'query', 'parseArgs: search commandArgs = [query]');
|
|
55
|
-
|
|
56
|
-
// pave update --config ./.pave
|
|
57
|
-
const r5 = parseArgs(['update', '--config', './.pave']);
|
|
58
|
-
assert(r5.commandArgs.length === 0, 'parseArgs: update commandArgs empty');
|
|
59
|
-
|
|
60
|
-
// --bind value doesn't leak
|
|
61
|
-
const r6 = parseArgs(['list', '--bind', '0.0.0.0', '--config', './.pave']);
|
|
62
|
-
assert(r6.commandArgs.indexOf('0.0.0.0') === -1, 'parseArgs: --bind value NOT in commandArgs');
|
|
63
|
-
|
|
64
|
-
// -b short form doesn't leak
|
|
65
|
-
const r7 = parseArgs(['list', '-b', '0.0.0.0']);
|
|
66
|
-
assert(r7.commandArgs.indexOf('0.0.0.0') === -1, 'parseArgs: -b value NOT in commandArgs');
|
|
67
|
-
|
|
68
|
-
// --shell doesn't leak
|
|
69
|
-
const r8 = parseArgs(['list', '--shell', '/bin/bash']);
|
|
70
|
-
assert(r8.commandArgs.indexOf('/bin/bash') === -1, 'parseArgs: --shell value NOT in commandArgs');
|
|
71
|
-
|
|
72
|
-
// --last doesn't leak
|
|
73
|
-
const r9 = parseArgs(['list', '--last', '5']);
|
|
74
|
-
assert(r9.commandArgs.indexOf('5') === -1, 'parseArgs: --last value NOT in commandArgs');
|
|
75
|
-
|
|
76
|
-
// --export doesn't leak
|
|
77
|
-
const r10 = parseArgs(['list', '--export', 'out.md']);
|
|
78
|
-
assert(r10.commandArgs.indexOf('out.md') === -1, 'parseArgs: --export value NOT in commandArgs');
|
|
79
|
-
|
|
80
|
-
// Multiple skills
|
|
81
|
-
const r11 = parseArgs(['install', 'a', 'b', '--config', './.pave']);
|
|
82
|
-
assert(r11.commandArgs.length === 2 && r11.commandArgs[0] === 'a' && r11.commandArgs[1] === 'b',
|
|
83
|
-
'parseArgs: install a b commandArgs = [a, b]');
|
|
84
|
-
|
|
85
|
-
// Optional-value flags: --ish-mode and --memory-monitor
|
|
86
|
-
const r13 = parseArgs(['list', '--ish-mode', 'true']);
|
|
87
|
-
assert(r13.commandArgs.indexOf('true') === -1, 'parseArgs: --ish-mode value NOT in commandArgs');
|
|
88
|
-
assert(r13.commandArgs.length === 0, 'parseArgs: list --ish-mode true -> empty commandArgs');
|
|
89
|
-
|
|
90
|
-
const r14 = parseArgs(['list', '--memory-monitor', 'off']);
|
|
91
|
-
assert(r14.commandArgs.indexOf('off') === -1, 'parseArgs: --memory-monitor value NOT in commandArgs');
|
|
92
|
-
assert(r14.commandArgs.length === 0, 'parseArgs: list --memory-monitor off -> empty commandArgs');
|
|
93
|
-
|
|
94
|
-
// --ish-mode without a recognized value should not consume next positional token
|
|
95
|
-
const r15 = parseArgs(['install', '--ish-mode', 'gmail']);
|
|
96
|
-
assert(r15.commandArgs.length === 1 && r15.commandArgs[0] === 'gmail',
|
|
97
|
-
'parseArgs: --ish-mode without value -> gmail stays in commandArgs');
|
|
98
|
-
|
|
99
|
-
// --config at end (missing value) - must run in subprocess since parseArgs calls process.exit(1)
|
|
100
|
-
const fs = require('fs');
|
|
101
|
-
const child_process = require('child_process');
|
|
102
|
-
const parseArgs = require('../lib/args-parser').parseArgs;
|
|
103
|
-
|
|
104
|
-
const tmpMissing = path.join(projDir, '_test_tmp_missing_' + Date.now() + '.js');
|
|
105
|
-
const configMissingScript = [
|
|
106
|
-
'var p = require("./src/packages/pave/lib/args-parser").parseArgs;',
|
|
107
|
-
'p(["list", "--config"]);',
|
|
108
|
-
].join('\n');
|
|
109
|
-
try {
|
|
110
|
-
fs.writeFileSync(tmpMissing, configMissingScript, 'utf8');
|
|
111
|
-
const child = child_process.spawnSync(process.execPath, [tmpMissing], {
|
|
112
|
-
cwd: projDir, encoding: 'utf8', timeout: 5000, stdio: 'pipe',
|
|
113
|
-
});
|
|
114
|
-
assert(child.status !== 0, 'parseArgs: --config at end -> non-zero exit in subprocess');
|
|
115
|
-
const output = (child.stderr || '') + (child.stdout || '');
|
|
116
|
-
if (output.length > 0) {
|
|
117
|
-
assert(output.indexOf('--config') !== -1, 'parseArgs: --config at end -> error mentions --config');
|
|
118
|
-
}
|
|
119
|
-
} catch (e) {
|
|
120
|
-
assert(false, 'parseArgs: --config at end -> subprocess test failed: ' + e.message);
|
|
121
|
-
} finally {
|
|
122
|
-
try { fs.unlinkSync(tmpMissing); } catch (e2) {}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// ---- 2. setPaveHome / getPaths behavioral tests ----
|
|
126
|
-
const sm = require('../lib/skill-manager');
|
|
127
|
-
const mp = require('../lib/marketplace');
|
|
128
|
-
|
|
129
|
-
const originalHome = sm.getPaveHome();
|
|
130
|
-
|
|
131
|
-
sm.setPaveHome(tmpTestCfg);
|
|
132
|
-
mp.setPaveHome(tmpTestCfg);
|
|
133
|
-
|
|
134
|
-
const p = sm.getPaths();
|
|
135
|
-
const c = mp.getCachePaths();
|
|
136
|
-
|
|
137
|
-
assert(p.skillsDir.indexOf(tmpTestCfg) !== -1, 'setPaveHome: skillsDir uses custom path');
|
|
138
|
-
assert(p.lockFile.indexOf(tmpTestCfg) !== -1, 'setPaveHome: lockFile uses custom path');
|
|
139
|
-
assert(p.permissionsFile.indexOf(tmpTestCfg) !== -1, 'setPaveHome: permissionsFile uses custom path');
|
|
140
|
-
assert(c.cacheDir.indexOf(tmpTestCfg) !== -1, 'setPaveHome: cacheDir uses custom path');
|
|
141
|
-
assert(c.cacheFile.indexOf(tmpTestCfg) !== -1, 'setPaveHome: cacheFile uses custom path');
|
|
142
|
-
|
|
143
|
-
// Verify deprecated getters exist and delegate to dynamic functions
|
|
144
|
-
assert(typeof sm.SKILLS_DIR === 'string', 'export: SKILLS_DIR is deprecated getter (string)');
|
|
145
|
-
assert(typeof sm.LOCK_FILE === 'string', 'export: LOCK_FILE is deprecated getter (string)');
|
|
146
|
-
assert(typeof mp.CACHE_DIR === 'string', 'export: CACHE_DIR is deprecated getter (string)');
|
|
147
|
-
assert(typeof mp.CACHE_FILE === 'string', 'export: CACHE_FILE is deprecated getter (string)');
|
|
148
|
-
assert(sm.SKILLS_DIR === sm.getPaths().skillsDir, 'export: SKILLS_DIR delegates to getPaths()');
|
|
149
|
-
assert(sm.LOCK_FILE === sm.getPaths().lockFile, 'export: LOCK_FILE delegates to getPaths()');
|
|
150
|
-
assert(mp.CACHE_DIR === mp.getCachePaths().cacheDir, 'export: CACHE_DIR delegates to getCachePaths()');
|
|
151
|
-
assert(mp.CACHE_FILE === mp.getCachePaths().cacheFile, 'export: CACHE_FILE delegates to getCachePaths()');
|
|
152
|
-
|
|
153
|
-
// Restore
|
|
154
|
-
sm.setPaveHome(originalHome);
|
|
155
|
-
mp.setPaveHome(originalHome);
|
|
156
|
-
|
|
157
|
-
console.log('');
|
|
158
|
-
console.log(passed + ' passed, ' + failed + ' failed');
|
|
159
|
-
if (failed > 0) { process.exitCode = 1; throw new Error(failed + ' test(s) failed'); }
|
package/test/run-cursor-drift.js
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// Standalone test runner for cursor drift fix (issue #112).
|
|
3
|
-
// Source pattern verification via grep + cursor math tests.
|
|
4
|
-
//
|
|
5
|
-
// Blessed wraps when line.length > width (not >=), so at exact multiples
|
|
6
|
-
// the cursor stays at col=wrapWidth on the same line.
|
|
7
|
-
const child_process = require("child_process");
|
|
8
|
-
const path = require("path");
|
|
9
|
-
|
|
10
|
-
let passed = 0;
|
|
11
|
-
let failed = 0;
|
|
12
|
-
let skipped = 0;
|
|
13
|
-
function assert(cond, msg) {
|
|
14
|
-
if (cond) { passed++; console.log("\u2705 " + msg); }
|
|
15
|
-
else { failed++; console.log("\u274c " + msg); }
|
|
16
|
-
}
|
|
17
|
-
function skip(msg) { skipped++; console.log("SKIP: " + msg); }
|
|
18
|
-
const tuiPath = path.resolve(__dirname, "../../tui/index.js");
|
|
19
|
-
function grepCount(pat, file) {
|
|
20
|
-
try {
|
|
21
|
-
const cmd = "grep -F -c " + JSON.stringify(pat) + " " + JSON.stringify(file);
|
|
22
|
-
return parseInt(child_process.execSync(cmd, { encoding: "utf8" }).trim(), 10);
|
|
23
|
-
} catch (e) {
|
|
24
|
-
if (e.stdout) {
|
|
25
|
-
const n = parseInt(String(e.stdout).trim(), 10);
|
|
26
|
-
if (!isNaN(n)) return n;
|
|
27
|
-
}
|
|
28
|
-
return -1;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
// Source verification the fix introduces wrapWidth = widgetWidth - 1
|
|
32
|
-
const c = grepCount("wrapWidth = widgetWidth - 1", tuiPath);
|
|
33
|
-
if (c < 0) { skip("source grep not available in sandbox"); }
|
|
34
|
-
else {
|
|
35
|
-
assert(c >= 3, "source: 3+ wrapWidth = widgetWidth - 1 locations (found " + c + ")");
|
|
36
|
-
// Ensure the old pattern (input.iwidth - 1 baked into widgetWidth) is gone
|
|
37
|
-
const old = grepCount("input.iwidth - 1", tuiPath);
|
|
38
|
-
assert(old === 0, "source: no old iwidth - 1 in widgetWidth calc (found " + old + ")");
|
|
39
|
-
// Fallback value should be 80 (widgetWidth) since wrapWidth = 80-1 = 79
|
|
40
|
-
const fb = grepCount(": 80", tuiPath);
|
|
41
|
-
assert(fb >= 1, "source: fallback 80 in _typeScroll widgetWidth (found " + fb + ")");
|
|
42
|
-
// Check for textarea margin comments
|
|
43
|
-
const cm = grepCount("textarea", tuiPath);
|
|
44
|
-
assert(cm >= 1, "source: textarea wrap margin comments (found " + cm + ")");
|
|
45
|
-
// Check for (cursorCol - 1) pattern (blessed wraps on > not >=)
|
|
46
|
-
const cc = grepCount("cursorCol - 1", tuiPath);
|
|
47
|
-
assert(cc >= 2, "source: cursorCol - 1 pattern for blessed > boundary (found " + cc + ")");
|
|
48
|
-
// Check selection: per-char wrap uses >= wrapWidth, after-last-char uses > wrapWidth
|
|
49
|
-
const gte = grepCount(">= wrapWidth", tuiPath);
|
|
50
|
-
assert(gte >= 1, "source: >= wrapWidth for per-char wrap (found " + gte + ")");
|
|
51
|
-
const gt = grepCount("> wrapWidth", tuiPath);
|
|
52
|
-
assert(gt >= 1, "source: > wrapWidth for after-last-char cursor (found " + gt + ")");
|
|
53
|
-
}
|
|
54
|
-
// Cursor math simulation (model Blessed _wrapContent: no wrap on exact width)
|
|
55
|
-
function sim(len, w) {
|
|
56
|
-
if (len <= 0) return { wraps: 0, col: 0 };
|
|
57
|
-
const wraps = Math.floor((len - 1) / w);
|
|
58
|
-
const col = ((len - 1) % w) + 1;
|
|
59
|
-
return { wraps, col };
|
|
60
|
-
}
|
|
61
|
-
const W = 39;
|
|
62
|
-
assert(sim(0, W).col === 0, "math: empty -> col 0");
|
|
63
|
-
assert(sim(20, W).col === 20 && sim(20, W).wraps === 0, "math: 20 -> col 20");
|
|
64
|
-
assert(sim(38, W).col === 38 && sim(38, W).wraps === 0, "math: 38 -> col 38 (end of page 1)");
|
|
65
|
-
assert(sim(39, W).col === W && sim(39, W).wraps === 0, "math: 39 -> col 39 (no wrap, matches blessed)");
|
|
66
|
-
assert(sim(40, W).col === 1 && sim(40, W).wraps === 1, "math: 40 -> col 1");
|
|
67
|
-
assert(sim(78, W).col === W && sim(78, W).wraps === 1, "math: 78 -> end of page 2");
|
|
68
|
-
assert(sim(80, W).col === 2 && sim(80, W).wraps === 2, "math: 80 -> col 2");
|
|
69
|
-
// Verify no drift at each page boundary (exact multiples stay at col=W)
|
|
70
|
-
for (let p = 1; p <= 10; p++) { const ch = p * W; const r = sim(ch, W); assert(r.col === W && r.wraps === p - 1, "math: no drift page " + p); }
|
|
71
|
-
// Verify old code drifts but new code does not
|
|
72
|
-
const oldW = 40;
|
|
73
|
-
for (let p = 1; p <= 5; p++) {
|
|
74
|
-
const ch = p * 39;
|
|
75
|
-
const oldPos = sim(ch, oldW);
|
|
76
|
-
const newPos = sim(ch, W);
|
|
77
|
-
assert(newPos.col === W, "no-drift: new code col=" + W + " at " + ch + " chars");
|
|
78
|
-
if (p > 1) assert(oldPos.col !== W, "drift: old code col!=" + W + " at " + ch + " chars (col=" + oldPos.col + ")");
|
|
79
|
-
}
|
|
80
|
-
console.log("");
|
|
81
|
-
console.log(passed + " passed, " + failed + " failed" + (skipped ? ", " + skipped + " skipped" : ""));
|
|
82
|
-
if (failed > 0) { process.exitCode = 1; throw new Error(failed + " test(s) failed"); }
|