@hncode/hncode 0.5.1 → 0.5.2
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/bin/install.cjs +24 -44
- package/bin/preinstall.cjs +0 -14
- package/package.json +1 -1
- package/src/agent-task.js +12 -1
- package/src/agent.js +111 -33
- package/src/cell-buffer.js +5 -0
- package/src/clipboard.js +42 -6
- package/src/context-bar.js +46 -5
- package/src/doctor.js +7 -2
- package/src/git.js +35 -4
- package/src/llm.js +6 -0
- package/src/permissions.js +29 -7
- package/src/registry-browser.js +21 -6
- package/src/session-core.js +203 -0
- package/src/session-host.js +213 -0
- package/src/session-view.js +110 -0
- package/src/settings-schema.js +12 -9
- package/src/shell-safety.js +95 -6
- package/src/swarm-progress.js +348 -293
- package/src/tasks-browser.js +4 -1
- package/src/team/constants.js +20 -0
- package/src/team/context.js +89 -0
- package/src/team/mailbox.js +139 -0
- package/src/team/runner.js +347 -0
- package/src/team/spawn.js +247 -0
- package/src/team/store.js +145 -0
- package/src/team/tasks.js +164 -0
- package/src/team/team.js +311 -0
- package/src/team-panel.js +226 -0
- package/src/term.js +27 -0
- package/src/tool-result.js +20 -5
- package/src/tools/agent-swarm.js +114 -11
- package/src/tools/agent.js +53 -3
- package/src/tools/edit.js +591 -436
- package/src/tools/git.js +15 -3
- package/src/tools/index.js +2 -0
- package/src/tools/team.js +398 -0
- package/src/tools/web-search.js +88 -75
- package/src/tui.js +5805 -4862
- package/src/update.js +285 -115
- package/src/web-daemon.js +27 -4
- package/src/web-live.js +250 -0
package/src/shell-safety.js
CHANGED
|
@@ -44,6 +44,31 @@
|
|
|
44
44
|
* metacharacters — unquoted operator characters seen (for the read-only test)
|
|
45
45
|
* redirects — unquoted redirect targets, e.g. ['/dev/null']
|
|
46
46
|
*/
|
|
47
|
+
/**
|
|
48
|
+
* Index of the `)` closing the `(` at `open`, or -1.
|
|
49
|
+
*
|
|
50
|
+
* Depth-counted so a nested `$(a $(b))` closes at the right place, and quote-aware so a `)`
|
|
51
|
+
* inside a string argument does not end the substitution early.
|
|
52
|
+
*/
|
|
53
|
+
function matchingParen(s, open) {
|
|
54
|
+
let depth = 0;
|
|
55
|
+
for (let i = open; i < s.length; i++) {
|
|
56
|
+
const c = s[i];
|
|
57
|
+
if (c === '\\') { i++; continue; }
|
|
58
|
+
if (c === "'" || c === '"') {
|
|
59
|
+
const quote = c;
|
|
60
|
+
for (i++; i < s.length; i++) {
|
|
61
|
+
if (quote === '"' && s[i] === '\\') { i++; continue; }
|
|
62
|
+
if (s[i] === quote) break;
|
|
63
|
+
}
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (c === '(') depth++;
|
|
67
|
+
else if (c === ')') { depth--; if (depth === 0) return i; }
|
|
68
|
+
}
|
|
69
|
+
return -1;
|
|
70
|
+
}
|
|
71
|
+
|
|
47
72
|
export function splitSegments(cmd) {
|
|
48
73
|
const s = String(cmd == null ? '' : cmd);
|
|
49
74
|
const segments = [];
|
|
@@ -91,11 +116,45 @@ export function splitSegments(cmd) {
|
|
|
91
116
|
i += 1;
|
|
92
117
|
continue;
|
|
93
118
|
}
|
|
94
|
-
if (c === '<'
|
|
119
|
+
if (c === '<') { metacharacters.add(c); i += 1; continue; }
|
|
120
|
+
// A backtick substitution is the same hazard as `$(…)`: it runs a command and splices the
|
|
121
|
+
// result into this one. Handled as a pair so the inner command is judged too.
|
|
122
|
+
if (c === '`') {
|
|
123
|
+
metacharacters.add(c);
|
|
124
|
+
const close = s.indexOf('`', i + 1);
|
|
125
|
+
if (close > 0) {
|
|
126
|
+
const inner = s.slice(i + 1, close);
|
|
127
|
+
if (inner.trim()) { segments.push(current); segments.push(inner); current = ''; }
|
|
128
|
+
current += s.slice(i, close + 1);
|
|
129
|
+
i = close + 1;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
i += 1; continue;
|
|
133
|
+
}
|
|
95
134
|
if (c === '$') {
|
|
96
|
-
// `$(…)`
|
|
97
|
-
//
|
|
98
|
-
|
|
135
|
+
// `$(…)` runs a command and its OUTPUT is spliced into this one. The text has to stay
|
|
136
|
+
// in the segment for tokenizing, but the substitution is also a command in its own
|
|
137
|
+
// right — so it is emitted as a segment of its own. Without that, `npm test $(rm -rf /)`
|
|
138
|
+
// was a single segment that the `Bash(npm test *)` prefix rule matched, and
|
|
139
|
+
// isDestructiveCommand never saw `rm` at all.
|
|
140
|
+
if (s[i + 1] === '(') {
|
|
141
|
+
metacharacters.add('$');
|
|
142
|
+
const close = matchingParen(s, i + 1);
|
|
143
|
+
if (close > 0) {
|
|
144
|
+
const inner = s.slice(i + 2, close);
|
|
145
|
+
if (inner.trim()) {
|
|
146
|
+
// The outer text so far, then the substitution itself as its own command.
|
|
147
|
+
segments.push(current);
|
|
148
|
+
segments.push(inner);
|
|
149
|
+
current = '';
|
|
150
|
+
}
|
|
151
|
+
// The outer command continues with the substitution text in place, so what follows
|
|
152
|
+
// it stays attached to the same command line.
|
|
153
|
+
current += s.slice(i, close + 1);
|
|
154
|
+
i = close + 1;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
99
158
|
current += c; i += 1; continue;
|
|
100
159
|
}
|
|
101
160
|
if (c === '(' || c === ')') { metacharacters.add(c); current += c; i += 1; continue; }
|
|
@@ -296,6 +355,32 @@ function rmIsDestructive(args) {
|
|
|
296
355
|
/** `mkfs`, `mkfs.ext4`, `mkfs.xfs` — the whole family. */
|
|
297
356
|
function isMkfs(cmd) { return cmd === 'mkfs' || cmd.startsWith('mkfs.'); }
|
|
298
357
|
|
|
358
|
+
/**
|
|
359
|
+
* The git SUBCOMMAND from an argv tail.
|
|
360
|
+
*
|
|
361
|
+
* The obvious `args.find(a => !a.startsWith('-'))` returns the first non-flag WORD, which
|
|
362
|
+
* for `git -C /repo push` is `/repo` — the VALUE of `-C`. Every subcommand rule then missed
|
|
363
|
+
* and `git -C x push`, `git -c core.pager=x reset --hard`, `git -c foo=bar clean` all
|
|
364
|
+
* passed the destructive check. Three flag shapes have to be stepped over:
|
|
365
|
+
*
|
|
366
|
+
* -C <path> / -c <name=value> a separate value in the NEXT word
|
|
367
|
+
* --git-dir=<path> the value is inside the SAME word
|
|
368
|
+
* -p / --paginate no value at all
|
|
369
|
+
*
|
|
370
|
+
* Only the flags git treats as value-taking are listed, so a bare `-x` is still skipped as
|
|
371
|
+
* a plain flag and cannot swallow the subcommand beside it.
|
|
372
|
+
*/
|
|
373
|
+
function gitSubcommand(args) {
|
|
374
|
+
const TAKES_VALUE = new Set(['-C', '-c', '--git-dir', '--work-tree', '--namespace', '--exec-path']);
|
|
375
|
+
for (let i = 0; i < args.length; i++) {
|
|
376
|
+
const a = args[i];
|
|
377
|
+
if (!a.startsWith('-')) return a.toLowerCase();
|
|
378
|
+
// `--git-dir=/x` carries its own value, so the next word is the subcommand.
|
|
379
|
+
if (a.includes('=')) continue;
|
|
380
|
+
if (TAKES_VALUE.has(a)) i++; // step over the value
|
|
381
|
+
}
|
|
382
|
+
return '';
|
|
383
|
+
}
|
|
299
384
|
// Judge ONE already-unwrapped command. Returns a reason string, or null when the
|
|
300
385
|
// command is not destructive.
|
|
301
386
|
function judgeUnwrapped(cmd, args) {
|
|
@@ -352,7 +437,7 @@ function judgeUnwrapped(cmd, args) {
|
|
|
352
437
|
if (args.includes('prune')) return 'docker prune';
|
|
353
438
|
break;
|
|
354
439
|
case 'git': {
|
|
355
|
-
const sub = (args
|
|
440
|
+
const sub = gitSubcommand(args);
|
|
356
441
|
// A dry run changes nothing, and flagging it is what made `git push
|
|
357
442
|
// --dry-run` prompt in YOLO mode.
|
|
358
443
|
if (args.some((a) => a === '-n' || a === '--dry-run')) break;
|
|
@@ -454,7 +539,11 @@ export function isReadOnlyCommand(cmd) {
|
|
|
454
539
|
if (!u.cmd) return false; // a bare `sh -c "…"` is not provable
|
|
455
540
|
if (!READ_ONLY_BINS.has(u.cmd)) return false;
|
|
456
541
|
if (u.cmd === 'git') {
|
|
457
|
-
|
|
542
|
+
// The shared extractor, not `args.find(a => !a.startsWith('-'))`: that form returns the
|
|
543
|
+
// VALUE of `-C`/`-c` (`git -C /repo status` yielded `/repo`), so the verb was never read
|
|
544
|
+
// and an ordinary read-only git command was refused. The destructive check learned this
|
|
545
|
+
// first; the two judged the same command differently until they shared one function.
|
|
546
|
+
const sub = gitSubcommand(u.args);
|
|
458
547
|
if (!sub || !GIT_READ_SUBCMDS.has(sub)) return false;
|
|
459
548
|
// `tag`/`branch`/`remote`/`config`/`worktree` are read-only only WITHOUT a
|
|
460
549
|
// writing flag, so any of those flags refuses the whole command.
|