xiaotime 0.2.0 → 0.3.0

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 (44) hide show
  1. package/dist/__tests__/approval_menu.test.d.ts +15 -0
  2. package/dist/__tests__/approval_menu.test.d.ts.map +1 -0
  3. package/dist/__tests__/approval_menu.test.js +107 -0
  4. package/dist/__tests__/approval_menu.test.js.map +1 -0
  5. package/dist/__tests__/server_tool_render.test.d.ts +17 -0
  6. package/dist/__tests__/server_tool_render.test.d.ts.map +1 -0
  7. package/dist/__tests__/server_tool_render.test.js +74 -0
  8. package/dist/__tests__/server_tool_render.test.js.map +1 -0
  9. package/dist/__tests__/settings.test.d.ts +16 -0
  10. package/dist/__tests__/settings.test.d.ts.map +1 -0
  11. package/dist/__tests__/settings.test.js +289 -0
  12. package/dist/__tests__/settings.test.js.map +1 -0
  13. package/dist/__tests__/tools.test.d.ts +17 -0
  14. package/dist/__tests__/tools.test.d.ts.map +1 -0
  15. package/dist/__tests__/tools.test.js +381 -0
  16. package/dist/__tests__/tools.test.js.map +1 -0
  17. package/dist/auth.d.ts.map +1 -1
  18. package/dist/auth.js +6 -2
  19. package/dist/auth.js.map +1 -1
  20. package/dist/config.d.ts +19 -0
  21. package/dist/config.d.ts.map +1 -1
  22. package/dist/config.js +24 -1
  23. package/dist/config.js.map +1 -1
  24. package/dist/display.d.ts +19 -0
  25. package/dist/display.d.ts.map +1 -1
  26. package/dist/display.js +23 -0
  27. package/dist/display.js.map +1 -1
  28. package/dist/settings.d.ts +109 -0
  29. package/dist/settings.d.ts.map +1 -0
  30. package/dist/settings.js +332 -0
  31. package/dist/settings.js.map +1 -0
  32. package/dist/tools.d.ts +11 -3
  33. package/dist/tools.d.ts.map +1 -1
  34. package/dist/tools.js +96 -33
  35. package/dist/tools.js.map +1 -1
  36. package/dist/ui_state.d.ts +134 -0
  37. package/dist/ui_state.d.ts.map +1 -0
  38. package/dist/ui_state.js +460 -0
  39. package/dist/ui_state.js.map +1 -0
  40. package/dist/ws.d.ts +5 -0
  41. package/dist/ws.d.ts.map +1 -1
  42. package/dist/ws.js +99 -66
  43. package/dist/ws.js.map +1 -1
  44. package/package.json +5 -2
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Per-user CLI settings + approval allowlist.
3
+ *
4
+ * SPRINT-CLI-APPROVAL-MENU-1 (#6509) PR2.
5
+ *
6
+ * Backs the option-2 "Run and don't ask again" path of
7
+ * `promptApprovalMenu`. Allowlist is persisted to
8
+ * `~/.xiaotime/settings.json` (mode 0600) and consulted before every
9
+ * `executeTool` invocation of the three prompting client-side tools
10
+ * (`shell_command` / `write_file` / `git_commit`). A match short-
11
+ * circuits the menu entirely; the inline log line gets an
12
+ * ` (auto-approved)` suffix so the user can still see what ran.
13
+ *
14
+ * Schema is `version: 1`; missing/malformed/unknown-version files
15
+ * fall back to an empty allowlist (with a stderr warning for
16
+ * malformed/unknown cases) — never crash the CLI on corrupted local
17
+ * config (ISC-6509-007).
18
+ *
19
+ * Pattern derivation differs per tool (see D2 in the sprint spec):
20
+ * - shell_command: argv[0] + leading flags + first subcommand
21
+ * token. `npm install --no-save lodash` → "npm install".
22
+ * Multi-level subcommand grammars (gh issue list) collapse to
23
+ * the first 2 tokens; users can hand-edit settings.json for
24
+ * finer-grained patterns.
25
+ * - write_file: `dirname(path) + "/**"`. Glob-matched against
26
+ * subsequent write_file invocations so allowlisting a write to
27
+ * `/repo/foo.ts` covers `/repo/bar.ts`.
28
+ * - git_commit: literal `"*"` — allowlisting one commit
29
+ * allowlists all future commits (the commit message + staged
30
+ * files vary by definition; there's no meaningful pattern
31
+ * parameter to narrow on).
32
+ *
33
+ * Distinct from `terminal_orchestrator/task_manager.py:57`'s
34
+ * server-side allowlist (EXACT-NAME on argv[0] basename, binary `gh`
35
+ * only). That's a security perimeter for `task_create`'s long-running
36
+ * managed-task envelope; this is a UX gate for client-side tool
37
+ * approvals. Two allowlists, two audiences, two update cadences.
38
+ */
39
+ export type AllowlistTool = "shell_command" | "write_file" | "git_commit";
40
+ export interface AllowlistEntry {
41
+ tool: AllowlistTool;
42
+ pattern: string;
43
+ added_at: string;
44
+ }
45
+ /**
46
+ * Settings-file location. Resolved lazily per call so the
47
+ * `XIAOTIME_SETTINGS_PATH` env-var override can redirect (used by
48
+ * tests to avoid touching the real `~/.xiaotime/settings.json`).
49
+ * Default: `~/.xiaotime/settings.json`.
50
+ */
51
+ export declare function _settingsPath(): string;
52
+ /**
53
+ * Read the allowlist from disk. Returns [] for any of:
54
+ * - file missing (first run; not an error)
55
+ * - file unreadable (permissions; logged + treated as empty)
56
+ * - malformed JSON (logged + treated as empty)
57
+ * - unknown schema version (logged + treated as empty)
58
+ * - shape doesn't match SettingsV1 (silently treated as empty)
59
+ *
60
+ * Never throws. The CLI must remain usable when the user has a
61
+ * corrupted local config (ISC-6509-007).
62
+ */
63
+ export declare function loadAllowlist(): AllowlistEntry[];
64
+ /**
65
+ * Append a new entry to the allowlist + persist. Creates
66
+ * ~/.xiaotime/ with mode 0700 if it doesn't exist; writes
67
+ * settings.json with mode 0600. Idempotent on duplicate
68
+ * (tool, pattern) — duplicate appends are silently dropped.
69
+ */
70
+ export declare function appendAllowlist(entry: {
71
+ tool: AllowlistTool;
72
+ pattern: string;
73
+ }): void;
74
+ /**
75
+ * Derive the allowlist pattern for a tool invocation. Returns null
76
+ * if the input shape is unusable (empty command, missing path). The
77
+ * pattern is used both at write time (option-2 selection persists
78
+ * `derivePattern(tool, input)`) and at read time (the next call's
79
+ * derived pattern feeds `matchAllowlist`).
80
+ *
81
+ * Per D2 in the sprint spec, derivation is coarse on purpose. For
82
+ * shell_command, the v1 rule captures argv[0] + leading flags + at
83
+ * most one subcommand token. Multi-level grammars (gh issue list,
84
+ * git remote add) collapse to 2-token prefixes; users who want finer
85
+ * patterns can hand-edit settings.json. For write_file, the directory
86
+ * the file lives in is allowlisted (`/**` suffix); writes deeper in
87
+ * the tree match. For git_commit, the literal `"*"` allowlists all
88
+ * future commits.
89
+ */
90
+ export declare function derivePattern(tool: AllowlistTool, input: Record<string, unknown>): string | null;
91
+ /**
92
+ * Check whether a tool invocation matches any entry in the
93
+ * allowlist. Matching semantics differ per tool:
94
+ * - shell_command: exact equality between derived pattern and
95
+ * stored pattern (`npm install` matches only `npm install`,
96
+ * not `npm install lodash` directly — but the derived pattern
97
+ * for `npm install lodash` IS `npm install`, so it matches.)
98
+ * - write_file: glob-match the incoming path against each
99
+ * `write_file` entry's stored glob.
100
+ * - git_commit: any `git_commit` entry matches (stored pattern
101
+ * is `"*"`).
102
+ *
103
+ * The cross-tool isolation invariant (PR3 ISC-spec): an entry with
104
+ * `tool === "shell_command"` can never authorize a `write_file` call
105
+ * even if patterns coincidentally match — the tool field is checked
106
+ * first.
107
+ */
108
+ export declare function matchAllowlist(tool: AllowlistTool, input: Record<string, unknown>, allowlist: AllowlistEntry[]): AllowlistEntry | null;
109
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAMH,MAAM,MAAM,aAAa,GAAG,eAAe,GAAG,YAAY,GAAG,YAAY,CAAC;AAQ1E,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CAClB;AASD;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAKtC;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,IAAI,cAAc,EAAE,CAoChD;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC9C,IAAI,CAwCN;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,GAAG,IAAI,CA+Cf;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,aAAa,EACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,cAAc,EAAE,GAC1B,cAAc,GAAG,IAAI,CAsBvB"}
@@ -0,0 +1,332 @@
1
+ "use strict";
2
+ /**
3
+ * Per-user CLI settings + approval allowlist.
4
+ *
5
+ * SPRINT-CLI-APPROVAL-MENU-1 (#6509) PR2.
6
+ *
7
+ * Backs the option-2 "Run and don't ask again" path of
8
+ * `promptApprovalMenu`. Allowlist is persisted to
9
+ * `~/.xiaotime/settings.json` (mode 0600) and consulted before every
10
+ * `executeTool` invocation of the three prompting client-side tools
11
+ * (`shell_command` / `write_file` / `git_commit`). A match short-
12
+ * circuits the menu entirely; the inline log line gets an
13
+ * ` (auto-approved)` suffix so the user can still see what ran.
14
+ *
15
+ * Schema is `version: 1`; missing/malformed/unknown-version files
16
+ * fall back to an empty allowlist (with a stderr warning for
17
+ * malformed/unknown cases) — never crash the CLI on corrupted local
18
+ * config (ISC-6509-007).
19
+ *
20
+ * Pattern derivation differs per tool (see D2 in the sprint spec):
21
+ * - shell_command: argv[0] + leading flags + first subcommand
22
+ * token. `npm install --no-save lodash` → "npm install".
23
+ * Multi-level subcommand grammars (gh issue list) collapse to
24
+ * the first 2 tokens; users can hand-edit settings.json for
25
+ * finer-grained patterns.
26
+ * - write_file: `dirname(path) + "/**"`. Glob-matched against
27
+ * subsequent write_file invocations so allowlisting a write to
28
+ * `/repo/foo.ts` covers `/repo/bar.ts`.
29
+ * - git_commit: literal `"*"` — allowlisting one commit
30
+ * allowlists all future commits (the commit message + staged
31
+ * files vary by definition; there's no meaningful pattern
32
+ * parameter to narrow on).
33
+ *
34
+ * Distinct from `terminal_orchestrator/task_manager.py:57`'s
35
+ * server-side allowlist (EXACT-NAME on argv[0] basename, binary `gh`
36
+ * only). That's a security perimeter for `task_create`'s long-running
37
+ * managed-task envelope; this is a UX gate for client-side tool
38
+ * approvals. Two allowlists, two audiences, two update cadences.
39
+ */
40
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
41
+ if (k2 === undefined) k2 = k;
42
+ var desc = Object.getOwnPropertyDescriptor(m, k);
43
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
44
+ desc = { enumerable: true, get: function() { return m[k]; } };
45
+ }
46
+ Object.defineProperty(o, k2, desc);
47
+ }) : (function(o, m, k, k2) {
48
+ if (k2 === undefined) k2 = k;
49
+ o[k2] = m[k];
50
+ }));
51
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
52
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
53
+ }) : function(o, v) {
54
+ o["default"] = v;
55
+ });
56
+ var __importStar = (this && this.__importStar) || (function () {
57
+ var ownKeys = function(o) {
58
+ ownKeys = Object.getOwnPropertyNames || function (o) {
59
+ var ar = [];
60
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
61
+ return ar;
62
+ };
63
+ return ownKeys(o);
64
+ };
65
+ return function (mod) {
66
+ if (mod && mod.__esModule) return mod;
67
+ var result = {};
68
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
69
+ __setModuleDefault(result, mod);
70
+ return result;
71
+ };
72
+ })();
73
+ Object.defineProperty(exports, "__esModule", { value: true });
74
+ exports._settingsPath = _settingsPath;
75
+ exports.loadAllowlist = loadAllowlist;
76
+ exports.appendAllowlist = appendAllowlist;
77
+ exports.derivePattern = derivePattern;
78
+ exports.matchAllowlist = matchAllowlist;
79
+ const fs = __importStar(require("fs"));
80
+ const path = __importStar(require("path"));
81
+ const os = __importStar(require("os"));
82
+ const ALLOWLIST_TOOLS = new Set([
83
+ "shell_command",
84
+ "write_file",
85
+ "git_commit",
86
+ ]);
87
+ /**
88
+ * Settings-file location. Resolved lazily per call so the
89
+ * `XIAOTIME_SETTINGS_PATH` env-var override can redirect (used by
90
+ * tests to avoid touching the real `~/.xiaotime/settings.json`).
91
+ * Default: `~/.xiaotime/settings.json`.
92
+ */
93
+ function _settingsPath() {
94
+ return (process.env.XIAOTIME_SETTINGS_PATH
95
+ ?? path.join(os.homedir(), ".xiaotime", "settings.json"));
96
+ }
97
+ function _settingsDir() {
98
+ return path.dirname(_settingsPath());
99
+ }
100
+ /**
101
+ * Read the allowlist from disk. Returns [] for any of:
102
+ * - file missing (first run; not an error)
103
+ * - file unreadable (permissions; logged + treated as empty)
104
+ * - malformed JSON (logged + treated as empty)
105
+ * - unknown schema version (logged + treated as empty)
106
+ * - shape doesn't match SettingsV1 (silently treated as empty)
107
+ *
108
+ * Never throws. The CLI must remain usable when the user has a
109
+ * corrupted local config (ISC-6509-007).
110
+ */
111
+ function loadAllowlist() {
112
+ if (!fs.existsSync(_settingsPath()))
113
+ return [];
114
+ let raw;
115
+ try {
116
+ raw = fs.readFileSync(_settingsPath(), "utf-8");
117
+ }
118
+ catch (err) {
119
+ process.stderr.write(`[xiaotime] ~/.xiaotime/settings.json unreadable: ${err.message}\n`);
120
+ return [];
121
+ }
122
+ let parsed;
123
+ try {
124
+ parsed = JSON.parse(raw);
125
+ }
126
+ catch (err) {
127
+ process.stderr.write(`[xiaotime] ~/.xiaotime/settings.json malformed JSON, treating as empty: ${err.message}\n`);
128
+ return [];
129
+ }
130
+ const obj = parsed;
131
+ if (!obj || typeof obj !== "object")
132
+ return [];
133
+ if (obj.version !== 1) {
134
+ process.stderr.write(`[xiaotime] ~/.xiaotime/settings.json: unrecognized schema version ${JSON.stringify(obj.version)}, treating as empty\n`);
135
+ return [];
136
+ }
137
+ const entries = obj.permissions?.allow;
138
+ if (!Array.isArray(entries))
139
+ return [];
140
+ return entries.filter(_isValidEntry);
141
+ }
142
+ /**
143
+ * Append a new entry to the allowlist + persist. Creates
144
+ * ~/.xiaotime/ with mode 0700 if it doesn't exist; writes
145
+ * settings.json with mode 0600. Idempotent on duplicate
146
+ * (tool, pattern) — duplicate appends are silently dropped.
147
+ */
148
+ function appendAllowlist(entry) {
149
+ const existing = loadAllowlist();
150
+ const isDup = existing.some((e) => e.tool === entry.tool && e.pattern === entry.pattern);
151
+ if (isDup)
152
+ return;
153
+ const newEntries = [
154
+ ...existing,
155
+ {
156
+ tool: entry.tool,
157
+ pattern: entry.pattern,
158
+ added_at: new Date().toISOString(),
159
+ },
160
+ ];
161
+ const settings = {
162
+ version: 1,
163
+ permissions: { allow: newEntries },
164
+ };
165
+ // Directory: mode 0700 (user-only). The settings file's mode is the
166
+ // load-bearing one but the parent permission tightens the surface
167
+ // against a hostile-co-tenant scenario on shared boxes.
168
+ fs.mkdirSync(_settingsDir(), { recursive: true, mode: 0o700 });
169
+ // openSync + writeSync + closeSync (with O_TRUNC + O_CREAT + 0o600
170
+ // mode) so the file is created with 0600 from the first byte. Using
171
+ // writeFileSync's `mode` option only sets mode on CREATE — but on
172
+ // overwrite of an existing file Node skips chmod entirely on some
173
+ // platforms. Explicit fchmod closes that gap.
174
+ const fd = fs.openSync(_settingsPath(), fs.constants.O_WRONLY | fs.constants.O_CREAT | fs.constants.O_TRUNC, 0o600);
175
+ try {
176
+ fs.writeSync(fd, JSON.stringify(settings, null, 2) + "\n");
177
+ fs.fchmodSync(fd, 0o600);
178
+ }
179
+ finally {
180
+ fs.closeSync(fd);
181
+ }
182
+ }
183
+ /**
184
+ * Derive the allowlist pattern for a tool invocation. Returns null
185
+ * if the input shape is unusable (empty command, missing path). The
186
+ * pattern is used both at write time (option-2 selection persists
187
+ * `derivePattern(tool, input)`) and at read time (the next call's
188
+ * derived pattern feeds `matchAllowlist`).
189
+ *
190
+ * Per D2 in the sprint spec, derivation is coarse on purpose. For
191
+ * shell_command, the v1 rule captures argv[0] + leading flags + at
192
+ * most one subcommand token. Multi-level grammars (gh issue list,
193
+ * git remote add) collapse to 2-token prefixes; users who want finer
194
+ * patterns can hand-edit settings.json. For write_file, the directory
195
+ * the file lives in is allowlisted (`/**` suffix); writes deeper in
196
+ * the tree match. For git_commit, the literal `"*"` allowlists all
197
+ * future commits.
198
+ */
199
+ function derivePattern(tool, input) {
200
+ if (tool === "shell_command") {
201
+ const cmd = String(input.command ?? "").trim();
202
+ if (!cmd)
203
+ return null;
204
+ const tokens = cmd.split(/\s+/);
205
+ // #6538 CR-r1: guard flag-only input. `"-la -h"` would
206
+ // otherwise produce pattern `"-la -h"`, which is nonsensical
207
+ // (no argv[0]) and would persist as a broken allowlist entry.
208
+ if (tokens[0].startsWith("-"))
209
+ return null;
210
+ const result = [tokens[0]];
211
+ for (let i = 1; i < tokens.length; i++) {
212
+ const tok = tokens[i];
213
+ if (tok.startsWith("-")) {
214
+ result.push(tok);
215
+ continue;
216
+ }
217
+ // First non-flag arg. Treat as a subcommand iff it's at
218
+ // position 1 (immediately after argv[0]). Otherwise stop —
219
+ // it's a data argument (path / value / package name) we
220
+ // don't want to bake into the pattern.
221
+ if (i === 1)
222
+ result.push(tok);
223
+ break;
224
+ }
225
+ return result.join(" ");
226
+ }
227
+ if (tool === "write_file") {
228
+ const p = String(input.path ?? "");
229
+ if (!p)
230
+ return null;
231
+ // #6538 CR-r1: normalize BEFORE deriving the pattern. Without
232
+ // this, a malicious or careless `input.path` like
233
+ // `"../../etc/passwd"` would persist as allowlist pattern
234
+ // `"../../etc/**"`, opening every future write to that
235
+ // resolved location to bypass the prompt. `path.resolve`
236
+ // anchors against the process cwd; tools.ts calls
237
+ // `resolvePath` (which uses the session cwd) before the file
238
+ // write itself, but derivePattern was operating on raw input
239
+ // — closing the gap here.
240
+ const normalized = path.resolve(p);
241
+ return path.dirname(normalized) + path.sep + "**";
242
+ }
243
+ if (tool === "git_commit") {
244
+ return "*";
245
+ }
246
+ return null;
247
+ }
248
+ /**
249
+ * Check whether a tool invocation matches any entry in the
250
+ * allowlist. Matching semantics differ per tool:
251
+ * - shell_command: exact equality between derived pattern and
252
+ * stored pattern (`npm install` matches only `npm install`,
253
+ * not `npm install lodash` directly — but the derived pattern
254
+ * for `npm install lodash` IS `npm install`, so it matches.)
255
+ * - write_file: glob-match the incoming path against each
256
+ * `write_file` entry's stored glob.
257
+ * - git_commit: any `git_commit` entry matches (stored pattern
258
+ * is `"*"`).
259
+ *
260
+ * The cross-tool isolation invariant (PR3 ISC-spec): an entry with
261
+ * `tool === "shell_command"` can never authorize a `write_file` call
262
+ * even if patterns coincidentally match — the tool field is checked
263
+ * first.
264
+ */
265
+ function matchAllowlist(tool, input, allowlist) {
266
+ for (const entry of allowlist) {
267
+ if (entry.tool !== tool)
268
+ continue;
269
+ if (tool === "git_commit")
270
+ return entry;
271
+ if (tool === "shell_command") {
272
+ const derived = derivePattern(tool, input);
273
+ if (derived !== null && derived === entry.pattern)
274
+ return entry;
275
+ continue;
276
+ }
277
+ if (tool === "write_file") {
278
+ const raw = String(input.path ?? "");
279
+ if (!raw)
280
+ continue;
281
+ // #6538 CR-r1: normalize the input path before matching for
282
+ // the same reason derivePattern does — so `../foo.txt`
283
+ // resolves to its absolute canonical form before being
284
+ // compared against stored absolute patterns.
285
+ const p = path.resolve(raw);
286
+ if (_globMatch(p, entry.pattern))
287
+ return entry;
288
+ continue;
289
+ }
290
+ }
291
+ return null;
292
+ }
293
+ // ── Internals ────────────────────────────────────────────────────
294
+ function _isValidEntry(e) {
295
+ if (!e || typeof e !== "object")
296
+ return false;
297
+ const entry = e;
298
+ return (typeof entry.tool === "string" &&
299
+ ALLOWLIST_TOOLS.has(entry.tool) &&
300
+ typeof entry.pattern === "string" &&
301
+ typeof entry.added_at === "string");
302
+ }
303
+ /**
304
+ * Tiny glob matcher. Supports `*` (matches everything) and
305
+ * `<prefix><sep>**` (matches `<prefix>` and any path under it),
306
+ * where `<sep>` is the platform path separator (`/` on Unix, `\`
307
+ * on Windows). Plain patterns require exact equality. Sufficient
308
+ * for write_file's directory-scope allowlist; not a general-
309
+ * purpose glob engine.
310
+ *
311
+ * #6538 CR-r1: was hardcoded to `/` — broken on Windows where
312
+ * `path.dirname` returns backslash-separated paths. Now uses
313
+ * `path.sep` so the comparison matches the form derivePattern
314
+ * stores (also using `path.sep`).
315
+ */
316
+ function _globMatch(p, pattern) {
317
+ if (pattern === "*")
318
+ return true;
319
+ const sepGlob = path.sep + "**";
320
+ if (pattern.endsWith(sepGlob)) {
321
+ const prefix = pattern.slice(0, -sepGlob.length);
322
+ return p === prefix || p.startsWith(prefix + path.sep);
323
+ }
324
+ // Tolerant of legacy entries that stored `/**` on Unix (no-op
325
+ // on Linux/macOS; harmless on Windows since path.sep is `\`).
326
+ if (pattern.endsWith("/**")) {
327
+ const prefix = pattern.slice(0, -3);
328
+ return p === prefix || p.startsWith(prefix + "/");
329
+ }
330
+ return p === pattern;
331
+ }
332
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../src/settings.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCH,sCAKC;AAiBD,sCAoCC;AAQD,0CA0CC;AAkBD,sCAkDC;AAmBD,wCA0BC;AA5PD,uCAAyB;AACzB,2CAA6B;AAC7B,uCAAyB;AAIzB,MAAM,eAAe,GAA+B,IAAI,GAAG,CAAC;IAC1D,eAAe;IACf,YAAY;IACZ,YAAY;CACb,CAAC,CAAC;AAeH;;;;;GAKG;AACH,SAAgB,aAAa;IAC3B,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,sBAAsB;WAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,eAAe,CAAC,CACzD,CAAC;AACJ,CAAC;AAED,SAAS,YAAY;IACnB,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;AACvC,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,aAAa;IAC3B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC;QAAE,OAAO,EAAE,CAAC;IAE/C,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,oDAAqD,GAAa,CAAC,OAAO,IAAI,CAC/E,CAAC;QACF,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,2EAA4E,GAAa,CAAC,OAAO,IAAI,CACtG,CAAC;QACF,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,GAAG,GAAG,MAAoC,CAAC;IACjD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IAC/C,IAAI,GAAG,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,qEAAqE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,uBAAuB,CACxH,CAAC;QACF,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,WAAW,EAAE,KAAK,CAAC;IACvC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC;IAEvC,OAAO,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;AACvC,CAAC;AAED;;;;;GAKG;AACH,SAAgB,eAAe,CAC7B,KAA+C;IAE/C,MAAM,QAAQ,GAAG,aAAa,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CACzB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,IAAI,CAAC,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,CAC5D,CAAC;IACF,IAAI,KAAK;QAAE,OAAO;IAElB,MAAM,UAAU,GAAqB;QACnC,GAAG,QAAQ;QACX;YACE,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACnC;KACF,CAAC;IACF,MAAM,QAAQ,GAAe;QAC3B,OAAO,EAAE,CAAC;QACV,WAAW,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE;KACnC,CAAC;IAEF,oEAAoE;IACpE,kEAAkE;IAClE,wDAAwD;IACxD,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/D,mEAAmE;IACnE,oEAAoE;IACpE,kEAAkE;IAClE,kEAAkE;IAClE,8CAA8C;IAC9C,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CACpB,aAAa,EAAE,EACf,EAAE,CAAC,SAAS,CAAC,QAAQ,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,OAAO,EACnE,KAAK,CACN,CAAC;IACF,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC3D,EAAE,CAAC,UAAU,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;YAAS,CAAC;QACT,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACnB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,SAAgB,aAAa,CAC3B,IAAmB,EACnB,KAA8B;IAE9B,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;QAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/C,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAChC,uDAAuD;QACvD,6DAA6D;QAC7D,8DAA8D;QAC9D,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAC3C,MAAM,MAAM,GAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACrC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACvC,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACjB,SAAS;YACX,CAAC;YACD,wDAAwD;YACxD,2DAA2D;YAC3D,wDAAwD;YACxD,uCAAuC;YACvC,IAAI,CAAC,KAAK,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM;QACR,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QACpB,8DAA8D;QAC9D,kDAAkD;QAClD,0DAA0D;QAC1D,uDAAuD;QACvD,yDAAyD;QACzD,kDAAkD;QAClD,6DAA6D;QAC7D,6DAA6D;QAC7D,0BAA0B;QAC1B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;IACpD,CAAC;IAED,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC1B,OAAO,GAAG,CAAC;IACb,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,cAAc,CAC5B,IAAmB,EACnB,KAA8B,EAC9B,SAA2B;IAE3B,KAAK,MAAM,KAAK,IAAI,SAAS,EAAE,CAAC;QAC9B,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI;YAAE,SAAS;QAClC,IAAI,IAAI,KAAK,YAAY;YAAE,OAAO,KAAK,CAAC;QACxC,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;YAC7B,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAC3C,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,CAAC,OAAO;gBAAE,OAAO,KAAK,CAAC;YAChE,SAAS;QACX,CAAC;QACD,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;YAC1B,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;YACrC,IAAI,CAAC,GAAG;gBAAE,SAAS;YACnB,4DAA4D;YAC5D,uDAAuD;YACvD,uDAAuD;YACvD,6CAA6C;YAC7C,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC5B,IAAI,UAAU,CAAC,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC;gBAAE,OAAO,KAAK,CAAC;YAC/C,SAAS;QACX,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,oEAAoE;AAEpE,SAAS,aAAa,CAAC,CAAU;IAC/B,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,KAAK,GAAG,CAA4B,CAAC;IAC3C,OAAO,CACL,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;QAC9B,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,IAAqB,CAAC;QAChD,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ;QACjC,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,CACnC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,SAAS,UAAU,CAAC,CAAS,EAAE,OAAe;IAC5C,IAAI,OAAO,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;IAChC,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACjD,OAAO,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACzD,CAAC;IACD,8DAA8D;IAC9D,8DAA8D;IAC9D,IAAI,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACpC,OAAO,CAAC,KAAK,MAAM,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,CAAC,KAAK,OAAO,CAAC;AACvB,CAAC"}
package/dist/tools.d.ts CHANGED
@@ -1,9 +1,17 @@
1
1
  /**
2
2
  * Local tool execution + approval gate.
3
3
  *
4
- * All tools execute on Harvey's machine. write_file and shell_command
5
- * require explicit y/N approval before executing.
4
+ * All tools execute on Harvey's machine. write_file, shell_command,
5
+ * and git_commit pass through `_gateApproval` which checks the
6
+ * persisted allowlist (`~/.xiaotime/settings.json`) before prompting.
7
+ * Matching allowlist entries short-circuit the menu; the post-execute
8
+ * status line is suffixed with ` (auto-approved)` so the user can
9
+ * see which calls bypassed the prompt (ISC-6509-004).
10
+ *
11
+ * Read-only tools (`read_file`, `list_directory`, `git_status`,
12
+ * `git_diff`) never gate (ISC-6509-006).
6
13
  */
14
+ import type { SessionUI } from "./ui_state.js";
7
15
  export interface ToolCall {
8
16
  tool_use_id: string;
9
17
  name: string;
@@ -15,5 +23,5 @@ export interface ToolResult {
15
23
  is_error: boolean;
16
24
  }
17
25
  export declare function setSessionCwd(cwd: string): void;
18
- export declare function executeTool(call: ToolCall): Promise<ToolResult>;
26
+ export declare function executeTool(call: ToolCall, ui: SessionUI): Promise<ToolResult>;
19
27
  //# sourceMappingURL=tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAID,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,QAExC;AAmBD,wBAAsB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAwJrE"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAS/C,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAID,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,QAExC;AA2ED,wBAAsB,WAAW,CAC/B,IAAI,EAAE,QAAQ,EACd,EAAE,EAAE,SAAS,GACZ,OAAO,CAAC,UAAU,CAAC,CA2KrB"}