@cnrai/pave 0.3.33 → 0.3.35

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.
Files changed (83) hide show
  1. package/MARKETPLACE.md +406 -0
  2. package/README.md +218 -21
  3. package/build-binary.js +591 -0
  4. package/build-npm.js +537 -0
  5. package/build.js +230 -0
  6. package/check-binary.js +26 -0
  7. package/deploy.sh +95 -0
  8. package/index.js +5776 -0
  9. package/lib/agent-registry.js +1037 -0
  10. package/lib/args-parser.js +837 -0
  11. package/lib/blessed-widget-patched.js +93 -0
  12. package/lib/cli-markdown.js +590 -0
  13. package/lib/compaction.js +153 -0
  14. package/lib/duration.js +94 -0
  15. package/lib/hash.js +22 -0
  16. package/lib/marketplace.js +866 -0
  17. package/lib/memory-config.js +166 -0
  18. package/lib/skill-manager.js +891 -0
  19. package/lib/soul.js +31 -0
  20. package/lib/tool-output-formatter.js +180 -0
  21. package/package.json +35 -32
  22. package/start-pave.sh +149 -0
  23. package/status.js +271 -0
  24. package/test/abort-stream.test.js +445 -0
  25. package/test/agent-auto-compaction.test.js +552 -0
  26. package/test/agent-comm-abort.test.js +95 -0
  27. package/test/agent-comm.test.js +598 -0
  28. package/test/agent-inbox.test.js +576 -0
  29. package/test/agent-init.test.js +264 -0
  30. package/test/agent-interrupt.test.js +314 -0
  31. package/test/agent-lifecycle.test.js +520 -0
  32. package/test/agent-log-files.test.js +349 -0
  33. package/test/agent-mode.manual-test.js +392 -0
  34. package/test/agent-parsing.test.js +228 -0
  35. package/test/agent-post-stream-idle.test.js +762 -0
  36. package/test/agent-registry.test.js +359 -0
  37. package/test/agent-rm.test.js +442 -0
  38. package/test/agent-spawn.test.js +933 -0
  39. package/test/agent-status-api.test.js +624 -0
  40. package/test/agent-update.test.js +435 -0
  41. package/test/args-parser.test.js +391 -0
  42. package/test/auto-compaction-chat.manual-test.js +227 -0
  43. package/test/auto-compaction.test.js +941 -0
  44. package/test/build-config.test.js +120 -0
  45. package/test/build-npm.test.js +388 -0
  46. package/test/chat-command.test.js +137 -0
  47. package/test/chat-leading-lines.test.js +159 -0
  48. package/test/config-flag.test.js +272 -0
  49. package/test/cursor-drift.test.js +135 -0
  50. package/test/debug-require.js +23 -0
  51. package/test/dir-migration.test.js +323 -0
  52. package/test/duration.test.js +229 -0
  53. package/test/ghostty-term.test.js +202 -0
  54. package/test/http500-backoff.test.js +854 -0
  55. package/test/integration.test.js +86 -0
  56. package/test/memory-guard-env.test.js +220 -0
  57. package/test/pr233-fixes.test.js +259 -0
  58. package/test/run-agent-init.js +297 -0
  59. package/test/run-all.js +64 -0
  60. package/test/run-config-flag.js +159 -0
  61. package/test/run-cursor-drift.js +82 -0
  62. package/test/run-session-path.js +154 -0
  63. package/test/run-tests.js +643 -0
  64. package/test/sandbox-redirect.test.js +202 -0
  65. package/test/session-path.test.js +132 -0
  66. package/test/shebang-strip.test.js +241 -0
  67. package/test/soul-reinject.test.js +1027 -0
  68. package/test/soul-reread.test.js +281 -0
  69. package/test/tool-output-formatter.test.js +486 -0
  70. package/test/tool-output-gating.test.js +143 -0
  71. package/test/tool-states.test.js +167 -0
  72. package/test/tools-flag.test.js +65 -0
  73. package/test/tui-attach.test.js +1255 -0
  74. package/test/tui-compaction.test.js +354 -0
  75. package/test/tui-wrap.test.js +568 -0
  76. package/test-binary.js +52 -0
  77. package/test-binary2.js +36 -0
  78. package/LICENSE +0 -21
  79. package/pave.js +0 -3
  80. package/sandbox/SandboxRunner.js +0 -1
  81. package/sandbox/pave-run.js +0 -2
  82. package/sandbox/permission.js +0 -1
  83. package/sandbox/utils/yaml.js +0 -1
