Commit graph

58 commits

Author SHA1 Message Date
Mikael Hugo
00a118ea71 chore: commit current workspace state 2026-05-05 14:46:18 +02:00
Mikael Hugo
f11c877224 style: format repository with biome 2026-05-05 14:31:16 +02:00
Mikael Hugo
47c806d733 fix: version sf extension runtime sources 2026-05-04 23:27:20 +02:00
Mikael Hugo
f712c339b3 sf snapshot: pre-dispatch, uncommitted changes after 1497m inactivity 2026-05-04 01:22:39 +02:00
Mikael Hugo
5f52680285 chore: snapshot in-flight work (mcp graph refactor, native edit module, misc)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 08:31:44 +02:00
Mikael Hugo
59aaf3dcf3 chore: migrate test suite from node:test to vitest
Add vitest.config.ts with forks pool, v8 coverage, and package aliases.
Run migrate-to-vitest.mjs to replace `from "node:test"` imports with
`from 'vitest'` across 749 test files, converting mock.fn→vi.fn and
mock.timers→vi fake timers where needed.

💘 Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>
2026-05-02 04:37:33 +02:00
Mikael Hugo
69be7aeeaa feat: Added renderSkillProposal() to detect recurring patterns in triag…
- src/resources/extensions/sf/commands-todo.ts
- src/resources/extensions/sf/tests/commands-todo.test.ts

SF-Task: S03/T01
2026-04-30 19:31:40 +02:00
Mikael Hugo
b24f426f2b batch: snapshot of in-flight v2 work
This commit captures uncommitted modifications that accumulated in the
working tree across multiple in-progress workstreams. It is a snapshot
to clear the deck before sf v3 work begins; individual workstreams
should land separately on top of this.

Notable additions:
- trace-collector.ts, traces.ts, src/tests/trace-export.test.ts —
  trace export plumbing
- biome.json — Biome linter configuration
- .gitignore — exclude native/npm/**/*.node compiled binaries

The bulk of the diff is across src/resources/extensions/sf/ (301 files)
and src/resources/extensions/sf/tests/ (277 files), reflecting the
ongoing sf extension work. Specific feature commits should follow this
snapshot rather than being archaeology'd out of it.

