gsd-pi 2.3.9 → 2.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-pi",
3
- "version": "2.3.9",
3
+ "version": "2.3.10",
4
4
  "description": "GSD — Get Shit Done coding agent",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -44,7 +44,9 @@
44
44
  "prepublishOnly": "npm run sync-pkg-version && npm run build"
45
45
  },
46
46
  "dependencies": {
47
+ "@clack/prompts": "^1.1.0",
47
48
  "@mariozechner/pi-coding-agent": "^0.57.1",
49
+ "picocolors": "^1.1.1",
48
50
  "playwright": "^1.58.2"
49
51
  },
50
52
  "devDependencies": {
@@ -1,18 +1,38 @@
1
1
  #!/usr/bin/env node
2
- import { execSync } from 'child_process'
2
+
3
+ import { exec as execCb } from 'child_process'
3
4
  import { createRequire } from 'module'
4
5
  import os from 'os'
5
- import { fileURLToPath } from 'url'
6
6
  import { dirname, resolve } from 'path'
7
+ import { fileURLToPath } from 'url'
7
8
 
8
9
  const __dirname = dirname(fileURLToPath(import.meta.url))
9
10
  const require = createRequire(import.meta.url)
10
11
  const pkg = require(resolve(__dirname, '..', 'package.json'))
12
+ const cwd = resolve(__dirname, '..')
13
+
14
+ // ---------------------------------------------------------------------------
15
+ // Async exec helper — captures stdout+stderr, never inherits to terminal
16
+ // ---------------------------------------------------------------------------
17
+ function run(cmd, options = {}) {
18
+ return new Promise((resolve) => {
19
+ execCb(cmd, { cwd, ...options }, (error, stdout, stderr) => {
20
+ resolve({ ok: !error, stdout, stderr, error })
21
+ })
22
+ })
23
+ }
11
24
 
12
- // Colors
25
+ // ---------------------------------------------------------------------------
26
+ // Redirect stdout → stderr so npm always shows postinstall output.
27
+ // npm ≥7 suppresses stdout from lifecycle scripts by default; stderr is
28
+ // always forwarded. Clack writes to process.stdout, so we reroute it.
29
+ // ---------------------------------------------------------------------------
30
+ process.stdout.write = process.stderr.write.bind(process.stderr)
31
+
32
+ // ---------------------------------------------------------------------------
33
+ // ASCII banner — printed before clack UI for brand recognition
34
+ // ---------------------------------------------------------------------------
13
35
  const cyan = '\x1b[36m'
14
- const green = '\x1b[32m'
15
- const yellow = '\x1b[33m'
16
36
  const dim = '\x1b[2m'
17
37
  const reset = '\x1b[0m'
18
38
 
@@ -27,58 +47,81 @@ const banner =
27
47
  ' ╚═════╝ ╚══════╝╚═════╝ ' +
28
48
  reset + '\n' +
29
49
  '\n' +
30
- ` Get Shit Done ${dim}v${pkg.version}${reset}\n` +
31
- ` A standalone coding agent that plans, executes, and ships.\n` +
32
- '\n' +
33
- ` ${green}✓${reset} Installed successfully\n` +
34
- ` ${dim}Run ${reset}${cyan}gsd${reset}${dim} to get started.${reset}\n`
35
-
36
- function run(command, options = {}) {
37
- return execSync(command, {
38
- cwd: resolve(__dirname, '..'),
39
- encoding: 'utf8',
40
- stdio: ['ignore', 'pipe', 'pipe'],
41
- ...options,
42
- })
43
- }
50
+ ` Get Shit Done ${dim}v${pkg.version}${reset}\n`
44
51
 
45
- function printCaptured(output) {
46
- if (output) process.stderr.write(output)
47
- }
52
+ // ---------------------------------------------------------------------------
53
+ // Main — wrapped in async IIFE, with graceful fallback if clack fails
54
+ // ---------------------------------------------------------------------------
55
+ ;(async () => {
56
+ process.stderr.write(banner)
48
57
 
49
- process.stderr.write(banner)
50
-
51
- // Apply patches to upstream dependencies (non-fatal)
52
- try {
53
- const output = run('npx patch-package')
54
- printCaptured(output)
55
- process.stderr.write(`\n ${green}✓${reset} Patches applied\n`)
56
- } catch (error) {
57
- printCaptured(error.stdout)
58
- printCaptured(error.stderr)
59
- process.stderr.write(`\n ${yellow}⚠${reset} Failed to apply patches — run ${cyan}npx patch-package${reset} manually\n`)
60
- }
58
+ let p, pc
61
59
 
62
- // Install Playwright chromium for browser tools (non-fatal).
63
- // We intentionally avoid --with-deps here because install scripts should not
64
- // block on interactive sudo prompts. Playwright validates host requirements
65
- // after download; if Linux libs are missing, suggest the explicit follow-up.
66
- try {
67
- const output = run('npx playwright install chromium')
68
- printCaptured(output)
69
- process.stderr.write(`\n ${green}✓${reset} Browser tools ready\n\n`)
70
- } catch (error) {
71
- const output = `${error.stdout ?? ''}${error.stderr ?? ''}`
72
- printCaptured(output)
73
-
74
- if (os.platform() === 'linux' && output.includes('Host system is missing dependencies to run browsers.')) {
75
- process.stderr.write(
76
- `\n ${yellow}⚠${reset} Browser downloaded, but Linux system dependencies are missing.\n` +
77
- ` ${dim}Run ${reset}${cyan}sudo npx playwright install-deps chromium${reset}${dim} to finish setup.${reset}\n\n`
78
- )
60
+ try {
61
+ p = await import('@clack/prompts')
62
+ pc = (await import('picocolors')).default
63
+ } catch {
64
+ // Clack or picocolors unavailable — fall back to minimal output
65
+ process.stderr.write(` Run gsd to get started.\n\n`)
66
+ await run('npx patch-package')
67
+ await run('npx playwright install chromium')
68
+ return
69
+ }
70
+
71
+ // --- Branded intro -------------------------------------------------------
72
+ p.intro('Setup')
73
+
74
+ const results = []
75
+ const s = p.spinner()
76
+
77
+ // --- Step 1: Apply patches -----------------------------------------------
78
+ s.start('Applying patches…')
79
+ const patchResult = await run('npx patch-package')
80
+ if (patchResult.ok) {
81
+ s.stop('Patches applied')
82
+ results.push({ label: 'Patches applied', ok: true })
79
83
  } else {
80
- process.stderr.write(
81
- `\n ${yellow}⚠${reset} Browser tools unavailable — run ${cyan}npx playwright install chromium${reset} to enable\n\n`
82
- )
84
+ s.stop(pc.yellow('Patches — skipped (non-fatal)'))
85
+ results.push({
86
+ label: 'Patches skipped — run ' + pc.cyan('npx patch-package') + ' manually',
87
+ ok: false,
88
+ })
83
89
  }
84
- }
90
+
91
+ // --- Step 2: Playwright browser ------------------------------------------
92
+ // Avoid --with-deps: install scripts should not block on interactive sudo
93
+ // prompts. If Linux libs are missing, suggest the explicit follow-up.
94
+ s.start('Setting up browser tools…')
95
+ const pwResult = await run('npx playwright install chromium')
96
+ if (pwResult.ok) {
97
+ s.stop('Browser tools ready')
98
+ results.push({ label: 'Browser tools ready', ok: true })
99
+ } else {
100
+ const output = `${pwResult.stdout ?? ''}${pwResult.stderr ?? ''}`
101
+ if (os.platform() === 'linux' && output.includes('Host system is missing dependencies to run browsers.')) {
102
+ s.stop(pc.yellow('Browser downloaded, missing Linux deps'))
103
+ results.push({
104
+ label: 'Run ' + pc.cyan('sudo npx playwright install-deps chromium') + ' to finish setup',
105
+ ok: false,
106
+ })
107
+ } else {
108
+ s.stop(pc.yellow('Browser tools — skipped (non-fatal)'))
109
+ results.push({
110
+ label: 'Browser tools unavailable — run ' + pc.cyan('npx playwright install chromium'),
111
+ ok: false,
112
+ })
113
+ }
114
+ }
115
+
116
+ // --- Summary note --------------------------------------------------------
117
+ const lines = results.map(
118
+ (r) => (r.ok ? pc.green('✓') : pc.yellow('⚠')) + ' ' + r.label
119
+ )
120
+ lines.push('')
121
+ lines.push('Run ' + pc.cyan('gsd') + ' to get started.')
122
+
123
+ p.note(lines.join('\n'), 'Installed')
124
+
125
+ // --- Outro ---------------------------------------------------------------
126
+ p.outro(pc.green('Done!'))
127
+ })()
@@ -48,6 +48,7 @@ import { createConnection } from "node:net";
48
48
  import { randomUUID } from "node:crypto";
49
49
  import { writeFileSync, readFileSync, existsSync, mkdirSync } from "node:fs";
50
50
  import { join } from "node:path";
51
+ import { shortcutDesc } from "../shared/terminal.js";
51
52
  import { createRequire } from "node:module";
52
53
 
53
54
  // ── Windows VT Input Restoration ────────────────────────────────────────────
@@ -2356,7 +2357,7 @@ export default function (pi: ExtensionAPI) {
2356
2357
  // ── Ctrl+Alt+B shortcut ──────────────────────────────────────────────
2357
2358
 
2358
2359
  pi.registerShortcut(Key.ctrlAlt("b"), {
2359
- description: "Open background process manager",
2360
+ description: shortcutDesc("Open background process manager", "/bg"),
2360
2361
  handler: async (ctx) => {
2361
2362
  latestCtx = ctx;
2362
2363
  await ctx.ui.custom<void>(
@@ -47,6 +47,7 @@ import {
47
47
  import { Key } from "@mariozechner/pi-tui";
48
48
  import { join } from "node:path";
49
49
  import { existsSync } from "node:fs";
50
+ import { shortcutDesc } from "../shared/terminal.js";
50
51
  import { Text } from "@mariozechner/pi-tui";
51
52
 
52
53
  // ── ASCII logo ────────────────────────────────────────────────────────────
@@ -184,10 +185,8 @@ export default function (pi: ExtensionAPI) {
184
185
  });
185
186
 
186
187
  // ── Ctrl+Alt+G shortcut — GSD dashboard overlay ────────────────────────
187
- // Requires Kitty keyboard protocol or modifyOtherKeys support.
188
- // Terminals without support (macOS Terminal.app, JetBrains): use /gsd status instead.
189
188
  pi.registerShortcut(Key.ctrlAlt("g"), {
190
- description: "Open GSD dashboard (or use /gsd status)",
189
+ description: shortcutDesc("Open GSD dashboard", "/gsd status"),
191
190
  handler: async (ctx) => {
192
191
  // Only show if .gsd/ exists
193
192
  if (!existsSync(join(process.cwd(), ".gsd"))) {
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Terminal capability detection for keyboard shortcut support.
3
+ *
4
+ * Ctrl+Alt shortcuts require the Kitty keyboard protocol or modifyOtherKeys.
5
+ * Terminals that lack this support silently swallow the key combos.
6
+ */
7
+
8
+ const UNSUPPORTED_TERMS = ["apple_terminal"];
9
+
10
+ export function supportsCtrlAltShortcuts(): boolean {
11
+ const term = (process.env.TERM_PROGRAM || "").toLowerCase();
12
+ const jetbrains = (process.env.TERMINAL_EMULATOR || "").toLowerCase().includes("jetbrains");
13
+ return !UNSUPPORTED_TERMS.some((t) => term.includes(t)) && !jetbrains;
14
+ }
15
+
16
+ /**
17
+ * Returns a shortcut description that includes a slash-command fallback hint
18
+ * when the current terminal likely can't fire Ctrl+Alt combos.
19
+ */
20
+ export function shortcutDesc(base: string, fallbackCmd: string): string {
21
+ if (supportsCtrlAltShortcuts()) return base;
22
+ return `${base} — shortcut may not work in this terminal, use ${fallbackCmd}`;
23
+ }
@@ -1,4 +1,5 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
2
+ import { shortcutDesc } from "../shared/terminal.js";
2
3
  import type { AssistantMessage } from "@mariozechner/pi-ai";
3
4
  import { isKeyRelease, Key, matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
4
5
  import { spawn, execSync, type ChildProcess } from "node:child_process";
@@ -131,7 +132,7 @@ export default function (pi: ExtensionAPI) {
131
132
  });
132
133
 
133
134
  pi.registerShortcut("ctrl+alt+v", {
134
- description: "Toggle voice mode",
135
+ description: shortcutDesc("Toggle voice mode", "/voice"),
135
136
  handler: async (ctx) => toggleVoice(ctx),
136
137
  });
137
138