package/lib/soul.js ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * SOUL file validation helper.
3
+ * This module has NO side effects when required - safe to import in tests.
4
+ *
5
+ * @module lib/soul
6
+ */
7
+
8
+ const fs = require('fs');
9
+
10
+ /**
11
+ * Validate that a SOUL file path points to an existing, readable, regular file.
12
+ * Returns { valid: true } on success, or { valid: false, error: string } on failure.
13
+ * @param {string} resolvedPath - Absolute path to the SOUL file
14
+ * @returns {{ valid: boolean, error?: string }}
15
+ */
16
+ function validateSoulFile(resolvedPath) {
17
+ try {
18
+ const stat = fs.statSync(resolvedPath);
19
+ if (!stat.isFile()) {
20
+ return { valid: false, error: `SOUL path is not a regular file: ${resolvedPath}` };
21
+ }
22
+ fs.accessSync(resolvedPath, fs.constants.R_OK);
23
+ return { valid: true };
24
+ } catch (err) {
25
+ const code = err && err.code ? ` (${err.code})` : '';
26
+ const message = err && err.message ? err.message : 'Unknown error';
27
+ return { valid: false, error: `Error validating SOUL file${code}: ${message} (path: ${resolvedPath})` };
28
+ }
29
+ }
30
+
31
+ module.exports = { validateSoulFile };
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Tool Output Formatting Utilities for PAVE CLI
3
+ * Provides shell-box style formatting matching the TUI output format
4
+ */
5
+
6
+ // ANSI color codes for tool output
7
+ const TOOL_COLORS = {
8
+ reset: '\x1b[0m',
9
+ bold: '\x1b[1m',
10
+ dim: '\x1b[2m',
11
+ red: '\x1b[31m',
12
+ green: '\x1b[32m',
13
+ yellow: '\x1b[33m',
14
+ cyan: '\x1b[36m',
15
+ gray: '\x1b[90m',
16
+ };
17
+
18
+ // Box drawing characters (matching TUI)
19
+ const BOX = {
20
+ topLeft: '╭',
21
+ topRight: '╮',
22
+ bottomLeft: '╰',
23
+ bottomRight: '╯',
24
+ horizontal: '─',
25
+ vertical: '│',
26
+ };
27
+
28
+ /**
29
+ * Extract command string from tool input for display
30
+ * Creates pseudo-commands for non-bash tools (matching TUI behavior)
31
+ * @param {string} tool - Tool name
32
+ * @param {object|string} input - Tool input data
33
+ * @returns {string} Command string for display
34
+ */
35
+ function extractToolCommand(tool, input) {
36
+ if (!input) return tool;
37
+
38
+ try {
39
+ const inputData = typeof input === 'string' ? JSON.parse(input) : input;
40
+
41
+ // bash tool - use actual command
42
+ if (inputData.command) {
43
+ return inputData.command;
44
+ }
45
+
46
+ // read tool - show as "read <filepath>"
47
+ if (inputData.filePath && tool === 'read') {
48
+ return `read ${inputData.filePath}`;
49
+ }
50
+
51
+ // grep tool - show as 'grep "pattern" path'
52
+ if (inputData.pattern && tool === 'grep') {
53
+ const path = inputData.path || inputData.include || '.';
54
+ return `grep "${inputData.pattern}" ${path}`;
55
+ }
56
+
57
+ // glob tool - show as 'glob "pattern"'
58
+ if (inputData.pattern && tool === 'glob') {
59
+ return `glob "${inputData.pattern}"`;
60
+ }
61
+
62
+ // edit tool - show as 'edit <filepath>'
63
+ if (inputData.filePath && tool === 'edit') {
64
+ return `edit ${inputData.filePath}`;
65
+ }
66
+
67
+ // write tool - show as 'write <filepath>'
68
+ if (inputData.filePath && tool === 'write') {
69
+ return `write ${inputData.filePath}`;
70
+ }
71
+
72
+ // task tool - show as 'task "<description>"'
73
+ if (inputData.description && tool === 'task') {
74
+ return `task "${inputData.description}"`;
75
+ }
76
+
77
+ // webfetch tool - show as 'fetch <url>'
78
+ if (inputData.url && tool === 'webfetch') {
79
+ return `fetch ${inputData.url}`;
80
+ }
81
+
82
+ // Default: just return tool name
83
+ return tool;
84
+ } catch (e) {
85
+ return tool;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Format tool output as a shell-style box (matching TUI format)
91
+ * @param {string} toolName - Name of the tool (e.g., 'bash', 'read')
92
+ * @param {string} status - Tool status ('running', 'completed', 'error')
93
+ * @param {object|string} input - Tool input parameters
94
+ * @param {string} output - Tool output/result
95
+ * @param {number} duration - Execution duration in milliseconds
96
+ * @param {object} options - Formatting options
97
+ * @param {boolean} options.verbose - Show input details (not used in box format)
98
+ * @param {boolean} options.noColor - Disable colors
99
+ * @param {number} options.width - Box width (default 60, matching TUI)
100
+ * @returns {string} Formatted shell box string
101
+ */
102
+ function formatToolOutput(toolName, status, input, output, duration, options = {}) {
103
+ const { noColor = false, width = 60 } = options;
104
+
105
+ // Use colors or empty strings based on noColor option
106
+ const C = noColor ? {
107
+ reset: '', bold: '', dim: '', red: '', green: '', yellow: '', cyan: '', gray: '',
108
+ } : TOOL_COLORS;
109
+
110
+ const b = BOX;
111
+ const boxWidth = width;
112
+
113
+ // Extract command for display
114
+ const command = extractToolCommand(toolName, input);
115
+
116
+ // Status indicator and header color
117
+ let statusIcon = '';
118
+ let headerColor = C.cyan;
119
+ if (status === 'completed') {
120
+ statusIcon = ` ${C.green}✓${C.reset}`;
121
+ headerColor = C.cyan;
122
+ } else if (status === 'error') {
123
+ statusIcon = ` ${C.red}✗${C.reset}`;
124
+ headerColor = C.red;
125
+ } else if (status === 'running') {
126
+ statusIcon = ` ${C.yellow}⟳${C.reset}`;
127
+ headerColor = C.yellow;
128
+ }
129
+
130
+ // Duration string
131
+ const durationStr = duration !== undefined ? ` ${C.gray}(${duration}ms)${C.reset}` : '';
132
+
133
+ // Build header: ╭─ bash ✓ (17ms) ────────╮
134
+ const headerLabel = ` ${toolName}${statusIcon}${durationStr} `;
135
+ // Calculate actual display length (strip ANSI codes)
136
+ const labelDisplayLen = headerLabel.replace(/\x1b\[[0-9;]*m/g, '').length;
137
+ // Account for: topLeft(1) + leading horizontal(1) + label + trailing fill + topRight(1) = boxWidth
138
+ const headerFill = Math.max(0, boxWidth - labelDisplayLen - 3);
139
+ const topBorder = `${C.gray}${b.topLeft}${b.horizontal}${C.reset}${headerColor}${headerLabel}${C.reset}${C.gray}${b.horizontal.repeat(headerFill)}${b.topRight}${C.reset}`;
140
+
141
+ // Build content lines
142
+ const lines = [];
143
+
144
+ // Command line with $ prompt and optional cursor for running state
145
+ const cursor = status === 'running' ? '█' : '';
146
+ const cmdLine = `${C.green}$${C.reset} ${command}${cursor}`;
147
+ lines.push(cmdLine);
148
+
149
+ // Output lines (show everything)
150
+ if (output) {
151
+ // Stringify object output if needed
152
+ const outputStr = typeof output === 'object' ? JSON.stringify(output, null, 2) : output;
153
+ const outputLines = outputStr.split('\n');
154
+ for (const line of outputLines) {
155
+ // Apply error coloring for error status
156
+ const lineContent = status === 'error' ? `${C.red}${line}${C.reset}` : line;
157
+ lines.push(lineContent);
158
+ }
159
+ }
160
+
161
+ // Remove trailing empty/whitespace-only lines to avoid extra blank lines at the bottom of the box
162
+ // Strip ANSI escape codes before checking emptiness so colored empty lines (e.g., error output) are caught
163
+ while (lines.length > 0 && lines[lines.length - 1].replace(/\x1b\[[0-9;]*m/g, '').trim() === '') {
164
+ lines.pop();
165
+ }
166
+
167
+ // Bottom border - account for: bottomLeft(1) + fill + bottomRight(1) = boxWidth
168
+ const bottomBorder = `${C.gray}${b.bottomLeft}${b.horizontal.repeat(Math.max(0, boxWidth - 2))}${b.bottomRight}${C.reset}`;
169
+
170
+ // Combine all parts, adding a leading and trailing newline around the box
171
+ const result = '\n' + [topBorder, ...lines, bottomBorder].join('\n') + '\n';
172
+ return result;
173
+ }
174
+
175
+ module.exports = {
176
+ formatToolOutput,
177
+ extractToolCommand,
178
+ TOOL_COLORS,
179
+ BOX,
180
+ };
package/package.json CHANGED
@@ -1,14 +1,36 @@
1
1
  {
2
2
  "name": "@cnrai/pave",
3
- "version": "0.3.33",
4
- "description": "PAVE - Personal AI Virtual Environment. AI agent framework for the terminal.",
5
- "main": "pave.js",
3
+ "version": "0.3.35",
4
+ "description": "Personal AI Virtual Environment - Terminal UI + OpenCode-Lite Server",
5
+ "main": "index.js",
6
6
  "bin": {
7
- "pave": "./pave.js"
7
+ "pave": "./index.js"
8
8
  },
9
9
  "scripts": {
10
- "start": "node pave.js"
10
+ "start": "node index.js",
11
+ "start:ish": "node --expose-gc index.js",
12
+ "start:debug": "node --expose-gc index.js --debug --verbose",
13
+ "help": "node index.js --help",
14
+ "build": "node build.js",
15
+ "build:install": "node build.js --install",
16
+ "build:npm": "node build-npm.js",
17
+ "build:npm:publish": "node build-npm.js --publish",
18
+ "clean": "rm -rf dist",
19
+ "test": "node test/run-tests.js",
20
+ "test:verbose": "node test/run-tests.js --verbose",
21
+ "test:ish": "node test/ish-fetch.test.js",
22
+ "status": "node status.js"
11
23
  },
24
+ "keywords": [
25
+ "ai",
26
+ "terminal",
27
+ "tui",
28
+ "opencode",
29
+ "ish",
30
+ "ios",
31
+ "memory-efficient",
32
+ "resource-constrained"
33
+ ],
12
34
  "engines": {
13
35
  "node": ">=16.0.0"
14
36
  },
@@ -18,34 +40,15 @@
18
40
  "win32"
19
41
  ],
20
42
  "dependencies": {
21
- "blessed": "^0.1.81",
22
- "js-yaml": "^4.1.0"
23
- },
24
- "files": [
25
- "pave.js",
26
- "sandbox/",
27
- "README.md",
28
- "LICENSE"
29
- ],
30
- "keywords": [
31
- "ai",
32
- "terminal",
33
- "tui",
34
- "agent",
35
- "cli",
36
- "ish",
37
- "ios",
38
- "node16",
39
- "memory-efficient"
40
- ],
41
- "repository": {
42
- "type": "git",
43
- "url": "git+https://github.com/cnrai/openpave.git"
43
+ "abort-controller": "^3.0.0",
44
+ "figlet": "^1.10.0",
45
+ "js-yaml": "^4.1.0",
46
+ "ulid": "^2.3.0"
44
47
  },
45
- "homepage": "https://github.com/cnrai/openpave",
46
- "bugs": {
47
- "url": "https://github.com/cnrai/openpave/issues"
48
+ "devDependencies": {
49
+ "esbuild": "^0.27.3",
50
+ "javascript-obfuscator": "^5.3.0"
48
51
  },
49
- "author": "CNRAI",
52
+ "author": "PAVE Team",
50
53
  "license": "MIT"
51
54
  }
package/start-pave.sh ADDED
@@ -0,0 +1,149 @@
1
+ #!/bin/bash
2
+ # PAVE Startup Script
3
+ # Automatically detects environment and applies optimal settings
4
+
5
+ set -e
6
+
7
+ # Get script directory
8
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9
+ PAVE_DIR="$SCRIPT_DIR"
10
+
11
+ # Default options
12
+ NODE_ARGS="--expose-gc"
13
+ PAVE_ARGS=""
14
+
15
+ # Parse arguments
16
+ HELP_REQUESTED=false
17
+ ISH_MODE=""
18
+ MEMORY_LIMIT=""
19
+ DEBUG_MODE=false
20
+
21
+ while [[ $# -gt 0 ]]; do
22
+ case $1 in
23
+ --help|-h)
24
+ HELP_REQUESTED=true
25
+ shift
26
+ ;;
27
+ --ish-mode)
28
+ ISH_MODE="--ish-mode"
29
+ if [[ $2 =~ ^(true|false|on|off|1|0)$ ]]; then
30
+ ISH_MODE="--ish-mode $2"
31
+ shift
32
+ fi
33
+ shift
34
+ ;;
35
+ --memory-limit)
36
+ if [[ $2 =~ ^[0-9]+$ ]]; then
37
+ MEMORY_LIMIT="--memory-limit $2"
38
+ shift
39
+ fi
40
+ shift
41
+ ;;
42
+ --debug)
43
+ DEBUG_MODE=true
44
+ PAVE_ARGS="$PAVE_ARGS --debug"
45
+ shift
46
+ ;;
47
+ --verbose)
48
+ PAVE_ARGS="$PAVE_ARGS --verbose"
49
+ shift
50
+ ;;
51
+ --no-gc)
52
+ NODE_ARGS=""
53
+ shift
54
+ ;;
55
+ *)
56
+ # Pass through other arguments to PAVE
57
+ PAVE_ARGS="$PAVE_ARGS $1"
58
+ shift
59
+ ;;
60
+ esac
61
+ done
62
+
63
+ # Show help if requested
64
+ if [ "$HELP_REQUESTED" = true ]; then
65
+ cat << EOF
66
+ PAVE Startup Script
67
+
68
+ USAGE:
69
+ $0 [options] [pave-options]
70
+
71
+ STARTUP OPTIONS:
72
+ --help, -h Show this help
73
+ --ish-mode [on|off] Force ISH mode on/off (auto-detected by default)
74
+ --memory-limit <MB> Set memory limit in MB
75
+ --debug Enable debug mode
76
+ --verbose Enable verbose output
77
+ --no-gc Disable --expose-gc Node.js flag
78
+
79
+ EXAMPLES:
80
+ $0 # Standard startup with auto-detection
81
+ $0 --ish-mode # Force ISH mode
82
+ $0 --memory-limit 32 # Custom memory limit
83
+ $0 --debug --verbose # Debug mode with verbose output
84
+
85
+ All other arguments are passed to PAVE directly.
86
+ For full PAVE options, run: $0 -- --help
87
+
88
+ ENVIRONMENT DETECTION:
89
+ - ISH mode is auto-detected based on platform and environment
90
+ - Garbage collection is enabled by default for memory protection
91
+ - Memory limits are automatically set based on detected environment
92
+
93
+ EOF
94
+ exit 0
95
+ fi
96
+
97
+ # Build final command arguments
98
+ FINAL_PAVE_ARGS="$ISH_MODE $MEMORY_LIMIT $PAVE_ARGS"
99
+
100
+ # Environment detection
101
+ detect_environment() {
102
+ local is_ish=false
103
+
104
+ # Check for iSH indicators
105
+ if [[ "$OSTYPE" == "linux"* ]]; then
106
+ if [[ "$TERM_PROGRAM" == "iSH" ]] || [[ -f "/etc/alpine-release" ]] || [[ "$(uname -a)" == *"alpine"* ]]; then
107
+ is_ish=true
108
+ fi
109
+ fi
110
+
111
+ echo $is_ish
112
+ }
113
+
114
+ IS_ISH=$(detect_environment)
115
+
116
+ # Print startup banner
117
+ echo "=============================================="
118
+ echo "PAVE Startup Script"
119
+ echo "=============================================="
120
+ echo "Environment: $(if [ "$IS_ISH" = true ]; then echo "iSH (iOS)"; else echo "Standard"; fi)"
121
+ echo "Node args: $NODE_ARGS"
122
+ echo "PAVE args: $FINAL_PAVE_ARGS"
123
+ echo "Working directory: $PAVE_DIR"
124
+
125
+ # ISH-specific recommendations
126
+ if [ "$IS_ISH" = true ]; then
127
+ echo ""
128
+ echo "ISH DETECTED:"
129
+ echo " - Memory protection enabled"
130
+ echo " - Garbage collection enabled"
131
+ echo " - Conservative memory limits applied"
132
+
133
+ if [ "$DEBUG_MODE" = true ]; then
134
+ echo " - Debug logging enabled (logs to ~/.pave/pave.log)"
135
+ fi
136
+ fi
137
+
138
+ echo "=============================================="
139
+ echo ""
140
+
141
+ # Change to PAVE directory
142
+ cd "$PAVE_DIR"
143
+
144
+ # Execute PAVE with appropriate arguments
145
+ if [ -n "$NODE_ARGS" ]; then
146
+ exec node $NODE_ARGS index.js $FINAL_PAVE_ARGS
147
+ else
148
+ exec node index.js $FINAL_PAVE_ARGS
149
+ fi