@mariozechner/pi-coding-agent 0.24.4 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -1
- package/README.md +16 -11
- package/dist/core/agent-session.d.ts +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +19 -12
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +30 -4
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +2 -0
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/oauth/index.d.ts +25 -22
- package/dist/core/oauth/index.d.ts.map +1 -1
- package/dist/core/oauth/index.js +32 -44
- package/dist/core/oauth/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +41 -5
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +7 -2
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +7 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +33 -17
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +1 -1
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +15 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/package.json +4 -4
- package/dist/core/oauth/anthropic.d.ts +0 -10
- package/dist/core/oauth/anthropic.d.ts.map +0 -1
- package/dist/core/oauth/anthropic.js +0 -100
- package/dist/core/oauth/anthropic.js.map +0 -1
- package/dist/core/oauth/github-copilot.d.ts +0 -30
- package/dist/core/oauth/github-copilot.d.ts.map +0 -1
- package/dist/core/oauth/github-copilot.js +0 -221
- package/dist/core/oauth/github-copilot.js.map +0 -1
- package/dist/core/oauth/storage.d.ts +0 -24
- package/dist/core/oauth/storage.d.ts.map +0 -1
- package/dist/core/oauth/storage.js +0 -69
- package/dist/core/oauth/storage.js.map +0 -1
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACN,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,GACjB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACN,KAAK,cAAc,EACnB,sBAAsB,EACtB,OAAO,EACP,2BAA2B,EAC3B,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,aAAa,GACb,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACX,uBAAuB,EACvB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,EACV,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,IAAI,gBAAgB,EAChC,OAAO,EACP,aAAa,GACb,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC3F,YAAY,EACX,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,WAAW,EACX,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,OAAO,EACP,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,mBAAmB,EACnB,eAAe,EACf,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,oBAAoB,GACpB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACN,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EACN,iBAAiB,EACjB,KAAK,EACL,MAAM,EACN,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACN,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,YAAY,GACjB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACN,KAAK,cAAc,EACnB,sBAAsB,EACtB,OAAO,EACP,2BAA2B,EAC3B,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,aAAa,GACb,MAAM,sBAAsB,CAAC;AAE9B,YAAY,EACX,uBAAuB,EACvB,eAAe,EACf,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,EACV,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,IAAI,gBAAgB,EAChC,OAAO,EACP,aAAa,GACb,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC3F,YAAY,EACX,aAAa,EACb,eAAe,EACf,mBAAmB,EACnB,WAAW,EACX,iBAAiB,EACjB,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,OAAO,EACP,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,mBAAmB,EACnB,eAAe,EACf,qBAAqB,EACrB,YAAY,EACZ,cAAc,EACd,oBAAoB,GACpB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACN,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EACN,iBAAiB,EACjB,KAAK,EACL,MAAM,EACN,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,aAAa,GAClB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACN,KAAK,eAAe,EACpB,oBAAoB,EACpB,wBAAwB,EACxB,KAAK,aAAa,EAClB,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,mBAAmB,EACnB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,cAAc,EACd,KAAK,mBAAmB,EACxB,cAAc,EACd,cAAc,EACd,KAAK,wBAAwB,GAC7B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACN,KAAK,kBAAkB,EACvB,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,eAAe,GACf,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EACN,qBAAqB,EACrB,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,EACrB,UAAU,EACV,iBAAiB,EACjB,KAAK,KAAK,EACV,KAAK,gBAAgB,EACrB,KAAK,YAAY,GACjB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACN,KAAK,eAAe,EACpB,QAAQ,EACR,WAAW,EACX,QAAQ,EACR,KAAK,eAAe,EACpB,QAAQ,EACR,KAAK,eAAe,EACpB,QAAQ,EACR,KAAK,aAAa,EAClB,MAAM,EACN,KAAK,eAAe,EACpB,QAAQ,EACR,KAAK,gBAAgB,EACrB,SAAS,GACT,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC","sourcesContent":["// Core session management\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype CompactionResult,\n\ttype ModelCycleResult,\n\ttype PromptOptions,\n\ttype SessionStats,\n} from \"./core/agent-session.js\";\n// Compaction\nexport {\n\ttype CutPointResult,\n\tcalculateContextTokens,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateTokens,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\tgenerateSummary,\n\tgetLastAssistantUsage,\n\tshouldCompact,\n} from \"./core/compaction.js\";\n// Custom tools\nexport type {\n\tAgentToolUpdateCallback,\n\tCustomAgentTool,\n\tCustomToolFactory,\n\tCustomToolsLoadResult,\n\tExecResult,\n\tLoadedCustomTool,\n\tRenderResultOptions,\n\tSessionEvent as ToolSessionEvent,\n\tToolAPI,\n\tToolUIContext,\n} from \"./core/custom-tools/index.js\";\nexport { discoverAndLoadCustomTools, loadCustomTools } from \"./core/custom-tools/index.js\";\nexport type {\n\tAgentEndEvent,\n\tAgentStartEvent,\n\tBashToolResultEvent,\n\tBranchEvent,\n\tBranchEventResult,\n\tCustomToolResultEvent,\n\tEditToolResultEvent,\n\tFindToolResultEvent,\n\tGrepToolResultEvent,\n\tHookAPI,\n\tHookEvent,\n\tHookEventContext,\n\tHookFactory,\n\tHookUIContext,\n\tLsToolResultEvent,\n\tReadToolResultEvent,\n\tSessionEvent,\n\tToolCallEvent,\n\tToolCallEventResult,\n\tToolResultEvent,\n\tToolResultEventResult,\n\tTurnEndEvent,\n\tTurnStartEvent,\n\tWriteToolResultEvent,\n} from \"./core/hooks/index.js\";\n// Hook system types and type guards\nexport {\n\tisBashToolResult,\n\tisEditToolResult,\n\tisFindToolResult,\n\tisGrepToolResult,\n\tisLsToolResult,\n\tisReadToolResult,\n\tisWriteToolResult,\n} from \"./core/hooks/index.js\";\nexport { messageTransformer } from \"./core/messages.js\";\n// Model configuration and OAuth\nexport { findModel, getApiKeyForModel, getAvailableModels } from \"./core/model-config.js\";\nexport {\n\tgetOAuthProviders,\n\tlogin,\n\tlogout,\n\ttype OAuthAuthInfo,\n\ttype OAuthPrompt,\n\ttype OAuthProvider,\n} from \"./core/oauth/index.js\";\nexport {\n\ttype CompactionEntry,\n\tcreateSummaryMessage,\n\tgetLatestCompactionEntry,\n\ttype LoadedSession,\n\tloadSessionFromEntries,\n\ttype ModelChangeEntry,\n\tparseSessionEntries,\n\ttype SessionEntry,\n\ttype SessionHeader,\n\tSessionManager,\n\ttype SessionMessageEntry,\n\tSUMMARY_PREFIX,\n\tSUMMARY_SUFFIX,\n\ttype ThinkingLevelChangeEntry,\n} from \"./core/session-manager.js\";\nexport {\n\ttype CompactionSettings,\n\ttype RetrySettings,\n\ttype Settings,\n\tSettingsManager,\n} from \"./core/settings-manager.js\";\n// Skills\nexport {\n\tformatSkillsForPrompt,\n\ttype LoadSkillsFromDirOptions,\n\ttype LoadSkillsResult,\n\tloadSkills,\n\tloadSkillsFromDir,\n\ttype Skill,\n\ttype SkillFrontmatter,\n\ttype SkillWarning,\n} from \"./core/skills.js\";\n// Tools\nexport {\n\ttype BashToolDetails,\n\tbashTool,\n\tcodingTools,\n\teditTool,\n\ttype FindToolDetails,\n\tfindTool,\n\ttype GrepToolDetails,\n\tgrepTool,\n\ttype LsToolDetails,\n\tlsTool,\n\ttype ReadToolDetails,\n\treadTool,\n\ttype TruncationResult,\n\twriteTool,\n} from \"./core/tools/index.js\";\n// Main entry point\nexport { main } from \"./main.js\";\n// Theme utilities for custom tools\nexport { getMarkdownTheme } from \"./modes/interactive/theme/theme.js\";\n"]}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,OAAO,EACN,YAAY,GAQZ,MAAM,yBAAyB,CAAC;AACjC,aAAa;AACb,OAAO,EAEN,sBAAsB,EACtB,OAAO,EACP,2BAA2B,EAC3B,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,aAAa,GACb,MAAM,sBAAsB,CAAC;AAc9B,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AA2B3F,oCAAoC;AACpC,OAAO,EACN,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,gCAAgC;AAChC,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EACN,iBAAiB,EACjB,KAAK,EACL,MAAM,GAIN,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAEN,oBAAoB,EACpB,wBAAwB,EAExB,sBAAsB,EAEtB,mBAAmB,EAGnB,cAAc,EAEd,cAAc,EACd,cAAc,GAEd,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAIN,eAAe,GACf,MAAM,4BAA4B,CAAC;AACpC,SAAS;AACT,OAAO,EACN,qBAAqB,EAGrB,UAAU,EACV,iBAAiB,GAIjB,MAAM,kBAAkB,CAAC;AAC1B,QAAQ;AACR,OAAO,EAEN,QAAQ,EACR,WAAW,EACX,QAAQ,EAER,QAAQ,EAER,QAAQ,EAER,MAAM,EAEN,QAAQ,EAER,SAAS,GACT,MAAM,uBAAuB,CAAC;AAC/B,mBAAmB;AACnB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,mCAAmC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC","sourcesContent":["// Core session management\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype CompactionResult,\n\ttype ModelCycleResult,\n\ttype PromptOptions,\n\ttype SessionStats,\n} from \"./core/agent-session.js\";\n// Compaction\nexport {\n\ttype CutPointResult,\n\tcalculateContextTokens,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateTokens,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\tgenerateSummary,\n\tgetLastAssistantUsage,\n\tshouldCompact,\n} from \"./core/compaction.js\";\n// Custom tools\nexport type {\n\tAgentToolUpdateCallback,\n\tCustomAgentTool,\n\tCustomToolFactory,\n\tCustomToolsLoadResult,\n\tExecResult,\n\tLoadedCustomTool,\n\tRenderResultOptions,\n\tSessionEvent as ToolSessionEvent,\n\tToolAPI,\n\tToolUIContext,\n} from \"./core/custom-tools/index.js\";\nexport { discoverAndLoadCustomTools, loadCustomTools } from \"./core/custom-tools/index.js\";\nexport type {\n\tAgentEndEvent,\n\tAgentStartEvent,\n\tBashToolResultEvent,\n\tBranchEvent,\n\tBranchEventResult,\n\tCustomToolResultEvent,\n\tEditToolResultEvent,\n\tFindToolResultEvent,\n\tGrepToolResultEvent,\n\tHookAPI,\n\tHookEvent,\n\tHookEventContext,\n\tHookFactory,\n\tHookUIContext,\n\tLsToolResultEvent,\n\tReadToolResultEvent,\n\tSessionEvent,\n\tToolCallEvent,\n\tToolCallEventResult,\n\tToolResultEvent,\n\tToolResultEventResult,\n\tTurnEndEvent,\n\tTurnStartEvent,\n\tWriteToolResultEvent,\n} from \"./core/hooks/index.js\";\n// Hook system types and type guards\nexport {\n\tisBashToolResult,\n\tisEditToolResult,\n\tisFindToolResult,\n\tisGrepToolResult,\n\tisLsToolResult,\n\tisReadToolResult,\n\tisWriteToolResult,\n} from \"./core/hooks/index.js\";\nexport { messageTransformer } from \"./core/messages.js\";\n// Model configuration and OAuth\nexport { findModel, getApiKeyForModel, getAvailableModels } from \"./core/model-config.js\";\nexport {\n\tgetOAuthProviders,\n\tlogin,\n\tlogout,\n\ttype OAuthAuthInfo,\n\ttype OAuthPrompt,\n\ttype
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,0BAA0B;AAC1B,OAAO,EACN,YAAY,GAQZ,MAAM,yBAAyB,CAAC;AACjC,aAAa;AACb,OAAO,EAEN,sBAAsB,EACtB,OAAO,EACP,2BAA2B,EAC3B,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,eAAe,EACf,qBAAqB,EACrB,aAAa,GACb,MAAM,sBAAsB,CAAC;AAc9B,OAAO,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AA2B3F,oCAAoC;AACpC,OAAO,EACN,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,iBAAiB,GACjB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,gCAAgC;AAChC,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC1F,OAAO,EACN,iBAAiB,EACjB,KAAK,EACL,MAAM,GAIN,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAEN,oBAAoB,EACpB,wBAAwB,EAExB,sBAAsB,EAEtB,mBAAmB,EAGnB,cAAc,EAEd,cAAc,EACd,cAAc,GAEd,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAIN,eAAe,GACf,MAAM,4BAA4B,CAAC;AACpC,SAAS;AACT,OAAO,EACN,qBAAqB,EAGrB,UAAU,EACV,iBAAiB,GAIjB,MAAM,kBAAkB,CAAC;AAC1B,QAAQ;AACR,OAAO,EAEN,QAAQ,EACR,WAAW,EACX,QAAQ,EAER,QAAQ,EAER,QAAQ,EAER,MAAM,EAEN,QAAQ,EAER,SAAS,GACT,MAAM,uBAAuB,CAAC;AAC/B,mBAAmB;AACnB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,mCAAmC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC","sourcesContent":["// Core session management\nexport {\n\tAgentSession,\n\ttype AgentSessionConfig,\n\ttype AgentSessionEvent,\n\ttype AgentSessionEventListener,\n\ttype CompactionResult,\n\ttype ModelCycleResult,\n\ttype PromptOptions,\n\ttype SessionStats,\n} from \"./core/agent-session.js\";\n// Compaction\nexport {\n\ttype CutPointResult,\n\tcalculateContextTokens,\n\tcompact,\n\tDEFAULT_COMPACTION_SETTINGS,\n\testimateTokens,\n\tfindCutPoint,\n\tfindTurnStartIndex,\n\tgenerateSummary,\n\tgetLastAssistantUsage,\n\tshouldCompact,\n} from \"./core/compaction.js\";\n// Custom tools\nexport type {\n\tAgentToolUpdateCallback,\n\tCustomAgentTool,\n\tCustomToolFactory,\n\tCustomToolsLoadResult,\n\tExecResult,\n\tLoadedCustomTool,\n\tRenderResultOptions,\n\tSessionEvent as ToolSessionEvent,\n\tToolAPI,\n\tToolUIContext,\n} from \"./core/custom-tools/index.js\";\nexport { discoverAndLoadCustomTools, loadCustomTools } from \"./core/custom-tools/index.js\";\nexport type {\n\tAgentEndEvent,\n\tAgentStartEvent,\n\tBashToolResultEvent,\n\tBranchEvent,\n\tBranchEventResult,\n\tCustomToolResultEvent,\n\tEditToolResultEvent,\n\tFindToolResultEvent,\n\tGrepToolResultEvent,\n\tHookAPI,\n\tHookEvent,\n\tHookEventContext,\n\tHookFactory,\n\tHookUIContext,\n\tLsToolResultEvent,\n\tReadToolResultEvent,\n\tSessionEvent,\n\tToolCallEvent,\n\tToolCallEventResult,\n\tToolResultEvent,\n\tToolResultEventResult,\n\tTurnEndEvent,\n\tTurnStartEvent,\n\tWriteToolResultEvent,\n} from \"./core/hooks/index.js\";\n// Hook system types and type guards\nexport {\n\tisBashToolResult,\n\tisEditToolResult,\n\tisFindToolResult,\n\tisGrepToolResult,\n\tisLsToolResult,\n\tisReadToolResult,\n\tisWriteToolResult,\n} from \"./core/hooks/index.js\";\nexport { messageTransformer } from \"./core/messages.js\";\n// Model configuration and OAuth\nexport { findModel, getApiKeyForModel, getAvailableModels } from \"./core/model-config.js\";\nexport {\n\tgetOAuthProviders,\n\tlogin,\n\tlogout,\n\ttype OAuthAuthInfo,\n\ttype OAuthPrompt,\n\ttype OAuthProvider,\n} from \"./core/oauth/index.js\";\nexport {\n\ttype CompactionEntry,\n\tcreateSummaryMessage,\n\tgetLatestCompactionEntry,\n\ttype LoadedSession,\n\tloadSessionFromEntries,\n\ttype ModelChangeEntry,\n\tparseSessionEntries,\n\ttype SessionEntry,\n\ttype SessionHeader,\n\tSessionManager,\n\ttype SessionMessageEntry,\n\tSUMMARY_PREFIX,\n\tSUMMARY_SUFFIX,\n\ttype ThinkingLevelChangeEntry,\n} from \"./core/session-manager.js\";\nexport {\n\ttype CompactionSettings,\n\ttype RetrySettings,\n\ttype Settings,\n\tSettingsManager,\n} from \"./core/settings-manager.js\";\n// Skills\nexport {\n\tformatSkillsForPrompt,\n\ttype LoadSkillsFromDirOptions,\n\ttype LoadSkillsResult,\n\tloadSkills,\n\tloadSkillsFromDir,\n\ttype Skill,\n\ttype SkillFrontmatter,\n\ttype SkillWarning,\n} from \"./core/skills.js\";\n// Tools\nexport {\n\ttype BashToolDetails,\n\tbashTool,\n\tcodingTools,\n\teditTool,\n\ttype FindToolDetails,\n\tfindTool,\n\ttype GrepToolDetails,\n\tgrepTool,\n\ttype LsToolDetails,\n\tlsTool,\n\ttype ReadToolDetails,\n\treadTool,\n\ttype TruncationResult,\n\twriteTool,\n} from \"./core/tools/index.js\";\n// Main entry point\nexport { main } from \"./main.js\";\n// Theme utilities for custom tools\nexport { getMarkdownTheme } from \"./modes/interactive/theme/theme.js\";\n"]}
|
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;GAEG;AA4IH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,iBAyTxC","sourcesContent":["/**\n * Main entry point for the coding agent\n */\n\nimport { Agent, type Attachment, ProviderTransport, type ThinkingLevel } from \"@mariozechner/pi-agent-core\";\nimport chalk from \"chalk\";\nimport { type Args, parseArgs, printHelp } from \"./cli/args.js\";\nimport { processFileArguments } from \"./cli/file-processor.js\";\nimport { listModels } from \"./cli/list-models.js\";\nimport { selectSession } from \"./cli/session-picker.js\";\nimport { getModelsPath, VERSION } from \"./config.js\";\nimport { AgentSession } from \"./core/agent-session.js\";\nimport { discoverAndLoadCustomTools, type LoadedCustomTool } from \"./core/custom-tools/index.js\";\nimport { exportFromFile } from \"./core/export-html.js\";\nimport { discoverAndLoadHooks, HookRunner, wrapToolsWithHooks } from \"./core/hooks/index.js\";\nimport { messageTransformer } from \"./core/messages.js\";\nimport { findModel, getApiKeyForModel, getAvailableModels } from \"./core/model-config.js\";\nimport { resolveModelScope, restoreModelFromSession, type ScopedModel } from \"./core/model-resolver.js\";\nimport { SessionManager } from \"./core/session-manager.js\";\nimport { SettingsManager } from \"./core/settings-manager.js\";\nimport { loadSlashCommands } from \"./core/slash-commands.js\";\nimport { buildSystemPrompt } from \"./core/system-prompt.js\";\nimport { allTools, codingTools } from \"./core/tools/index.js\";\nimport { InteractiveMode, runPrintMode, runRpcMode } from \"./modes/index.js\";\nimport { initTheme, stopThemeWatcher } from \"./modes/interactive/theme/theme.js\";\nimport { getChangelogPath, getNewEntries, parseChangelog } from \"./utils/changelog.js\";\nimport { ensureTool } from \"./utils/tools-manager.js\";\n\n/** Check npm registry for new version (non-blocking) */\nasync function checkForNewVersion(currentVersion: string): Promise<string | null> {\n\ttry {\n\t\tconst response = await fetch(\"https://registry.npmjs.org/@mariozechner/pi-coding-agent/latest\");\n\t\tif (!response.ok) return null;\n\n\t\tconst data = (await response.json()) as { version?: string };\n\t\tconst latestVersion = data.version;\n\n\t\tif (latestVersion && latestVersion !== currentVersion) {\n\t\t\treturn latestVersion;\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\t// Silently fail - don't disrupt the user experience\n\t\treturn null;\n\t}\n}\n\n/** Run interactive mode with TUI */\nasync function runInteractiveMode(\n\tsession: AgentSession,\n\tversion: string,\n\tchangelogMarkdown: string | null,\n\tmodelFallbackMessage: string | null,\n\tversionCheckPromise: Promise<string | null>,\n\tinitialMessages: string[],\n\tcustomTools: LoadedCustomTool[],\n\tsetToolUIContext: (uiContext: import(\"./core/hooks/types.js\").HookUIContext, hasUI: boolean) => void,\n\tinitialMessage?: string,\n\tinitialAttachments?: Attachment[],\n\tfdPath: string | null = null,\n): Promise<void> {\n\tconst mode = new InteractiveMode(session, version, changelogMarkdown, customTools, setToolUIContext, fdPath);\n\n\t// Initialize TUI (subscribes to agent events internally)\n\tawait mode.init();\n\n\t// Handle version check result when it completes (don't block)\n\tversionCheckPromise.then((newVersion) => {\n\t\tif (newVersion) {\n\t\t\tmode.showNewVersionNotification(newVersion);\n\t\t}\n\t});\n\n\t// Render any existing messages (from --continue mode)\n\tmode.renderInitialMessages(session.state);\n\n\t// Show model fallback warning at the end of the chat if applicable\n\tif (modelFallbackMessage) {\n\t\tmode.showWarning(modelFallbackMessage);\n\t}\n\n\t// Process initial message with attachments if provided (from @file args)\n\tif (initialMessage) {\n\t\ttry {\n\t\t\tawait session.prompt(initialMessage, { attachments: initialAttachments });\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n\n\t// Process remaining initial messages if provided (from CLI args)\n\tfor (const message of initialMessages) {\n\t\ttry {\n\t\t\tawait session.prompt(message);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n\n\t// Interactive loop\n\twhile (true) {\n\t\tconst userInput = await mode.getUserInput();\n\n\t\t// Process the message\n\t\ttry {\n\t\t\tawait session.prompt(userInput);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n}\n\n/** Prepare initial message from @file arguments */\nasync function prepareInitialMessage(parsed: Args): Promise<{\n\tinitialMessage?: string;\n\tinitialAttachments?: Attachment[];\n}> {\n\tif (parsed.fileArgs.length === 0) {\n\t\treturn {};\n\t}\n\n\tconst { textContent, imageAttachments } = await processFileArguments(parsed.fileArgs);\n\n\t// Combine file content with first plain text message (if any)\n\tlet initialMessage: string;\n\tif (parsed.messages.length > 0) {\n\t\tinitialMessage = textContent + parsed.messages[0];\n\t\tparsed.messages.shift(); // Remove first message as it's been combined\n\t} else {\n\t\tinitialMessage = textContent;\n\t}\n\n\treturn {\n\t\tinitialMessage,\n\t\tinitialAttachments: imageAttachments.length > 0 ? imageAttachments : undefined,\n\t};\n}\n\nexport async function main(args: string[]) {\n\tconst parsed = parseArgs(args);\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\treturn;\n\t}\n\n\tif (parsed.help) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\t// Handle --list-models flag: list available models and exit\n\tif (parsed.listModels !== undefined) {\n\t\tconst searchPattern = typeof parsed.listModels === \"string\" ? parsed.listModels : undefined;\n\t\tawait listModels(searchPattern);\n\t\treturn;\n\t}\n\n\t// Handle --export flag: convert session file to HTML and exit\n\tif (parsed.export) {\n\t\ttry {\n\t\t\tconst outputPath = parsed.messages.length > 0 ? parsed.messages[0] : undefined;\n\t\t\tconst result = exportFromFile(parsed.export, outputPath);\n\t\t\tconsole.log(`Exported to: ${result}`);\n\t\t\treturn;\n\t\t} catch (error: unknown) {\n\t\t\tconst message = error instanceof Error ? error.message : \"Failed to export session\";\n\t\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\t// Validate: RPC mode doesn't support @file arguments\n\tif (parsed.mode === \"rpc\" && parsed.fileArgs.length > 0) {\n\t\tconsole.error(chalk.red(\"Error: @file arguments are not supported in RPC mode\"));\n\t\tprocess.exit(1);\n\t}\n\n\t// Process @file arguments\n\tconst { initialMessage, initialAttachments } = await prepareInitialMessage(parsed);\n\n\t// Determine if we're in interactive mode (needed for theme watcher)\n\tconst isInteractive = !parsed.print && parsed.mode === undefined;\n\n\t// Initialize theme (before any TUI rendering)\n\tconst settingsManager = new SettingsManager();\n\tconst themeName = settingsManager.getTheme();\n\tinitTheme(themeName, isInteractive);\n\n\t// Setup session manager\n\tconst sessionManager = new SessionManager(parsed.continue && !parsed.resume, parsed.session);\n\n\tif (parsed.noSession) {\n\t\tsessionManager.disable();\n\t}\n\n\t// Handle --resume flag: show session selector\n\tif (parsed.resume) {\n\t\tconst selectedSession = await selectSession(sessionManager);\n\t\tif (!selectedSession) {\n\t\t\tconsole.log(chalk.dim(\"No session selected\"));\n\t\t\treturn;\n\t\t}\n\t\tsessionManager.setSessionFile(selectedSession);\n\t}\n\n\t// Resolve model scope early if provided\n\tlet scopedModels: ScopedModel[] = [];\n\tif (parsed.models && parsed.models.length > 0) {\n\t\tscopedModels = await resolveModelScope(parsed.models);\n\t}\n\n\t// Determine mode and output behavior\n\tconst mode = parsed.mode || \"text\";\n\tconst shouldPrintMessages = isInteractive;\n\n\t// Find initial model\n\tlet initialModel = await findInitialModelForSession(parsed, scopedModels, settingsManager);\n\tlet initialThinking: ThinkingLevel = \"off\";\n\n\t// Get thinking level from scoped models if applicable\n\tif (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {\n\t\tinitialThinking = scopedModels[0].thinkingLevel;\n\t} else {\n\t\t// Try saved thinking level\n\t\tconst savedThinking = settingsManager.getDefaultThinkingLevel();\n\t\tif (savedThinking) {\n\t\t\tinitialThinking = savedThinking;\n\t\t}\n\t}\n\n\t// Non-interactive mode: fail early if no model available\n\tif (!isInteractive && !initialModel) {\n\t\tconsole.error(chalk.red(\"No models available.\"));\n\t\tconsole.error(chalk.yellow(\"\\nSet an API key environment variable:\"));\n\t\tconsole.error(\" ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, etc.\");\n\t\tconsole.error(chalk.yellow(`\\nOr create ${getModelsPath()}`));\n\t\tprocess.exit(1);\n\t}\n\n\t// Non-interactive mode: validate API key exists\n\tif (!isInteractive && initialModel) {\n\t\tconst apiKey = parsed.apiKey || (await getApiKeyForModel(initialModel));\n\t\tif (!apiKey) {\n\t\t\tconsole.error(chalk.red(`No API key found for ${initialModel.provider}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\t// Build system prompt\n\tconst skillsEnabled = !parsed.noSkills && settingsManager.getSkillsEnabled();\n\tconst systemPrompt = buildSystemPrompt({\n\t\tcustomPrompt: parsed.systemPrompt,\n\t\tselectedTools: parsed.tools,\n\t\tappendSystemPrompt: parsed.appendSystemPrompt,\n\t\tskillsEnabled,\n\t});\n\n\t// Handle session restoration\n\tlet modelFallbackMessage: string | null = null;\n\n\tif (parsed.continue || parsed.resume || parsed.session) {\n\t\tconst savedModel = sessionManager.loadModel();\n\t\tif (savedModel) {\n\t\t\tconst result = await restoreModelFromSession(\n\t\t\t\tsavedModel.provider,\n\t\t\t\tsavedModel.modelId,\n\t\t\t\tinitialModel,\n\t\t\t\tshouldPrintMessages,\n\t\t\t);\n\n\t\t\tif (result.model) {\n\t\t\t\tinitialModel = result.model;\n\t\t\t}\n\t\t\tmodelFallbackMessage = result.fallbackMessage;\n\t\t}\n\n\t\t// Load and restore thinking level\n\t\tconst thinkingLevel = sessionManager.loadThinkingLevel() as ThinkingLevel;\n\t\tif (thinkingLevel) {\n\t\t\tinitialThinking = thinkingLevel;\n\t\t\tif (shouldPrintMessages) {\n\t\t\t\tconsole.log(chalk.dim(`Restored thinking level: ${thinkingLevel}`));\n\t\t\t}\n\t\t}\n\t}\n\n\t// CLI --thinking flag takes highest priority\n\tif (parsed.thinking) {\n\t\tinitialThinking = parsed.thinking;\n\t}\n\n\t// Determine which tools to use\n\tlet selectedTools = parsed.tools ? parsed.tools.map((name) => allTools[name]) : codingTools;\n\n\t// Discover and load hooks from:\n\t// 1. ~/.pi/agent/hooks/*.ts (global)\n\t// 2. cwd/.pi/hooks/*.ts (project-local)\n\t// 3. Explicit paths in settings.json\n\t// 4. CLI --hook flags\n\tlet hookRunner: HookRunner | null = null;\n\tconst cwd = process.cwd();\n\tconst configuredHookPaths = [...settingsManager.getHookPaths(), ...(parsed.hooks ?? [])];\n\tconst { hooks, errors } = await discoverAndLoadHooks(configuredHookPaths, cwd);\n\n\t// Report hook loading errors\n\tfor (const { path, error } of errors) {\n\t\tconsole.error(chalk.red(`Failed to load hook \"${path}\": ${error}`));\n\t}\n\n\tif (hooks.length > 0) {\n\t\tconst timeout = settingsManager.getHookTimeout();\n\t\thookRunner = new HookRunner(hooks, cwd, timeout);\n\t}\n\n\t// Discover and load custom tools from:\n\t// 1. ~/.pi/agent/tools/*.ts (global)\n\t// 2. cwd/.pi/tools/*.ts (project-local)\n\t// 3. Explicit paths in settings.json\n\t// 4. CLI --tool flags\n\tconst configuredToolPaths = [...settingsManager.getCustomToolPaths(), ...(parsed.customTools ?? [])];\n\tconst builtInToolNames = Object.keys(allTools);\n\tconst {\n\t\ttools: loadedCustomTools,\n\t\terrors: toolErrors,\n\t\tsetUIContext: setToolUIContext,\n\t} = await discoverAndLoadCustomTools(configuredToolPaths, cwd, builtInToolNames);\n\n\t// Report custom tool loading errors\n\tfor (const { path, error } of toolErrors) {\n\t\tconsole.error(chalk.red(`Failed to load custom tool \"${path}\": ${error}`));\n\t}\n\n\t// Add custom tools to selected tools\n\tif (loadedCustomTools.length > 0) {\n\t\tconst customToolInstances = loadedCustomTools.map((lt) => lt.tool);\n\t\tselectedTools = [...selectedTools, ...customToolInstances] as typeof selectedTools;\n\t}\n\n\t// Wrap tools with hook callbacks (built-in and custom)\n\tif (hookRunner) {\n\t\tselectedTools = wrapToolsWithHooks(selectedTools, hookRunner);\n\t}\n\n\t// Create agent\n\tconst agent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt,\n\t\t\tmodel: initialModel as any, // Can be null in interactive mode\n\t\t\tthinkingLevel: initialThinking,\n\t\t\ttools: selectedTools,\n\t\t},\n\t\tmessageTransformer,\n\t\tqueueMode: settingsManager.getQueueMode(),\n\t\ttransport: new ProviderTransport({\n\t\t\tgetApiKey: async () => {\n\t\t\t\tconst currentModel = agent.state.model;\n\t\t\t\tif (!currentModel) {\n\t\t\t\t\tthrow new Error(\"No model selected\");\n\t\t\t\t}\n\n\t\t\t\tif (parsed.apiKey) {\n\t\t\t\t\treturn parsed.apiKey;\n\t\t\t\t}\n\n\t\t\t\tconst key = await getApiKeyForModel(currentModel);\n\t\t\t\tif (!key) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`No API key found for provider \"${currentModel.provider}\". Please set the appropriate environment variable or update ${getModelsPath()}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn key;\n\t\t\t},\n\t\t}),\n\t});\n\n\t// If initial thinking was requested but model doesn't support it, reset to off\n\tif (initialThinking !== \"off\" && initialModel && !initialModel.reasoning) {\n\t\tagent.setThinkingLevel(\"off\");\n\t}\n\n\t// Load previous messages if continuing, resuming, or using --session\n\tif (parsed.continue || parsed.resume || parsed.session) {\n\t\tconst messages = sessionManager.loadMessages();\n\t\tif (messages.length > 0) {\n\t\t\tagent.replaceMessages(messages);\n\t\t}\n\t}\n\n\t// Load file commands for slash command expansion\n\tconst fileCommands = loadSlashCommands();\n\n\t// Create session\n\tconst session = new AgentSession({\n\t\tagent,\n\t\tsessionManager,\n\t\tsettingsManager,\n\t\tscopedModels,\n\t\tfileCommands,\n\t\thookRunner,\n\t\tcustomTools: loadedCustomTools,\n\t});\n\n\t// Route to appropriate mode\n\tif (mode === \"rpc\") {\n\t\tawait runRpcMode(session);\n\t} else if (isInteractive) {\n\t\t// Check for new version in the background\n\t\tconst versionCheckPromise = checkForNewVersion(VERSION).catch(() => null);\n\n\t\t// Check if we should show changelog\n\t\tconst changelogMarkdown = getChangelogForDisplay(parsed, settingsManager);\n\n\t\t// Show model scope if provided\n\t\tif (scopedModels.length > 0) {\n\t\t\tconst modelList = scopedModels\n\t\t\t\t.map((sm) => {\n\t\t\t\t\tconst thinkingStr = sm.thinkingLevel !== \"off\" ? `:${sm.thinkingLevel}` : \"\";\n\t\t\t\t\treturn `${sm.model.id}${thinkingStr}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \");\n\t\t\tconsole.log(chalk.dim(`Model scope: ${modelList} ${chalk.gray(\"(Ctrl+P to cycle)\")}`));\n\t\t}\n\n\t\t// Ensure fd tool is available for file autocomplete\n\t\tconst fdPath = await ensureTool(\"fd\");\n\n\t\tawait runInteractiveMode(\n\t\t\tsession,\n\t\t\tVERSION,\n\t\t\tchangelogMarkdown,\n\t\t\tmodelFallbackMessage,\n\t\t\tversionCheckPromise,\n\t\t\tparsed.messages,\n\t\t\tloadedCustomTools,\n\t\t\tsetToolUIContext,\n\t\t\tinitialMessage,\n\t\t\tinitialAttachments,\n\t\t\tfdPath,\n\t\t);\n\t} else {\n\t\t// Non-interactive mode (--print flag or --mode flag)\n\t\tawait runPrintMode(session, mode, parsed.messages, initialMessage, initialAttachments);\n\t\t// Clean up and exit (file watchers keep process alive)\n\t\tstopThemeWatcher();\n\t\t// Wait for stdout to fully flush before exiting\n\t\tif (process.stdout.writableLength > 0) {\n\t\t\tawait new Promise<void>((resolve) => process.stdout.once(\"drain\", resolve));\n\t\t}\n\t\tprocess.exit(0);\n\t}\n}\n\n/** Find initial model based on CLI args, scoped models, settings, or available models */\nasync function findInitialModelForSession(parsed: Args, scopedModels: ScopedModel[], settingsManager: SettingsManager) {\n\t// 1. CLI args take priority\n\tif (parsed.provider && parsed.model) {\n\t\tconst { model, error } = findModel(parsed.provider, parsed.model);\n\t\tif (error) {\n\t\t\tconsole.error(chalk.red(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (!model) {\n\t\t\tconsole.error(chalk.red(`Model ${parsed.provider}/${parsed.model} not found`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\treturn model;\n\t}\n\n\t// 2. Use first model from scoped models (skip if continuing/resuming)\n\tif (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {\n\t\treturn scopedModels[0].model;\n\t}\n\n\t// 3. Try saved default from settings\n\tconst defaultProvider = settingsManager.getDefaultProvider();\n\tconst defaultModelId = settingsManager.getDefaultModel();\n\tif (defaultProvider && defaultModelId) {\n\t\tconst { model, error } = findModel(defaultProvider, defaultModelId);\n\t\tif (error) {\n\t\t\tconsole.error(chalk.red(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (model) {\n\t\t\treturn model;\n\t\t}\n\t}\n\n\t// 4. Try first available model with valid API key\n\tconst { models: availableModels, error } = await getAvailableModels();\n\n\tif (error) {\n\t\tconsole.error(chalk.red(error));\n\t\tprocess.exit(1);\n\t}\n\n\tif (availableModels.length > 0) {\n\t\treturn availableModels[0];\n\t}\n\n\treturn null;\n}\n\n/** Get changelog markdown to display (only for new sessions with updates) */\nfunction getChangelogForDisplay(parsed: Args, settingsManager: SettingsManager): string | null {\n\tif (parsed.continue || parsed.resume) {\n\t\treturn null;\n\t}\n\n\tconst lastVersion = settingsManager.getLastChangelogVersion();\n\tconst changelogPath = getChangelogPath();\n\tconst entries = parseChangelog(changelogPath);\n\n\tif (!lastVersion) {\n\t\t// First run - show all entries\n\t\tif (entries.length > 0) {\n\t\t\tsettingsManager.setLastChangelogVersion(VERSION);\n\t\t\treturn entries.map((e) => e.content).join(\"\\n\\n\");\n\t\t}\n\t} else {\n\t\t// Check for new entries since last version\n\t\tconst newEntries = getNewEntries(entries, lastVersion);\n\t\tif (newEntries.length > 0) {\n\t\t\tsettingsManager.setLastChangelogVersion(VERSION);\n\t\t\treturn newEntries.map((e) => e.content).join(\"\\n\\n\");\n\t\t}\n\t}\n\n\treturn null;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;GAEG;AAyKH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,iBAiUxC","sourcesContent":["/**\n * Main entry point for the coding agent\n */\n\nimport { Agent, type Attachment, ProviderTransport, type ThinkingLevel } from \"@mariozechner/pi-agent-core\";\nimport { setOAuthStorage, supportsXhigh } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname } from \"path\";\nimport { type Args, parseArgs, printHelp } from \"./cli/args.js\";\nimport { processFileArguments } from \"./cli/file-processor.js\";\nimport { listModels } from \"./cli/list-models.js\";\nimport { selectSession } from \"./cli/session-picker.js\";\nimport { getModelsPath, getOAuthPath, VERSION } from \"./config.js\";\nimport { AgentSession } from \"./core/agent-session.js\";\nimport { discoverAndLoadCustomTools, type LoadedCustomTool } from \"./core/custom-tools/index.js\";\nimport { exportFromFile } from \"./core/export-html.js\";\nimport { discoverAndLoadHooks, HookRunner, wrapToolsWithHooks } from \"./core/hooks/index.js\";\nimport { messageTransformer } from \"./core/messages.js\";\nimport { findModel, getApiKeyForModel, getAvailableModels } from \"./core/model-config.js\";\nimport { resolveModelScope, restoreModelFromSession, type ScopedModel } from \"./core/model-resolver.js\";\nimport { SessionManager } from \"./core/session-manager.js\";\nimport { SettingsManager } from \"./core/settings-manager.js\";\nimport { loadSlashCommands } from \"./core/slash-commands.js\";\nimport { buildSystemPrompt } from \"./core/system-prompt.js\";\nimport { allTools, codingTools } from \"./core/tools/index.js\";\nimport { InteractiveMode, runPrintMode, runRpcMode } from \"./modes/index.js\";\nimport { initTheme, stopThemeWatcher } from \"./modes/interactive/theme/theme.js\";\nimport { getChangelogPath, getNewEntries, parseChangelog } from \"./utils/changelog.js\";\nimport { ensureTool } from \"./utils/tools-manager.js\";\n\n/** Configure OAuth storage to use the coding-agent's configurable path */\nfunction configureOAuthStorage(): void {\n\tconst oauthPath = getOAuthPath();\n\n\tsetOAuthStorage({\n\t\tload: () => {\n\t\t\tif (!existsSync(oauthPath)) {\n\t\t\t\treturn {};\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(readFileSync(oauthPath, \"utf-8\"));\n\t\t\t} catch {\n\t\t\t\treturn {};\n\t\t\t}\n\t\t},\n\t\tsave: (storage) => {\n\t\t\tconst dir = dirname(oauthPath);\n\t\t\tif (!existsSync(dir)) {\n\t\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t\t}\n\t\t\twriteFileSync(oauthPath, JSON.stringify(storage, null, 2), \"utf-8\");\n\t\t\tchmodSync(oauthPath, 0o600);\n\t\t},\n\t});\n}\n\n/** Check npm registry for new version (non-blocking) */\nasync function checkForNewVersion(currentVersion: string): Promise<string | null> {\n\ttry {\n\t\tconst response = await fetch(\"https://registry.npmjs.org/@mariozechner/pi-coding-agent/latest\");\n\t\tif (!response.ok) return null;\n\n\t\tconst data = (await response.json()) as { version?: string };\n\t\tconst latestVersion = data.version;\n\n\t\tif (latestVersion && latestVersion !== currentVersion) {\n\t\t\treturn latestVersion;\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\t// Silently fail - don't disrupt the user experience\n\t\treturn null;\n\t}\n}\n\n/** Run interactive mode with TUI */\nasync function runInteractiveMode(\n\tsession: AgentSession,\n\tversion: string,\n\tchangelogMarkdown: string | null,\n\tmodelFallbackMessage: string | null,\n\tversionCheckPromise: Promise<string | null>,\n\tinitialMessages: string[],\n\tcustomTools: LoadedCustomTool[],\n\tsetToolUIContext: (uiContext: import(\"./core/hooks/types.js\").HookUIContext, hasUI: boolean) => void,\n\tinitialMessage?: string,\n\tinitialAttachments?: Attachment[],\n\tfdPath: string | null = null,\n): Promise<void> {\n\tconst mode = new InteractiveMode(session, version, changelogMarkdown, customTools, setToolUIContext, fdPath);\n\n\t// Initialize TUI (subscribes to agent events internally)\n\tawait mode.init();\n\n\t// Handle version check result when it completes (don't block)\n\tversionCheckPromise.then((newVersion) => {\n\t\tif (newVersion) {\n\t\t\tmode.showNewVersionNotification(newVersion);\n\t\t}\n\t});\n\n\t// Render any existing messages (from --continue mode)\n\tmode.renderInitialMessages(session.state);\n\n\t// Show model fallback warning at the end of the chat if applicable\n\tif (modelFallbackMessage) {\n\t\tmode.showWarning(modelFallbackMessage);\n\t}\n\n\t// Process initial message with attachments if provided (from @file args)\n\tif (initialMessage) {\n\t\ttry {\n\t\t\tawait session.prompt(initialMessage, { attachments: initialAttachments });\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n\n\t// Process remaining initial messages if provided (from CLI args)\n\tfor (const message of initialMessages) {\n\t\ttry {\n\t\t\tawait session.prompt(message);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n\n\t// Interactive loop\n\twhile (true) {\n\t\tconst userInput = await mode.getUserInput();\n\n\t\t// Process the message\n\t\ttry {\n\t\t\tawait session.prompt(userInput);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n}\n\n/** Prepare initial message from @file arguments */\nasync function prepareInitialMessage(parsed: Args): Promise<{\n\tinitialMessage?: string;\n\tinitialAttachments?: Attachment[];\n}> {\n\tif (parsed.fileArgs.length === 0) {\n\t\treturn {};\n\t}\n\n\tconst { textContent, imageAttachments } = await processFileArguments(parsed.fileArgs);\n\n\t// Combine file content with first plain text message (if any)\n\tlet initialMessage: string;\n\tif (parsed.messages.length > 0) {\n\t\tinitialMessage = textContent + parsed.messages[0];\n\t\tparsed.messages.shift(); // Remove first message as it's been combined\n\t} else {\n\t\tinitialMessage = textContent;\n\t}\n\n\treturn {\n\t\tinitialMessage,\n\t\tinitialAttachments: imageAttachments.length > 0 ? imageAttachments : undefined,\n\t};\n}\n\nexport async function main(args: string[]) {\n\t// Configure OAuth storage to use the coding-agent's configurable path\n\t// This must happen before any OAuth operations\n\tconfigureOAuthStorage();\n\n\tconst parsed = parseArgs(args);\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\treturn;\n\t}\n\n\tif (parsed.help) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\t// Handle --list-models flag: list available models and exit\n\tif (parsed.listModels !== undefined) {\n\t\tconst searchPattern = typeof parsed.listModels === \"string\" ? parsed.listModels : undefined;\n\t\tawait listModels(searchPattern);\n\t\treturn;\n\t}\n\n\t// Handle --export flag: convert session file to HTML and exit\n\tif (parsed.export) {\n\t\ttry {\n\t\t\tconst outputPath = parsed.messages.length > 0 ? parsed.messages[0] : undefined;\n\t\t\tconst result = exportFromFile(parsed.export, outputPath);\n\t\t\tconsole.log(`Exported to: ${result}`);\n\t\t\treturn;\n\t\t} catch (error: unknown) {\n\t\t\tconst message = error instanceof Error ? error.message : \"Failed to export session\";\n\t\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\t// Validate: RPC mode doesn't support @file arguments\n\tif (parsed.mode === \"rpc\" && parsed.fileArgs.length > 0) {\n\t\tconsole.error(chalk.red(\"Error: @file arguments are not supported in RPC mode\"));\n\t\tprocess.exit(1);\n\t}\n\n\t// Process @file arguments\n\tconst { initialMessage, initialAttachments } = await prepareInitialMessage(parsed);\n\n\t// Determine if we're in interactive mode (needed for theme watcher)\n\tconst isInteractive = !parsed.print && parsed.mode === undefined;\n\n\t// Initialize theme (before any TUI rendering)\n\tconst settingsManager = new SettingsManager();\n\tconst themeName = settingsManager.getTheme();\n\tinitTheme(themeName, isInteractive);\n\n\t// Setup session manager\n\tconst sessionManager = new SessionManager(parsed.continue && !parsed.resume, parsed.session);\n\n\tif (parsed.noSession) {\n\t\tsessionManager.disable();\n\t}\n\n\t// Handle --resume flag: show session selector\n\tif (parsed.resume) {\n\t\tconst selectedSession = await selectSession(sessionManager);\n\t\tif (!selectedSession) {\n\t\t\tconsole.log(chalk.dim(\"No session selected\"));\n\t\t\treturn;\n\t\t}\n\t\tsessionManager.setSessionFile(selectedSession);\n\t}\n\n\t// Resolve model scope early if provided\n\tlet scopedModels: ScopedModel[] = [];\n\tif (parsed.models && parsed.models.length > 0) {\n\t\tscopedModels = await resolveModelScope(parsed.models);\n\t}\n\n\t// Determine mode and output behavior\n\tconst mode = parsed.mode || \"text\";\n\tconst shouldPrintMessages = isInteractive;\n\n\t// Find initial model\n\tlet initialModel = await findInitialModelForSession(parsed, scopedModels, settingsManager);\n\tlet initialThinking: ThinkingLevel = \"off\";\n\n\t// Get thinking level from scoped models if applicable\n\tif (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {\n\t\tinitialThinking = scopedModels[0].thinkingLevel;\n\t} else {\n\t\t// Try saved thinking level\n\t\tconst savedThinking = settingsManager.getDefaultThinkingLevel();\n\t\tif (savedThinking) {\n\t\t\tinitialThinking = savedThinking;\n\t\t}\n\t}\n\n\t// Non-interactive mode: fail early if no model available\n\tif (!isInteractive && !initialModel) {\n\t\tconsole.error(chalk.red(\"No models available.\"));\n\t\tconsole.error(chalk.yellow(\"\\nSet an API key environment variable:\"));\n\t\tconsole.error(\" ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, etc.\");\n\t\tconsole.error(chalk.yellow(`\\nOr create ${getModelsPath()}`));\n\t\tprocess.exit(1);\n\t}\n\n\t// Non-interactive mode: validate API key exists\n\tif (!isInteractive && initialModel) {\n\t\tconst apiKey = parsed.apiKey || (await getApiKeyForModel(initialModel));\n\t\tif (!apiKey) {\n\t\t\tconsole.error(chalk.red(`No API key found for ${initialModel.provider}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\t// Build system prompt\n\tconst skillsEnabled = !parsed.noSkills && settingsManager.getSkillsEnabled();\n\tconst systemPrompt = buildSystemPrompt({\n\t\tcustomPrompt: parsed.systemPrompt,\n\t\tselectedTools: parsed.tools,\n\t\tappendSystemPrompt: parsed.appendSystemPrompt,\n\t\tskillsEnabled,\n\t});\n\n\t// Handle session restoration\n\tlet modelFallbackMessage: string | null = null;\n\n\tif (parsed.continue || parsed.resume || parsed.session) {\n\t\tconst savedModel = sessionManager.loadModel();\n\t\tif (savedModel) {\n\t\t\tconst result = await restoreModelFromSession(\n\t\t\t\tsavedModel.provider,\n\t\t\t\tsavedModel.modelId,\n\t\t\t\tinitialModel,\n\t\t\t\tshouldPrintMessages,\n\t\t\t);\n\n\t\t\tif (result.model) {\n\t\t\t\tinitialModel = result.model;\n\t\t\t}\n\t\t\tmodelFallbackMessage = result.fallbackMessage;\n\t\t}\n\n\t\t// Load and restore thinking level\n\t\tconst thinkingLevel = sessionManager.loadThinkingLevel() as ThinkingLevel;\n\t\tif (thinkingLevel) {\n\t\t\tinitialThinking = thinkingLevel;\n\t\t\tif (shouldPrintMessages) {\n\t\t\t\tconsole.log(chalk.dim(`Restored thinking level: ${thinkingLevel}`));\n\t\t\t}\n\t\t}\n\t}\n\n\t// CLI --thinking flag takes highest priority\n\tif (parsed.thinking) {\n\t\tinitialThinking = parsed.thinking;\n\t}\n\n\t// Clamp thinking level to model capabilities\n\tif (initialModel) {\n\t\tif (!initialModel.reasoning) {\n\t\t\tinitialThinking = \"off\";\n\t\t} else if (initialThinking === \"xhigh\" && !supportsXhigh(initialModel)) {\n\t\t\tinitialThinking = \"high\";\n\t\t}\n\t}\n\n\t// Determine which tools to use\n\tlet selectedTools = parsed.tools ? parsed.tools.map((name) => allTools[name]) : codingTools;\n\n\t// Discover and load hooks from:\n\t// 1. ~/.pi/agent/hooks/*.ts (global)\n\t// 2. cwd/.pi/hooks/*.ts (project-local)\n\t// 3. Explicit paths in settings.json\n\t// 4. CLI --hook flags\n\tlet hookRunner: HookRunner | null = null;\n\tconst cwd = process.cwd();\n\tconst configuredHookPaths = [...settingsManager.getHookPaths(), ...(parsed.hooks ?? [])];\n\tconst { hooks, errors } = await discoverAndLoadHooks(configuredHookPaths, cwd);\n\n\t// Report hook loading errors\n\tfor (const { path, error } of errors) {\n\t\tconsole.error(chalk.red(`Failed to load hook \"${path}\": ${error}`));\n\t}\n\n\tif (hooks.length > 0) {\n\t\tconst timeout = settingsManager.getHookTimeout();\n\t\thookRunner = new HookRunner(hooks, cwd, timeout);\n\t}\n\n\t// Discover and load custom tools from:\n\t// 1. ~/.pi/agent/tools/*.ts (global)\n\t// 2. cwd/.pi/tools/*.ts (project-local)\n\t// 3. Explicit paths in settings.json\n\t// 4. CLI --tool flags\n\tconst configuredToolPaths = [...settingsManager.getCustomToolPaths(), ...(parsed.customTools ?? [])];\n\tconst builtInToolNames = Object.keys(allTools);\n\tconst {\n\t\ttools: loadedCustomTools,\n\t\terrors: toolErrors,\n\t\tsetUIContext: setToolUIContext,\n\t} = await discoverAndLoadCustomTools(configuredToolPaths, cwd, builtInToolNames);\n\n\t// Report custom tool loading errors\n\tfor (const { path, error } of toolErrors) {\n\t\tconsole.error(chalk.red(`Failed to load custom tool \"${path}\": ${error}`));\n\t}\n\n\t// Add custom tools to selected tools\n\tif (loadedCustomTools.length > 0) {\n\t\tconst customToolInstances = loadedCustomTools.map((lt) => lt.tool);\n\t\tselectedTools = [...selectedTools, ...customToolInstances] as typeof selectedTools;\n\t}\n\n\t// Wrap tools with hook callbacks (built-in and custom)\n\tif (hookRunner) {\n\t\tselectedTools = wrapToolsWithHooks(selectedTools, hookRunner);\n\t}\n\n\t// Create agent\n\tconst agent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt,\n\t\t\tmodel: initialModel as any, // Can be null in interactive mode\n\t\t\tthinkingLevel: initialThinking,\n\t\t\ttools: selectedTools,\n\t\t},\n\t\tmessageTransformer,\n\t\tqueueMode: settingsManager.getQueueMode(),\n\t\ttransport: new ProviderTransport({\n\t\t\tgetApiKey: async () => {\n\t\t\t\tconst currentModel = agent.state.model;\n\t\t\t\tif (!currentModel) {\n\t\t\t\t\tthrow new Error(\"No model selected\");\n\t\t\t\t}\n\n\t\t\t\tif (parsed.apiKey) {\n\t\t\t\t\treturn parsed.apiKey;\n\t\t\t\t}\n\n\t\t\t\tconst key = await getApiKeyForModel(currentModel);\n\t\t\t\tif (!key) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`No API key found for provider \"${currentModel.provider}\". Please set the appropriate environment variable or update ${getModelsPath()}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn key;\n\t\t\t},\n\t\t}),\n\t});\n\n\t// Load previous messages if continuing, resuming, or using --session\n\tif (parsed.continue || parsed.resume || parsed.session) {\n\t\tconst messages = sessionManager.loadMessages();\n\t\tif (messages.length > 0) {\n\t\t\tagent.replaceMessages(messages);\n\t\t}\n\t}\n\n\t// Load file commands for slash command expansion\n\tconst fileCommands = loadSlashCommands();\n\n\t// Create session\n\tconst session = new AgentSession({\n\t\tagent,\n\t\tsessionManager,\n\t\tsettingsManager,\n\t\tscopedModels,\n\t\tfileCommands,\n\t\thookRunner,\n\t\tcustomTools: loadedCustomTools,\n\t});\n\n\t// Route to appropriate mode\n\tif (mode === \"rpc\") {\n\t\tawait runRpcMode(session);\n\t} else if (isInteractive) {\n\t\t// Check for new version in the background\n\t\tconst versionCheckPromise = checkForNewVersion(VERSION).catch(() => null);\n\n\t\t// Check if we should show changelog\n\t\tconst changelogMarkdown = getChangelogForDisplay(parsed, settingsManager);\n\n\t\t// Show model scope if provided\n\t\tif (scopedModels.length > 0) {\n\t\t\tconst modelList = scopedModels\n\t\t\t\t.map((sm) => {\n\t\t\t\t\tconst thinkingStr = sm.thinkingLevel !== \"off\" ? `:${sm.thinkingLevel}` : \"\";\n\t\t\t\t\treturn `${sm.model.id}${thinkingStr}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \");\n\t\t\tconsole.log(chalk.dim(`Model scope: ${modelList} ${chalk.gray(\"(Ctrl+P to cycle)\")}`));\n\t\t}\n\n\t\t// Ensure fd tool is available for file autocomplete\n\t\tconst fdPath = await ensureTool(\"fd\");\n\n\t\tawait runInteractiveMode(\n\t\t\tsession,\n\t\t\tVERSION,\n\t\t\tchangelogMarkdown,\n\t\t\tmodelFallbackMessage,\n\t\t\tversionCheckPromise,\n\t\t\tparsed.messages,\n\t\t\tloadedCustomTools,\n\t\t\tsetToolUIContext,\n\t\t\tinitialMessage,\n\t\t\tinitialAttachments,\n\t\t\tfdPath,\n\t\t);\n\t} else {\n\t\t// Non-interactive mode (--print flag or --mode flag)\n\t\tawait runPrintMode(session, mode, parsed.messages, initialMessage, initialAttachments);\n\t\t// Clean up and exit (file watchers keep process alive)\n\t\tstopThemeWatcher();\n\t\t// Wait for stdout to fully flush before exiting\n\t\tif (process.stdout.writableLength > 0) {\n\t\t\tawait new Promise<void>((resolve) => process.stdout.once(\"drain\", resolve));\n\t\t}\n\t\tprocess.exit(0);\n\t}\n}\n\n/** Find initial model based on CLI args, scoped models, settings, or available models */\nasync function findInitialModelForSession(parsed: Args, scopedModels: ScopedModel[], settingsManager: SettingsManager) {\n\t// 1. CLI args take priority\n\tif (parsed.provider && parsed.model) {\n\t\tconst { model, error } = findModel(parsed.provider, parsed.model);\n\t\tif (error) {\n\t\t\tconsole.error(chalk.red(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (!model) {\n\t\t\tconsole.error(chalk.red(`Model ${parsed.provider}/${parsed.model} not found`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\treturn model;\n\t}\n\n\t// 2. Use first model from scoped models (skip if continuing/resuming)\n\tif (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {\n\t\treturn scopedModels[0].model;\n\t}\n\n\t// 3. Try saved default from settings\n\tconst defaultProvider = settingsManager.getDefaultProvider();\n\tconst defaultModelId = settingsManager.getDefaultModel();\n\tif (defaultProvider && defaultModelId) {\n\t\tconst { model, error } = findModel(defaultProvider, defaultModelId);\n\t\tif (error) {\n\t\t\tconsole.error(chalk.red(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (model) {\n\t\t\treturn model;\n\t\t}\n\t}\n\n\t// 4. Try first available model with valid API key\n\tconst { models: availableModels, error } = await getAvailableModels();\n\n\tif (error) {\n\t\tconsole.error(chalk.red(error));\n\t\tprocess.exit(1);\n\t}\n\n\tif (availableModels.length > 0) {\n\t\treturn availableModels[0];\n\t}\n\n\treturn null;\n}\n\n/** Get changelog markdown to display (only for new sessions with updates) */\nfunction getChangelogForDisplay(parsed: Args, settingsManager: SettingsManager): string | null {\n\tif (parsed.continue || parsed.resume) {\n\t\treturn null;\n\t}\n\n\tconst lastVersion = settingsManager.getLastChangelogVersion();\n\tconst changelogPath = getChangelogPath();\n\tconst entries = parseChangelog(changelogPath);\n\n\tif (!lastVersion) {\n\t\t// First run - show all entries\n\t\tif (entries.length > 0) {\n\t\t\tsettingsManager.setLastChangelogVersion(VERSION);\n\t\t\treturn entries.map((e) => e.content).join(\"\\n\\n\");\n\t\t}\n\t} else {\n\t\t// Check for new entries since last version\n\t\tconst newEntries = getNewEntries(entries, lastVersion);\n\t\tif (newEntries.length > 0) {\n\t\t\tsettingsManager.setLastChangelogVersion(VERSION);\n\t\t\treturn newEntries.map((e) => e.content).join(\"\\n\\n\");\n\t\t}\n\t}\n\n\treturn null;\n}\n"]}
|
package/dist/main.js
CHANGED
|
@@ -2,12 +2,15 @@
|
|
|
2
2
|
* Main entry point for the coding agent
|
|
3
3
|
*/
|
|
4
4
|
import { Agent, ProviderTransport } from "@mariozechner/pi-agent-core";
|
|
5
|
+
import { setOAuthStorage, supportsXhigh } from "@mariozechner/pi-ai";
|
|
5
6
|
import chalk from "chalk";
|
|
7
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
8
|
+
import { dirname } from "path";
|
|
6
9
|
import { parseArgs, printHelp } from "./cli/args.js";
|
|
7
10
|
import { processFileArguments } from "./cli/file-processor.js";
|
|
8
11
|
import { listModels } from "./cli/list-models.js";
|
|
9
12
|
import { selectSession } from "./cli/session-picker.js";
|
|
10
|
-
import { getModelsPath, VERSION } from "./config.js";
|
|
13
|
+
import { getModelsPath, getOAuthPath, VERSION } from "./config.js";
|
|
11
14
|
import { AgentSession } from "./core/agent-session.js";
|
|
12
15
|
import { discoverAndLoadCustomTools } from "./core/custom-tools/index.js";
|
|
13
16
|
import { exportFromFile } from "./core/export-html.js";
|
|
@@ -24,6 +27,31 @@ import { InteractiveMode, runPrintMode, runRpcMode } from "./modes/index.js";
|
|
|
24
27
|
import { initTheme, stopThemeWatcher } from "./modes/interactive/theme/theme.js";
|
|
25
28
|
import { getChangelogPath, getNewEntries, parseChangelog } from "./utils/changelog.js";
|
|
26
29
|
import { ensureTool } from "./utils/tools-manager.js";
|
|
30
|
+
/** Configure OAuth storage to use the coding-agent's configurable path */
|
|
31
|
+
function configureOAuthStorage() {
|
|
32
|
+
const oauthPath = getOAuthPath();
|
|
33
|
+
setOAuthStorage({
|
|
34
|
+
load: () => {
|
|
35
|
+
if (!existsSync(oauthPath)) {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(readFileSync(oauthPath, "utf-8"));
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
save: (storage) => {
|
|
46
|
+
const dir = dirname(oauthPath);
|
|
47
|
+
if (!existsSync(dir)) {
|
|
48
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
49
|
+
}
|
|
50
|
+
writeFileSync(oauthPath, JSON.stringify(storage, null, 2), "utf-8");
|
|
51
|
+
chmodSync(oauthPath, 0o600);
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
}
|
|
27
55
|
/** Check npm registry for new version (non-blocking) */
|
|
28
56
|
async function checkForNewVersion(currentVersion) {
|
|
29
57
|
try {
|
|
@@ -113,6 +141,9 @@ async function prepareInitialMessage(parsed) {
|
|
|
113
141
|
};
|
|
114
142
|
}
|
|
115
143
|
export async function main(args) {
|
|
144
|
+
// Configure OAuth storage to use the coding-agent's configurable path
|
|
145
|
+
// This must happen before any OAuth operations
|
|
146
|
+
configureOAuthStorage();
|
|
116
147
|
const parsed = parseArgs(args);
|
|
117
148
|
if (parsed.version) {
|
|
118
149
|
console.log(VERSION);
|
|
@@ -239,6 +270,15 @@ export async function main(args) {
|
|
|
239
270
|
if (parsed.thinking) {
|
|
240
271
|
initialThinking = parsed.thinking;
|
|
241
272
|
}
|
|
273
|
+
// Clamp thinking level to model capabilities
|
|
274
|
+
if (initialModel) {
|
|
275
|
+
if (!initialModel.reasoning) {
|
|
276
|
+
initialThinking = "off";
|
|
277
|
+
}
|
|
278
|
+
else if (initialThinking === "xhigh" && !supportsXhigh(initialModel)) {
|
|
279
|
+
initialThinking = "high";
|
|
280
|
+
}
|
|
281
|
+
}
|
|
242
282
|
// Determine which tools to use
|
|
243
283
|
let selectedTools = parsed.tools ? parsed.tools.map((name) => allTools[name]) : codingTools;
|
|
244
284
|
// Discover and load hooks from:
|
|
@@ -306,10 +346,6 @@ export async function main(args) {
|
|
|
306
346
|
},
|
|
307
347
|
}),
|
|
308
348
|
});
|
|
309
|
-
// If initial thinking was requested but model doesn't support it, reset to off
|
|
310
|
-
if (initialThinking !== "off" && initialModel && !initialModel.reasoning) {
|
|
311
|
-
agent.setThinkingLevel("off");
|
|
312
|
-
}
|
|
313
349
|
// Load previous messages if continuing, resuming, or using --session
|
|
314
350
|
if (parsed.continue || parsed.resume || parsed.session) {
|
|
315
351
|
const messages = sessionManager.loadMessages();
|
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Main entry point for the coding agent\n */\n\nimport { Agent, type Attachment, ProviderTransport, type ThinkingLevel } from \"@mariozechner/pi-agent-core\";\nimport chalk from \"chalk\";\nimport { type Args, parseArgs, printHelp } from \"./cli/args.js\";\nimport { processFileArguments } from \"./cli/file-processor.js\";\nimport { listModels } from \"./cli/list-models.js\";\nimport { selectSession } from \"./cli/session-picker.js\";\nimport { getModelsPath, VERSION } from \"./config.js\";\nimport { AgentSession } from \"./core/agent-session.js\";\nimport { discoverAndLoadCustomTools, type LoadedCustomTool } from \"./core/custom-tools/index.js\";\nimport { exportFromFile } from \"./core/export-html.js\";\nimport { discoverAndLoadHooks, HookRunner, wrapToolsWithHooks } from \"./core/hooks/index.js\";\nimport { messageTransformer } from \"./core/messages.js\";\nimport { findModel, getApiKeyForModel, getAvailableModels } from \"./core/model-config.js\";\nimport { resolveModelScope, restoreModelFromSession, type ScopedModel } from \"./core/model-resolver.js\";\nimport { SessionManager } from \"./core/session-manager.js\";\nimport { SettingsManager } from \"./core/settings-manager.js\";\nimport { loadSlashCommands } from \"./core/slash-commands.js\";\nimport { buildSystemPrompt } from \"./core/system-prompt.js\";\nimport { allTools, codingTools } from \"./core/tools/index.js\";\nimport { InteractiveMode, runPrintMode, runRpcMode } from \"./modes/index.js\";\nimport { initTheme, stopThemeWatcher } from \"./modes/interactive/theme/theme.js\";\nimport { getChangelogPath, getNewEntries, parseChangelog } from \"./utils/changelog.js\";\nimport { ensureTool } from \"./utils/tools-manager.js\";\n\n/** Check npm registry for new version (non-blocking) */\nasync function checkForNewVersion(currentVersion: string): Promise<string | null> {\n\ttry {\n\t\tconst response = await fetch(\"https://registry.npmjs.org/@mariozechner/pi-coding-agent/latest\");\n\t\tif (!response.ok) return null;\n\n\t\tconst data = (await response.json()) as { version?: string };\n\t\tconst latestVersion = data.version;\n\n\t\tif (latestVersion && latestVersion !== currentVersion) {\n\t\t\treturn latestVersion;\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\t// Silently fail - don't disrupt the user experience\n\t\treturn null;\n\t}\n}\n\n/** Run interactive mode with TUI */\nasync function runInteractiveMode(\n\tsession: AgentSession,\n\tversion: string,\n\tchangelogMarkdown: string | null,\n\tmodelFallbackMessage: string | null,\n\tversionCheckPromise: Promise<string | null>,\n\tinitialMessages: string[],\n\tcustomTools: LoadedCustomTool[],\n\tsetToolUIContext: (uiContext: import(\"./core/hooks/types.js\").HookUIContext, hasUI: boolean) => void,\n\tinitialMessage?: string,\n\tinitialAttachments?: Attachment[],\n\tfdPath: string | null = null,\n): Promise<void> {\n\tconst mode = new InteractiveMode(session, version, changelogMarkdown, customTools, setToolUIContext, fdPath);\n\n\t// Initialize TUI (subscribes to agent events internally)\n\tawait mode.init();\n\n\t// Handle version check result when it completes (don't block)\n\tversionCheckPromise.then((newVersion) => {\n\t\tif (newVersion) {\n\t\t\tmode.showNewVersionNotification(newVersion);\n\t\t}\n\t});\n\n\t// Render any existing messages (from --continue mode)\n\tmode.renderInitialMessages(session.state);\n\n\t// Show model fallback warning at the end of the chat if applicable\n\tif (modelFallbackMessage) {\n\t\tmode.showWarning(modelFallbackMessage);\n\t}\n\n\t// Process initial message with attachments if provided (from @file args)\n\tif (initialMessage) {\n\t\ttry {\n\t\t\tawait session.prompt(initialMessage, { attachments: initialAttachments });\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n\n\t// Process remaining initial messages if provided (from CLI args)\n\tfor (const message of initialMessages) {\n\t\ttry {\n\t\t\tawait session.prompt(message);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n\n\t// Interactive loop\n\twhile (true) {\n\t\tconst userInput = await mode.getUserInput();\n\n\t\t// Process the message\n\t\ttry {\n\t\t\tawait session.prompt(userInput);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n}\n\n/** Prepare initial message from @file arguments */\nasync function prepareInitialMessage(parsed: Args): Promise<{\n\tinitialMessage?: string;\n\tinitialAttachments?: Attachment[];\n}> {\n\tif (parsed.fileArgs.length === 0) {\n\t\treturn {};\n\t}\n\n\tconst { textContent, imageAttachments } = await processFileArguments(parsed.fileArgs);\n\n\t// Combine file content with first plain text message (if any)\n\tlet initialMessage: string;\n\tif (parsed.messages.length > 0) {\n\t\tinitialMessage = textContent + parsed.messages[0];\n\t\tparsed.messages.shift(); // Remove first message as it's been combined\n\t} else {\n\t\tinitialMessage = textContent;\n\t}\n\n\treturn {\n\t\tinitialMessage,\n\t\tinitialAttachments: imageAttachments.length > 0 ? imageAttachments : undefined,\n\t};\n}\n\nexport async function main(args: string[]) {\n\tconst parsed = parseArgs(args);\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\treturn;\n\t}\n\n\tif (parsed.help) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\t// Handle --list-models flag: list available models and exit\n\tif (parsed.listModels !== undefined) {\n\t\tconst searchPattern = typeof parsed.listModels === \"string\" ? parsed.listModels : undefined;\n\t\tawait listModels(searchPattern);\n\t\treturn;\n\t}\n\n\t// Handle --export flag: convert session file to HTML and exit\n\tif (parsed.export) {\n\t\ttry {\n\t\t\tconst outputPath = parsed.messages.length > 0 ? parsed.messages[0] : undefined;\n\t\t\tconst result = exportFromFile(parsed.export, outputPath);\n\t\t\tconsole.log(`Exported to: ${result}`);\n\t\t\treturn;\n\t\t} catch (error: unknown) {\n\t\t\tconst message = error instanceof Error ? error.message : \"Failed to export session\";\n\t\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\t// Validate: RPC mode doesn't support @file arguments\n\tif (parsed.mode === \"rpc\" && parsed.fileArgs.length > 0) {\n\t\tconsole.error(chalk.red(\"Error: @file arguments are not supported in RPC mode\"));\n\t\tprocess.exit(1);\n\t}\n\n\t// Process @file arguments\n\tconst { initialMessage, initialAttachments } = await prepareInitialMessage(parsed);\n\n\t// Determine if we're in interactive mode (needed for theme watcher)\n\tconst isInteractive = !parsed.print && parsed.mode === undefined;\n\n\t// Initialize theme (before any TUI rendering)\n\tconst settingsManager = new SettingsManager();\n\tconst themeName = settingsManager.getTheme();\n\tinitTheme(themeName, isInteractive);\n\n\t// Setup session manager\n\tconst sessionManager = new SessionManager(parsed.continue && !parsed.resume, parsed.session);\n\n\tif (parsed.noSession) {\n\t\tsessionManager.disable();\n\t}\n\n\t// Handle --resume flag: show session selector\n\tif (parsed.resume) {\n\t\tconst selectedSession = await selectSession(sessionManager);\n\t\tif (!selectedSession) {\n\t\t\tconsole.log(chalk.dim(\"No session selected\"));\n\t\t\treturn;\n\t\t}\n\t\tsessionManager.setSessionFile(selectedSession);\n\t}\n\n\t// Resolve model scope early if provided\n\tlet scopedModels: ScopedModel[] = [];\n\tif (parsed.models && parsed.models.length > 0) {\n\t\tscopedModels = await resolveModelScope(parsed.models);\n\t}\n\n\t// Determine mode and output behavior\n\tconst mode = parsed.mode || \"text\";\n\tconst shouldPrintMessages = isInteractive;\n\n\t// Find initial model\n\tlet initialModel = await findInitialModelForSession(parsed, scopedModels, settingsManager);\n\tlet initialThinking: ThinkingLevel = \"off\";\n\n\t// Get thinking level from scoped models if applicable\n\tif (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {\n\t\tinitialThinking = scopedModels[0].thinkingLevel;\n\t} else {\n\t\t// Try saved thinking level\n\t\tconst savedThinking = settingsManager.getDefaultThinkingLevel();\n\t\tif (savedThinking) {\n\t\t\tinitialThinking = savedThinking;\n\t\t}\n\t}\n\n\t// Non-interactive mode: fail early if no model available\n\tif (!isInteractive && !initialModel) {\n\t\tconsole.error(chalk.red(\"No models available.\"));\n\t\tconsole.error(chalk.yellow(\"\\nSet an API key environment variable:\"));\n\t\tconsole.error(\" ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, etc.\");\n\t\tconsole.error(chalk.yellow(`\\nOr create ${getModelsPath()}`));\n\t\tprocess.exit(1);\n\t}\n\n\t// Non-interactive mode: validate API key exists\n\tif (!isInteractive && initialModel) {\n\t\tconst apiKey = parsed.apiKey || (await getApiKeyForModel(initialModel));\n\t\tif (!apiKey) {\n\t\t\tconsole.error(chalk.red(`No API key found for ${initialModel.provider}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\t// Build system prompt\n\tconst skillsEnabled = !parsed.noSkills && settingsManager.getSkillsEnabled();\n\tconst systemPrompt = buildSystemPrompt({\n\t\tcustomPrompt: parsed.systemPrompt,\n\t\tselectedTools: parsed.tools,\n\t\tappendSystemPrompt: parsed.appendSystemPrompt,\n\t\tskillsEnabled,\n\t});\n\n\t// Handle session restoration\n\tlet modelFallbackMessage: string | null = null;\n\n\tif (parsed.continue || parsed.resume || parsed.session) {\n\t\tconst savedModel = sessionManager.loadModel();\n\t\tif (savedModel) {\n\t\t\tconst result = await restoreModelFromSession(\n\t\t\t\tsavedModel.provider,\n\t\t\t\tsavedModel.modelId,\n\t\t\t\tinitialModel,\n\t\t\t\tshouldPrintMessages,\n\t\t\t);\n\n\t\t\tif (result.model) {\n\t\t\t\tinitialModel = result.model;\n\t\t\t}\n\t\t\tmodelFallbackMessage = result.fallbackMessage;\n\t\t}\n\n\t\t// Load and restore thinking level\n\t\tconst thinkingLevel = sessionManager.loadThinkingLevel() as ThinkingLevel;\n\t\tif (thinkingLevel) {\n\t\t\tinitialThinking = thinkingLevel;\n\t\t\tif (shouldPrintMessages) {\n\t\t\t\tconsole.log(chalk.dim(`Restored thinking level: ${thinkingLevel}`));\n\t\t\t}\n\t\t}\n\t}\n\n\t// CLI --thinking flag takes highest priority\n\tif (parsed.thinking) {\n\t\tinitialThinking = parsed.thinking;\n\t}\n\n\t// Determine which tools to use\n\tlet selectedTools = parsed.tools ? parsed.tools.map((name) => allTools[name]) : codingTools;\n\n\t// Discover and load hooks from:\n\t// 1. ~/.pi/agent/hooks/*.ts (global)\n\t// 2. cwd/.pi/hooks/*.ts (project-local)\n\t// 3. Explicit paths in settings.json\n\t// 4. CLI --hook flags\n\tlet hookRunner: HookRunner | null = null;\n\tconst cwd = process.cwd();\n\tconst configuredHookPaths = [...settingsManager.getHookPaths(), ...(parsed.hooks ?? [])];\n\tconst { hooks, errors } = await discoverAndLoadHooks(configuredHookPaths, cwd);\n\n\t// Report hook loading errors\n\tfor (const { path, error } of errors) {\n\t\tconsole.error(chalk.red(`Failed to load hook \"${path}\": ${error}`));\n\t}\n\n\tif (hooks.length > 0) {\n\t\tconst timeout = settingsManager.getHookTimeout();\n\t\thookRunner = new HookRunner(hooks, cwd, timeout);\n\t}\n\n\t// Discover and load custom tools from:\n\t// 1. ~/.pi/agent/tools/*.ts (global)\n\t// 2. cwd/.pi/tools/*.ts (project-local)\n\t// 3. Explicit paths in settings.json\n\t// 4. CLI --tool flags\n\tconst configuredToolPaths = [...settingsManager.getCustomToolPaths(), ...(parsed.customTools ?? [])];\n\tconst builtInToolNames = Object.keys(allTools);\n\tconst {\n\t\ttools: loadedCustomTools,\n\t\terrors: toolErrors,\n\t\tsetUIContext: setToolUIContext,\n\t} = await discoverAndLoadCustomTools(configuredToolPaths, cwd, builtInToolNames);\n\n\t// Report custom tool loading errors\n\tfor (const { path, error } of toolErrors) {\n\t\tconsole.error(chalk.red(`Failed to load custom tool \"${path}\": ${error}`));\n\t}\n\n\t// Add custom tools to selected tools\n\tif (loadedCustomTools.length > 0) {\n\t\tconst customToolInstances = loadedCustomTools.map((lt) => lt.tool);\n\t\tselectedTools = [...selectedTools, ...customToolInstances] as typeof selectedTools;\n\t}\n\n\t// Wrap tools with hook callbacks (built-in and custom)\n\tif (hookRunner) {\n\t\tselectedTools = wrapToolsWithHooks(selectedTools, hookRunner);\n\t}\n\n\t// Create agent\n\tconst agent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt,\n\t\t\tmodel: initialModel as any, // Can be null in interactive mode\n\t\t\tthinkingLevel: initialThinking,\n\t\t\ttools: selectedTools,\n\t\t},\n\t\tmessageTransformer,\n\t\tqueueMode: settingsManager.getQueueMode(),\n\t\ttransport: new ProviderTransport({\n\t\t\tgetApiKey: async () => {\n\t\t\t\tconst currentModel = agent.state.model;\n\t\t\t\tif (!currentModel) {\n\t\t\t\t\tthrow new Error(\"No model selected\");\n\t\t\t\t}\n\n\t\t\t\tif (parsed.apiKey) {\n\t\t\t\t\treturn parsed.apiKey;\n\t\t\t\t}\n\n\t\t\t\tconst key = await getApiKeyForModel(currentModel);\n\t\t\t\tif (!key) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`No API key found for provider \"${currentModel.provider}\". Please set the appropriate environment variable or update ${getModelsPath()}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn key;\n\t\t\t},\n\t\t}),\n\t});\n\n\t// If initial thinking was requested but model doesn't support it, reset to off\n\tif (initialThinking !== \"off\" && initialModel && !initialModel.reasoning) {\n\t\tagent.setThinkingLevel(\"off\");\n\t}\n\n\t// Load previous messages if continuing, resuming, or using --session\n\tif (parsed.continue || parsed.resume || parsed.session) {\n\t\tconst messages = sessionManager.loadMessages();\n\t\tif (messages.length > 0) {\n\t\t\tagent.replaceMessages(messages);\n\t\t}\n\t}\n\n\t// Load file commands for slash command expansion\n\tconst fileCommands = loadSlashCommands();\n\n\t// Create session\n\tconst session = new AgentSession({\n\t\tagent,\n\t\tsessionManager,\n\t\tsettingsManager,\n\t\tscopedModels,\n\t\tfileCommands,\n\t\thookRunner,\n\t\tcustomTools: loadedCustomTools,\n\t});\n\n\t// Route to appropriate mode\n\tif (mode === \"rpc\") {\n\t\tawait runRpcMode(session);\n\t} else if (isInteractive) {\n\t\t// Check for new version in the background\n\t\tconst versionCheckPromise = checkForNewVersion(VERSION).catch(() => null);\n\n\t\t// Check if we should show changelog\n\t\tconst changelogMarkdown = getChangelogForDisplay(parsed, settingsManager);\n\n\t\t// Show model scope if provided\n\t\tif (scopedModels.length > 0) {\n\t\t\tconst modelList = scopedModels\n\t\t\t\t.map((sm) => {\n\t\t\t\t\tconst thinkingStr = sm.thinkingLevel !== \"off\" ? `:${sm.thinkingLevel}` : \"\";\n\t\t\t\t\treturn `${sm.model.id}${thinkingStr}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \");\n\t\t\tconsole.log(chalk.dim(`Model scope: ${modelList} ${chalk.gray(\"(Ctrl+P to cycle)\")}`));\n\t\t}\n\n\t\t// Ensure fd tool is available for file autocomplete\n\t\tconst fdPath = await ensureTool(\"fd\");\n\n\t\tawait runInteractiveMode(\n\t\t\tsession,\n\t\t\tVERSION,\n\t\t\tchangelogMarkdown,\n\t\t\tmodelFallbackMessage,\n\t\t\tversionCheckPromise,\n\t\t\tparsed.messages,\n\t\t\tloadedCustomTools,\n\t\t\tsetToolUIContext,\n\t\t\tinitialMessage,\n\t\t\tinitialAttachments,\n\t\t\tfdPath,\n\t\t);\n\t} else {\n\t\t// Non-interactive mode (--print flag or --mode flag)\n\t\tawait runPrintMode(session, mode, parsed.messages, initialMessage, initialAttachments);\n\t\t// Clean up and exit (file watchers keep process alive)\n\t\tstopThemeWatcher();\n\t\t// Wait for stdout to fully flush before exiting\n\t\tif (process.stdout.writableLength > 0) {\n\t\t\tawait new Promise<void>((resolve) => process.stdout.once(\"drain\", resolve));\n\t\t}\n\t\tprocess.exit(0);\n\t}\n}\n\n/** Find initial model based on CLI args, scoped models, settings, or available models */\nasync function findInitialModelForSession(parsed: Args, scopedModels: ScopedModel[], settingsManager: SettingsManager) {\n\t// 1. CLI args take priority\n\tif (parsed.provider && parsed.model) {\n\t\tconst { model, error } = findModel(parsed.provider, parsed.model);\n\t\tif (error) {\n\t\t\tconsole.error(chalk.red(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (!model) {\n\t\t\tconsole.error(chalk.red(`Model ${parsed.provider}/${parsed.model} not found`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\treturn model;\n\t}\n\n\t// 2. Use first model from scoped models (skip if continuing/resuming)\n\tif (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {\n\t\treturn scopedModels[0].model;\n\t}\n\n\t// 3. Try saved default from settings\n\tconst defaultProvider = settingsManager.getDefaultProvider();\n\tconst defaultModelId = settingsManager.getDefaultModel();\n\tif (defaultProvider && defaultModelId) {\n\t\tconst { model, error } = findModel(defaultProvider, defaultModelId);\n\t\tif (error) {\n\t\t\tconsole.error(chalk.red(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (model) {\n\t\t\treturn model;\n\t\t}\n\t}\n\n\t// 4. Try first available model with valid API key\n\tconst { models: availableModels, error } = await getAvailableModels();\n\n\tif (error) {\n\t\tconsole.error(chalk.red(error));\n\t\tprocess.exit(1);\n\t}\n\n\tif (availableModels.length > 0) {\n\t\treturn availableModels[0];\n\t}\n\n\treturn null;\n}\n\n/** Get changelog markdown to display (only for new sessions with updates) */\nfunction getChangelogForDisplay(parsed: Args, settingsManager: SettingsManager): string | null {\n\tif (parsed.continue || parsed.resume) {\n\t\treturn null;\n\t}\n\n\tconst lastVersion = settingsManager.getLastChangelogVersion();\n\tconst changelogPath = getChangelogPath();\n\tconst entries = parseChangelog(changelogPath);\n\n\tif (!lastVersion) {\n\t\t// First run - show all entries\n\t\tif (entries.length > 0) {\n\t\t\tsettingsManager.setLastChangelogVersion(VERSION);\n\t\t\treturn entries.map((e) => e.content).join(\"\\n\\n\");\n\t\t}\n\t} else {\n\t\t// Check for new entries since last version\n\t\tconst newEntries = getNewEntries(entries, lastVersion);\n\t\tif (newEntries.length > 0) {\n\t\t\tsettingsManager.setLastChangelogVersion(VERSION);\n\t\t\treturn newEntries.map((e) => e.content).join(\"\\n\\n\");\n\t\t}\n\t}\n\n\treturn null;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Main entry point for the coding agent\n */\n\nimport { Agent, type Attachment, ProviderTransport, type ThinkingLevel } from \"@mariozechner/pi-agent-core\";\nimport { setOAuthStorage, supportsXhigh } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\nimport { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname } from \"path\";\nimport { type Args, parseArgs, printHelp } from \"./cli/args.js\";\nimport { processFileArguments } from \"./cli/file-processor.js\";\nimport { listModels } from \"./cli/list-models.js\";\nimport { selectSession } from \"./cli/session-picker.js\";\nimport { getModelsPath, getOAuthPath, VERSION } from \"./config.js\";\nimport { AgentSession } from \"./core/agent-session.js\";\nimport { discoverAndLoadCustomTools, type LoadedCustomTool } from \"./core/custom-tools/index.js\";\nimport { exportFromFile } from \"./core/export-html.js\";\nimport { discoverAndLoadHooks, HookRunner, wrapToolsWithHooks } from \"./core/hooks/index.js\";\nimport { messageTransformer } from \"./core/messages.js\";\nimport { findModel, getApiKeyForModel, getAvailableModels } from \"./core/model-config.js\";\nimport { resolveModelScope, restoreModelFromSession, type ScopedModel } from \"./core/model-resolver.js\";\nimport { SessionManager } from \"./core/session-manager.js\";\nimport { SettingsManager } from \"./core/settings-manager.js\";\nimport { loadSlashCommands } from \"./core/slash-commands.js\";\nimport { buildSystemPrompt } from \"./core/system-prompt.js\";\nimport { allTools, codingTools } from \"./core/tools/index.js\";\nimport { InteractiveMode, runPrintMode, runRpcMode } from \"./modes/index.js\";\nimport { initTheme, stopThemeWatcher } from \"./modes/interactive/theme/theme.js\";\nimport { getChangelogPath, getNewEntries, parseChangelog } from \"./utils/changelog.js\";\nimport { ensureTool } from \"./utils/tools-manager.js\";\n\n/** Configure OAuth storage to use the coding-agent's configurable path */\nfunction configureOAuthStorage(): void {\n\tconst oauthPath = getOAuthPath();\n\n\tsetOAuthStorage({\n\t\tload: () => {\n\t\t\tif (!existsSync(oauthPath)) {\n\t\t\t\treturn {};\n\t\t\t}\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(readFileSync(oauthPath, \"utf-8\"));\n\t\t\t} catch {\n\t\t\t\treturn {};\n\t\t\t}\n\t\t},\n\t\tsave: (storage) => {\n\t\t\tconst dir = dirname(oauthPath);\n\t\t\tif (!existsSync(dir)) {\n\t\t\t\tmkdirSync(dir, { recursive: true, mode: 0o700 });\n\t\t\t}\n\t\t\twriteFileSync(oauthPath, JSON.stringify(storage, null, 2), \"utf-8\");\n\t\t\tchmodSync(oauthPath, 0o600);\n\t\t},\n\t});\n}\n\n/** Check npm registry for new version (non-blocking) */\nasync function checkForNewVersion(currentVersion: string): Promise<string | null> {\n\ttry {\n\t\tconst response = await fetch(\"https://registry.npmjs.org/@mariozechner/pi-coding-agent/latest\");\n\t\tif (!response.ok) return null;\n\n\t\tconst data = (await response.json()) as { version?: string };\n\t\tconst latestVersion = data.version;\n\n\t\tif (latestVersion && latestVersion !== currentVersion) {\n\t\t\treturn latestVersion;\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\t// Silently fail - don't disrupt the user experience\n\t\treturn null;\n\t}\n}\n\n/** Run interactive mode with TUI */\nasync function runInteractiveMode(\n\tsession: AgentSession,\n\tversion: string,\n\tchangelogMarkdown: string | null,\n\tmodelFallbackMessage: string | null,\n\tversionCheckPromise: Promise<string | null>,\n\tinitialMessages: string[],\n\tcustomTools: LoadedCustomTool[],\n\tsetToolUIContext: (uiContext: import(\"./core/hooks/types.js\").HookUIContext, hasUI: boolean) => void,\n\tinitialMessage?: string,\n\tinitialAttachments?: Attachment[],\n\tfdPath: string | null = null,\n): Promise<void> {\n\tconst mode = new InteractiveMode(session, version, changelogMarkdown, customTools, setToolUIContext, fdPath);\n\n\t// Initialize TUI (subscribes to agent events internally)\n\tawait mode.init();\n\n\t// Handle version check result when it completes (don't block)\n\tversionCheckPromise.then((newVersion) => {\n\t\tif (newVersion) {\n\t\t\tmode.showNewVersionNotification(newVersion);\n\t\t}\n\t});\n\n\t// Render any existing messages (from --continue mode)\n\tmode.renderInitialMessages(session.state);\n\n\t// Show model fallback warning at the end of the chat if applicable\n\tif (modelFallbackMessage) {\n\t\tmode.showWarning(modelFallbackMessage);\n\t}\n\n\t// Process initial message with attachments if provided (from @file args)\n\tif (initialMessage) {\n\t\ttry {\n\t\t\tawait session.prompt(initialMessage, { attachments: initialAttachments });\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n\n\t// Process remaining initial messages if provided (from CLI args)\n\tfor (const message of initialMessages) {\n\t\ttry {\n\t\t\tawait session.prompt(message);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n\n\t// Interactive loop\n\twhile (true) {\n\t\tconst userInput = await mode.getUserInput();\n\n\t\t// Process the message\n\t\ttry {\n\t\t\tawait session.prompt(userInput);\n\t\t} catch (error: unknown) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : \"Unknown error occurred\";\n\t\t\tmode.showError(errorMessage);\n\t\t}\n\t}\n}\n\n/** Prepare initial message from @file arguments */\nasync function prepareInitialMessage(parsed: Args): Promise<{\n\tinitialMessage?: string;\n\tinitialAttachments?: Attachment[];\n}> {\n\tif (parsed.fileArgs.length === 0) {\n\t\treturn {};\n\t}\n\n\tconst { textContent, imageAttachments } = await processFileArguments(parsed.fileArgs);\n\n\t// Combine file content with first plain text message (if any)\n\tlet initialMessage: string;\n\tif (parsed.messages.length > 0) {\n\t\tinitialMessage = textContent + parsed.messages[0];\n\t\tparsed.messages.shift(); // Remove first message as it's been combined\n\t} else {\n\t\tinitialMessage = textContent;\n\t}\n\n\treturn {\n\t\tinitialMessage,\n\t\tinitialAttachments: imageAttachments.length > 0 ? imageAttachments : undefined,\n\t};\n}\n\nexport async function main(args: string[]) {\n\t// Configure OAuth storage to use the coding-agent's configurable path\n\t// This must happen before any OAuth operations\n\tconfigureOAuthStorage();\n\n\tconst parsed = parseArgs(args);\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\treturn;\n\t}\n\n\tif (parsed.help) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\t// Handle --list-models flag: list available models and exit\n\tif (parsed.listModels !== undefined) {\n\t\tconst searchPattern = typeof parsed.listModels === \"string\" ? parsed.listModels : undefined;\n\t\tawait listModels(searchPattern);\n\t\treturn;\n\t}\n\n\t// Handle --export flag: convert session file to HTML and exit\n\tif (parsed.export) {\n\t\ttry {\n\t\t\tconst outputPath = parsed.messages.length > 0 ? parsed.messages[0] : undefined;\n\t\t\tconst result = exportFromFile(parsed.export, outputPath);\n\t\t\tconsole.log(`Exported to: ${result}`);\n\t\t\treturn;\n\t\t} catch (error: unknown) {\n\t\t\tconst message = error instanceof Error ? error.message : \"Failed to export session\";\n\t\t\tconsole.error(chalk.red(`Error: ${message}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\t// Validate: RPC mode doesn't support @file arguments\n\tif (parsed.mode === \"rpc\" && parsed.fileArgs.length > 0) {\n\t\tconsole.error(chalk.red(\"Error: @file arguments are not supported in RPC mode\"));\n\t\tprocess.exit(1);\n\t}\n\n\t// Process @file arguments\n\tconst { initialMessage, initialAttachments } = await prepareInitialMessage(parsed);\n\n\t// Determine if we're in interactive mode (needed for theme watcher)\n\tconst isInteractive = !parsed.print && parsed.mode === undefined;\n\n\t// Initialize theme (before any TUI rendering)\n\tconst settingsManager = new SettingsManager();\n\tconst themeName = settingsManager.getTheme();\n\tinitTheme(themeName, isInteractive);\n\n\t// Setup session manager\n\tconst sessionManager = new SessionManager(parsed.continue && !parsed.resume, parsed.session);\n\n\tif (parsed.noSession) {\n\t\tsessionManager.disable();\n\t}\n\n\t// Handle --resume flag: show session selector\n\tif (parsed.resume) {\n\t\tconst selectedSession = await selectSession(sessionManager);\n\t\tif (!selectedSession) {\n\t\t\tconsole.log(chalk.dim(\"No session selected\"));\n\t\t\treturn;\n\t\t}\n\t\tsessionManager.setSessionFile(selectedSession);\n\t}\n\n\t// Resolve model scope early if provided\n\tlet scopedModels: ScopedModel[] = [];\n\tif (parsed.models && parsed.models.length > 0) {\n\t\tscopedModels = await resolveModelScope(parsed.models);\n\t}\n\n\t// Determine mode and output behavior\n\tconst mode = parsed.mode || \"text\";\n\tconst shouldPrintMessages = isInteractive;\n\n\t// Find initial model\n\tlet initialModel = await findInitialModelForSession(parsed, scopedModels, settingsManager);\n\tlet initialThinking: ThinkingLevel = \"off\";\n\n\t// Get thinking level from scoped models if applicable\n\tif (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {\n\t\tinitialThinking = scopedModels[0].thinkingLevel;\n\t} else {\n\t\t// Try saved thinking level\n\t\tconst savedThinking = settingsManager.getDefaultThinkingLevel();\n\t\tif (savedThinking) {\n\t\t\tinitialThinking = savedThinking;\n\t\t}\n\t}\n\n\t// Non-interactive mode: fail early if no model available\n\tif (!isInteractive && !initialModel) {\n\t\tconsole.error(chalk.red(\"No models available.\"));\n\t\tconsole.error(chalk.yellow(\"\\nSet an API key environment variable:\"));\n\t\tconsole.error(\" ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY, etc.\");\n\t\tconsole.error(chalk.yellow(`\\nOr create ${getModelsPath()}`));\n\t\tprocess.exit(1);\n\t}\n\n\t// Non-interactive mode: validate API key exists\n\tif (!isInteractive && initialModel) {\n\t\tconst apiKey = parsed.apiKey || (await getApiKeyForModel(initialModel));\n\t\tif (!apiKey) {\n\t\t\tconsole.error(chalk.red(`No API key found for ${initialModel.provider}`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\t// Build system prompt\n\tconst skillsEnabled = !parsed.noSkills && settingsManager.getSkillsEnabled();\n\tconst systemPrompt = buildSystemPrompt({\n\t\tcustomPrompt: parsed.systemPrompt,\n\t\tselectedTools: parsed.tools,\n\t\tappendSystemPrompt: parsed.appendSystemPrompt,\n\t\tskillsEnabled,\n\t});\n\n\t// Handle session restoration\n\tlet modelFallbackMessage: string | null = null;\n\n\tif (parsed.continue || parsed.resume || parsed.session) {\n\t\tconst savedModel = sessionManager.loadModel();\n\t\tif (savedModel) {\n\t\t\tconst result = await restoreModelFromSession(\n\t\t\t\tsavedModel.provider,\n\t\t\t\tsavedModel.modelId,\n\t\t\t\tinitialModel,\n\t\t\t\tshouldPrintMessages,\n\t\t\t);\n\n\t\t\tif (result.model) {\n\t\t\t\tinitialModel = result.model;\n\t\t\t}\n\t\t\tmodelFallbackMessage = result.fallbackMessage;\n\t\t}\n\n\t\t// Load and restore thinking level\n\t\tconst thinkingLevel = sessionManager.loadThinkingLevel() as ThinkingLevel;\n\t\tif (thinkingLevel) {\n\t\t\tinitialThinking = thinkingLevel;\n\t\t\tif (shouldPrintMessages) {\n\t\t\t\tconsole.log(chalk.dim(`Restored thinking level: ${thinkingLevel}`));\n\t\t\t}\n\t\t}\n\t}\n\n\t// CLI --thinking flag takes highest priority\n\tif (parsed.thinking) {\n\t\tinitialThinking = parsed.thinking;\n\t}\n\n\t// Clamp thinking level to model capabilities\n\tif (initialModel) {\n\t\tif (!initialModel.reasoning) {\n\t\t\tinitialThinking = \"off\";\n\t\t} else if (initialThinking === \"xhigh\" && !supportsXhigh(initialModel)) {\n\t\t\tinitialThinking = \"high\";\n\t\t}\n\t}\n\n\t// Determine which tools to use\n\tlet selectedTools = parsed.tools ? parsed.tools.map((name) => allTools[name]) : codingTools;\n\n\t// Discover and load hooks from:\n\t// 1. ~/.pi/agent/hooks/*.ts (global)\n\t// 2. cwd/.pi/hooks/*.ts (project-local)\n\t// 3. Explicit paths in settings.json\n\t// 4. CLI --hook flags\n\tlet hookRunner: HookRunner | null = null;\n\tconst cwd = process.cwd();\n\tconst configuredHookPaths = [...settingsManager.getHookPaths(), ...(parsed.hooks ?? [])];\n\tconst { hooks, errors } = await discoverAndLoadHooks(configuredHookPaths, cwd);\n\n\t// Report hook loading errors\n\tfor (const { path, error } of errors) {\n\t\tconsole.error(chalk.red(`Failed to load hook \"${path}\": ${error}`));\n\t}\n\n\tif (hooks.length > 0) {\n\t\tconst timeout = settingsManager.getHookTimeout();\n\t\thookRunner = new HookRunner(hooks, cwd, timeout);\n\t}\n\n\t// Discover and load custom tools from:\n\t// 1. ~/.pi/agent/tools/*.ts (global)\n\t// 2. cwd/.pi/tools/*.ts (project-local)\n\t// 3. Explicit paths in settings.json\n\t// 4. CLI --tool flags\n\tconst configuredToolPaths = [...settingsManager.getCustomToolPaths(), ...(parsed.customTools ?? [])];\n\tconst builtInToolNames = Object.keys(allTools);\n\tconst {\n\t\ttools: loadedCustomTools,\n\t\terrors: toolErrors,\n\t\tsetUIContext: setToolUIContext,\n\t} = await discoverAndLoadCustomTools(configuredToolPaths, cwd, builtInToolNames);\n\n\t// Report custom tool loading errors\n\tfor (const { path, error } of toolErrors) {\n\t\tconsole.error(chalk.red(`Failed to load custom tool \"${path}\": ${error}`));\n\t}\n\n\t// Add custom tools to selected tools\n\tif (loadedCustomTools.length > 0) {\n\t\tconst customToolInstances = loadedCustomTools.map((lt) => lt.tool);\n\t\tselectedTools = [...selectedTools, ...customToolInstances] as typeof selectedTools;\n\t}\n\n\t// Wrap tools with hook callbacks (built-in and custom)\n\tif (hookRunner) {\n\t\tselectedTools = wrapToolsWithHooks(selectedTools, hookRunner);\n\t}\n\n\t// Create agent\n\tconst agent = new Agent({\n\t\tinitialState: {\n\t\t\tsystemPrompt,\n\t\t\tmodel: initialModel as any, // Can be null in interactive mode\n\t\t\tthinkingLevel: initialThinking,\n\t\t\ttools: selectedTools,\n\t\t},\n\t\tmessageTransformer,\n\t\tqueueMode: settingsManager.getQueueMode(),\n\t\ttransport: new ProviderTransport({\n\t\t\tgetApiKey: async () => {\n\t\t\t\tconst currentModel = agent.state.model;\n\t\t\t\tif (!currentModel) {\n\t\t\t\t\tthrow new Error(\"No model selected\");\n\t\t\t\t}\n\n\t\t\t\tif (parsed.apiKey) {\n\t\t\t\t\treturn parsed.apiKey;\n\t\t\t\t}\n\n\t\t\t\tconst key = await getApiKeyForModel(currentModel);\n\t\t\t\tif (!key) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`No API key found for provider \"${currentModel.provider}\". Please set the appropriate environment variable or update ${getModelsPath()}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn key;\n\t\t\t},\n\t\t}),\n\t});\n\n\t// Load previous messages if continuing, resuming, or using --session\n\tif (parsed.continue || parsed.resume || parsed.session) {\n\t\tconst messages = sessionManager.loadMessages();\n\t\tif (messages.length > 0) {\n\t\t\tagent.replaceMessages(messages);\n\t\t}\n\t}\n\n\t// Load file commands for slash command expansion\n\tconst fileCommands = loadSlashCommands();\n\n\t// Create session\n\tconst session = new AgentSession({\n\t\tagent,\n\t\tsessionManager,\n\t\tsettingsManager,\n\t\tscopedModels,\n\t\tfileCommands,\n\t\thookRunner,\n\t\tcustomTools: loadedCustomTools,\n\t});\n\n\t// Route to appropriate mode\n\tif (mode === \"rpc\") {\n\t\tawait runRpcMode(session);\n\t} else if (isInteractive) {\n\t\t// Check for new version in the background\n\t\tconst versionCheckPromise = checkForNewVersion(VERSION).catch(() => null);\n\n\t\t// Check if we should show changelog\n\t\tconst changelogMarkdown = getChangelogForDisplay(parsed, settingsManager);\n\n\t\t// Show model scope if provided\n\t\tif (scopedModels.length > 0) {\n\t\t\tconst modelList = scopedModels\n\t\t\t\t.map((sm) => {\n\t\t\t\t\tconst thinkingStr = sm.thinkingLevel !== \"off\" ? `:${sm.thinkingLevel}` : \"\";\n\t\t\t\t\treturn `${sm.model.id}${thinkingStr}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \");\n\t\t\tconsole.log(chalk.dim(`Model scope: ${modelList} ${chalk.gray(\"(Ctrl+P to cycle)\")}`));\n\t\t}\n\n\t\t// Ensure fd tool is available for file autocomplete\n\t\tconst fdPath = await ensureTool(\"fd\");\n\n\t\tawait runInteractiveMode(\n\t\t\tsession,\n\t\t\tVERSION,\n\t\t\tchangelogMarkdown,\n\t\t\tmodelFallbackMessage,\n\t\t\tversionCheckPromise,\n\t\t\tparsed.messages,\n\t\t\tloadedCustomTools,\n\t\t\tsetToolUIContext,\n\t\t\tinitialMessage,\n\t\t\tinitialAttachments,\n\t\t\tfdPath,\n\t\t);\n\t} else {\n\t\t// Non-interactive mode (--print flag or --mode flag)\n\t\tawait runPrintMode(session, mode, parsed.messages, initialMessage, initialAttachments);\n\t\t// Clean up and exit (file watchers keep process alive)\n\t\tstopThemeWatcher();\n\t\t// Wait for stdout to fully flush before exiting\n\t\tif (process.stdout.writableLength > 0) {\n\t\t\tawait new Promise<void>((resolve) => process.stdout.once(\"drain\", resolve));\n\t\t}\n\t\tprocess.exit(0);\n\t}\n}\n\n/** Find initial model based on CLI args, scoped models, settings, or available models */\nasync function findInitialModelForSession(parsed: Args, scopedModels: ScopedModel[], settingsManager: SettingsManager) {\n\t// 1. CLI args take priority\n\tif (parsed.provider && parsed.model) {\n\t\tconst { model, error } = findModel(parsed.provider, parsed.model);\n\t\tif (error) {\n\t\t\tconsole.error(chalk.red(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (!model) {\n\t\t\tconsole.error(chalk.red(`Model ${parsed.provider}/${parsed.model} not found`));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\treturn model;\n\t}\n\n\t// 2. Use first model from scoped models (skip if continuing/resuming)\n\tif (scopedModels.length > 0 && !parsed.continue && !parsed.resume) {\n\t\treturn scopedModels[0].model;\n\t}\n\n\t// 3. Try saved default from settings\n\tconst defaultProvider = settingsManager.getDefaultProvider();\n\tconst defaultModelId = settingsManager.getDefaultModel();\n\tif (defaultProvider && defaultModelId) {\n\t\tconst { model, error } = findModel(defaultProvider, defaultModelId);\n\t\tif (error) {\n\t\t\tconsole.error(chalk.red(error));\n\t\t\tprocess.exit(1);\n\t\t}\n\t\tif (model) {\n\t\t\treturn model;\n\t\t}\n\t}\n\n\t// 4. Try first available model with valid API key\n\tconst { models: availableModels, error } = await getAvailableModels();\n\n\tif (error) {\n\t\tconsole.error(chalk.red(error));\n\t\tprocess.exit(1);\n\t}\n\n\tif (availableModels.length > 0) {\n\t\treturn availableModels[0];\n\t}\n\n\treturn null;\n}\n\n/** Get changelog markdown to display (only for new sessions with updates) */\nfunction getChangelogForDisplay(parsed: Args, settingsManager: SettingsManager): string | null {\n\tif (parsed.continue || parsed.resume) {\n\t\treturn null;\n\t}\n\n\tconst lastVersion = settingsManager.getLastChangelogVersion();\n\tconst changelogPath = getChangelogPath();\n\tconst entries = parseChangelog(changelogPath);\n\n\tif (!lastVersion) {\n\t\t// First run - show all entries\n\t\tif (entries.length > 0) {\n\t\t\tsettingsManager.setLastChangelogVersion(VERSION);\n\t\t\treturn entries.map((e) => e.content).join(\"\\n\\n\");\n\t\t}\n\t} else {\n\t\t// Check for new entries since last version\n\t\tconst newEntries = getNewEntries(entries, lastVersion);\n\t\tif (newEntries.length > 0) {\n\t\t\tsettingsManager.setLastChangelogVersion(VERSION);\n\t\t\treturn newEntries.map((e) => e.content).join(\"\\n\\n\");\n\t\t}\n\t}\n\n\treturn null;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,sBAAsB,CAAC;AA0BpE;;GAEG;AACH,qBAAa,eAAgB,YAAW,SAAS;IAChD,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,YAAY,CAAwC;IAC5D,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,kBAAkB,CAAiB;IAE3C,YAAY,KAAK,EAAE,UAAU,EAE5B;IAED,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE5C;IAED;;;OAGG;IACH,WAAW,CAAC,cAAc,EAAE,MAAM,IAAI,GAAG,IAAI,CAG5C;IAED,OAAO,CAAC,eAAe;IAwBvB;;OAEG;IACH,OAAO,IAAI,IAAI,CAKd;IAED,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAEnC;IAED,UAAU,IAAI,IAAI,CAGjB;IAED;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA6BxB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAqJ9B;CACD","sourcesContent":["import type { AgentState } from \"@mariozechner/pi-agent-core\";\nimport type { AssistantMessage } from \"@mariozechner/pi-ai\";\nimport { type Component, visibleWidth } from \"@mariozechner/pi-tui\";\nimport { existsSync, type FSWatcher, readFileSync, watch } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { isModelUsingOAuth } from \"../../../core/model-config.js\";\nimport { theme } from \"../theme/theme.js\";\n\n/**\n * Find the git root directory by walking up from cwd.\n * Returns the path to .git/HEAD if found, null otherwise.\n */\nfunction findGitHeadPath(): string | null {\n\tlet dir = process.cwd();\n\twhile (true) {\n\t\tconst gitHeadPath = join(dir, \".git\", \"HEAD\");\n\t\tif (existsSync(gitHeadPath)) {\n\t\t\treturn gitHeadPath;\n\t\t}\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) {\n\t\t\t// Reached filesystem root\n\t\t\treturn null;\n\t\t}\n\t\tdir = parent;\n\t}\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage\n */\nexport class FooterComponent implements Component {\n\tprivate state: AgentState;\n\tprivate cachedBranch: string | null | undefined = undefined; // undefined = not checked yet, null = not in git repo, string = branch name\n\tprivate gitWatcher: FSWatcher | null = null;\n\tprivate onBranchChange: (() => void) | null = null;\n\tprivate autoCompactEnabled: boolean = true;\n\n\tconstructor(state: AgentState) {\n\t\tthis.state = state;\n\t}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\t/**\n\t * Set up a file watcher on .git/HEAD to detect branch changes.\n\t * Call the provided callback when branch changes.\n\t */\n\twatchBranch(onBranchChange: () => void): void {\n\t\tthis.onBranchChange = onBranchChange;\n\t\tthis.setupGitWatcher();\n\t}\n\n\tprivate setupGitWatcher(): void {\n\t\t// Clean up existing watcher\n\t\tif (this.gitWatcher) {\n\t\t\tthis.gitWatcher.close();\n\t\t\tthis.gitWatcher = null;\n\t\t}\n\n\t\tconst gitHeadPath = findGitHeadPath();\n\t\tif (!gitHeadPath) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.gitWatcher = watch(gitHeadPath, () => {\n\t\t\t\tthis.cachedBranch = undefined; // Invalidate cache\n\t\t\t\tif (this.onBranchChange) {\n\t\t\t\t\tthis.onBranchChange();\n\t\t\t\t}\n\t\t\t});\n\t\t} catch {\n\t\t\t// Silently fail if we can't watch\n\t\t}\n\t}\n\n\t/**\n\t * Clean up the file watcher\n\t */\n\tdispose(): void {\n\t\tif (this.gitWatcher) {\n\t\t\tthis.gitWatcher.close();\n\t\t\tthis.gitWatcher = null;\n\t\t}\n\t}\n\n\tupdateState(state: AgentState): void {\n\t\tthis.state = state;\n\t}\n\n\tinvalidate(): void {\n\t\t// Invalidate cached branch so it gets re-read on next render\n\t\tthis.cachedBranch = undefined;\n\t}\n\n\t/**\n\t * Get current git branch by reading .git/HEAD directly.\n\t * Returns null if not in a git repo, branch name otherwise.\n\t */\n\tprivate getCurrentBranch(): string | null {\n\t\t// Return cached value if available\n\t\tif (this.cachedBranch !== undefined) {\n\t\t\treturn this.cachedBranch;\n\t\t}\n\n\t\ttry {\n\t\t\tconst gitHeadPath = findGitHeadPath();\n\t\t\tif (!gitHeadPath) {\n\t\t\t\tthis.cachedBranch = null;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst content = readFileSync(gitHeadPath, \"utf8\").trim();\n\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\t// Normal branch: extract branch name\n\t\t\t\tthis.cachedBranch = content.slice(16);\n\t\t\t} else {\n\t\t\t\t// Detached HEAD state\n\t\t\t\tthis.cachedBranch = \"detached\";\n\t\t\t}\n\t\t} catch {\n\t\t\t// Not in a git repo or error reading file\n\t\t\tthis.cachedBranch = null;\n\t\t}\n\n\t\treturn this.cachedBranch;\n\t}\n\n\trender(width: number): string[] {\n\t\t// Calculate cumulative usage from all assistant messages\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\n\t\tfor (const message of this.state.messages) {\n\t\t\tif (message.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = message as AssistantMessage;\n\t\t\t\ttotalInput += assistantMsg.usage.input;\n\t\t\t\ttotalOutput += assistantMsg.usage.output;\n\t\t\t\ttotalCacheRead += assistantMsg.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += assistantMsg.usage.cacheWrite;\n\t\t\t\ttotalCost += assistantMsg.usage.cost.total;\n\t\t\t}\n\t\t}\n\n\t\t// Get last assistant message for context percentage calculation (skip aborted messages)\n\t\tconst lastAssistantMessage = this.state.messages\n\t\t\t.slice()\n\t\t\t.reverse()\n\t\t\t.find((m) => m.role === \"assistant\" && m.stopReason !== \"aborted\") as AssistantMessage | undefined;\n\n\t\t// Calculate context percentage from last message (input + output + cacheRead + cacheWrite)\n\t\tconst contextTokens = lastAssistantMessage\n\t\t\t? lastAssistantMessage.usage.input +\n\t\t\t\tlastAssistantMessage.usage.output +\n\t\t\t\tlastAssistantMessage.usage.cacheRead +\n\t\t\t\tlastAssistantMessage.usage.cacheWrite\n\t\t\t: 0;\n\t\tconst contextWindow = this.state.model?.contextWindow || 0;\n\t\tconst contextPercentValue = contextWindow > 0 ? (contextTokens / contextWindow) * 100 : 0;\n\t\tconst contextPercent = contextPercentValue.toFixed(1);\n\n\t\t// Format token counts (similar to web-ui)\n\t\tconst formatTokens = (count: number): string => {\n\t\t\tif (count < 1000) return count.toString();\n\t\t\tif (count < 10000) return (count / 1000).toFixed(1) + \"k\";\n\t\t\tif (count < 1000000) return Math.round(count / 1000) + \"k\";\n\t\t\tif (count < 10000000) return (count / 1000000).toFixed(1) + \"M\";\n\t\t\treturn Math.round(count / 1000000) + \"M\";\n\t\t};\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = process.cwd();\n\t\tconst home = process.env.HOME || process.env.USERPROFILE;\n\t\tif (home && pwd.startsWith(home)) {\n\t\t\tpwd = \"~\" + pwd.slice(home.length);\n\t\t}\n\n\t\t// Add git branch if available\n\t\tconst branch = this.getCurrentBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Truncate path if too long to fit width\n\t\tif (pwd.length > width) {\n\t\t\tconst half = Math.floor(width / 2) - 2;\n\t\t\tif (half > 0) {\n\t\t\t\tconst start = pwd.slice(0, half);\n\t\t\t\tconst end = pwd.slice(-(half - 1));\n\t\t\t\tpwd = `${start}...${end}`;\n\t\t\t} else {\n\t\t\t\tpwd = pwd.slice(0, Math.max(1, width));\n\t\t\t}\n\t\t}\n\n\t\t// Build stats line\n\t\tconst statsParts = [];\n\t\tif (totalInput) statsParts.push(`↑${formatTokens(totalInput)}`);\n\t\tif (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);\n\t\tif (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);\n\t\tif (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);\n\n\t\t// Show cost with \"(sub)\" indicator if using OAuth subscription\n\t\tconst usingSubscription = this.state.model ? isModelUsingOAuth(this.state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tconst costStr = `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tstatsParts.push(costStr);\n\t\t}\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay = `${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = contextPercentDisplay;\n\t\t}\n\t\tstatsParts.push(contextPercentStr);\n\n\t\tlet statsLeft = statsParts.join(\" \");\n\n\t\t// Add model name on the right side, plus thinking level if model supports it\n\t\tconst modelName = this.state.model?.id || \"no-model\";\n\n\t\t// Add thinking level hint if model supports reasoning and thinking is enabled\n\t\tlet rightSide = modelName;\n\t\tif (this.state.model?.reasoning) {\n\t\t\tconst thinkingLevel = this.state.thinkingLevel || \"off\";\n\t\t\tif (thinkingLevel !== \"off\") {\n\t\t\t\trightSide = `${modelName} • ${thinkingLevel}`;\n\t\t\t}\n\t\t}\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\t// Truncate statsLeft to fit width (no room for right side)\n\t\t\tconst plainStatsLeft = statsLeft.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n\t\t\tstatsLeft = plainStatsLeft.substring(0, width - 3) + \"...\";\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 3) {\n\t\t\t\t// Truncate to fit (strip ANSI codes for length calculation, then truncate raw string)\n\t\t\t\tconst plainRightSide = rightSide.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n\t\t\t\tconst truncatedPlain = plainRightSide.substring(0, availableForRight);\n\t\t\t\t// For simplicity, just use plain truncated version (loses color, but fits)\n\t\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - truncatedPlain.length);\n\t\t\t\tstatsLine = statsLeft + padding + truncatedPlain;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Return two lines: pwd and stats\n\t\treturn [theme.fg(\"dim\", pwd), theme.fg(\"dim\", statsLine)];\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAAE,KAAK,SAAS,EAAgB,MAAM,sBAAsB,CAAC;AA0BpE;;GAEG;AACH,qBAAa,eAAgB,YAAW,SAAS;IAChD,OAAO,CAAC,KAAK,CAAa;IAC1B,OAAO,CAAC,YAAY,CAAwC;IAC5D,OAAO,CAAC,UAAU,CAA0B;IAC5C,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,kBAAkB,CAAiB;IAE3C,YAAY,KAAK,EAAE,UAAU,EAE5B;IAED,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE5C;IAED;;;OAGG;IACH,WAAW,CAAC,cAAc,EAAE,MAAM,IAAI,GAAG,IAAI,CAG5C;IAED,OAAO,CAAC,eAAe;IAwBvB;;OAEG;IACH,OAAO,IAAI,IAAI,CAKd;IAED,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAEnC;IAED,UAAU,IAAI,IAAI,CAGjB;IAED;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA6BxB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA2J9B;CACD","sourcesContent":["import type { AgentState } from \"@mariozechner/pi-agent-core\";\nimport type { AssistantMessage } from \"@mariozechner/pi-ai\";\nimport { type Component, visibleWidth } from \"@mariozechner/pi-tui\";\nimport { existsSync, type FSWatcher, readFileSync, watch } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { isModelUsingOAuth } from \"../../../core/model-config.js\";\nimport { theme } from \"../theme/theme.js\";\n\n/**\n * Find the git root directory by walking up from cwd.\n * Returns the path to .git/HEAD if found, null otherwise.\n */\nfunction findGitHeadPath(): string | null {\n\tlet dir = process.cwd();\n\twhile (true) {\n\t\tconst gitHeadPath = join(dir, \".git\", \"HEAD\");\n\t\tif (existsSync(gitHeadPath)) {\n\t\t\treturn gitHeadPath;\n\t\t}\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) {\n\t\t\t// Reached filesystem root\n\t\t\treturn null;\n\t\t}\n\t\tdir = parent;\n\t}\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage\n */\nexport class FooterComponent implements Component {\n\tprivate state: AgentState;\n\tprivate cachedBranch: string | null | undefined = undefined; // undefined = not checked yet, null = not in git repo, string = branch name\n\tprivate gitWatcher: FSWatcher | null = null;\n\tprivate onBranchChange: (() => void) | null = null;\n\tprivate autoCompactEnabled: boolean = true;\n\n\tconstructor(state: AgentState) {\n\t\tthis.state = state;\n\t}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\t/**\n\t * Set up a file watcher on .git/HEAD to detect branch changes.\n\t * Call the provided callback when branch changes.\n\t */\n\twatchBranch(onBranchChange: () => void): void {\n\t\tthis.onBranchChange = onBranchChange;\n\t\tthis.setupGitWatcher();\n\t}\n\n\tprivate setupGitWatcher(): void {\n\t\t// Clean up existing watcher\n\t\tif (this.gitWatcher) {\n\t\t\tthis.gitWatcher.close();\n\t\t\tthis.gitWatcher = null;\n\t\t}\n\n\t\tconst gitHeadPath = findGitHeadPath();\n\t\tif (!gitHeadPath) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.gitWatcher = watch(gitHeadPath, () => {\n\t\t\t\tthis.cachedBranch = undefined; // Invalidate cache\n\t\t\t\tif (this.onBranchChange) {\n\t\t\t\t\tthis.onBranchChange();\n\t\t\t\t}\n\t\t\t});\n\t\t} catch {\n\t\t\t// Silently fail if we can't watch\n\t\t}\n\t}\n\n\t/**\n\t * Clean up the file watcher\n\t */\n\tdispose(): void {\n\t\tif (this.gitWatcher) {\n\t\t\tthis.gitWatcher.close();\n\t\t\tthis.gitWatcher = null;\n\t\t}\n\t}\n\n\tupdateState(state: AgentState): void {\n\t\tthis.state = state;\n\t}\n\n\tinvalidate(): void {\n\t\t// Invalidate cached branch so it gets re-read on next render\n\t\tthis.cachedBranch = undefined;\n\t}\n\n\t/**\n\t * Get current git branch by reading .git/HEAD directly.\n\t * Returns null if not in a git repo, branch name otherwise.\n\t */\n\tprivate getCurrentBranch(): string | null {\n\t\t// Return cached value if available\n\t\tif (this.cachedBranch !== undefined) {\n\t\t\treturn this.cachedBranch;\n\t\t}\n\n\t\ttry {\n\t\t\tconst gitHeadPath = findGitHeadPath();\n\t\t\tif (!gitHeadPath) {\n\t\t\t\tthis.cachedBranch = null;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst content = readFileSync(gitHeadPath, \"utf8\").trim();\n\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\t// Normal branch: extract branch name\n\t\t\t\tthis.cachedBranch = content.slice(16);\n\t\t\t} else {\n\t\t\t\t// Detached HEAD state\n\t\t\t\tthis.cachedBranch = \"detached\";\n\t\t\t}\n\t\t} catch {\n\t\t\t// Not in a git repo or error reading file\n\t\t\tthis.cachedBranch = null;\n\t\t}\n\n\t\treturn this.cachedBranch;\n\t}\n\n\trender(width: number): string[] {\n\t\t// Calculate cumulative usage from all assistant messages\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\n\t\tfor (const message of this.state.messages) {\n\t\t\tif (message.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = message as AssistantMessage;\n\t\t\t\ttotalInput += assistantMsg.usage.input;\n\t\t\t\ttotalOutput += assistantMsg.usage.output;\n\t\t\t\ttotalCacheRead += assistantMsg.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += assistantMsg.usage.cacheWrite;\n\t\t\t\ttotalCost += assistantMsg.usage.cost.total;\n\t\t\t}\n\t\t}\n\n\t\t// Get last assistant message for context percentage calculation (skip aborted messages)\n\t\tconst lastAssistantMessage = this.state.messages\n\t\t\t.slice()\n\t\t\t.reverse()\n\t\t\t.find((m) => m.role === \"assistant\" && m.stopReason !== \"aborted\") as AssistantMessage | undefined;\n\n\t\t// Calculate context percentage from last message (input + output + cacheRead + cacheWrite)\n\t\tconst contextTokens = lastAssistantMessage\n\t\t\t? lastAssistantMessage.usage.input +\n\t\t\t\tlastAssistantMessage.usage.output +\n\t\t\t\tlastAssistantMessage.usage.cacheRead +\n\t\t\t\tlastAssistantMessage.usage.cacheWrite\n\t\t\t: 0;\n\t\tconst contextWindow = this.state.model?.contextWindow || 0;\n\t\tconst contextPercentValue = contextWindow > 0 ? (contextTokens / contextWindow) * 100 : 0;\n\t\tconst contextPercent = contextPercentValue.toFixed(1);\n\n\t\t// Format token counts (similar to web-ui)\n\t\tconst formatTokens = (count: number): string => {\n\t\t\tif (count < 1000) return count.toString();\n\t\t\tif (count < 10000) return (count / 1000).toFixed(1) + \"k\";\n\t\t\tif (count < 1000000) return Math.round(count / 1000) + \"k\";\n\t\t\tif (count < 10000000) return (count / 1000000).toFixed(1) + \"M\";\n\t\t\treturn Math.round(count / 1000000) + \"M\";\n\t\t};\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = process.cwd();\n\t\tconst home = process.env.HOME || process.env.USERPROFILE;\n\t\tif (home && pwd.startsWith(home)) {\n\t\t\tpwd = \"~\" + pwd.slice(home.length);\n\t\t}\n\n\t\t// Add git branch if available\n\t\tconst branch = this.getCurrentBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Truncate path if too long to fit width\n\t\tif (pwd.length > width) {\n\t\t\tconst half = Math.floor(width / 2) - 2;\n\t\t\tif (half > 0) {\n\t\t\t\tconst start = pwd.slice(0, half);\n\t\t\t\tconst end = pwd.slice(-(half - 1));\n\t\t\t\tpwd = `${start}...${end}`;\n\t\t\t} else {\n\t\t\t\tpwd = pwd.slice(0, Math.max(1, width));\n\t\t\t}\n\t\t}\n\n\t\t// Build stats line\n\t\tconst statsParts = [];\n\t\tif (totalInput) statsParts.push(`↑${formatTokens(totalInput)}`);\n\t\tif (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);\n\t\tif (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);\n\t\tif (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);\n\n\t\t// Show cost with \"(sub)\" indicator if using OAuth subscription\n\t\tconst usingSubscription = this.state.model ? isModelUsingOAuth(this.state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tconst costStr = `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tstatsParts.push(costStr);\n\t\t}\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay = `${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = contextPercentDisplay;\n\t\t}\n\t\tstatsParts.push(contextPercentStr);\n\n\t\tlet statsLeft = statsParts.join(\" \");\n\n\t\t// Add model name on the right side, plus thinking level if model supports it\n\t\tconst modelName = this.state.model?.id || \"no-model\";\n\n\t\t// Add thinking level hint if model supports reasoning and thinking is enabled\n\t\tlet rightSide = modelName;\n\t\tif (this.state.model?.reasoning) {\n\t\t\tconst thinkingLevel = this.state.thinkingLevel || \"off\";\n\t\t\tif (thinkingLevel !== \"off\") {\n\t\t\t\trightSide = `${modelName} • ${thinkingLevel}`;\n\t\t\t}\n\t\t}\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\t// Truncate statsLeft to fit width (no room for right side)\n\t\t\tconst plainStatsLeft = statsLeft.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n\t\t\tstatsLeft = plainStatsLeft.substring(0, width - 3) + \"...\";\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 3) {\n\t\t\t\t// Truncate to fit (strip ANSI codes for length calculation, then truncate raw string)\n\t\t\t\tconst plainRightSide = rightSide.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n\t\t\t\tconst truncatedPlain = plainRightSide.substring(0, availableForRight);\n\t\t\t\t// For simplicity, just use plain truncated version (loses color, but fits)\n\t\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - truncatedPlain.length);\n\t\t\t\tstatsLine = statsLeft + padding + truncatedPlain;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Apply dim to each part separately. statsLeft may contain color codes (for context %)\n\t\t// that end with a reset, which would clear an outer dim wrapper. So we dim the parts\n\t\t// before and after the colored section independently.\n\t\tconst dimStatsLeft = theme.fg(\"dim\", statsLeft);\n\t\tconst remainder = statsLine.slice(statsLeft.length); // padding + rightSide\n\t\tconst dimRemainder = theme.fg(\"dim\", remainder);\n\n\t\treturn [theme.fg(\"dim\", pwd), dimStatsLeft + dimRemainder];\n\t}\n}\n"]}
|
|
@@ -256,8 +256,13 @@ export class FooterComponent {
|
|
|
256
256
|
statsLine = statsLeft;
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
|
-
//
|
|
260
|
-
|
|
259
|
+
// Apply dim to each part separately. statsLeft may contain color codes (for context %)
|
|
260
|
+
// that end with a reset, which would clear an outer dim wrapper. So we dim the parts
|
|
261
|
+
// before and after the colored section independently.
|
|
262
|
+
const dimStatsLeft = theme.fg("dim", statsLeft);
|
|
263
|
+
const remainder = statsLine.slice(statsLeft.length); // padding + rightSide
|
|
264
|
+
const dimRemainder = theme.fg("dim", remainder);
|
|
265
|
+
return [theme.fg("dim", pwd), dimStatsLeft + dimRemainder];
|
|
261
266
|
}
|
|
262
267
|
}
|
|
263
268
|
//# sourceMappingURL=footer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"footer.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentState } from \"@mariozechner/pi-agent-core\";\nimport type { AssistantMessage } from \"@mariozechner/pi-ai\";\nimport { type Component, visibleWidth } from \"@mariozechner/pi-tui\";\nimport { existsSync, type FSWatcher, readFileSync, watch } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { isModelUsingOAuth } from \"../../../core/model-config.js\";\nimport { theme } from \"../theme/theme.js\";\n\n/**\n * Find the git root directory by walking up from cwd.\n * Returns the path to .git/HEAD if found, null otherwise.\n */\nfunction findGitHeadPath(): string | null {\n\tlet dir = process.cwd();\n\twhile (true) {\n\t\tconst gitHeadPath = join(dir, \".git\", \"HEAD\");\n\t\tif (existsSync(gitHeadPath)) {\n\t\t\treturn gitHeadPath;\n\t\t}\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) {\n\t\t\t// Reached filesystem root\n\t\t\treturn null;\n\t\t}\n\t\tdir = parent;\n\t}\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage\n */\nexport class FooterComponent implements Component {\n\tprivate state: AgentState;\n\tprivate cachedBranch: string | null | undefined = undefined; // undefined = not checked yet, null = not in git repo, string = branch name\n\tprivate gitWatcher: FSWatcher | null = null;\n\tprivate onBranchChange: (() => void) | null = null;\n\tprivate autoCompactEnabled: boolean = true;\n\n\tconstructor(state: AgentState) {\n\t\tthis.state = state;\n\t}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\t/**\n\t * Set up a file watcher on .git/HEAD to detect branch changes.\n\t * Call the provided callback when branch changes.\n\t */\n\twatchBranch(onBranchChange: () => void): void {\n\t\tthis.onBranchChange = onBranchChange;\n\t\tthis.setupGitWatcher();\n\t}\n\n\tprivate setupGitWatcher(): void {\n\t\t// Clean up existing watcher\n\t\tif (this.gitWatcher) {\n\t\t\tthis.gitWatcher.close();\n\t\t\tthis.gitWatcher = null;\n\t\t}\n\n\t\tconst gitHeadPath = findGitHeadPath();\n\t\tif (!gitHeadPath) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.gitWatcher = watch(gitHeadPath, () => {\n\t\t\t\tthis.cachedBranch = undefined; // Invalidate cache\n\t\t\t\tif (this.onBranchChange) {\n\t\t\t\t\tthis.onBranchChange();\n\t\t\t\t}\n\t\t\t});\n\t\t} catch {\n\t\t\t// Silently fail if we can't watch\n\t\t}\n\t}\n\n\t/**\n\t * Clean up the file watcher\n\t */\n\tdispose(): void {\n\t\tif (this.gitWatcher) {\n\t\t\tthis.gitWatcher.close();\n\t\t\tthis.gitWatcher = null;\n\t\t}\n\t}\n\n\tupdateState(state: AgentState): void {\n\t\tthis.state = state;\n\t}\n\n\tinvalidate(): void {\n\t\t// Invalidate cached branch so it gets re-read on next render\n\t\tthis.cachedBranch = undefined;\n\t}\n\n\t/**\n\t * Get current git branch by reading .git/HEAD directly.\n\t * Returns null if not in a git repo, branch name otherwise.\n\t */\n\tprivate getCurrentBranch(): string | null {\n\t\t// Return cached value if available\n\t\tif (this.cachedBranch !== undefined) {\n\t\t\treturn this.cachedBranch;\n\t\t}\n\n\t\ttry {\n\t\t\tconst gitHeadPath = findGitHeadPath();\n\t\t\tif (!gitHeadPath) {\n\t\t\t\tthis.cachedBranch = null;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst content = readFileSync(gitHeadPath, \"utf8\").trim();\n\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\t// Normal branch: extract branch name\n\t\t\t\tthis.cachedBranch = content.slice(16);\n\t\t\t} else {\n\t\t\t\t// Detached HEAD state\n\t\t\t\tthis.cachedBranch = \"detached\";\n\t\t\t}\n\t\t} catch {\n\t\t\t// Not in a git repo or error reading file\n\t\t\tthis.cachedBranch = null;\n\t\t}\n\n\t\treturn this.cachedBranch;\n\t}\n\n\trender(width: number): string[] {\n\t\t// Calculate cumulative usage from all assistant messages\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\n\t\tfor (const message of this.state.messages) {\n\t\t\tif (message.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = message as AssistantMessage;\n\t\t\t\ttotalInput += assistantMsg.usage.input;\n\t\t\t\ttotalOutput += assistantMsg.usage.output;\n\t\t\t\ttotalCacheRead += assistantMsg.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += assistantMsg.usage.cacheWrite;\n\t\t\t\ttotalCost += assistantMsg.usage.cost.total;\n\t\t\t}\n\t\t}\n\n\t\t// Get last assistant message for context percentage calculation (skip aborted messages)\n\t\tconst lastAssistantMessage = this.state.messages\n\t\t\t.slice()\n\t\t\t.reverse()\n\t\t\t.find((m) => m.role === \"assistant\" && m.stopReason !== \"aborted\") as AssistantMessage | undefined;\n\n\t\t// Calculate context percentage from last message (input + output + cacheRead + cacheWrite)\n\t\tconst contextTokens = lastAssistantMessage\n\t\t\t? lastAssistantMessage.usage.input +\n\t\t\t\tlastAssistantMessage.usage.output +\n\t\t\t\tlastAssistantMessage.usage.cacheRead +\n\t\t\t\tlastAssistantMessage.usage.cacheWrite\n\t\t\t: 0;\n\t\tconst contextWindow = this.state.model?.contextWindow || 0;\n\t\tconst contextPercentValue = contextWindow > 0 ? (contextTokens / contextWindow) * 100 : 0;\n\t\tconst contextPercent = contextPercentValue.toFixed(1);\n\n\t\t// Format token counts (similar to web-ui)\n\t\tconst formatTokens = (count: number): string => {\n\t\t\tif (count < 1000) return count.toString();\n\t\t\tif (count < 10000) return (count / 1000).toFixed(1) + \"k\";\n\t\t\tif (count < 1000000) return Math.round(count / 1000) + \"k\";\n\t\t\tif (count < 10000000) return (count / 1000000).toFixed(1) + \"M\";\n\t\t\treturn Math.round(count / 1000000) + \"M\";\n\t\t};\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = process.cwd();\n\t\tconst home = process.env.HOME || process.env.USERPROFILE;\n\t\tif (home && pwd.startsWith(home)) {\n\t\t\tpwd = \"~\" + pwd.slice(home.length);\n\t\t}\n\n\t\t// Add git branch if available\n\t\tconst branch = this.getCurrentBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Truncate path if too long to fit width\n\t\tif (pwd.length > width) {\n\t\t\tconst half = Math.floor(width / 2) - 2;\n\t\t\tif (half > 0) {\n\t\t\t\tconst start = pwd.slice(0, half);\n\t\t\t\tconst end = pwd.slice(-(half - 1));\n\t\t\t\tpwd = `${start}...${end}`;\n\t\t\t} else {\n\t\t\t\tpwd = pwd.slice(0, Math.max(1, width));\n\t\t\t}\n\t\t}\n\n\t\t// Build stats line\n\t\tconst statsParts = [];\n\t\tif (totalInput) statsParts.push(`↑${formatTokens(totalInput)}`);\n\t\tif (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);\n\t\tif (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);\n\t\tif (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);\n\n\t\t// Show cost with \"(sub)\" indicator if using OAuth subscription\n\t\tconst usingSubscription = this.state.model ? isModelUsingOAuth(this.state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tconst costStr = `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tstatsParts.push(costStr);\n\t\t}\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay = `${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = contextPercentDisplay;\n\t\t}\n\t\tstatsParts.push(contextPercentStr);\n\n\t\tlet statsLeft = statsParts.join(\" \");\n\n\t\t// Add model name on the right side, plus thinking level if model supports it\n\t\tconst modelName = this.state.model?.id || \"no-model\";\n\n\t\t// Add thinking level hint if model supports reasoning and thinking is enabled\n\t\tlet rightSide = modelName;\n\t\tif (this.state.model?.reasoning) {\n\t\t\tconst thinkingLevel = this.state.thinkingLevel || \"off\";\n\t\t\tif (thinkingLevel !== \"off\") {\n\t\t\t\trightSide = `${modelName} • ${thinkingLevel}`;\n\t\t\t}\n\t\t}\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\t// Truncate statsLeft to fit width (no room for right side)\n\t\t\tconst plainStatsLeft = statsLeft.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n\t\t\tstatsLeft = plainStatsLeft.substring(0, width - 3) + \"...\";\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 3) {\n\t\t\t\t// Truncate to fit (strip ANSI codes for length calculation, then truncate raw string)\n\t\t\t\tconst plainRightSide = rightSide.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n\t\t\t\tconst truncatedPlain = plainRightSide.substring(0, availableForRight);\n\t\t\t\t// For simplicity, just use plain truncated version (loses color, but fits)\n\t\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - truncatedPlain.length);\n\t\t\t\tstatsLine = statsLeft + padding + truncatedPlain;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Return two lines: pwd and stats\n\t\treturn [theme.fg(\"dim\", pwd), theme.fg(\"dim\", statsLine)];\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"footer.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentState } from \"@mariozechner/pi-agent-core\";\nimport type { AssistantMessage } from \"@mariozechner/pi-ai\";\nimport { type Component, visibleWidth } from \"@mariozechner/pi-tui\";\nimport { existsSync, type FSWatcher, readFileSync, watch } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { isModelUsingOAuth } from \"../../../core/model-config.js\";\nimport { theme } from \"../theme/theme.js\";\n\n/**\n * Find the git root directory by walking up from cwd.\n * Returns the path to .git/HEAD if found, null otherwise.\n */\nfunction findGitHeadPath(): string | null {\n\tlet dir = process.cwd();\n\twhile (true) {\n\t\tconst gitHeadPath = join(dir, \".git\", \"HEAD\");\n\t\tif (existsSync(gitHeadPath)) {\n\t\t\treturn gitHeadPath;\n\t\t}\n\t\tconst parent = dirname(dir);\n\t\tif (parent === dir) {\n\t\t\t// Reached filesystem root\n\t\t\treturn null;\n\t\t}\n\t\tdir = parent;\n\t}\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage\n */\nexport class FooterComponent implements Component {\n\tprivate state: AgentState;\n\tprivate cachedBranch: string | null | undefined = undefined; // undefined = not checked yet, null = not in git repo, string = branch name\n\tprivate gitWatcher: FSWatcher | null = null;\n\tprivate onBranchChange: (() => void) | null = null;\n\tprivate autoCompactEnabled: boolean = true;\n\n\tconstructor(state: AgentState) {\n\t\tthis.state = state;\n\t}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\t/**\n\t * Set up a file watcher on .git/HEAD to detect branch changes.\n\t * Call the provided callback when branch changes.\n\t */\n\twatchBranch(onBranchChange: () => void): void {\n\t\tthis.onBranchChange = onBranchChange;\n\t\tthis.setupGitWatcher();\n\t}\n\n\tprivate setupGitWatcher(): void {\n\t\t// Clean up existing watcher\n\t\tif (this.gitWatcher) {\n\t\t\tthis.gitWatcher.close();\n\t\t\tthis.gitWatcher = null;\n\t\t}\n\n\t\tconst gitHeadPath = findGitHeadPath();\n\t\tif (!gitHeadPath) {\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tthis.gitWatcher = watch(gitHeadPath, () => {\n\t\t\t\tthis.cachedBranch = undefined; // Invalidate cache\n\t\t\t\tif (this.onBranchChange) {\n\t\t\t\t\tthis.onBranchChange();\n\t\t\t\t}\n\t\t\t});\n\t\t} catch {\n\t\t\t// Silently fail if we can't watch\n\t\t}\n\t}\n\n\t/**\n\t * Clean up the file watcher\n\t */\n\tdispose(): void {\n\t\tif (this.gitWatcher) {\n\t\t\tthis.gitWatcher.close();\n\t\t\tthis.gitWatcher = null;\n\t\t}\n\t}\n\n\tupdateState(state: AgentState): void {\n\t\tthis.state = state;\n\t}\n\n\tinvalidate(): void {\n\t\t// Invalidate cached branch so it gets re-read on next render\n\t\tthis.cachedBranch = undefined;\n\t}\n\n\t/**\n\t * Get current git branch by reading .git/HEAD directly.\n\t * Returns null if not in a git repo, branch name otherwise.\n\t */\n\tprivate getCurrentBranch(): string | null {\n\t\t// Return cached value if available\n\t\tif (this.cachedBranch !== undefined) {\n\t\t\treturn this.cachedBranch;\n\t\t}\n\n\t\ttry {\n\t\t\tconst gitHeadPath = findGitHeadPath();\n\t\t\tif (!gitHeadPath) {\n\t\t\t\tthis.cachedBranch = null;\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tconst content = readFileSync(gitHeadPath, \"utf8\").trim();\n\n\t\t\tif (content.startsWith(\"ref: refs/heads/\")) {\n\t\t\t\t// Normal branch: extract branch name\n\t\t\t\tthis.cachedBranch = content.slice(16);\n\t\t\t} else {\n\t\t\t\t// Detached HEAD state\n\t\t\t\tthis.cachedBranch = \"detached\";\n\t\t\t}\n\t\t} catch {\n\t\t\t// Not in a git repo or error reading file\n\t\t\tthis.cachedBranch = null;\n\t\t}\n\n\t\treturn this.cachedBranch;\n\t}\n\n\trender(width: number): string[] {\n\t\t// Calculate cumulative usage from all assistant messages\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\n\t\tfor (const message of this.state.messages) {\n\t\t\tif (message.role === \"assistant\") {\n\t\t\t\tconst assistantMsg = message as AssistantMessage;\n\t\t\t\ttotalInput += assistantMsg.usage.input;\n\t\t\t\ttotalOutput += assistantMsg.usage.output;\n\t\t\t\ttotalCacheRead += assistantMsg.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += assistantMsg.usage.cacheWrite;\n\t\t\t\ttotalCost += assistantMsg.usage.cost.total;\n\t\t\t}\n\t\t}\n\n\t\t// Get last assistant message for context percentage calculation (skip aborted messages)\n\t\tconst lastAssistantMessage = this.state.messages\n\t\t\t.slice()\n\t\t\t.reverse()\n\t\t\t.find((m) => m.role === \"assistant\" && m.stopReason !== \"aborted\") as AssistantMessage | undefined;\n\n\t\t// Calculate context percentage from last message (input + output + cacheRead + cacheWrite)\n\t\tconst contextTokens = lastAssistantMessage\n\t\t\t? lastAssistantMessage.usage.input +\n\t\t\t\tlastAssistantMessage.usage.output +\n\t\t\t\tlastAssistantMessage.usage.cacheRead +\n\t\t\t\tlastAssistantMessage.usage.cacheWrite\n\t\t\t: 0;\n\t\tconst contextWindow = this.state.model?.contextWindow || 0;\n\t\tconst contextPercentValue = contextWindow > 0 ? (contextTokens / contextWindow) * 100 : 0;\n\t\tconst contextPercent = contextPercentValue.toFixed(1);\n\n\t\t// Format token counts (similar to web-ui)\n\t\tconst formatTokens = (count: number): string => {\n\t\t\tif (count < 1000) return count.toString();\n\t\t\tif (count < 10000) return (count / 1000).toFixed(1) + \"k\";\n\t\t\tif (count < 1000000) return Math.round(count / 1000) + \"k\";\n\t\t\tif (count < 10000000) return (count / 1000000).toFixed(1) + \"M\";\n\t\t\treturn Math.round(count / 1000000) + \"M\";\n\t\t};\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = process.cwd();\n\t\tconst home = process.env.HOME || process.env.USERPROFILE;\n\t\tif (home && pwd.startsWith(home)) {\n\t\t\tpwd = \"~\" + pwd.slice(home.length);\n\t\t}\n\n\t\t// Add git branch if available\n\t\tconst branch = this.getCurrentBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Truncate path if too long to fit width\n\t\tif (pwd.length > width) {\n\t\t\tconst half = Math.floor(width / 2) - 2;\n\t\t\tif (half > 0) {\n\t\t\t\tconst start = pwd.slice(0, half);\n\t\t\t\tconst end = pwd.slice(-(half - 1));\n\t\t\t\tpwd = `${start}...${end}`;\n\t\t\t} else {\n\t\t\t\tpwd = pwd.slice(0, Math.max(1, width));\n\t\t\t}\n\t\t}\n\n\t\t// Build stats line\n\t\tconst statsParts = [];\n\t\tif (totalInput) statsParts.push(`↑${formatTokens(totalInput)}`);\n\t\tif (totalOutput) statsParts.push(`↓${formatTokens(totalOutput)}`);\n\t\tif (totalCacheRead) statsParts.push(`R${formatTokens(totalCacheRead)}`);\n\t\tif (totalCacheWrite) statsParts.push(`W${formatTokens(totalCacheWrite)}`);\n\n\t\t// Show cost with \"(sub)\" indicator if using OAuth subscription\n\t\tconst usingSubscription = this.state.model ? isModelUsingOAuth(this.state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tconst costStr = `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tstatsParts.push(costStr);\n\t\t}\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay = `${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = contextPercentDisplay;\n\t\t}\n\t\tstatsParts.push(contextPercentStr);\n\n\t\tlet statsLeft = statsParts.join(\" \");\n\n\t\t// Add model name on the right side, plus thinking level if model supports it\n\t\tconst modelName = this.state.model?.id || \"no-model\";\n\n\t\t// Add thinking level hint if model supports reasoning and thinking is enabled\n\t\tlet rightSide = modelName;\n\t\tif (this.state.model?.reasoning) {\n\t\t\tconst thinkingLevel = this.state.thinkingLevel || \"off\";\n\t\t\tif (thinkingLevel !== \"off\") {\n\t\t\t\trightSide = `${modelName} • ${thinkingLevel}`;\n\t\t\t}\n\t\t}\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\t// Truncate statsLeft to fit width (no room for right side)\n\t\t\tconst plainStatsLeft = statsLeft.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n\t\t\tstatsLeft = plainStatsLeft.substring(0, width - 3) + \"...\";\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 3) {\n\t\t\t\t// Truncate to fit (strip ANSI codes for length calculation, then truncate raw string)\n\t\t\t\tconst plainRightSide = rightSide.replace(/\\x1b\\[[0-9;]*m/g, \"\");\n\t\t\t\tconst truncatedPlain = plainRightSide.substring(0, availableForRight);\n\t\t\t\t// For simplicity, just use plain truncated version (loses color, but fits)\n\t\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - truncatedPlain.length);\n\t\t\t\tstatsLine = statsLeft + padding + truncatedPlain;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Apply dim to each part separately. statsLeft may contain color codes (for context %)\n\t\t// that end with a reset, which would clear an outer dim wrapper. So we dim the parts\n\t\t// before and after the colored section independently.\n\t\tconst dimStatsLeft = theme.fg(\"dim\", statsLeft);\n\t\tconst remainder = statsLine.slice(statsLeft.length); // padding + rightSide\n\t\tconst dimRemainder = theme.fg(\"dim\", remainder);\n\n\t\treturn [theme.fg(\"dim\", pwd), dimStatsLeft + dimRemainder];\n\t}\n}\n"]}
|