@pugi/cli 0.1.0-alpha.10

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 (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/bin/run.js +2 -0
  4. package/dist/commands/jobs.js +245 -0
  5. package/dist/core/agents/loader.js +104 -0
  6. package/dist/core/agents/registry.js +69 -0
  7. package/dist/core/auto-open-browser.js +128 -0
  8. package/dist/core/bash-classifier.js +1001 -0
  9. package/dist/core/clipboard.js +70 -0
  10. package/dist/core/context/builder.js +114 -0
  11. package/dist/core/context/compaction-events.js +99 -0
  12. package/dist/core/context/compaction.js +602 -0
  13. package/dist/core/context/invariants.js +250 -0
  14. package/dist/core/context/markdown-loader.js +270 -0
  15. package/dist/core/credentials.js +355 -0
  16. package/dist/core/engine/adapter-runner.js +8 -0
  17. package/dist/core/engine/anvil-client.js +156 -0
  18. package/dist/core/engine/compaction-hook.js +154 -0
  19. package/dist/core/engine/index.js +12 -0
  20. package/dist/core/engine/native-pugi.js +369 -0
  21. package/dist/core/engine/noop.js +27 -0
  22. package/dist/core/engine/prompts.js +118 -0
  23. package/dist/core/engine/tool-bridge.js +313 -0
  24. package/dist/core/file-cache.js +29 -0
  25. package/dist/core/hooks.js +415 -0
  26. package/dist/core/index-store.js +260 -0
  27. package/dist/core/jobs/registry.js +462 -0
  28. package/dist/core/mcp/client.js +316 -0
  29. package/dist/core/mcp/registry.js +171 -0
  30. package/dist/core/mcp/trust.js +91 -0
  31. package/dist/core/path-security.js +63 -0
  32. package/dist/core/permission.js +309 -0
  33. package/dist/core/repl/cap-warning.js +91 -0
  34. package/dist/core/repl/clipboard-read.js +174 -0
  35. package/dist/core/repl/history-search.js +175 -0
  36. package/dist/core/repl/history.js +172 -0
  37. package/dist/core/repl/kill-ring.js +138 -0
  38. package/dist/core/repl/session.js +618 -0
  39. package/dist/core/repl/slash-commands.js +227 -0
  40. package/dist/core/repl/workspace-context.js +113 -0
  41. package/dist/core/session.js +258 -0
  42. package/dist/core/settings.js +59 -0
  43. package/dist/core/skills/loader.js +454 -0
  44. package/dist/core/skills/sources.js +480 -0
  45. package/dist/core/skills/trust.js +172 -0
  46. package/dist/core/subagents/dispatcher.js +258 -0
  47. package/dist/core/subagents/index.js +26 -0
  48. package/dist/core/subagents/spawn.js +86 -0
  49. package/dist/core/trust.js +109 -0
  50. package/dist/index.js +8 -0
  51. package/dist/runtime/cli.js +3405 -0
  52. package/dist/runtime/commands/agents.js +385 -0
  53. package/dist/runtime/commands/budget.js +192 -0
  54. package/dist/runtime/commands/config.js +231 -0
  55. package/dist/runtime/commands/privacy.js +107 -0
  56. package/dist/runtime/commands/skills.js +401 -0
  57. package/dist/runtime/commands/undo.js +329 -0
  58. package/dist/runtime/update-check.js +294 -0
  59. package/dist/tools/bash.js +660 -0
  60. package/dist/tools/file-tools.js +346 -0
  61. package/dist/tools/registry.js +25 -0
  62. package/dist/tools/web-fetch.js +535 -0
  63. package/dist/tui/agent-tree.js +66 -0
  64. package/dist/tui/conversation-pane.js +45 -0
  65. package/dist/tui/device-flow.js +142 -0
  66. package/dist/tui/input-box.js +474 -0
  67. package/dist/tui/login-picker.js +69 -0
  68. package/dist/tui/render.js +125 -0
  69. package/dist/tui/repl-render.js +240 -0
  70. package/dist/tui/repl-splash-art.js +64 -0
  71. package/dist/tui/repl-splash.js +111 -0
  72. package/dist/tui/repl.js +214 -0
  73. package/dist/tui/slash-palette.js +106 -0
  74. package/dist/tui/splash-data.js +61 -0
  75. package/dist/tui/splash.js +31 -0
  76. package/dist/tui/status-bar.js +71 -0
  77. package/dist/tui/update-banner.js +8 -0
  78. package/dist/tui/workspace-context.js +105 -0
  79. package/package.json +71 -0
@@ -0,0 +1,128 @@
1
+ import { spawn } from 'node:child_process';
2
+ /**
3
+ * Open `url` in the default browser. Returns `{ opened: true }` on a
4
+ * successful spawn, `{ opened: false }` on any failure. The url is not
5
+ * shell-escaped here; we always pass it as a single argv element to a
6
+ * direct spawn (no shell), so quoting is irrelevant.
7
+ */
8
+ export async function autoOpenBrowser(url, deps = {}) {
9
+ const platform = deps.platform ?? process.platform;
10
+ const spawnDetached = deps.spawnDetached ?? defaultSpawnDetached;
11
+ // Refuse anything that does not parse as http(s). The device-flow URL
12
+ // is always https — guarding here keeps a hostile server response
13
+ // from convincing us to spawn `open file:///etc/passwd` or worse.
14
+ if (!isSafeHttpUrl(url)) {
15
+ return { opened: false };
16
+ }
17
+ if (platform === 'darwin') {
18
+ return { opened: spawnDetached('open', [url]) };
19
+ }
20
+ if (platform === 'win32') {
21
+ // P1-3 (triple-review 2026-05-24): cmd.exe parses `&` as a command
22
+ // separator BEFORE Node hands argv to the child, regardless of
23
+ // `shell: false`. A device-flow URL like
24
+ // `https://app.pugi.io/devices/authorize?user_code=ABC&trace=xyz`
25
+ // would be truncated at the `&`, opening only the first half. We
26
+ // prefer PowerShell (its argv parser is sane: a single quoted URL
27
+ // round-trips verbatim) and fall back to a double-quoted cmd
28
+ // invocation when PowerShell is missing from PATH.
29
+ if (spawnDetached('powershell', [
30
+ '-NoProfile',
31
+ '-NonInteractive',
32
+ '-Command',
33
+ 'Start-Process',
34
+ quoteForPowerShell(url),
35
+ ])) {
36
+ return { opened: true };
37
+ }
38
+ // Fallback: `cmd /c start "" "<url>"`. The URL itself is wrapped
39
+ // in double quotes so cmd does not split on `&`; any embedded `"`
40
+ // in the URL is escaped using cmd's caret-quote convention.
41
+ return {
42
+ opened: spawnDetached('cmd', ['/c', 'start', '""', quoteForCmd(url)]),
43
+ };
44
+ }
45
+ if (platform === 'linux' || platform === 'freebsd' || platform === 'openbsd') {
46
+ // Try xdg-open first (the freedesktop standard), then gio (GNOME),
47
+ // then a couple of well-known browsers as a last resort. Each
48
+ // attempt is a fresh spawn — if the binary is missing the helper
49
+ // returns false and we fall through.
50
+ for (const cmd of LINUX_OPENERS) {
51
+ if (spawnDetached(cmd, [url]))
52
+ return { opened: true };
53
+ }
54
+ return { opened: false };
55
+ }
56
+ // Unknown platform (android, aix, sunos…) — degrade gracefully.
57
+ return { opened: false };
58
+ }
59
+ const LINUX_OPENERS = ['xdg-open', 'gio', 'gnome-open', 'kde-open'];
60
+ function isSafeHttpUrl(candidate) {
61
+ try {
62
+ const url = new URL(candidate);
63
+ return url.protocol === 'https:' || url.protocol === 'http:';
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ /**
70
+ * P1-3 (triple-review 2026-05-24): wrap a URL for PowerShell's
71
+ * `Start-Process` invocation. PowerShell's single-quote string literal
72
+ * does NOT process escape sequences; the only metachar inside is the
73
+ * single quote itself (escaped by doubling). URLs do not contain
74
+ * single quotes in practice, but the helper handles them defensively
75
+ * so future input shapes do not break.
76
+ */
77
+ function quoteForPowerShell(url) {
78
+ return `'${url.replace(/'/g, "''")}'`;
79
+ }
80
+ /**
81
+ * P1-3 (triple-review 2026-05-24): wrap a URL for cmd.exe's
82
+ * `start ""` invocation. Double quotes pin the URL as a single token
83
+ * so cmd does NOT split on `&`, `|`, `^`, `<`, `>`. Embedded `"` (rare
84
+ * in URLs but defensible) is escaped via cmd's caret-quote convention:
85
+ * `^"`.
86
+ */
87
+ function quoteForCmd(url) {
88
+ return `"${url.replace(/"/g, '^"')}"`;
89
+ }
90
+ /**
91
+ * Real spawn implementation. Detaches the child so the Pugi CLI can
92
+ * exit (or keep polling) without inheriting the browser's lifecycle.
93
+ * Returns `true` only when the spawn produced a pid; any error event
94
+ * (ENOENT for a missing binary, EACCES for a sandboxed permission
95
+ * denial) flips the result to `false`.
96
+ */
97
+ function defaultSpawnDetached(cmd, args) {
98
+ try {
99
+ const options = {
100
+ detached: true,
101
+ stdio: 'ignore',
102
+ shell: false,
103
+ };
104
+ const child = spawn(cmd, args.slice(), options);
105
+ // P3 polish (triple-review 2026-05-24): swallow the async `error`
106
+ // event (ENOENT for a missing binary, EACCES for a sandboxed
107
+ // permission denial). The old `errored` flag was always false at
108
+ // the synchronous `return !errored` point (the `error` event is
109
+ // delivered later in the next tick), so the flag was dead. The
110
+ // listener is still required: without it, Node escalates the
111
+ // event to an unhandled-error and crashes the host process when
112
+ // the binary is missing.
113
+ child.on('error', () => undefined);
114
+ if (typeof child.pid !== 'number')
115
+ return false;
116
+ // unref so the parent event loop is not held open by the browser
117
+ // handle. The browser process continues independently.
118
+ child.unref();
119
+ // Callers treat `true` as best-effort: the fallback "Browser
120
+ // didn't open?" hint is always rendered, so a silent spawn
121
+ // failure still leaves the user a usable path.
122
+ return true;
123
+ }
124
+ catch {
125
+ return false;
126
+ }
127
+ }
128
+ //# sourceMappingURL=auto-open-browser.js.map