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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
Non-execute-task and execute-task idle recovery now:
- Checks if the artifact already exists before steering (early advance)
- Escalates steering on final attempt ("last chance before skip")
- Writes blocker placeholder artifacts and advances the pipeline when
retries are exhausted, instead of pausing auto-mode silently
Closes#17
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Milestones completed before the roadmap convention (e.g. M001-M003)
have SUMMARY files but no ROADMAP. Previously these were treated as
incomplete, causing the first one to become the active milestone with
a fallback title like 'M001: M001'.
Now getActiveMilestoneId(), the completeMilestoneIds pre-computation,
and the deriveState() registry loop all check for a SUMMARY file when
no roadmap exists and mark the milestone as complete, extracting the
title from the summary H1.
Uses getAll() instead of getAvailable() so stale models like grok-2
are caught even when the user has no auth for the correct provider yet.
Resets thinking to off when the configured model was invalid.
- Remove gsd-pi from its own dependencies (circular dep caused ENOTEMPTY install failures)
- Auto-select anthropic/claude-sonnet-4-6 as default model for new installs
- Reset to valid model if configured model no longer exists in registry
- Default thinking level to off
When Tab opens the notes field, committed/checked options now stay
visually prominent (text color + muted description) while unselected
options dim. Previously Tab greyed everything out equally.
- optionUnselected: respect isCommitted when isFocusDimmed
- checkboxUnselected: respect isChecked for description color when isFocusDimmed
All prompt files under src/resources/extensions/gsd/prompts/ hardcoded
~/.pi/agent/extensions/gsd/templates/ but GSD overrides the agent
directory to ~/.gsd/agent/ in loader.ts. This caused all template reads
to fail silently — the agent never loaded formatting guidance.
Replaced all 26 occurrences across 16 files.
- All outward-facing GitHub tool actions now require user confirmation
via a themed yes/no dialog before executing (create, update, close,
reopen issues; create/update PRs; add comments; submit reviews;
request reviewers; create labels/milestones)
- New shared confirm-ui.ts component using the shared UI design system
- Read-only actions (list, view, search, diff, files, checks) ungated
- License changed from BUSL-1.1 to MIT
When no milestone exists, both /gsd and /gsd auto reach showSmartEntry
and call dispatchWorkflow — overwriting pendingAutoStart and restarting
the conversation mid-interview every time the user re-ran the command.
Add a pendingAutoStart check at the top of the !activeMilestone branch.
If a discuss session is already in progress, notify the user instead of
re-dispatching.
Fixes#6
When users logged in via /login but never explicitly ran /model, the agent
would throw 'No model configured' on every action. Now GSD auto-selects
a default model from available authenticated providers on startup.
Preference order: claude-sonnet-4-20250514 > any Anthropic model > first available.
Also documented /model command in README Getting Started section.
Closes#4
- Consolidate execution guidance: merge AGENTS.md hard rules, heuristics,
playbooks, and communication style into system.md as single source of truth
- Remove now-redundant AGENTS.md file
- Add ensurePreferences() to auto-create .gsd/PREFERENCES.md template during
GSD bootstrap with complete YAML frontmatter, field descriptions, and examples
- Update guided-flow to call ensurePreferences() on first .gsd/ init
- Build verified
These extensions were removed from the repo but the loader still
referenced them and the README still listed them. Removed from:
- src/loader.ts extension entry points (11 → 9)
- README bundled extensions table
- app-smoke test assertions
Brave now uses separate API keys per plan:
- BRAVE_API_KEY (Search plan) → web search, LLM context, news, etc.
- BRAVE_ANSWERS_KEY (Answers plan) → chat/completions
Updated:
- wizard: prompts for and stores both keys
- loadStoredEnvKeys: hydrates BRAVE_ANSWERS_KEY from auth.json
- smoke tests: covers BRAVE_ANSWERS_KEY hydration
- verify-s03.sh: includes BRAVE_ANSWERS_KEY in env and structural checks