The 76MB native/npm/linux-x64-gnu/forge_engine.node compiled binary
was left out of the commit — it's now gitignored and built locally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 12:42:31 +02:00
ace-pm
f92ee8d64c
Rename @sf-run/* → @singularity-forge/* package scope
- All 373 source files updated
- Package.json scopes in all workspace packages
- Loader workspace symlink dir updated
- RpcClient import unified from pi-coding-agent (fixes type mismatch)
- Scripts, configs, flake.nix updated
- Workspace symlinks rebuilt
2026-04-15 22:56:33 +02:00
ace-pm
9d739dfa5d Rename GSD→SF: complete rebrand from fork origin
- All gsdDir/gsdRoot/gsdHome → sfDir/sfRootDir/sfHome
- GSDWorkspace* → SFWorkspace* interfaces
- bootstrapGsdProject → bootstrapProject
- runGSDDoctor → runSFDoctor
- GsdClient → SfClient, gsd-client.ts → sf-client.ts
- .gsd/ → .sf/ in all tests, docs, docker, native, vscode
- Auto-migration: headless detects .gsd/ → renames to .sf/
- Deleted gsd-phase-state.ts backward-compat re-export
- Renamed bin/gsd-from-source → bin/sf-from-source
- Updated mintlify docs, github workflows, docker configs
2026-04-15 18:33:47 +02:00
ace-pm
6b0ac484ba refactor: update log prefixes and string values from gsd- to sf- namespace
Updates channel prefixes, log messages, comments, and configuration values
across daemon, mcp-server, and related packages to complete the rebrand from
gsd to sf-run naming.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 15:37:12 +02:00
ace-pm
b29c12d5e5 refactor(native): rename gsd_parser.rs to forge_parser.rs
Final rebrand: rename remaining Rust source file to complete the gsd → forge
transition. All parser references already use forge_parser after earlier commits.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:58:21 +02:00
ace-pm
35dc87ef53 chore: sync workspace state after rebrand
- Rebrand commits already in history (gsd → forge)
- Sync pre-existing doc, docker, and CI config updates
- All rebrand artifacts verified in place:
  * Native crates: forge-engine, forge-ast, forge-grep
  * Log prefixes: [forge] across 22+ files
  * Binary: ~/bin/sf-run
  * Workspace scopes: @sf-run/*, @singularity-forge/*
  * Nix flake: Rust toolchain ready

System ready for: nix develop && bun run build:native

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:54:20 +02:00
ace-pm
d501ca7d6d fix: clean up git state after directory restoration
- Accept deletion of gsd-phase-state.ts (renamed to forge-phase-state.ts earlier)
- Accept deletion of create-gsd-extension/ (renamed to create-forge-extension/ earlier)
- These renames were part of the rebrand and are preserved in commit history

Stabilize git state after restoration operations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:34:53 +02:00
ace-pm
e5d655bdb3 chore: checkpoint workspace changes 2026-04-15 13:38:15 +02:00
Jeremy
0c19ca88f2 feat(agents): add GSD phase guard to prevent subagent/phase conflicts
When GSD auto-mode is running a planning phase, the planner subagent
could bypass GSD's state machine and artifact system. This adds a
shared state module and conflict check to block agents that overlap
with the active GSD phase.

- Add shared/gsd-phase-state.ts for cross-extension phase coordination
- Add conflicts_with frontmatter field to agent definitions
- Block conflicting agents with clear error directing to GSD workflow
- Tag planner agent with conflicts_with for plan/research phases
- 10 new tests for phase state and conflict parsing
2026-04-12 21:56:52 -05:00
Jeremy
74fb4913b1 fix(remote-questions): cancel local TUI when remote answer wins the race
showInterviewRound now accepts an AbortSignal via opts.signal. When the
remote channel wins the race, controller.abort() closes the local TUI
modal instead of leaving an orphaned interactive prompt capturing input.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:08:33 -05:00
Tibsfox
94536a01fd fix(test): update notes loop test for notesVisible guard behavior
The fix adds a notesVisible guard that prevents re-opening notes when
they are already visible, even if empty. Update the test to expect
empty notes and direct advancement instead of re-opening.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 22:52:51 -07:00
Tibsfox
771a729eb7 fix(tui): prevent Enter key infinite loop in interview notes mode
When "None of the above" was selected and the notes field was already
visible but empty, pressing Enter re-opened the notes field endlessly
because the guard only checked !notes (empty string is falsy).

Add !notesVisible guard so auto-open only triggers on the initial
selection, not when the user is confirming from an already-open notes
field.

Closes #3450
Closes #3449

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 19:25:52 -07:00
Jeremy
ca55b5c269 fix(test): reset dedup cache between ask-user-freetext tests
The per-turn dedup cache introduced in the parent commit persists across
test cases since they all use the same question signature. Test 1 populates
the cache, causing tests 2 and 3 to get cached results instead of exercising
their intended code paths.
2026-04-04 19:59:54 -05:00
Jeremy
e0884375e6 test: add regression test for interview-ui notes loop (#3502)
Exercises the goNextOrSubmit → notes auto-open path to ensure:
- Enter after typing a note advances instead of looping
- Empty notes still trigger the auto-open
- Normal option selection is unaffected

Fixes #3502
2026-04-04 11:22:15 -05:00
Jeremy
f153745c4f fix: break infinite notes loop when selecting "None of the above"
goNextOrSubmit() unconditionally reopened the notes field whenever the
cursor sat on the "None of the above" slot, even after the user had
already typed a note and pressed Enter. This trapped users in an
endless loop where Enter always bounced back to notes mode.

Add a `!states[currentIdx].notes` guard so the auto-open only fires
when notes are still empty.

Fixes #3502
2026-04-04 11:12:17 -05:00
Tom Boucher
6ac30ae9d9 fix: add free-text input to ask-user-questions when "None of the above" is selected (#3081)
Fixes #2715

The ask-user-questions UI trapped users in a re-asking loop when the agent
needed a free-text explanation rather than a fixed choice. Two paths were
affected:

- RPC fallback (ctx.ui.select): selecting "None of the above" recorded
  the label but never prompted for a free-text explanation. Now follows up
  with ctx.ui.input() so the user can type their answer.

- Custom interview UI: selecting "None of the above" advanced to the next
  question without opening the notes editor. Now auto-focuses the notes
  field so the user can immediately type a free-text response.

Also updated the "None of the above" description from "Press TAB to add
optional notes." to "Select to type your own answer." for discoverability.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 14:39:35 -06:00
Iouri Goussev
b6e105b058 perf(test): compile unit tests with esbuild, reclassify integration tests, fix node_modules symlink (#2809)
* fix(test): wire src/resources/extensions/shared/tests/ into test:unit runner

The test:unit glob excluded src/resources/extensions/shared/tests/ entirely,
leaving format-utils.test.ts (and any future tests there) silently unfired.

- Add shared/tests/*.test.ts to the test:unit glob in package.json
- Export newestSrcMtime from ensure-workspace-builds.cjs (require.main guard
  prevents side-effects on require) so the staleness logic can be tested
- Add src/tests/ensure-workspace-builds.test.ts covering newestSrcMtime:
  non-existent dir, no .ts files, single file, max of multiple, recursion,
  node_modules skip

Closes #2808

* perf(test): compile unit tests with esbuild and fix dist-test/node_modules

Replace per-file --experimental-strip-types with a single esbuild compilation
step (scripts/compile-tests.mjs) that compiles all src/ TypeScript to dist-test/
in ~3s, then runs the pre-compiled JS. Eliminates ~1.7s Node startup overhead
per test file.

- scripts/compile-tests.mjs: esbuild compilation, asset copy, .ts→.js rewrite,
  stale file cleanup; creates dist-test/node_modules symlink so resource-loader.ts
  resolves gsdNodeModules to a real path (fixes node-modules-symlink test failure)
- scripts/dist-test-resolve.mjs: ESM loader hook for @gsd/* bare specifiers and
  .ts→.js fallback rewriting at runtime
- .gitignore: exclude dist-test/ from version control
- package.json: add test:compile script; update test:unit to compile-then-run;
  update test:integration globs to cover new integration/ subdirectories
- worker-registry.ts: unref() cleanup timer so it does not keep the Node process
  alive after tests complete

Closes #2858

* fix(test): update relative imports in tests/integration/ after directory move

When tests were moved from tests/ to tests/integration/ in the previous
commit, relative imports weren't updated. ../foo now resolves one level
too shallow.

Fix all 117 import paths across 43 test files:
- ../foo → ../../foo (source files at gsd/ level)
- ../../get-secrets-from-user.ts → ../../../ (at extensions/ level)
- ../../subagent/worker-registry.ts → ../../../ (at extensions/ level)
- ./marketplace-test-fixtures.js → ../marketplace-test-fixtures.ts
- ./test-helpers.ts → ../test-helpers.ts

typecheck:extensions now passes with zero errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(integration): set 10-minute timeout for integration test runner

build job takes ~7min on main. Without a global timeout, hanging tests
block the suite indefinitely. --test-timeout=600000 caps each test at 10min.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Revert "test(integration): set 10-minute timeout for integration test runner"

This reverts commit be77ead77d369ad8569292ae6b69ba56435f5433.

* fix(test): correct formatDuration(0) edge case and docker test root path

- formatDuration(0) now returns '0s' instead of '0ms' by guarding the
  sub-second branch with ms > 0
- docker-template.test.ts root path goes ../../.. from dist-test/src/tests/
  to reach project root instead of landing in dist-test/
- replace require() calls in skill-health.ts and visualizer-overlay.ts
  with proper ES module imports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(test): correct relative import paths in integration tests

All affected tests were one directory level off — importing from ../web/
and ../resources/ when the correct paths are ../../web/ and ../../resources/.
Tests live at src/tests/integration/, not src/tests/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(test): add esbuild to root devDeps and wire dist-test-resolve hook

P1: esbuild was only in web/package.json — compile-tests.mjs requires it
at the root node_modules path, so CI failed on clean installs.

P2: dist-test-resolve.mjs existed but was never loaded; @gsd/* imports in
compiled tests resolved to installed workspace packages instead of freshly
compiled dist-test output. Add --import to test:unit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(deps): align esbuild version with lock file (0.25.12)

^0.27.4 didn't satisfy the existing lock file entry. Use the version
already present so npm ci passes without regenerating the lock file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(test): correct all relative import depths in src/tests/integration/

Tests in src/tests/integration/ need 3 levels up (../../..) to reach
project-root dirs (web/, packages/) and 2 levels up (../..) to reach
src-level dirs (src/web/, src/cli-web-branch.ts).

Fixes:
- ../../web/lib/ → ../../../web/lib/   (Next.js app, not src/web/)
- ../../web/app/ → ../../../web/app/
- ../../packages/ → ../../../packages/
- ../cli-web-branch.ts → ../../cli-web-branch.ts
- ../web-mode.ts → ../../web-mode.ts
- ../resources/extensions/ → ../../resources/extensions/
- ci_monitor ROOT path: 2 levels up → 3 levels up
- web-responsive WEB_ROOT: 2 levels up → 3 levels up

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(test): use dot reporter for test:unit to reduce noise

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(test): switch test:unit reporter to tap

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(test): compact test reporter — silent on pass, failures + summary only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore(test): include shared/tests in test:coverage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(test): correct path depths in tests moved to integration/

Tests moved from tests/ to tests/integration/ need one extra ../
to reach the same source files. Also fix web component paths — those
files live at web/ not src/web/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(test): fix web component paths in web-session-parity-contract

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(test): use process.cwd() for project root in docker-template test

Resolving relative to __dirname breaks under test:coverage which runs
source files directly from src/tests/ — needs ../.. not ../../..
(the extra level only exists in the compiled dist-test/ output).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: retrigger CI

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 14:51:49 -06:00
mastertyko
d7755e596c test(gsd): harden suite-level stability for RTK, worktree, and git bootstrap (#2786)
* test: harden web runtime auth token and lock retry tests

Teach the packaged web runtime harness to recover the auth token from the launcher stderr when the browser-open stub log is absent. Also widen the transient session-lock retry tests so they stay stable under full-suite CPU contention.

* test: harden suite-level RTK and worktree stability

Stabilize the RTK seam tests under full-suite load by using a faster fake RTK binary on Unix and allowing the tests to raise the rewrite timeout without changing the production default. Also widen the transient session-lock retry budget and give the heavy auto-worktree milestone merge suite an explicit timeout so it can complete under CI-level contention.

* test: harden git-service repo bootstrap under suite load

Switch repo bootstrap steps in git-service.test.ts to runGit(...) where the setup only needs direct git invocations.

This removes avoidable shell wrappers from the highest-churn repo setup paths, which makes the full unit suite less prone to child-process flake under load while keeping the test behavior unchanged.
2026-03-26 20:02:41 -06:00
Iouri Goussev
de600c1db0 refactor(gsd): extract duplicated status guards and validation helpers (#2767)
* fix: rebuild stale workspace packages after git pull

ensure-workspace-builds.cjs only triggered a build when dist/index.js
was missing entirely. After `git pull` updates package sources, the old
dist/ stayed in place causing TypeScript type errors (bash_transform,
authMode, malformedArguments missing from compiled .d.ts files).

Now compares newest .ts mtime under src/ against dist/index.js mtime
and rebuilds any package whose sources are newer than its dist.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(rtk): trust explicit binaryPath without existsSync check; add options object to shared rewriteCommandWithRtk

resolveRtkBinaryPath was calling existsSync on options.binaryPath, making
it impossible to inject a non-existent test binary — tests expected the
options-object API to bypass filesystem checks.

Also brings src/resources/extensions/shared/rtk.ts rewriteCommandWithRtk
in line with the same options-object signature already in src/rtk.ts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(gsd): extract duplicated status guards and validation helpers

isClosedStatus(), isNonEmptyString(), and validateStringArray() were
each copy-pasted across 5-10 tool handler files with no shared module.
Extract them into status-guards.ts and validation.ts, replace all 26
inline status checks and 8 duplicated validation functions with imports.

Standardizes "inside a closed" -> "in a closed" in two reopen error
messages as a side effect of the normalization pass.

Closes #2727

* refactor(gsd): migrate state.ts isStatusDone to isClosedStatus; fix blank lines and import order

- state.ts had a private isStatusDone() identical to isClosedStatus() —
  replace with import from status-guards.ts
- Remove double blank lines left behind in plan-{milestone,slice,task}.ts
  and replan-slice.ts after local function extraction
- Fix import ordering in reassess-roadmap.ts (node built-ins first,
  status-guards/validation before gsd-db block)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-26 18:14:43 -06:00
Andrew
815be0a698 feat: managed RTK integration with opt-in preference and web UI toggle (#2620)
* feat: integrate managed RTK across shell workflows

* fix(rtk): unify managed fallback and live savings wiring

* fix(rtk): improve TUI status visibility

* fix(tests): make portability tests independent of pi-coding-agent dist build

The CI portability test runs don't guarantee that
packages/pi-coding-agent has been compiled. Any test that
imported files pulling in @gsd/pi-coding-agent (resource-loader,
preferences-skills, async-bash-tool, etc.) crashed with
ERR_MODULE_NOT_FOUND pointing at dist/index.js.

Two changes to dist-redirect.mjs (the Node ESM loader hook used by
all unit tests):
- Redirect the bare @gsd/pi-coding-agent specifier to the workspace
  source entrypoint (src/index.ts) so no dist/ artifact is needed.
- Extend the load() hook to transpile *.ts files under
  packages/pi-coding-agent/src/ through TypeScript's transpileModule.
  Node's --experimental-strip-types can't handle parameter properties
  and similar syntax present in that package's source; full transpilation
  avoids the ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX crash.

Also fix the dashboard.tsx responsive grid:
- xl:grid-cols-5 → xl:grid-cols-4 2xl:grid-cols-5
  (5 metric cards no longer fit at xl without overflow; test contract
  expected xl:grid-cols-4)
- Keep loading-skeletons.tsx in sync with the same breakpoints.

Add src/tests/resolve-ts-loader.test.ts to guard the loader behaviour:
- bare @gsd/pi-coding-agent redirect points to workspace source
- direct source-entry rewrite (.js → .ts)
- transpilation removes TS parameter property syntax that strip-only
  mode cannot parse

* fix(tests): redirect all workspace package imports to source in portability tests

The previous fix only redirected @gsd/pi-coding-agent to its
source entrypoint. In CI, pi-coding-agent/src itself imports
@gsd/pi-ai (and other workspace packages) which were still pointing
at dist/. Since no workspace dist is built during the portability
test run, any transitive resolution hit the same ERR_MODULE_NOT_FOUND.

Changes to dist-redirect.mjs:
- Redirect @gsd/pi-ai, @gsd/pi-ai/oauth, @gsd/pi-agent-core, and
  @gsd/pi-tui bare imports to their workspace src/ entrypoints.
- Broaden the load() transpilation condition from
  '/packages/pi-coding-agent/src/' to '/packages/*/src/' so that
  all workspace source files are run through TypeScript's
  transpileModule, handling parameter properties and other syntax
  that Node's strip-only mode rejects.

