Commit graph

4506 commits

Author SHA1 Message Date
Mikael Hugo
605cd712be refactor: capability-tier isHeavyModelId, search provider registry, frontmatter_version field, schema docs
- preferences-models.js: replace 6-regex isHeavyModelId() with MODEL_CAPABILITY_TIER
  lookup + regex fallback for unknown models; new models in model-router.js
  are automatically reflected without touching preferences-models.js
- search-the-web/provider.js: replace ~200-line per-provider waterfall with
  PROVIDER_REGISTRY array + firstAvailable()/resolveWithFallback() helpers;
  preserves Tavily→Brave→Serper→Exa→Ollama→MiniMax auto-fallback order
- sf-db.js: bump SCHEMA_VERSION 58→60 (v59 now reachable); add
  frontmatter_version column to tasks table via v60 migration and CREATE
  TABLE definition; wire frontmatter_version into upsertTaskPlanning() SQL
  and .run() params
- task-frontmatter.js: add frontmatterVersion:1 to DEFAULT_TASK_FRONTMATTER,
  add validation block in validateTaskFrontmatter(), add frontmatterVersion
  mapping in taskFrontmatterFromRecord()
- sf-db-migration.test.mjs: update hardcoded version assertion 58→60
- docs/specs/sf-operating-model.md: add Planning Schema section documenting
  the 3-table model (milestones/slices/tasks, their PKs, spec tables, and
  ID naming conventions)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 23:42:29 +02:00
Mikael Hugo
b228bc9f5c feat(learning): weight failure_mode in Bayesian blender — rate_limit=0.7, quota=0.2, auth=0.0
- AGGREGATE_ONE/GROUPED_SQL: compute effective_success_rate with CASE WHEN failure_mode
- AggregatedStats: add effective_success_rate, hard_failure_count fields
- computeObservedScore: uses effective_success_rate when available; 0.5x penalty if >50% hard failures
- Tests: verify rate_limit ranked above quota_exhausted; hard failure penalty verified

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 23:20:33 +02:00
Mikael Hugo
2dea73398d fix(learning): add save_knowledge to manifest, failure_mode to aggregator SELECT + index
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 23:18:02 +02:00
Mikael Hugo
e50321b62b feat(selection): thread unitType + failure_mode into fallback outcome records
- FallbackResolver.setUnitContext() stores {unitType,unitId} from autonomous dispatch
- run-unit.js calls pi.setFallbackUnitContext() before/after each unit
- _findAnyAvailableFallback uses real unitType/unitId from context, not sentinel
- Schema v59: failure_mode column in llm_task_outcomes
- insertLlmTaskOutcome accepts failure_mode (rate_limit, quota_exhausted, auth_error)
- register-hooks.js passes event.classification.reason as failure_mode
- register-hooks.js uses real event.unitId when available
- ExtensionRuntimeActions.setFallbackUnitContext added to pi API surface

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 23:14:22 +02:00
Mikael Hugo
009651e86f feat(selection): wire before_model_select into FallbackResolver for outcome-aware fallback
When a model fails and FallbackResolver picks a replacement, it now:
1. Fires the before_model_select hook with reason='fallback' and the
   failing model's ID — the learning system records the failure outcome
   and returns the best Bayesian-blended replacement from llm_task_outcomes
2. Falls back to the existing heuristic sort (reasoning + context window)
   if the hook is unavailable or returns no override

Changes:
- BeforeModelSelectEvent: add optional currentModelId and reason fields
- FallbackResolver: accept emitBeforeModelSelect in constructor; make
  _findAnyAvailableFallback async; fire hook before heuristic fallback
- agent-session.ts: inject lazy emitBeforeModelSelect closure into resolver
- register-hooks.js: record failure outcome when reason='fallback' before
  returning selectLearnedModel result

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 23:05:33 +02:00
Mikael Hugo
fb1bd3e5fa refactor(shared): deduplicate shared/ utilities against coding-agent package exports
- Add packages/coding-agent/src/utils/format.ts as the canonical source
  for formatDuration, formatTokenCount, truncateWithEllipsis, sparkline,
  formatDateShort, fileLink, stripAnsi, normalizeStringArray — all already
  exported from @singularity-forge/coding-agent via index.ts.

- Convert shared/format-utils.js to a compatibility shim that re-exports
  the 8 functions from @singularity-forge/coding-agent. All 13 importers
  continue to work with no import changes required.

- Convert shared/path-display.js to a compatibility shim that re-exports
  toPosixPath from @singularity-forge/coding-agent. Implementation in
  packages/coding-agent/src/utils/path-display.ts was already canonical.

