* Initial plan
* chore: establish baseline before implementing em-dash fix
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
* fix: validate milestone titles against delimiter characters (em dash, slash) that break state management
- Changed STATE.md separator from em dash to colon in buildStateMarkdown and state.md template
- Removed ambiguous '— Context' suffix from context.md H1 template
- Added validateTitle() function to detect problematic delimiter characters
- Added delimiter_in_title doctor issue code for milestone/slice title validation
- Added tests for validateTitle() and doctor delimiter detection
- Added em-dash-in-title cases to regex-hardening test
Fixes: milestone titles containing '—' caused state corruption when the LLM
misread the ambiguous STATE.md separator format and wrote incorrect planning files.
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
Add Ollama Cloud (ollama.com) as a built-in provider with both model
hosting and web search/fetch capabilities.
Model provider:
- 13 curated models via OpenAI-compatible API (Llama 3.1, Qwen 3,
DeepSeek R1, Gemma 3, Mistral, Phi-4, GPT-OSS)
- Auth via OLLAMA_API_KEY environment variable
- Registered in onboarding, env hydration, and model resolver
Web tool provider:
- Search via POST ollama.com/api/web_search
- Page fetch via POST ollama.com/api/web_fetch (fallback after Jina)
- Added as third search provider option alongside Tavily and Brave
- /search-provider command updated with ollama option
Closes#430
auto.ts has selfHealRuntimeRecords() which cleans up stale .gsd/runtime/units/
records when /gsd auto starts. However, guided-flow.ts (used by /gsd manual
mode) had zero awareness of runtime records — it only checked auto.lock.
This means if auto-mode crashes mid-unit, the stale runtime records persist
until the next /gsd auto run. Users who alternate between manual and auto
mode, or who only use manual mode after a crash, would accumulate stale
records that could cause spurious re-dispatch or confusing state.
Add selfHealRuntimeRecords() to guided-flow.ts that:
- Clears records where the expected artifact already exists (completed but
closeout didn't finish)
- Clears records stuck in dispatched or timeout phase (process died mid-unit)
- Notifies the user how many stale records were cleaned
Called in showSmartEntry() before the crash lock check so the wizard always
starts from a clean state regardless of how the previous session ended.
Co-authored-by: Thomas <twilliams1234@gmail.com>
When a server fails to bind to the configured ready_port, the process
would stay in "starting" status indefinitely after the probing interval
cleared, with no error surfaced to the agent. This fixes the hang by:
- Transitioning process to "error" status when port probing times out
- Detecting process exit during port polling and reporting stderr context
- Adding ready_timeout parameter for custom timeout values
- Including stderr output in waitForReady timeout/error responses
- Registering SIGTERM/SIGINT handlers to clean up bg processes on exit
Closes#428
The directory listing cache in paths.ts has no TTL and was never cleared
in production, causing dispatchNextUnit to re-dispatch the same unit
when files written by the previous unit weren't visible to deriveState.
Add clearPathCache() calls at the top of dispatchNextUnit (before
deriveState) and verifyExpectedArtifact so each dispatch cycle and
artifact check sees fresh disk state.
Closes#431
Add loadTemplate() and inlineTemplate() to prompt-loader.ts, then use
them in all 7 auto.ts builder functions and ~9 guided-flow.ts callsites
to inject template content at prompt-build time. Update 16 prompt .md
files to reference inlined templates instead of instructing agents to
read them from disk.
Over a typical 3-slice/15-task milestone run, this eliminates ~44
unnecessary READ tool calls (~45-90s latency, ~5-9k wasted tokens).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ensures auto-mode reads fresh file data after unit completion,
slice merges, and self-healing — prevents stale cached parses
from the memoized deriveState pipeline.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The --version flag outputs a banner with ANSI escape codes. The smoke
test compared the entire multi-line output against the bare version
string, causing false failures on every release.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements post-unit hooks, pre-dispatch hooks, state persistence, and
a /gsd hooks status command — all configured via preferences.md without
code changes. Enables code review loops, simplify passes, convention
enforcement, and custom unit interception as opt-in extensions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When all credentials for a provider are exhausted, the system now
automatically falls back to the next available provider in a
user-configured fallback chain. Higher-priority providers are
restored automatically when their backoff expires.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
deriveState() was called ~7 times per dispatch cycle, each call re-reading
the entire .gsd/milestones/ tree from disk (~50-60 file reads per call,
~350-420 redundant reads per cycle). Add a 100ms TTL cache keyed by
basePath so repeated calls within the same dispatch cycle return the
cached result. Expose invalidateStateCache() and call it at every
mutation boundary in auto.ts: handleAgentEnd start, post-merge
re-derivations, and resume-from-pause.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests that write files and immediately call deriveState() got stale results
because the path resolution cache (dirEntryCache/dirListCache) returned
cached directory listings that didn't include newly written files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds a "Custom (OpenAI-compatible)" provider option to the API key
flow in the onboarding wizard. When selected, prompts for base URL,
API key, and model ID, then writes the config to models.json.
Add explicit provider targeting for model preferences when the same
model ID exists across multiple providers (e.g., claude-sonnet-4-6
on both Anthropic and Bedrock).
Two formats supported:
- String: "bedrock/claude-sonnet-4-6"
- Object: { model: claude-sonnet-4-6, provider: bedrock }
The provider/model string format already worked in the resolution
code but was undocumented. This adds the provider field to the
object format and documents both approaches.
OpenRouter models use slash-separated IDs (e.g. "moonshotai/kimi-k2.5") where the
full string is the model ID on the "openrouter" provider. The auto-mode model
switcher incorrectly split on the first slash and treated the prefix as a provider
name, causing all OpenRouter preference models to fail resolution and fall back to
the default model for every phase.
Now the resolver first checks whether the slash-prefix is a known provider, and if
not (or if no match is found), falls back to matching the full string as a model ID
— consistent with model-resolver.ts.
Also improves the progress widget and notifications to show [PHASE] and
provider/model so users can confirm the correct model is active.
Closes#402
Systems with a buggy git-svn Perl module (notably Arch Linux) emit
"Duplicate specification" warnings on every git invocation. Filter
these from error messages and suppress git-svn loading via GIT_SVN_ID.
Also update repository URLs from stale glittercowboy/gsd-pi to
gsd-build/gsd-2.
loader.ts previously maintained a hardcoded list of bundled extension paths
for GSD_BUNDLED_EXTENSION_PATHS. This required manual updates whenever
extensions were added or removed, and created a consistency gap with
buildResourceLoader() which already discovers extensions dynamically.
Replace with runtime directory scanning that mirrors the discovery rules
in resource-loader.ts:
- Top-level .ts/.js files → extension entry point
- Directories with index.ts or index.js → extension entry point
- Directories without either (shared/, remote-questions/) → skipped
Benefits:
- Adding a new extension no longer requires editing loader.ts
- GSD_BUNDLED_EXTENSION_PATHS stays in sync with what buildResourceLoader()
loads in the main process — subagents now receive the same extensions
- Fixes: 5 extensions (google-search, mcporter, ttsr, universal-config,
voice) were loaded in the main process but missing from
GSD_BUNDLED_EXTENSION_PATHS, meaning subagents did not receive them
- Eliminates a common source of merge conflicts for contributors and forks
that add custom extensions
* fix: include export-html templates in pkg/ shim for --export support (#370)
The --export command fails with ENOENT because getExportTemplateDir()
resolves to pkg/dist/core/export-html/ which doesn't exist. The build
script copies themes into pkg/dist/ but had no equivalent step for
export-html templates.
- Add copy-export-html build script mirroring copy-themes pattern
- Chain copy-export-html into the build script
- Fix .gitignore: re-negate pkg/dist/ after the catch-all dist/ rule
- Add vendor/ exception for pkg/dist/core/export-html/vendor/
- Commit template.html, template.css, template.js, and vendor/ files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: tighten .gitignore negation to specific pkg/dist subdirs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevent duplicate slice/dependency summaries from being inlined into
prompts when the same ID appears more than once. Uses a Set to track
already-included IDs in inlineDependencySummaries and
buildCompleteMilestonePrompt.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>