Verified by hiding all four workspace dist/ directories locally and
running the failing test set — 96/96 pass.

* fix(tests): redirect @gsd/native sub-paths; fix Windows .cmd spawnSync

Two more portability failures after the previous fix:

1. @gsd/native sub-path imports (@gsd/native/fd, @gsd/native/text, etc.)
   were not redirected — the loader only handled the bare specifier.
   Added a prefix-match redirect for @gsd/native/* → packages/native/src/<sub>/index.ts.

2. Windows RTK tests failed because createFakeRtk produces a .cmd wrapper
   on Windows, and spawnSync(binaryPath, [...]) without shell:true silently
   returns non-zero when the binary is a .cmd file.
   Added shell: /\.(cmd|bat)$/i.test(binaryPath) to the spawnSync calls in:
   - src/resources/extensions/shared/rtk.ts (rewriteCommandWithRtk)
   - src/resources/extensions/shared/rtk-session-stats.ts (readCurrentRtkGainSummary)
   - packages/pi-coding-agent/src/utils/rtk.ts (rewriteCommandForGsd)
   Production use of rtk.exe is unaffected; the shell flag is only true for
   .cmd/.bat paths.

Verified: all 93 portability tests pass with all workspace dist/ directories
removed (simulating CI portability environment).

* fix(tests): Windows portability fixes — HOME env, managed RTK path, perf threshold

Four Windows-specific failures fixed:

1. app-smoke.test.ts: process.env.HOME is undefined on Windows (uses
   USERPROFILE instead). Changed to homedir() from node:os which works
   cross-platform.

2. Managed RTK path tests on Windows: tests placed a fake RTK as rtk.exe
   (by copying a .cmd script into a .exe filename), which Windows cannot
   execute. Two-part fix:
   - resolveRtkBinaryPath() in both rtk.ts files now falls back to rtk.cmd
     in the managed dir on Windows when rtk.exe is absent.
   - withManagedFakeRtk and equivalent patterns in rtk.test.ts,
     rtk-session-stats.test.ts, rtk-execution-seams.test.ts changed to
     place the fake at rtk.cmd instead of rtk.exe on Windows.

3. bg_shell RTK test on Windows: requires bash (for shell sessions), which
   is not available on the blacksmith-4vcpu-windows-2025 runner without
   Git Bash installed. Test now skips on win32.

4. derive-state-db perf assertion: 10ms threshold was too tight for Windows
   CI runners (measured 12ms under load). Raised to 25ms — still catches
   real regressions (baseline is 3ms locally and ~12ms on stressed runners).

* fix(tests): fix managed RTK path fallback on Windows in src/rtk.ts + fix copyable fake

Two remaining Windows failures:

1. src/rtk.ts was never patched with the rtk.cmd managed-dir fallback
   (only the shared/rtk.ts and pi-coding-agent/src/utils/rtk.ts were updated).
   Added the same rtk.cmd fallback and shell:.cmd detection to src/rtk.ts,
   which is what rtk.test.ts imports from.

2. createFakeRtk on Windows wrote '%~dp0\fake-rtk.js' in the .cmd content —
   this resolves relative to the .cmd file's own directory. When the test
   copies rtk.cmd to a different managed dir, %~dp0 resolves to the copy
   destination where fake-rtk.js does not exist. Fixed by embedding the
   absolute path to fake-rtk.js directly in the .cmd content so the fake
   works correctly regardless of where the .cmd is copied.

* feat(experimental): add RTK opt-in preference with web UI toggle

- Add `experimental` category to GSDPreferences with `rtk: boolean` (default: false)
- RTK is now opt-in: disabled by default for all projects unless explicitly enabled
- Validate experimental.* keys; unknown experimental keys produce warnings

Web UI:
- Add ExperimentalPanel component with animated toggle switch per flag
- Add /api/experimental route (GET/PATCH) to read/write flags in preferences.md
- Add 'Experimental' tab to settings dialog sidebar nav (FlaskConical icon)
- Include ExperimentalPanel at bottom of gsd-prefs mega-scroll
- Fix toggle disabled state: trigger loadSettingsData for 'experimental' section
  and self-fetch on mount when data is absent

Dashboard:
- Gate RTK Saved metric card on rtkEnabled from live auto state (web)
- Gate TUI dashboard RTK savings row on rtkEnabled
- Gate TUI footer RTK status updates on experimental.rtk preference
- Propagate rtkEnabled through AutoDashboardData → bridge-service → store

Build:
- Add scripts/build-if-stale.cjs: incremental build driver that skips each
  step (packages, root tsc, copy-resources, web) when output is newer than
  source; replaces full rebuild chain in gsd:web
- Add scripts/web-stop.cjs: robust stop with registry + legacy PID + orphan
  sweep via pgrep; handles crash/restart orphaned next-server processes
- gsd:web now uses build-if-stale.cjs (fast cold starts, instant when unchanged)
- gsd:web:stop / gsd:web:stop:all use web-stop.cjs directly

Fix: correct import path in rtk-status.ts (./preferences.js not ../preferences.js)

* fix: restore em-dash encoding in package.json to match upstream

* refactor(rtk): move command rewrite out of pi-coding-agent into GSD extension

Per review feedback from igouss: pi-coding-agent should not be modified to add
GSD-specific logic. Instead, add a proper extension point and wire RTK through it.

Changes to packages/pi-coding-agent (extension API only — no RTK logic):
- Add BashTransformEvent + BashTransformEventResult types to extension API
- Add on('bash_transform') overload to ExtensionAPI interface
- Add emitBashTransform() to ExtensionRunner (chains all handlers in order)
- Call emitBashTransform() in wrapToolWithExtensions before bash tool execution
- Export new types from extensions/index.ts and package index.ts
- Revert all RTK-specific changes from bash-executor.ts, tools/bash.ts
- Remove packages/pi-coding-agent/src/utils/rtk.ts entirely

Changes to GSD extension:
- Register bash_transform handler in register-hooks.ts that calls
  rewriteCommandWithRtk() from the existing shared/rtk.ts module
- Handler is a no-op when RTK is disabled or not installed

* fix: correct import path for shared/rtk.js in register-hooks

* fix(tests): remove deleted pi-coding-agent/utils/rtk imports from execution seams test

The RTK rewrite logic was moved out of pi-coding-agent into the GSD
extension (bash_transform hook). Tests that directly imported the
deleted utils/rtk.ts are removed; remaining tests verify the shared
RTK module and GSD-layer surfaces that still call rewriteCommandWithRtk.
2026-03-26 09:33:07 -06:00
Iouri Goussev
b486177066 refactor: split shared/mod.ts into pure and TUI-dependent barrels (#1807) 2026-03-21 11:48:32 -06:00
Lex Christopherson
050f260f7b fix: use createRequire instead of bare require for lazy pi-tui import
ESM modules don't have require(). Use createRequire(import.meta.url)
which works in both jiti-loaded and native ESM contexts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 10:25:28 -06:00
TÂCHES
182e4a5f85 fix: lazy-load @gsd/pi-tui in shared/ui.ts to prevent /exit crash (#1761)
The eager top-level import of @gsd/pi-tui in shared/ui.ts caused any
command that transitively loaded the shared/mod barrel (including /exit)
to fail when extensions were loaded from ~/.gsd/agent/extensions/ where
@gsd/pi-tui has no node_modules resolution path.

Replaced the static import with a lazy require() accessor that defers
resolution to the first makeUI() call, so modules that import shared/mod
for non-TUI exports (constants, format utils, etc.) no longer trigger
the unresolvable dependency.

Closes #1640

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 09:19:41 -06:00
Derek Pearson
90d6d71e38 feat(gsd): activate matching skills in dispatched prompts (#1630)
* fix(gsd extension): detect initialized projects in health widget

Use .gsd presence plus project-state detection for the health widget so bootstrapped projects no longer appear as unloaded before metrics exist.

* fix(gsd extension): detect initialized projects in health widget

Use .gsd presence plus project-state detection for the health widget so bootstrapped projects no longer appear as unloaded before metrics exist.

* feat(gsd): activate matching skills in dispatched prompts

Inject skill activations from installed skills, preferences, and task-plan handoff so GSD agents load the right skills automatically instead of relying on generic guidance. Align prompt templates and tests with the activation flow and current resource sync behavior.

* fix(gsd extension): detect initialized projects in health widget

Use .gsd presence plus project-state detection for the health widget so bootstrapped projects no longer appear as unloaded before metrics exist.

* fix(gsd extension): restore health widget build paths

* test(resource-loader): fix sibling cleanup assertion
2026-03-20 13:20:06 -06:00
Jeremy McSpadden
aa8d3ee059 fix: separate pi-tui-dependent layout utils to fix report generation (#1527)
Report generation in auto-loop uses native dynamic import() which
bypasses jiti's alias resolution. The import chain
metrics.js → mod.js → ui.js → @gsd/pi-tui failed because Node
cannot resolve @gsd/pi-tui from ~/.gsd/agent/extensions/.

Split ANSI-aware layout helpers (padRight, joinColumns, centerLine,
fitColumns) into layout-utils.ts and keep format-utils.ts pure so
report modules can import formatting functions without pulling in
the @gsd/pi-tui dependency.
2026-03-19 21:14:03 -06:00
Jeremy McSpadden
b247c3510e feat: integrate cmux with gsd runtime (#1532) 2026-03-19 20:05:06 -06:00
TÂCHES
2ea7abcd0c refine: extensions elegance improvements (#1503)
* refine: R1 delete dead wizard-ui.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refine: R2 remove dead BgProcess fields (commandHistory, envKeys)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refine: R3 remove no-op acknowledgeDeliveries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refine: R4 remove unused lineDedup tracking

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refine: R5 remove unused ProcessEvent types (output, pattern_match)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refine: S1 replace duplicate formatTokens with shared formatTokenCount

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refine: R1 remove re-staged wizard-ui.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refine: S2 consolidate maskEditorLine into shared/sanitize

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refine: S3 add session cleanup to context7 and google-search

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 16:59:52 -06:00
TÂCHES
5c45f9e4c8 cleanup: remove dead widgets and identity function (#1199)
Delete thinking-widget.ts and progress-widget.ts (fully implemented
but never imported anywhere) and remove the buildDirName identity
function from paths.ts.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:20:56 -06:00
TÂCHES
0b22394496 fix: complete shared barrel exports and add import-claude to help text (#1198)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:20:25 -06:00
Vedant
3f9085a588 feat: add OSC 8 clickable hyperlinks for file paths in export notifications (#1114) 2026-03-18 08:21:56 -06:00
TÂCHES
94be09482f fix: add barrel files for remote-questions, ttsr, and shared extensions (#1048)
* fix: add barrel files for remote-questions, ttsr, and shared extensions

Centralizes public API surface for three extension directories behind
index.ts barrel files. External consumers now import from the barrel
instead of reaching into internal module files, reducing coupling and
making future refactors safer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: rename barrel files to mod.ts to avoid extension loader auto-discovery

The extension loader auto-discovers extensions by looking for index.ts files
inside extensions/*/ directories. remote-questions/ and shared/ are utility
directories, not extensions — their index.ts barrel files caused load failures.

