Commit graph

79 commits

Author SHA1 Message Date
Lex Christopherson
b10b78cb75 fix: guard formatCost against non-number cost values (#74)
Handle both plain number and { total: number } shapes for msg.usage.cost
in snapshotUnitMetrics, and coerce formatCost input to prevent crashes
when cost is null/undefined/NaN from corrupted ledger data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 18:28:25 -06:00
dan bachelder
dfebda73af fix: avoid sudo prompts in postinstall (#73)
Co-authored-by: Ada <ada@clawdbot>
2026-03-11 18:19:33 -06:00
jonathancostin
b1e769b4d9 feat: hide footer during auto-mode, show all stats in progress widget (#75)
During auto-mode, the built-in footer is hidden entirely via setFooter()
and all its info is moved into the progress widget:

- pwd + git branch shown inside the widget
- Token stats (↑/↓/R/W) from current unit session
- Cumulative cost from metrics ledger (survives across unit resets)
- Context window usage with color coding (warning >70%, error >90%)
- Model name right-aligned
- Footer restored to built-in on pause or stop
- No model duplication (removed from hints)
2026-03-11 18:18:08 -06:00
jonathancostin
bf6fefa16e fix: register dynamic-cwd write/read/edit tools for worktree support (#72)
The built-in write, read, and edit tools capture process.cwd() once at
startup. When /worktree switch calls process.chdir() into a worktree,
these tools still resolve relative paths against the original launch
directory. This caused GSD auto-mode to write .gsd/ artifacts to the
main project instead of the worktree.

The bash tool was already patched with a spawnHook for dynamic CWD.
Apply the same pattern to write, read, and edit: each execute() call
creates a fresh tool instance with the current process.cwd(), so
relative paths always resolve against the active working directory.
2026-03-11 17:44:57 -06:00
jonathancostin
2b9451dfd4 fix: general merge guard prevents infinite loop when complete-slice is bypassed (#71)
Replace the narrow 'if currentUnit === complete-slice' merge check with a
general merge guard that detects any completed slice branch and merges it
to main before dispatching the next unit.

The old check only triggered merges after the complete-slice unit type.
When the LLM or the doctor post-hook completed slice bookkeeping during
task execution, complete-slice was skipped entirely, leaving the slice
branch unmerged. On milestone transition, the next slice branch (forked
from main) couldn't see the prior milestone's summary, causing deriveState
to oscillate between milestones in an infinite loop.

The new guard checks: are we on a gsd/MID/SID branch where the roadmap
entry is [x]? If so, merge to main and re-derive state before dispatching.
2026-03-11 17:44:55 -06:00
Lex Christopherson
1872e8db78 fix: prevent auto-mode model switches from persisting as global default (#30) and harden resume resilience (#16)
Patch SDK setModel() to accept { persist: false } so per-unit model
switching in auto-mode no longer overwrites the user's global default.
Add state rebuild + doctor on resume, guard logging for silent dispatch
failures, and active-state check before prompt injection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 17:39:46 -06:00
Lex Christopherson
f4c46516a6 fix: harden remote questions — validate IDs before test-send, remove dead code
- Validate channel IDs via isValidChannelId() before URL interpolation
  in setup wizard, preventing SSRF during test-send
- Add 15s fetch timeout to setup API calls (fetchJson, Discord test-send)
- Sanitize Discord error responses before surfacing to user
- Remove dead send.ts + channels.ts (unused parallel implementation)
- Add poll retry tolerance in manager.ts (1 transient error before fail)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 17:37:24 -06:00
TÂCHES
28a30c2cc3 Merge pull request #42 from FacuVCanale/feat/remote-user-questions
Merging — minor cleanup items will be handled in a follow-up commit. Thanks @FacuVCanale for the solid work and patience through the review rounds.
2026-03-11 17:30:23 -06:00
Lex Christopherson
a21022a3ef docs: update README for current state, remove github extension
- Remove github extension (3 files) and its test
- Fix GitHub badge/link URLs (glittercowboy/gsd-pi → gsd-build/GSD-2)
- Update /gsd description: wizard → step mode (default since /gsd next)
- Add missing commands: /gsd next, /worktree, /voice, /exit, /clear, keybindings
- Update bundled extensions: 9 → 13 (add Google Search, Mac Tools, MCPorter, Voice)
- Add Google Gemini API key to first launch, requirements, architecture tree
2026-03-11 17:10:51 -06:00
Lex Christopherson
5eb02e9a1c fix: auto-commit before branch switch and migrate legacy flat sessions
ensureSliceBranch() now auto-commits dirty files before git checkout,
preventing "would be overwritten" errors when doctor/STATE.md rebuild
leaves uncommitted changes between slice dispatches. (closes #63)

On startup, migrate any .jsonl session files from the flat
~/.gsd/sessions/ directory into the per-cwd subdirectory so /resume
can find sessions created before per-directory scoping was added.
(closes #64)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 17:07:54 -06:00
Lex Christopherson
8a4572edef fix(voice): preserve transcription across pauses
Apple's on-device speech recognition resets bestTranscription after
silence gaps, discarding previous text. The Swift recognizer now
detects these resets (word count drop / different starting word) and
accumulates finalized segments so speech continues appending instead
of overwriting. TS side simplified to pass through the already-
accumulated text from the Swift process.
2026-03-11 16:59:03 -06:00
Facu_Viñas
f5c7dd1664 merge: resolve conflict with upstream/main — combine next + remote subcommands
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 19:49:58 -03:00
Vedant
bc0049e51d feat: add google-search extension powered by Gemini Search grounding (#66)
Provides a `google_search` tool as an alternative to Brave-based web
search for users with Google Cloud / Gemini API credits. Sends queries
to Gemini 3 Flash with `googleSearch: {}` grounding enabled, returning
an AI-synthesized answer with source URLs from grounding metadata.

Features:
- In-session caching (keyed by normalized query)
- Defensive truncation via truncateHead
- Classified error handling (auth, rate-limit, general)
- Custom TUI rendering for call/result display
- Session start warning if GEMINI_API_KEY is missing
2026-03-11 16:48:17 -06:00
Facu_Viñas
9b6d04ba28 Merge remote-tracking branch 'origin/main' into feat/remote-user-questions 2026-03-11 19:47:54 -03:00
Lex Christopherson
0d35e86549 feat(M001/S04): npm publish and install smoke test 2026-03-11 16:28:01 -06:00
Lex Christopherson
8d04971ac1 feat: add /voice extension for real-time speech-to-text
- macOS-only (SFSpeechRecognizer), no-op on other platforms
- /voice command and Ctrl+Alt+V shortcut to toggle
- Streams partial transcription results directly into editor input
- Custom footer with flashing red dot + 'transcribing' indicator on row 1
- Enter to stop and keep text, Esc to cancel
- Ships precompiled Swift binary (60KB)
2026-03-11 16:13:49 -06:00
Lex Christopherson
0c9fb1d1da feat: add mcporter extension for lazy MCP server integration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 16:12:12 -06:00
Lex Christopherson
b4ccbadd09 feat(gsd): add post-hook bookkeeping after each auto-mode unit
Run doctor (fix mode) and rebuild STATE.md after each unit completes
in handleAgentEnd. Catches missed checkboxes and stub summaries the
LLM may have skipped, and keeps STATE.md in sync with disk state.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 16:12:08 -06:00
Lex Christopherson
5bb3229a85 feat: add /gsd next (step mode), make bare /gsd default to step mode, delete /gsd-run
- /gsd next: same state machine as /gsd auto but pauses between units
  with a wizard showing what completed and what's next
- /gsd (bare): now defaults to step mode instead of the old guided flow
- /gsd auto: unchanged — continuous execution without pausing
- Deleted /gsd-run slash command (redundant with /gsd auto)
- Step mode preserves through discuss → auto-start transition
- User can switch from step → auto mid-session via wizard option
- Progress widget shows NEXT/AUTO based on current mode
2026-03-11 16:11:16 -06:00
TÂCHES
8bd27f74e0 fix: idle watchdog false-fires on active agents (#52) (#65)
The idle watchdog checked lastProgressAt to detect stalled agents, but
nothing updated that timestamp during normal execution. Any task taking
>10min triggered false idle recovery, steering messages, and eventually
got skipped — even while actively writing code.

Add detectWorkingTreeActivity() check before recovery: if git reports
uncommitted changes, the agent is working. Bump lastProgressAt and
skip recovery. Genuinely idle agents (clean working tree) still get
recovered as before.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 16:08:46 -06:00
jonathancostin
85f60451fb feat: improve worktree merge, create, remove, and reload resilience (#61)
Merge improvements:
- Auto-detect current worktree: /worktree merge (bare) and /worktree merge main
  work from inside a worktree without specifying the worktree name
- Full repo diffs: preview and LLM prompt show all changed files, not just .gsd/
- Accurate preview: direct diff (main vs branch) shows actual merge impact
- Per-file line stats: +N/-N shown for each file in merge preview
- CWD fix: chdir to main tree before dispatching merge to prevent broken CWD
  after worktree cleanup
- Prompt includes explicit paths so the LLM knows where to read/write

Create/switch:
- /worktree create <name> works as alias for create-or-switch behavior
- Guard against creating a worktree when the branch is already in use

Remove:
- /worktree remove <name> validates the name exists before attempting removal
- /worktree remove <name> confirms before deleting
- /worktree remove all removes every worktree after confirmation prompt

Reload resilience:
- Detects if CWD is inside a worktree on extension init and restores
  originalCwd tracking, surviving /reload without losing worktree state

Command descriptions:
- /worktree shows '(also /wt)' in description
- /wt shows 'Alias for /worktree'
2026-03-11 15:46:34 -06:00
jonathancostin
09c5aa33ee feat: add /exit command to kill GSD process immediately (#60)
Registers /exit as a slash command that calls process.exit(0).
Quick way to quit without typing /quit.
2026-03-11 15:45:50 -06:00
jonathancostin
3b9a8c1c63 feat: add /clear as alias for /new slash command (#59)
Registers /clear as a command that calls ctx.newSession(), identical to /new.
Shows in autocomplete when typing /cl with 'Alias for /new' description.
2026-03-11 15:45:34 -06:00
Facu_Viñas
090554373c refactor: use discriminated union for remote vs local result details
Replace the inline union cast in renderResult with a proper
discriminated union (LocalResultDetails | RemoteResultDetails)
keyed on the `remote` field. Improves type safety and makes
the rendering logic self-documenting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 18:03:31 -03:00
Facu_Viñas
cd813ddded fix(reliability): distinguish Discord 404 from auth errors in reactions
The catch-all in checkReactions() silently swallowed auth failures
(401/403), making them indistinguishable from "no reaction yet". Now:
- 404: expected (no reactions for this emoji), continue
- 401/403: re-thrown so the poll loop surfaces the auth failure
- Other errors: best-effort skip (rate limits, network)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 18:02:20 -03:00
Facu_Viñas
003cb44007 fix(security): sanitize error messages to prevent token leakage
Error messages from adapter auth/send failures may contain token
fragments. Added sanitizeError() that strips Slack token patterns
(xoxb-, xoxp-, xoxa-) and long opaque secrets (20+ alphanumeric
chars). Also truncates verbose Discord API error responses to 200
chars. Applied to all error paths in manager.ts and discord-adapter.ts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 18:01:42 -03:00
Facu_Viñas
492daaf709 fix(reliability): add 15s per-request fetch timeout to adapters
Individual HTTP calls to Slack/Discord APIs could hang indefinitely
if the network stalls. The overall poll deadline only bounds the loop,
not each request. Now each fetch() gets AbortSignal.timeout(15_000).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 18:00:19 -03:00
Facu_Viñas
c67151bef3 fix(security): cap user_note at 500 chars to prevent LLM context DoS
Arbitrary-length free-text replies from remote channels were passed
directly into the LLM context. Now truncated to 500 chars with
trailing ellipsis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 17:59:32 -03:00
Facu_Viñas
41f362841e fix(security): validate channel ID format to prevent SSRF
Slack IDs must match ^[A-Z0-9]{9,12}$, Discord snowflakes must match
^\d{17,20}$. resolveRemoteConfig() and getRemoteConfigStatus() now
reject malformed IDs before they reach any URL interpolation.

Also fixes pre-existing false-positive in config tests (env overrides
couldn't affect module-level homedir() cache) — replaced with direct
isValidChannelId() unit tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 17:58:47 -03:00
Lex Christopherson
7b28162ade docs: rewrite discuss prompt with reflection step, questioning philosophy, depth enforcement, and visible previews 2026-03-11 14:46:05 -06:00
Lex Christopherson
d1d0b1acab fix: constrain browser screenshots to 1568px max dimension (#56)
The Anthropic API rejects images exceeding 2000px in multi-image requests.
With deviceScaleFactor=2, viewport screenshots were 2560x1600px, triggering
400 errors that halted execution. Add scale:"css" to all API-facing screenshot
calls and a constrainScreenshot() fallback that downscales oversized images
(e.g. fullPage on tall pages) via the browser's canvas — zero new dependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 14:46:05 -06:00
Gary Trakhman
0a955c0b98 fix: support pi extensions from ~/.pi/agent/extensions/ (#51)
Update buildResourceLoader to include ~/.pi/agent/extensions/ in
additionalExtensionPaths, allowing GSD to discover and use extensions
installed in pi's default location.

This resolves extension loading issues when users have extensions
installed in ~/.pi/agent/extensions/ instead of ~/.gsd/agent/extensions/.

- resource-loader.ts: add piExtensionsDir to additionalExtensionPaths
- app-smoke.test.ts: add test verifying the source includes .pi path
2026-03-11 13:09:30 -06:00
Facu_Viñas
9b80c221ce fix: isolate remote-questions config test for Windows compatibility
resolveRemoteConfig test used process.env.HOME which is undefined on
Windows (Node uses USERPROFILE). Use a temp directory with both HOME
and USERPROFILE set, and clean up in a finally block.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 15:46:44 -03:00
Facu_Viñas
8a00605e51 fix: sort prompt store by updatedAt instead of filename
getLatestPromptSummary() sorted JSON filenames alphabetically to find
the most recent prompt. Since filenames are UUIDs (random, not temporal),
this returned arbitrary results. Now reads updatedAt from each record
and picks the highest.

Also fixes test isolation on Windows (USERPROFILE) and adds a regression
test that fails with the old alphabetical sort.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 15:46:01 -03:00
Lex Christopherson
a37ef56146 feat: harden remote questions flow 2026-03-11 15:46:01 -03:00
Lex Christopherson
58ca04e7de fix: restore Windows VT input after child processes exit (#41)
Child processes (Git Bash/MSYS2) strip the ENABLE_VIRTUAL_TERMINAL_INPUT
flag from the shared stdin console handle, corrupting terminal input.
Re-enable the flag after every child process exits in bash.js, bg-shell,
and cache FFI handles in pi-tui for cheap repeated calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 11:24:44 -06:00
Lex Christopherson
d4a46beef7 fix: support print/JSON mode in cli.js so subagents don't hang
cli.ts unconditionally entered InteractiveMode, ignoring --mode, -p,
--no-session and other flags the subagent extension passes to child
processes. The child would wait for TTY input that never arrives
(stdin is "ignore"), causing the parent to hang forever on "working".

Parse CLI args to detect print/subagent mode and route to runPrintMode()
with proper session, model, extension, and system prompt handling.

Closes #45

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 11:21:12 -06:00
Lex Christopherson
97f27f17ce fix: prevent discuss prompt loop and refresh star history link 2026-03-11 10:57:19 -06:00
TÂCHES
0d251d9707 fix: bootstrap managed tools and gh auth
Preserve the original #39 fix while adding the missing hardening and regression coverage. Credit to @LuxVTZ for the original fix incorporated here.
2026-03-11 10:52:45 -06:00
vp275
f307923db2 fix: scope session list to current working directory
Sessions are stored in a single flat ~/.gsd/sessions/ directory, so
/resume shows sessions from all projects regardless of which folder
you're in.

Use per-cwd subdirectories under ~/.gsd/sessions/ with the same
path encoding the upstream SDK uses (--path-segments--), so /resume
only lists sessions from the current working directory.
2026-03-11 21:39:02 +05:30
Facundo Viñas Canale
dc7c44f047 Merge pull request #5 from FacuVCanale/fix/40-bash-hang-windows
fix: bash/bg_shell hang and process kill failures on Windows (#40)
2026-03-11 12:16:14 -03:00
Facu_Viñas
0643d63480 fix: desugar TypeScript parameter properties for strip-types compat
Node's --experimental-strip-types doesn't support parameter properties
(private readonly in constructor params). Convert to explicit field
declarations + constructor assignments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 12:02:23 -03:00
Facu_Viñas
c39388b2e3 fix: use Editor from pi-tui for remote command input prompts
The custom input handlers called tui.invalidate() which caused infinite
recursion. Rewrite promptMaskedInput and promptInput to use the Editor
component (same pattern as get-secrets-from-user.ts) with proper
tui.requestRender() and cache invalidation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 12:02:23 -03:00
Facu_Viñas
c9cb8dd1eb fix: rename remote-questions/index.ts to send.ts to avoid extension loader
Pi's DefaultResourceLoader auto-discovers extensions/*/index.ts and
expects a default export factory. remote-questions is an internal
library (consumed via dynamic import), not an extension — having an
index.ts caused the loader to try loading it as one and fail.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 12:02:23 -03:00
Facu_Viñas
45fdf5d54d feat: remote user questions via Slack/Discord for headless auto-mode
When ask_user_questions is called in non-interactive mode (ctx.hasUI = false),
transparently route questions to a configured Slack or Discord channel and poll
for the user's response. Same tool interface, automatic routing.

- Add adapter pattern for Slack (Bot Token API) and Discord (HTTP API)
- Add /gsd remote command for interactive setup wizard
- Add SLACK_BOT_TOKEN / DISCORD_BOT_TOKEN to wizard and env hydration
- Add remote_questions config to GSD preferences with merge support
- Fix parseScalar to preserve large numeric IDs (Discord channel IDs)
- Show remote channel status on session_start

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 12:02:23 -03:00
Facu_Viñas
18d1418597 fix: bash/bg_shell hang and kill issues on Windows (#40)
- bg_shell killProcess: add Windows-specific taskkill /F /T /PID path
  with proper error handling (spawnSync with timeout, not stdio: "ignore")
- bg_shell startProcess: use getShellConfig() instead of hardcoded "bash",
  disable detached mode on Windows (process groups don't apply)
- GSD bash tool: wrap execute to inject 120s default timeout when the LLM
  omits the timeout parameter, preventing indefinite hangs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 11:56:48 -03:00
Marcel Reschke
8e1ddd51f2 fix: /gsd-run uses hardcoded ~/.pi/ path instead of GSD_WORKFLOW_PATH (#38)
* feat: /gsd migrate — .planning to .gsd migration tool

Add `/gsd migrate [path]` command that reads old get-shit-done .planning
directories and writes complete .gsd directory trees for GSD-2.

Pipeline: validate → parse → transform → preview → confirm → write → review

Parser (S01):
- 7 per-file parsers: roadmap, plan, summary, requirements, project, state, config
- Handles flat, milestone-sectioned, and <details>-block roadmap formats
- Bold phase entries, "Phase N:" format, decimal numbering, duplicate phase numbers
- Bullet-format requirements (- [x] **ID**: Description)
- Graceful null returns for missing files, severity-classified validation

Transformer (S02):
- Phases → slices, plans → tasks, milestones → milestones
- Float-sorted decimal phases renumbered sequentially (S01, S02, ...)
- Completion state preserved (roadmap [x] → slice done, summary → task done)
- Research consolidated with fixed file-type ordering
- Requirements classified with complete/done/shipped → validated normalization
- Vision derived from PROJECT.md with three-level fallback
- Duplicate phase numbers disambiguated by title similarity

Writer (S03):
- Format functions for all GSD-2 file types with round-trip verification
- writeGSDDirectory produces tree that deriveState() reads correctly
- generatePreview computes milestone/slice/task counts + completion %
- Null research and empty requirements silently skipped

Command (S04):
- Default to cwd when no args given; ~/path expansion
- Validation gating (fatal issues block pipeline)
- Preview with showNextAction confirmation
- Post-write agent review via prompts/review-migration.md template
- Wired into commands.ts with tab completion

Also:
- .gitignore: replace granular .gsd/* entries with .gsd/ catch-all
- README: add /gsd migrate to commands table + "Migrating from v1" section
- files.ts: widen parseRequirementCounts regex for non-R prefixed IDs

478 assertions across 6 test suites, all passing.
UAT against blade/bladeai (28 phases, 8 milestones) and aire (10 phases, 2 milestones).

* fix: persist skipped API keys so wizard doesn't repeat on every launch

When users skip optional API keys (Brave, Context7, Jina) by pressing
Enter, the wizard stores nothing. On next launch, authStorage.has()
returns false for those providers, so the wizard prompts again.

Fix: store an empty-key sentinel for skipped providers. Also guard
loadStoredEnvKeys against injecting empty strings into process.env.

* fix: respect GSD_WORKFLOW_PATH in /gsd-run command

---------

Co-authored-by: Jonathan Costin <jonathan.costin@outlook.com>
Co-authored-by: vp275 <vedantp42@gmail.com>
2026-03-11 08:11:54 -06:00
amanape
1a05266326 Rename 'Get Stuff Done' to 'Get Shit Done' 2026-03-11 08:11:11 -06:00
Lex Christopherson
a32d6fb7b5 fix: handle Windows backspace in masked input + support custom browser path (#36, #34)
- wizard.ts: also check for \b (0x08) which Windows terminals send for backspace
- browser-tools: read BROWSER_PATH env var and pass as executablePath to Playwright

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 07:47:37 -06:00
jonathancostin
6b2f8b0a05 feat: /worktree (/wt) — git worktree lifecycle for GSD (#31) 2026-03-11 06:59:02 -06:00