Helix Code Grok Rules — Free Grok Rules Template
    Neura Market
    Neura Market
    /Grok
    Marketplace
    Directories
    Resources
    Grok
    ChatGPTChatGPTClaudeClaudeGeminiGeminiCursorCursorGrokGrokPerplexityPerplexityDeepSeekDeepSeekCoPilotCoPilotStable DiffusionStable DiffusionMidjourneyMidjourney
    OverviewRulesPromptsMCPsAgentsGamesBlogVideosGuidesCoursesCommunityTrending
    GrokRulesHelix Code Grok Rules
    Back to Rules

    Helix Code Grok Rules

    HelixDevelopment July 19, 2026
    0 copies 0 downloads
    Rule Content
    package main
    
    import (
    	"bufio"
    	"context"
    	"errors"
    	"flag"
    	"fmt"
    	"log"
    	"os"
    	"os/exec"
    	"os/signal"
    	"path/filepath"
    	"strings"
    	"sync"
    	"syscall"
    	"time"
    
    	"dev.helix.code/cmd/cli/i18n"
    	"dev.helix.code/internal/adapters/speckit_debate_adapter"
    	"dev.helix.code/internal/agent"
    	"dev.helix.code/internal/agent/subagent"
    	"dev.helix.code/internal/approval"
    	"dev.helix.code/internal/approvalwire"
    	"dev.helix.code/internal/autocommit"
    	"dev.helix.code/internal/checkpoint"
    	"dev.helix.code/internal/commands"
    	"dev.helix.code/internal/commands/builtin"
    	"dev.helix.code/internal/config"
    	"dev.helix.code/internal/continua"
    	"dev.helix.code/internal/hooks"
    	"dev.helix.code/internal/i18nwiring"
    	"dev.helix.code/internal/kilocode"
    	"dev.helix.code/internal/llm"
    	"dev.helix.code/internal/mcp"
    	"dev.helix.code/internal/notification"
    	taskplanner "dev.helix.code/internal/planner"
    	"dev.helix.code/internal/plantree"
    	"dev.helix.code/internal/pprofutil"
    	"dev.helix.code/internal/projectmemory"
    	"dev.helix.code/internal/rag"
    	"dev.helix.code/internal/render"
    	"dev.helix.code/internal/roocode"
    	"dev.helix.code/internal/secrets"
    	"dev.helix.code/internal/server"
    	"dev.helix.code/internal/session"
    	"dev.helix.code/internal/telemetry"
    	"dev.helix.code/internal/theme"
    	"dev.helix.code/internal/tools"
    	"dev.helix.code/internal/tools/askuser"
    	"dev.helix.code/internal/tools/browser"
    	"dev.helix.code/internal/tools/confirmation"
    	"dev.helix.code/internal/tools/permissions"
    	"dev.helix.code/internal/tools/permissions/sessionrules"
    	"dev.helix.code/internal/tools/persistence"
    	"dev.helix.code/internal/tools/sandbox"
    	"dev.helix.code/internal/tools/smartedit"
    	"dev.helix.code/internal/tools/task"
    	"dev.helix.code/internal/tools/worktree"
    	"dev.helix.code/internal/verifier"
    	"dev.helix.code/internal/version"
    	"dev.helix.code/internal/voice"
    	"dev.helix.code/internal/worker"
    	"dev.helix.code/internal/workflow"
    	"dev.helix.code/internal/workflow/planmode"
    	"dev.helix.code/internal/workspace"
    	speckitconfig "digital.vasic.helixspecifier/pkg/config"
    	"digital.vasic.helixspecifier/pkg/speckit"
    	speckittypes "digital.vasic.helixspecifier/pkg/types"
    	"github.com/sirupsen/logrus"
    	"go.uber.org/zap"
    )
    
    // translator resolves CONST-046 message IDs for every user-facing
    // string emitted by this CLI. Defaults to i18n.NoopTranslator{} (loud
    // message-ID echo) so unit tests + ad-hoc invocations remain obvious.
    // helix_code wires a real *i18nadapter.Translator at boot via
    // SetTranslator (round-131 §11.4 anti-bluff sweep, 2026-05-18).
    //
    // A package-level variable is the chosen DI seam because the legacy
    // CLI handler signatures (func(*CLI, context.Context) error) do not
    // support extra parameters without restructuring the handler tree —
    // global injection matches the cli's existing use of package-level
    // state (the flag.CommandLine, the *CLI receiver tree) and keeps the
    // migration minimally invasive.
    var translator i18n.Translator = i18n.NoopTranslator{}
    
    // SetTranslator wires a CONST-046-compliant Translator. Passing nil
    // resets to i18n.NoopTranslator{} (loud echo) — never silently
    // disables translation lookup (which would be a §11.4 PASS-bluff at
    // the i18n injection layer).
    func SetTranslator(t i18n.Translator) {
    	if t == nil {
    		translator = i18n.NoopTranslator{}
    		return
    	}
    	translator = t
    }
    
    // tr is the internal CONST-046 resolver used by every migrated
    // user-facing string emission in this file. It NEVER returns an error
    // to the caller — translation failures degrade to the message ID
    // itself (matching NoopTranslator behaviour) so production output
    // remains loud + obvious instead of silently empty.
    func tr(ctx context.Context, msgID string, data map[string]any) string {
    	if translator == nil {
    		translator = i18n.NoopTranslator{}
    	}
    	out, err := translator.T(ctx, msgID, data)
    	if err != nil || out == "" {
    		return msgID
    	}
    	return out
    }
    
    // trc is the CONST-046 resolver for strings that are needed at cobra
    // command-construction time (Short / Long descriptions, flag-help text)
    // — points where no request-scoped context.Context is available. It
    // resolves against context.Background() through the same package-level
    // translator the runtime tr() helper uses, so cobra metadata is just as
    // locale-aware as runtime output. The package-level translator is wired
    // via SetTranslator before main() builds the cobra tree, so trc() sees a
    // real Translator in production and i18n.NoopTranslator{} (loud message-
    // ID echo) in unit tests that build commands without wiring one.
    func trc(msgID string, data map[string]any) string {
    	return tr(context.Background(), msgID, data)
    }
    
    // defaultConfigPathFromEnv resolves the on-disk wizard config path using the
    // supplied env-lookup. Mirrors internal/llm.defaultWizardConfigPath but lives
    // here so cmd/cli does not need to import an unexported helper. Honours
    // XDG_CONFIG_HOME, falls back to $HOME/.config/helixcode/llm.yaml.
    func defaultConfigPathFromEnv(env func(string) string) string {
    	if xdg := strings.TrimSpace(env("XDG_CONFIG_HOME")); xdg != "" {
    		return filepath.Join(xdg, "helixcode", "llm.yaml")
    	}
    	home := strings.TrimSpace(env("HOME"))
    	if home == "" {
    		return filepath.Join(".", ".config", "helixcode", "llm.yaml")
    	}
    	return filepath.Join(home, ".config", "helixcode", "llm.yaml")
    }
    
    // loadProviderConfigFromDisk reads the wizard-written YAML at
    // $XDG_CONFIG_HOME/helixcode/llm.yaml (or $HOME/.config fallback) and returns
    // the persisted ProviderType + ProviderConfigEntry. Missing-file is reported
    // as an os.ErrNotExist-wrapped error so callers can fall through to other
    // selection sources (env / wizard prompt). Unmarshal failures are surfaced as
    // non-sentinel errors.
    //
    // Anti-bluff anchor: this is a real-disk reader. There is no stubbed config
    // path or fake "no config means everything is fine" mode — callers handle
    // the missing file with errors.Is(err, os.ErrNotExist).
    func loadProviderConfigFromDisk(envLookup func(string) string) (string, llm.ProviderConfigEntry, error) {
    	path := defaultConfigPathFromEnv(envLookup)
    	res, err := llm.LoadWizardConfig(path)
    	if err != nil {
    		return "", llm.ProviderConfigEntry{}, err
    	}
    	if res == nil {
    		return "", llm.ProviderConfigEntry{}, fmt.Errorf("loadProviderConfigFromDisk: nil result from %s", path)
    	}
    	return string(res.ProviderType), res.ConfigEntry, nil
    }
    
    // buildSubagentLLMProvider constructs the LLM provider used by a subagent
    // helper child (P1-F15-T08). Wired into main() via subagent.RunAsSubagent.
    //
    // The child does NOT replay the parent's full F12 bootstrap (no flag parsing,
    // no friendly Stderr hints): it reads the same config-file + HELIX_LLM_PROVIDER
    // env that the parent uses, resolves the type via llm.Select, and constructs
    // the cloud provider. On any failure (no config, env-only with missing creds,
    // construction error) it falls back to the local Ollama default — matching the
    // parent's "keep working for non-LLM paths" stance.
    //
    // Pragmatic v1: we read env+config-file only; the child does not see the
    // parent's --provider flag because the spawner re-exec's the helper without
    // argv. This is documented in the spec § 4.2; if a future task plumbs
    // --provider through the env protocol, this function should consult it.
    //
    // Anti-bluff: this function MUST construct a real provider — never a stub or
    // the FakeLLMProvider (which lives in the subagent package as a test-only
    // type with the "fake-test-only" sentinel ProviderType).
    func buildSubagentLLMProvider(ctx context.Context) (llm.Provider, error) {
    	configProviderName, configEntry, configErr := loadProviderConfigFromDisk(os.Getenv)
    	if configErr != nil && !errors.Is(configErr, os.ErrNotExist) {
    		// Config read failed for a real reason; surface it but keep going so
    		// we still try env / default.
    		log.Printf("subagent: config load failed (continuing): %v", configErr)
    	}
    	selectorInput := llm.SelectorInput{
    		Flag:   "",
    		Env:    os.Getenv("HELIX_LLM_PROVIDER"),
    		Config: configProviderName,
    	}
    	ptype, selErr := llm.Select(selectorInput)
    	switch {
    	case errors.Is(selErr, llm.ErrNoProviderConfigured):
    		// Fall through to default Ollama.
    	case selErr != nil:
    		// Unknown provider name — surface and fall back to default rather
    		// than aborting the subagent run.
    		log.Printf("subagent: provider selector error: %v (falling back to default)", selErr)
    	default:
    		entry := configEntry
    		entry.Type = ptype
    		cloud, cErr := llm.NewCloudProvider(ptype, entry)
    		if cErr == nil && cloud != nil {
    			return cloud, nil
    		}
    		log.Printf("subagent: failed to construct cloud provider %q (%v); falling back to local default", ptype, cErr)
    	}
    
    	// Default: local Ollama on the standard port. Mirrors NewCLI()'s default.
    	provider, err := llm.NewOllamaProvider(llm.OllamaConfig{
    		DefaultModel: "llama3.2",
    		BaseURL:      "http://localhost:11434",
    	})
    	if err != nil {
    		return nil, fmt.Errorf("subagent: default Ollama provider construction failed: %w", err)
    	}
    	return provider, nil
    }
    
    // CLI represents the command-line interface
    type CLI struct {
    	workerPool         *worker.SSHWorkerPool
    	llmProvider        llm.Provider
    	notificationEngine *notification.NotificationEngine
    	verifierAdapter    *verifier.Adapter
    	permissionMode     string
    	permissionsEngine  *permissions.Engine
    	// sessionRuleStore is the single, shared, session-scoped permission-rule
    	// store. It is the SAME instance consulted by the live permissions.Engine
    	// at decision time AND mutated by the `/permissions add|remove` slash
    	// command, so a rule added in-session takes effect on the live gate
    	// immediately (no restart). See initPermissions + the slash-command wiring.
    	sessionRuleStore   *sessionrules.Store
    	persistenceManager *persistence.Manager
    	worktreeManager    *worktree.Manager
    	sessionMgr         *session.Manager
    	toolRegistry       *tools.ToolRegistry
    	commandRegistry    *commands.Registry
    	mcpManager         *mcp.Manager
    	browserManager     *browser.BrowserManager       // F23: cline-style single-session browser façade
    	memoryRegistry     *projectmemory.MemoryRegistry // F24: codex-style project memory + hot-reload
    	hooksLoaded        int                           // count of hooks loaded at startup (for diagnostics)
    
    	// --- Speed programme P1-T03: lazy CLI startup (R1 B01/B13/B14/B18) ---
    	//
    	// The eager monolith in an earlier revision of Run() built ~25 subsystems
    	// (telemetry, permissions, worktree git shell-out, hooks YAML, tool
    	// registry, LSP exec.LookPath sweep, MCP server spawn, sandbox detection,
    	// the F22-F30 tool/slash-command suite, the F15 subagent manager, …)
    	// sequentially BEFORE the first user action — so even `--help`,
    	// `--list-models` and `--command` paid the full bootstrap cost.
    	//
    	// The bootstrap is now split into sync.Once-guarded getters. Each getter
    	// constructs its subsystem exactly once, lazily, on first access; commands
    	// that do not need a subsystem never trigger its construction. Construction
    	// ORDER is preserved inside each getter (getter-calls-getter where there is
    	// a genuine dependency — e.g. the heavy-subsystem getter calls telemetry()
    	// first because the tool-registry instrumentation needs the provider).
    	//
    	// cleanups holds the deferred teardown closures that the eager monolith
    	// registered with `defer` inside Run(). They are drained by runCleanups()
    	// via a single `defer` in Run() so a lazily-constructed subsystem is still
    	// torn down at process exit, regardless of WHICH command triggered it.
    	telemetryOnce  sync.Once
    	telemetryProv  telemetry.TelemetryProvider
    	telemetryCfg   telemetry.TelemetryConfig
    	llmOnce        sync.Once
    	subsystemsOnce sync.Once
    	subsystemsErr  error
    	cleanups       []func()
    	cleanupsMu     sync.Mutex
    
    	// constructionCount records how many times each lazy getter actually ran
    	// its sync.Once body. It exists so integration tests can prove a command
    	// that needs subsystem X triggers exactly one construction of X, and a
    	// command that does NOT need X triggers zero. Key: subsystem name.
    	constructionCount map[string]int
    	constructionMu    sync.Mutex
    
    	// flagState carries the parsed command-line flag values that the lazy
    	// subsystem getters consume. Run() populates it immediately after
    	// flag.Parse(); ensureSubsystems / ensureLLMProvider read it. This keeps
    	// the getters free of a *flag dependency so they can be invoked from any
    	// command handler (e.g. handleInteractive) without a flag receiver.
    	flagState cliFlagState
    }
    
    // cliFlagState is the parsed-flag snapshot the lazy getters need. Populated
    // once by Run() after flag.Parse().
    type cliFlagState struct {
    	approvalFlag      string
    	providerFlag      string
    	resumeFlag        bool
    	continueFlag      bool
    	resumeSessionFlag string
    }
    
    // recordConstruction increments the lazy-construction probe counter for name.
    // It is the anti-bluff seam P1-T03's integration tests assert against: a
    // getter that constructed must show count==1; a subsystem never touched must
    // show count==0.
    func (c *CLI) recordConstruction(name string) {
    	c.constructionMu.Lock()
    	defer c.constructionMu.Unlock()
    	if c.constructionCount == nil {
    		c.constructionCount = make(map[string]int)
    	}
    	c.constructionCount[name]++
    }
    
    // ConstructionCount returns how many times the named lazy getter ran its
    // sync.Once body. Used by P1-T03 tests (same package) to prove laziness.
    func (c *CLI) ConstructionCount(name string) int {
    	c.constructionMu.Lock()
    	defer c.constructionMu.Unlock()
    	return c.constructionCount[name]
    }
    
    // addCleanup registers a teardown closure to run at Run() exit. Safe for
    // concurrent use — the F09/F10 watcher goroutines never call it, but the
    // background subagent / workspace wiring may, so the mutex is cheap insurance.
    func (c *CLI) addCleanup(fn func()) {
    	if fn == nil {
    		return
    	}
    	c.cleanupsMu.Lock()
    	defer c.cleanupsMu.Unlock()
    	c.cleanups = append(c.cleanups, fn)
    }
    
    // runCleanups drains the registered teardown closures in LIFO order — the
    // same order Go's `defer` stack would have unwound them in the old eager
    // monolith. Called exactly once via a single `defer` in Run().
    func (c *CLI) runCleanups() {
    	c.cleanupsMu.Lock()
    	fns := c.cleanups
    	c.cleanups = nil
    	c.cleanupsMu.Unlock()
    	for i := len(fns) - 1; i >= 0; i-- {
    		fns[i]()
    	}
    }
    
    // NewCLI creates a new CLI instance
    func NewCLI() *CLI {
    	// Initialize LLM provider from config - use Ollama on port 11434.
    	// Speed programme P1-T02: NewOllamaProvider no longer performs a
    	// blocking /api/tags discovery round-trip here — model discovery is
    	// deferred to first real use (GetModels/GetHealth), so every CLI
    	// start (including `--help`) is freed of one synchronous Ollama call.
    	llmProvider, _ := llm.NewOllamaProvider(llm.OllamaConfig{
    		DefaultModel: "llama3.2",               // Use Ollama's model name
    		BaseURL:      "http://localhost:11434", // Ollama default port
    	})
    
    	// Initialize LLMsVerifier subsystem if config is available.
    	// Speed programme P2-T07: config.Get() loads + caches the config once
    	// per process (sync.Once), so repeated NewCLI() / subagent construction
    	// reuses the same *Config instead of re-reading YAML and re-churning
    	// viper — and is race-free (closes the P0-T02 concurrent-map-write bug).
    	var verifierAdapter *verifier.Adapter
    	cfg, err := config.Get()
    	if err == nil && cfg.Verifier != nil && cfg.Verifier.Enabled {
    		vResult, vErr := verifier.Bootstrap(cfg.Verifier)
    		if vErr == nil && vResult != nil {
    			verifierAdapter = vResult.Adapter
    		}
    	}
    
    	return &CLI{
    		workerPool:         worker.NewSSHWorkerPool(true),
    		llmProvider:        llmProvider,
    		notificationEngine: notification.NewNotificationEngine(),
    		verifierAdapter:    verifierAdapter,
    	}
    }
    
    // initPermissions bootstraps the permissions.Engine with rules loaded from
    // ~/.helixcode/permissions.yaml (user scope) and <cwd>/.helixcode/permissions.yaml
    // (project scope), then registers a default confirmation.Policy with pe.
    //
    // CONCERN (T10 / Phase 3): pe is a locally constructed PolicyEngine that is not
    // yet wired into the tool execution path. The --permission-mode flag is parsed
    // and validated here, and the Engine is ready, but actual tool calls are not yet
    // gated by it. T10 will introduce the `permissions` subcommand; Phase 3 will
    // thread pe through the session/tool dispatcher so denies block execution.
    func (c *CLI) initPermissions(ctx context.Context, pe *confirmation.PolicyEngine) error {
    	if c.permissionMode != "" && !permissions.IsValidMode(c.permissionMode) {
    		return fmt.Errorf("invalid --permission-mode %q (valid: %v)", c.permissionMode, permissions.ValidModes)
    	}
    	home, err := os.UserHomeDir()
    	if err != nil {
    		return fmt.Errorf("resolving user home dir: %w", err)
    	}
    	cwd, err := os.Getwd()
    	if err != nil {
    		return fmt.Errorf("resolving cwd: %w", err)
    	}
    	loader := &permissions.FileLoader{
    		UserPath:    filepath.Join(home, ".helixcode", "permissions.yaml"),
    		ProjectPath: filepath.Join(cwd, ".helixcode", "permissions.yaml"),
    		Mode:        c.permissionMode,
    	}
    	// Construct the single shared session-rule store and wire it into the live
    	// engine as the session-rule overlay (consulted FIRST at decision time). The
    	// SAME store instance is handed to the `/permissions add|remove` slash
    	// command (see the command-registry wiring), so a rule added in-session
    	// gates live tool execution immediately. sessionrules.Store.Decide is
    	// fail-closed on a corrupt session rule set.
    	if c.sessionRuleStore == nil {
    		c.sessionRuleStore = sessionrules.New()
    	}
    	eng, err := permissions.NewEngine(ctx, loader, pe,
    		permissions.WithSessionDecider(c.sessionRuleStore.Decide))
    	if err != nil {
    		return fmt.Errorf("initialising permissions engine: %w", err)
    	}
    	c.permissionsEngine = eng
    	return nil
    }
    
    // initPersistence bootstraps the persistence.Manager rooted at the current
    // working directory. Large tool outputs (>50 KB) will be written under
    // <cwd>/.helix/tool-results/ so the context window is not flooded.
    // A background goroutine prunes files older than DefaultMaxAge (7 days).
    func (c *CLI) initPersistence() error {
    	cwd, err := os.Getwd()
    	if err != nil {
    		return fmt.Errorf("resolving cwd for persistence: %w", err)
    	}
    	c.persistenceManager = persistence.NewManager(cwd)
    	go func() {
    		if err := c.persistenceManager.CleanupOld(persistence.DefaultMaxAge); err != nil {
    			log.Printf("WARN persistence cleanup: %v", err)
    		}
    	}()
    	return nil
    }
    
    // initWorktree bootstraps the worktree.Manager with repoRoot resolved via
    // `git rev-parse --show-toplevel`, falling back to os.Getwd() if the cwd
    // is not a git repo.
    func (c *CLI) initWorktree(ctx context.Context) error {
    	cwd, err := os.Getwd()
    	if err != nil {
    		return fmt.Errorf("resolving cwd for worktree: %w", err)
    	}
    	repoRoot := cwd
    	if root, err := worktreeRevParseToplevel(ctx, cwd); err == nil {
    		repoRoot = root
    	}
    	c.worktreeManager = worktree.NewManager(repoRoot)
    	return nil
    }
    
    // initHooks loads ~/.helixcode/hooks.yaml + <cwd>/.helixcode/hooks.yaml,
    // wraps each enabled entry in a shell-runner HookFunc, and registers it
    // with the session.Manager.hooksManager. Errors fail-fast.
    func (c *CLI) initHooks(ctx context.Context, sessionMgr *session.Manager) error {
    	home, err := os.UserHomeDir()
    	if err != nil {
    		return fmt.Errorf("resolving home dir for hooks: %w", err)
    	}
    	cwd, err := os.Getwd()
    	if err != nil {
    		return fmt.Errorf("resolving cwd for hooks: %w", err)
    	}
    	loader := &hooks.FileLoader{
    		UserPath:    filepath.Join(home, ".helixcode", "hooks.yaml"),
    		ProjectPath: filepath.Join(cwd, ".helixcode", "hooks.yaml"),
    	}
    	hs, sources, err := loader.Load(ctx)
    	if err != nil {
    		return fmt.Errorf("loading hooks: %w", err)
    	}
    	hm := sessionMgr.GetHooksManager()
    	for _, h := range hs {
    		scriptPath := h.Metadata["script"]
    		h.Handler = hooks.NewShellRunner(scriptPath, h.Timeout)
    		if err := hm.Register(h); err != nil {
    			return fmt.Errorf("registering hook %q: %w", h.ID, err)
    		}
    	}
    	c.hooksLoaded = len(hs)
    	if len(sources) > 0 {
    		log.Printf("hooks: loaded %d hook(s) from %v", len(hs), sources)
    	}
    	return nil
    }
    
    // sessionStoreBaseDir resolves the on-disk root for F11 session transcripts.
    // Resolution order (XDG Base Directory Specification):
    //  1. $XDG_DATA_HOME/helixcode/sessions/   (when $XDG_DATA_HOME is set and absolute)
    //  2. $HOME/.local/share/helixcode/sessions/  (XDG default)
    //  3. ./.helixcode-sessions/  (last-resort fallback when $HOME is also unset)
    //
    // The directory is not created here; TranscriptStore creates per-session
    // subdirectories on first append.
    func sessionStoreBaseDir() string {
    	if xdg := os.Getenv("XDG_DATA_HOME"); xdg != "" && filepath.IsAbs(xdg) {
    		return filepath.Join(xdg, "helixcode", "sessions")
    	}
    	if home, err := os.UserHomeDir(); err == nil && home != "" {
    		return filepath.Join(home, ".local", "share", "helixcode", "sessions")
    	}
    	return filepath.Join(".", ".helixcode-sessions")
    }
    
    // worktreeRevParseToplevel is a tiny shim to avoid leaking the worktree
    // package's internal helpers; it shells out to git directly.
    func worktreeRevParseToplevel(ctx context.Context, cwd string) (string, error) {
    	cmd := exec.CommandContext(ctx, "git", "rev-parse", "--show-toplevel")
    	cmd.Dir = cwd
    	out, err := cmd.CombinedOutput()
    	if err != nil {
    		return "", err
    	}
    	return strings.TrimSpace(string(out)), nil
    }
    
    // telemetry lazily constructs the OTel telemetry provider exactly once.
    //
    // Speed programme P1-T03: in the old eager monolith this ran unconditionally
    // on every CLI start. It is cheap when no OTEL_* env vars are set (the SDK
    // builds a noop provider) but it is still pure overhead for `--help` /
    // `--command` / `--list-models` which never emit a span. It is now lazy:
    // constructed on first access by ensureLLMProvider (the traced-LLM decorator
    // needs it) and ensureSubsystems (the tool-registry instrumentation needs it).
    //
    // Construction never returns an error to the caller — failure degrades to a
    // noop provider, matching the old monolith's behaviour. The telemetry-shutdown
    // teardown is registered via addCleanup so it still runs at Run() exit.
    func (c *CLI) telemetry() telemetry.TelemetryProvider {
    	c.telemetryOnce.Do(func() {
    		c.recordConstruction("telemetry")
    		cfg, cfgErr := telemetry.LoadConfigFromEnv(os.Getenv)
    		if cfgErr != nil {
    			log.Printf("telemetry: config invalid (continuing with noop): %v", cfgErr)
    			cfg = telemetry.TelemetryConfig{Enabled: false, Exporter: telemetry.ExporterNoop}
    		}
    		c.telemetryCfg = cfg
    		prov, provErr := telemetry.NewTelemetryProvider(cfg, zap.NewNop())
    		if provErr != nil {
    			log.Printf("telemetry: provider construction failed (continuing with noop): %v", provErr)
    		}
    		if prov == nil {
    			// Defence in depth — NewTelemetryProvider should never return nil,
    			// but a nil here would nil-panic the decorators below.
    			prov, _ = telemetry.NewTelemetryProvider(
    				telemetry.TelemetryConfig{Enabled: false, Exporter: telemetry.ExporterNoop},
    				zap.NewNop(),
    			)
    		}
    		c.telemetryProv = prov
    		c.addCleanup(func() {
    			shutdownTimeout := c.telemetryCfg.ShutdownTimeout
    			if shutdownTimeout <= 0 {
    				shutdownTimeout = telemetry.DefaultShutdownTimeout
    			}
    			shutdownCtx, cancel := context.WithTimeout(context.Background(), shutdownTimeout)
    			defer cancel()
    			if err := c.telemetryProv.Shutdown(shutdownCtx); err != nil {
    				log.Printf("telemetry: shutdown error: %v", err)
    			}
    		})
    		log.Printf("telemetry: initialised (exporter=%s)", string(prov.Exporter()))
    	})
    	return c.telemetryProv
    }
    
    // ensureLLMProvider lazily resolves the cloud LLM provider override (F12) and
    // wraps the active provider with the telemetry decorator (P1-F16-T10), exactly
    // once.
    //
    // Speed programme P1-T03: this is needed only by commands that actually talk
    // to an LLM — `--prompt` (handleGenerate), `--list-models` (handleListModels)
    // and the interactive REPL. `--command`, `--health`, `--list-workers`,
    // `--notify`, `--qa-*` and `--help` never call it, so they skip the F12
    // config-file read + any cloud-provider construction.
    //
    // providerFlag is the parsed value of the --provider flag. Errors are
    // non-fatal except an explicitly unknown --provider value (a fixable user
    // error), which is returned so Run() can surface a non-zero exit.
    func (c *CLI) ensureLLMProvider(ctx context.Context, providerFlag string) error {
    	var fatal error
    	c.llmOnce.Do(func() {
    		c.recordConstruction("llmProvider")
    		// F12: resolve cloud LLM provider via flag > env > config-file.
    		configProviderName, configEntry, configErr := loadProviderConfigFromDisk(os.Getenv)
    		if configErr != nil && !errors.Is(configErr, os.ErrNotExist) {
    			log.Printf("F12 provider: config load failed (continuing without): %v", configErr)
    		}
    		selectorInput := llm.SelectorInput{
    			Flag:   providerFlag,
    			Env:    os.Getenv("HELIX_LLM_PROVIDER"),
    			Config: configProviderName,
    		}
    		ptype, selErr := llm.Select(selectorInput)
    		switch {
    		case errors.Is(selErr, llm.ErrNoProviderConfigured):
    			// Friendly hint, then keep the default provider.
    			fmt.Fprintln(os.Stderr, tr(ctx, "cli_f12_no_cloud_provider", nil))
    		case selErr != nil:
    			// User typed an unknown value -> fail loudly with a non-zero exit.
    			fatal = fmt.Errorf("F12 provider: %w", selErr)
    			return
    		default:
    			entry := configEntry
    			entry.Type = ptype
    			cloud, cErr := llm.NewCloudProvider(ptype, entry)
    			if cErr != nil {
    				fmt.Fprintln(os.Stderr, tr(ctx, "cli_f12_construct_failed", map[string]any{
    					"Provider": fmt.Sprintf("%q", ptype),
    					"Error":    fmt.Sprintf("%v", cErr),
    				}))
    			} else if cloud != nil {
    				c.llmProvider = cloud
    				fmt.Fprintf(os.Stderr, "F12 provider: using %q\n", ptype)
    			}
    		}
    
    		// P1-F16-T10: wrap the resolved provider with the telemetry decorator.
    		// Order anchor preserved from the old monolith — this runs AFTER F12
    		// settles c.llmProvider and BEFORE any caller of c.llmProvider observes
    		// it. When telemetry is in noop mode the decorator is pass-through.
    		if c.llmProvider != nil {
    			tracedLLM, traceErr := telemetry.NewTracedLLMProvider(c.llmProvider, c.telemetry())
    			if traceErr != nil {
    				log.Printf("telemetry: LLM decorator construction failed (using undecorated provider): %v", traceErr)
    			} else {
    				c.llmProvider = tracedLLM
    			}
    		}
    	})
    	return fatal
    }
    
    // ensureSubsystems lazily constructs the heavy interactive-session subsystem
    // cluster exactly once: permissions, persistence, worktree, hooks, the tool
    // registry + the full F07-F30 tool/slash-command suite, LSP, MCP, sandbox,
    // approval, the F15 subagent manager and the F09/F10 markdown-command + skill
    // loaders/watchers.
    //
    // Speed programme P1-T03 (R1 B01/B13/B14/B18): this is the hundreds-of-
    // milliseconds cost the old eager monolith paid on EVERY CLI start. It is now
    // gated behind a sync.Once and is invoked ONLY by the interactive REPL path
    // (handleInteractive) — the only command that genuinely needs the slash-
    // command registry + agent tool surface. Short commands (`--list-models`,
    // `--command`, `--health`, `--qa-*`, `--help`, …) skip the entire cluster:
    // no worktree git shell-out, no hooks YAML read, no LSP exec.LookPath sweep,
    // no MCP server spawn, no sandbox detection.
    //
    // Construction ORDER inside this getter is identical to the old monolith's
    // statement order — every genuine dependency (sessionMgr before hooks,
    // toolReg before the F-feature tool registrations, cmdRegistry before the
    // slash-command registrations, telemetry before tool instrumentation) is
    // preserved. Every `defer` the monolith used is converted to addCleanup so
    // teardown still happens at Run() exit.
    func (c *CLI) ensureSubsystems(ctx context.Context) error {
    	c.subsystemsOnce.Do(func() {
    		c.recordConstruction("subsystems")
    		// The F22 auto-committer and the F15 subagent manager both capture
    		// c.llmProvider by value at construction time, so the F12 cloud
    		// override + telemetry wrapper MUST settle before this getter runs —
    		// getter-calls-getter preserves the old monolith's ordering anchor.
    		if err := c.ensureLLMProvider(ctx, c.flagState.providerFlag); err != nil {
    			c.subsystemsErr = err
    			return
    		}
    		c.subsystemsErr = c.buildSubsystems(ctx)
    	})
    	return c.subsystemsErr
    }
    
    // buildSubsystems constructs the heavy interactive-session subsystem cluster.
    // It is the body of ensureSubsystems' sync.Once — never call it directly; it
    // is unguarded and would double-register tools/slash-commands. See
    // ensureSubsystems for the laziness + ordering contract.
    func (c *CLI) buildSubsystems(ctx context.Context) error {
    	// HXC-036: wire the CONST-046 boot-time translators BEFORE any subsystem
    	// that emits user-facing prompts is constructed (the ask_user prompter and
    	// the approval gate below both render localized text). Without this, those
    	// packages run on their NoopTranslator{} default and users see raw
    	// message-ID keys (e.g. "askuser_prompt_invalid_choice_hint") instead of
    	// resolved + interpolated text — a CONST-046 / §11.4 regression. A failed
    	// translator build is logged but non-fatal: the loud message-ID echo is a
    	// degraded-but-honest fallback, never a silent swallow.
    	if err := i18nwiring.WireAll(); err != nil {
    		log.Printf("i18n: boot-time translator wiring failed (prompts degrade to message-ID echo): %v", err)
    	}
    
    	// Bootstrap permissions engine. A locally constructed PolicyEngine is used
    	// here; T10/Phase 3 will thread it into the tool dispatcher so deny rules
    	// actually block execution.
    	policyEngine := confirmation.NewPolicyEngine()
    	if err := c.initPermissions(ctx, policyEngine); err != nil {
    		return fmt.Errorf("permissions bootstrap: %w", err)
    	}
    	if err := c.initPersistence(); err != nil {
    		return fmt.Errorf("persistence init: %w", err)
    	}
    	if err := c.initWorktree(ctx); err != nil {
    		return fmt.Errorf("worktree init: %w", err)
    	}
    
    	// Construct the session manager (carries the hooks.Manager inside it).
    	sessionMgr := session.NewManager()
    	c.sessionMgr = sessionMgr
    
    	// Load hooks from ~/.helixcode/hooks.yaml + <cwd>/.helixcode/hooks.yaml
    	// and register them with the session's hooks manager.
    	if err := c.initHooks(ctx, sessionMgr); err != nil {
    		return fmt.Errorf("hooks init: %w", err)
    	}
    
    	// Construct the tool registry and wire the hooks manager so that
    	// BeforeToolCall / AfterToolCall / BeforeBash / AfterBash actually fire.
    	toolReg, err := tools.NewToolRegistry(tools.DefaultRegistryConfig())
    	if err != nil {
    		return fmt.Errorf("tool registry init: %w", err)
    	}
    	toolReg.SetHooksManager(sessionMgr.GetHooksManager())
    	// Wire F02 permissions engine into the confirmation pipeline.
    	toolReg.GetConfirmation().SetPolicyEngine(policyEngine)
    	c.toolRegistry = toolReg
    
    	// P1-F19-T05: ask_user tool registration.
    	askUserPrompter, askUserErr := askuser.NewStdinPrompter(askuser.StdinPrompterOptions{})
    	if askUserErr != nil {
    		log.Printf("ask_user: stdinPrompter construction failed; tool unavailable: %v", askUserErr)
    	} else {
    		toolReg.Register(askuser.NewAskUserTool(askUserPrompter))
    		log.Printf("ask_user: wired (interactive=auto-detect, max-retries=%d, timeout=%s)",
    			askuser.DefaultMaxRetries, askuser.DefaultTimeout)
    	}
    
    	// F13: LSP manager — curated 5-server allowlist filtered by exec.LookPath
    	// at startup.
    	curatedLSPSpecs := tools.CuratedServerSpecs()
    	detectedLSPSpecs := tools.DetectAvailableServers(curatedLSPSpecs)
    	lspWorkingDir, err := os.Getwd()
    	if err != nil {
    		return fmt.Errorf("lsp manager: resolving cwd: %w", err)
    	}
    	lspManager := tools.NewLSPManager(lspWorkingDir, detectedLSPSpecs, zap.NewNop())
    	c.addCleanup(func() {
    		shutCtx, shutCancel := context.WithTimeout(context.Background(), 5*time.Second)
    		defer shutCancel()
    		_ = lspManager.Shutdown(shutCtx)
    	})
    	toolReg.SetLSPManager(lspManager)
    
    	// Construct the MCP Manager, load merged config, start alwaysLoad servers.
    	mcpMgr := mcp.NewManager()
    	{
    		var userMCPPath string
    		if configHome, err := os.UserConfigDir(); err == nil {
    			userMCPPath = filepath.Join(configHome, "helixcode", "mcp.yml")
    		}
    		projMCPPath := ".helixcode/mcp.yml"
    		cfg, cfgErr := mcp.LoadMerged(userMCPPath, projMCPPath)
    		if cfgErr != nil {
    			log.Printf("mcp: config load failed: %v (continuing without MCP)", cfgErr)
    		} else {
    			mcpMgr.SetConfig(cfg)
    			if startErr := mcpMgr.Start(ctx); startErr != nil {
    				log.Printf("mcp: start failed: %v", startErr)
    			}
    		}
    	}
    	c.mcpManager = mcpMgr
    	c.addCleanup(func() { _ = mcpMgr.Close() })
    
    	// Wire MCP-discovered tools into the tool registry as "<server>:<tool>".
    	toolReg.RegisterMCPManager(mcpMgr)
    
    	// Build the commands registry and register all builtin slash commands.
    	cmdRegistry := commands.NewRegistry()
    	if regErr := builtin.RegisterBuiltinCommandsWithMCP(cmdRegistry, mcpMgr); regErr != nil {
    		log.Printf("mcp: register slash command failed: %v", regErr)
    	}
    	c.commandRegistry = cmdRegistry
    
    	// Share the live session-rule store with the /permissions command so rules
    	// added via `/permissions add|remove` reach the SAME store the permissions
    	// Engine consults at decision time (initPermissions, run earlier, wired the
    	// engine via WithSessionDecider(c.sessionRuleStore.Decide)). Without this the
    	// command and the engine hold two unconnected stores and a session rule never
    	// gates live execution. Replaces the builtin-registered default (own store).
    	if c.sessionRuleStore != nil {
    		cmdRegistry.Unregister("permissions")
    		if regErr := cmdRegistry.Register(commands.NewPermissionsCommandWithStore(c.sessionRuleStore)); regErr != nil {
    			log.Printf("permissions: register store-shared slash command failed: %v", regErr)
    		}
    	}
    
    	// F07: background task manager.
    	bgMgr := workflow.NewBackgroundManager(zap.NewNop(), workflow.ManagerConfig{})
    	c.addCleanup(func() { bgMgr.Close() })
    	toolReg.SetBackgroundManager(bgMgr)
    	toolReg.RegisterTaskTools(bgMgr)
    	if regErr := cmdRegistry.Register(commands.NewTasksCommand(bgMgr)); regErr != nil {
    		log.Printf("tasks: register slash command failed: %v", regErr)
    	}
    
    	// F08: plan-mode gate.
    	modeCtrl := planmode.NewModeController()
    	planner := planmode.NewDefaultPlanner()
    	gate := planmode.NewToolGate(modeCtrl, planner)
    	toolReg.SetPlanModeGate(gate)
    	toolReg.Register(tools.NewEnterPlanModeTool(modeCtrl))
    	toolReg.Register(tools.NewExitPlanModeTool(modeCtrl))
    	if regErr := cmdRegistry.Register(commands.NewPlanCommand(planner, modeCtrl)); regErr != nil {
    		log.Printf("plan: register slash command failed: %v", regErr)
    	}
    
    	// F13: register /lsp slash command.
    	if regErr := cmdRegistry.Register(commands.NewLSPCommand(lspManager, curatedLSPSpecs)); regErr != nil {
    		log.Printf("lsp: register slash command failed: %v", regErr)
    	}
    
    	// F14: sandbox manager + shell_sandboxed tool + /sandbox slash command.
    	sandboxConfigPath := sandbox.DefaultConfigPath(os.Getenv)
    	sandboxConfig, sandboxCfgErr := sandbox.LoadSandboxConfig(sandboxConfigPath)
    	if sandboxCfgErr != nil {
    		log.Printf("sandbox: config load failed (using defaults): %v", sandboxCfgErr)
    		sandboxConfig = sandbox.DefaultSandboxConfig()
    	}
    	sandboxWorkDir, err := os.Getwd()
    	if err != nil {
    		return fmt.Errorf("sandbox manager: resolving cwd: %w", err)
    	}
    	sandboxMgr, sandboxCaps, sbErr := sandbox.NewSandboxManagerFromDetector(sandboxWorkDir, sandboxConfig, zap.NewNop())
    	if sbErr != nil {
    		log.Printf("sandbox: manager init failed: %v", sbErr)
    	} else {
    		log.Printf("sandbox: backend=%s reason=%q",
    			sandboxCaps.SelectedBackend.String(), sandboxCaps.UnavailableReason)
    	}
    	if sandboxMgr != nil {
    		toolReg.Register(sandbox.NewSandboxedShellTool(sandboxMgr))
    		if regErr := cmdRegistry.Register(commands.NewSandboxCommand(sandboxMgr)); regErr != nil {
    			log.Printf("sandbox: register slash command failed: %v", regErr)
    		}
    	}
    
    	// F21: approval gate.
    	sandboxAvailable := sandboxMgr != nil && sandboxMgr.SelectedBackend() != sandbox.BackendNone
    	approvalSelectorInput := approval.SelectorInput{
    		Flag:       c.flagState.approvalFlag,
    		Env:        os.Getenv(approval.EnvVarName),
    		ConfigPath: approval.DefaultConfigPath(os.Getenv),
    	}
    	approvalMode, approvalSource, approvalSelErr := approval.Select(approvalSelectorInput)
    	if approvalSelErr != nil {
    		log.Printf("approval: selector reported parse errors (using mode=%s source=%s): %v",
    			approvalMode, approvalSource, approvalSelErr)
    	}
    	approvalMgrOpts := approval.ApprovalManagerOptions{
    		InitialMode:      approvalMode,
    		Source:           approvalSource,
    		SandboxAvailable: sandboxAvailable,
    		PauseDangerous:   2 * time.Second,
    	}
    	if askUserPrompter != nil {
    		approvalMgrOpts.Responder = &approvalwire.AskUserYesNoPrompter{Inner: askUserPrompter}
    	}
    	approvalMgr, approvalMgrErr := approval.NewApprovalManager(approvalMgrOpts)
    	if approvalMgrErr != nil {
    		log.Printf("approval: manager init failed for mode=%s (falling back to suggest): %v",
    			approvalMode, approvalMgrErr)
    		approvalMgrOpts.InitialMode = approval.ModeSuggest
    		approvalMgrOpts.Source = approval.SourceDefault
    		approvalMgr, approvalMgrErr = approval.NewApprovalManager(approvalMgrOpts)
    		if approvalMgrErr != nil {
    			return fmt.Errorf("approval manager init (fallback): %w", approvalMgrErr)
    		}
    	}
    	toolReg.SetApprovalManager(approvalMgr)
    	log.Printf("approval: mode=%s source=%s sandbox_available=%t",
    		approvalMgr.Mode(), approvalMgr.Source(), sandboxAvailable)
    	if regErr := cmdRegistry.Register(commands.NewApprovalCommand(approvalMgr)); regErr != nil {
    		log.Printf("approval: register slash command failed: %v", regErr)
    	}
    
    	// F22: per-edit git auto-commit (Aider-style).
    	acEnabled := os.Getenv(autocommit.EnvVarName) != "off"
    	cwd, _ := os.Getwd()
    	autoCommitter := autocommit.NewAutoCommitter(autocommit.Options{
    		Enabled:    acEnabled,
    		Provider:   c.llmProvider,
    		WorkingDir: cwd,
    		Logger:     zap.NewNop(),
    	})
    	toolReg.SetAutoCommitter(autoCommitter)
    	log.Printf("git_auto_commit: enabled=%t cwd=%s git_repo=%t",
    		autoCommitter.Enabled(), cwd, autoCommitter.IsGitRepo())
    	if regErr := cmdRegistry.Register(commands.NewGitAutoCommitCommand(autoCommitter)); regErr != nil {
    		log.Printf("git_auto_commit: register slash command failed: %v", regErr)
    	}
    
    	// F23: cline-style browser tool suite.
    	browserMgr := browser.NewBrowserManager(browser.NewDefaultChromeDiscovery(), zap.NewNop())
    	if err := tools.RegisterBrowserToolsV2(toolReg, browserMgr); err != nil {
    		log.Printf("browser: register tools failed: %v", err)
    	}
    	c.browserManager = browserMgr
    	if regErr := cmdRegistry.Register(commands.NewBrowserCommand(browserMgr)); regErr != nil {
    		log.Printf("browser: register slash command failed: %v", regErr)
    	}
    	c.addCleanup(func() { _ = browserMgr.CloseSession() })
    
    	// F24: codex-style project memory subsystem.
    	memCwd, _ := os.Getwd()
    	memLoader := projectmemory.NewMemoryLoader(zap.NewNop())
    	memRegistry := projectmemory.NewMemoryRegistry(memLoader, memCwd)
    	if _, err := memRegistry.Reload(ctx); err != nil {
    		log.Printf("projectmemory: initial reload failed: %v", err)
    	}
    	memWatcher := projectmemory.NewMemoryWatcher(memRegistry, zap.NewNop())
    	if err := memWatcher.Start(ctx); err != nil {
    		log.Printf("projectmemory: watcher start failed (degrading to slash-only reload): %v", err)
    	}
    	c.addCleanup(func() { _ = memWatcher.Close() })
    	if regErr := cmdRegistry.Register(commands.NewMemoryCommand(memRegistry)); regErr != nil {
    		log.Printf("projectmemory: register slash command failed: %v", regErr)
    	}
    	c.memoryRegistry = memRegistry
    
    	// F25: plandex-style plan tree system.
    	planStore := plantree.NewFileStore(cwd)
    	planSummariser := plantree.DeterministicSummariser{}
    	if err := plantree.RegisterPlanTools(toolReg, planStore); err != nil {
    		log.Printf("plantree: register tools failed: %v", err)
    	}
    	if regErr := cmdRegistry.Register(commands.NewPlanTreeCommand(planStore, planSummariser)); regErr != nil {
    		log.Printf("plantree: register slash command failed: %v", regErr)
    	}
    
    	// F26: Openhands-style workspace + planner system.
    	wsMgr, wsErr := workspace.NewWorkspaceManager()
    	if wsErr != nil {
    		log.Printf("workspace: manager init failed (container runtime not available): %v", wsErr)
    	} else {
    		toolReg.Register(workspace.NewWorkspaceCreateTool(wsMgr))
    		toolReg.Register(workspace.NewWorkspaceListTool(wsMgr))
    		toolReg.Register(workspace.NewWorkspaceCleanupTool(wsMgr))
    		if regErr := cmdRegistry.Register(commands.NewOpenhandsCommand(wsMgr)); regErr != nil {
    			log.Printf("openhands: register slash command failed: %v", regErr)
    		}
    	}
    	plannerExec := taskplanner.NewSequentialExecutor(nil)
    	toolReg.Register(taskplanner.NewTaskPlanTool(plannerExec))
    	toolReg.Register(taskplanner.NewTaskStepTool(plannerExec))
    
    	// F27: aider-style voice input + repo-map.
    	// Wire the operator-configured capture backend (HELIX_VOICE_CAPTURE_CMD)
    	// so NewVoiceRecorderWithCmd / VoiceConfig.CaptureCmd are actually
    	// exercised in production instead of being dead. When unset, fall back
    	// to the auto-detected arecord/sox/parec via NewVoiceRecorder(). The
    	// value is a command NAME (whitespace-split, first field LookPath-
    	// validated inside detectCaptureCmd — no shell injection).
    	voiceCaptureCmd := os.Getenv("HELIX_VOICE_CAPTURE_CMD")
    	var voiceRec *voice.VoiceRecorder
    	if voiceCaptureCmd != "" {
    		voiceRec = voice.NewVoiceRecorderWithCmd(voiceCaptureCmd)
    	} else {
    		voiceRec = voice.NewVoiceRecorder()
    	}
    	voiceTrans := voice.NewVoiceTranscriber(voice.VoiceConfig{
    		WhisperAPIKey: os.Getenv("OPENAI_API_KEY"),
    		CaptureCmd:    voiceCaptureCmd,
    	})
    	toolReg.Register(voice.NewVoiceStartTool(voiceRec))
    	toolReg.Register(voice.NewVoiceStopTool(voiceRec))
    	toolReg.Register(voice.NewVoiceTranscribeTool(voiceRec, voiceTrans))
    	if regErr := cmdRegistry.Register(commands.NewAiderCommand(voiceRec, voiceTrans)); regErr != nil {
    		log.Printf("aider: register slash failed: %v", regErr)
    	}
    
    	// F28: kilo-code AST-aware refactoring.
    	kcEngine := kilocode.NewRenameEngine(cwd)
    	toolReg.Register(kilocode.NewKiloRenameTool(kcEngine))
    	kcAnalyzer, kcErr := kilocode.NewImpactAnalyzer(cwd)
    	if kcErr == nil {
    		toolReg.Register(kilocode.NewKiloImpactTool(kcAnalyzer))
    	}
    	kcRefactorer := kilocode.NewRefactorer(cwd)
    	toolReg.Register(kilocode.NewKiloMultiEditTool(kcRefactorer))
    	if regErr := cmdRegistry.Register(commands.NewKilocodeCommand(kcEngine, kcAnalyzer, kcRefactorer)); regErr != nil {
    		log.Printf("kilocode: register slash failed: %v", regErr)
    	}
    
    	// F29: Roo-code full port.
    	rooDelegator := roocode.NewTaskDelegator()
    	toolReg.Register(roocode.NewRooDelegateTool(rooDelegator))
    	rooGen := roocode.NewCodeGenerator(cwd)
    	toolReg.Register(roocode.NewRooGenerateTool(rooGen))
    	toolReg.Register(roocode.NewRooBootstrapTool(rooGen))
    	rooReviewer := roocode.NewCodeReviewer()
    	rooConvStore := roocode.NewConversationStore()
    	if regErr := cmdRegistry.Register(commands.NewRooCodeCommand(rooDelegator, rooGen, rooReviewer, rooConvStore)); regErr != nil {
    		log.Printf("roocode: register slash failed: %v", regErr)
    	}
    
    	// F30: Continue.dev IDE integration.
    	contEditor := continua.NewWorkspaceEditor()
    	toolReg.Register(continua.NewContinueEditTool(contEditor))
    	contCompletion := continua.NewCompletionEngine()
    	toolReg.Register(continua.NewContinueCompleteTool(contCompletion))
    	contChat := continua.NewChatManager()
    	if regErr := cmdRegistry.Register(commands.NewContinueCommand(contEditor, contCompletion, contChat)); regErr != nil {
    		log.Printf("continue: register slash failed: %v", regErr)
    	}
    
    	// F09: user-defined Markdown slash commands.
    	projectCmds := filepath.Join(".", ".helix", "commands")
    	var userCmds string
    	if userCfg, err := os.UserConfigDir(); err == nil {
    		userCmds = filepath.Join(userCfg, "helixcode", "commands")
    	}
    	mdLoader := commands.NewMarkdownLoader(cmdRegistry, projectCmds, userCmds)
    	if loadErr := mdLoader.Load(); loadErr != nil {
    		log.Printf("markdown commands: load failed: %v", loadErr)
    	}
    	mdWatcher, mdwErr := commands.NewMarkdownWatcher(mdLoader, []string{projectCmds, userCmds})
    	if mdwErr != nil {
    		log.Printf("markdown commands: watcher init failed: %v", mdwErr)
    	} else {
    		go mdWatcher.Run(ctx)
    		c.addCleanup(func() { _ = mdWatcher.Close() })
    	}
    	if regErr := cmdRegistry.Register(commands.NewCommandsCommand(mdLoader, cmdRegistry)); regErr != nil {
    		log.Printf("commands: register slash failed: %v", regErr)
    	}
    
    	// F10: agent-invoked Skills.
    	skillProjectDir := filepath.Join(".", ".helix", "skills")
    	var skillUserDir string
    	if userCfg, err := os.UserConfigDir(); err == nil {
    		skillUserDir = filepath.Join(userCfg, "helixcode", "skills")
    	}
    	skillReg := commands.NewSkillRegistry()
    	skillLoader := commands.NewSkillLoader(skillReg, skillProjectDir, skillUserDir)
    	if loadErr := skillLoader.Load(); loadErr != nil {
    		log.Printf("skills: load failed: %v", loadErr)
    	}
    	skillWatcher, swErr := commands.NewSkillsWatcher(skillLoader, []string{skillProjectDir, skillUserDir})
    	if swErr != nil {
    		log.Printf("skills: watcher init failed: %v", swErr)
    	} else {
    		go skillWatcher.Run(ctx)
    		c.addCleanup(func() { _ = skillWatcher.Close() })
    	}
    	_ = agent.NewSkillDispatcher(skillReg, nil) // wired into baseAgent in a follow-up
    	if regErr := cmdRegistry.Register(commands.NewSkillsCommand(skillLoader, skillReg)); regErr != nil {
    		log.Printf("skills: register slash failed: %v", regErr)
    	}
    
    	// F11: session transcript persistence + resume.
    	transcriptStore := session.NewTranscriptStore(sessionStoreBaseDir())
    	resumeFinder := session.NewResumeFinder(transcriptStore)
    	resumeMgr := session.NewSessionManager()
    	resumeMgr.SetStore(transcriptStore)
    
    	currentProject, projErr := session.ComputeProjectIdentity()
    	if projErr != nil {
    		log.Printf("session: project identity unresolved: %v (continuing with empty scope)", projErr)
    		currentProject = ""
    	}
    
    	// Process F11 resume flags BEFORE the interactive loop runs.
    	if c.flagState.resumeSessionFlag != "" {
    		if err := resumeMgr.Resume(ctx, c.flagState.resumeSessionFlag); err != nil {
    			return fmt.Errorf("resume session %s: %w", c.flagState.resumeSessionFlag, err)
    		}
    		fmt.Fprintln(os.Stderr, tr(ctx, "cli_session_resumed", map[string]any{
    			"ID":    resumeMgr.CurrentID(),
    			"Count": resumeMgr.LoadedMessageCountForTestF11(),
    		}))
    	} else if c.flagState.resumeFlag || c.flagState.continueFlag {
    		mode := session.ResumeProject
    		scope := currentProject
    		if c.flagState.continueFlag {
    			mode = session.ResumeGlobal
    			scope = ""
    		}
    		target, ferr := resumeFinder.FindResumeTarget(ctx, mode, scope)
    		if ferr != nil {
    			fmt.Fprintln(os.Stderr, tr(ctx, "cli_session_no_resumable",
    				map[string]any{"Error": fmt.Sprintf("%v", ferr)}))
    		} else {
    			if err := resumeMgr.Resume(ctx, target.SessionID); err != nil {
    				return fmt.Errorf("resume session %s: %w", target.SessionID, err)
    			}
    			fmt.Fprintln(os.Stderr, tr(ctx, "cli_session_resumed_active", map[string]any{
    				"ID":         resumeMgr.CurrentID(),
    				"Count":      resumeMgr.LoadedMessageCountForTestF11(),
    				"LastActive": target.LastActivity.Format("2006-01-02 15:04:05"),
    			}))
    		}
    	}
    	if regErr := cmdRegistry.Register(commands.NewSessionsCommand(transcriptStore, currentProject)); regErr != nil {
    		log.Printf("sessions: register slash failed: %v", regErr)
    	}
    
    	// P1-F16-T10: Wire telemetry into the tool registry. The provider is the
    	// shared lazy telemetry() instance — also used by the F12 LLM decorator.
    	telemetryProv := c.telemetry()
    	if toolInstr, tiErr := telemetry.NewToolInstrumentation(telemetryProv); tiErr != nil {
    		log.Printf("telemetry: tool instrumentation construction failed: %v", tiErr)
    	} else {
    		toolReg.SetTelemetryInstrumentation(toolInstr)
    	}
    	if regErr := cmdRegistry.Register(commands.NewTelemetryCommand(telemetryProv)); regErr != nil {
    		log.Printf("telemetry: register slash command failed: %v", regErr)
    	}
    
    	// P1-F20-T07: Register the /theme slash command.
    	{
    		slashThemeRegistry := theme.NewThemeRegistry()
    		if path := theme.DefaultThemePath(os.Getenv); path != "" {
    			if err := slashThemeRegistry.LoadFromFile(path); err != nil {
    				log.Printf("theme(slash): yaml load failed (continuing with built-ins): %v", err)
    			}
    		}
    		slashThemeName := theme.DetectThemeName(os.Getenv)
    		slashSelectedTheme, slashErr := slashThemeRegistry.Get(slashThemeName)
    		if slashErr != nil {
    			log.Printf("theme(slash): get %q failed (%v), falling back to dark", slashThemeName, slashErr)
    			slashSelectedTheme, _ = slashThemeRegistry.Get(theme.ThemeDark)
    			slashThemeName = theme.ThemeDark
    		}
    		slashColorDepth := theme.DetectColorDepth(os.Getenv)
    		slashSource := commands.ResolveThemeSource(os.Getenv)
    		slashStyler := theme.NewStyler(slashSelectedTheme, slashColorDepth)
    		if regErr := cmdRegistry.Register(commands.NewThemeCommand(slashThemeRegistry, slashThemeName, slashColorDepth, slashSource, slashStyler)); regErr != nil {
    			log.Printf("theme: register slash command failed: %v", regErr)
    		}
    	}
    
    	// P1-F15-T10: Subagent system wiring. c.llmProvider has already settled
    	// (ensureLLMProvider ran before this getter via ensureSubsystems).
    	{
    		subagentLogger := zap.NewNop()
    		subagentWorkDir, swdErr := os.Getwd()
    		if swdErr != nil {
    			log.Printf("subagent: resolving cwd failed (skipping wire-in): %v", swdErr)
    		} else if c.llmProvider == nil {
    			log.Printf("subagent: no LLM provider available (F12 default failed); skipping wire-in")
    		} else {
    			inProcessSpawner := subagent.NewInProcessSpawner()
    			subprocessSpawner, spErr := subagent.NewSubprocessSpawner(subagentWorkDir)
    			if spErr != nil {
    				log.Printf("subagent: subprocess spawner unavailable (continuing in-process only): %v", spErr)
    			}
    			subagentMgr, smErr := subagent.NewSubagentManager(subagent.SubagentManagerOptions{
    				InProcessSpawner:  inProcessSpawner,
    				SubprocessSpawner: subprocessSpawner,
    				LLMProvider:       c.llmProvider,
    				Logger:            subagentLogger,
    				WorkDir:           subagentWorkDir,
    			})
    			if smErr != nil {
    				log.Printf("subagent: manager construction failed (skipping wire-in): %v", smErr)
    			} else {
    				c.addCleanup(func() {
    					shutCtx, shutCancel := context.WithTimeout(context.Background(), 5*time.Second)
    					defer shutCancel()
    					_ = subagentMgr.Shutdown(shutCtx)
    				})
    				toolReg.Register(task.NewTaskTool(subagentMgr))
    				if regErr := cmdRegistry.Register(commands.NewSubagentsCommand(subagentMgr)); regErr != nil {
    					log.Printf("subagent: register slash command failed: %v", regErr)
    				}
    				log.Printf("subagent: manager initialised (max_concurrency=%d)",
    					subagent.DefaultMaxConcurrency)
    			}
    		}
    	}
    
    	// P1-F17-T08: Smart File Editing wiring.
    	{
    		mfe := toolReg.GetMultiEdit()
    		if mfe == nil {
    			log.Printf("smart-edit: multiedit unavailable; smart_edit tool + /edit slash skipped")
    		} else {
    			smartWorkDir, swdErr := os.Getwd()
    			if swdErr != nil {
    				log.Printf("smart-edit: resolving cwd failed (skipping wire-in): %v", swdErr)
    			} else {
    				committer := smartedit.NewMultieditCommitter(mfe)
    				smartTool := smartedit.NewSmartEditTool(committer, smartWorkDir)
    				toolReg.Register(smartTool)
    				if regErr := cmdRegistry.Register(commands.NewEditCommand(smartTool)); regErr != nil {
    					log.Printf("smart-edit: register slash command failed: %v", regErr)
    				}
    				log.Printf("smart-edit: wired (workdir=%s)", smartWorkDir)
    			}
    		}
    	}
    
    	return nil
    }
    
    // Run executes the CLI
    func (c *CLI) Run() error {
    	// Parse command-line flags
    	var (
    		command        = flag.String("command", "", "Command to execute")
    		workerHost     = flag.String("worker", "", "Worker host to add")
    		workerUser     = flag.String("user", "", "Worker SSH username")
    		workerKey      = flag.String("key", "", "Worker SSH key path")
    		model          = flag.String("model", "llama-3-8b", "LLM model to use")
    		prompt         = flag.String("prompt", "", "Prompt for LLM generation")
    		maxTokens      = flag.Int("max-tokens", 1000, "Maximum tokens to generate")
    		temperature    = flag.Float64("temperature", 0.7, "Generation temperature")
    		stream         = flag.Bool("stream", false, "Stream the response")
    		listWorkers    = flag.Bool("list-workers", false, "List all workers")
    		listModels     = flag.Bool("list-models", false, "List available models")
    		healthCheck    = flag.Bool("health", false, "Perform health check")
    		notify         = flag.String("notify", "", "Send notification with message")
    		notifyType     = flag.String("notify-type", "info", "Notification type")
    		notifyPriority = flag.String("notify-priority", "medium", "Notification priority")
    		nonInteractive = flag.Bool("non-interactive", false, "Run in non-interactive mode")
    		permissionMode = flag.String("permission-mode", "", "permission preset: default|auto|acceptEdits|dontAsk|bypassPermissions")
    
    		// QA flags
    		qaRun        = flag.Bool("qa-run", false, "Start a QA session")
    		qaList       = flag.Bool("qa-list", false, "List QA sessions")
    		qaReport     = flag.String("qa-report", "", "Get QA report for session ID")
    		qaScreenshot = flag.String("qa-screenshot", "", "Capture screenshot for session ID")
    		qaCancel     = flag.String("qa-cancel", "", "Cancel QA session by ID")
    		qaPlatforms  = flag.String("qa-platforms", "web", "Comma-separated platforms for QA")
    		qaBanks      = flag.String("qa-banks", "", "Comma-separated bank paths for QA")
    		qaFormat     = flag.String("qa-format", "markdown", "Report format: markdown|html|json")
    		qaWait       = flag.Bool("qa-wait", false, "Wait for QA session to complete")
    		qaServerURL  = flag.String("qa-server", "http://localhost:8080", "HelixCode server URL for QA")
    
    		// F11: session transcript resume.
    		// `--resume` resumes the most recently active session for the current project.
    		// `--continue` resumes the most recently active session globally (any project).
    		// `--resume-session <id>` resumes a specific session by ID (overrides the bool flags).
    		// We use three separate flags rather than a NoOptDefVal sentinel because this
    		// command-line surface is built on stdlib `flag`, which does not support
    		// optional-value flags. Three flags also disambiguate "no id supplied" from
    		// "id is the empty string".
    		resumeFlag        = flag.Bool("resume", false, "Resume most recent session for current project (F11)")
    		continueFlag      = flag.Bool("continue", false, "Resume most recent session globally across projects (F11)")
    		resumeSessionFlag = flag.String("resume-session", "", "Resume a specific session by ID (F11)")
    
    		// F12: cloud LLM provider override.
    		// Precedence (handled by llm.Select): --provider > HELIX_LLM_PROVIDER > config-file > wizard.
    		// On ErrNoProviderConfigured we print a friendly message and continue with
    		// the existing default Ollama provider — we never auto-launch the TUI here
    		// because that would hang non-TTY runs. Users start the wizard explicitly
    		// via `helixcode wizard`.
    		providerFlag = flag.String("provider", "", "F12 cloud LLM provider override (anthropic|bedrock|vertexai|azure)")
    
    		// F21: approval mode override.
    		// Precedence (handled by approval.Select): --approval > HELIXCODE_APPROVAL >
    		// $XDG_CONFIG_HOME/helixcode/approval.yaml > built-in default (suggest).
    		// Garbage values fall through to the next source; the selector aggregates
    		// parse errors so we can warn the user without losing the runtime mode.
    		approvalFlag = flag.String("approval", "", "F21 approval mode override (suggest|auto-edit|full-auto|dangerously-bypass)")
    
    		// P0-T01 (speed programme): opt-in pprof capture. When --pprof <dir> is
    		// supplied (or the HELIX_PPROF env var is set), the run writes a CPU
    		// profile (<dir>/cpu.pprof) and a heap profile (<dir>/heap.pprof). It is
    		// OFF by default — when neither the flag nor the env var is set there is
    		// zero behaviour change to the CLI's normal path. The captured profiles
    		// are the anti-bluff baseline evidence for the Phase 0 measurement gate.
    		pprofDir = flag.String("pprof", "", "P0-T01 speed programme: write CPU+heap pprof profiles to this directory (off by default; also via HELIX_PPROF env)")
    
    		versionFlag = flag.Bool("version", false, "Print the HelixCode brand banner + version, then exit")
    	)
    	flag.Parse()
    
    	// --version: print the HelixCode brand banner (lime/teal wordmark,
    	// NO_COLOR / non-TTY aware) followed by the resolved version string, then
    	// exit. Placed immediately after flag.Parse() so it short-circuits before
    	// any subsystem or LLM-provider construction.
    	if *versionFlag {
    		fmt.Print(brandBanner())
    		fmt.Println("  " + version.GetFullVersion())
    		return nil
    	}
    
    	// P0-T01: start opt-in pprof capture immediately after flag parsing so the
    	// profile covers as much of the run as possible. pprofutil.Start returns a
    	// nil *Capture when profiling was not requested — the deferred Stop is then
    	// a safe no-op, so this adds nothing to the unprofiled hot path.
    	if profDir := pprofutil.ResolveDir(*pprofDir, os.Getenv); profDir != "" {
    		pc, perr := pprofutil.Start(profDir, "")
    		if perr != nil {
    			log.Printf("pprof: capture disabled — %v", perr)
    		} else {
    			fmt.Fprintf(os.Stderr, "pprof: capturing CPU profile to %s\n", pc.CPUPath())
    			defer func() {
    				elapsed, heapPath, stopErr := pc.Stop("")
    				if stopErr != nil {
    					log.Printf("pprof: stop failed: %v", stopErr)
    					return
    				}
    				fmt.Fprintf(os.Stderr, "pprof: wrote profiles after %s (heap: %s)\n", elapsed, heapPath)
    			}()
    		}
    	}
    
    	// Debug: print flag values
    	fmt.Fprintln(os.Stderr, trc("cli_debug_flags_parsed", map[string]any{
    		"ListWorkers":    fmt.Sprintf("%v", *listWorkers),
    		"NonInteractive": fmt.Sprintf("%v", *nonInteractive),
    	}))
    
    	ctx := context.Background()
    
    	// Speed programme P1-T03: lazy CLI startup.
    	//
    	// The heavy interactive-session subsystem cluster (telemetry, permissions,
    	// persistence, worktree git shell-out, hooks YAML, the tool registry + the
    	// full F07-F30 tool/slash-command suite, LSP exec.LookPath sweep, MCP
    	// server spawn, sandbox detection, the F15 subagent manager, the F09/F10
    	// markdown-command + skill loaders/watchers) is NO LONGER built eagerly
    	// here. It is constructed on demand by c.ensureSubsystems(), gated by a
    	// sync.Once. Likewise the F12 cloud-provider override + telemetry-wrapped
    	// LLM provider is built on demand by c.ensureLLMProvider().
    	//
    	// Snapshot the parsed flag values the lazy getters consume so they need no
    	// *flag receiver (the getters are invoked from command handlers).
    	c.permissionMode = *permissionMode
    	c.flagState = cliFlagState{
    		approvalFlag:      *approvalFlag,
    		providerFlag:      *providerFlag,
    		resumeFlag:        *resumeFlag,
    		continueFlag:      *continueFlag,
    		resumeSessionFlag: *resumeSessionFlag,
    	}
    
    	// Single teardown seam: whatever a lazily-constructed subsystem registered
    	// via addCleanup is drained here at Run() exit (LIFO — the order Go's
    	// `defer` stack used in the old eager monolith).
    	defer c.runCleanups()
    
    	// Handle different commands
    	switch {
    	case *listWorkers:
    		return c.handleListWorkers(ctx)
    	case *listModels:
    		// --list-models needs the resolved LLM provider (F12 + telemetry
    		// wrapper) but NOT the heavy subsystem cluster.
    		if err := c.ensureLLMProvider(ctx, c.flagState.providerFlag); err != nil {
    			return err
    		}
    		return c.handleListModels(ctx)
    	case *healthCheck:
    		return c.handleHealthCheck(ctx)
    	case *workerHost != "":
    		return c.handleAddWorker(ctx, *workerHost, *workerUser, *workerKey)
    	case *prompt != "":
    		// --prompt (generate) needs the resolved LLM provider but NOT the
    		// heavy subsystem cluster.
    		if err := c.ensureLLMProvider(ctx, c.flagState.providerFlag); err != nil {
    			return err
    		}
    		return c.handleGenerate(ctx, *prompt, *model, *maxTokens, *temperature, *stream)
    	case *notify != "":
    		return c.handleNotification(ctx, *notify, *notifyType, *notifyPriority)
    	case *qaRun:
    		return c.handleQARun(ctx, *qaServerURL, *qaPlatforms, *qaBanks, *qaWait)
    	case *qaList:
    		return c.handleQAList(ctx, *qaServerURL)
    	case *qaReport != "":
    		return c.handleQAReport(ctx, *qaServerURL, *qaReport, *qaFormat)
    	case *qaScreenshot != "":
    		return c.handleQAScreenshot(ctx, *qaServerURL, *qaScreenshot)
    	case *qaCancel != "":
    		return c.handleQACancel(ctx, *qaServerURL, *qaCancel)
    	case *command != "":
    		return c.handleCommand(ctx, *command)
    	case *nonInteractive:
    		// In non-interactive mode, exit gracefully if no command specified
    		return nil
    	default:
    		// The interactive REPL is the one path that genuinely needs the full
    		// slash-command registry + agent tool surface, so it (and only it)
    		// triggers the heavy subsystem cluster construction.
    		if err := c.ensureSubsystems(ctx); err != nil {
    			return err
    		}
    		return c.handleInteractive(ctx)
    	}
    }
    
    // handleListWorkers lists all workers
    func (c *CLI) handleListWorkers(ctx context.Context) error {
    	stats := c.workerPool.GetWorkerStats(ctx)
    
    	fmt.Println()
    	fmt.Println(tr(ctx, "cli_workers_header", nil))
    	fmt.Println(tr(ctx, "cli_workers_total", map[string]any{"Count": stats.TotalWorkers}))
    	fmt.Println(tr(ctx, "cli_workers_active", map[string]any{"Count": stats.ActiveWorkers}))
    	fmt.Println(tr(ctx, "cli_workers_healthy", map[string]any{"Count": stats.HealthyWorkers}))
    	fmt.Println(tr(ctx, "cli_workers_total_cpu", map[string]any{"Count": stats.TotalCPU}))
    	fmt.Println(tr(ctx, "cli_workers_total_memory_gb", map[string]any{"GB": fmt.Sprintf("%.2f", float64(stats.TotalMemory)/(1024*1024*1024))}))
    	fmt.Println(tr(ctx, "cli_workers_total_gpu", map[string]any{"Count": stats.TotalGPU}))
    
    	return nil
    }
    
    // handleListModels lists available models.
    // BLUFF-002 FIX: Uses LLMsVerifier as the single source of truth when enabled.
    // Falls back to provider discovery and then to the constitutional fallback list.
    func (c *CLI) handleListModels(ctx context.Context) error {
    	fmt.Println()
    	fmt.Println(tr(ctx, "cli_models_header", nil))
    
    	// Priority 1: LLMsVerifier adapter (CONST-036 single source of truth).
    	//
    	// D-2 (SP1): list only WORKING models — key-present provider ∧ Verified ∧
    	// status=="verified" ∧ score>=min. Presenting failed/pending/rate-limited or
    	// no-key models as available is a §11.4 / CONST-035 PASS-bluff (the user
    	// cannot actually use them).
    	if c.verifierAdapter != nil && c.verifierAdapter.IsEnabled() {
    		// Key-presence gate sourced from the committed, multi-alias
    		// llm.PresentProviderNames() — the SAME funnel input the server's
    		// model-listing path consumes (handlers.go listLLMModels). This
    		// retires the CLI-local single-alias presentProviders table at the
    		// production call site so both surfaces recognize keys identically
    		// (e.g. CLAUDE_API_KEY / DASHSCOPE_API_KEY secondary aliases).
    		present := llm.PresentProviderNames()
    		models, err := c.verifierAdapter.GetWorkingModels(ctx, present)
    		if err == nil && len(models) > 0 {
    			c.printVerifiedModels(models)
    
    			// Supplement: for any key-present provider with zero verified
    			// models (verifier DB has no data for that provider yet), append
    			// seed models from FallbackModels so the user sees ALL providers
    			// they configured — not just those the verifier has scored.
    			covered := make(map[string]bool, len(models))
    			for _, m := range models {
    				covered[m.Provider] = true
    			}
    			c.printSupplementalModels(present, covered)
    			return nil
    		}
    		// Log warning but continue to fallback
    		fmt.Fprintf(os.Stderr, "⚠️  Verifier unavailable (%v), using fallback...\n", err)
    	}
    
    	// Priority 2: Provider's own model list (e.g., Ollama /api/tags)
    	if c.llmProvider != nil {
    		providerModels := c.llmProvider.GetModels()
    		if len(providerModels) > 0 {
    			for _, m := range providerModels {
    				fmt.Printf("%s\n\n", tr(ctx, "cli_model_info_provider", map[string]any{
    					"ID":          m.ID,
    					"Name":        m.Name,
    					"Provider":    m.Provider,
    					"ContextSize": m.ContextSize,
    				}))
    			}
    			// Supplement: show seed models for other key-present providers
    			// that are not the active LLM provider.
    			present := llm.PresentProviderNames()
    			covered := make(map[string]bool)
    			covered[string(c.llmProvider.GetType())] = true
    			c.printSupplementalModels(present, covered)
    			return nil
    		}
    	}
    
    	// Priority 3: Constitutional fallback list (CONST-035 compliance).
    	//
    	// D-2: fallback models are NOT verifier-confirmed "working" — they are an
    	// offline baseline. They MUST NOT be rendered through printVerifiedModels
    	// (which now asserts Verified+verified status), and MUST carry an explicit
    	// "unverified fallback" label so the user is never told they are verified.
    	c.printFallbackModels(verifier.FallbackModels)
    	fmt.Printf("ℹ️  %s\n", tr(ctx, "cli_models_fallback_notice", nil))
    	return nil
    }
    
    // printFallbackModels renders the offline fallback list with an explicit
    // unverified label (D-2 anti-bluff: never claim fallback == verified working).
    func (c *CLI) printFallbackModels(models []*verifier.VerifiedModel) {
    	fmt.Print(renderFallbackModels(models))
    }
    
    // renderFallbackModels builds the offline-fallback --list-models output for
    // a slice of fallback models and returns it as a string (HXC-117 follow-up
    // fix: testable seam mirroring renderVerifiedModels below).
    //
    // Fallback rows share the cli_model_info_verified template with
    // renderVerifiedModels -- that template gained a "Capabilities:" line when
    // HXC-117 first landed, but printFallbackModels (this function's
    // predecessor) supplied no "Capabilities" key. Go's text/template renders a
    // missing map key as the literal string "<no value>", so every offline
    // fallback row leaked "Capabilities: <no value>" to end users -- a §11.4.1
    // fix-A-breaks-B regression against this file's OTHER caller of the shared
    // template key. Fallback models are reached precisely when the verifier is
    // unavailable (Priority 3 in handleListModels), so no per-flag CONST-040
    // capability data exists for them; fabricating ✓/✗ values here would itself
    // be a CONST-040 / BLUFF-002 bluff. The honest fix is an explicit,
    // i18n-sourced "capabilities unknown" indicator -- never the raw
    // text/template zero-value string, never a fabricated capability.
    func renderFallbackModels(models []*verifier.VerifiedModel) string {
    	var sb strings.Builder
    	for _, m := range models {
    		scoreStr := fmt.Sprintf("SC:%.1f", m.OverallScore)
    		sb.WriteString(fmt.Sprintf("%s\n\n", trc("cli_model_info_verified", map[string]any{
    			"ID":           m.ID,
    			"Name":         m.DisplayName,
    			"Provider":     m.Provider,
    			"Score":        scoreStr,
    			"ContextSize":  m.ContextSize,
    			"Status":       "⚠ unverified fallback",
    			"Capabilities": trc("cli_capability_flags_unknown", nil),
    		})))
    	}
    	return sb.String()
    }
    
    // printSupplementalModels appends seed models from FallbackModels for any
    // key-present provider whose models were NOT already covered by a prior
    // display pass (verified or provider-owned). This ensures that providers
    // with a configured API key always appear in --list-models output, even
    // when the verifier DB has no data for them yet.
    func (c *CLI) printSupplementalModels(present map[string]bool, covered map[string]bool) {
    	var supplemental []*verifier.VerifiedModel
    	for _, fm := range verifier.FallbackModels {
    		if present[fm.Provider] && !covered[fm.Provider] {
    			supplemental = append(supplemental, fm)
    		}
    	}
    	if len(supplemental) > 0 {
    		c.printFallbackModels(supplemental)
    	}
    }
    
    // providerEnvAliases maps a verifier provider type to the environment-variable
    // aliases that supply that provider's API key. A non-empty, non-placeholder
    // value in ANY alias marks the provider key-present. Decoupled, data-only table
    // (lifted from helix_agent SupportedProviders.EnvVars per §11.4.74); converges
    // on the shared substrate when the replace dev.helix.agent wiring lands.
    var providerEnvAliases = map[string][]string{
    	"openai":     {"OPENAI_API_KEY"},
    	"anthropic":  {"ANTHROPIC_API_KEY", "CLAUDE_API_KEY"},
    	"gemini":     {"GEMINI_API_KEY", "GOOGLE_API_KEY"},
    	"deepseek":   {"DEEPSEEK_API_KEY"},
    	"groq":       {"GROQ_API_KEY"},
    	"mistral":    {"MISTRAL_API_KEY"},
    	"xai":        {"XAI_API_KEY", "GROK_API_KEY"},
    	"openrouter": {"OPENROUTER_API_KEY"},
    	"xiaomi":     {"XIAOMI_MIMO_API_KEY", "ApiKey_Xiaomi_MiMo"},
    }
    
    // isPlaceholderKey reports whether a key value is an obvious placeholder that
    // must NOT count as a recognized key (e.g. ".env.example" stubs).
    func isPlaceholderKey(v string) bool {
    	v = strings.TrimSpace(v)
    	if v == "" {
    		return true
    	}
    	lower := strings.ToLower(v)
    	for _, p := range []string{"your-", "your_", "changeme", "placeholder", "xxx", "sk-...", "<", "example"} {
    		if strings.Contains(lower, p) {
    			return true
    		}
    	}
    	return false
    }
    
    // presentProviders returns the set of provider types for which a usable API key
    // was recognized in the environment (after D-3 secrets.LoadAPIKeys ran at
    // startup). getenv is injected for testability. This is the key-presence input
    // to the working-model funnel (D-2): only key-present providers' models display.
    func presentProviders(getenv func(string) string) map[string]bool {
    	present := make(map[string]bool, len(providerEnvAliases))
    	for provider, aliases := range providerEnvAliases {
    		for _, alias := range aliases {
    			if v := getenv(alias); !isPlaceholderKey(v) {
    				present[provider] = true
    				break
    			}
    		}
    	}
    	return present
    }
    
    // isWorkingForDisplay reports whether a model is usable enough to show in the
    // "available models" listing (D-2). A model is working iff it is Verified with
    // status "verified". failed / pending / rate-limited rows are NOT working and
    // MUST NOT be presented as available (§11.4 / CONST-035 — listing an unusable
    // model is a PASS-bluff at the model-listing layer).
    func isWorkingForDisplay(m *verifier.VerifiedModel) bool {
    	return m.Verified && m.VerificationStatus == "verified"
    }
    
    func (c *CLI) printVerifiedModels(models []*verifier.VerifiedModel) {
    	fmt.Print(renderVerifiedModels(models))
    }
    
    // renderVerifiedModels builds the --list-models output for a slice of
    // verified models and returns it as a string (HXC-117: split out of
    // printVerifiedModels as a testable seam -- the stdout call site above is
    // unchanged, but unit tests can now assert on the rendered text without
    // capturing os.Stdout).
    func renderVerifiedModels(models []*verifier.VerifiedModel) string {
    	var sb strings.Builder
    	for _, m := range models {
    		// D-2 defensive gate: never render a non-working row, regardless of
    		// which caller supplied the list (the verifier funnel SHOULD already
    		// have filtered, but the printer must not be the place a bluff leaks).
    		if !isWorkingForDisplay(m) {
    			continue
    		}
    		status := "✓ verified"
    		scoreStr := fmt.Sprintf("SC:%.1f", m.OverallScore)
    		sb.WriteString(fmt.Sprintf("%s\n\n", trc("cli_model_info_verified", map[string]any{
    			"ID":           m.ID,
    			"Name":         m.DisplayName,
    			"Provider":     m.Provider,
    			"Score":        scoreStr,
    			"ContextSize":  m.ContextSize,
    			"Status":       status,
    			"Capabilities": formatCapabilityFlags(m),
    		})))
    	}
    	return sb.String()
    }
    
    // formatCapabilityFlags renders the 6 CONST-040 capability flags (MCP / LSP
    // / ACP / RAG / Skills / Plugins) for CLI display, sourced directly from
    // the verifier-decoded VerifiedModel fields (internal/verifier/types.go
    // SupportsMCP / SupportsLSP / SupportsACP / SupportsRAG / SupportsSkills /
    // SupportsPlugins -- the fields client.go's VerifyModel decodes from the
    // LLMsVerifier response). NEVER hardcode a capability value here: CONST-040
    // / BLUFF-002 requires every displayed capability to trace back to
    // LLMsVerifier via the decoded model, never a constant.
    //
    // CONST-046: MCP/LSP/ACP/RAG are protocol acronyms (technical terms, not
    // translatable prose) so they stay as literals here, matching the JSON key
    // convention on the server surface. The Skills/Plugins labels and the
    // supported/unsupported indicators ARE user-facing translatable content, so
    // they are i18n-sourced from the active bundle rather than hardcoded English
    // words / hardcoded glyphs.
    func formatCapabilityFlags(m *verifier.VerifiedModel) string {
    	supportedIndicator := trc("cli_capability_indicator_supported", nil)
    	unsupportedIndicator := trc("cli_capability_indicator_unsupported", nil)
    	skillsLabel := trc("cli_capability_label_skills", nil)
    	pluginsLabel := trc("cli_capability_label_plugins", nil)
    
    	flag := func(supported bool, label string) string {
    		if supported {
    			return label + ":" + supportedIndicator
    		}
    		return label + ":" + unsupportedIndicator
    	}
    	return strings.Join([]string{
    		flag(m.SupportsMCP, "MCP"),
    		flag(m.SupportsLSP, "LSP"),
    		flag(m.SupportsACP, "ACP"),
    		flag(m.SupportsRAG, "RAG"),
    		flag(m.SupportsSkills, skillsLabel),
    		flag(m.SupportsPlugins, pluginsLabel),
    	}, " ")
    }
    
    // handleHealthCheck performs system health check
    func (c *CLI) handleHealthCheck(ctx context.Context) error {
    	fmt.Println()
    	fmt.Println(tr(ctx, "cli_health_header", nil))
    
    	// Check worker pool
    	stats := c.workerPool.GetWorkerStats(ctx)
    	if stats.HealthyWorkers > 0 {
    		fmt.Printf("✅ %s\n", tr(ctx, "cli_health_worker_pool_ok",
    			map[string]any{"Count": stats.HealthyWorkers}))
    	} else {
    		fmt.Printf("⚠️ %s\n", tr(ctx, "cli_health_worker_pool_none", nil))
    	}
    
    	// Check notification engine
    	channelStats := c.notificationEngine.GetChannelStats()
    	enabledChannels := 0
    	for _, stats := range channelStats {
    		if statsMap, ok := stats.(map[string]interface{}); ok {
    			if enabled, ok := statsMap["enabled"].(bool); ok && enabled {
    				enabledChannels++
    			}
    		}
    	}
    
    	if enabledChannels > 0 {
    		fmt.Printf("✅ %s\n", tr(ctx, "cli_health_notification_ok",
    			map[string]any{"Count": enabledChannels}))
    	} else {
    		fmt.Printf("⚠️ %s\n", tr(ctx, "cli_health_notification_none", nil))
    	}
    
    	fmt.Printf("✅ %s\n", tr(ctx, "cli_health_operational", nil))
    	return nil
    }
    
    // handleAddWorker adds a new worker
    func (c *CLI) handleAddWorker(ctx context.Context, host, username, keyPath string) error {
    	if username == "" {
    		return fmt.Errorf("username is required")
    	}
    
    	sshConfig := &worker.SSHWorkerConfig{
    		Host:     host,
    		Port:     22,
    		Username: username,
    		KeyPath:  keyPath,
    	}
    
    	worker := &worker.SSHWorker{
    		Hostname:    host,
    		DisplayName: fmt.Sprintf("worker-%s", host),
    		SSHConfig:   sshConfig,
    	}
    
    	if err := c.workerPool.AddWorker(ctx, worker); err != nil {
    		return fmt.Errorf("failed to add worker: %v", err)
    	}
    
    	fmt.Printf("✅ %s\n", tr(ctx, "cli_worker_added_success", map[string]any{"Host": host}))
    	return nil
    }
    
    // isKnownProviderPrefix reports whether seg (the part before the first ":" in a
    // "provider:model" string) names a recognized LLM provider type. It is sourced
    // from the canonical llm.ProviderType* constants so it cannot drift from the
    // provider registry (CONST-036/CONST-039). Comparison is case-insensitive.
    //
    // HXC-096: this gate is what distinguishes a real provider prefix
    // ("ollama:qwen2.5:3b" → strip "ollama") from an Ollama native "name:tag"
    // model id ("qwen2.5:3b" → keep intact, because "qwen2.5" is NOT a provider).
    func isKnownProviderPrefix(seg string) bool {
    	switch llm.ProviderType(strings.ToLower(seg)) {
    	case llm.ProviderTypeOllama,
    		llm.ProviderTypeLocal,
    		llm.ProviderTypeLlamaCpp,
    		llm.ProviderTypeOpenAI,
    		llm.ProviderTypeAnthropic,
    		llm.ProviderTypeGemini,
    		llm.ProviderTypeGroq,
    		llm.ProviderTypeMistral,
    		llm.ProviderTypeDeepSeek,
    		llm.ProviderTypeOpenRouter,
    		llm.ProviderTypeXAI,
    		llm.ProviderTypeQwen,
    		llm.ProviderTypeCohere,
    		llm.ProviderTypeAzure,
    		llm.ProviderTypeBedrock,
    		llm.ProviderTypeVertexAI:
    		return true
    	default:
    		return false
    	}
    }
    
    // parseModelSpec splits a CLI model string into an optional provider prefix and
    // the model name to send to the provider. It is the pure, testable core of the
    // HXC-096 fix (see handleGenerate): the segment before the first ":" is stripped
    // ONLY when it names a recognized provider type (isKnownProviderPrefix), so an
    // Ollama native "name:tag" model id ("qwen2.5:3b", "llama3.2:1b") is kept intact
    // — the prior unconditional split-on-first-colon mangled "qwen2.5:3b" into "3b"
    // and Ollama's /api/chat returned a 404. When no recognized provider prefix is
    // present, provider is "" and modelName is the input unchanged.
    func parseModelSpec(model string) (provider, modelName string) {
    	if idx := strings.Index(model, ":"); idx > 0 {
    		if isKnownProviderPrefix(model[:idx]) {
    			return model[:idx], model[idx+1:]
    		}
    	}
    	return "", model
    }
    
    // handleGenerate performs LLM generation
    func (c *CLI) handleGenerate(ctx context.Context, prompt, model string, maxTokens int, temperature float64, stream bool) error {
    	// ANTI-BLUFF: MUST use real LLM provider, not simulation
    	if c.llmProvider == nil {
    		return fmt.Errorf("LLM provider not initialized - please check configuration")
    	}
    
    	// Parse model from string (format: provider:model or just model).
    	//
    	// HXC-096 (CONST-035 / BLUFF-001): the prior code stripped everything
    	// before the FIRST ":" unconditionally. That is WRONG for Ollama, whose
    	// native model tags ARE "name:tag" (e.g. "qwen2.5:3b", "llama3.2:1b").
    	// The unconditional split mangled "qwen2.5:3b" into "3b", which Ollama's
    	// /api/chat rejects with a 404 ("model '3b' not found") — exactly the CLI
    	// 404 the web path never hit because internal/server passes req.Model
    	// through verbatim. The "provider:model" form is only meant to strip a
    	// LEADING provider name (e.g. "ollama:qwen2.5:3b", "anthropic:claude-...").
    	// So strip the prefix ONLY when the segment before the first ":" is a
    	// recognized provider type; otherwise keep the model string intact so a
    	// real Ollama "name:tag" reaches the provider unmodified.
    	_, modelName := parseModelSpec(model)
    
    	// Round-41 readiness fix (CONST-035): the CLI's default model
    	// is "llama-3-8b" which is a generic-Ollama name that does NOT
    	// exist on Groq, OpenAI, Anthropic, Gemini, OpenRouter, or
    	// most actual providers. Sending it as-is to any of those
    	// returns a confusing 404. If the user accepted the default
    	// (or supplied an empty value), pick the provider's first
    	// reported model so the just-plug-in-API-key-and-go workflow
    	// works without the user knowing the provider's exact model
    	// names.
    	if modelName == "" || modelName == "llama-3-8b" {
    		if models := c.llmProvider.GetModels(); len(models) > 0 {
    			modelName = models[0].Name
    		}
    	}
    
    	fmt.Println()
    	fmt.Println(tr(ctx, "cli_generating_header", map[string]any{"Model": modelName}))
    	fmt.Println(tr(ctx, "cli_generating_prompt", map[string]any{"Prompt": prompt}))
    	fmt.Println()
    
    	// Get provider
    	provider := c.llmProvider
    
    	// CONST-040 §HXC-118 Phase 2/3: optional RAG (Retrieval-Augmented
    	// Generation) context injection — default-OFF.
    	//
    	// rag.NewFromEnv resolves HELIXCODE_RAG_ENABLED (unset/false by
    	// default per internal/rag/config.go) and constructs an Adapter that
    	// is disabled unless the operator explicitly opts in. When disabled,
    	// Adapter.Retrieve (internal/rag/adapter.go, Phase 1) returns
    	// (nil, false, nil) WITHOUT calling the underlying retriever — no
    	// HTTP call, no allocation beyond the inert Adapter/Embedder/store
    	// structs themselves — so effectivePrompt stays byte-identical to
    	// prompt and the request built below is IDENTICAL to the pre-HXC-118
    	// behavior. ANTI-BLUFF (§11.4.6): when enabled, ragAdapter.Retrieve
    	// performs a REAL embedding call + REAL in-memory cosine-similarity
    	// search (internal/rag/vectorstore.go); a retrieval failure degrades
    	// gracefully (logged, generation proceeds on the original prompt)
    	// rather than aborting the user's request.
    	effectivePrompt := prompt
    	ragAdapter := rag.NewFromEnv(os.Getenv)
    	if ragAdapter.Enabled() {
    		ragDocs, ragRan, ragErr := ragAdapter.Retrieve(ctx, prompt, rag.RetrieveOptionsFromEnv(os.Getenv))
    		if ragErr != nil {
    			log.Printf("rag: retrieval failed, continuing without RAG context: %v", ragErr)
    		} else if ragRan && len(ragDocs) > 0 {
    			effectivePrompt = rag.PrependContext(prompt, ragDocs)
    		}
    	}
    
    	// Create generation request with the prompt as a user message
    	req := &llm.LLMRequest{
    		Model:       modelName,
    		MaxTokens:   maxTokens,
    		Temperature: temperature,
    		Stream:      stream,
    		Messages: []llm.Message{
    			{Role: "user", Content: effectivePrompt},
    		},
    	}
    
    	// P1-F20-T06: Theme + Styler construction.
    	//
    	// Building the registry / theme / depth here (rather than once at process
    	// start) keeps the wire-in scoped to the only call site that consumes it
    	// in F20 (the non-stream LLM body print). The cost is one map allocation
    	// + an opportunistic YAML stat per generate; both are negligible relative
    	// to the LLM call itself, and centralising the construction would require
    	// threading a Styler through CLI's struct and Run() which are out of
    	// scope for this task.
    	//
    	// Failure mode: every fallible step degrades gracefully. YAML missing /
    	// malformed -> log + use built-ins. Unknown theme name -> log + dark
    	// fallback. The user always gets a working binary; the worst case is
    	// that styling falls back to the dark baseline.
    	themeRegistry := theme.NewThemeRegistry()
    	if path := theme.DefaultThemePath(os.Getenv); path != "" {
    		if err := themeRegistry.LoadFromFile(path); err != nil {
    			log.Printf("theme: yaml load failed (continuing with built-ins): %v", err)
    		}
    	}
    	themeName := theme.DetectThemeName(os.Getenv)
    	selectedTheme, themeErr := themeRegistry.Get(themeName)
    	if themeErr != nil {
    		// Should not happen for built-in names, but be defensive: fall back
    		// to dark explicitly so an exotic HELIXCODE_THEME value can never
    		// produce a zero-Theme that styles to no-op silently.
    		log.Printf("theme: get %q failed (%v), falling back to dark", themeName, themeErr)
    		selectedTheme, _ = themeRegistry.Get(theme.ThemeDark)
    		themeName = theme.ThemeDark
    	}
    	colorDepth := theme.DetectColorDepth(os.Getenv)
    
    	if stream {
    		// Real streaming from provider, wired through the P1-F18 Renderer
    		// so that token output respects HELIXCODE_RENDER + TTY detection
    		// (fancy = in-place line update, plain = line-buffered transcript).
    		r, rerr := render.NewRenderer(render.FactoryOptions{})
    		if rerr != nil {
    			// Constructing a renderer should be infallible for default
    			// options; surface the error rather than silently dropping
    			// to fmt.Printf — callers need to know if config is broken.
    			return fmt.Errorf("renderer init failed: %w", rerr)
    		}
    		defer func() { _ = r.Close() }()
    		// Plain-mode protection (F20 spec §11): even though we don't style
    		// the streaming path in v1, log the active theme depth AFTER the
    		// adjustment so operators see the same observable depth they'd see
    		// for the non-stream branch.
    		effectiveDepth := adjustDepthForRenderer(r, colorDepth)
    		log.Printf("theme: name=%s depth=%s", themeName, effectiveDepth)
    
    		chunkChan := make(chan llm.LLMResponse, 100)
    		errCh := make(chan error, 1)
    		go func() {
    			errCh <- provider.GenerateStream(ctx, req, chunkChan)
    		}()
    
    		blockID := "llm-" + modelName
    		if rerr := streamToRenderer(ctx, chunkChan, r, blockID); rerr != nil {
    			// Drain provider error before returning to avoid goroutine leak.
    			<-errCh
    			return fmt.Errorf("renderer stream failed: %w", rerr)
    		}
    		if perr := <-errCh; perr != nil {
    			return fmt.Errorf("streaming generation failed: %w", perr)
    		}
    	} else {
    		// Real non-streaming from provider, wired through the P1-F18
    		// Renderer (T08) so the full LLM response respects HELIXCODE_RENDER
    		// + TTY detection. Plain mode -> zero-ANSI/zero-CR transcript;
    		// fancy mode -> hide-cursor + per-line emit. The blockID is empty
    		// (one-shot) because a non-stream completion is not re-rendered.
    		//
    		// P1-F20-T06: decorate the response body with theme.Styler before
    		// it reaches RenderTextBlock. The depth is forced to DepthOff when
    		// the renderer is in plain mode so the styler emits zero ANSI bytes
    		// that the plain renderer would otherwise pass through verbatim.
    		r, rerr := render.NewRenderer(render.FactoryOptions{})
    		if rerr != nil {
    			return fmt.Errorf("renderer init failed: %w", rerr)
    		}
    		defer func() { _ = r.Close() }()
    
    		effectiveDepth := adjustDepthForRenderer(r, colorDepth)
    		styler := theme.NewStyler(selectedTheme, effectiveDepth)
    		log.Printf("theme: name=%s depth=%s", themeName, effectiveDepth)
    
    		resp, err := provider.Generate(ctx, req)
    		if err != nil {
    			return fmt.Errorf("generation failed: %w", err)
    		}
    		if perr := printResponseThroughRendererStyled(r, styler, resp.Content); perr != nil {
    			return fmt.Errorf("renderer print failed: %w", perr)
    		}
    		// Round 41 readiness fix (CONST-035): modern CLI agents
    		// (Claude Code, Aider, Cline) report token usage + duration
    		// after each generation so users can track consumption.
    		// Previously the CLI swallowed this telemetry — the user
    		// got the response but no insight into cost/usage.
    		if resp != nil {
    			printGenerationStats(resp, contextWindowForModel(provider, modelName))
    		}
    	}
    
    	fmt.Printf("\n✅ Generation completed\n")
    	return nil
    }
    
    // printGenerationStats surfaces token usage + processing time per turn so
    // users can see consumption like they'd see in Claude Code / Aider / Cline.
    // Anti-bluff: only prints fields that the provider populated; absence-of-data
    // is honestly shown rather than fabricated zero counts.
    //
    // contextWindow is the active model's REAL context-window size (in tokens),
    // resolved from the provider's model catalogue (ModelInfo.ContextSize). When
    // it is > 0 the render adds a `context: <used>/<window> (NN%)` indicator so
    // the user can see how close they are to the model limit. When it is 0 (the
    // provider did not report a window for the active model) the indicator is
    // OMITTED entirely — never fabricated with a guessed denominator (CONST-035).
    func printGenerationStats(resp *llm.LLMResponse, contextWindow int) {
    	if resp == nil {
    		return
    	}
    	in, out := resp.Usage.PromptTokens, resp.Usage.CompletionTokens
    	if in == 0 && out == 0 && resp.ProcessingTime == 0 {
    		// Provider didn't populate any telemetry; don't fabricate it.
    		return
    	}
    	fmt.Printf("\n📊 %s", trc("cli_tokens_summary", map[string]any{
    		"In": in, "Out": out, "Total": in + out,
    	}))
    	// Context-window USED-% indicator. Computed from the REAL total token
    	// count (prompt+completion) against the REAL model window. Emitted only
    	// when the window is known; formatContextUsage returns "" otherwise.
    	if usage := formatContextUsage(in+out, contextWindow); usage != "" {
    		fmt.Print(usage)
    	}
    	if resp.ProcessingTime > 0 {
    		fmt.Printf("   time: %s", resp.ProcessingTime.Round(time.Millisecond))
    		if out > 0 && resp.ProcessingTime.Seconds() > 0 {
    			tps := float64(out) / resp.ProcessingTime.Seconds()
    			fmt.Printf("   tps: %.1f", tps)
    		}
    	}
    	if resp.FinishReason != "" {
    		fmt.Printf("   finish: %s", resp.FinishReason)
    	}
    	fmt.Println()
    }
    
    // formatContextUsage renders the context-window USED-% indicator as a pure
    // function so it is unit-testable in isolation.
    //
    //   - When window <= 0 (the model's context-window size is NOT reliably known
    //     for the active model) it returns "" — the caller OMITS the indicator
    //     rather than printing a fabricated percentage against a guessed window.
    //     This is the honest-conditional mandated by CONST-035: no fake denominator.
    //   - When window > 0 it returns "   context: <used>/<window> (NN%)" where NN
    //     is the integer percentage of used/window. used is clamped to >= 0; the
    //     percentage is allowed to exceed 100 (an honest signal that the reported
    //     token total ran past the catalogue window) rather than being silently
    //     capped, but never goes negative.
    func formatContextUsage(used, window int) string {
    	if window <= 0 {
    		return ""
    	}
    	if used < 0 {
    		used = 0
    	}
    	pct := used * 100 / window
    	return fmt.Sprintf("   context: %d/%d (%d%%)", used, window, pct)
    }
    
    // contextWindowForModel resolves the REAL context-window size (in tokens) for
    // the named model. It returns 0 when no real window is reachable — in which
    // case the caller honestly omits the indicator (CONST-035: never substitute a
    // guessed window).
    //
    // Resolution order, both REAL sources (no hardcoded fallback):
    //  1. The provider's model catalogue (ModelInfo.ContextSize) matched by Name
    //     then ID, mirroring how modelName is resolved on the request path — the
    //     most precise per-model figure.
    //  2. The provider's GetContextWindow() — the active model's window reported
    //     by the provider — used when the catalogue has no matching entry or it
    //     reports 0 for that model.
    func contextWindowForModel(provider llm.Provider, modelName string) int {
    	if provider == nil {
    		return 0
    	}
    	if modelName != "" {
    		for _, m := range provider.GetModels() {
    			if m.Name == modelName || m.ID == modelName {
    				if m.ContextSize > 0 {
    					return m.ContextSize
    				}
    				break
    			}
    		}
    	}
    	if w := provider.GetContextWindow(); w > 0 {
    		return w
    	}
    	return 0
    }
    
    // expandAtMentions scans `*prompt` for `@<path>` tokens and, for each
    // token that resolves to a readable file on disk, appends a context
    // block at the end of the prompt of the form:
    //
    //	<attached_files>
    //	<file path="path/to/file">
    //	... content ...
    //	</file>
    //	...
    //	</attached_files>
    //
    // Modern-CLI-agent parity feature (Claude Code, Cursor, Aider).
    // Returns the list of resolved paths so the REPL can surface them
    // to the user. Tokens that don't resolve to a file stay verbatim in
    // the prompt — the LLM sees them as-is. Per-file size cap is 256 KiB
    // to keep prompts within typical context budgets; oversized files
    // are listed with a `(skipped: too large)` note but not embedded.
    //
    // CONST-035 anti-bluff: this function never silently invents content
    // for a missing file. Every attached path corresponds to a real file
    // read at runtime.
    func expandAtMentions(prompt *string) []string {
    	if prompt == nil || *prompt == "" {
    		return nil
    	}
    	const maxFileBytes = 256 * 1024
    	tokens := atMentionTokens(*prompt)
    	if len(tokens) == 0 {
    		return nil
    	}
    	attached := make([]string, 0, len(tokens))
    	var blocks []string
    	seen := make(map[string]bool, len(tokens))
    	for _, tok := range tokens {
    		path := strings.TrimPrefix(tok, "@")
    		if seen[path] {
    			continue
    		}
    		seen[path] = true
    		info, err := os.Stat(path)
    		if err != nil || info.IsDir() {
    			continue
    		}
    		if info.Size() > maxFileBytes {
    			blocks = append(blocks, fmt.Sprintf(
    				`<file path=%q size_bytes=%d>%s</file>`,
    				path, info.Size(),
    				trc("cli_file_skipped_too_large", nil),
    			))
    			attached = append(attached, path+trc("cli_file_skipped_label", nil))
    			continue
    		}
    		body, err := os.ReadFile(path)
    		if err != nil {
    			continue
    		}
    		blocks = append(blocks, fmt.Sprintf(
    			"<file path=%q>\n%s\n</file>",
    			path, string(body),
    		))
    		attached = append(attached, path)
    	}
    	if len(blocks) == 0 {
    		return nil
    	}
    	*prompt = *prompt + "\n\n<attached_files>\n" + strings.Join(blocks, "\n") + "\n</attached_files>"
    	return attached
    }
    
    // atMentionTokens extracts `@<token>` mentions from text. A token is
    // a maximal run of non-whitespace characters after `@`, stripped of
    // trailing punctuation (comma, period, semicolon, colon, close
    // paren/bracket/brace, quote) that's commonly attached to a path in
    // prose but isn't part of the path itself.
    //
    // Tokens like `@README.md` capture the trailing `.md`; tokens like
    // `@docs/file.go.` strip the trailing dot. Tokens shorter than 2
    // chars after `@` are ignored (avoids matching e.g. `@`-as-pronoun).
    func atMentionTokens(text string) []string {
    	var tokens []string
    	for i := 0; i < len(text); i++ {
    		if text[i] != '@' {
    			continue
    		}
    		// `@` must be at start-of-text or NOT preceded by an
    		// identifier-character (letter/digit/underscore/dot), so we
    		// don't pick up emails like `user@host` or struct-tag forms.
    		// Punctuation like `(` or `,` is fine — `(@path)` should match.
    		if i > 0 {
    			c := text[i-1]
    			isIdent := (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
    				(c >= '0' && c <= '9') || c == '_' || c == '.'
    			if isIdent {
    				continue
    			}
    		}
    		end := i + 1
    		for end < len(text) {
    			c := text[end]
    			if c == ' ' || c == '\t' || c == '\n' || c == '\r' {
    				break
    			}
    			end++
    		}
    		tok := text[i:end]
    		// Strip trailing prose punctuation.
    		tok = strings.TrimRight(tok, ".,;:)]}\"'!?")
    		if len(tok) < 3 { // `@` + ≥2 chars
    			continue
    		}
    		tokens = append(tokens, tok)
    		i = end - 1
    	}
    	return tokens
    }
    
    // streamToRenderer pumps LLM streaming chunks from ch through the supplied
    // Renderer using the Begin -> WriteToken... -> Commit token-streaming flow
    // (P1-F18 spec §4.1, §11.6).
    //
    // Invariants enforced:
    //   - Begin is called exactly once with blockID before the first WriteToken.
    //   - Commit runs unconditionally on return so the trailing newline is always
    //     emitted, even if the producer goroutine returns an error mid-stream.
    //   - ctx cancellation aborts the loop without surfacing an error from the
    //     channel-drain phase; the caller is responsible for joining the producer
    //     goroutine separately.
    //
    // The function reads until ch is closed by the producer; it does NOT close the
    // channel itself. Empty content chunks are forwarded so the renderer's
    // auto-Begin / no-op fast paths get exercised in the same way the production
    // path will see them under real provider implementations.
    func streamToRenderer(ctx context.Context, ch <-chan llm.LLMResponse, r render.Renderer, blockID string) (retErr error) {
    	if err := r.Begin(blockID); err != nil {
    		return fmt.Errorf("renderer begin: %w", err)
    	}
    	defer func() {
    		if cerr := r.Commit(); cerr != nil && retErr == nil {
    			retErr = fmt.Errorf("renderer commit: %w", cerr)
    		}
    	}()
    
    	for {
    		select {
    		case <-ctx.Done():
    			return ctx.Err()
    		case chunk, ok := <-ch:
    			if !ok {
    				return nil
    			}
    			if err := r.WriteToken(chunk.Content); err != nil {
    				return fmt.Errorf("renderer write: %w", err)
    			}
    		}
    	}
    }
    
    // printResponseThroughRenderer prints a single non-stream LLM response
    // through the P1-F18 Renderer (T08) so the bytes flow through plain/fancy
    // resolution + zero-ANSI/zero-CR enforcement instead of bare fmt.Println.
    //
    // Construction is local: the non-stream branch in handleGenerate doesn't
    // need a longer-lived renderer (the response is one-shot, never re-rendered),
    // so we build, render, and Close on each call. blockID is empty so
    // RenderTextBlock generates a synthetic one-shot ID under the hood.
    //
    // Empty content is a no-op (mirrors RenderTextBlock contract); since
    // fmt.Println("") would have emitted a stray newline we keep the empty case
    // silent here to preserve transcript hygiene.
    //
    // Retained as a thin wrapper for the F18-T08 smoke tests; new call sites
    // should prefer printResponseThroughRendererStyled which composes the
    // theme.Styler decorator (P1-F20-T06) on top of the same RenderTextBlock
    // path.
    func printResponseThroughRenderer(content string) error {
    	r, err := render.NewRenderer(render.FactoryOptions{})
    	if err != nil {
    		return fmt.Errorf("renderer init failed: %w", err)
    	}
    	defer func() { _ = r.Close() }()
    	return printResponseThroughRendererStyled(r, nil, content)
    }
    
    // adjustDepthForRenderer collapses requested to theme.DepthOff when r is in
    // plain mode; otherwise returns requested unchanged.
    //
    // Load-bearing per F20 spec §11: "plain mode forces zero color emission
    // regardless of theme setting". The plain renderer's pass-through clause
    // (plain_renderer.go: tool-output passthrough lets caller-supplied ANSI
    // bytes reach the writer verbatim) means we MUST prevent the Styler from
    // emitting ANSI in the first place when targeting a plain renderer —
    // otherwise pre-styled bytes from Stylize() would leak straight through into
    // log files and pipes. Forcing the depth here rather than down-converting
    // the bytes keeps the chokepoint at a single, testable function.
    //
    // The fancy renderer applies its own escape sequences for cursor moves and
    // dirty-line redraws, but it does NOT strip caller-supplied ANSI either; for
    // fancy mode we WANT the styler to colour the text, so we pass the requested
    // depth through untouched.
    func adjustDepthForRenderer(r render.Renderer, requested theme.ColorDepth) theme.ColorDepth {
    	if r != nil && r.Mode() == render.ModePlain {
    		return theme.DepthOff
    	}
    	return requested
    }
    
    // printResponseThroughRendererStyled prints a single non-stream LLM response
    // through the supplied Renderer, optionally decorating the text with the
    // supplied Styler before passing it to RenderTextBlock.
    //
    // Why styling lives here and NOT in streamToRenderer: the streaming hot path
    // emits one token at a time and relies on a stable per-line dirty-diff in
    // fancy mode; injecting a per-token open/close sequence would either
    // fragment the role-styled region across ANSI clear-line boundaries or
    // require buffering the entire stream before colouring it. Both options
    // regress streaming UX. We therefore restrict styling to the non-stream
    // branch in v1, which is the plan's documented compromise (cf. plan T06).
    //
    // Role choice: theme.RoleHighlight. Justification: the LLM's final answer is
    // the most important content in the transcript and benefits from a visual
    // pop relative to the surrounding `=== Generating … ===` headers and the
    // trailing `✅ Generation completed`, both of which are emitted via bare
    // fmt.Printf and remain unstyled. RoleInfo would render the LLM body the
    // same colour as routine status messages and lose the contrast.
    //
    // nil styler short-circuits to RenderTextBlock with the raw content — used
    // by callers that opt out of theming (e.g., the F18 backward-compat shim
    // printResponseThroughRenderer above) and by tests.
    //
    // Empty content is a no-op (mirrors RenderTextBlock contract).
    func printResponseThroughRendererStyled(r render.Renderer, styler *theme.Styler, content string) error {
    	if styler != nil {
    		content = styler.Stylize(theme.RoleHighlight, content)
    	}
    	return render.RenderTextBlock(r, "", content)
    }
    
    // handleNotification sends a notification
    func (c *CLI) handleNotification(ctx context.Context, message, notifyType, priority string) error {
    	notificationType := notification.NotificationType(notifyType)
    	notificationPriority := notification.NotificationPriority(priority)
    
    	notif := &notification.Notification{
    		Title:    "CLI Notification",
    		Message:  message,
    		Type:     notificationType,
    		Priority: notificationPriority,
    		Channels: []string{"cli"}, // Default to CLI output
    	}
    
    	if err := c.notificationEngine.SendDirect(ctx, notif, []string{"cli"}); err != nil {
    		return fmt.Errorf("failed to send notification: %v", err)
    	}
    
    	fmt.Printf("✅ %s\n", tr(ctx, "cli_notification_sent", map[string]any{"Message": message}))
    	return nil
    }
    
    // exitCodeError carries a child process's real exit code up to main() so the
    // CLI process can surface that exact code as its own exit status.
    //
    // ANTI-BLUFF (BLUFF-003 FIX): a `--command` invocation is a transparent proxy
    // for the child process. If `helixcode --command 'exit 42'` exited 1 (because
    // main() blanket-mapped every Run() error to log.Fatalf → exit 1), the child's
    // real exit code would be silently lost — breaking shell scripts and CI gates
    // that branch on the exit status. The real code MUST propagate to the process.
    type exitCodeError struct {
    	code int
    	err  error
    }
    
    func (e *exitCodeError) Error() string { return e.err.Error() }
    func (e *exitCodeError) Unwrap() error { return e.err }
    func (e *exitCodeError) ExitCode() int { return e.code }
    
    // handleCommand executes a command locally via os/exec.
    // ANTI-BLUFF (BLUFF-003 FIX): This executes REAL commands, not simulations,
    // and surfaces the child process's REAL exit code as the CLI's exit code.
    func (c *CLI) handleCommand(ctx context.Context, command string) error {
    	fmt.Printf("\n=== Executing Command ===\n")
    	fmt.Printf("Command: %s\n\n", command)
    
    	cmd := exec.CommandContext(ctx, "sh", "-c", command)
    	cmd.Stdout = os.Stdout
    	cmd.Stderr = os.Stderr
    
    	if err := cmd.Run(); err != nil {
    		// If the child ran to completion with a non-zero status, surface that
    		// exact code (via *exec.ExitError → ProcessState.ExitCode()). For any
    		// other failure (sh not found, killed by signal, context cancelled),
    		// fall back to a generic non-zero exit so genuine errors stay non-zero.
    		var exitErr *exec.ExitError
    		if errors.As(err, &exitErr) {
    			code := exitErr.ProcessState.ExitCode()
    			if code <= 0 {
    				// Signalled/terminated abnormally — ExitCode() returns -1.
    				// Map to a generic non-zero so the proxy never reports success.
    				code = 1
    			}
    			return &exitCodeError{code: code, err: fmt.Errorf("command failed: %w", err)}
    		}
    		return fmt.Errorf("command failed: %w", err)
    	}
    
    	fmt.Printf("\n✅ %s\n", tr(ctx, "cli_command_completed",
    		map[string]any{"ExitCode": cmd.ProcessState.ExitCode()}))
    	return nil
    }
    
    // handleInteractive starts the conversational REPL.
    //
    // Round 41 readiness fix (CONST-035): the prior REPL only accepted 6
    // hardcoded commands (workers/models/health/help/exit/quit) and used
    // fmt.Scanln which reads a single whitespace-delimited token — so a
    // multi-word prompt like "What is 2+2?" was silently truncated to
    // "What" and dispatched as an unknown command. The REPL did NOT
    // accept LLM prompts at all, which is the core feature a user expects
    // from a modern CLI agent (Claude Code, Aider, Cline).
    //
    // Now: bufio.Scanner reads entire lines; lines that start with "/"
    // are treated as slash commands (/workers, /models, /health, /help,
    // /exit, /quit, /clear); plain text lines are sent to the resolved
    // LLM provider and the response is printed. Multi-turn context is
    // preserved across turns within a single REPL session.
    func (c *CLI) handleInteractive(ctx context.Context) error {
    	// Brand identity: print the lime/teal HelixCode wordmark banner at REPL
    	// startup. brandBanner() is NO_COLOR / non-TTY aware (no escapes emitted
    	// when color is disabled), so piped/redirected sessions stay clean.
    	fmt.Print(brandBanner())
    	fmt.Println(brandHeading(tr(ctx, "cli_repl_header", nil)))
    	fmt.Println(brandInfo(tr(ctx, "cli_repl_intro", nil)))
    	if c.llmProvider != nil {
    		if models := c.llmProvider.GetModels(); len(models) > 0 {
    			fmt.Println(tr(ctx, "cli_provider_default_model", map[string]any{
    				"Provider": c.llmProvider.GetName(),
    				"Model":    models[0].Name,
    			}))
    		}
    	}
    
    	// Set up signal handling for graceful shutdown
    	sigChan := make(chan os.Signal, 1)
    	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
    	defer signal.Stop(sigChan)
    
    	scanner := bufio.NewScanner(os.Stdin)
    	// Allow long prompts (default 64 KB is too small for big code snippets).
    	scanner.Buffer(make([]byte, 0, 1<<20), 16<<20)
    
    	// Multi-turn conversation state (in-session only — F11 session
    	// persistence is via the sessions subcommand).
    	var conversation []llm.Message
    
    	for {
    		select {
    		case <-sigChan:
    			fmt.Println()
    			fmt.Println()
    			fmt.Println(tr(ctx, "cli_repl_shutting_down", nil))
    			return nil
    		default:
    		}
    
    		fmt.Print("\n" + brandPrompt("helix>") + " ")
    		if !scanner.Scan() {
    			if err := scanner.Err(); err != nil {
    				return fmt.Errorf("REPL read error: %w", err)
    			}
    			// EOF — clean exit
    			fmt.Println(tr(ctx, "cli_repl_goodbye", nil))
    			return nil
    		}
    
    		input := strings.TrimSpace(scanner.Text())
    		if input == "" {
    			continue
    		}
    
    		// Slash commands (extended with /clear, /reset). Backward-
    		// compat: bare 'exit'/'quit'/'help'/'workers'/'models'/'health'
    		// still recognised since the old REPL used those.
    		lower := strings.ToLower(input)
    		switch lower {
    		case "/exit", "/quit", "exit", "quit":
    			fmt.Println(tr(ctx, "cli_repl_goodbye", nil))
    			return nil
    		case "/help", "help":
    			c.showHelp(ctx)
    			continue
    		case "/workers", "workers":
    			_ = c.handleListWorkers(ctx)
    			continue
    		case "/models", "models":
    			_ = c.handleListModels(ctx)
    			continue
    		case "/health", "health":
    			_ = c.handleHealthCheck(ctx)
    			continue
    		case "/clear", "/reset":
    			conversation = conversation[:0]
    			fmt.Println("(conversation history cleared)")
    			continue
    		case "/undo", "undo":
    			_ = c.handleUndo(ctx)
    			continue
    		}
    		// /diff [ref] — takes an optional ref argument, so it is matched by
    		// prefix (the bare-case switch above matches the whole line). Both
    		// "/diff" and the legacy bare "diff" are recognised, mirroring the
    		// other dual-form commands.
    		if lower == "/diff" || lower == "diff" ||
    			strings.HasPrefix(lower, "/diff ") || strings.HasPrefix(lower, "diff ") {
    			fields := strings.Fields(input)
    			ref := ""
    			if len(fields) > 1 {
    				ref = fields[1]
    			}
    			_ = c.handleDiff(ctx, ref)
    			continue
    		}
    		// /debate <prompt> — runs the prompt through the DebateOrchestrator
    		// 8-phase MASTER protocol wired to the CLI's REAL llm provider via
    		// speckit_debate_adapter.NewLLMBackedResponder. Prefix-matched (takes
    		// the remainder of the line as the debate topic), mirroring /diff.
    		if strings.HasPrefix(lower, "/debate ") || lower == "/debate" {
    			topic := strings.TrimSpace(strings.TrimPrefix(input, "/debate"))
    			_ = c.handleDebate(ctx, topic)
    			continue
    		}
    		// /specify <request> — runs the HelixSpecifier speckit Specify phase,
    		// backed by the REAL debate responder over the REAL llm provider.
    		// Prefix-matched (remainder of the line is the spec request), mirroring
    		// /debate.
    		if strings.HasPrefix(lower, "/specify ") || lower == "/specify" {
    			request := strings.TrimSpace(strings.TrimPrefix(input, "/specify"))
    			_ = c.handleSpecify(ctx, request)
    			continue
    		}
    		// /checkpoint [create [label] | list | restore <id>] — F12 workspace
    		// checkpoints: snapshot the working-tree file bytes now and restore
    		// them later. Prefix-matched (takes subcommand + args), mirroring the
    		// other multi-arg commands. Bare "/checkpoint" prints usage.
    		if lower == "/checkpoint" || strings.HasPrefix(lower, "/checkpoint ") {
    			args := strings.TrimSpace(strings.TrimPrefix(input, "/checkpoint"))
    			_ = c.handleCheckpoint(ctx, args)
    			continue
    		}
    		// Unknown slash command: surface clearly, don't send to LLM
    		if strings.HasPrefix(input, "/") {
    			fmt.Println(tr(ctx, "cli_repl_unknown_slash", map[string]any{"Input": input}))
    			continue
    		}
    
    		// Plain text: send as LLM prompt (the core REPL contract).
    		if c.llmProvider == nil {
    			fmt.Println(tr(ctx, "cli_repl_no_provider", nil))
    			continue
    		}
    
    		// @-file mentions (modern-CLI-agent parity with Claude Code,
    		// Cursor, Aider). The user types `@path/to/file` anywhere in
    		// the prompt; we resolve each token to a file on disk and
    		// attach its content as a context block at the end of the
    		// prompt. Tokens that don't resolve to a file stay verbatim
    		// (no scary error — the LLM sees them as-is).
    		promptToSend := input
    		if attached := expandAtMentions(&promptToSend); len(attached) > 0 {
    			for _, p := range attached {
    				fmt.Printf("  📎 attached: %s\n", p)
    			}
    		}
    
    		conversation = append(conversation, llm.Message{Role: "user", Content: promptToSend})
    
    		modelName := ""
    		if models := c.llmProvider.GetModels(); len(models) > 0 {
    			modelName = models[0].Name
    		}
    		req := &llm.LLMRequest{
    			Model:       modelName,
    			MaxTokens:   1000,
    			Temperature: 0.7,
    			// P1-T07 (speed programme): the REPL consumes the streaming
    			// provider API so tokens reach the terminal as they arrive
    			// (time-to-first-visible-token is the major perceived-speed
    			// lever). Stream is set true so providers that branch on the
    			// flag take their streaming code path.
    			Stream:   true,
    			Messages: conversation,
    		}
    		assembled, stats, err := streamREPLTurn(ctx, c.llmProvider, req)
    		if err != nil {
    			fmt.Printf("Error: %v\n", err)
    			// Pop the user turn so a retry doesn't accumulate bad state.
    			conversation = conversation[:len(conversation)-1]
    			continue
    		}
    		if assembled != "" {
    			conversation = append(conversation, llm.Message{Role: "assistant", Content: assembled})
    		}
    		if stats != nil {
    			printGenerationStats(stats, contextWindowForModel(c.llmProvider, modelName))
    		}
    	}
    }
    
    // streamREPLTurn drives one interactive-REPL turn over the provider's
    // streaming API (P1-T07, speed programme Phase 1).
    //
    // It prints each chunk's Content to stdout the instant it arrives — so the
    // first token is visible long before the completion finishes — while
    // accumulating the full text so the caller can append the assistant turn to
    // the conversation. The final chunk that carries a populated Usage block is
    // returned as `stats` so the caller can print token/usage telemetry exactly
    // as the buffered path did.
    //
    // Channel-close robustness: the llm.Provider streaming contract is NOT
    // uniform — Anthropic/OpenAI/Groq/DeepSeek `defer close(ch)` from inside
    // GenerateStream, but Ollama and the OpenAI-compatible provider return
    // WITHOUT closing the channel. A naive `for range chunkChan` therefore
    // deadlocks against the non-closing providers. We instead select on BOTH the
    // chunk channel AND the provider's return signal: once GenerateStream has
    // returned we drain whatever is still buffered, then stop — correct for both
    // the closing and the non-closing provider families.
    //
    // No-regression guarantee: the assembled string is the concatenation of every
    // chunk's Content, which is byte-identical to the buffered `Generate` result
    // for any conformant provider (each provider's GenerateStream emits the same
    // total content as Generate — verified by the per-provider GenerateStream
    // tests). Only WHEN the bytes appear changes, not WHAT appears.
    func streamREPLTurn(ctx context.Context, provider llm.Provider, req *llm.LLMRequest) (assembled string, stats *llm.LLMResponse, err error) {
    	chunkChan := make(chan llm.LLMResponse, 100)
    	errCh := make(chan error, 1)
    	go func() {
    		errCh <- provider.GenerateStream(ctx, req, chunkChan)
    	}()
    
    	var sb strings.Builder
    	var last llm.LLMResponse
    	haveStats := false
    	render := func(chunk llm.LLMResponse) {
    		if chunk.Content != "" {
    			fmt.Print(chunk.Content)
    			sb.WriteString(chunk.Content)
    		}
    		// Capture the most recent chunk that carried usage/timing telemetry
    		// so the post-turn stats line matches the buffered path.
    		if chunk.Usage.TotalTokens > 0 || chunk.Usage.PromptTokens > 0 ||
    			chunk.Usage.CompletionTokens > 0 || chunk.ProcessingTime > 0 {
    			last = chunk
    			haveStats = true
    		}
    	}
    
    	streamErr := drainProviderStream(chunkChan, errCh, render)
    	if streamErr != nil {
    		return "", nil, streamErr
    	}
    	// Terminate the streamed line so the stats line / next prompt start clean.
    	if sb.Len() > 0 {
    		fmt.Println()
    	}
    	if haveStats {
    		stats = &last
    	}
    	return sb.String(), stats, nil
    }
    
    // drainProviderStream consumes every chunk a provider's GenerateStream emits
    // onto chunkChan, invoking onChunk for each, and returns the provider's error.
    //
    // It is the single chokepoint that copes with the non-uniform channel-close
    // contract across llm.Provider implementations (see streamREPLTurn doc). The
    // algorithm:
    //   - Phase 1: select on chunkChan AND errCh. A chunk -> render it. A close of
    //     chunkChan (ok == false) -> the channel-closing providers finished;
    //     join errCh and return. An errCh send -> the non-closing providers
    //     finished; move to phase 2.
    //   - Phase 2: non-blocking drain of whatever is still buffered in chunkChan
    //     (the producer goroutine has already returned, so no more will arrive),
    //     then return the provider error.
    //
    // This terminates for BOTH provider families and never deadlocks.
    func drainProviderStream(chunkChan chan llm.LLMResponse, errCh chan error, onChunk func(llm.LLMResponse)) error {
    	for {
    		select {
    		case chunk, ok := <-chunkChan:
    			if !ok {
    				// Channel-closing provider: drain done, join the error.
    				return <-errCh
    			}
    			onChunk(chunk)
    		case provErr := <-errCh:
    			// Non-closing provider (Ollama / OpenAI-compatible): the producer
    			// has returned. Drain any chunks it left buffered, then stop.
    			for {
    				select {
    				case chunk, ok := <-chunkChan:
    					if !ok {
    						return provErr
    					}
    					onChunk(chunk)
    				default:
    					return provErr
    				}
    			}
    		}
    	}
    }
    
    // handleCheckpoint implements the /checkpoint REPL command (F12 workspace
    // checkpoints). It snapshots/restores the REAL working-tree file bytes via the
    // internal/checkpoint Manager (git plumbing when in a repo, real file-copy
    // otherwise) rooted at the CLI's current working directory.
    //
    // Subcommands:
    //
    //	/checkpoint create [label]   snapshot the working tree now
    //	/checkpoint list             list checkpoints (newest first)
    //	/checkpoint restore <id>     restore the working tree to a snapshot
    //
    // Anti-bluff (§11.4 / CONST-035): every path drives the real Manager against
    // the real filesystem — there is no fake/printed-only output. Restore
    // writes real bytes back to disk (covered by internal/checkpoint round-trip
    // tests).
    func (c *CLI) handleCheckpoint(ctx context.Context, args string) error {
    	_ = ctx
    	cwd, err := os.Getwd()
    	if err != nil {
    		fmt.Printf("❌ /checkpoint: cannot determine working directory: %v\n", err)
    		return err
    	}
    	mgr, err := checkpoint.NewManager(cwd)
    	if err != nil {
    		fmt.Printf("❌ /checkpoint: %v\n", err)
    		return err
    	}
    
    	fields := strings.Fields(args)
    	sub := "list"
    	if len(fields) > 0 {
    		sub = strings.ToLower(fields[0])
    	}
    
    	switch sub {
    	case "create":
    		label := strings.TrimSpace(strings.TrimPrefix(args, fields[0]))
    		id, err := mgr.Create(label)
    		if err != nil {
    			fmt.Printf("❌ /checkpoint create failed: %v\n", err)
    			return err
    		}
    		fmt.Printf("✅ checkpoint created: %s (backend: %s)\n", id, mgr.Backend())
    		if label != "" {
    			fmt.Printf("   label: %s\n", label)
    		}
    		return nil
    
    	case "list":
    		cps := mgr.List()
    		if len(cps) == 0 {
    			fmt.Println("(no checkpoints)")
    			return nil
    		}
    		fmt.Printf("Checkpoints (%s backend, newest first):\n", mgr.Backend())
    		for _, cp := range cps {
    			label := cp.Label
    			if label == "" {
    				label = "(no label)"
    			}
    			fmt.Printf("  %s  %s  %s\n", cp.ID, cp.CreatedAt.Format("2006-01-02 15:04:05"), label)
    		}
    		return nil
    
    	case "restore":
    		if len(fields) < 2 {
    			fmt.Println("usage: /checkpoint restore <id>")
    			return nil
    		}
    		id := fields[1]
    		report, err := mgr.RestoreReported(id)
    		if err != nil {
    			fmt.Printf("❌ /checkpoint restore failed: %v\n", err)
    			return err
    		}
    		// Anti-bluff (§11.4 / CONST-035): the git backend snapshots ONLY
    		// git-tracked content, so untracked working-tree files are NOT restored.
    		// Report the truth instead of an unconditional success — never claim the
    		// whole working tree was restored when untracked files were left as-is.
    		if report.FullyRestored() {
    			fmt.Printf("✅ working tree restored to checkpoint %s\n", id)
    		} else {
    			fmt.Printf("⚠️  tracked files restored to checkpoint %s, but %d untracked file(s) were NOT restored (the %s checkpoint only snapshots git-tracked content):\n",
    				id, len(report.UntrackedNotRestored), report.Backend)
    			for _, f := range report.UntrackedNotRestored {
    				fmt.Printf("     - %s\n", f)
    			}
    		}
    		return nil
    
    	default:
    		fmt.Println("usage: /checkpoint create [label] | list | restore <id>")
    		return nil
    	}
    }
    
    // replGit constructs an *autocommit.Git rooted at the process working
    // directory — the same construction the F22 auto-committer uses at bootstrap
    // (NewAutoCommitter with WorkingDir: os.Getwd()). It returns an error if the
    // working directory cannot be determined or is not inside a git work tree, so
    // the /diff and /undo REPL handlers refuse cleanly instead of shelling out
    // into a non-repo. The Git handle is cheap (two fields) and stateless across
    // reads, so building it per-command keeps the CLI struct unchanged.
    func (c *CLI) replGit(ctx context.Context) (*autocommit.Git, error) {
    	cwd, err := os.Getwd()
    	if err != nil {
    		return nil, fmt.Errorf("cannot determine working directory: %w", err)
    	}
    	g := autocommit.NewGit(cwd, zap.NewNop())
    	isRepo, err := g.IsRepo(ctx)
    	if err != nil {
    		return nil, err
    	}
    	if !isRepo {
    		return nil, fmt.Errorf("%s is not a git repository", cwd)
    	}
    	return g, nil
    }
    
    // handleDiff prints the real working-tree diff. With no argument it shows the
    // unstaged diff (`git diff` — "what changed in my working tree"); with a ref
    // argument it shows `git diff <ref>` (everything since that commit/tag/ref).
    // Output is verbatim git output — no placeholder text.
    func (c *CLI) handleDiff(ctx context.Context, ref string) error {
    	g, err := c.replGit(ctx)
    	if err != nil {
    		fmt.Printf("❌ /diff: %v\n", err)
    		return err
    	}
    	ref = strings.TrimSpace(ref)
    	var out string
    	if ref == "" {
    		out, err = g.DiffUnstaged(ctx)
    	} else {
    		out, err = g.DiffSinceRef(ctx, ref)
    	}
    	if err != nil {
    		fmt.Printf("❌ /diff failed: %v\n", err)
    		return err
    	}
    	if strings.TrimSpace(out) == "" {
    		fmt.Println("(no changes)")
    		return nil
    	}
    	fmt.Print(out)
    	if !strings.HasSuffix(out, "\n") {
    		fmt.Println()
    	}
    	return nil
    }
    
    // handleDebate runs the given topic through DebateOrchestrator's 8-phase
    // MASTER protocol, wired to the CLI's REAL llm provider. This is the
    // production call-site for speckit_debate_adapter.NewLLMBackedResponder:
    // the orchestrator's ProviderInvoker is supplied by wrapping the CLI's
    // existing *llm.Provider.Generate (the same provider the REPL uses for plain
    // prompts) into the (ctx, prompt) (string, error) shape the adapter requires.
    //
    // Anti-bluff (§11.4 / CONST-035): the invoker makes a REAL provider.Generate
    // call — no fabricated/synthesised output. When no provider is configured the
    // command refuses cleanly rather than fabricating a debate. The adapter
    // itself refuses (ErrSpeckitDebateInvokerNotProvided) if a nil invoker were
    // ever passed, and surfaces orchestrator errors verbatim.
    func (c *CLI) handleDebate(ctx context.Context, topic string) error {
    	topic = strings.TrimSpace(topic)
    	if topic == "" {
    		fmt.Println("usage: /debate <topic>")
    		return nil
    	}
    	if c.llmProvider == nil {
    		fmt.Println(tr(ctx, "cli_repl_no_provider", nil))
    		return nil
    	}
    
    	// Resolve the provider's first advertised model. The orchestrator's
    	// RegisterProvider requires a non-empty model name, and the provider's
    	// Generate needs one too, so a provider with zero models cannot drive a
    	// debate — refuse cleanly rather than hand the adapter an empty spec
    	// that RegisterProvider would reject (§11.4.6 no-guessing).
    	provider := c.llmProvider
    	modelName := ""
    	if models := provider.GetModels(); len(models) > 0 {
    		modelName = models[0].Name
    	}
    	if strings.TrimSpace(modelName) == "" {
    		fmt.Println("❌ /debate: active provider advertises no models; cannot run a debate")
    		return nil
    	}
    
    	// Wrap the REAL CLI provider into the adapter's ProviderInvoker shape.
    	// provider.Generate is (ctx, *LLMRequest) (*LLMResponse, error); the
    	// orchestrator wants (ctx, prompt) (string, error). This closure is the
    	// honest seam: every debate agent turn round-trips through a real LLM
    	// call against c.llmProvider.
    	invoker := func(ictx context.Context, prompt string) (string, error) {
    		resp, err := provider.Generate(ictx, &llm.LLMRequest{
    			Model:       modelName,
    			MaxTokens:   1000,
    			Temperature: 0.7,
    			Messages:    []llm.Message{{Role: "user", Content: prompt}},
    		})
    		if err != nil {
    			return "", err
    		}
    		if resp == nil {
    			return "", fmt.Errorf("provider returned nil response")
    		}
    		return resp.Content, nil
    	}
    
    	responder, err := speckit_debate_adapter.NewLLMBackedResponder(
    		invoker,
    		// Two agents (same provider/model, distinct scores) — the debate orchestrator
    		// requires >=2 participants (MinAgentsPerDebate); a single agent fails at
    		// runtime with "insufficient agents (have 1, need 2)" (HXC-080).
    		[]speckit_debate_adapter.AgentSpec{
    			{Provider: provider.GetName(), Model: modelName, Score: 0.9},
    			{Provider: provider.GetName(), Model: modelName, Score: 0.85},
    		},
    	)
    	if err != nil {
    		fmt.Printf("❌ /debate setup failed: %v\n", err)
    		return err
    	}
    
    	out, err := responder.Generate(ctx, topic)
    	if err != nil {
    		fmt.Printf("❌ /debate failed: %v\n", err)
    		return err
    	}
    	fmt.Print(out)
    	if !strings.HasSuffix(out, "\n") {
    		fmt.Println()
    	}
    	return nil
    }
    
    // handleSpecify runs HelixSpecifier's speckit Specify phase against the CLI's
    // REAL llm provider. It mirrors handleDebate's provider→responder wiring, then
    // drives the real speckit engine:
    //
    //	pillar := speckit.NewPillar(config.DefaultConfig(), logger)
    //	pillar.SetDebateFunc(speckit.LLMBackedDebateFunc(responder))
    //	result, err := pillar.ExecutePhase(ctx, types.PhaseSpecify, &types.PhaseInput{...})
    //
    // Anti-bluff (§11.4 / CONST-035): the responder round-trips every phase debate
    // turn through a REAL provider.Generate call — no fabricated output. The speckit
    // engine itself REQUIRES a real DebateFunc: a nil DebateFunc returns
    // speckit.ErrDebateFuncNotConfigured (the round-28 §11.4 audit removed the prior
    // fabricating branch). When no provider/model is configured, OR the engine/debate
    // returns an error, the command surfaces the REAL error rather than fabricating
    // any phase output.
    func (c *CLI) handleSpecify(ctx context.Context, request string) error {
    	request = strings.TrimSpace(request)
    	if request == "" {
    		fmt.Println("usage: /specify <request>")
    		return nil
    	}
    	if c.llmProvider == nil {
    		fmt.Println(tr(ctx, "cli_repl_no_provider", nil))
    		return nil
    	}
    
    	// Resolve the provider's first advertised model (same guard as /debate):
    	// the responder's RegisterProvider + the provider's Generate both require a
    	// non-empty model name (§11.4.6 no-guessing).
    	provider := c.llmProvider
    	modelName := ""
    	if models := provider.GetModels(); len(models) > 0 {
    		modelName = models[0].Name
    	}
    	if strings.TrimSpace(modelName) == "" {
    		fmt.Println("❌ /specify: active provider advertises no models; cannot run the specify phase")
    		return nil
    	}
    
    	// Wrap the REAL CLI provider into the adapter's ProviderInvoker shape —
    	// identical honest seam to handleDebate.
    	invoker := func(ictx context.Context, prompt string) (string, error) {
    		resp, err := provider.Generate(ictx, &llm.LLMRequest{
    			Model:       modelName,
    			MaxTokens:   1000,
    			Temperature: 0.7,
    			Messages:    []llm.Message{{Role: "user", Content: prompt}},
    		})
    		if err != nil {
    			return "", err
    		}
    		if resp == nil {
    			return "", fmt.Errorf("provider returned nil response")
    		}
    		return resp.Content, nil
    	}
    
    	responder, err := speckit_debate_adapter.NewLLMBackedResponder(
    		invoker,
    		// Two agents (same provider/model, distinct scores) — the debate orchestrator
    		// requires >=2 participants (MinAgentsPerDebate); a single agent fails at
    		// runtime with "insufficient agents (have 1, need 2)" (HXC-080).
    		[]speckit_debate_adapter.AgentSpec{
    			{Provider: provider.GetName(), Model: modelName, Score: 0.9},
    			{Provider: provider.GetName(), Model: modelName, Score: 0.85},
    		},
    	)
    	if err != nil {
    		fmt.Printf("❌ /specify setup failed: %v\n", err)
    		return err
    	}
    
    	// Build the real speckit pillar and wire the REAL debate responder into it
    	// via the canonical SetDebateFunc(LLMBackedDebateFunc(responder)) path. A nil
    	// DebateFunc would make ExecutePhase return ErrDebateFuncNotConfigured.
    	pillar := speckit.NewPillar(speckitconfig.DefaultConfig(), logrus.New())
    	pillar.SetDebateFunc(speckit.LLMBackedDebateFunc(responder))
    
    	result, err := pillar.ExecutePhase(ctx, speckittypes.PhaseSpecify, &speckittypes.PhaseInput{
    		UserRequest: request,
    	})
    	if err != nil {
    		// Surfaces the REAL error verbatim — including
    		// speckit.ErrDebateFuncNotConfigured and any debate/provider failure.
    		// Never a fabricated phase output (§11.4 / CONST-035).
    		fmt.Printf("❌ /specify failed: %v\n", err)
    		return err
    	}
    	if result == nil {
    		err := fmt.Errorf("speckit ExecutePhase returned nil result")
    		fmt.Printf("❌ /specify failed: %v\n", err)
    		return err
    	}
    
    	fmt.Printf("# Specify phase (quality score %.3f, debate %s)\n",
    		result.QualityScore, result.DebateID)
    	fmt.Print(result.Output)
    	if !strings.HasSuffix(result.Output, "\n") {
    		fmt.Println()
    	}
    	return nil
    }
    
    // handleUndo reverts the last commit via autocommit.Git.RevertLastCommit
    // (`git revert --no-edit HEAD` — a NEW commit that inverts HEAD, never a
    // history rewrite, so it is force-push-free per §11.4.113) and prints the
    // resulting HEAD SHA. Real git result only.
    func (c *CLI) handleUndo(ctx context.Context) error {
    	g, err := c.replGit(ctx)
    	if err != nil {
    		fmt.Printf("❌ /undo: %v\n", err)
    		return err
    	}
    	head, err := g.RevertLastCommit(ctx)
    	if err != nil {
    		fmt.Printf("❌ /undo failed: %v\n", err)
    		return err
    	}
    	fmt.Printf("✅ reverted last commit; HEAD is now %s\n", head)
    	return nil
    }
    
    // showHelp displays available commands.
    //
    // Round-202 §11.4 (CONST-046 Phase 4 round 85, 2026-05-19): the seven
    // highest-impact help-screen lines (section headers + the five `slash`
    // command entries) are routed through tr() so non-English users see a
    // locale-appropriate help screen. The eleven Command-Line-Options lines
    // remain literal in this round because they refer to verbatim flag
    // names (--list-workers, --user, etc.) which MUST stay machine-stable
    // across locales — they are command-line tokens, not human-readable
    // content, and translating them would break shell scripts that parse
    // the help screen. A future round MAY migrate the trailing description
    // half of each option line while keeping the flag-name half literal.
    func (c *CLI) showHelp(ctx context.Context) {
    	fmt.Println()
    	fmt.Println(tr(ctx, "cli_help_commands_header", nil))
    	fmt.Println(tr(ctx, "cli_help_cmd_workers", nil))
    	fmt.Println(tr(ctx, "cli_help_cmd_models", nil))
    	fmt.Println(tr(ctx, "cli_help_cmd_health", nil))
    	// /diff and /undo are git command tokens (machine-stable, like the
    	// flag-name help lines below): plain literals rather than tr() keys so
    	// they stay consistent across locales and don't require new i18n keys.
    	fmt.Println("/diff [ref]      - Show working-tree git diff (optionally since <ref>)")
    	fmt.Println("/undo            - Revert the last commit (git revert HEAD)")
    	fmt.Println("/debate <topic>  - Run a real LLM-backed debate on <topic>")
    	fmt.Println("/specify <req>   - Run the speckit Specify phase (real LLM-backed) for <req>")
    	fmt.Println("/checkpoint ...  - Snapshot/restore working-tree files (create [label] | list | restore <id>)")
    	fmt.Println(tr(ctx, "cli_help_cmd_help", nil))
    	fmt.Println(tr(ctx, "cli_help_cmd_exit", nil))
    	fmt.Println("")
    	fmt.Println(tr(ctx, "cli_help_options_header", nil))
    	fmt.Println("--list-workers   - List all workers")
    	fmt.Println("--list-models    - List available models")
    	fmt.Println("--health         - Perform health check")
    	fmt.Println("--worker         - Add a worker (requires --user)")
    	fmt.Println("--user           - Worker SSH username")
    	fmt.Println("--key            - Worker SSH key path")
    	fmt.Println("--prompt         - Generate with LLM")
    	fmt.Println("--model          - LLM model to use")
    	fmt.Println("--stream         - Stream the response")
    	fmt.Println("--notify         - Send notification")
    	fmt.Println("--notify-type    - Notification type (info/warning/error/success/alert)")
    	fmt.Println("--notify-priority - Notification priority (low/medium/high/urgent)")
    }
    
    // loadAPIKeysAtStartup wires the previously-dead secrets.LoadAPIKeys into the
    // CLI bootstrap (D-3 / SP1). It recognizes provider API keys from
    // $HOME/api_keys.sh or a walked-up .env and applies them to the process env via
    // gap-fill precedence (already-exported vars win — DECISION-1). A missing source
    // is non-fatal: returning false means no recognition source was found, which is
    // a legitimate state (the operator may have exported keys directly). The error
    // is intentionally swallowed (never logged — CONST-042) but the boolean is
    // returned so tests can assert the wiring is live (anti-bluff: this is the seam
    // that proves the loader is no longer dead code).
    func loadAPIKeysAtStartup() bool {
    	return secrets.LoadAPIKeys() == nil
    }
    
    func main() {
    	// P1-F15-T08: subagent helper-mode dispatch — MUST run BEFORE the F14
    	// sandbox helper-mode dispatch (per spec §3 anchor). A subagent helper
    	// child does NOT initialise the sandbox; the parent re-exec'd us with
    	// HELIXCODE_SUBAGENT_HELPER=1 and a JSON-encoded SubagentTask in
    	// HELIXCODE_SUBAGENT_HELPER_PAYLOAD. We must decode, run an
    	// InProcessSpawner with the child's own LLM provider, write a
    	// SubagentResult JSON to stdout, and exit — without touching the sandbox
    	// or the rest of the CLI bootstrap.
    	//
    	// Anti-bluff anchor: this MUST be the very first statement in main().
    	// Reordering it after the sandbox dispatch (or any flag parsing) would
    	// break the protocol round-trip the parent depends on.
    	if subagent.IsSubagentInvocation() {
    		os.Exit(subagent.RunAsSubagent(buildSubagentLLMProvider))
    	}
    
    	// F14: native sandbox helper re-exec dispatch.
    	//
    	// The native backend launches the host binary with HELIX_SANDBOX_NATIVE_HELPER
    	// set; that invocation must run the helper (mount /proc, apply rlimits,
    	// chdir, exec /bin/sh -c <command>) and exit. It must NOT continue into
    	// normal CLI logic — the helper child is inside fresh PID/MNT/USER/UTS/IPC
    	// namespaces and re-entering the CLI bootstrap would hang or crash.
    	//
    	// Anti-bluff anchor: this dispatch MUST be the SECOND statement in
    	// main() (the first is subagent.IsSubagentInvocation above) so no flag
    	// parsing, cobra subcommand interception, or env-driven init runs before
    	// the helper takes over. On non-Linux this is a no-op (IsHelperInvocation
    	// returns false there — see native_backend_other.go).
    	if sandbox.IsHelperInvocation() {
    		os.Exit(sandbox.RunAsHelper())
    	}
    
    	// D-3 (SP1): recognize provider API keys from $HOME/api_keys.sh or a
    	// walked-up .env BEFORE any config / env read, so a key supplied only via
    	// those files becomes visible to config.Load() (viper AutomaticEnv) and the
    	// working-model funnel. Gap-fill precedence (DECISION-1): an already-exported
    	// shell var is NEVER overwritten — the file only fills gaps. A missing source
    	// is non-fatal (the operator may export keys directly). Values are never
    	// logged (CONST-042). This runs after the helper-mode early-exit dispatches
    	// (which never reach normal CLI logic) and before every config-reading path.
    	loadAPIKeysAtStartup()
    
    	// Minimal dispatcher: intercept the "permissions" subcommand group before
    	// flag.Parse() so that Cobra handles its own flag parsing.
    	if len(os.Args) > 1 && os.Args[1] == "permissions" {
    		cmd := newPermissionsCommand()
    		cmd.SetArgs(os.Args[2:])
    		if err := cmd.Execute(); err != nil {
    			log.Fatalf("Error: %v", err)
    		}
    		return
    	}
    
    	// Dispatcher: intercept the "worktree" subcommand group before flag.Parse()
    	// so that Cobra handles its own flag parsing (same pattern as "permissions").
    	if len(os.Args) >= 2 && os.Args[1] == "worktree" {
    		cwd, _ := os.Getwd()
    		m := worktree.NewManager(cwd)
    		cmd := newWorktreeCommand(m)
    		cmd.SetArgs(os.Args[2:])
    		if err := cmd.Execute(); err != nil {
    			os.Exit(1)
    		}
    		return
    	}
    
    	// Dispatcher: intercept the "hooks" subcommand group before flag.Parse()
    	// so that Cobra handles its own flag parsing (same pattern as "permissions" / "worktree").
    	if len(os.Args) >= 2 && os.Args[1] == "hooks" {
    		cmd := newHooksCommand()
    		cmd.SetArgs(os.Args[2:])
    		if err := cmd.Execute(); err != nil {
    			os.Exit(1)
    		}
    		return
    	}
    
    	// Dispatcher: intercept the "mcp" subcommand group before flag.Parse()
    	// so that Cobra handles its own flag parsing (same pattern as "hooks").
    	if len(os.Args) >= 2 && os.Args[1] == "mcp" {
    		cmd := newMCPCommand(MCPCommandDeps{ConfigPath: ".helixcode/mcp.yml"})
    		cmd.SetArgs(os.Args[2:])
    		if err := cmd.Execute(); err != nil {
    			os.Exit(1)
    		}
    		return
    	}
    
    	// Dispatcher: intercept the "acp" subcommand before flag.Parse() so that
    	// Cobra handles its own flag parsing (same pattern as "mcp"). This is an
    	// opt-in surface (HXC-119 Phase 1-3 scaffold, see internal/acp/doc.go):
    	// it only runs when explicitly invoked as `helixcode acp` and does not
    	// alter any other CLI invocation's behavior. Wires real os.Stdin/
    	// os.Stdout — the stdio subprocess transport an ACP-aware editor
    	// (Zed, JetBrains) launches HelixCode with.
    	if len(os.Args) >= 2 && os.Args[1] == "acp" {
    		cmd := newACPCommand(ACPCommandDeps{In: os.Stdin, Out: os.Stdout})
    		cmd.SetArgs(os.Args[2:])
    		if err := cmd.Execute(); err != nil {
    			os.Exit(1)
    		}
    		return
    	}
    
    	// Dispatcher: intercept the "commands" subcommand group before flag.Parse()
    	// so that Cobra handles its own flag parsing (same pattern as "mcp").
    	// The loader and registry are constructed here with zero startup cost
    	// (Load() is a no-op when dirs are absent); the full wiring runs later
    	// inside cli.Run() for other code paths.
    	if len(os.Args) >= 2 && os.Args[1] == "commands" {
    		projectCmds := filepath.Join(".", ".helix", "commands")
    		var userCmds string
    		if userCfg, err := os.UserConfigDir(); err == nil {
    			userCmds = filepath.Join(userCfg, "helixcode", "commands")
    		}
    		cmdReg := commands.NewRegistry()
    		mdLdr := commands.NewMarkdownLoader(cmdReg, projectCmds, userCmds)
    		if err := mdLdr.Load(); err != nil {
    			log.Printf("commands dispatcher: load failed: %v", err)
    		}
    		cmd := newCommandsCmd(commandsCmdDeps{Loader: mdLdr, Registry: cmdReg})
    		cmd.SetArgs(os.Args[2:])
    		if err := cmd.Execute(); err != nil {
    			os.Exit(1)
    		}
    		return
    	}
    
    	// Dispatcher: intercept the "sessions" subcommand group before flag.Parse()
    	// so that Cobra handles its own flag parsing (same pattern as "commands").
    	// The TranscriptStore is constructed here directly off the F11 base dir so
    	// `helixcode sessions list` works without a full CLI bootstrap.
    	if len(os.Args) >= 2 && os.Args[1] == "sessions" {
    		store := session.NewTranscriptStore(sessionStoreBaseDir())
    		currentProject, _ := session.ComputeProjectIdentity()
    		cmd := newSessionsCmd(sessionsCmdDeps{Store: store, CurrentProject: currentProject})
    		cmd.SetArgs(os.Args[2:])
    		if err := cmd.Execute(); err != nil {
    			os.Exit(1)
    		}
    		return
    	}
    
    	// Dispatcher: intercept the "wizard" subcommand group before flag.Parse()
    	// so Cobra handles its own flag parsing (same pattern as "sessions").
    	// The wizard runs RunWizard (interactive tview) or, when --provider is
    	// supplied, builds a NonInteractiveResult and writes the YAML directly.
    	if len(os.Args) >= 2 && os.Args[1] == "wizard" {
    		cmd := newWizardCmd(wizardCmdDeps{})
    		cmd.SetArgs(os.Args[2:])
    		if err := cmd.Execute(); err != nil {
    			os.Exit(1)
    		}
    		return
    	}
    
    	// Dispatcher: intercept the "lsp" subcommand group before flag.Parse() so
    	// Cobra handles its own flag parsing (same pattern as "sessions"/"wizard").
    	// The LSPManager is constructed here directly off the curated allowlist
    	// filtered by exec.LookPath, so `helixcode lsp list-servers` works without
    	// the full CLI bootstrap. status/restart/stop also work but only over the
    	// servers visible to this short-lived manager — for a long-running session
    	// the in-process /lsp slash command is the right surface.
    	if len(os.Args) >= 2 && os.Args[1] == "lsp" {
    		curated := tools.CuratedServerSpecs()
    		detected := tools.DetectAvailableServers(curated)
    		cwd, _ := os.Getwd()
    		mgr := tools.NewLSPManager(cwd, detected, zap.NewNop())
    		defer func() {
    			shutCtx, shutCancel := context.WithTimeout(context.Background(), 5*time.Second)
    			defer shutCancel()
    			_ = mgr.Shutdown(shutCtx)
    		}()
    		cmd := newLSPCmd(lspCmdDeps{Manager: mgr, CuratedSpecs: curated})
    		cmd.SetArgs(os.Args[2:])
    		if err := cmd.Execute(); err != nil {
    			os.Exit(1)
    		}
    		return
    	}
    
    	// Dispatcher: intercept the "skills" subcommand group before flag.Parse()
    	// so that Cobra handles its own flag parsing (same pattern as "commands").
    	if len(os.Args) >= 2 && os.Args[1] == "skills" {
    		projDir := filepath.Join(".", ".helix", "skills")
    		var userDir string
    		if userCfg, err := os.UserConfigDir(); err == nil {
    			userDir = filepath.Join(userCfg, "helixcode", "skills")
    		}
    		skillReg := commands.NewSkillRegistry()
    		loader := commands.NewSkillLoader(skillReg, projDir, userDir)
    		if err := loader.Load(); err != nil {
    			log.Printf("skills dispatcher: load failed: %v", err)
    		}
    		cmd := newSkillsCmd(skillsCmdDeps{Loader: loader, Registry: skillReg})
    		cmd.SetArgs(os.Args[2:])
    		if err := cmd.Execute(); err != nil {
    			os.Exit(1)
    		}
    		return
    	}
    
    	cli := NewCLI()
    
    	if err := cli.Run(); err != nil {
    		// BLUFF-003: when a `--command` child exited with a real non-zero
    		// status, propagate that exact code instead of the blanket exit 1.
    		var ece *exitCodeError
    		if errors.As(err, &ece) {
    			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
    			os.Exit(ece.ExitCode())
    		}
    		log.Fatalf("Error: %v", err)
    	}
    }
    
    // QA command handlers
    
    func (c *CLI) handleQARun(ctx context.Context, serverURL, platforms, banks string, wait bool) error {
    	if banks == "" {
    		return fmt.Errorf("--qa-banks is required for --qa-run")
    	}
    	client := server.NewClient(serverURL)
    	req := server.StartSessionRequest{
    		Platforms: strings.Split(platforms, ","),
    		Banks:     strings.Split(banks, ","),
    	}
    	state, err := client.StartQASession(req)
    	if err != nil {
    		return fmt.Errorf("failed to start QA session: %w", err)
    	}
    	fmt.Println(tr(ctx, "cli_qa_session_started", map[string]any{"ID": state.ID}))
    	fmt.Printf("Platforms: %s\n", strings.Join(state.Platforms, ", "))
    	fmt.Printf("Status: %s\n", state.Status)
    
    	if wait {
    		fmt.Println(tr(ctx, "cli_qa_waiting", nil))
    		if err := client.WaitForSession(state.ID, os.Stdout); err != nil {
    			return err
    		}
    		fmt.Println(tr(ctx, "cli_qa_session_completed", nil))
    	}
    	return nil
    }
    
    func (c *CLI) handleQAList(ctx context.Context, serverURL string) error {
    	client := server.NewClient(serverURL)
    	sessions, err := client.ListQASessions()
    	if err != nil {
    		return fmt.Errorf("failed to list sessions: %w", err)
    	}
    	if len(sessions) == 0 {
    		fmt.Println(tr(ctx, "cli_qa_no_sessions", nil))
    		return nil
    	}
    	fmt.Println("\n=== QA Sessions ===")
    	fmt.Printf("%-12s %-12s %-20s %-30s %-20s\n", "ID", "Status", "Phase", "Platforms", "Started")
    	for _, s := range sessions {
    		id := s.ID
    		if len(id) > 10 {
    			id = id[:10]
    		}
    		fmt.Printf("%-12s %-12s %-20s %-30s %-20s\n",
    			id, s.Status, s.Phase, strings.Join(s.Platforms, ","), s.StartTime.Format("2006-01-02 15:04:05"))
    	}
    	return nil
    }
    
    func (c *CLI) handleQAReport(ctx context.Context, serverURL, sessionID, format string) error {
    	client := server.NewClient(serverURL)
    	data, err := client.GetReport(sessionID, format)
    	if err != nil {
    		return fmt.Errorf("failed to get report: %w", err)
    	}
    	fmt.Println(string(data))
    	return nil
    }
    
    func (c *CLI) handleQAScreenshot(ctx context.Context, serverURL, sessionID string) error {
    	client := server.NewClient(serverURL)
    	data, meta, err := client.CaptureScreenshot(sessionID, "", false)
    	if err != nil {
    		return fmt.Errorf("failed to capture screenshot: %w", err)
    	}
    	filename := fmt.Sprintf("screenshot-%s.png", sessionID)
    	if err := os.WriteFile(filename, data, 0644); err != nil {
    		return err
    	}
    	fmt.Println(tr(ctx, "cli_screenshot_saved", map[string]any{
    		"Filename": filename,
    		"Bytes":    len(data),
    		"Platform": fmt.Sprintf("%v", meta["platform"]),
    	}))
    	return nil
    }
    
    func (c *CLI) handleQACancel(ctx context.Context, serverURL, sessionID string) error {
    	client := server.NewClient(serverURL)
    	if err := client.CancelQASession(sessionID); err != nil {
    		return fmt.Errorf("failed to cancel session: %w", err)
    	}
    	fmt.Println(tr(ctx, "cli_session_cancelled", map[string]any{"ID": sessionID}))
    	return nil
    }
    

    Comments

    More Rules

    View all

    AI RPG Grok Rules

    Z
    Zenoffice-co-ltd

    Python Code Develop Grok Rules

    X
    XCBOSA

    Collab QRC Grok Rules

    H
    HanshangZhu

    Avidtools Grok Rules

    A
    avidml

    Lucid Grok Rules

    L
    lucid-fdn

    Livetalk 3d Model Grok Rules

    K
    kosdesign

    Stay up to date

    Get the latest Grok prompts, rules, and resources delivered to your inbox weekly.

    Neura Market LogoNeura Market

    Discover the best AI prompts, plugins, and resources for Grok and more.

    Content Types

    • Rules
    • Prompts
    • MCPs
    • Agents
    • Games
    • Blog
    • Videos
    • Guides
    • Courses
    • Community

    Platforms

    • ChatGPT Directory
    • Claude Directory
    • Gemini Directory
    • Cursor Directory
    • Grok Directory
    • Perplexity Directory
    • DeepSeek Directory
    • CoPilot Directory
    • Stable Diffusion Directory
    • Midjourney Directory
    • All Directories

    Resources

    • Blog
    • Documentation
    • Help Center
    • Marketplace

    Legal

    • Privacy Policy
    • Terms of Service

    © 2026 Neura Market. All rights reserved.

    |

    Not affiliated with any AI platform vendors.

    Neura Market

    Custom AI Systems & Services

    Our team of experienced AI builders will help build custom AI systems, workflows, and solutions.

    Request custom work

    Ready-made automations for this

    Workflows from the Neura Market marketplace related to this Grok resource

    • Automated Execution Cleanup System with n8n API and Custom Retention Rulesn8n · $9.99 · Related topic
    • Receive and Analyze Emails with Rules in Sublime Securityn8n · $9.99 · Related topic
    • Create an IPL Cricket Rules Q&A Chatbot with Google Gemini APIn8n · $14.99 · Related topic
    • Dynamic AI Agent with Self-Updating Behavioral Rulesn8n · $9.99 · Related topic
    Browse all workflows