Renamed to mod.ts which the loader ignores, and updated all import paths.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:48:32 -06:00
TÂCHES
6a2a6a9e2c fix: consolidate frontmatter parsing into shared module (#1040)
* fix: consolidate frontmatter parsing into shared module

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: strip quotes from frontmatter scalar values

The shared parseFrontmatterMap was missing quote-stripping that the old
rule-loader had, causing 3 test failures in ttsr-rule-loader.test.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:44:25 -06:00
TÂCHES
9488865b9e refactor(gsd): unify duplicate padRight/truncate into shared format-utils (#1045)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:36:11 -06:00
TÂCHES
edda01e438 fix: extract sanitizeError to shared module and apply to ask-user-questions (#1033)
Closes a security gap where ask-user-questions errorResult() could leak
tokens in error messages. The sanitizeError function and TOKEN_PATTERNS
are now in shared/sanitize.ts, imported by both manager.ts and
ask-user-questions.ts.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:30:05 -06:00
TÂCHES
4407c24522 fix: deduplicate formatDateShort into shared/format-utils (#1032)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:29:54 -06:00
TÂCHES
e8de7dfa30 fix(gsd): consolidate string-array normalizer functions into shared utility (#1009)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:01:52 -06:00
TÂCHES
058f682f67 fix: deduplicate formatTokenCount into shared format-utils (#987)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 17:06:14 -06:00
TÂCHES
2bdcabfbfc fix: deduplicate formatDuration into shared format-utils (#989)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 17:06:06 -06:00
Lex Christopherson
12e65afd36 fix: consolidate duplicate bundled-extension-paths.ts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:17:53 -06:00
Jeremy McSpadden
1ea653b5fc refactor: TUI dashboard cleanup, dedup, and feature improvements (#931)
* refactor: TUI dashboard cleanup, dedup, and feature improvements

- Extract shared format-utils.ts: formatDuration, padRight, joinColumns,
  centerLine, fitColumns, sparkline, stripAnsi — eliminating 3× duplication
  across dashboard-overlay, visualizer-views, and auto-dashboard
- Use shared STATUS_GLYPH/STATUS_COLOR from ui.ts consistently across all
  overlay and view files instead of hardcoded Unicode glyphs
- Fix redundant dynamic import('node:fs') in visualizer-data.ts (statSync
  already imported at top level)
- Replace (entry as any) casts with proper SessionMessageEntry type narrowing
- Add mtime-based file content cache for visualizer data loader to avoid
  re-parsing unchanged roadmap/plan files on every refresh
- Increase visualizer refresh interval from 2s to 5s (with mtime cache,
  unchanged files are effectively free)
- Fix sparkline to use loop-based max instead of Math.max(...values) to
  avoid stack overflow on large arrays
- Add ETA/time-remaining estimate to progress widget and dashboard overlay
  based on average unit duration from metrics ledger
- Show warning glyph for budget-pressured units in completed units list
  (continueHereFired units now show ⚠ instead of ✓)
- Add terminal resize (SIGWINCH) handling to both overlays — invalidates
  cache and re-renders on window size change
- Fix dispose race in dashboard overlay close path — now calls dispose()
  before onClose() to prevent timer callbacks firing after teardown
- Add 23 unit tests for format-utils.ts (including 100k-element sparkline)
- Add 2 tests for estimateTimeRemaining
- Add source-contract tests for resize handler and shared imports

* fix: use STATUS_GLYPH.warning instead of STATUS_GLYPH.statusWarning

STATUS_GLYPH is keyed by ProgressStatus ("warning"), not by GLYPH
property name ("statusWarning"). Fixes typecheck failure in CI.
2026-03-17 14:02:26 -06:00
Tom Boucher
3542b17c97 fix: normalize Windows paths in LLM-visible text to prevent bash failures (#874) (#884)
On Windows, process.cwd() returns backslash paths (C:\Users\name\...).
When these paths are injected into system prompts, worktree context
blocks, or tool results, the model copies them into bash commands.
Bash interprets backslashes as escape characters, silently stripping
them — producing invalid paths like 'C:Usersnamedevelopmentapp-name'.

This is not a regex hack — it's a proper cross-platform boundary:
- Filesystem operations (fs, path.join, spawn cwd) use native paths
  unchanged. Node handles both separators correctly for I/O.
- LLM-visible text (prompts, tool results, extension messages) uses
  toPosixPath() to normalize to forward slashes. C:/Users/name/...
  is valid in Git Bash, WSL bash, PowerShell, and Node.js.

Changes:

- utils/path-display.ts: New toPosixPath() utility in pi-coding-agent
  package (for system prompt) and shared extension module (for
  extensions that can't import from the compiled package at dev time)

- system-prompt.ts: Normalize resolvedCwd before injecting into the
  'Current working directory' line

- gsd/index.ts: Normalize all process.cwd() and originalBase paths in
  worktree context blocks injected into the system prompt

- bg-shell/index.ts: Normalize cwd in tool result text (start, env
  actions) that the model reads and may reference in commands

- path-display.test.ts: 9 regression tests covering toPosixPath
  behavior and system prompt output verification. Includes a scanner
  that fails if any Windows absolute paths with backslashes appear in
  buildSystemPrompt() output.

Audit scope: Checked all process.cwd() usage across pi-coding-agent
and all bundled extensions. Filesystem-only paths (join, readFile,
spawn cwd, existsSync) are correct and left unchanged. Only paths
entering LLM text are normalized.
2026-03-17 09:02:23 -06:00
Lex Christopherson
36345c12fc fix: add Warp terminal to unsupported Ctrl+Alt shortcut list
Warp terminal (both macOS and Windows) does not emit recognized escape
sequences for Ctrl+Alt key combos. This adds Warp to the unsupported
terminals list so users see the /gsd status fallback hint.

Closes #643

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 13:37:53 -06:00
Tom Boucher
2924a1d666 fix: showNextAction falls back to select() when custom() returns undefined (#447) (#615)
In RPC mode, ctx.ui.custom() returns undefined without emitting any event.
This caused showNextAction() — and all 13+ call sites in guided-flow.ts —
to silently complete without taking action. No error thrown, no event
emitted, command handler returns normally.

Fix: After custom() returns, check for undefined/null and fall back to
ctx.ui.select() which IS implemented in RPC mode. Maps the action list
to select labels and resolves the chosen action id.
2026-03-16 07:09:37 -06:00