@cnrai/pave 0.3.32 → 0.3.34
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/MARKETPLACE.md +406 -0
- package/README.md +218 -21
- package/build-binary.js +591 -0
- package/build-npm.js +537 -0
- package/build.js +230 -0
- package/check-binary.js +26 -0
- package/deploy.sh +95 -0
- package/index.js +5775 -0
- package/lib/agent-registry.js +1037 -0
- package/lib/args-parser.js +837 -0
- package/lib/blessed-widget-patched.js +93 -0
- package/lib/cli-markdown.js +590 -0
- package/lib/compaction.js +153 -0
- package/lib/duration.js +94 -0
- package/lib/hash.js +22 -0
- package/lib/marketplace.js +866 -0
- package/lib/memory-config.js +166 -0
- package/lib/skill-manager.js +891 -0
- package/lib/soul.js +31 -0
- package/lib/tool-output-formatter.js +180 -0
- package/package.json +35 -33
- package/start-pave.sh +149 -0
- package/status.js +271 -0
- package/test/abort-stream.test.js +445 -0
- package/test/agent-auto-compaction.test.js +552 -0
- package/test/agent-comm-abort.test.js +95 -0
- package/test/agent-comm.test.js +598 -0
- package/test/agent-inbox.test.js +576 -0
- package/test/agent-init.test.js +264 -0
- package/test/agent-interrupt.test.js +314 -0
- package/test/agent-lifecycle.test.js +520 -0
- package/test/agent-log-files.test.js +349 -0
- package/test/agent-mode.manual-test.js +392 -0
- package/test/agent-parsing.test.js +228 -0
- package/test/agent-post-stream-idle.test.js +762 -0
- package/test/agent-registry.test.js +359 -0
- package/test/agent-rm.test.js +442 -0
- package/test/agent-spawn.test.js +933 -0
- package/test/agent-status-api.test.js +624 -0
- package/test/agent-update.test.js +435 -0
- package/test/args-parser.test.js +391 -0
- package/test/auto-compaction-chat.manual-test.js +227 -0
- package/test/auto-compaction.test.js +941 -0
- package/test/build-config.test.js +120 -0
- package/test/build-npm.test.js +388 -0
- package/test/chat-command.test.js +137 -0
- package/test/chat-leading-lines.test.js +159 -0
- package/test/config-flag.test.js +272 -0
- package/test/cursor-drift.test.js +135 -0
- package/test/debug-require.js +23 -0
- package/test/dir-migration.test.js +323 -0
- package/test/duration.test.js +229 -0
- package/test/ghostty-term.test.js +202 -0
- package/test/http500-backoff.test.js +854 -0
- package/test/integration.test.js +86 -0
- package/test/memory-guard-env.test.js +220 -0
- package/test/pr233-fixes.test.js +259 -0
- package/test/run-agent-init.js +297 -0
- package/test/run-all.js +64 -0
- package/test/run-config-flag.js +159 -0
- package/test/run-cursor-drift.js +82 -0
- package/test/run-session-path.js +154 -0
- package/test/run-tests.js +643 -0
- package/test/sandbox-redirect.test.js +202 -0
- package/test/session-path.test.js +132 -0
- package/test/shebang-strip.test.js +241 -0
- package/test/soul-reinject.test.js +1027 -0
- package/test/soul-reread.test.js +281 -0
- package/test/tool-output-formatter.test.js +486 -0
- package/test/tool-output-gating.test.js +143 -0
- package/test/tool-states.test.js +167 -0
- package/test/tools-flag.test.js +65 -0
- package/test/tui-attach.test.js +1255 -0
- package/test/tui-compaction.test.js +354 -0
- package/test/tui-wrap.test.js +568 -0
- package/test-binary.js +52 -0
- package/test-binary2.js +36 -0
- package/LICENSE +0 -21
- package/pave.js +0 -2
- package/sandbox/SandboxRunner.js +0 -1
- package/sandbox/pave-run.js +0 -2
- package/sandbox/permission.js +0 -1
- package/sandbox/utils/yaml.js +0 -1
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* PAVE CLI Argument Parser
|
|
4
|
+
* Node 16 compatible - no external dependencies
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
/* global PAVE_VERSION */
|
|
11
|
+
|
|
12
|
+
// Shared flag definitions used by multiple command branches (hoisted to avoid duplication)
|
|
13
|
+
const FLAGS_WITH_VALUES = new Set([
|
|
14
|
+
'--config', '--category', '-c', '--model', '-m', '--session', '-s',
|
|
15
|
+
'--port', '-p', '--host', '--max-memory', '--memory-limit', '--memory-warning',
|
|
16
|
+
'--memory-critical', '--memory-startup', '--opencode-dir',
|
|
17
|
+
'--bind', '-b', '--shell', '--last', '--export',
|
|
18
|
+
'--name', '-n', '--sleep', '--soul', '--reinject-interval',
|
|
19
|
+
'--query', '-q', '--skill', '--path', '--output', '-o',
|
|
20
|
+
'--lines', '--agent',
|
|
21
|
+
]);
|
|
22
|
+
const BOOLEAN_FLAGS = new Set([
|
|
23
|
+
'--json', '--no-stream', '--color', '--verbose', '--no-tools', '--debug',
|
|
24
|
+
'--force', '-f', '--all', '-a', '--help', '-h', '--version', '-v',
|
|
25
|
+
'--list', '--full', '--quiet', '--silent',
|
|
26
|
+
'--daemon', '--follow',
|
|
27
|
+
'--urgent',
|
|
28
|
+
]);
|
|
29
|
+
// Flags that optionally take a value (on/off/true/false/0/1)
|
|
30
|
+
const OPTIONAL_VALUE_FLAGS = new Set(['--memory-monitor', '--ish-mode']);
|
|
31
|
+
// Include common case variants so --ish-mode TRUE / --memory-monitor ON work
|
|
32
|
+
const OPTIONAL_VALUES = new Set([
|
|
33
|
+
'on', 'off', 'true', 'false', '0', '1',
|
|
34
|
+
'ON', 'OFF', 'TRUE', 'FALSE',
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
function parseArgs(argv = process.argv.slice(2)) {
|
|
38
|
+
const args = {
|
|
39
|
+
// Subcommand (install, list, remove, etc.) - null means run TUI
|
|
40
|
+
command: null,
|
|
41
|
+
commandArgs: [], // Arguments for the subcommand
|
|
42
|
+
|
|
43
|
+
// Memory configuration
|
|
44
|
+
ishMode: null, // Force ISH mode: true/false/null (auto-detect)
|
|
45
|
+
memoryLimit: null, // Heap limit in MB
|
|
46
|
+
memoryWarning: null, // Warning threshold in MB
|
|
47
|
+
memoryCritical: null, // Critical threshold in MB
|
|
48
|
+
memoryStartup: null, // Startup limit in MB
|
|
49
|
+
memoryMonitor: null, // Enable memory monitoring: true/false/null (auto)
|
|
50
|
+
|
|
51
|
+
// Server configuration
|
|
52
|
+
port: null, // Server port
|
|
53
|
+
host: null, // Server hostname
|
|
54
|
+
opencodedir: null, // Working directory
|
|
55
|
+
|
|
56
|
+
// General options
|
|
57
|
+
help: false,
|
|
58
|
+
version: false,
|
|
59
|
+
debug: false,
|
|
60
|
+
verbose: false,
|
|
61
|
+
force: false, // Force overwrite for install
|
|
62
|
+
json: false, // JSON output for list
|
|
63
|
+
|
|
64
|
+
// Marketplace options
|
|
65
|
+
all: false, // Show all skills (for search)
|
|
66
|
+
category: null, // Filter by category (for search)
|
|
67
|
+
check: false, // Check only, don't install (for update)
|
|
68
|
+
createPr: false, // Create PR for publish
|
|
69
|
+
upgrade: false, // Upgrade existing skill in marketplace
|
|
70
|
+
|
|
71
|
+
// Chat options
|
|
72
|
+
session: null, // Session ID or 'new' for new session
|
|
73
|
+
noStream: false, // Disable streaming output
|
|
74
|
+
color: false, // Enable colored output (implies --no-stream)
|
|
75
|
+
model: null, // Model to use (e.g., 'github-copilot/claude-sonnet-4')
|
|
76
|
+
showTools: true, // Tool output shown by default; use --no-tools to hide
|
|
77
|
+
agent: null, // Target agent name for inbox message (pave chat -a <agent>)
|
|
78
|
+
urgent: false, // Send as urgent interrupt (pave chat -a <agent> --urgent "msg")
|
|
79
|
+
|
|
80
|
+
// Agent options
|
|
81
|
+
name: null, // Agent name (e.g., 'OpenPAVE software engineer')
|
|
82
|
+
sleep: null, // Sleep duration between iterations (e.g., '30m', '1h', '5s')
|
|
83
|
+
soul: null, // Path to SOUL.md file for agent instructions
|
|
84
|
+
reinjectInterval: null, // Re-send SOUL every N iterations to prevent drift; null uses agent default (10)
|
|
85
|
+
agentSubcommand: null, // Agent subcommand: 'init', 'stop', 'logs' for pave agent <sub>
|
|
86
|
+
daemon: false, // Start agent in background (--daemon)
|
|
87
|
+
follow: false, // Follow log output in real-time (--follow)
|
|
88
|
+
lines: null, // Number of log lines to show (--lines <n>)
|
|
89
|
+
|
|
90
|
+
// History options
|
|
91
|
+
last: null, // Show last N messages
|
|
92
|
+
export: null, // Export to file path
|
|
93
|
+
listSessions: false, // List all sessions
|
|
94
|
+
|
|
95
|
+
// Shell-init options
|
|
96
|
+
bind: null, // Key binding (e.g., 'ctrl+g', 'ctrl+p')
|
|
97
|
+
shell: null, // Shell type: zsh, bash, fish (auto-detected)
|
|
98
|
+
installShell: false, // Auto-install to shell config file
|
|
99
|
+
|
|
100
|
+
// Config path
|
|
101
|
+
config: null, // Custom .pave config directory path
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Check for subcommands first (must be first non-flag argument)
|
|
105
|
+
const subcommands = ['install', 'list', 'remove', 'uninstall', 'info', 'run', 'search', 'publish', 'update', 'chat', 'history', 'shell-init', 'agent', 'agents'];
|
|
106
|
+
if (argv.length > 0 && subcommands.includes(argv[0])) {
|
|
107
|
+
args.command = argv[0];
|
|
108
|
+
|
|
109
|
+
// For 'run' command, pass ALL remaining arguments (including flags) to the skill
|
|
110
|
+
// For 'chat' command, collect the message (may be multiple words)
|
|
111
|
+
// For other commands, only collect non-flag arguments
|
|
112
|
+
if (args.command === 'run') {
|
|
113
|
+
// Pass everything after 'run' to the skill (it handles its own flags)
|
|
114
|
+
args.commandArgs = argv.slice(1);
|
|
115
|
+
// Return early - don't parse any flags for 'run' command
|
|
116
|
+
// They all belong to the skill
|
|
117
|
+
return args;
|
|
118
|
+
} if (args.command === 'chat') {
|
|
119
|
+
// Collect message parts (everything that's not a flag we handle)
|
|
120
|
+
for (let i = 1; i < argv.length; i++) {
|
|
121
|
+
const a = argv[i];
|
|
122
|
+
// Skip our flags and their values
|
|
123
|
+
if (a === '--session' || a === '-s' || a === '--model' || a === '-m') {
|
|
124
|
+
i++; // Skip the value too
|
|
125
|
+
} else if (a === '--agent' || a === '-a') {
|
|
126
|
+
// Agent target for inbox message (#231: validate value is not a flag)
|
|
127
|
+
if (i + 1 < argv.length && !argv[i + 1].startsWith('-')) {
|
|
128
|
+
args.agent = argv[i + 1];
|
|
129
|
+
i++; // Skip the value
|
|
130
|
+
} else {
|
|
131
|
+
console.error('Error: --agent requires an agent name');
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
} else if (a === '--json' || a === '--no-stream' || a === '--color' || a === '--verbose' || a === '--no-tools' || a === '--debug') {
|
|
135
|
+
// Skip boolean flags
|
|
136
|
+
} else if (!a.startsWith('-')) {
|
|
137
|
+
args.commandArgs.push(a);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
} else if (args.command === 'agent') {
|
|
141
|
+
// For agent command, extract positional args (SOUL path) while skipping flags and their values.
|
|
142
|
+
// Also detect 'init' subcommand: the first non-flag positional token that is 'init'
|
|
143
|
+
// sets agentSubcommand and is excluded from commandArgs.
|
|
144
|
+
// Key insight: the agent command should have exactly one positional arg (the SOUL path).
|
|
145
|
+
// All unknown --flags are assumed to take a value (consumed as next token).
|
|
146
|
+
|
|
147
|
+
for (let i = 1; i < argv.length; i++) {
|
|
148
|
+
const a = argv[i];
|
|
149
|
+
const next = argv[i + 1];
|
|
150
|
+
|
|
151
|
+
// Skip known flags that take values (and their values)
|
|
152
|
+
if (FLAGS_WITH_VALUES.has(a)) {
|
|
153
|
+
i++; // Skip the value too
|
|
154
|
+
} else if (BOOLEAN_FLAGS.has(a)) {
|
|
155
|
+
// Skip boolean flags
|
|
156
|
+
} else if (OPTIONAL_VALUE_FLAGS.has(a)) {
|
|
157
|
+
// Skip optional value flags - check if next arg is an optional value
|
|
158
|
+
if (next && OPTIONAL_VALUES.has(next.toLowerCase())) {
|
|
159
|
+
i++; // Skip the optional value
|
|
160
|
+
}
|
|
161
|
+
} else if (a.startsWith('--') && a.includes('=')) {
|
|
162
|
+
// Handle --flag=value style (skip entirely)
|
|
163
|
+
continue;
|
|
164
|
+
} else if (a.startsWith('-') && !a.startsWith('--') && a.length === 2 && next && !next.startsWith('-')) {
|
|
165
|
+
// Handle unknown short flags like -x value (assume they take a value if next doesn't start with -)
|
|
166
|
+
i++; // Skip the presumed value
|
|
167
|
+
} else if (a.startsWith('--') && next && !next.startsWith('-')) {
|
|
168
|
+
// Handle unknown long flags: assume they take a value whenever next arg exists
|
|
169
|
+
// and doesn't start with '-'. This prevents URIs like discord://123 from being
|
|
170
|
+
// misclassified as positional args.
|
|
171
|
+
i++; // Skip the presumed value
|
|
172
|
+
} else if (!a.startsWith('-')) {
|
|
173
|
+
// Positional argument: either a subcommand keyword (init/stop/start/restart/
|
|
174
|
+
// logs/update/rm/ps) as the first positional, or a SOUL path / agent name after that.
|
|
175
|
+
// Detect agent subcommands if it's the first positional token
|
|
176
|
+
if ((a === 'init' || a === 'stop' || a === 'logs' || a === 'restart' || a === 'start' || a === 'update' || a === 'rm') && args.agentSubcommand === null && args.commandArgs.length === 0) {
|
|
177
|
+
args.agentSubcommand = a;
|
|
178
|
+
} else if (a === 'ps' && args.command === 'agent' && args.agentSubcommand === null && args.commandArgs.length === 0) {
|
|
179
|
+
// Map 'pave agent ps' to 'pave agents' to avoid .pave directory creation side effect
|
|
180
|
+
args.command = 'agents';
|
|
181
|
+
// Skip adding 'ps' to commandArgs; extra args are collected below and validated
|
|
182
|
+
} else if (args.command === 'agents') {
|
|
183
|
+
// Already mapped to agents; collect any remaining args (should be empty)
|
|
184
|
+
args.commandArgs.push(a);
|
|
185
|
+
} else {
|
|
186
|
+
args.commandArgs.push(a);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// Skip any other flags we don't recognize (flags with no value after them)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Validate positional args for agent commands.
|
|
193
|
+
// For subcommands (stop/start/restart/logs/update/rm), enforce at most one agent name.
|
|
194
|
+
// For bare "pave agent" with multiple positional args, heuristically pick the
|
|
195
|
+
// SOUL file (prefer .md files, fall back to last arg) and warn.
|
|
196
|
+
// 'pave agent ps' maps to 'pave agents' - reject any extra args
|
|
197
|
+
if (args.command === 'agents' && args.commandArgs.length > 0) {
|
|
198
|
+
console.error('Error: "pave agent ps" accepts no arguments, got: ' + args.commandArgs.join(', '));
|
|
199
|
+
console.error('Usage: pave agent ps');
|
|
200
|
+
process.exit(1);
|
|
201
|
+
} else if (args.agentSubcommand === 'stop' || args.agentSubcommand === 'logs' || args.agentSubcommand === 'restart' || args.agentSubcommand === 'start' || args.agentSubcommand === 'update' || args.agentSubcommand === 'rm') {
|
|
202
|
+
// stop/start/restart/logs/update/rm accept at most one agent name
|
|
203
|
+
if (args.commandArgs.length > 1) {
|
|
204
|
+
console.error('Error: "pave agent ' + args.agentSubcommand + '" accepts at most one agent name, got: ' + args.commandArgs.join(', '));
|
|
205
|
+
console.error('Usage: pave agent ' + args.agentSubcommand + ' [name]');
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
} else if (!args.agentSubcommand && args.commandArgs.length > 1) {
|
|
209
|
+
const mdFiles = args.commandArgs.filter((a) => a.endsWith('.md'));
|
|
210
|
+
if (mdFiles.length === 1) {
|
|
211
|
+
// Use the .md file as SOUL path, discard others
|
|
212
|
+
args.commandArgs = mdFiles;
|
|
213
|
+
} else if (mdFiles.length > 1) {
|
|
214
|
+
// Multiple .md files - use --soul to disambiguate
|
|
215
|
+
console.error(`Warning: Multiple potential SOUL files found: ${args.commandArgs.join(', ')}`);
|
|
216
|
+
console.error(` Use --soul <path> to specify the SOUL file explicitly.`);
|
|
217
|
+
// Keep the last one as the presumed SOUL path
|
|
218
|
+
args.commandArgs = [args.commandArgs[args.commandArgs.length - 1]];
|
|
219
|
+
} else {
|
|
220
|
+
// No .md files, use the last positional arg
|
|
221
|
+
console.error(`Warning: Multiple positional args found: ${args.commandArgs.join(', ')}`);
|
|
222
|
+
console.error(` Using the last one as SOUL path. Use --soul to be explicit.`);
|
|
223
|
+
args.commandArgs = [args.commandArgs[args.commandArgs.length - 1]];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
// Collect remaining non-flag arguments as command args.
|
|
228
|
+
// Skip values that follow flags expecting arguments (e.g., --config ./.pave)
|
|
229
|
+
// Uses shared FLAGS_WITH_VALUES, BOOLEAN_FLAGS, OPTIONAL_VALUE_FLAGS from module scope.
|
|
230
|
+
for (let i = 1; i < argv.length; i++) {
|
|
231
|
+
const a = argv[i];
|
|
232
|
+
const next = argv[i + 1];
|
|
233
|
+
if (FLAGS_WITH_VALUES.has(a)) {
|
|
234
|
+
i++; // skip the flag's value
|
|
235
|
+
} else if (OPTIONAL_VALUE_FLAGS.has(a)) {
|
|
236
|
+
// Skip optional value flags - consume next token only when it matches an allowed value
|
|
237
|
+
if (next && OPTIONAL_VALUES.has(next.toLowerCase())) {
|
|
238
|
+
i++; // Skip the optional value
|
|
239
|
+
}
|
|
240
|
+
} else if (!a.startsWith('-')) {
|
|
241
|
+
args.commandArgs.push(a);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
for (let i = 0; i < argv.length; i++) {
|
|
248
|
+
const arg = argv[i];
|
|
249
|
+
const next = argv[i + 1];
|
|
250
|
+
|
|
251
|
+
// Context-sensitive short flags for 'agent logs' subcommand.
|
|
252
|
+
// -f means --follow and -n means --lines when inside 'agent logs',
|
|
253
|
+
// overriding their normal meanings (--force, --name).
|
|
254
|
+
if (args.agentSubcommand === 'logs') {
|
|
255
|
+
if (arg === '-f') {
|
|
256
|
+
args.follow = true;
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
if (arg === '-n') {
|
|
260
|
+
const linesValue = argv[i + 1];
|
|
261
|
+
if (!linesValue || linesValue.startsWith('-')) {
|
|
262
|
+
console.error('Error: -n/--lines requires a positive integer value');
|
|
263
|
+
process.exit(1);
|
|
264
|
+
}
|
|
265
|
+
if (!/^\d+$/.test(linesValue)) {
|
|
266
|
+
console.error('Error: -n/--lines requires a positive integer, got "' + linesValue + '"');
|
|
267
|
+
process.exit(1);
|
|
268
|
+
}
|
|
269
|
+
const parsedLines = parseInt(linesValue, 10);
|
|
270
|
+
if (parsedLines <= 0) {
|
|
271
|
+
console.error('Error: -n/--lines requires a positive integer, got "' + linesValue + '"');
|
|
272
|
+
process.exit(1);
|
|
273
|
+
}
|
|
274
|
+
args.lines = parsedLines;
|
|
275
|
+
i++;
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Boolean flags
|
|
281
|
+
if (arg === '--help' || arg === '-h') {
|
|
282
|
+
args.help = true;
|
|
283
|
+
} else if (arg === '--version' || arg === '-v') {
|
|
284
|
+
args.version = true;
|
|
285
|
+
} else if (arg === '--debug') {
|
|
286
|
+
args.debug = true;
|
|
287
|
+
} else if (arg === '--verbose') {
|
|
288
|
+
args.verbose = true;
|
|
289
|
+
} else if (arg === '--force' || arg === '-f') {
|
|
290
|
+
args.force = true;
|
|
291
|
+
} else if (arg === '--json') {
|
|
292
|
+
args.json = true;
|
|
293
|
+
} else if (arg === '--all' || (arg === '-a' && args.command !== 'chat' && args.command !== null)) {
|
|
294
|
+
args.all = true;
|
|
295
|
+
} else if (arg === '--check') {
|
|
296
|
+
args.check = true;
|
|
297
|
+
} else if (arg === '--create-pr') {
|
|
298
|
+
args.createPr = true;
|
|
299
|
+
} else if (arg === '--upgrade' || arg === '-u') {
|
|
300
|
+
args.upgrade = true;
|
|
301
|
+
} else if ((arg === '--category' || arg === '-c') && next) {
|
|
302
|
+
args.category = next;
|
|
303
|
+
i++;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Chat options
|
|
307
|
+
else if ((arg === '--session' || arg === '-s') && next) {
|
|
308
|
+
args.session = next;
|
|
309
|
+
i++;
|
|
310
|
+
} else if (arg === '--no-stream') {
|
|
311
|
+
args.noStream = true;
|
|
312
|
+
} else if (arg === '--color') {
|
|
313
|
+
args.color = true;
|
|
314
|
+
args.noStream = true; // Color output requires waiting for full response
|
|
315
|
+
} else if ((arg === '--model' || arg === '-m') && next) {
|
|
316
|
+
args.model = next;
|
|
317
|
+
i++;
|
|
318
|
+
} else if (arg === '--no-tools') {
|
|
319
|
+
args.showTools = false;
|
|
320
|
+
} else if ((arg === '--agent' || arg === '-a') && next && !next.startsWith('-')) {
|
|
321
|
+
args.agent = next;
|
|
322
|
+
i++;
|
|
323
|
+
} else if (arg === '--urgent') {
|
|
324
|
+
args.urgent = true;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Agent options
|
|
328
|
+
else if (arg === '--name' || arg === '-n') {
|
|
329
|
+
const nameValue = argv[i + 1];
|
|
330
|
+
if (!nameValue || nameValue.startsWith('-')) {
|
|
331
|
+
console.error('Error: --name requires a value');
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
args.name = nameValue;
|
|
335
|
+
i++;
|
|
336
|
+
} else if ((arg === '--sleep')) {
|
|
337
|
+
const sleepValue = argv[i + 1];
|
|
338
|
+
if (!sleepValue || sleepValue.startsWith('-')) {
|
|
339
|
+
console.error('Error: --sleep requires a value');
|
|
340
|
+
process.exit(1);
|
|
341
|
+
}
|
|
342
|
+
args.sleep = sleepValue;
|
|
343
|
+
i++;
|
|
344
|
+
} else if (arg === '--soul') {
|
|
345
|
+
const soulValue = argv[i + 1];
|
|
346
|
+
if (!soulValue || soulValue.startsWith('-')) {
|
|
347
|
+
console.error('Error: --soul requires a value');
|
|
348
|
+
process.exit(1);
|
|
349
|
+
}
|
|
350
|
+
args.soul = soulValue;
|
|
351
|
+
i++;
|
|
352
|
+
} else if (arg === '--reinject-interval') {
|
|
353
|
+
const riValue = argv[i + 1];
|
|
354
|
+
if (!riValue || riValue.startsWith('-')) {
|
|
355
|
+
console.error('Error: --reinject-interval requires a positive integer value');
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
358
|
+
if (!/^\d+$/.test(riValue)) {
|
|
359
|
+
console.error(`Error: --reinject-interval requires a positive integer, got "${riValue}"`);
|
|
360
|
+
process.exit(1);
|
|
361
|
+
}
|
|
362
|
+
const parsed = parseInt(riValue, 10);
|
|
363
|
+
if (parsed <= 0) {
|
|
364
|
+
console.error(`Error: --reinject-interval requires a positive integer, got "${riValue}"`);
|
|
365
|
+
process.exit(1);
|
|
366
|
+
}
|
|
367
|
+
args.reinjectInterval = parsed;
|
|
368
|
+
i++;
|
|
369
|
+
} else if (arg === '--daemon') {
|
|
370
|
+
args.daemon = true;
|
|
371
|
+
} else if (arg === '--follow') {
|
|
372
|
+
args.follow = true;
|
|
373
|
+
} else if (arg === '--lines') {
|
|
374
|
+
const linesValue = argv[i + 1];
|
|
375
|
+
if (!linesValue || linesValue.startsWith('-')) {
|
|
376
|
+
console.error('Error: --lines requires a positive integer value');
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
if (!/^\d+$/.test(linesValue)) {
|
|
380
|
+
console.error('Error: --lines requires a positive integer, got "' + linesValue + '"');
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
const parsedLines = parseInt(linesValue, 10);
|
|
384
|
+
if (parsedLines <= 0) {
|
|
385
|
+
console.error('Error: --lines requires a positive integer, got "' + linesValue + '"');
|
|
386
|
+
process.exit(1);
|
|
387
|
+
}
|
|
388
|
+
args.lines = parsedLines;
|
|
389
|
+
i++;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// History options
|
|
393
|
+
else if (arg === '--list') {
|
|
394
|
+
args.listSessions = true;
|
|
395
|
+
} else if (arg === '--last' && next) {
|
|
396
|
+
const lastValue = parseInt(next, 10);
|
|
397
|
+
if (!Number.isInteger(lastValue) || lastValue <= 0) {
|
|
398
|
+
console.error(`Error: --last requires a positive integer, got "${next}"`);
|
|
399
|
+
process.exit(1);
|
|
400
|
+
}
|
|
401
|
+
args.last = lastValue;
|
|
402
|
+
i++;
|
|
403
|
+
} else if (arg === '--export' && next) {
|
|
404
|
+
args.export = next;
|
|
405
|
+
i++;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Shell-init options
|
|
409
|
+
else if ((arg === '--bind' || arg === '-b') && next) {
|
|
410
|
+
// Validate keybinding: only allow ctrl+<letter>, ^<letter>, or ctrl+/ patterns
|
|
411
|
+
// These are the only formats parseKeyBinding() can reliably convert to shell codes
|
|
412
|
+
const validBindPattern = /^(ctrl[+\-_][a-zA-Z\/\\]|\^[a-zA-Z])$/i;
|
|
413
|
+
if (!validBindPattern.test(next)) {
|
|
414
|
+
console.error(`Error: --bind must be in format: ctrl+<letter> or ^<letter> (e.g., ctrl+p, ^G)`);
|
|
415
|
+
process.exit(1);
|
|
416
|
+
}
|
|
417
|
+
args.bind = next;
|
|
418
|
+
i++;
|
|
419
|
+
} else if (arg === '--shell' && next) {
|
|
420
|
+
// Validate shell type
|
|
421
|
+
const validShells = ['zsh', 'bash', 'fish'];
|
|
422
|
+
if (!validShells.includes(next.toLowerCase())) {
|
|
423
|
+
console.error(`Error: --shell must be one of: ${validShells.join(', ')}`);
|
|
424
|
+
process.exit(1);
|
|
425
|
+
}
|
|
426
|
+
args.shell = next.toLowerCase();
|
|
427
|
+
i++;
|
|
428
|
+
} else if (arg === '--install' || arg === '-i') {
|
|
429
|
+
args.installShell = true;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// ISH Memory Configuration
|
|
433
|
+
else if (arg === '--ish-mode') {
|
|
434
|
+
if (next === 'true' || next === 'on' || next === '1') {
|
|
435
|
+
args.ishMode = true;
|
|
436
|
+
} else if (next === 'false' || next === 'off' || next === '0') {
|
|
437
|
+
args.ishMode = false;
|
|
438
|
+
} else {
|
|
439
|
+
args.ishMode = true; // Default to true if no value
|
|
440
|
+
}
|
|
441
|
+
if (next && (next === 'true' || next === 'false' || next === 'on' || next === 'off' || next === '1' || next === '0')) {
|
|
442
|
+
i++; // Skip next arg if it was consumed
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
else if (arg === '--memory-limit' && next) {
|
|
447
|
+
const mb = parseInt(next, 10);
|
|
448
|
+
if (!isNaN(mb) && mb > 0) {
|
|
449
|
+
args.memoryLimit = mb;
|
|
450
|
+
i++;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
else if (arg === '--memory-warning' && next) {
|
|
455
|
+
const mb = parseInt(next, 10);
|
|
456
|
+
if (!isNaN(mb) && mb > 0) {
|
|
457
|
+
args.memoryWarning = mb;
|
|
458
|
+
i++;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
else if (arg === '--memory-critical' && next) {
|
|
463
|
+
const mb = parseInt(next, 10);
|
|
464
|
+
if (!isNaN(mb) && mb > 0) {
|
|
465
|
+
args.memoryCritical = mb;
|
|
466
|
+
i++;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
else if (arg === '--memory-startup' && next) {
|
|
471
|
+
const mb = parseInt(next, 10);
|
|
472
|
+
if (!isNaN(mb) && mb > 0) {
|
|
473
|
+
args.memoryStartup = mb;
|
|
474
|
+
i++;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
else if (arg === '--memory-monitor') {
|
|
479
|
+
if (next === 'true' || next === 'on' || next === '1') {
|
|
480
|
+
args.memoryMonitor = true;
|
|
481
|
+
} else if (next === 'false' || next === 'off' || next === '0') {
|
|
482
|
+
args.memoryMonitor = false;
|
|
483
|
+
} else {
|
|
484
|
+
args.memoryMonitor = true; // Default to true if no value
|
|
485
|
+
}
|
|
486
|
+
if (next && (next === 'true' || next === 'false' || next === 'on' || next === 'off' || next === '1' || next === '0')) {
|
|
487
|
+
i++;
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// Server Configuration
|
|
492
|
+
else if (arg === '--port' && next) {
|
|
493
|
+
const port = parseInt(next, 10);
|
|
494
|
+
if (!isNaN(port) && port > 0 && port < 65536) {
|
|
495
|
+
args.port = port;
|
|
496
|
+
i++;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
else if (arg === '--host' && next) {
|
|
501
|
+
args.host = next;
|
|
502
|
+
i++;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
else if (arg === '--opencode-dir' && next) {
|
|
506
|
+
args.opencodedir = next;
|
|
507
|
+
i++;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Config path - ensure next is a value, not another flag
|
|
511
|
+
else if (arg === '--config') {
|
|
512
|
+
if (next && !next.startsWith('-')) {
|
|
513
|
+
args.config = next;
|
|
514
|
+
i++;
|
|
515
|
+
} else {
|
|
516
|
+
console.error('Error: --config requires a path argument');
|
|
517
|
+
process.exit(1);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Handle combined short flags
|
|
522
|
+
else if (arg.startsWith('-') && !arg.startsWith('--') && arg.length > 2) {
|
|
523
|
+
for (let j = 1; j < arg.length; j++) {
|
|
524
|
+
const flag = arg[j];
|
|
525
|
+
if (flag === 'h') args.help = true;
|
|
526
|
+
else if (flag === 'v') args.version = true;
|
|
527
|
+
else if (flag === 'd') args.debug = true;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Apply defaults for agent command for any omitted options
|
|
533
|
+
// Each option is defaulted independently if not explicitly provided
|
|
534
|
+
// Skip defaults when --help or --version is requested to avoid side effects (e.g., directory creation)
|
|
535
|
+
if (args.command === 'agent' && !args.help && !args.version) {
|
|
536
|
+
// Default config: ./.pave (relative to current working directory)
|
|
537
|
+
if (!args.config) {
|
|
538
|
+
args.config = './.pave';
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// For 'agent init/stop/logs', only config default is needed (no sleep/SOUL defaults)
|
|
542
|
+
if (!args.agentSubcommand) {
|
|
543
|
+
// Default sleep: 1m (1 minute)
|
|
544
|
+
if (!args.sleep) {
|
|
545
|
+
args.sleep = '1m';
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Default SOUL file: AGENTS.md (if no soul flag and no command args)
|
|
549
|
+
if (!args.soul && args.commandArgs.length === 0) {
|
|
550
|
+
args.commandArgs = ['AGENTS.md'];
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
return args;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
function showHelp() {
|
|
559
|
+
console.log(`
|
|
560
|
+
PAVE - Personal AI Virtual Environment
|
|
561
|
+
|
|
562
|
+
USAGE:
|
|
563
|
+
pave [options] Start the PAVE TUI + server
|
|
564
|
+
pave chat <message> Send a message (non-interactive)
|
|
565
|
+
pave agent [options] [SOUL.md] Run as continuous autonomous agent (default: AGENTS.md)
|
|
566
|
+
pave agent --daemon Start agent in background (daemon mode)
|
|
567
|
+
pave agent init Initialize agent environment (.pave + permissions.yaml)
|
|
568
|
+
pave agent stop [name] Stop a running agent (SIGTERM, SIGKILL after 5s)
|
|
569
|
+
pave agent start [name] Start a stopped agent using saved config from status.json
|
|
570
|
+
pave agent restart [name] Restart a running agent (preserves session)
|
|
571
|
+
pave agent update [name] [--sleep <dur>] [--reinject-interval <n>] [--soul <path>]
|
|
572
|
+
Update agent config (sleep, reinject interval, soul)
|
|
573
|
+
pave agent logs [name] [-f|--follow] [-n|--lines <n>] View agent log output
|
|
574
|
+
pave agent ps List all registered agent instances (alias: pave agents)
|
|
575
|
+
pave agents List all registered agent instances (alias: pave agent ps)
|
|
576
|
+
pave history [options] Show session conversation history
|
|
577
|
+
pave shell-init [options] Output shell integration code
|
|
578
|
+
pave run <skill> <command> [args] Run a skill command
|
|
579
|
+
pave install <name|path|url> Install a skill
|
|
580
|
+
pave search <query> Search the skill marketplace
|
|
581
|
+
pave publish [path] Publish a skill to marketplace
|
|
582
|
+
pave update <name> Update an installed skill
|
|
583
|
+
pave list [options] List installed skills
|
|
584
|
+
pave remove <name> Remove an installed skill
|
|
585
|
+
pave info <name> Show info about a skill
|
|
586
|
+
|
|
587
|
+
SHELL INTEGRATION:
|
|
588
|
+
shell-init Output shell integration code
|
|
589
|
+
|
|
590
|
+
--bind, -b <key> Key binding (default: ctrl+p)
|
|
591
|
+
--shell <shell> Shell type: zsh, bash, fish (auto-detected)
|
|
592
|
+
--install, -i Auto-install to shell config (~/.zshrc, etc.)
|
|
593
|
+
|
|
594
|
+
Setup:
|
|
595
|
+
# Auto-install (recommended)
|
|
596
|
+
pave shell-init --install
|
|
597
|
+
|
|
598
|
+
# Or manually add to your shell config:
|
|
599
|
+
# Zsh (~/.zshrc)
|
|
600
|
+
eval "$(pave shell-init)"
|
|
601
|
+
|
|
602
|
+
# Bash (~/.bashrc)
|
|
603
|
+
eval "$(pave shell-init --shell bash)"
|
|
604
|
+
|
|
605
|
+
# Fish (~/.config/fish/config.fish)
|
|
606
|
+
pave shell-init --shell fish | source
|
|
607
|
+
|
|
608
|
+
Then press Ctrl+P to start chatting inline!
|
|
609
|
+
|
|
610
|
+
CHAT (Non-Interactive):
|
|
611
|
+
chat <message> Send a message and get response
|
|
612
|
+
chat "multi word msg" Use quotes for multi-word messages
|
|
613
|
+
echo "msg" | pave chat Pipe input as message
|
|
614
|
+
|
|
615
|
+
--session, -s <id> Use specific session ID
|
|
616
|
+
--session new Force new session (ignore .pave-session.json)
|
|
617
|
+
--model, -m <model> Model to use (e.g., github-copilot/claude-sonnet-4)
|
|
618
|
+
--agent, -a <name> Send message to a running agent's inbox
|
|
619
|
+
--urgent Send as urgent interrupt (aborts current work)
|
|
620
|
+
--no-tools Hide tool execution progress/results (shown by default)
|
|
621
|
+
--no-stream Wait for complete response (no streaming)
|
|
622
|
+
--color Colored markdown output (implies --no-stream)
|
|
623
|
+
--json Output response as JSON
|
|
624
|
+
|
|
625
|
+
AGENT MODE (Continuous Autonomous Agent):
|
|
626
|
+
agent [SOUL.md] Run agent following instructions (default: AGENTS.md)
|
|
627
|
+
agent init Initialize agent environment (create .pave + default permissions.yaml)
|
|
628
|
+
agent init --config <dir> Initialize at a custom config directory
|
|
629
|
+
agent --soul <path> Alternative way to specify SOUL file
|
|
630
|
+
|
|
631
|
+
--name, -n <name> Agent name (e.g., 'OpenPAVE software engineer')
|
|
632
|
+
--sleep <duration> Sleep between iterations (e.g., '30m', '1h', '5s')
|
|
633
|
+
Default: 1m (1 minute)
|
|
634
|
+
--reinject-interval <n> Re-send SOUL every N iterations to prevent drift
|
|
635
|
+
Default: 10
|
|
636
|
+
--config <path> Config directory (default: ./.pave)
|
|
637
|
+
--session, -s <id> Use existing session (resumes conversation)
|
|
638
|
+
--session new Force new session (re-sends SOUL instructions)
|
|
639
|
+
--model, -m <model> Model to use
|
|
640
|
+
|
|
641
|
+
Defaults: SOUL=AGENTS.md, sleep=1m, reinject-interval=10, config=./.pave
|
|
642
|
+
|
|
643
|
+
The agent runs in a continuous loop:
|
|
644
|
+
1. On startup, reads SOUL.md for instructions (personality, goals, channels)
|
|
645
|
+
2. If starting a NEW session: sends SOUL.md content as first message
|
|
646
|
+
If RESUMING a session: skips initial SOUL send, starts with "keep going"
|
|
647
|
+
3. SOUL is re-injected automatically when:
|
|
648
|
+
- The SOUL file is edited (hot-reload via content hash detection)
|
|
649
|
+
- After auto-compaction (to restore full instructions in new session)
|
|
650
|
+
- Every N iterations (periodic, controlled by --reinject-interval)
|
|
651
|
+
4. Between SOUL re-injections, sends "keep going" to continue
|
|
652
|
+
5. Sleeps for the specified duration between iterations
|
|
653
|
+
6. Repeats until stopped (Ctrl+C)
|
|
654
|
+
|
|
655
|
+
Session Persistence:
|
|
656
|
+
- Agent sessions are stored in ~/.pave/state/agent-session-<hash>.json
|
|
657
|
+
- Restarting the agent will resume the previous session automatically
|
|
658
|
+
- Use --session new to force a fresh session (re-sends SOUL instructions)
|
|
659
|
+
|
|
660
|
+
Example SOUL.md:
|
|
661
|
+
You are an AI software engineer for the OpenPAVE project.
|
|
662
|
+
|
|
663
|
+
Your goals:
|
|
664
|
+
1. Check GitHub issues: gh issue list --repo cnrai/openpave
|
|
665
|
+
2. Check open PRs: gh pr list --repo cnrai/openpave
|
|
666
|
+
3. If there are issues assigned to you or unassigned, work on them
|
|
667
|
+
4. Create PRs for fixes, monitor Copilot reviews
|
|
668
|
+
|
|
669
|
+
HISTORY:
|
|
670
|
+
history Show conversation history for current session
|
|
671
|
+
history --session <id> Show history for specific session
|
|
672
|
+
history --list List all available sessions
|
|
673
|
+
history --last <n> Show only last N messages (default: all)
|
|
674
|
+
history --export <file> Export history to file (markdown format)
|
|
675
|
+
history --json Output as JSON
|
|
676
|
+
|
|
677
|
+
SKILL EXECUTION:
|
|
678
|
+
run <skill> <command> [args...] Run a skill command in the sandbox
|
|
679
|
+
Example: pave run gmail list --max 5
|
|
680
|
+
|
|
681
|
+
MARKETPLACE:
|
|
682
|
+
search <query> Search marketplace by name/keywords
|
|
683
|
+
search --all List all available skills
|
|
684
|
+
search -c <category> Filter by category
|
|
685
|
+
install <name> Install from marketplace (e.g., pave install gmail)
|
|
686
|
+
install <owner/repo> Install from GitHub (e.g., pave install cnrai/openpave-gmail)
|
|
687
|
+
install <path> Install from local directory
|
|
688
|
+
publish [path] Validate and publish skill to marketplace
|
|
689
|
+
publish --create-pr Auto-create PR to marketplace (requires gh CLI)
|
|
690
|
+
publish --upgrade Upgrade existing skill in marketplace
|
|
691
|
+
update <name> Update skill to latest version
|
|
692
|
+
update --all Update all installed skills
|
|
693
|
+
update --check Check for updates without installing
|
|
694
|
+
|
|
695
|
+
SKILL MANAGEMENT:
|
|
696
|
+
list List all installed skills
|
|
697
|
+
remove <name> Remove/uninstall a skill
|
|
698
|
+
info <name> Show detailed skill information
|
|
699
|
+
|
|
700
|
+
SKILL OPTIONS:
|
|
701
|
+
--force, -f Force overwrite existing skill
|
|
702
|
+
--json Output in JSON format
|
|
703
|
+
--verbose Show detailed execution info
|
|
704
|
+
--all, -a Show all (for search/update)
|
|
705
|
+
--category, -c <cat> Filter by category
|
|
706
|
+
--check Check only, don't install
|
|
707
|
+
--create-pr Create PR for publishing
|
|
708
|
+
--upgrade, -u Upgrade existing skill in marketplace
|
|
709
|
+
|
|
710
|
+
MEMORY CONFIGURATION (ISH/Resource-Constrained Environments):
|
|
711
|
+
--ish-mode [on|off] Force ISH mode (auto-detected by default)
|
|
712
|
+
--memory-limit <MB> Heap memory limit in MB (default: 8MB for ISH, 100MB standard)
|
|
713
|
+
--memory-warning <MB> Warning threshold in MB (default: 80% of limit)
|
|
714
|
+
--memory-critical <MB> Critical threshold in MB (default: same as limit)
|
|
715
|
+
--memory-startup <MB> Startup limit in MB (default: higher to allow init)
|
|
716
|
+
--memory-monitor [on|off] Enable memory monitoring (auto-enabled for ISH)
|
|
717
|
+
|
|
718
|
+
SERVER CONFIGURATION:
|
|
719
|
+
--port <port> Server port (default: 4096)
|
|
720
|
+
--host <hostname> Server hostname (default: 127.0.0.1)
|
|
721
|
+
--opencode-dir <path> Working directory (default: current)
|
|
722
|
+
|
|
723
|
+
GENERAL OPTIONS:
|
|
724
|
+
--config <path> Custom .pave config directory
|
|
725
|
+
Default: ~/.pave (global)
|
|
726
|
+
For 'agent' command: ./.pave (project-local, allows per-project config)
|
|
727
|
+
--debug Enable debug logging
|
|
728
|
+
--verbose Verbose output
|
|
729
|
+
--help, -h Show this help
|
|
730
|
+
--version, -v Show version
|
|
731
|
+
|
|
732
|
+
EXAMPLES:
|
|
733
|
+
# Start PAVE TUI
|
|
734
|
+
pave
|
|
735
|
+
|
|
736
|
+
# Non-interactive chat
|
|
737
|
+
pave chat "What is 2+2?"
|
|
738
|
+
pave chat "Explain this error" --session new
|
|
739
|
+
pave chat "Hello" --model github-copilot/claude-sonnet-4
|
|
740
|
+
echo "Summarize this" | pave chat
|
|
741
|
+
cat file.txt | pave chat "Review this code"
|
|
742
|
+
|
|
743
|
+
# Send message to a running agent
|
|
744
|
+
pave chat -a github-watcher "check the error logs"
|
|
745
|
+
pave chat --agent gw "check logs" # prefix matching
|
|
746
|
+
pave chat -a gw --urgent "stop, check production errors"
|
|
747
|
+
|
|
748
|
+
# Search marketplace
|
|
749
|
+
pave search email
|
|
750
|
+
pave search --all
|
|
751
|
+
pave search -c communication
|
|
752
|
+
|
|
753
|
+
# Install from marketplace
|
|
754
|
+
pave install gmail
|
|
755
|
+
pave install dropbox
|
|
756
|
+
|
|
757
|
+
# Install from GitHub
|
|
758
|
+
pave install cnrai/openpave-gmail
|
|
759
|
+
|
|
760
|
+
# Run a skill command
|
|
761
|
+
pave run gmail list --max 5
|
|
762
|
+
pave run gmail unread
|
|
763
|
+
pave run gmail read <messageId>
|
|
764
|
+
|
|
765
|
+
# Publish a skill
|
|
766
|
+
pave publish ./my-skill
|
|
767
|
+
pave publish --create-pr
|
|
768
|
+
pave publish --create-pr --upgrade # Upgrade existing skill
|
|
769
|
+
|
|
770
|
+
# Update skills
|
|
771
|
+
pave update gmail
|
|
772
|
+
pave update --all
|
|
773
|
+
|
|
774
|
+
# List installed skills
|
|
775
|
+
pave list
|
|
776
|
+
pave list --json
|
|
777
|
+
|
|
778
|
+
# Remove a skill
|
|
779
|
+
pave remove gmail
|
|
780
|
+
|
|
781
|
+
# Use custom config directory (for agent command)
|
|
782
|
+
# Note: The agent command supports --config as an option
|
|
783
|
+
pave agent --config /tmp/.pave SOUL.md
|
|
784
|
+
pave agent --config "$HOME/work/.pave" --sleep 30m
|
|
785
|
+
|
|
786
|
+
# View conversation history
|
|
787
|
+
pave history # Current session history
|
|
788
|
+
pave history --list # List all sessions
|
|
789
|
+
pave history --session <id> # Specific session
|
|
790
|
+
pave history --last 10 # Last 10 messages
|
|
791
|
+
pave history --export chat.md # Export to markdown file
|
|
792
|
+
pave history --json # Output as JSON
|
|
793
|
+
|
|
794
|
+
# Run as autonomous agent
|
|
795
|
+
pave agent # Run with defaults (AGENTS.md, 1m sleep, ./.pave)
|
|
796
|
+
pave agent init # Initialize .pave directory with default permissions
|
|
797
|
+
pave agent init --config ./my-config # Initialize at custom config path
|
|
798
|
+
pave agent SOUL.md --sleep 30m # Check every 30 minutes
|
|
799
|
+
pave agent SOUL.md --name "OpenPAVE software engineer"
|
|
800
|
+
pave agent --soul ./agents/github-watcher.md --sleep 1h
|
|
801
|
+
|
|
802
|
+
SKILL LOCATIONS:
|
|
803
|
+
Skills installed to: ~/.pave/skills/<name>/
|
|
804
|
+
Lock file: ~/.pave/skills.lock.json
|
|
805
|
+
Registry cache: ~/.pave/cache/registry.yaml
|
|
806
|
+
(Use --config to override ~/.pave)
|
|
807
|
+
`);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function showVersion() {
|
|
811
|
+
// PAVE_VERSION is injected at build time by esbuild define
|
|
812
|
+
// It gets replaced with the actual version string
|
|
813
|
+
let version = '0.0.0'; // fallback
|
|
814
|
+
|
|
815
|
+
// Check if PAVE_VERSION was injected (it becomes a string literal)
|
|
816
|
+
if (typeof PAVE_VERSION === 'string') {
|
|
817
|
+
version = PAVE_VERSION;
|
|
818
|
+
} else {
|
|
819
|
+
// Fallback: try to read package.json (for development)
|
|
820
|
+
try {
|
|
821
|
+
const pkgPath = path.join(__dirname, '..', 'package.json');
|
|
822
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
823
|
+
version = pkg.version || '0.0.0';
|
|
824
|
+
} catch (e) {
|
|
825
|
+
// Use fallback
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
console.log(`PAVE version ${version}`);
|
|
830
|
+
process.exit(0);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
module.exports = {
|
|
834
|
+
parseArgs,
|
|
835
|
+
showHelp,
|
|
836
|
+
showVersion,
|
|
837
|
+
};
|