- shared/frontmatter.js is intentionally NOT shimmed: splitFrontmatter/
  parseFrontmatterMap have a different API from the package's parseFrontmatter/
  stripFrontmatter (flat-map vs {frontmatter, body} object).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 22:41:03 +02:00
Mikael Hugo
7227912a29 perf(search): move web-search provider injection from extension hook to native middleware
- Create packages/coding-agent/src/core/providers/web-search-middleware.ts with
  WebSearchMiddleware class: injects web_search tool, enforces session budget (#1309),
  strips thinking blocks from history, and respects PREFERENCES.md search_provider.

- Wire webSearchMiddleware.applyToPayload into sdk.ts onPayload callback (before
  extension hook dispatch) so injection runs as compiled TypeScript with zero
  jiti-dispatch overhead.

- Export WebSearchMiddleware, webSearchMiddleware singleton, setPreferBraveResolver,
  CUSTOM_SEARCH_TOOL_NAMES, MAX_NATIVE_SEARCHES_PER_SESSION, and stripThinkingFromHistory
  from @singularity-forge/coding-agent so the extension can delegate to the same instance.

- Refactor search-the-web/native-search.js: remove self-contained injection logic;
  import and delegate before_provider_request to webSearchMiddleware singleton.
  Use tri-state isAnthropicProvider (null/false/true) to synthesize a provider hint
  when event.model is absent but model_select has already fired — prevents the
  model-name heuristic from wrongly injecting into Copilot claude-* requests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 22:37:42 +02:00
Mikael Hugo
a798aa1f6e feat(swarm): wire @a2a-js/sdk as real A2A transport for SF_A2A_ENABLED dispatch path
- Install @a2a-js/sdk v0.3.13 as a dependency
- Add a2a-transport.js: A2ATransport class with spawnAgent, dispatch,
  getOrSpawnAgent, and buildAgentCard; spawns pi subprocesses with
  SF_A2A_AGENT_* env vars and dispatches envelopes via A2A JSON-RPC
- Add a2a-agent-server.js: A2A HTTP server entrypoint for spawned agent
  processes; starts express + A2AExpressApp with DefaultRequestHandler,
  handles incoming DispatchEnvelopes via SwarmAgentExecutor, writes
  envelope to SQLite MessageBus, and signals readiness via stdout JSON
- Update swarm-dispatch.js: split dispatch() into _busDispatch()
  (existing SQLite path) and _a2aDispatch() (new A2A path); lazy-load
  A2ATransport singleton only when SF_A2A_ENABLED is set; default
  path unchanged for all existing callers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 22:33:01 +02:00
Mikael Hugo
3fba4bcb03 refactor(mcp): move MCP connection manager to packages/coding-agent/src/core/mcp/
- Create config.ts with McpServerConfig types and readMcpConfigs/getServerConfig
- Create auth.ts with buildHttpTransportOpts and createCliOAuthProvider
- Create connection-manager.ts with McpConnectionManager class
- Create index.ts re-exporting the public API
- Export McpConnectionManager and helpers from @singularity-forge/coding-agent
- Rewrite mcp-client extension as thin wrapper using McpConnectionManager
- Rewrite auth.js as re-export shim from @singularity-forge/coding-agent
- Update test to import buildHttpTransportOpts from @singularity-forge/coding-agent

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 22:19:46 +02:00
Mikael Hugo
9e484e67b7 refactor(sf): fold sf-tui extension into sf/ui/ — remove separate extension layer
sf-tui was a 'bundled' extension with zero features independent of the sf/
extension. Every hook, shortcut, tool, header and footer render depended
on sf/ internals (getAutoSession, isAutoActive, projectRoot,
getExperimentalFlag). The separation was artificial.

Changes:
- Moved all sf-tui/*.js into sf/ui/ (header, footer, git, color-band, emoji,
  prompt-history, marketplace, powerline, shared)
- Fixed imports: ../sf/ → ../ (one level up from ui/)
- Registered sf/ui/index.js from sf/index.js in a try/catch so a UI failure
  can't take out the core SF commands
- Merged sf-tui manifest entries (9 commands, 3 shortcuts, agent_start hook)
  into sf/extension-manifest.json
- Deleted src/resources/extensions/sf-tui/ entirely
- Fixed prompt-history.test.mjs import path

Result: one fewer extension to discover, load and validate at startup.
sf is now the single extension that owns both planning state and UI chrome.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 22:04:00 +02:00
Mikael Hugo
9e55528c95 revert(tui): remove Ink bridge, restore pure custom differential renderer
The Ink bridge added today was a misguided gradual-migration wrapper:
- Components still rendered via the old string-line protocol (no Ink layout)
- Key decodes were re-encoded to escape sequences → keys.ts decoded again (double round-trip bug)
- The _useInk / _inkHandle path blocked TTY start unconditionally via process.stdout.isTTY check

Removed: ink-bridge.tsx, ink-bridge.test.ts, useInk() method, _useInk/_inkHandle fields,
startInkRenderer import/export, Ink branch in start()/stop()/requestRender().

Removed ink and react from packages/tui dependencies and peerDependencies.
Reverted tsconfig.extensions.json jsx settings (only needed for the .tsx bridge file).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 21:38:54 +02:00
Mikael Hugo
8c764f6c98 fix(tsconfig): add jsx/jsxImportSource to tsconfig.extensions.json for tsgo compat
tsgo (TS7 native port) requires explicit jsx setting when .tsx files are
in scope. tsc 6 was lenient; tsgo errors without it.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 21:31:53 +02:00
Mikael Hugo
702ec3fc0e refactor(sf): rename guidance files TASTE.md→STYLE.md, ANTI-GOALS.md→NON-GOALS.md
More self-explanatory names. No behavioral change — same files, same purpose.

- .sf/TASTE.md → .sf/STYLE.md (# Taste → # Style)
- .sf/ANTI-GOALS.md → .sf/NON-GOALS.md (# Anti-goals → # Non-goals)
- All code references updated: auto-bootstrap-context, system-context,
  gitignore, milestone-framing-check, scaffold-constants, spec-projections
- Section headings injected into agent context updated to match

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 21:28:31 +02:00
Mikael Hugo
48a01dd764 refactor(prefs): remove all legacy PREFERENCES.md / preferences.md support
preferences.yaml is now the only preferences file. No fallback chains,
no .md parsing paths, no legacy path getters.

- preferences.js: remove globalPreferencesPath, globalPreferencesPathUppercase,
  legacyGlobalPreferencesPath, projectPreferencesPath, projectPreferencesPathUppercase,
  getLegacyGlobalSFPreferencesPath; simplify load functions to yaml-only;
  parsePreferencesMarkdown kept as thin deprecated shim over parsePreferencesYaml
- commands-prefs-wizard.js: remove parseFrontmatterMap/splitFrontmatter usage,
  .md branch in savePreferencesFile/ensurePreferencesFile, legacyGlobal display
- auto-dashboard.js: parsePreferencesMarkdown → parsePreferencesYaml
- guided-flow.js / worktree-root.js: remove PREFERENCES.md existence checks
- detection.js: remove .md fallbacks from all 3 detection functions
- auto-bootstrap-context.js: remove .sf/PREFERENCES.md from priority list
- auto-worktree.js: remove LEGACY_PREFERENCES_FILES array and all copy fallbacks
- deep-project-setup-policy.js: only check preferences.yaml
- gitignore.js: ensurePreferences checks yaml only
- planning-depth.js: returns plain string path (not {path,isYaml}); yaml-only
- preferences-template-upgrade.js: remove .md branch; always write raw YAML
- tests: update fixtures to preferences.yaml with plain YAML content
- docs/learning: update all remaining PREFERENCES.md references

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 21:14:43 +02:00
Mikael Hugo
48dbb175c0 feat(prefs): migrate canonical preferences file from PREFERENCES.md to preferences.yaml
New installations create .sf/preferences.yaml (pure YAML, no frontmatter
markers) and ~/.sf/preferences.yaml. Existing .md files are read as fallbacks
with no migration required for current users.

Changes:
- preferences.js: add yaml path getters, load chain tries .yaml first, add
  parsePreferencesYaml() for direct YAML parse without frontmatter extraction
- templates/preferences.yaml: new canonical template (pure YAML with comment
  header pointing to preferences-reference.md)
- gitignore.js: ensurePreferences() creates preferences.yaml; simplified by
  removing scaffold-versioning dependency
- init-wizard.js: buildPreferencesFile() produces pure YAML, writes preferences.yaml
- commands-prefs-wizard.js: savePreferencesFile() helper handles .yaml vs .md;
  ensurePreferencesFile uses yaml template for yaml paths
- preferences-template-upgrade.js: yaml files get raw YAML on upgrade
- planning-depth.js: returns {path, isYaml}, handles both formats
- deep-project-setup-policy.js: isWorkflowPrefsCaptured() tries all 3 paths
- detection.js: preferences.yaml added to all detection checks
- auto-worktree.js: canonical=yaml, LEGACY_PREFERENCES_FILES=["PREFERENCES.md","preferences.md"]
- auto-bootstrap-context.js: preferences.yaml before PREFERENCES.md in list
- guided-flow.js / worktree-root.js: existence checks include preferences.yaml
- User-visible strings / comments updated throughout

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 21:05:10 +02:00
Mikael Hugo
ce13017519 chore(.sf): update PROJECT.md — DB healthy, S01+S02 complete, S03 next
- Remove stale BLOCKED/corrupted-DB claim
- Mark M001-3hf5k0 complete, reflect S01+S02 done in M001-6377a4
- Clarify S03 has T02-T04 pending (verification evidence work)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 20:49:08 +02:00
Mikael Hugo
61b4fecdaf fix(notices+db): complete NOTICE_KIND tagging, fix slice-dep query, cap error storage
NOTICE_KIND tagging:
- auto.js: ctrl-c-pause (USER_VISIBLE), auto-start-failed/session-lock-lost/
  stopAuto/debug-summary-written (SYSTEM_NOTICE), auto-no-command-ctx (USER_VISIBLE)
- loop.js: model-policy-blocked SYSTEM_NOTICE→BLOCKING_NOTICE (user must act),
  solver-eval results/infra-stop/consecutive-cooldowns (SYSTEM_NOTICE),
  phase-timeout/credential-cooldown-wait/iteration-error (TOOL_NOTICE); fix import order
- register-hooks.js: destructive-command (TOOL_NOTICE), gemini-preflight (SYSTEM_NOTICE)
- provider-error-pause.js: auto-resume (TOOL_NOTICE), scheduled-resume (SYSTEM_NOTICE),
  permanent-pause (BLOCKING_NOTICE)
- uok-parity-summary.js: parity warning (SYSTEM_NOTICE)

sf-db fixes:
- getActiveSliceFromDb: use slice_dependencies junction table instead of
  json_each(s.depends) — junction table is kept in sync by syncSliceDependencies
- capErrorForStorage: cap UOK run error blobs at 4 KB; excess spills to
  .sf/runtime/errors/<runId>.txt to prevent DB bloat from large stack traces

ARCHITECTURE.md:
- Document DB-first invariant; remove .sf/DECISIONS.md/.REQUIREMENTS.md/.KNOWLEDGE.md
  from tracked-file list (they are rendered projections, not authoritative sources)
- Add .sf/traces/ and .sf/metrics.db to gitignored list
- Update system-context assembly order to show DB-sourced decisions/requirements
- Correct system-context.ts → system-context.js

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 20:26:18 +02:00
Mikael Hugo
ad380d5602 fix(db-first): remove all .sf/*.md direct-write instructions from prompts; requirement-promoter uses DB only
- Prompts: replace 'append to .sf/DECISIONS.md' → 'call save_decision' in
  plan-slice, heal-skill (KNOWLEDGE.md), refine-slice, queue, guided-execute-task
- Prompts: replace 'Read .sf/DECISIONS.md if it exists' / 'Read .sf/REQUIREMENTS.md if it exists'
  with 'injected from DB into system context' in guided-plan-slice, guided-research-slice
- requirement-promoter: remove dead appendRequirementRow() and readHighestRNumber(file)
  that read/wrote REQUIREMENTS.md; replace with DB-only readHighestRNumber() using
  getActiveRequirements(); remove sfRoot import, mkdirSync, writeFileSync
- requirement-promoter: pre-compute highestNum once per sweep loop instead of
  re-reading for each cluster (fixes ID collision when promoting multiple at once)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 20:22:55 +02:00
Mikael Hugo
62fcf8fd20 feat(notifications): tag remaining auto/loop/register-hooks notices + trace-writer
- auto.js, auto/loop.js, bootstrap/register-hooks.js: tag all
  autonomous-mode system notices with NOTICE_KIND.SYSTEM_NOTICE;
  add dedupe_key to loop-level model-policy and flow-audit notices
- web/notifications-service.ts: add repeatCount/lastTs/noticeKind to
  Notification type (schema v2 fields)
- uok/trace-writer.js: new unit trace writer
- tests/notification-store-grouping.test.mjs: grouping test coverage

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 20:14:22 +02:00
Mikael Hugo
d33e30e885 feat(notifications): NOTICE_KIND enum, schema v2 dedup, sf-db cleanup
- notification-store: schema v2 — repeatCount/lastTs merge for non-blocking
  notices; NOTICE_KIND enum (SYSTEM_NOTICE, TOOL_NOTICE, BLOCKING_NOTICE,
  USER_VISIBLE) for renderer classification without message parsing
- sf-db: remove gate_runs and audit_events tables (replaced by uok audit.js
  and trace-writer); schema reduced by ~370 lines
- notify-interceptor: tag auto-mode system notices with NOTICE_KIND.SYSTEM_NOTICE
- auto-prompts, guided-flow, system-context: use NOTICE_KIND on emit calls
- cli-status: expanded headless status surface + test coverage
- headless-types: new status fields
- Makefile/justfile: dev workflow improvements
- record-promoter, requirement-promoter: minor cleanup
- sf-db-migration tests: updated for dropped tables
- uok-gate-runner, uok-metrics, uok-outcome, uok-status tests: updated

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 20:13:58 +02:00
Mikael Hugo
5c2e3eec24 fix(memory): add missing readGatewayFromAuthJson to source + update tests
The function and node:fs/os/path imports were dropped from the source
during editing. Added them back. Updated memory-embeddings-llm-gateway
test to cover auth.json-only behavior (no env var aliases).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 18:28:18 +02:00
Mikael Hugo
6f6ad76a77 feat(memory): load LLM gateway key from auth.json only, not env vars
Gateway key and URL are now read exclusively from ~/.sf/agent/auth.json
under the 'llm-gateway' entry. Removed env var support for the API key
(SF_LLM_GATEWAY_KEY, LLM_MUX_API_KEY, etc.) — credentials belong in
auth.json alongside all other provider keys, not in the environment.

Model/instruction overrides (SF_LLM_GATEWAY_EMBED_MODEL etc.) still
read from env vars as they are tuning knobs, not secrets.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 18:25:23 +02:00
Mikael Hugo
a77e1551d2 refactor(memory): consolidate memory system, remove dead code
- Delete memory-backfill.js — not imported anywhere, dead code
- Rename memory-sleeper.js → tool-watchdog.js — misnamed; it is a
  tool-output watchdog with no relation to the memory store
- Collapse memory-embeddings-llm-gateway.js into memory-embeddings.js —
  removes the lazy-import split; loadGatewayConfigFromEnv,
  createGatewayEmbedFn, and rerankCandidates are now direct exports
- Remove buildEmbeddingFn() dead stub (always returned null)
- Enable packages/coding-agent memory extraction extension by default
  (memory.enabled ?? true) so session-level extraction is active
- Update all import sites and tests

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 18:17:49 +02:00
Mikael Hugo
2a1309d127 fix(memory): make SM opt-in (SM_ENABLED=true) instead of opt-out
Local SQLite is the memory system. External Singularity Memory is an
optional cross-project enhancement, not a dependency. Flip the default
so SM is disabled unless explicitly opted in via SM_ENABLED=true:
- sm-client.js: return disconnected early unless SM_ENABLED=true
- memory-store.js: only pass smConnected=true when SM_ENABLED=true
- doctor-config-checks.js: skip SM health check when not opted in
- sm-client.test.ts: update test to reflect opt-in behaviour

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 18:03:13 +02:00
Mikael Hugo
a3019e5402 fix(knowledge): db-first dedup, numeric confidence, consistent IDs
- knowledge-compounding.js: replace KNOWLEDGE.md file-read dedup with
  getActiveMemories() DB query; file was never written so dedup was
  always empty, causing duplicates to accumulate on every milestone close
- knowledge-compounding.js + save_knowledge tool: map confidence strings
  ('high'/'medium'/'low') to numeric scores (0.9/0.6/0.3) for the
  memories.confidence REAL column; string values coerced to 0.0 by
  SQLite, silently making all knowledge entries rank last and never
  appear in system context
- save_knowledge: use K-${randomUUID()} (full UUID) instead of
  K-${randomUUID().slice(0,8)} to match knowledge-compounding.js and
  avoid collision risk
- complete-milestone.md: replace '.sf/DECISIONS.md' file reference with
  'decisions inlined from DB' — the file is not generated anymore

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 17:57:11 +02:00
Mikael Hugo
3ffd882c8c sf snapshot: uncommitted changes after 56m inactivity 2026-05-10 17:16:30 +02:00
Mikael Hugo
37ebfcf53a test(summary-helpers): add regression tests for extractSliceExecutionExcerpt
Verifies the function handles null/undefined content gracefully and
correctly extracts goal, demo, verification, and observability sections
from slice plan content. Addresses sf-mozutl5d-ei3ec6 by ensuring the
function is importable and behaves correctly end-to-end.
2026-05-10 16:20:15 +02:00
Mikael Hugo
924383b6f7 sf snapshot: uncommitted changes after 197m inactivity 2026-05-10 15:59:33 +02:00
Mikael Hugo
de77cf439f fix(tui): error boundary in doRender, extract autonomousStatus, clean parseCellSize
Some checks failed
CI / detect-changes (push) Has been cancelled
CI / docs-check (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / build (push) Has been cancelled
CI / integration-tests (push) Has been cancelled
CI / windows-portability (push) Has been cancelled
CI / rtk-portability (linux, blacksmith-4vcpu-ubuntu-2404) (push) Has been cancelled
CI / rtk-portability (macos, macos-15) (push) Has been cancelled
CI / rtk-portability (windows, blacksmith-4vcpu-windows-2025) (push) Has been cancelled
- doRender() now catches render errors and emits a fallback line
- autonomousStatus ANSI formatting extracted to renderAutonomousStatus()
  with named color constants instead of raw escape strings
- parseCellSizeResponse extracted to pure function with proper validation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 12:41:47 +02:00
Mikael Hugo
2d34d3a386 fix(web): resolve ESLint regressions from eslint-config-next upgrade
- Escape unescaped entities (react/no-unescaped-entities) in step-remote,
  step-welcome, projects-view, settings-panels
- Add targeted eslint-disable-next-line for react-hooks/set-state-in-effect
  on established async-fetch and prop-sync patterns in useEffect bodies:
  chat-mode, file-content-viewer, files-view, step-dev-root, projects-view,
  settings-panels, update-banner, visualizer-view, carousel, use-mobile
- Add targeted eslint-disable-next-line for react-hooks/purity on Date.now()
  display timestamps in streaming chat messages (chat-mode)
- Remove now-unused eslint-disable directives (projects-view, settings-panels)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 12:18:58 +02:00
Mikael Hugo
b0a8f32a10 feat(tui): wire Ink bridge into TUI.start() and stop()
- TUI.useInk() opts into Ink-backed rendering (call before start())
- In start(): if _useInk || process.stdout.isTTY, mount Ink renderer via
  startInkRenderer() and skip the legacy differential render path entirely
- In stop(): unmount Ink handle and return early; legacy terminal cleanup
  (cursor repositioning, showCursor, terminal.stop) is skipped since Ink
  handles terminal restoration itself
- Passes this.render()/invalidate() via a plain Component wrapper to avoid
  the private handleInput TypeScript conflict
- Two new contract tests: useInk() flag and stop() Ink handle teardown
- 80/80 tests pass; legacy path unchanged for non-TTY (CI/tests)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 12:15:09 +02:00
Mikael Hugo
4e97058d7e feat(tui): add Ink bridge for gradual migration from custom renderer
Install ink@7.0.2 + react@19.2.6. Add JSX/react-jsx support to
packages/tui tsconfig. Create ink-bridge.tsx: LegacyComponentView wraps
existing Component objects as React nodes, startInkRenderer drives the
Ink render loop around any legacy Component tree.

Exports startInkRenderer from @singularity-forge/tui public API.
All 78 existing tui tests pass; 3 new ink-bridge tests added.

This is the infrastructure step for migrating components one-by-one from
the custom differential renderer to native Ink React components, without
breaking interactive mode.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 12:10:39 +02:00
Mikael Hugo
280303ef9a fix(lint): reformat 6 files touched during web dep upgrade
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 12:10:10 +02:00
Mikael Hugo
d447095bd7 build: switch full build pipeline to TypeScript 7 native (tsgo)
Replace tsc with tsgo in all build scripts — 5.6x faster emit.
tsgo has full emit parity for this codebase (NodeNext, ES2022, strict).

- build:core: tsc → tsgo (root tsconfig.json)
- copy-resources.cjs: typescript/bin/tsc → @typescript/native-preview/bin/tsgo.js
- All workspace packages (agent-core, ai, coding-agent, daemon,
  google-gemini-cli-provider, native, rpc-client, tui): tsc → tsgo

Benchmarks (root project):
  tsc --project tsconfig.json: 7.7s
  tsgo --project tsconfig.json: 1.4s  (5.6x faster)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 11:58:58 +02:00
Mikael Hugo
e09eb8f899 build: add TypeScript 7 (native preview) for fast type checking
- Remove vestigial experimentalDecorators/emitDecoratorMetadata from all
  package tsconfigs (no actual decorators in source — flags were from
  pi-mono vendor copy)
- Add @typescript/native-preview for 8-10x faster type checking (measured
  4.6x on this repo: tsc 6.5s vs tsgo 1.4s)
- Fix tsconfig.extensions.json: remove baseUrl (removed in tsgo/TS7) and
  use relative paths in paths mappings — compatible with both tsc and tsgo
- Add typecheck/typecheck:extensions scripts using tsgo

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 11:53:22 +02:00
Mikael Hugo
e50d96e1f8 chore(web): upgrade all dependencies to latest stable
- @hookform/resolvers 3.10.0 → 5.2.2
- @tailwindcss/postcss 4.2.1 → 4.3.0
- @types/node 24.12.2 → 25.6.2
- @uiw/codemirror-* 4.25.8 → 4.25.9
- autoprefixer 10.4.27 → 10.5.0
- esbuild 0.27.4 → 0.28.0
- eslint 9.39.4 → 9.x (pinned; eslint 10 incompatible with eslint-config-next)
- eslint-config-next 16.2.3 → 16.2.6
- lucide-react 0.564.0 → 1.14.0
- motion 12.36.0 → 12.38.0
- next 16.2.3 → 16.2.6
- postcss 8.5.8 → 8.5.14
- react/react-dom 19.2.4 → 19.2.6
- react-day-picker 9.13.2 → 10.0.0
- react-hook-form 7.71.2 → 7.75.0
- react-resizable-panels 2.1.9 → 4.11.0
- recharts 2.15.0 → 3.8.1
- sonner 1.7.4 → 2.0.7
- tailwindcss 4.2.1 → 4.3.0
- tw-animate-css 1.3.3 → 1.4.0
- typescript 5.7.3 → 6.0.3
- zod 3.25.76 → 4.4.3

Breaking changes fixed:
- react-resizable-panels v4: PanelGroup→Group, PanelResizeHandle→Separator
- react-day-picker v10: ClassNames.table renamed to month_grid
- recharts v3: TooltipContentProps/DefaultLegendContentProps type changes,
  DataKey type for key prop
- shiki: cast createHighlighter promise to local ShikiHighlighter type
- voice/route.ts: pass requestUrl through buildDigitsResponse
- pty-chat-parser.ts: declare _lastInputAt private field
- sf-workspace-store.tsx: fix stale pi-coding-agent import path,
  add import for locally-used workspace types

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 11:52:54 +02:00
Mikael Hugo
cab8b5decc refactor: strip internal pi branding (Phase 2A)
- CURSOR_MARKER: \x1b_pi:c\x07 → \x1b_sf:c\x07
- process.title: "pi" → "sf"
- PiManifest → SFManifest (with pi field backwards compat)
- readPiManifest → readSFManifest (loader.ts and package-manager.ts)
- readPiManifestFile → readSFManifestFile (package-manager.ts)
- .pi/skills → .sf/skills (keeps .pi/skills for backwards compat)
- User-facing path strings updated to .sf/ where appropriate
- ARCHITECTURE.md: "Pi coding-agent extension" → "coding-agent extension"
- Temp editor file: pi-editor-*.pi.md → sf-editor-*.sf.md
- Test fixtures: appName "pi" → "sf", pi manifest field → sf

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 11:50:55 +02:00
Mikael Hugo
02a4339a51 refactor: rename pi-* packages to forge-native names (Phase 1)
Rename all four packages/pi-* directories to forge-native names,
stripping the 'pi' identity and establishing forge's own:

- packages/pi-coding-agent → packages/coding-agent
- packages/pi-ai → packages/ai
- packages/pi-agent-core → packages/agent-core
- packages/pi-tui → packages/tui

Package names updated:
- @singularity-forge/pi-coding-agent → @singularity-forge/coding-agent
- @singularity-forge/pi-ai → @singularity-forge/ai
- @singularity-forge/pi-agent-core → @singularity-forge/agent-core
- @singularity-forge/pi-tui → @singularity-forge/tui

All import references, bare string references, path references,
internal variable names (_bundledPi*), and dist files updated.
@mariozechner/pi-* third-party compat aliases preserved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 11:28:01 +02:00
Mikael Hugo
6725a55591 feat(web): add error boundaries, expand test coverage, add README
- Add class-based ErrorBoundary component wrapping all 7 main views
  inside WorkspaceChrome; fallback shows view name, error, reload button
- Add 30 new unit tests (boot null-project path × 9, onboarding
  pure-function logic × 21); all 43 web/lib tests pass
- Add web/README.md: architecture, auth flow, 7 views, dev setup,
  API route pattern, test instructions

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 11:24:40 +02:00
Mikael Hugo
05953e9599 fix(lint): restore 0 Biome diagnostics and fix web-mode-onboarding test timeout
- Remove/prefix unused imports and variables across 11 src/ files to clear
  74 diagnostics introduced by 37 subsequent commits since run #3
- Fix pre-existing timeout in web-mode-onboarding integration test:
  - Add timeoutMs: 120_000 to launchPackagedWebHost call (was unbounded)
  - Raise AbortSignal.timeout on simple fetches 10s → 30s (under parallel load)
  - Raise overall test timeout 180s → 420s (budget: 120+60+30+30+120+30=390s)
- Log autoresearch run #4 and update lessons in autoresearch.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 11:01:43 +02:00
Mikael Hugo
b2bcb922de sf snapshot: uncommitted changes after 37m inactivity 2026-05-10 09:56:56 +02:00
Mikael Hugo
7e8e3aa846 sf snapshot: pre-dispatch, uncommitted changes after 30m inactivity 2026-05-10 09:19:51 +02:00
Mikael Hugo
e58e138457 feat(db): DB-only UAT verdicts — backfill on open, write on ASSESSMENT save, no file fallbacks
- sf-db.js: add backfillUatVerdicts(basePath) that scans ASSESSMENT/UAT_RESULT
  files for slices with no uat_verdict in DB and populates them on open
- dynamic-tools.js: call backfillUatVerdicts after openDatabase succeeds so
  all 3 repos with existing verdict files are covered on next launch
- workflow-tool-executors.js: call setSliceUatVerdict when saving ASSESSMENT
  at slice scope so future verdicts are written directly to DB
- workflow-helpers.js: remove all file fallbacks from checkNeedsRunUat;
  verdict check is DB-only (backfill guarantees DB is populated on open)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 08:49:45 +02:00
Mikael Hugo
6c113be473 fix(uat): treat ASSESSMENT file with verdict as completed UAT result
checkNeedsRunUat only checked for UAT_RESULT file, but the autonomous
runner writes ASSESSMENT files. This caused run-uat to dispatch 5x with
no verdict when only an ASSESSMENT (with verdict: PASS) existed.

Now ASSESSMENT file with any verdict counts as a completed UAT result,
stopping the infinite dispatch loop.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 08:32:21 +02:00
Mikael Hugo
d8c687702b fix(auto): cache lastCommandCtx from any SF command so Ctrl+Y works immediately
Previously required /autonomous first. Now any slash command (/next, /chat,
/clear etc.) caches the ExtensionCommandContext, so Ctrl+Y YOLO shortcut
works on first press after any command interaction.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 08:10:27 +02:00
Mikael Hugo
d56e68c789 fix(auto): revert YOLO shortcut to ctrl+y
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 07:59:10 +02:00
Mikael Hugo
60ee46aebb fix(auto): cache lastCommandCtx to survive shortcut-handler restarts
Shortcut handlers (registerShortcut) receive ExtensionContext which has
no newSession(). This caused autonomous mode started via Ctrl+Y to
always crash with 'newSession is not a function'.

- AutoSession.lastCommandCtx: new field that persists across stopAuto/reset
  so shortcut handlers can fall back to the last valid command context
- startAuto(): cache valid command ctx; fall back and notify user if ctx
  has no newSession; return early with actionable message if no cache yet
- dispatchHookUnit(): same guard — resolve hookCtx before s.cmdCtx = ctx
- run-unit.js: last-resort guard before newSession() call returns clean
  error category instead of TypeError
- steerable-autonomous-extension.js: rename ctrl+y → ctrl+alt+y to avoid
  conflict with terminal yank built-in

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 07:56:31 +02:00
Mikael Hugo
529138db9a sf snapshot: uncommitted changes after 33m inactivity 2026-05-10 07:54:07 +02:00
Mikael Hugo
7085ad850d refactor(tools): remove sf_ prefix from all remaining tool names
plan_milestone, plan_slice, plan_task, complete_task, complete_slice,
complete_milestone, skip_slice, replan_slice, reassess_roadmap,
validate_milestone, save_requirement, update_requirement, milestone_status

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 07:20:56 +02:00
Mikael Hugo
e7bd6a76b9 refactor(tools): improve description fields to be action-oriented and agent-facing
Rewrite all 13 renamed tool descriptions to follow Copilot tool conventions:
- Imperative verb opening
- One sentence on what it returns
- One sentence on when to use it
- No internal jargon or SF-specific acronyms

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-05-10 07:13:59 +02:00