singularity-forge/packages/pi-coding-agent/src/index.ts
Tom Boucher 3542b17c97 fix: normalize Windows paths in LLM-visible text to prevent bash failures (#874) (#884)
On Windows, process.cwd() returns backslash paths (C:\Users\name\...).
When these paths are injected into system prompts, worktree context
blocks, or tool results, the model copies them into bash commands.
Bash interprets backslashes as escape characters, silently stripping
them — producing invalid paths like 'C:Usersnamedevelopmentapp-name'.

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

Changes:

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

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

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

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

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

Audit scope: Checked all process.cwd() usage across pi-coding-agent
and all bundled extensions. Filesystem-only paths (join, readFile,
spawn cwd, existsSync) are correct and left unchanged. Only paths
entering LLM text are normalized.
2026-03-17 09:02:23 -06:00

365 lines
8.7 KiB
TypeScript

// Core session management
// Config paths
export { getAgentDir, VERSION } from "./config.js";
export {
AgentSession,
type AgentSessionConfig,
type AgentSessionEvent,
type AgentSessionEventListener,
type ModelCycleResult,
type ParsedSkillBlock,
type PromptOptions,
parseSkillBlock,
type SessionStats,
} from "./core/agent-session.js";
// Auth and model registry
export {
type ApiKeyCredential,
type AuthCredential,
AuthStorage,
type AuthStorageBackend,
FileAuthStorageBackend,
InMemoryAuthStorageBackend,
type OAuthCredential,
} from "./core/auth-storage.js";
// Compaction
export {
type BranchPreparation,
type BranchSummaryResult,
type CollectEntriesResult,
type CompactionResult,
type CutPointResult,
calculateContextTokens,
collectEntriesForBranchSummary,
compact,
DEFAULT_COMPACTION_SETTINGS,
estimateTokens,
type FileOperations,
findCutPoint,
findTurnStartIndex,
type GenerateBranchSummaryOptions,
generateBranchSummary,
generateSummary,
getLastAssistantUsage,
prepareBranchEntries,
serializeConversation,
shouldCompact,
} from "./core/compaction/index.js";
export { createEventBus, type EventBus, type EventBusController } from "./core/event-bus.js";
// Extension system
export type {
AgentEndEvent,
AgentStartEvent,
AgentToolResult,
AgentToolUpdateCallback,
AppAction,
BashToolCallEvent,
BeforeAgentStartEvent,
BeforeProviderRequestEvent,
BeforeProviderRequestEventResult,
CompactOptions,
ContextEvent,
ContextUsage,
CustomToolCallEvent,
EditToolCallEvent,
ExecOptions,
ExecResult,
Extension,
ExtensionActions,
ExtensionAPI,
ExtensionCommandContext,
ExtensionCommandContextActions,
ExtensionContext,
ExtensionContextActions,
ExtensionError,
ExtensionEvent,
ExtensionFactory,
ExtensionFlag,
ExtensionHandler,
ExtensionRuntime,
ExtensionShortcut,
ExtensionUIContext,
ExtensionUIDialogOptions,
ExtensionWidgetOptions,
FindToolCallEvent,
GrepToolCallEvent,
InputEvent,
InputEventResult,
InputSource,
KeybindingsManager,
LoadExtensionsResult,
LsToolCallEvent,
MessageRenderer,
MessageRenderOptions,
ProviderConfig,
ProviderModelConfig,
ReadToolCallEvent,
RegisteredCommand,
RegisteredTool,
SessionBeforeCompactEvent,
SessionBeforeForkEvent,
SessionBeforeSwitchEvent,
SessionBeforeTreeEvent,
SessionCompactEvent,
SessionForkEvent,
SessionShutdownEvent,
SessionStartEvent,
SessionSwitchEvent,
SessionTreeEvent,
SlashCommandInfo,
SlashCommandLocation,
SlashCommandSource,
TerminalInputHandler,
ToolCallEvent,
ToolDefinition,
ToolInfo,
ToolRenderResultOptions,
ToolResultEvent,
TurnEndEvent,
TurnStartEvent,
UserBashEvent,
UserBashEventResult,
WidgetPlacement,
WriteToolCallEvent,
} from "./core/extensions/index.js";
export {
createExtensionRuntime,
discoverAndLoadExtensions,
ExtensionRunner,
isBashToolResult,
isEditToolResult,
isFindToolResult,
isGrepToolResult,
isLsToolResult,
isReadToolResult,
isToolCallEventType,
isWriteToolResult,
wrapRegisteredTool,
wrapRegisteredTools,
wrapToolsWithExtensions,
wrapToolWithExtensions,
} from "./core/extensions/index.js";
// Footer data provider (git branch + extension statuses - data not otherwise available to extensions)
export type { ReadonlyFooterDataProvider } from "./core/footer-data-provider.js";
export { convertToLlm } from "./core/messages.js";
export { ModelDiscoveryCache } from "./core/discovery-cache.js";
export type { DiscoveredModel, DiscoveryResult, ProviderDiscoveryAdapter } from "./core/model-discovery.js";
export { getDiscoverableProviders, getDiscoveryAdapter } from "./core/model-discovery.js";
export { ModelRegistry } from "./core/model-registry.js";
export { ModelsJsonWriter } from "./core/models-json-writer.js";
export type {
PackageManager,
PathMetadata,
ProgressCallback,
ProgressEvent,
ResolvedPaths,
ResolvedResource,
} from "./core/package-manager.js";
export { DefaultPackageManager } from "./core/package-manager.js";
export type { ResourceCollision, ResourceDiagnostic, ResourceLoader } from "./core/resource-loader.js";
export { DefaultResourceLoader } from "./core/resource-loader.js";
// SDK for programmatic usage
export {
type CreateAgentSessionOptions,
type CreateAgentSessionResult,
// Factory
createAgentSession,
createBashTool,
// Tool factories (for custom cwd)
createCodingTools,
createEditTool,
createFindTool,
createGrepTool,
createLsTool,
createReadOnlyTools,
createReadTool,
createWriteTool,
type PromptTemplate,
// Pre-built tools (use process.cwd())
readOnlyTools,
} from "./core/sdk.js";
export {
type BranchSummaryEntry,
buildSessionContext,
type CompactionEntry,
CURRENT_SESSION_VERSION,
type CustomEntry,
type CustomMessageEntry,
type FileEntry,
getLatestCompactionEntry,
type ModelChangeEntry,
migrateSessionEntries,
type NewSessionOptions,
parseSessionEntries,
type SessionContext,
type SessionEntry,
type SessionEntryBase,
type SessionHeader,
type SessionInfo,
type SessionInfoEntry,
SessionManager,
type SessionMessageEntry,
type ThinkingLevelChangeEntry,
} from "./core/session-manager.js";
// Blob and artifact storage
export { BlobStore, isBlobRef, parseBlobRef, externalizeImageData, resolveImageData } from "./core/blob-store.js";
export { ArtifactManager } from "./core/artifact-manager.js";
export {
type AsyncSettings,
type CompactionSettings,
type ImageSettings,
type MemorySettings,
type PackageSource,
type RetrySettings,
SettingsManager,
type TaskIsolationSettings,
} from "./core/settings-manager.js";
// Skills
export {
formatSkillsForPrompt,
type LoadSkillsFromDirOptions,
type LoadSkillsResult,
loadSkills,
loadSkillsFromDir,
type Skill,
type SkillFrontmatter,
} from "./core/skills.js";
// Tools
export {
type BashInterceptorRule,
type BashOperations,
type BashSpawnContext,
type BashSpawnHook,
type BashToolDetails,
type BashToolInput,
type BashToolOptions,
bashTool,
rewriteBackgroundCommand,
checkBashInterception,
type CompiledInterceptor,
compileInterceptor,
DEFAULT_BASH_INTERCEPTOR_RULES,
codingTools,
DEFAULT_MAX_BYTES,
DEFAULT_MAX_LINES,
type EditOperations,
type EditToolDetails,
type EditToolInput,
type EditToolOptions,
editTool,
type FindOperations,
type FindToolDetails,
type FindToolInput,
type FindToolOptions,
findTool,
formatSize,
type GrepOperations,
type GrepToolDetails,
type GrepToolInput,
type GrepToolOptions,
grepTool,
type LsOperations,
type LsToolDetails,
type LsToolInput,
type LsToolOptions,
lsTool,
type ReadOperations,
type ReadToolDetails,
type ReadToolInput,
type ReadToolOptions,
readTool,
type ToolsOptions,
type TruncationOptions,
type TruncationResult,
truncateHead,
truncateLine,
truncateTail,
type WriteOperations,
type WriteToolInput,
type WriteToolOptions,
writeTool,
// Hashline edit mode tools
hashlineEditTool,
hashlineReadTool,
hashlineCodingTools,
createHashlineEditTool,
createHashlineReadTool,
createHashlineCodingTools,
type HashlineEditInput,
type HashlineEditToolDetails,
type HashlineEditToolOptions,
type HashlineReadToolDetails,
type HashlineReadToolInput,
type HashlineReadToolOptions,
} from "./core/tools/index.js";
// Main entry point
export { main } from "./main.js";
// Run modes for programmatic SDK usage
export {
InteractiveMode,
type InteractiveModeOptions,
type PrintModeOptions,
runPrintMode,
runRpcMode,
} from "./modes/index.js";
// UI components for extensions
export {
ArminComponent,
AssistantMessageComponent,
appKey,
appKeyHint,
BashExecutionComponent,
BorderedLoader,
BranchSummaryMessageComponent,
CompactionSummaryMessageComponent,
CustomEditor,
CustomMessageComponent,
DynamicBorder,
ExtensionEditorComponent,
ExtensionInputComponent,
ExtensionSelectorComponent,
editorKey,
FooterComponent,
keyHint,
LoginDialogComponent,
ModelSelectorComponent,
OAuthSelectorComponent,
ProviderManagerComponent,
type RenderDiffOptions,
rawKeyHint,
renderDiff,
SessionSelectorComponent,
type SettingsCallbacks,
type SettingsConfig,
SettingsSelectorComponent,
ShowImagesSelectorComponent,
SkillInvocationMessageComponent,
ThemeSelectorComponent,
ThinkingSelectorComponent,
ToolExecutionComponent,
type ToolExecutionOptions,
TreeSelectorComponent,
truncateToVisualLines,
UserMessageComponent,
UserMessageSelectorComponent,
type VisualTruncateResult,
} from "./modes/interactive/components/index.js";
// Theme utilities for custom tools and extensions
export {
getLanguageFromPath,
getMarkdownTheme,
getSelectListTheme,
getSettingsListTheme,
highlightCode,
initTheme,
Theme,
type ThemeColor,
} from "./modes/interactive/theme/theme.js";
// Clipboard utilities
export { copyToClipboard } from "./utils/clipboard.js";
export { parseFrontmatter, stripFrontmatter } from "./utils/frontmatter.js";
// Shell utilities
export { getShellConfig, sanitizeCommand } from "./utils/shell.js";
// Cross-platform path display
export { toPosixPath } from "./utils/path-display.js";