sapper-iq 1.1.36 โ 1.1.38
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/README.md +65 -4
- package/package.json +6 -1
- package/sapper-ui.mjs +57 -3
- package/sapper.mjs +2347 -275
- package/.github/workflows/ci.yml +0 -35
- package/.github/workflows/publish.yml +0 -46
- package/PUBLISHING.md +0 -148
- package/old/sapper copy 2.mjs +0 -673
- package/old/sapper copy 3.mjs +0 -1154
- package/old/sapper copy.mjs +0 -483
- package/old/sapper copy4.mjs +0 -1950
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ Sapper is a command-line interface that connects to Ollama models to help you bu
|
|
|
13
13
|
- ๐ฏ **Context-aware** - Automatically detects directory contents
|
|
14
14
|
- โก **Live streaming** - See AI responses in real-time
|
|
15
15
|
- ๐ **Security prompts** - Review commands before execution
|
|
16
|
+
- โ๏ธ **Inline approval feedback** - Type feedback, or use `f` and `e` shortcuts at shell or file approval prompts, to make Sapper revise the command or change
|
|
17
|
+
- ๐งต **Background shell sessions** - Long-running commands can hand off to tracked background sessions with chunked output inspection
|
|
16
18
|
|
|
17
19
|
## Installation
|
|
18
20
|
|
|
@@ -36,13 +38,17 @@ sapper
|
|
|
36
38
|
|
|
37
39
|
- `/reset` or `/clear-session` - Start a new session
|
|
38
40
|
- `/session-info` - Show current session details
|
|
41
|
+
- `/summary` - View or change auto-summary settings
|
|
42
|
+
- `/shell` - Inspect shell config and tracked background sessions
|
|
43
|
+
- `/shell read <id>` - Read output from a tracked shell session
|
|
44
|
+
- `/shell stop <id>` - Stop a tracked shell session
|
|
39
45
|
- `/step` - Toggle step-by-step mode
|
|
40
46
|
- `/help` - Show command help
|
|
41
47
|
- `exit` - Exit Sapper
|
|
42
48
|
|
|
43
49
|
### Example Interactions
|
|
44
50
|
|
|
45
|
-
```
|
|
51
|
+
```text
|
|
46
52
|
> set up a React project in ./my-app
|
|
47
53
|
> run the development server
|
|
48
54
|
> create a login component with TypeScript
|
|
@@ -57,6 +63,59 @@ sapper
|
|
|
57
63
|
4. **Review & approve** - Security prompts for shell commands
|
|
58
64
|
5. **Context awareness** - Sapper understands your project structure
|
|
59
65
|
|
|
66
|
+
## Config
|
|
67
|
+
|
|
68
|
+
Sapper creates `.sapper/config.json` on first run. You can tune context behavior there.
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"autoAttach": true,
|
|
73
|
+
"contextLimit": null,
|
|
74
|
+
"toolRoundLimit": 40,
|
|
75
|
+
"summaryPhases": true,
|
|
76
|
+
"summarizeTriggerPercent": 65,
|
|
77
|
+
"shell": {
|
|
78
|
+
"streamToModel": true,
|
|
79
|
+
"backgroundMode": "auto",
|
|
80
|
+
"backgroundAfterSeconds": 8,
|
|
81
|
+
"outputChunkChars": 4000
|
|
82
|
+
},
|
|
83
|
+
"thinking": {
|
|
84
|
+
"mode": "auto"
|
|
85
|
+
},
|
|
86
|
+
"streaming": {
|
|
87
|
+
"showPhaseStatus": true,
|
|
88
|
+
"showHeartbeat": true,
|
|
89
|
+
"idleNoticeSeconds": 4
|
|
90
|
+
},
|
|
91
|
+
"prompt": {
|
|
92
|
+
"prepend": "",
|
|
93
|
+
"append": "Prefer concise answers.",
|
|
94
|
+
"coreOverride": ""
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- `toolRoundLimit`: maximum tool-call rounds Sapper will allow in one prompt loop before it forces a final answer. Default: `40`.
|
|
100
|
+
- `summaryPhases`: show or hide the step-by-step auto-summary progress list.
|
|
101
|
+
- `summarizeTriggerPercent`: start summarizing older context near this percentage of the active context window. Lower values summarize earlier and reduce large-context pauses.
|
|
102
|
+
- `shell.streamToModel`: include shell output chunks in tool results when a command is handed off to a background session.
|
|
103
|
+
- `shell.backgroundMode`: three modes: `off`, `auto`, or `on`. `off` keeps commands fully attached so you keep seeing live shell output in the terminal. `auto` backgrounds likely long-running commands like dev servers; `on` applies the timeout to every shell command.
|
|
104
|
+
- `shell.backgroundAfterSeconds`: how long Sapper waits before handing an eligible running command off to a background shell session.
|
|
105
|
+
- `shell.outputChunkChars`: maximum shell output chunk size returned to the model for background handoffs and session reads.
|
|
106
|
+
- `thinking.mode`: three modes: `auto`, `on`, or `off`. `auto` skips long reasoning blocks for simple prompts, `on` always enables reasoning for every prompt, and `off` disables it for every prompt. This controls model reasoning visibility, not shell backgrounding.
|
|
107
|
+
- `streaming.showPhaseStatus`: show short status lines when Sapper is finalizing output, executing tools, or looping for the next model turn.
|
|
108
|
+
- `streaming.showHeartbeat`: keep updating the live progress line during quiet streamed phases instead of looking frozen.
|
|
109
|
+
- `streaming.idleNoticeSeconds`: print an idle notice after this many seconds without visible streamed output.
|
|
110
|
+
- `prompt.prepend`: insert custom instructions before the default Sapper prompt.
|
|
111
|
+
- `prompt.append`: add custom instructions near the end of the system prompt.
|
|
112
|
+
- `prompt.coreOverride`: replace the default Sapper core prompt block while keeping the tool, context, agent, and skill sections.
|
|
113
|
+
|
|
114
|
+
You can also change these inside Sapper with `/summary`, for example `/summary phases off` or `/summary trigger 60`.
|
|
115
|
+
Prompt config is read from `.sapper/config.json` and Sapper refreshes it on the next turn if you edit the file while it is running.
|
|
116
|
+
Background shell sessions are controlled through `run_shell` with `__shell_list__`, `__shell_read__ <session_id>`, and `__shell_stop__ <session_id>`.
|
|
117
|
+
You can also inspect them directly in Sapper with `/shell`, `/shell read <session_id>`, and `/shell stop <session_id>`.
|
|
118
|
+
|
|
60
119
|
## Supported Tools
|
|
61
120
|
|
|
62
121
|
- `SHELL` - Execute terminal commands
|
|
@@ -69,12 +128,14 @@ sapper
|
|
|
69
128
|
## Examples
|
|
70
129
|
|
|
71
130
|
**Create a Next.js project:**
|
|
72
|
-
|
|
131
|
+
|
|
132
|
+
```text
|
|
73
133
|
> create a Next.js app with TypeScript and Tailwind in ./my-nextjs-app
|
|
74
134
|
```
|
|
75
135
|
|
|
76
136
|
**Add features to existing project:**
|
|
77
|
-
|
|
137
|
+
|
|
138
|
+
```text
|
|
78
139
|
> analyze the codebase in ./my-project
|
|
79
140
|
> add a user authentication system
|
|
80
141
|
> create API endpoints for user management
|
|
@@ -96,4 +157,4 @@ MIT
|
|
|
96
157
|
|
|
97
158
|
## Author
|
|
98
159
|
|
|
99
|
-
Ibrahim Ihsan
|
|
160
|
+
Ibrahim Ihsan
|
package/package.json
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sapper-iq",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.38",
|
|
4
4
|
"description": "AI-powered development assistant that executes commands and builds projects",
|
|
5
5
|
"main": "sapper.mjs",
|
|
6
6
|
"bin": {
|
|
7
7
|
"sapper": "sapper.mjs",
|
|
8
8
|
"sapper-ui": "sapper-ui.mjs"
|
|
9
9
|
},
|
|
10
|
+
"files": [
|
|
11
|
+
"sapper.mjs",
|
|
12
|
+
"sapper-ui.mjs",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
10
15
|
"type": "module",
|
|
11
16
|
"scripts": {
|
|
12
17
|
"start": "node sapper.mjs"
|
package/sapper-ui.mjs
CHANGED
|
@@ -88,6 +88,56 @@ function loadSkills() {
|
|
|
88
88
|
|
|
89
89
|
const IGNORE_DIRS = new Set(['node_modules', '.git', '.sapper', '__pycache__', '.next', 'dist', 'build', '.cache']);
|
|
90
90
|
|
|
91
|
+
// โโโ .sapperignore Support โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
92
|
+
const SAPPERIGNORE_FILE = '.sapperignore';
|
|
93
|
+
|
|
94
|
+
function loadSapperIgnorePatterns() {
|
|
95
|
+
const patterns = [];
|
|
96
|
+
try {
|
|
97
|
+
const ignorePath = join(workingDir, SAPPERIGNORE_FILE);
|
|
98
|
+
if (fs.existsSync(ignorePath)) {
|
|
99
|
+
const lines = fs.readFileSync(ignorePath, 'utf8').split('\n');
|
|
100
|
+
for (const rawLine of lines) {
|
|
101
|
+
const line = rawLine.trim();
|
|
102
|
+
if (!line || line.startsWith('#')) continue;
|
|
103
|
+
const negate = line.startsWith('!');
|
|
104
|
+
const pattern = negate ? line.slice(1) : line;
|
|
105
|
+
patterns.push({ pattern, negate });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch (e) {}
|
|
109
|
+
return patterns;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let _sapperIgnorePatterns = null;
|
|
113
|
+
function getSapperIgnorePatterns() {
|
|
114
|
+
if (_sapperIgnorePatterns === null) _sapperIgnorePatterns = loadSapperIgnorePatterns();
|
|
115
|
+
return _sapperIgnorePatterns;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function ignorePatternToRegex(pattern) {
|
|
119
|
+
let p = pattern.replace(/\/+$/, '');
|
|
120
|
+
p = p.replace(/([.+^${}()|[\]\\])/g, '\\$1');
|
|
121
|
+
p = p.replace(/\*\*/g, '<<<GLOBSTAR>>>');
|
|
122
|
+
p = p.replace(/\*/g, '[^/]*');
|
|
123
|
+
p = p.replace(/<<<GLOBSTAR>>>/g, '.*');
|
|
124
|
+
p = p.replace(/\?/g, '[^/]');
|
|
125
|
+
return new RegExp(`(^|/)${p}($|/)`, 'i');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function shouldIgnore(nameOrPath) {
|
|
129
|
+
const baseName = nameOrPath.includes('/') ? nameOrPath.split('/').pop() : nameOrPath;
|
|
130
|
+
if (IGNORE_DIRS.has(baseName)) return true;
|
|
131
|
+
const patterns = getSapperIgnorePatterns();
|
|
132
|
+
if (patterns.length === 0) return false;
|
|
133
|
+
let ignored = false;
|
|
134
|
+
for (const { pattern, negate } of patterns) {
|
|
135
|
+
const regex = ignorePatternToRegex(pattern);
|
|
136
|
+
if (regex.test(nameOrPath) || regex.test(baseName)) ignored = !negate;
|
|
137
|
+
}
|
|
138
|
+
return ignored;
|
|
139
|
+
}
|
|
140
|
+
|
|
91
141
|
function safePath(p) {
|
|
92
142
|
const resolved = resolve(workingDir, p || '.');
|
|
93
143
|
if (!resolved.startsWith(workingDir)) return null;
|
|
@@ -150,7 +200,7 @@ const tools = {
|
|
|
150
200
|
let dir = resolve(workingDir, path.trim() || '.');
|
|
151
201
|
if (dir === '/') dir = workingDir;
|
|
152
202
|
const entries = fs.readdirSync(dir);
|
|
153
|
-
return entries.filter(e => !
|
|
203
|
+
return entries.filter(e => !shouldIgnore(e) && !e.startsWith('.')).join('\n') || '(empty)';
|
|
154
204
|
} catch (e) { return `Error: ${e.message}`; }
|
|
155
205
|
},
|
|
156
206
|
read: (path) => {
|
|
@@ -208,7 +258,11 @@ const tools = {
|
|
|
208
258
|
},
|
|
209
259
|
search: (pattern) => {
|
|
210
260
|
return new Promise((res) => {
|
|
211
|
-
const
|
|
261
|
+
const allIgnoreDirs = new Set(IGNORE_DIRS);
|
|
262
|
+
for (const { pattern: p, negate } of getSapperIgnorePatterns()) {
|
|
263
|
+
if (!negate && p.endsWith('/')) allIgnoreDirs.add(p.replace(/\/+$/, ''));
|
|
264
|
+
}
|
|
265
|
+
const excludes = [...allIgnoreDirs].join(',');
|
|
212
266
|
const cmd = `grep -rEin "${pattern.replace(/"/g, '\\"')}" . --exclude-dir={${excludes}} --include="*.{js,ts,jsx,tsx,py,java,go,rs,rb,php,c,cpp,h,css,scss,html,json,md,txt,yml,yaml,toml,sh}" 2>/dev/null | head -50`;
|
|
213
267
|
const proc = spawn('sh', ['-c', cmd], { cwd: workingDir });
|
|
214
268
|
let out = '';
|
|
@@ -403,7 +457,7 @@ function getTreeEntries(dirPath) {
|
|
|
403
457
|
try {
|
|
404
458
|
const entries = fs.readdirSync(safe);
|
|
405
459
|
return entries
|
|
406
|
-
.filter(e => !
|
|
460
|
+
.filter(e => !shouldIgnore(e) && !e.startsWith('.'))
|
|
407
461
|
.map(e => {
|
|
408
462
|
try {
|
|
409
463
|
const stat = fs.statSync(join(safe, e));
|