@mariozechner/pi-coding-agent 0.47.0 → 0.49.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 +61 -1
- package/README.md +43 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +10 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +7 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +63 -9
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +11 -4
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +11 -0
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +50 -3
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +4 -0
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +5 -0
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js +7 -0
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +44 -5
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/prompt-templates.d.ts +5 -1
- package/dist/core/prompt-templates.d.ts.map +1 -1
- package/dist/core/prompt-templates.js +18 -1
- package/dist/core/prompt-templates.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +5 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +8 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +43 -0
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +12 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +28 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +16 -21
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +2 -0
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js +4 -1
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/index.d.ts +4 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +8 -3
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/path-utils.d.ts +0 -1
- package/dist/core/tools/path-utils.d.ts.map +1 -1
- package/dist/core/tools/path-utils.js +0 -7
- package/dist/core/tools/path-utils.js.map +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +2 -12
- package/dist/core/tools/read.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +52 -14
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +2 -2
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +2 -2
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts +2 -2
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +3 -3
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +5 -3
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +4 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +24 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +3 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +168 -80
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +23 -3
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js +22 -1
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +22 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/image-convert.d.ts.map +1 -1
- package/dist/utils/image-convert.js +8 -2
- package/dist/utils/image-convert.js.map +1 -1
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js +14 -1
- package/dist/utils/image-resize.js.map +1 -1
- package/dist/utils/photon.d.ts +20 -0
- package/dist/utils/photon.d.ts.map +1 -0
- package/dist/utils/photon.js +39 -0
- package/dist/utils/photon.js.map +1 -0
- package/docs/extensions.md +99 -5
- package/docs/rpc.md +2 -0
- package/docs/sdk.md +1 -1
- package/docs/tree.md +20 -2
- package/examples/extensions/README.md +1 -0
- package/examples/extensions/custom-header.ts +2 -1
- package/examples/extensions/trigger-compact.ts +40 -0
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-manager.js","sourceRoot":"","sources":["../../src/core/settings-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../config.js\";\n\nexport interface CompactionSettings {\n\tenabled?: boolean; // default: true\n\treserveTokens?: number; // default: 16384\n\tkeepRecentTokens?: number; // default: 20000\n}\n\nexport interface BranchSummarySettings {\n\treserveTokens?: number; // default: 16384 (tokens reserved for prompt + LLM response)\n}\n\nexport interface RetrySettings {\n\tenabled?: boolean; // default: true\n\tmaxRetries?: number; // default: 3\n\tbaseDelayMs?: number; // default: 2000 (exponential backoff: 2s, 4s, 8s)\n}\n\nexport interface SkillsSettings {\n\tenabled?: boolean; // default: true\n\tenableCodexUser?: boolean; // default: true\n\tenableClaudeUser?: boolean; // default: true\n\tenableClaudeProject?: boolean; // default: true\n\tenablePiUser?: boolean; // default: true\n\tenablePiProject?: boolean; // default: true\n\tenableSkillCommands?: boolean; // default: true - register skills as /skill:name commands\n\tcustomDirectories?: string[]; // default: []\n\tignoredSkills?: string[]; // default: [] (glob patterns to exclude; takes precedence over includeSkills)\n\tincludeSkills?: string[]; // default: [] (empty = include all; glob patterns to filter)\n}\n\nexport interface TerminalSettings {\n\tshowImages?: boolean; // default: true (only relevant if terminal supports images)\n}\n\nexport interface ImageSettings {\n\tautoResize?: boolean; // default: true (resize images to 2000x2000 max for better model compatibility)\n\tblockImages?: boolean; // default: false - when true, prevents all images from being sent to LLM providers\n}\n\nexport interface ThinkingBudgetsSettings {\n\tminimal?: number;\n\tlow?: number;\n\tmedium?: number;\n\thigh?: number;\n}\n\nexport interface Settings {\n\tlastChangelogVersion?: string;\n\tdefaultProvider?: string;\n\tdefaultModel?: string;\n\tdefaultThinkingLevel?: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\tsteeringMode?: \"all\" | \"one-at-a-time\";\n\tfollowUpMode?: \"all\" | \"one-at-a-time\";\n\ttheme?: string;\n\tcompaction?: CompactionSettings;\n\tbranchSummary?: BranchSummarySettings;\n\tretry?: RetrySettings;\n\thideThinkingBlock?: boolean;\n\tshellPath?: string; // Custom shell path (e.g., for Cygwin users on Windows)\n\tcollapseChangelog?: boolean; // Show condensed changelog after update (use /changelog for full)\n\textensions?: string[]; // Array of extension file paths\n\tskills?: SkillsSettings;\n\tterminal?: TerminalSettings;\n\timages?: ImageSettings;\n\tenabledModels?: string[]; // Model patterns for cycling (same format as --models CLI flag)\n\tdoubleEscapeAction?: \"fork\" | \"tree\"; // Action for double-escape with empty editor (default: \"tree\")\n\tthinkingBudgets?: ThinkingBudgetsSettings; // Custom token budgets for thinking levels\n}\n\n/** Deep merge settings: project/overrides take precedence, nested objects merge recursively */\nfunction deepMergeSettings(base: Settings, overrides: Settings): Settings {\n\tconst result: Settings = { ...base };\n\n\tfor (const key of Object.keys(overrides) as (keyof Settings)[]) {\n\t\tconst overrideValue = overrides[key];\n\t\tconst baseValue = base[key];\n\n\t\tif (overrideValue === undefined) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// For nested objects, merge recursively\n\t\tif (\n\t\t\ttypeof overrideValue === \"object\" &&\n\t\t\toverrideValue !== null &&\n\t\t\t!Array.isArray(overrideValue) &&\n\t\t\ttypeof baseValue === \"object\" &&\n\t\t\tbaseValue !== null &&\n\t\t\t!Array.isArray(baseValue)\n\t\t) {\n\t\t\t(result as Record<string, unknown>)[key] = { ...baseValue, ...overrideValue };\n\t\t} else {\n\t\t\t// For primitives and arrays, override value wins\n\t\t\t(result as Record<string, unknown>)[key] = overrideValue;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport class SettingsManager {\n\tprivate settingsPath: string | null;\n\tprivate projectSettingsPath: string | null;\n\tprivate globalSettings: Settings;\n\tprivate settings: Settings;\n\tprivate persist: boolean;\n\n\tprivate constructor(\n\t\tsettingsPath: string | null,\n\t\tprojectSettingsPath: string | null,\n\t\tinitialSettings: Settings,\n\t\tpersist: boolean,\n\t) {\n\t\tthis.settingsPath = settingsPath;\n\t\tthis.projectSettingsPath = projectSettingsPath;\n\t\tthis.persist = persist;\n\t\tthis.globalSettings = initialSettings;\n\t\tconst projectSettings = this.loadProjectSettings();\n\t\tthis.settings = deepMergeSettings(this.globalSettings, projectSettings);\n\t}\n\n\t/** Create a SettingsManager that loads from files */\n\tstatic create(cwd: string = process.cwd(), agentDir: string = getAgentDir()): SettingsManager {\n\t\tconst settingsPath = join(agentDir, \"settings.json\");\n\t\tconst projectSettingsPath = join(cwd, CONFIG_DIR_NAME, \"settings.json\");\n\t\tconst globalSettings = SettingsManager.loadFromFile(settingsPath);\n\t\treturn new SettingsManager(settingsPath, projectSettingsPath, globalSettings, true);\n\t}\n\n\t/** Create an in-memory SettingsManager (no file I/O) */\n\tstatic inMemory(settings: Partial<Settings> = {}): SettingsManager {\n\t\treturn new SettingsManager(null, null, settings, false);\n\t}\n\n\tprivate static loadFromFile(path: string): Settings {\n\t\tif (!existsSync(path)) {\n\t\t\treturn {};\n\t\t}\n\t\ttry {\n\t\t\tconst content = readFileSync(path, \"utf-8\");\n\t\t\tconst settings = JSON.parse(content);\n\t\t\treturn SettingsManager.migrateSettings(settings);\n\t\t} catch (error) {\n\t\t\tconsole.error(`Warning: Could not read settings file ${path}: ${error}`);\n\t\t\treturn {};\n\t\t}\n\t}\n\n\t/** Migrate old settings format to new format */\n\tprivate static migrateSettings(settings: Record<string, unknown>): Settings {\n\t\t// Migrate queueMode -> steeringMode\n\t\tif (\"queueMode\" in settings && !(\"steeringMode\" in settings)) {\n\t\t\tsettings.steeringMode = settings.queueMode;\n\t\t\tdelete settings.queueMode;\n\t\t}\n\t\treturn settings as Settings;\n\t}\n\n\tprivate loadProjectSettings(): Settings {\n\t\tif (!this.projectSettingsPath || !existsSync(this.projectSettingsPath)) {\n\t\t\treturn {};\n\t\t}\n\n\t\ttry {\n\t\t\tconst content = readFileSync(this.projectSettingsPath, \"utf-8\");\n\t\t\tconst settings = JSON.parse(content);\n\t\t\treturn SettingsManager.migrateSettings(settings);\n\t\t} catch (error) {\n\t\t\tconsole.error(`Warning: Could not read project settings file: ${error}`);\n\t\t\treturn {};\n\t\t}\n\t}\n\n\t/** Apply additional overrides on top of current settings */\n\tapplyOverrides(overrides: Partial<Settings>): void {\n\t\tthis.settings = deepMergeSettings(this.settings, overrides);\n\t}\n\n\tprivate save(): void {\n\t\tif (this.persist && this.settingsPath) {\n\t\t\ttry {\n\t\t\t\tconst dir = dirname(this.settingsPath);\n\t\t\t\tif (!existsSync(dir)) {\n\t\t\t\t\tmkdirSync(dir, { recursive: true });\n\t\t\t\t}\n\n\t\t\t\t// Re-read current file to preserve any settings added externally while running\n\t\t\t\tconst currentFileSettings = SettingsManager.loadFromFile(this.settingsPath);\n\t\t\t\t// Merge: file settings as base, globalSettings (in-memory changes) as overrides\n\t\t\t\tconst mergedSettings = deepMergeSettings(currentFileSettings, this.globalSettings);\n\t\t\t\tthis.globalSettings = mergedSettings;\n\n\t\t\t\t// Save merged settings (project settings are read-only)\n\t\t\t\twriteFileSync(this.settingsPath, JSON.stringify(this.globalSettings, null, 2), \"utf-8\");\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(`Warning: Could not save settings file: ${error}`);\n\t\t\t}\n\t\t}\n\n\t\t// Always re-merge to update active settings (needed for both file and inMemory modes)\n\t\tconst projectSettings = this.loadProjectSettings();\n\t\tthis.settings = deepMergeSettings(this.globalSettings, projectSettings);\n\t}\n\n\tgetLastChangelogVersion(): string | undefined {\n\t\treturn this.settings.lastChangelogVersion;\n\t}\n\n\tsetLastChangelogVersion(version: string): void {\n\t\tthis.globalSettings.lastChangelogVersion = version;\n\t\tthis.save();\n\t}\n\n\tgetDefaultProvider(): string | undefined {\n\t\treturn this.settings.defaultProvider;\n\t}\n\n\tgetDefaultModel(): string | undefined {\n\t\treturn this.settings.defaultModel;\n\t}\n\n\tsetDefaultProvider(provider: string): void {\n\t\tthis.globalSettings.defaultProvider = provider;\n\t\tthis.save();\n\t}\n\n\tsetDefaultModel(modelId: string): void {\n\t\tthis.globalSettings.defaultModel = modelId;\n\t\tthis.save();\n\t}\n\n\tsetDefaultModelAndProvider(provider: string, modelId: string): void {\n\t\tthis.globalSettings.defaultProvider = provider;\n\t\tthis.globalSettings.defaultModel = modelId;\n\t\tthis.save();\n\t}\n\n\tgetSteeringMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.settings.steeringMode || \"one-at-a-time\";\n\t}\n\n\tsetSteeringMode(mode: \"all\" | \"one-at-a-time\"): void {\n\t\tthis.globalSettings.steeringMode = mode;\n\t\tthis.save();\n\t}\n\n\tgetFollowUpMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.settings.followUpMode || \"one-at-a-time\";\n\t}\n\n\tsetFollowUpMode(mode: \"all\" | \"one-at-a-time\"): void {\n\t\tthis.globalSettings.followUpMode = mode;\n\t\tthis.save();\n\t}\n\n\tgetTheme(): string | undefined {\n\t\treturn this.settings.theme;\n\t}\n\n\tsetTheme(theme: string): void {\n\t\tthis.globalSettings.theme = theme;\n\t\tthis.save();\n\t}\n\n\tgetDefaultThinkingLevel(): \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | undefined {\n\t\treturn this.settings.defaultThinkingLevel;\n\t}\n\n\tsetDefaultThinkingLevel(level: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\"): void {\n\t\tthis.globalSettings.defaultThinkingLevel = level;\n\t\tthis.save();\n\t}\n\n\tgetCompactionEnabled(): boolean {\n\t\treturn this.settings.compaction?.enabled ?? true;\n\t}\n\n\tsetCompactionEnabled(enabled: boolean): void {\n\t\tif (!this.globalSettings.compaction) {\n\t\t\tthis.globalSettings.compaction = {};\n\t\t}\n\t\tthis.globalSettings.compaction.enabled = enabled;\n\t\tthis.save();\n\t}\n\n\tgetCompactionReserveTokens(): number {\n\t\treturn this.settings.compaction?.reserveTokens ?? 16384;\n\t}\n\n\tgetCompactionKeepRecentTokens(): number {\n\t\treturn this.settings.compaction?.keepRecentTokens ?? 20000;\n\t}\n\n\tgetCompactionSettings(): { enabled: boolean; reserveTokens: number; keepRecentTokens: number } {\n\t\treturn {\n\t\t\tenabled: this.getCompactionEnabled(),\n\t\t\treserveTokens: this.getCompactionReserveTokens(),\n\t\t\tkeepRecentTokens: this.getCompactionKeepRecentTokens(),\n\t\t};\n\t}\n\n\tgetBranchSummarySettings(): { reserveTokens: number } {\n\t\treturn {\n\t\t\treserveTokens: this.settings.branchSummary?.reserveTokens ?? 16384,\n\t\t};\n\t}\n\n\tgetRetryEnabled(): boolean {\n\t\treturn this.settings.retry?.enabled ?? true;\n\t}\n\n\tsetRetryEnabled(enabled: boolean): void {\n\t\tif (!this.globalSettings.retry) {\n\t\t\tthis.globalSettings.retry = {};\n\t\t}\n\t\tthis.globalSettings.retry.enabled = enabled;\n\t\tthis.save();\n\t}\n\n\tgetRetrySettings(): { enabled: boolean; maxRetries: number; baseDelayMs: number } {\n\t\treturn {\n\t\t\tenabled: this.getRetryEnabled(),\n\t\t\tmaxRetries: this.settings.retry?.maxRetries ?? 3,\n\t\t\tbaseDelayMs: this.settings.retry?.baseDelayMs ?? 2000,\n\t\t};\n\t}\n\n\tgetHideThinkingBlock(): boolean {\n\t\treturn this.settings.hideThinkingBlock ?? false;\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.globalSettings.hideThinkingBlock = hide;\n\t\tthis.save();\n\t}\n\n\tgetShellPath(): string | undefined {\n\t\treturn this.settings.shellPath;\n\t}\n\n\tsetShellPath(path: string | undefined): void {\n\t\tthis.globalSettings.shellPath = path;\n\t\tthis.save();\n\t}\n\n\tgetCollapseChangelog(): boolean {\n\t\treturn this.settings.collapseChangelog ?? false;\n\t}\n\n\tsetCollapseChangelog(collapse: boolean): void {\n\t\tthis.globalSettings.collapseChangelog = collapse;\n\t\tthis.save();\n\t}\n\n\tgetExtensionPaths(): string[] {\n\t\treturn [...(this.settings.extensions ?? [])];\n\t}\n\n\tsetExtensionPaths(paths: string[]): void {\n\t\tthis.globalSettings.extensions = paths;\n\t\tthis.save();\n\t}\n\n\tgetSkillsEnabled(): boolean {\n\t\treturn this.settings.skills?.enabled ?? true;\n\t}\n\n\tsetSkillsEnabled(enabled: boolean): void {\n\t\tif (!this.globalSettings.skills) {\n\t\t\tthis.globalSettings.skills = {};\n\t\t}\n\t\tthis.globalSettings.skills.enabled = enabled;\n\t\tthis.save();\n\t}\n\n\tgetSkillsSettings(): Required<SkillsSettings> {\n\t\treturn {\n\t\t\tenabled: this.settings.skills?.enabled ?? true,\n\t\t\tenableCodexUser: this.settings.skills?.enableCodexUser ?? true,\n\t\t\tenableClaudeUser: this.settings.skills?.enableClaudeUser ?? true,\n\t\t\tenableClaudeProject: this.settings.skills?.enableClaudeProject ?? true,\n\t\t\tenablePiUser: this.settings.skills?.enablePiUser ?? true,\n\t\t\tenablePiProject: this.settings.skills?.enablePiProject ?? true,\n\t\t\tenableSkillCommands: this.settings.skills?.enableSkillCommands ?? true,\n\t\t\tcustomDirectories: [...(this.settings.skills?.customDirectories ?? [])],\n\t\t\tignoredSkills: [...(this.settings.skills?.ignoredSkills ?? [])],\n\t\t\tincludeSkills: [...(this.settings.skills?.includeSkills ?? [])],\n\t\t};\n\t}\n\n\tgetEnableSkillCommands(): boolean {\n\t\treturn this.settings.skills?.enableSkillCommands ?? true;\n\t}\n\n\tsetEnableSkillCommands(enabled: boolean): void {\n\t\tif (!this.globalSettings.skills) {\n\t\t\tthis.globalSettings.skills = {};\n\t\t}\n\t\tthis.globalSettings.skills.enableSkillCommands = enabled;\n\t\tthis.save();\n\t}\n\n\tgetThinkingBudgets(): ThinkingBudgetsSettings | undefined {\n\t\treturn this.settings.thinkingBudgets;\n\t}\n\n\tgetShowImages(): boolean {\n\t\treturn this.settings.terminal?.showImages ?? true;\n\t}\n\n\tsetShowImages(show: boolean): void {\n\t\tif (!this.globalSettings.terminal) {\n\t\t\tthis.globalSettings.terminal = {};\n\t\t}\n\t\tthis.globalSettings.terminal.showImages = show;\n\t\tthis.save();\n\t}\n\n\tgetImageAutoResize(): boolean {\n\t\treturn this.settings.images?.autoResize ?? true;\n\t}\n\n\tsetImageAutoResize(enabled: boolean): void {\n\t\tif (!this.globalSettings.images) {\n\t\t\tthis.globalSettings.images = {};\n\t\t}\n\t\tthis.globalSettings.images.autoResize = enabled;\n\t\tthis.save();\n\t}\n\n\tgetBlockImages(): boolean {\n\t\treturn this.settings.images?.blockImages ?? false;\n\t}\n\n\tsetBlockImages(blocked: boolean): void {\n\t\tif (!this.globalSettings.images) {\n\t\t\tthis.globalSettings.images = {};\n\t\t}\n\t\tthis.globalSettings.images.blockImages = blocked;\n\t\tthis.save();\n\t}\n\n\tgetEnabledModels(): string[] | undefined {\n\t\treturn this.settings.enabledModels;\n\t}\n\n\tsetEnabledModels(patterns: string[] | undefined): void {\n\t\tthis.globalSettings.enabledModels = patterns;\n\t\tthis.save();\n\t}\n\n\tgetDoubleEscapeAction(): \"fork\" | \"tree\" {\n\t\treturn this.settings.doubleEscapeAction ?? \"tree\";\n\t}\n\n\tsetDoubleEscapeAction(action: \"fork\" | \"tree\"): void {\n\t\tthis.globalSettings.doubleEscapeAction = action;\n\t\tthis.save();\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"settings-manager.js","sourceRoot":"","sources":["../../src/core/settings-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../config.js\";\n\nexport interface CompactionSettings {\n\tenabled?: boolean; // default: true\n\treserveTokens?: number; // default: 16384\n\tkeepRecentTokens?: number; // default: 20000\n}\n\nexport interface BranchSummarySettings {\n\treserveTokens?: number; // default: 16384 (tokens reserved for prompt + LLM response)\n}\n\nexport interface RetrySettings {\n\tenabled?: boolean; // default: true\n\tmaxRetries?: number; // default: 3\n\tbaseDelayMs?: number; // default: 2000 (exponential backoff: 2s, 4s, 8s)\n}\n\nexport interface SkillsSettings {\n\tenabled?: boolean; // default: true\n\tenableCodexUser?: boolean; // default: true\n\tenableClaudeUser?: boolean; // default: true\n\tenableClaudeProject?: boolean; // default: true\n\tenablePiUser?: boolean; // default: true\n\tenablePiProject?: boolean; // default: true\n\tenableSkillCommands?: boolean; // default: true - register skills as /skill:name commands\n\tcustomDirectories?: string[]; // default: []\n\tignoredSkills?: string[]; // default: [] (glob patterns to exclude; takes precedence over includeSkills)\n\tincludeSkills?: string[]; // default: [] (empty = include all; glob patterns to filter)\n}\n\nexport interface TerminalSettings {\n\tshowImages?: boolean; // default: true (only relevant if terminal supports images)\n}\n\nexport interface ImageSettings {\n\tautoResize?: boolean; // default: true (resize images to 2000x2000 max for better model compatibility)\n\tblockImages?: boolean; // default: false - when true, prevents all images from being sent to LLM providers\n}\n\nexport interface ThinkingBudgetsSettings {\n\tminimal?: number;\n\tlow?: number;\n\tmedium?: number;\n\thigh?: number;\n}\n\nexport interface Settings {\n\tlastChangelogVersion?: string;\n\tdefaultProvider?: string;\n\tdefaultModel?: string;\n\tdefaultThinkingLevel?: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\";\n\tsteeringMode?: \"all\" | \"one-at-a-time\";\n\tfollowUpMode?: \"all\" | \"one-at-a-time\";\n\ttheme?: string;\n\tcompaction?: CompactionSettings;\n\tbranchSummary?: BranchSummarySettings;\n\tretry?: RetrySettings;\n\thideThinkingBlock?: boolean;\n\tshellPath?: string; // Custom shell path (e.g., for Cygwin users on Windows)\n\tquietStartup?: boolean;\n\tshellCommandPrefix?: string; // Prefix prepended to every bash command (e.g., \"shopt -s expand_aliases\" for alias support)\n\tcollapseChangelog?: boolean; // Show condensed changelog after update (use /changelog for full)\n\textensions?: string[]; // Array of extension file paths\n\tskills?: SkillsSettings;\n\tterminal?: TerminalSettings;\n\timages?: ImageSettings;\n\tenabledModels?: string[]; // Model patterns for cycling (same format as --models CLI flag)\n\tdoubleEscapeAction?: \"fork\" | \"tree\"; // Action for double-escape with empty editor (default: \"tree\")\n\tthinkingBudgets?: ThinkingBudgetsSettings; // Custom token budgets for thinking levels\n\teditorPaddingX?: number; // Horizontal padding for input editor (default: 0)\n\tshowHardwareCursor?: boolean; // Show terminal cursor while still positioning it for IME\n}\n\n/** Deep merge settings: project/overrides take precedence, nested objects merge recursively */\nfunction deepMergeSettings(base: Settings, overrides: Settings): Settings {\n\tconst result: Settings = { ...base };\n\n\tfor (const key of Object.keys(overrides) as (keyof Settings)[]) {\n\t\tconst overrideValue = overrides[key];\n\t\tconst baseValue = base[key];\n\n\t\tif (overrideValue === undefined) {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// For nested objects, merge recursively\n\t\tif (\n\t\t\ttypeof overrideValue === \"object\" &&\n\t\t\toverrideValue !== null &&\n\t\t\t!Array.isArray(overrideValue) &&\n\t\t\ttypeof baseValue === \"object\" &&\n\t\t\tbaseValue !== null &&\n\t\t\t!Array.isArray(baseValue)\n\t\t) {\n\t\t\t(result as Record<string, unknown>)[key] = { ...baseValue, ...overrideValue };\n\t\t} else {\n\t\t\t// For primitives and arrays, override value wins\n\t\t\t(result as Record<string, unknown>)[key] = overrideValue;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nexport class SettingsManager {\n\tprivate settingsPath: string | null;\n\tprivate projectSettingsPath: string | null;\n\tprivate globalSettings: Settings;\n\tprivate settings: Settings;\n\tprivate persist: boolean;\n\n\tprivate constructor(\n\t\tsettingsPath: string | null,\n\t\tprojectSettingsPath: string | null,\n\t\tinitialSettings: Settings,\n\t\tpersist: boolean,\n\t) {\n\t\tthis.settingsPath = settingsPath;\n\t\tthis.projectSettingsPath = projectSettingsPath;\n\t\tthis.persist = persist;\n\t\tthis.globalSettings = initialSettings;\n\t\tconst projectSettings = this.loadProjectSettings();\n\t\tthis.settings = deepMergeSettings(this.globalSettings, projectSettings);\n\t}\n\n\t/** Create a SettingsManager that loads from files */\n\tstatic create(cwd: string = process.cwd(), agentDir: string = getAgentDir()): SettingsManager {\n\t\tconst settingsPath = join(agentDir, \"settings.json\");\n\t\tconst projectSettingsPath = join(cwd, CONFIG_DIR_NAME, \"settings.json\");\n\t\tconst globalSettings = SettingsManager.loadFromFile(settingsPath);\n\t\treturn new SettingsManager(settingsPath, projectSettingsPath, globalSettings, true);\n\t}\n\n\t/** Create an in-memory SettingsManager (no file I/O) */\n\tstatic inMemory(settings: Partial<Settings> = {}): SettingsManager {\n\t\treturn new SettingsManager(null, null, settings, false);\n\t}\n\n\tprivate static loadFromFile(path: string): Settings {\n\t\tif (!existsSync(path)) {\n\t\t\treturn {};\n\t\t}\n\t\ttry {\n\t\t\tconst content = readFileSync(path, \"utf-8\");\n\t\t\tconst settings = JSON.parse(content);\n\t\t\treturn SettingsManager.migrateSettings(settings);\n\t\t} catch (error) {\n\t\t\tconsole.error(`Warning: Could not read settings file ${path}: ${error}`);\n\t\t\treturn {};\n\t\t}\n\t}\n\n\t/** Migrate old settings format to new format */\n\tprivate static migrateSettings(settings: Record<string, unknown>): Settings {\n\t\t// Migrate queueMode -> steeringMode\n\t\tif (\"queueMode\" in settings && !(\"steeringMode\" in settings)) {\n\t\t\tsettings.steeringMode = settings.queueMode;\n\t\t\tdelete settings.queueMode;\n\t\t}\n\t\treturn settings as Settings;\n\t}\n\n\tprivate loadProjectSettings(): Settings {\n\t\tif (!this.projectSettingsPath || !existsSync(this.projectSettingsPath)) {\n\t\t\treturn {};\n\t\t}\n\n\t\ttry {\n\t\t\tconst content = readFileSync(this.projectSettingsPath, \"utf-8\");\n\t\t\tconst settings = JSON.parse(content);\n\t\t\treturn SettingsManager.migrateSettings(settings);\n\t\t} catch (error) {\n\t\t\tconsole.error(`Warning: Could not read project settings file: ${error}`);\n\t\t\treturn {};\n\t\t}\n\t}\n\n\t/** Apply additional overrides on top of current settings */\n\tapplyOverrides(overrides: Partial<Settings>): void {\n\t\tthis.settings = deepMergeSettings(this.settings, overrides);\n\t}\n\n\tprivate save(): void {\n\t\tif (this.persist && this.settingsPath) {\n\t\t\ttry {\n\t\t\t\tconst dir = dirname(this.settingsPath);\n\t\t\t\tif (!existsSync(dir)) {\n\t\t\t\t\tmkdirSync(dir, { recursive: true });\n\t\t\t\t}\n\n\t\t\t\t// Re-read current file to preserve any settings added externally while running\n\t\t\t\tconst currentFileSettings = SettingsManager.loadFromFile(this.settingsPath);\n\t\t\t\t// Merge: file settings as base, globalSettings (in-memory changes) as overrides\n\t\t\t\tconst mergedSettings = deepMergeSettings(currentFileSettings, this.globalSettings);\n\t\t\t\tthis.globalSettings = mergedSettings;\n\n\t\t\t\t// Save merged settings (project settings are read-only)\n\t\t\t\twriteFileSync(this.settingsPath, JSON.stringify(this.globalSettings, null, 2), \"utf-8\");\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(`Warning: Could not save settings file: ${error}`);\n\t\t\t}\n\t\t}\n\n\t\t// Always re-merge to update active settings (needed for both file and inMemory modes)\n\t\tconst projectSettings = this.loadProjectSettings();\n\t\tthis.settings = deepMergeSettings(this.globalSettings, projectSettings);\n\t}\n\n\tgetLastChangelogVersion(): string | undefined {\n\t\treturn this.settings.lastChangelogVersion;\n\t}\n\n\tsetLastChangelogVersion(version: string): void {\n\t\tthis.globalSettings.lastChangelogVersion = version;\n\t\tthis.save();\n\t}\n\n\tgetDefaultProvider(): string | undefined {\n\t\treturn this.settings.defaultProvider;\n\t}\n\n\tgetDefaultModel(): string | undefined {\n\t\treturn this.settings.defaultModel;\n\t}\n\n\tsetDefaultProvider(provider: string): void {\n\t\tthis.globalSettings.defaultProvider = provider;\n\t\tthis.save();\n\t}\n\n\tsetDefaultModel(modelId: string): void {\n\t\tthis.globalSettings.defaultModel = modelId;\n\t\tthis.save();\n\t}\n\n\tsetDefaultModelAndProvider(provider: string, modelId: string): void {\n\t\tthis.globalSettings.defaultProvider = provider;\n\t\tthis.globalSettings.defaultModel = modelId;\n\t\tthis.save();\n\t}\n\n\tgetSteeringMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.settings.steeringMode || \"one-at-a-time\";\n\t}\n\n\tsetSteeringMode(mode: \"all\" | \"one-at-a-time\"): void {\n\t\tthis.globalSettings.steeringMode = mode;\n\t\tthis.save();\n\t}\n\n\tgetFollowUpMode(): \"all\" | \"one-at-a-time\" {\n\t\treturn this.settings.followUpMode || \"one-at-a-time\";\n\t}\n\n\tsetFollowUpMode(mode: \"all\" | \"one-at-a-time\"): void {\n\t\tthis.globalSettings.followUpMode = mode;\n\t\tthis.save();\n\t}\n\n\tgetTheme(): string | undefined {\n\t\treturn this.settings.theme;\n\t}\n\n\tsetTheme(theme: string): void {\n\t\tthis.globalSettings.theme = theme;\n\t\tthis.save();\n\t}\n\n\tgetDefaultThinkingLevel(): \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | undefined {\n\t\treturn this.settings.defaultThinkingLevel;\n\t}\n\n\tsetDefaultThinkingLevel(level: \"off\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\"): void {\n\t\tthis.globalSettings.defaultThinkingLevel = level;\n\t\tthis.save();\n\t}\n\n\tgetCompactionEnabled(): boolean {\n\t\treturn this.settings.compaction?.enabled ?? true;\n\t}\n\n\tsetCompactionEnabled(enabled: boolean): void {\n\t\tif (!this.globalSettings.compaction) {\n\t\t\tthis.globalSettings.compaction = {};\n\t\t}\n\t\tthis.globalSettings.compaction.enabled = enabled;\n\t\tthis.save();\n\t}\n\n\tgetCompactionReserveTokens(): number {\n\t\treturn this.settings.compaction?.reserveTokens ?? 16384;\n\t}\n\n\tgetCompactionKeepRecentTokens(): number {\n\t\treturn this.settings.compaction?.keepRecentTokens ?? 20000;\n\t}\n\n\tgetCompactionSettings(): { enabled: boolean; reserveTokens: number; keepRecentTokens: number } {\n\t\treturn {\n\t\t\tenabled: this.getCompactionEnabled(),\n\t\t\treserveTokens: this.getCompactionReserveTokens(),\n\t\t\tkeepRecentTokens: this.getCompactionKeepRecentTokens(),\n\t\t};\n\t}\n\n\tgetBranchSummarySettings(): { reserveTokens: number } {\n\t\treturn {\n\t\t\treserveTokens: this.settings.branchSummary?.reserveTokens ?? 16384,\n\t\t};\n\t}\n\n\tgetRetryEnabled(): boolean {\n\t\treturn this.settings.retry?.enabled ?? true;\n\t}\n\n\tsetRetryEnabled(enabled: boolean): void {\n\t\tif (!this.globalSettings.retry) {\n\t\t\tthis.globalSettings.retry = {};\n\t\t}\n\t\tthis.globalSettings.retry.enabled = enabled;\n\t\tthis.save();\n\t}\n\n\tgetRetrySettings(): { enabled: boolean; maxRetries: number; baseDelayMs: number } {\n\t\treturn {\n\t\t\tenabled: this.getRetryEnabled(),\n\t\t\tmaxRetries: this.settings.retry?.maxRetries ?? 3,\n\t\t\tbaseDelayMs: this.settings.retry?.baseDelayMs ?? 2000,\n\t\t};\n\t}\n\n\tgetHideThinkingBlock(): boolean {\n\t\treturn this.settings.hideThinkingBlock ?? false;\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.globalSettings.hideThinkingBlock = hide;\n\t\tthis.save();\n\t}\n\n\tgetShellPath(): string | undefined {\n\t\treturn this.settings.shellPath;\n\t}\n\n\tsetShellPath(path: string | undefined): void {\n\t\tthis.globalSettings.shellPath = path;\n\t\tthis.save();\n\t}\n\n\tgetQuietStartup(): boolean {\n\t\treturn this.settings.quietStartup ?? false;\n\t}\n\n\tsetQuietStartup(quiet: boolean): void {\n\t\tthis.globalSettings.quietStartup = quiet;\n\t\tthis.save();\n\t}\n\n\tgetShellCommandPrefix(): string | undefined {\n\t\treturn this.settings.shellCommandPrefix;\n\t}\n\n\tsetShellCommandPrefix(prefix: string | undefined): void {\n\t\tthis.globalSettings.shellCommandPrefix = prefix;\n\t\tthis.save();\n\t}\n\n\tgetCollapseChangelog(): boolean {\n\t\treturn this.settings.collapseChangelog ?? false;\n\t}\n\n\tsetCollapseChangelog(collapse: boolean): void {\n\t\tthis.globalSettings.collapseChangelog = collapse;\n\t\tthis.save();\n\t}\n\n\tgetExtensionPaths(): string[] {\n\t\treturn [...(this.settings.extensions ?? [])];\n\t}\n\n\tsetExtensionPaths(paths: string[]): void {\n\t\tthis.globalSettings.extensions = paths;\n\t\tthis.save();\n\t}\n\n\tgetSkillsEnabled(): boolean {\n\t\treturn this.settings.skills?.enabled ?? true;\n\t}\n\n\tsetSkillsEnabled(enabled: boolean): void {\n\t\tif (!this.globalSettings.skills) {\n\t\t\tthis.globalSettings.skills = {};\n\t\t}\n\t\tthis.globalSettings.skills.enabled = enabled;\n\t\tthis.save();\n\t}\n\n\tgetSkillsSettings(): Required<SkillsSettings> {\n\t\treturn {\n\t\t\tenabled: this.settings.skills?.enabled ?? true,\n\t\t\tenableCodexUser: this.settings.skills?.enableCodexUser ?? true,\n\t\t\tenableClaudeUser: this.settings.skills?.enableClaudeUser ?? true,\n\t\t\tenableClaudeProject: this.settings.skills?.enableClaudeProject ?? true,\n\t\t\tenablePiUser: this.settings.skills?.enablePiUser ?? true,\n\t\t\tenablePiProject: this.settings.skills?.enablePiProject ?? true,\n\t\t\tenableSkillCommands: this.settings.skills?.enableSkillCommands ?? true,\n\t\t\tcustomDirectories: [...(this.settings.skills?.customDirectories ?? [])],\n\t\t\tignoredSkills: [...(this.settings.skills?.ignoredSkills ?? [])],\n\t\t\tincludeSkills: [...(this.settings.skills?.includeSkills ?? [])],\n\t\t};\n\t}\n\n\tgetEnableSkillCommands(): boolean {\n\t\treturn this.settings.skills?.enableSkillCommands ?? true;\n\t}\n\n\tsetEnableSkillCommands(enabled: boolean): void {\n\t\tif (!this.globalSettings.skills) {\n\t\t\tthis.globalSettings.skills = {};\n\t\t}\n\t\tthis.globalSettings.skills.enableSkillCommands = enabled;\n\t\tthis.save();\n\t}\n\n\tgetThinkingBudgets(): ThinkingBudgetsSettings | undefined {\n\t\treturn this.settings.thinkingBudgets;\n\t}\n\n\tgetShowImages(): boolean {\n\t\treturn this.settings.terminal?.showImages ?? true;\n\t}\n\n\tsetShowImages(show: boolean): void {\n\t\tif (!this.globalSettings.terminal) {\n\t\t\tthis.globalSettings.terminal = {};\n\t\t}\n\t\tthis.globalSettings.terminal.showImages = show;\n\t\tthis.save();\n\t}\n\n\tgetImageAutoResize(): boolean {\n\t\treturn this.settings.images?.autoResize ?? true;\n\t}\n\n\tsetImageAutoResize(enabled: boolean): void {\n\t\tif (!this.globalSettings.images) {\n\t\t\tthis.globalSettings.images = {};\n\t\t}\n\t\tthis.globalSettings.images.autoResize = enabled;\n\t\tthis.save();\n\t}\n\n\tgetBlockImages(): boolean {\n\t\treturn this.settings.images?.blockImages ?? false;\n\t}\n\n\tsetBlockImages(blocked: boolean): void {\n\t\tif (!this.globalSettings.images) {\n\t\t\tthis.globalSettings.images = {};\n\t\t}\n\t\tthis.globalSettings.images.blockImages = blocked;\n\t\tthis.save();\n\t}\n\n\tgetEnabledModels(): string[] | undefined {\n\t\treturn this.settings.enabledModels;\n\t}\n\n\tsetEnabledModels(patterns: string[] | undefined): void {\n\t\tthis.globalSettings.enabledModels = patterns;\n\t\tthis.save();\n\t}\n\n\tgetDoubleEscapeAction(): \"fork\" | \"tree\" {\n\t\treturn this.settings.doubleEscapeAction ?? \"tree\";\n\t}\n\n\tsetDoubleEscapeAction(action: \"fork\" | \"tree\"): void {\n\t\tthis.globalSettings.doubleEscapeAction = action;\n\t\tthis.save();\n\t}\n\n\tgetShowHardwareCursor(): boolean {\n\t\treturn this.settings.showHardwareCursor ?? process.env.PI_HARDWARE_CURSOR === \"1\";\n\t}\n\n\tsetShowHardwareCursor(enabled: boolean): void {\n\t\tthis.globalSettings.showHardwareCursor = enabled;\n\t\tthis.save();\n\t}\n\n\tgetEditorPaddingX(): number {\n\t\treturn this.settings.editorPaddingX ?? 0;\n\t}\n\n\tsetEditorPaddingX(padding: number): void {\n\t\tthis.globalSettings.editorPaddingX = Math.max(0, Math.min(3, Math.floor(padding)));\n\t\tthis.save();\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAqC,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAajD,mDAAmD;AACnD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAerG;AAqBD,MAAM,WAAW,uBAAuB;IACvC,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACtC,OAAO,GAAE,uBAA4B,GACnC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAyC1C;AAED,MAAM,WAAW,wBAAwB;IACxC,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,aAAa,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC3B,uCAAuC;IACvC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,qCAAqC;IACrC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,uDAAuD;IACvD,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CACjB;AAaD,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,GAAG,MAAM,CAkKhF","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport { PI_STATIC_INSTRUCTIONS } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport { getAgentDir, getReadmePath } from \"../config.js\";\nimport type { SkillsSettings } from \"./settings-manager.js\";\nimport { formatSkillsForPrompt, loadSkills, type Skill } from \"./skills.js\";\nimport type { ToolName } from \"./tools/index.js\";\n\n/** Tool descriptions for system prompt */\nconst toolDescriptions: Record<ToolName, string> = {\n\tread: \"Read file contents\",\n\tbash: \"Execute bash commands (ls, grep, find, etc.)\",\n\tedit: \"Make surgical edits to files (find exact text and replace)\",\n\twrite: \"Create or overwrite files\",\n\tgrep: \"Search file contents for patterns (respects .gitignore)\",\n\tfind: \"Find files by glob pattern (respects .gitignore)\",\n\tls: \"List directory contents\",\n};\n\n/** Resolve input as file path or literal string */\nexport function resolvePromptInput(input: string | undefined, description: string): string | undefined {\n\tif (!input) {\n\t\treturn undefined;\n\t}\n\n\tif (existsSync(input)) {\n\t\ttry {\n\t\t\treturn readFileSync(input, \"utf-8\");\n\t\t} catch (error) {\n\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${description} file ${input}: ${error}`));\n\t\t\treturn input;\n\t\t}\n\t}\n\n\treturn input;\n}\n\n/** Look for AGENTS.md or CLAUDE.md in a directory (prefers AGENTS.md) */\nfunction loadContextFileFromDir(dir: string): { path: string; content: string } | null {\n\tconst candidates = [\"AGENTS.md\", \"CLAUDE.md\"];\n\tfor (const filename of candidates) {\n\t\tconst filePath = join(dir, filename);\n\t\tif (existsSync(filePath)) {\n\t\t\ttry {\n\t\t\t\treturn {\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\tcontent: readFileSync(filePath, \"utf-8\"),\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${filePath}: ${error}`));\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\nexport interface LoadContextFilesOptions {\n\t/** Working directory to start walking up from. Default: process.cwd() */\n\tcwd?: string;\n\t/** Agent config directory for global context. Default: from getAgentDir() */\n\tagentDir?: string;\n}\n\n/**\n * Load all project context files in order:\n * 1. Global: agentDir/AGENTS.md or CLAUDE.md\n * 2. Parent directories (top-most first) down to cwd\n * Each returns {path, content} for separate messages\n */\nexport function loadProjectContextFiles(\n\toptions: LoadContextFilesOptions = {},\n): Array<{ path: string; content: string }> {\n\tconst resolvedCwd = options.cwd ?? process.cwd();\n\tconst resolvedAgentDir = options.agentDir ?? getAgentDir();\n\n\tconst contextFiles: Array<{ path: string; content: string }> = [];\n\tconst seenPaths = new Set<string>();\n\n\t// 1. Load global context from agentDir\n\tconst globalContext = loadContextFileFromDir(resolvedAgentDir);\n\tif (globalContext) {\n\t\tcontextFiles.push(globalContext);\n\t\tseenPaths.add(globalContext.path);\n\t}\n\n\t// 2. Walk up from cwd to root, collecting all context files\n\tconst ancestorContextFiles: Array<{ path: string; content: string }> = [];\n\n\tlet currentDir = resolvedCwd;\n\tconst root = resolve(\"/\");\n\n\twhile (true) {\n\t\tconst contextFile = loadContextFileFromDir(currentDir);\n\t\tif (contextFile && !seenPaths.has(contextFile.path)) {\n\t\t\t// Add to beginning so we get top-most parent first\n\t\t\tancestorContextFiles.unshift(contextFile);\n\t\t\tseenPaths.add(contextFile.path);\n\t\t}\n\n\t\t// Stop if we've reached root\n\t\tif (currentDir === root) break;\n\n\t\t// Move up one directory\n\t\tconst parentDir = resolve(currentDir, \"..\");\n\t\tif (parentDir === currentDir) break; // Safety check\n\t\tcurrentDir = parentDir;\n\t}\n\n\t// Add ancestor files in order (top-most → cwd)\n\tcontextFiles.push(...ancestorContextFiles);\n\n\treturn contextFiles;\n}\n\nexport interface BuildSystemPromptOptions {\n\t/** Custom system prompt (replaces default). */\n\tcustomPrompt?: string;\n\t/** Tools to include in prompt. Default: [read, bash, edit, write] */\n\tselectedTools?: ToolName[];\n\t/** Text to append to system prompt. */\n\tappendSystemPrompt?: string;\n\t/** Skills settings for discovery. */\n\tskillsSettings?: SkillsSettings;\n\t/** Working directory. Default: process.cwd() */\n\tcwd?: string;\n\t/** Agent config directory. Default: from getAgentDir() */\n\tagentDir?: string;\n\t/** Pre-loaded context files (skips discovery if provided). */\n\tcontextFiles?: Array<{ path: string; content: string }>;\n\t/** Pre-loaded skills (skips discovery if provided). */\n\tskills?: Skill[];\n}\n\n/**\n * Get the Pi installation path for documentation references.\n * This resolves the pi-internal:// scheme used in the static instructions.\n */\nfunction getPiPath(): string {\n\t// getReadmePath returns something like /path/to/pi/README.md\n\t// We want the parent directory\n\tconst readmePath = getReadmePath();\n\treturn resolve(readmePath, \"..\");\n}\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions = {}): string {\n\tconst {\n\t\tcustomPrompt,\n\t\tselectedTools,\n\t\tappendSystemPrompt,\n\t\tskillsSettings,\n\t\tcwd,\n\t\tagentDir,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst resolvedCwd = cwd ?? process.cwd();\n\tconst resolvedCustomPrompt = resolvePromptInput(customPrompt, \"system prompt\");\n\tconst resolvedAppendPrompt = resolvePromptInput(appendSystemPrompt, \"append system prompt\");\n\n\tconst now = new Date();\n\tconst dateTime = now.toLocaleString(\"en-US\", {\n\t\tweekday: \"long\",\n\t\tyear: \"numeric\",\n\t\tmonth: \"long\",\n\t\tday: \"numeric\",\n\t\thour: \"2-digit\",\n\t\tminute: \"2-digit\",\n\t\tsecond: \"2-digit\",\n\t\ttimeZoneName: \"short\",\n\t});\n\n\tconst appendSection = resolvedAppendPrompt ? `\\n\\n${resolvedAppendPrompt}` : \"\";\n\n\t// Resolve context files: use provided or discover\n\tconst contextFiles = providedContextFiles ?? loadProjectContextFiles({ cwd: resolvedCwd, agentDir });\n\n\t// Resolve skills: use provided or discover\n\tconst skills =\n\t\tprovidedSkills ??\n\t\t(skillsSettings?.enabled !== false ? loadSkills({ ...skillsSettings, cwd: resolvedCwd, agentDir }).skills : []);\n\n\t// Handle custom prompt (full replacement)\n\tif (resolvedCustomPrompt) {\n\t\tlet prompt = resolvedCustomPrompt;\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\t\tprompt += \"The following project context files have been loaded:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t\t}\n\t\t}\n\n\t\t// Append skills section (only if read tool is available)\n\t\tconst customPromptHasRead = !selectedTools || selectedTools.includes(\"read\");\n\t\tif (customPromptHasRead && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills);\n\t\t}\n\n\t\t// Add date/time and working directory last\n\t\tprompt += `\\nCurrent date and time: ${dateTime}`;\n\t\tprompt += `\\nCurrent working directory: ${resolvedCwd}`;\n\n\t\treturn prompt;\n\t}\n\n\t// Build tools list based on selected tools\n\tconst tools = selectedTools || ([\"read\", \"bash\", \"edit\", \"write\"] as ToolName[]);\n\tconst toolsList = tools.length > 0 ? tools.map((t) => `- ${t}: ${toolDescriptions[t]}`).join(\"\\n\") : \"(none)\";\n\n\t// Build guidelines based on which tools are actually available\n\tconst guidelinesList: string[] = [];\n\n\tconst hasBash = tools.includes(\"bash\");\n\tconst hasEdit = tools.includes(\"edit\");\n\tconst hasWrite = tools.includes(\"write\");\n\tconst hasGrep = tools.includes(\"grep\");\n\tconst hasFind = tools.includes(\"find\");\n\tconst hasLs = tools.includes(\"ls\");\n\tconst hasRead = tools.includes(\"read\");\n\n\t// Bash without edit/write = read-only bash mode\n\tif (hasBash && !hasEdit && !hasWrite) {\n\t\tguidelinesList.push(\n\t\t\t\"Use bash ONLY for read-only operations (git log, gh issue view, curl, etc.) - do NOT modify any files\",\n\t\t);\n\t}\n\n\t// File exploration guidelines\n\tif (hasBash && !hasGrep && !hasFind && !hasLs) {\n\t\tguidelinesList.push(\"Use bash for file operations like ls, grep, find\");\n\t} else if (hasBash && (hasGrep || hasFind || hasLs)) {\n\t\tguidelinesList.push(\"Prefer grep/find/ls tools over bash for file exploration (faster, respects .gitignore)\");\n\t}\n\n\t// Read before edit guideline\n\tif (hasRead && hasEdit) {\n\t\tguidelinesList.push(\"Use read to examine files before editing. You must use this tool instead of cat or sed.\");\n\t}\n\n\t// Edit guideline\n\tif (hasEdit) {\n\t\tguidelinesList.push(\"Use edit for precise changes (old text must match exactly)\");\n\t}\n\n\t// Write guideline\n\tif (hasWrite) {\n\t\tguidelinesList.push(\"Use write only for new files or complete rewrites\");\n\t}\n\n\t// Output guideline (only when actually writing/executing)\n\tif (hasEdit || hasWrite) {\n\t\tguidelinesList.push(\n\t\t\t\"When summarizing your actions, output plain text directly - do NOT use cat or bash to display what you did\",\n\t\t);\n\t}\n\n\t// Always include these\n\tguidelinesList.push(\"Be concise in your responses\");\n\tguidelinesList.push(\"Show file paths clearly when working with files\");\n\n\tconst guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n\t// Build prompt with static prefix + dynamic parts\n\tconst piPath = getPiPath();\n\n\tlet prompt = `${PI_STATIC_INSTRUCTIONS}\nPi path:\npi-internal:// refers to paths in ${piPath}\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\tprompt += \"The following project context files have been loaded:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t}\n\t}\n\n\t// Append skills section (only if read tool is available)\n\tif (hasRead && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills);\n\t}\n\n\t// Add date/time and working directory last\n\tprompt += `\\nCurrent date and time: ${dateTime}`;\n\tprompt += `\\nCurrent working directory: ${resolvedCwd}`;\n\n\treturn prompt;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAqC,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAC5E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAajD,mDAAmD;AACnD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAerG;AAqBD,MAAM,WAAW,uBAAuB;IACvC,yEAAyE;IACzE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACtC,OAAO,GAAE,uBAA4B,GACnC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAyC1C;AAED,MAAM,WAAW,wBAAwB;IACxC,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qEAAqE;IACrE,aAAa,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC3B,uCAAuC;IACvC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,qCAAqC;IACrC,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,gDAAgD;IAChD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,uDAAuD;IACvD,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,wBAA6B,GAAG,MAAM,CAwKhF","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport chalk from \"chalk\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport { getAgentDir, getDocsPath, getExamplesPath, getReadmePath } from \"../config.js\";\nimport type { SkillsSettings } from \"./settings-manager.js\";\nimport { formatSkillsForPrompt, loadSkills, type Skill } from \"./skills.js\";\nimport type { ToolName } from \"./tools/index.js\";\n\n/** Tool descriptions for system prompt */\nconst toolDescriptions: Record<ToolName, string> = {\n\tread: \"Read file contents\",\n\tbash: \"Execute bash commands (ls, grep, find, etc.)\",\n\tedit: \"Make surgical edits to files (find exact text and replace)\",\n\twrite: \"Create or overwrite files\",\n\tgrep: \"Search file contents for patterns (respects .gitignore)\",\n\tfind: \"Find files by glob pattern (respects .gitignore)\",\n\tls: \"List directory contents\",\n};\n\n/** Resolve input as file path or literal string */\nexport function resolvePromptInput(input: string | undefined, description: string): string | undefined {\n\tif (!input) {\n\t\treturn undefined;\n\t}\n\n\tif (existsSync(input)) {\n\t\ttry {\n\t\t\treturn readFileSync(input, \"utf-8\");\n\t\t} catch (error) {\n\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${description} file ${input}: ${error}`));\n\t\t\treturn input;\n\t\t}\n\t}\n\n\treturn input;\n}\n\n/** Look for AGENTS.md or CLAUDE.md in a directory (prefers AGENTS.md) */\nfunction loadContextFileFromDir(dir: string): { path: string; content: string } | null {\n\tconst candidates = [\"AGENTS.md\", \"CLAUDE.md\"];\n\tfor (const filename of candidates) {\n\t\tconst filePath = join(dir, filename);\n\t\tif (existsSync(filePath)) {\n\t\t\ttry {\n\t\t\t\treturn {\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\tcontent: readFileSync(filePath, \"utf-8\"),\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${filePath}: ${error}`));\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\nexport interface LoadContextFilesOptions {\n\t/** Working directory to start walking up from. Default: process.cwd() */\n\tcwd?: string;\n\t/** Agent config directory for global context. Default: from getAgentDir() */\n\tagentDir?: string;\n}\n\n/**\n * Load all project context files in order:\n * 1. Global: agentDir/AGENTS.md or CLAUDE.md\n * 2. Parent directories (top-most first) down to cwd\n * Each returns {path, content} for separate messages\n */\nexport function loadProjectContextFiles(\n\toptions: LoadContextFilesOptions = {},\n): Array<{ path: string; content: string }> {\n\tconst resolvedCwd = options.cwd ?? process.cwd();\n\tconst resolvedAgentDir = options.agentDir ?? getAgentDir();\n\n\tconst contextFiles: Array<{ path: string; content: string }> = [];\n\tconst seenPaths = new Set<string>();\n\n\t// 1. Load global context from agentDir\n\tconst globalContext = loadContextFileFromDir(resolvedAgentDir);\n\tif (globalContext) {\n\t\tcontextFiles.push(globalContext);\n\t\tseenPaths.add(globalContext.path);\n\t}\n\n\t// 2. Walk up from cwd to root, collecting all context files\n\tconst ancestorContextFiles: Array<{ path: string; content: string }> = [];\n\n\tlet currentDir = resolvedCwd;\n\tconst root = resolve(\"/\");\n\n\twhile (true) {\n\t\tconst contextFile = loadContextFileFromDir(currentDir);\n\t\tif (contextFile && !seenPaths.has(contextFile.path)) {\n\t\t\t// Add to beginning so we get top-most parent first\n\t\t\tancestorContextFiles.unshift(contextFile);\n\t\t\tseenPaths.add(contextFile.path);\n\t\t}\n\n\t\t// Stop if we've reached root\n\t\tif (currentDir === root) break;\n\n\t\t// Move up one directory\n\t\tconst parentDir = resolve(currentDir, \"..\");\n\t\tif (parentDir === currentDir) break; // Safety check\n\t\tcurrentDir = parentDir;\n\t}\n\n\t// Add ancestor files in order (top-most → cwd)\n\tcontextFiles.push(...ancestorContextFiles);\n\n\treturn contextFiles;\n}\n\nexport interface BuildSystemPromptOptions {\n\t/** Custom system prompt (replaces default). */\n\tcustomPrompt?: string;\n\t/** Tools to include in prompt. Default: [read, bash, edit, write] */\n\tselectedTools?: ToolName[];\n\t/** Text to append to system prompt. */\n\tappendSystemPrompt?: string;\n\t/** Skills settings for discovery. */\n\tskillsSettings?: SkillsSettings;\n\t/** Working directory. Default: process.cwd() */\n\tcwd?: string;\n\t/** Agent config directory. Default: from getAgentDir() */\n\tagentDir?: string;\n\t/** Pre-loaded context files (skips discovery if provided). */\n\tcontextFiles?: Array<{ path: string; content: string }>;\n\t/** Pre-loaded skills (skips discovery if provided). */\n\tskills?: Skill[];\n}\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions = {}): string {\n\tconst {\n\t\tcustomPrompt,\n\t\tselectedTools,\n\t\tappendSystemPrompt,\n\t\tskillsSettings,\n\t\tcwd,\n\t\tagentDir,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst resolvedCwd = cwd ?? process.cwd();\n\tconst resolvedCustomPrompt = resolvePromptInput(customPrompt, \"system prompt\");\n\tconst resolvedAppendPrompt = resolvePromptInput(appendSystemPrompt, \"append system prompt\");\n\n\tconst now = new Date();\n\tconst dateTime = now.toLocaleString(\"en-US\", {\n\t\tweekday: \"long\",\n\t\tyear: \"numeric\",\n\t\tmonth: \"long\",\n\t\tday: \"numeric\",\n\t\thour: \"2-digit\",\n\t\tminute: \"2-digit\",\n\t\tsecond: \"2-digit\",\n\t\ttimeZoneName: \"short\",\n\t});\n\n\tconst appendSection = resolvedAppendPrompt ? `\\n\\n${resolvedAppendPrompt}` : \"\";\n\n\t// Resolve context files: use provided or discover\n\tconst contextFiles = providedContextFiles ?? loadProjectContextFiles({ cwd: resolvedCwd, agentDir });\n\n\t// Resolve skills: use provided or discover\n\tconst skills =\n\t\tprovidedSkills ??\n\t\t(skillsSettings?.enabled !== false ? loadSkills({ ...skillsSettings, cwd: resolvedCwd, agentDir }).skills : []);\n\n\tif (resolvedCustomPrompt) {\n\t\tlet prompt = resolvedCustomPrompt;\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t\t}\n\t\t}\n\n\t\t// Append skills section (only if read tool is available)\n\t\tconst customPromptHasRead = !selectedTools || selectedTools.includes(\"read\");\n\t\tif (customPromptHasRead && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills);\n\t\t}\n\n\t\t// Add date/time and working directory last\n\t\tprompt += `\\nCurrent date and time: ${dateTime}`;\n\t\tprompt += `\\nCurrent working directory: ${resolvedCwd}`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\t// Build tools list based on selected tools\n\tconst tools = selectedTools || ([\"read\", \"bash\", \"edit\", \"write\"] as ToolName[]);\n\tconst toolsList = tools.length > 0 ? tools.map((t) => `- ${t}: ${toolDescriptions[t]}`).join(\"\\n\") : \"(none)\";\n\n\t// Build guidelines based on which tools are actually available\n\tconst guidelinesList: string[] = [];\n\n\tconst hasBash = tools.includes(\"bash\");\n\tconst hasEdit = tools.includes(\"edit\");\n\tconst hasWrite = tools.includes(\"write\");\n\tconst hasGrep = tools.includes(\"grep\");\n\tconst hasFind = tools.includes(\"find\");\n\tconst hasLs = tools.includes(\"ls\");\n\tconst hasRead = tools.includes(\"read\");\n\n\t// Bash without edit/write = read-only bash mode\n\tif (hasBash && !hasEdit && !hasWrite) {\n\t\tguidelinesList.push(\n\t\t\t\"Use bash ONLY for read-only operations (git log, gh issue view, curl, etc.) - do NOT modify any files\",\n\t\t);\n\t}\n\n\t// File exploration guidelines\n\tif (hasBash && !hasGrep && !hasFind && !hasLs) {\n\t\tguidelinesList.push(\"Use bash for file operations like ls, grep, find\");\n\t} else if (hasBash && (hasGrep || hasFind || hasLs)) {\n\t\tguidelinesList.push(\"Prefer grep/find/ls tools over bash for file exploration (faster, respects .gitignore)\");\n\t}\n\n\t// Read before edit guideline\n\tif (hasRead && hasEdit) {\n\t\tguidelinesList.push(\"Use read to examine files before editing. You must use this tool instead of cat or sed.\");\n\t}\n\n\t// Edit guideline\n\tif (hasEdit) {\n\t\tguidelinesList.push(\"Use edit for precise changes (old text must match exactly)\");\n\t}\n\n\t// Write guideline\n\tif (hasWrite) {\n\t\tguidelinesList.push(\"Use write only for new files or complete rewrites\");\n\t}\n\n\t// Output guideline (only when actually writing/executing)\n\tif (hasEdit || hasWrite) {\n\t\tguidelinesList.push(\n\t\t\t\"When summarizing your actions, output plain text directly - do NOT use cat or bash to display what you did\",\n\t\t);\n\t}\n\n\t// Always include these\n\tguidelinesList.push(\"Be concise in your responses\");\n\tguidelinesList.push(\"Show file paths clearly when working with files\");\n\n\tconst guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n\tlet prompt = `You are an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}\n\nPi documentation (only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):\n- Main documentation: ${readmePath}\n- Additional docs: ${docsPath}\n- Examples: ${examplesPath} (extensions, custom tools, SDK)\n- When asked to create: custom models/providers (README.md), extensions (docs/extensions.md, examples/extensions/), themes (docs/theme.md), skills (docs/skills.md), TUI components (docs/tui.md - has copy-paste patterns)\n- When working on pi topics, read the docs and examples, and follow .md cross-references before implementing`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t}\n\t}\n\n\t// Append skills section (only if read tool is available)\n\tif (hasRead && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills);\n\t}\n\n\t// Add date/time and working directory last\n\tprompt += `\\nCurrent date and time: ${dateTime}`;\n\tprompt += `\\nCurrent working directory: ${resolvedCwd}`;\n\n\treturn prompt;\n}\n"]}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* System prompt construction and project context loading
|
|
3
3
|
*/
|
|
4
|
-
import { PI_STATIC_INSTRUCTIONS } from "@mariozechner/pi-ai";
|
|
5
4
|
import chalk from "chalk";
|
|
6
5
|
import { existsSync, readFileSync } from "fs";
|
|
7
6
|
import { join, resolve } from "path";
|
|
8
|
-
import { getAgentDir, getReadmePath } from "../config.js";
|
|
7
|
+
import { getAgentDir, getDocsPath, getExamplesPath, getReadmePath } from "../config.js";
|
|
9
8
|
import { formatSkillsForPrompt, loadSkills } from "./skills.js";
|
|
10
9
|
/** Tool descriptions for system prompt */
|
|
11
10
|
const toolDescriptions = {
|
|
@@ -93,16 +92,6 @@ export function loadProjectContextFiles(options = {}) {
|
|
|
93
92
|
contextFiles.push(...ancestorContextFiles);
|
|
94
93
|
return contextFiles;
|
|
95
94
|
}
|
|
96
|
-
/**
|
|
97
|
-
* Get the Pi installation path for documentation references.
|
|
98
|
-
* This resolves the pi-internal:// scheme used in the static instructions.
|
|
99
|
-
*/
|
|
100
|
-
function getPiPath() {
|
|
101
|
-
// getReadmePath returns something like /path/to/pi/README.md
|
|
102
|
-
// We want the parent directory
|
|
103
|
-
const readmePath = getReadmePath();
|
|
104
|
-
return resolve(readmePath, "..");
|
|
105
|
-
}
|
|
106
95
|
/** Build the system prompt with tools, guidelines, and context */
|
|
107
96
|
export function buildSystemPrompt(options = {}) {
|
|
108
97
|
const { customPrompt, selectedTools, appendSystemPrompt, skillsSettings, cwd, agentDir, contextFiles: providedContextFiles, skills: providedSkills, } = options;
|
|
@@ -126,7 +115,6 @@ export function buildSystemPrompt(options = {}) {
|
|
|
126
115
|
// Resolve skills: use provided or discover
|
|
127
116
|
const skills = providedSkills ??
|
|
128
117
|
(skillsSettings?.enabled !== false ? loadSkills({ ...skillsSettings, cwd: resolvedCwd, agentDir }).skills : []);
|
|
129
|
-
// Handle custom prompt (full replacement)
|
|
130
118
|
if (resolvedCustomPrompt) {
|
|
131
119
|
let prompt = resolvedCustomPrompt;
|
|
132
120
|
if (appendSection) {
|
|
@@ -135,7 +123,7 @@ export function buildSystemPrompt(options = {}) {
|
|
|
135
123
|
// Append project context files
|
|
136
124
|
if (contextFiles.length > 0) {
|
|
137
125
|
prompt += "\n\n# Project Context\n\n";
|
|
138
|
-
prompt += "
|
|
126
|
+
prompt += "Project-specific instructions and guidelines:\n\n";
|
|
139
127
|
for (const { path: filePath, content } of contextFiles) {
|
|
140
128
|
prompt += `## ${filePath}\n\n${content}\n\n`;
|
|
141
129
|
}
|
|
@@ -150,6 +138,10 @@ export function buildSystemPrompt(options = {}) {
|
|
|
150
138
|
prompt += `\nCurrent working directory: ${resolvedCwd}`;
|
|
151
139
|
return prompt;
|
|
152
140
|
}
|
|
141
|
+
// Get absolute paths to documentation and examples
|
|
142
|
+
const readmePath = getReadmePath();
|
|
143
|
+
const docsPath = getDocsPath();
|
|
144
|
+
const examplesPath = getExamplesPath();
|
|
153
145
|
// Build tools list based on selected tools
|
|
154
146
|
const tools = selectedTools || ["read", "bash", "edit", "write"];
|
|
155
147
|
const toolsList = tools.length > 0 ? tools.map((t) => `- ${t}: ${toolDescriptions[t]}`).join("\n") : "(none)";
|
|
@@ -193,11 +185,7 @@ export function buildSystemPrompt(options = {}) {
|
|
|
193
185
|
guidelinesList.push("Be concise in your responses");
|
|
194
186
|
guidelinesList.push("Show file paths clearly when working with files");
|
|
195
187
|
const guidelines = guidelinesList.map((g) => `- ${g}`).join("\n");
|
|
196
|
-
|
|
197
|
-
const piPath = getPiPath();
|
|
198
|
-
let prompt = `${PI_STATIC_INSTRUCTIONS}
|
|
199
|
-
Pi path:
|
|
200
|
-
pi-internal:// refers to paths in ${piPath}
|
|
188
|
+
let prompt = `You are an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.
|
|
201
189
|
|
|
202
190
|
Available tools:
|
|
203
191
|
${toolsList}
|
|
@@ -205,14 +193,21 @@ ${toolsList}
|
|
|
205
193
|
In addition to the tools above, you may have access to other custom tools depending on the project.
|
|
206
194
|
|
|
207
195
|
Guidelines:
|
|
208
|
-
${guidelines}
|
|
196
|
+
${guidelines}
|
|
197
|
+
|
|
198
|
+
Pi documentation (only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):
|
|
199
|
+
- Main documentation: ${readmePath}
|
|
200
|
+
- Additional docs: ${docsPath}
|
|
201
|
+
- Examples: ${examplesPath} (extensions, custom tools, SDK)
|
|
202
|
+
- When asked to create: custom models/providers (README.md), extensions (docs/extensions.md, examples/extensions/), themes (docs/theme.md), skills (docs/skills.md), TUI components (docs/tui.md - has copy-paste patterns)
|
|
203
|
+
- When working on pi topics, read the docs and examples, and follow .md cross-references before implementing`;
|
|
209
204
|
if (appendSection) {
|
|
210
205
|
prompt += appendSection;
|
|
211
206
|
}
|
|
212
207
|
// Append project context files
|
|
213
208
|
if (contextFiles.length > 0) {
|
|
214
209
|
prompt += "\n\n# Project Context\n\n";
|
|
215
|
-
prompt += "
|
|
210
|
+
prompt += "Project-specific instructions and guidelines:\n\n";
|
|
216
211
|
for (const { path: filePath, content } of contextFiles) {
|
|
217
212
|
prompt += `## ${filePath}\n\n${content}\n\n`;
|
|
218
213
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE1D,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAc,MAAM,aAAa,CAAC;AAG5E,0CAA0C;AAC1C,MAAM,gBAAgB,GAA6B;IAClD,IAAI,EAAE,oBAAoB;IAC1B,IAAI,EAAE,8CAA8C;IACpD,IAAI,EAAE,4DAA4D;IAClE,KAAK,EAAE,2BAA2B;IAClC,IAAI,EAAE,yDAAyD;IAC/D,IAAI,EAAE,kDAAkD;IACxD,EAAE,EAAE,yBAAyB;CAC7B,CAAC;AAEF,mDAAmD;AACnD,MAAM,UAAU,kBAAkB,CAAC,KAAyB,EAAE,WAAmB,EAAsB;IACtG,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC;YACJ,OAAO,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,2BAA2B,WAAW,SAAS,KAAK,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC;YAC9F,OAAO,KAAK,CAAC;QACd,CAAC;IACF,CAAC;IAED,OAAO,KAAK,CAAC;AAAA,CACb;AAED,yEAAyE;AACzE,SAAS,sBAAsB,CAAC,GAAW,EAA4C;IACtF,MAAM,UAAU,GAAG,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAC9C,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QACrC,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1B,IAAI,CAAC;gBACJ,OAAO;oBACN,IAAI,EAAE,QAAQ;oBACd,OAAO,EAAE,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC;iBACxC,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,2BAA2B,QAAQ,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC;YAC9E,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AAAA,CACZ;AASD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACtC,OAAO,GAA4B,EAAE,EACM;IAC3C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACjD,MAAM,gBAAgB,GAAG,OAAO,CAAC,QAAQ,IAAI,WAAW,EAAE,CAAC;IAE3D,MAAM,YAAY,GAA6C,EAAE,CAAC;IAClE,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IAEpC,uCAAuC;IACvC,MAAM,aAAa,GAAG,sBAAsB,CAAC,gBAAgB,CAAC,CAAC;IAC/D,IAAI,aAAa,EAAE,CAAC;QACnB,YAAY,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QACjC,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,4DAA4D;IAC5D,MAAM,oBAAoB,GAA6C,EAAE,CAAC;IAE1E,IAAI,UAAU,GAAG,WAAW,CAAC;IAC7B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAE1B,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,WAAW,GAAG,sBAAsB,CAAC,UAAU,CAAC,CAAC;QACvD,IAAI,WAAW,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;YACrD,mDAAmD;YACnD,oBAAoB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YAC1C,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;QACjC,CAAC;QAED,6BAA6B;QAC7B,IAAI,UAAU,KAAK,IAAI;YAAE,MAAM;QAE/B,wBAAwB;QACxB,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QAC5C,IAAI,SAAS,KAAK,UAAU;YAAE,MAAM,CAAC,eAAe;QACpD,UAAU,GAAG,SAAS,CAAC;IACxB,CAAC;IAED,iDAA+C;IAC/C,YAAY,CAAC,IAAI,CAAC,GAAG,oBAAoB,CAAC,CAAC;IAE3C,OAAO,YAAY,CAAC;AAAA,CACpB;AAqBD;;;GAGG;AACH,SAAS,SAAS,GAAW;IAC5B,6DAA6D;IAC7D,+BAA+B;IAC/B,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IACnC,OAAO,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;AAAA,CACjC;AAED,kEAAkE;AAClE,MAAM,UAAU,iBAAiB,CAAC,OAAO,GAA6B,EAAE,EAAU;IACjF,MAAM,EACL,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,GAAG,EACH,QAAQ,EACR,YAAY,EAAE,oBAAoB,EAClC,MAAM,EAAE,cAAc,GACtB,GAAG,OAAO,CAAC;IACZ,MAAM,WAAW,GAAG,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,oBAAoB,GAAG,kBAAkB,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;IAC/E,MAAM,oBAAoB,GAAG,kBAAkB,CAAC,kBAAkB,EAAE,sBAAsB,CAAC,CAAC;IAE5F,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,QAAQ,GAAG,GAAG,CAAC,cAAc,CAAC,OAAO,EAAE;QAC5C,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,MAAM;QACb,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,SAAS;QACjB,YAAY,EAAE,OAAO;KACrB,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,oBAAoB,CAAC,CAAC,CAAC,OAAO,oBAAoB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAEhF,kDAAkD;IAClD,MAAM,YAAY,GAAG,oBAAoB,IAAI,uBAAuB,CAAC,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC;IAErG,2CAA2C;IAC3C,MAAM,MAAM,GACX,cAAc;QACd,CAAC,cAAc,EAAE,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,GAAG,cAAc,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAEjH,0CAA0C;IAC1C,IAAI,oBAAoB,EAAE,CAAC;QAC1B,IAAI,MAAM,GAAG,oBAAoB,CAAC;QAElC,IAAI,aAAa,EAAE,CAAC;YACnB,MAAM,IAAI,aAAa,CAAC;QACzB,CAAC;QAED,+BAA+B;QAC/B,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,2BAA2B,CAAC;YACtC,MAAM,IAAI,2DAA2D,CAAC;YACtE,KAAK,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,YAAY,EAAE,CAAC;gBACxD,MAAM,IAAI,MAAM,QAAQ,OAAO,OAAO,MAAM,CAAC;YAC9C,CAAC;QACF,CAAC;QAED,yDAAyD;QACzD,MAAM,mBAAmB,GAAG,CAAC,aAAa,IAAI,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC7E,IAAI,mBAAmB,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,qBAAqB,CAAC,MAAM,CAAC,CAAC;QACzC,CAAC;QAED,2CAA2C;QAC3C,MAAM,IAAI,4BAA4B,QAAQ,EAAE,CAAC;QACjD,MAAM,IAAI,gCAAgC,WAAW,EAAE,CAAC;QAExD,OAAO,MAAM,CAAC;IACf,CAAC;IAED,2CAA2C;IAC3C,MAAM,KAAK,GAAG,aAAa,IAAK,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAgB,CAAC;IACjF,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAE9G,+DAA+D;IAC/D,MAAM,cAAc,GAAa,EAAE,CAAC;IAEpC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IACzC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEvC,gDAAgD;IAChD,IAAI,OAAO,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ,EAAE,CAAC;QACtC,cAAc,CAAC,IAAI,CAClB,uGAAuG,CACvG,CAAC;IACH,CAAC;IAED,8BAA8B;IAC9B,IAAI,OAAO,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;QAC/C,cAAc,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;IACzE,CAAC;SAAM,IAAI,OAAO,IAAI,CAAC,OAAO,IAAI,OAAO,IAAI,KAAK,CAAC,EAAE,CAAC;QACrD,cAAc,CAAC,IAAI,CAAC,wFAAwF,CAAC,CAAC;IAC/G,CAAC;IAED,6BAA6B;IAC7B,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;QACxB,cAAc,CAAC,IAAI,CAAC,yFAAyF,CAAC,CAAC;IAChH,CAAC;IAED,iBAAiB;IACjB,IAAI,OAAO,EAAE,CAAC;QACb,cAAc,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC;IACnF,CAAC;IAED,kBAAkB;IAClB,IAAI,QAAQ,EAAE,CAAC;QACd,cAAc,CAAC,IAAI,CAAC,mDAAmD,CAAC,CAAC;IAC1E,CAAC;IAED,0DAA0D;IAC1D,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC;QACzB,cAAc,CAAC,IAAI,CAClB,4GAA4G,CAC5G,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,cAAc,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;IACpD,cAAc,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;IAEvE,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAElE,kDAAkD;IAClD,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,IAAI,MAAM,GAAG,GAAG,sBAAsB;;oCAEH,MAAM;;;EAGxC,SAAS;;;;;EAKT,UAAU,EAAE,CAAC;IAEd,IAAI,aAAa,EAAE,CAAC;QACnB,MAAM,IAAI,aAAa,CAAC;IACzB,CAAC;IAED,+BAA+B;IAC/B,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,2BAA2B,CAAC;QACtC,MAAM,IAAI,2DAA2D,CAAC;QACtE,KAAK,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,YAAY,EAAE,CAAC;YACxD,MAAM,IAAI,MAAM,QAAQ,OAAO,OAAO,MAAM,CAAC;QAC9C,CAAC;IACF,CAAC;IAED,yDAAyD;IACzD,IAAI,OAAO,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IAED,2CAA2C;IAC3C,MAAM,IAAI,4BAA4B,QAAQ,EAAE,CAAC;IACjD,MAAM,IAAI,gCAAgC,WAAW,EAAE,CAAC;IAExD,OAAO,MAAM,CAAC;AAAA,CACd","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport { PI_STATIC_INSTRUCTIONS } from \"@mariozechner/pi-ai\";\nimport chalk from \"chalk\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport { getAgentDir, getReadmePath } from \"../config.js\";\nimport type { SkillsSettings } from \"./settings-manager.js\";\nimport { formatSkillsForPrompt, loadSkills, type Skill } from \"./skills.js\";\nimport type { ToolName } from \"./tools/index.js\";\n\n/** Tool descriptions for system prompt */\nconst toolDescriptions: Record<ToolName, string> = {\n\tread: \"Read file contents\",\n\tbash: \"Execute bash commands (ls, grep, find, etc.)\",\n\tedit: \"Make surgical edits to files (find exact text and replace)\",\n\twrite: \"Create or overwrite files\",\n\tgrep: \"Search file contents for patterns (respects .gitignore)\",\n\tfind: \"Find files by glob pattern (respects .gitignore)\",\n\tls: \"List directory contents\",\n};\n\n/** Resolve input as file path or literal string */\nexport function resolvePromptInput(input: string | undefined, description: string): string | undefined {\n\tif (!input) {\n\t\treturn undefined;\n\t}\n\n\tif (existsSync(input)) {\n\t\ttry {\n\t\t\treturn readFileSync(input, \"utf-8\");\n\t\t} catch (error) {\n\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${description} file ${input}: ${error}`));\n\t\t\treturn input;\n\t\t}\n\t}\n\n\treturn input;\n}\n\n/** Look for AGENTS.md or CLAUDE.md in a directory (prefers AGENTS.md) */\nfunction loadContextFileFromDir(dir: string): { path: string; content: string } | null {\n\tconst candidates = [\"AGENTS.md\", \"CLAUDE.md\"];\n\tfor (const filename of candidates) {\n\t\tconst filePath = join(dir, filename);\n\t\tif (existsSync(filePath)) {\n\t\t\ttry {\n\t\t\t\treturn {\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\tcontent: readFileSync(filePath, \"utf-8\"),\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${filePath}: ${error}`));\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\nexport interface LoadContextFilesOptions {\n\t/** Working directory to start walking up from. Default: process.cwd() */\n\tcwd?: string;\n\t/** Agent config directory for global context. Default: from getAgentDir() */\n\tagentDir?: string;\n}\n\n/**\n * Load all project context files in order:\n * 1. Global: agentDir/AGENTS.md or CLAUDE.md\n * 2. Parent directories (top-most first) down to cwd\n * Each returns {path, content} for separate messages\n */\nexport function loadProjectContextFiles(\n\toptions: LoadContextFilesOptions = {},\n): Array<{ path: string; content: string }> {\n\tconst resolvedCwd = options.cwd ?? process.cwd();\n\tconst resolvedAgentDir = options.agentDir ?? getAgentDir();\n\n\tconst contextFiles: Array<{ path: string; content: string }> = [];\n\tconst seenPaths = new Set<string>();\n\n\t// 1. Load global context from agentDir\n\tconst globalContext = loadContextFileFromDir(resolvedAgentDir);\n\tif (globalContext) {\n\t\tcontextFiles.push(globalContext);\n\t\tseenPaths.add(globalContext.path);\n\t}\n\n\t// 2. Walk up from cwd to root, collecting all context files\n\tconst ancestorContextFiles: Array<{ path: string; content: string }> = [];\n\n\tlet currentDir = resolvedCwd;\n\tconst root = resolve(\"/\");\n\n\twhile (true) {\n\t\tconst contextFile = loadContextFileFromDir(currentDir);\n\t\tif (contextFile && !seenPaths.has(contextFile.path)) {\n\t\t\t// Add to beginning so we get top-most parent first\n\t\t\tancestorContextFiles.unshift(contextFile);\n\t\t\tseenPaths.add(contextFile.path);\n\t\t}\n\n\t\t// Stop if we've reached root\n\t\tif (currentDir === root) break;\n\n\t\t// Move up one directory\n\t\tconst parentDir = resolve(currentDir, \"..\");\n\t\tif (parentDir === currentDir) break; // Safety check\n\t\tcurrentDir = parentDir;\n\t}\n\n\t// Add ancestor files in order (top-most → cwd)\n\tcontextFiles.push(...ancestorContextFiles);\n\n\treturn contextFiles;\n}\n\nexport interface BuildSystemPromptOptions {\n\t/** Custom system prompt (replaces default). */\n\tcustomPrompt?: string;\n\t/** Tools to include in prompt. Default: [read, bash, edit, write] */\n\tselectedTools?: ToolName[];\n\t/** Text to append to system prompt. */\n\tappendSystemPrompt?: string;\n\t/** Skills settings for discovery. */\n\tskillsSettings?: SkillsSettings;\n\t/** Working directory. Default: process.cwd() */\n\tcwd?: string;\n\t/** Agent config directory. Default: from getAgentDir() */\n\tagentDir?: string;\n\t/** Pre-loaded context files (skips discovery if provided). */\n\tcontextFiles?: Array<{ path: string; content: string }>;\n\t/** Pre-loaded skills (skips discovery if provided). */\n\tskills?: Skill[];\n}\n\n/**\n * Get the Pi installation path for documentation references.\n * This resolves the pi-internal:// scheme used in the static instructions.\n */\nfunction getPiPath(): string {\n\t// getReadmePath returns something like /path/to/pi/README.md\n\t// We want the parent directory\n\tconst readmePath = getReadmePath();\n\treturn resolve(readmePath, \"..\");\n}\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions = {}): string {\n\tconst {\n\t\tcustomPrompt,\n\t\tselectedTools,\n\t\tappendSystemPrompt,\n\t\tskillsSettings,\n\t\tcwd,\n\t\tagentDir,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst resolvedCwd = cwd ?? process.cwd();\n\tconst resolvedCustomPrompt = resolvePromptInput(customPrompt, \"system prompt\");\n\tconst resolvedAppendPrompt = resolvePromptInput(appendSystemPrompt, \"append system prompt\");\n\n\tconst now = new Date();\n\tconst dateTime = now.toLocaleString(\"en-US\", {\n\t\tweekday: \"long\",\n\t\tyear: \"numeric\",\n\t\tmonth: \"long\",\n\t\tday: \"numeric\",\n\t\thour: \"2-digit\",\n\t\tminute: \"2-digit\",\n\t\tsecond: \"2-digit\",\n\t\ttimeZoneName: \"short\",\n\t});\n\n\tconst appendSection = resolvedAppendPrompt ? `\\n\\n${resolvedAppendPrompt}` : \"\";\n\n\t// Resolve context files: use provided or discover\n\tconst contextFiles = providedContextFiles ?? loadProjectContextFiles({ cwd: resolvedCwd, agentDir });\n\n\t// Resolve skills: use provided or discover\n\tconst skills =\n\t\tprovidedSkills ??\n\t\t(skillsSettings?.enabled !== false ? loadSkills({ ...skillsSettings, cwd: resolvedCwd, agentDir }).skills : []);\n\n\t// Handle custom prompt (full replacement)\n\tif (resolvedCustomPrompt) {\n\t\tlet prompt = resolvedCustomPrompt;\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\t\tprompt += \"The following project context files have been loaded:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t\t}\n\t\t}\n\n\t\t// Append skills section (only if read tool is available)\n\t\tconst customPromptHasRead = !selectedTools || selectedTools.includes(\"read\");\n\t\tif (customPromptHasRead && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills);\n\t\t}\n\n\t\t// Add date/time and working directory last\n\t\tprompt += `\\nCurrent date and time: ${dateTime}`;\n\t\tprompt += `\\nCurrent working directory: ${resolvedCwd}`;\n\n\t\treturn prompt;\n\t}\n\n\t// Build tools list based on selected tools\n\tconst tools = selectedTools || ([\"read\", \"bash\", \"edit\", \"write\"] as ToolName[]);\n\tconst toolsList = tools.length > 0 ? tools.map((t) => `- ${t}: ${toolDescriptions[t]}`).join(\"\\n\") : \"(none)\";\n\n\t// Build guidelines based on which tools are actually available\n\tconst guidelinesList: string[] = [];\n\n\tconst hasBash = tools.includes(\"bash\");\n\tconst hasEdit = tools.includes(\"edit\");\n\tconst hasWrite = tools.includes(\"write\");\n\tconst hasGrep = tools.includes(\"grep\");\n\tconst hasFind = tools.includes(\"find\");\n\tconst hasLs = tools.includes(\"ls\");\n\tconst hasRead = tools.includes(\"read\");\n\n\t// Bash without edit/write = read-only bash mode\n\tif (hasBash && !hasEdit && !hasWrite) {\n\t\tguidelinesList.push(\n\t\t\t\"Use bash ONLY for read-only operations (git log, gh issue view, curl, etc.) - do NOT modify any files\",\n\t\t);\n\t}\n\n\t// File exploration guidelines\n\tif (hasBash && !hasGrep && !hasFind && !hasLs) {\n\t\tguidelinesList.push(\"Use bash for file operations like ls, grep, find\");\n\t} else if (hasBash && (hasGrep || hasFind || hasLs)) {\n\t\tguidelinesList.push(\"Prefer grep/find/ls tools over bash for file exploration (faster, respects .gitignore)\");\n\t}\n\n\t// Read before edit guideline\n\tif (hasRead && hasEdit) {\n\t\tguidelinesList.push(\"Use read to examine files before editing. You must use this tool instead of cat or sed.\");\n\t}\n\n\t// Edit guideline\n\tif (hasEdit) {\n\t\tguidelinesList.push(\"Use edit for precise changes (old text must match exactly)\");\n\t}\n\n\t// Write guideline\n\tif (hasWrite) {\n\t\tguidelinesList.push(\"Use write only for new files or complete rewrites\");\n\t}\n\n\t// Output guideline (only when actually writing/executing)\n\tif (hasEdit || hasWrite) {\n\t\tguidelinesList.push(\n\t\t\t\"When summarizing your actions, output plain text directly - do NOT use cat or bash to display what you did\",\n\t\t);\n\t}\n\n\t// Always include these\n\tguidelinesList.push(\"Be concise in your responses\");\n\tguidelinesList.push(\"Show file paths clearly when working with files\");\n\n\tconst guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n\t// Build prompt with static prefix + dynamic parts\n\tconst piPath = getPiPath();\n\n\tlet prompt = `${PI_STATIC_INSTRUCTIONS}\nPi path:\npi-internal:// refers to paths in ${piPath}\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\tprompt += \"The following project context files have been loaded:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t}\n\t}\n\n\t// Append skills section (only if read tool is available)\n\tif (hasRead && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills);\n\t}\n\n\t// Add date/time and working directory last\n\tprompt += `\\nCurrent date and time: ${dateTime}`;\n\tprompt += `\\nCurrent working directory: ${resolvedCwd}`;\n\n\treturn prompt;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport chalk from \"chalk\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { join, resolve } from \"path\";\nimport { getAgentDir, getDocsPath, getExamplesPath, getReadmePath } from \"../config.js\";\nimport type { SkillsSettings } from \"./settings-manager.js\";\nimport { formatSkillsForPrompt, loadSkills, type Skill } from \"./skills.js\";\nimport type { ToolName } from \"./tools/index.js\";\n\n/** Tool descriptions for system prompt */\nconst toolDescriptions: Record<ToolName, string> = {\n\tread: \"Read file contents\",\n\tbash: \"Execute bash commands (ls, grep, find, etc.)\",\n\tedit: \"Make surgical edits to files (find exact text and replace)\",\n\twrite: \"Create or overwrite files\",\n\tgrep: \"Search file contents for patterns (respects .gitignore)\",\n\tfind: \"Find files by glob pattern (respects .gitignore)\",\n\tls: \"List directory contents\",\n};\n\n/** Resolve input as file path or literal string */\nexport function resolvePromptInput(input: string | undefined, description: string): string | undefined {\n\tif (!input) {\n\t\treturn undefined;\n\t}\n\n\tif (existsSync(input)) {\n\t\ttry {\n\t\t\treturn readFileSync(input, \"utf-8\");\n\t\t} catch (error) {\n\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${description} file ${input}: ${error}`));\n\t\t\treturn input;\n\t\t}\n\t}\n\n\treturn input;\n}\n\n/** Look for AGENTS.md or CLAUDE.md in a directory (prefers AGENTS.md) */\nfunction loadContextFileFromDir(dir: string): { path: string; content: string } | null {\n\tconst candidates = [\"AGENTS.md\", \"CLAUDE.md\"];\n\tfor (const filename of candidates) {\n\t\tconst filePath = join(dir, filename);\n\t\tif (existsSync(filePath)) {\n\t\t\ttry {\n\t\t\t\treturn {\n\t\t\t\t\tpath: filePath,\n\t\t\t\t\tcontent: readFileSync(filePath, \"utf-8\"),\n\t\t\t\t};\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${filePath}: ${error}`));\n\t\t\t}\n\t\t}\n\t}\n\treturn null;\n}\n\nexport interface LoadContextFilesOptions {\n\t/** Working directory to start walking up from. Default: process.cwd() */\n\tcwd?: string;\n\t/** Agent config directory for global context. Default: from getAgentDir() */\n\tagentDir?: string;\n}\n\n/**\n * Load all project context files in order:\n * 1. Global: agentDir/AGENTS.md or CLAUDE.md\n * 2. Parent directories (top-most first) down to cwd\n * Each returns {path, content} for separate messages\n */\nexport function loadProjectContextFiles(\n\toptions: LoadContextFilesOptions = {},\n): Array<{ path: string; content: string }> {\n\tconst resolvedCwd = options.cwd ?? process.cwd();\n\tconst resolvedAgentDir = options.agentDir ?? getAgentDir();\n\n\tconst contextFiles: Array<{ path: string; content: string }> = [];\n\tconst seenPaths = new Set<string>();\n\n\t// 1. Load global context from agentDir\n\tconst globalContext = loadContextFileFromDir(resolvedAgentDir);\n\tif (globalContext) {\n\t\tcontextFiles.push(globalContext);\n\t\tseenPaths.add(globalContext.path);\n\t}\n\n\t// 2. Walk up from cwd to root, collecting all context files\n\tconst ancestorContextFiles: Array<{ path: string; content: string }> = [];\n\n\tlet currentDir = resolvedCwd;\n\tconst root = resolve(\"/\");\n\n\twhile (true) {\n\t\tconst contextFile = loadContextFileFromDir(currentDir);\n\t\tif (contextFile && !seenPaths.has(contextFile.path)) {\n\t\t\t// Add to beginning so we get top-most parent first\n\t\t\tancestorContextFiles.unshift(contextFile);\n\t\t\tseenPaths.add(contextFile.path);\n\t\t}\n\n\t\t// Stop if we've reached root\n\t\tif (currentDir === root) break;\n\n\t\t// Move up one directory\n\t\tconst parentDir = resolve(currentDir, \"..\");\n\t\tif (parentDir === currentDir) break; // Safety check\n\t\tcurrentDir = parentDir;\n\t}\n\n\t// Add ancestor files in order (top-most → cwd)\n\tcontextFiles.push(...ancestorContextFiles);\n\n\treturn contextFiles;\n}\n\nexport interface BuildSystemPromptOptions {\n\t/** Custom system prompt (replaces default). */\n\tcustomPrompt?: string;\n\t/** Tools to include in prompt. Default: [read, bash, edit, write] */\n\tselectedTools?: ToolName[];\n\t/** Text to append to system prompt. */\n\tappendSystemPrompt?: string;\n\t/** Skills settings for discovery. */\n\tskillsSettings?: SkillsSettings;\n\t/** Working directory. Default: process.cwd() */\n\tcwd?: string;\n\t/** Agent config directory. Default: from getAgentDir() */\n\tagentDir?: string;\n\t/** Pre-loaded context files (skips discovery if provided). */\n\tcontextFiles?: Array<{ path: string; content: string }>;\n\t/** Pre-loaded skills (skips discovery if provided). */\n\tskills?: Skill[];\n}\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions = {}): string {\n\tconst {\n\t\tcustomPrompt,\n\t\tselectedTools,\n\t\tappendSystemPrompt,\n\t\tskillsSettings,\n\t\tcwd,\n\t\tagentDir,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst resolvedCwd = cwd ?? process.cwd();\n\tconst resolvedCustomPrompt = resolvePromptInput(customPrompt, \"system prompt\");\n\tconst resolvedAppendPrompt = resolvePromptInput(appendSystemPrompt, \"append system prompt\");\n\n\tconst now = new Date();\n\tconst dateTime = now.toLocaleString(\"en-US\", {\n\t\tweekday: \"long\",\n\t\tyear: \"numeric\",\n\t\tmonth: \"long\",\n\t\tday: \"numeric\",\n\t\thour: \"2-digit\",\n\t\tminute: \"2-digit\",\n\t\tsecond: \"2-digit\",\n\t\ttimeZoneName: \"short\",\n\t});\n\n\tconst appendSection = resolvedAppendPrompt ? `\\n\\n${resolvedAppendPrompt}` : \"\";\n\n\t// Resolve context files: use provided or discover\n\tconst contextFiles = providedContextFiles ?? loadProjectContextFiles({ cwd: resolvedCwd, agentDir });\n\n\t// Resolve skills: use provided or discover\n\tconst skills =\n\t\tprovidedSkills ??\n\t\t(skillsSettings?.enabled !== false ? loadSkills({ ...skillsSettings, cwd: resolvedCwd, agentDir }).skills : []);\n\n\tif (resolvedCustomPrompt) {\n\t\tlet prompt = resolvedCustomPrompt;\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t\t}\n\t\t}\n\n\t\t// Append skills section (only if read tool is available)\n\t\tconst customPromptHasRead = !selectedTools || selectedTools.includes(\"read\");\n\t\tif (customPromptHasRead && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills);\n\t\t}\n\n\t\t// Add date/time and working directory last\n\t\tprompt += `\\nCurrent date and time: ${dateTime}`;\n\t\tprompt += `\\nCurrent working directory: ${resolvedCwd}`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\t// Build tools list based on selected tools\n\tconst tools = selectedTools || ([\"read\", \"bash\", \"edit\", \"write\"] as ToolName[]);\n\tconst toolsList = tools.length > 0 ? tools.map((t) => `- ${t}: ${toolDescriptions[t]}`).join(\"\\n\") : \"(none)\";\n\n\t// Build guidelines based on which tools are actually available\n\tconst guidelinesList: string[] = [];\n\n\tconst hasBash = tools.includes(\"bash\");\n\tconst hasEdit = tools.includes(\"edit\");\n\tconst hasWrite = tools.includes(\"write\");\n\tconst hasGrep = tools.includes(\"grep\");\n\tconst hasFind = tools.includes(\"find\");\n\tconst hasLs = tools.includes(\"ls\");\n\tconst hasRead = tools.includes(\"read\");\n\n\t// Bash without edit/write = read-only bash mode\n\tif (hasBash && !hasEdit && !hasWrite) {\n\t\tguidelinesList.push(\n\t\t\t\"Use bash ONLY for read-only operations (git log, gh issue view, curl, etc.) - do NOT modify any files\",\n\t\t);\n\t}\n\n\t// File exploration guidelines\n\tif (hasBash && !hasGrep && !hasFind && !hasLs) {\n\t\tguidelinesList.push(\"Use bash for file operations like ls, grep, find\");\n\t} else if (hasBash && (hasGrep || hasFind || hasLs)) {\n\t\tguidelinesList.push(\"Prefer grep/find/ls tools over bash for file exploration (faster, respects .gitignore)\");\n\t}\n\n\t// Read before edit guideline\n\tif (hasRead && hasEdit) {\n\t\tguidelinesList.push(\"Use read to examine files before editing. You must use this tool instead of cat or sed.\");\n\t}\n\n\t// Edit guideline\n\tif (hasEdit) {\n\t\tguidelinesList.push(\"Use edit for precise changes (old text must match exactly)\");\n\t}\n\n\t// Write guideline\n\tif (hasWrite) {\n\t\tguidelinesList.push(\"Use write only for new files or complete rewrites\");\n\t}\n\n\t// Output guideline (only when actually writing/executing)\n\tif (hasEdit || hasWrite) {\n\t\tguidelinesList.push(\n\t\t\t\"When summarizing your actions, output plain text directly - do NOT use cat or bash to display what you did\",\n\t\t);\n\t}\n\n\t// Always include these\n\tguidelinesList.push(\"Be concise in your responses\");\n\tguidelinesList.push(\"Show file paths clearly when working with files\");\n\n\tconst guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n\tlet prompt = `You are an expert coding assistant operating inside pi, a coding agent harness. You help users by reading files, executing commands, editing code, and writing new files.\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}\n\nPi documentation (only when the user asks about pi itself, its SDK, extensions, themes, skills, or TUI):\n- Main documentation: ${readmePath}\n- Additional docs: ${docsPath}\n- Examples: ${examplesPath} (extensions, custom tools, SDK)\n- When asked to create: custom models/providers (README.md), extensions (docs/extensions.md, examples/extensions/), themes (docs/theme.md), skills (docs/skills.md), TUI components (docs/tui.md - has copy-paste patterns)\n- When working on pi topics, read the docs and examples, and follow .md cross-references before implementing`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n# Project Context\\n\\n\";\n\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `## ${filePath}\\n\\n${content}\\n\\n`;\n\t\t}\n\t}\n\n\t// Append skills section (only if read tool is available)\n\tif (hasRead && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills);\n\t}\n\n\t// Add date/time and working directory last\n\tprompt += `\\nCurrent date and time: ${dateTime}`;\n\tprompt += `\\nCurrent working directory: ${resolvedCwd}`;\n\n\treturn prompt;\n}\n"]}
|
|
@@ -31,6 +31,8 @@ export interface BashOperations {
|
|
|
31
31
|
export interface BashToolOptions {
|
|
32
32
|
/** Custom operations for command execution. Default: local shell */
|
|
33
33
|
operations?: BashOperations;
|
|
34
|
+
/** Command prefix prepended to every command (e.g., "shopt -s expand_aliases" for alias support) */
|
|
35
|
+
commandPrefix?: string;
|
|
34
36
|
}
|
|
35
37
|
export declare function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema>;
|
|
36
38
|
/** Default bash tool using process.cwd() - for backwards compatibility */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAI7D,OAAO,EAAoD,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAUtH,QAAA,MAAM,UAAU;;;EAGd,CAAC;AAEH,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;OAMG;IACH,IAAI,EAAE,CACL,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QACR,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;KACjB,KACG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CAC1C;AAqFD,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;CAC5B;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CA6InG;AAED,0EAA0E;AAC1E,eAAO,MAAM,QAAQ;;;QAAgC,CAAC","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { createWriteStream, existsSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AgentTool } from \"@mariozechner/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport { spawn } from \"child_process\";\nimport { getShellConfig, killProcessTree } from \"../../utils/shell.js\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateTail } from \"./truncate.js\";\n\n/**\n * Generate a unique temp file path for bash output\n */\nfunction getTempFilePath(): string {\n\tconst id = randomBytes(8).toString(\"hex\");\n\treturn join(tmpdir(), `pi-bash-${id}.log`);\n}\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (e.g., SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command - The command to execute\n\t * @param cwd - Working directory\n\t * @param options - Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t},\n\t) => Promise<{ exitCode: number | null }>;\n}\n\n/**\n * Default bash operations using local shell\n */\nconst defaultBashOperations: BashOperations = {\n\texec: (command, cwd, { onData, signal, timeout }) => {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst { shell, args } = getShellConfig();\n\n\t\t\tif (!existsSync(cwd)) {\n\t\t\t\treject(new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst child = spawn(shell, [...args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: true,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t});\n\n\t\t\tlet timedOut = false;\n\n\t\t\t// Set timeout if provided\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tif (timeout !== undefined && timeout > 0) {\n\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\ttimedOut = true;\n\t\t\t\t\tif (child.pid) {\n\t\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t\t}\n\t\t\t\t}, timeout * 1000);\n\t\t\t}\n\n\t\t\t// Stream stdout and stderr\n\t\t\tif (child.stdout) {\n\t\t\t\tchild.stdout.on(\"data\", onData);\n\t\t\t}\n\t\t\tif (child.stderr) {\n\t\t\t\tchild.stderr.on(\"data\", onData);\n\t\t\t}\n\n\t\t\t// Handle shell spawn errors\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t\treject(err);\n\t\t\t});\n\n\t\t\t// Handle abort signal - kill entire process tree\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) {\n\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tif (signal) {\n\t\t\t\tif (signal.aborted) {\n\t\t\t\t\tonAbort();\n\t\t\t\t} else {\n\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle process exit\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (timedOut) {\n\t\t\t\t\treject(new Error(`timeout:${timeout}`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tresolve({ exitCode: code });\n\t\t\t});\n\t\t});\n\t},\n};\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\tconst ops = options?.operations ?? defaultBashOperations;\n\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tparameters: bashSchema,\n\t\texecute: async (\n\t\t\t_toolCallId: string,\n\t\t\t{ command, timeout }: { command: string; timeout?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t) => {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t// We'll stream to a temp file if output gets large\n\t\t\t\tlet tempFilePath: string | undefined;\n\t\t\t\tlet tempFileStream: ReturnType<typeof createWriteStream> | undefined;\n\t\t\t\tlet totalBytes = 0;\n\n\t\t\t\t// Keep a rolling buffer of the last chunk for tail truncation\n\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\tlet chunksBytes = 0;\n\t\t\t\t// Keep more than we need so we have enough for truncation\n\t\t\t\tconst maxChunksBytes = DEFAULT_MAX_BYTES * 2;\n\n\t\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\t\ttotalBytes += data.length;\n\n\t\t\t\t\t// Start writing to temp file once we exceed the threshold\n\t\t\t\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\t\t\t\ttempFilePath = getTempFilePath();\n\t\t\t\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\t\t\t\t// Write all buffered chunks to the file\n\t\t\t\t\t\tfor (const chunk of chunks) {\n\t\t\t\t\t\t\ttempFileStream.write(chunk);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Write to temp file if we have one\n\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\ttempFileStream.write(data);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Keep rolling buffer of recent data\n\t\t\t\t\tchunks.push(data);\n\t\t\t\t\tchunksBytes += data.length;\n\n\t\t\t\t\t// Trim old chunks if buffer is too large\n\t\t\t\t\twhile (chunksBytes > maxChunksBytes && chunks.length > 1) {\n\t\t\t\t\t\tconst removed = chunks.shift()!;\n\t\t\t\t\t\tchunksBytes -= removed.length;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Stream partial output to callback (truncated rolling buffer)\n\t\t\t\t\tif (onUpdate) {\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tconst fullText = fullBuffer.toString(\"utf-8\");\n\t\t\t\t\t\tconst truncation = truncateTail(fullText);\n\t\t\t\t\t\tonUpdate({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: truncation.content || \"\" }],\n\t\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\t\ttruncation: truncation.truncated ? truncation : undefined,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tops.exec(command, cwd, { onData: handleData, signal, timeout })\n\t\t\t\t\t.then(({ exitCode }) => {\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Combine all buffered chunks\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tconst fullOutput = fullBuffer.toString(\"utf-8\");\n\n\t\t\t\t\t\t// Apply tail truncation\n\t\t\t\t\t\tconst truncation = truncateTail(fullOutput);\n\t\t\t\t\t\tlet outputText = truncation.content || \"(no output)\";\n\n\t\t\t\t\t\t// Build details with truncation info\n\t\t\t\t\t\tlet details: BashToolDetails | undefined;\n\n\t\t\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\t\t\tdetails = {\n\t\t\t\t\t\t\t\ttruncation,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t// Build actionable notice\n\t\t\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\t\t\tconst endLine = truncation.totalLines;\n\n\t\t\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\t\t\t// Edge case: last line alone > 30KB\n\t\t\t\t\t\t\t\tconst lastLineSize = formatSize(Buffer.byteLength(fullOutput.split(\"\\n\").pop() || \"\", \"utf-8\"));\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\t\t\toutputText += `\\n\\nCommand exited with code ${exitCode}`;\n\t\t\t\t\t\t\treject(new Error(outputText));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: outputText }], details });\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err: Error) => {\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Combine all buffered chunks for error output\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tlet output = fullBuffer.toString(\"utf-8\");\n\n\t\t\t\t\t\tif (err.message === \"aborted\") {\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += \"Command aborted\";\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else if (err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += `Command timed out after ${timeoutSecs} seconds`;\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t});\n\t\t},\n\t};\n}\n\n/** Default bash tool using process.cwd() - for backwards compatibility */\nexport const bashTool = createBashTool(process.cwd());\n"]}
|
|
1
|
+
{"version":3,"file":"bash.d.ts","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAC;AAI7D,OAAO,EAAoD,KAAK,gBAAgB,EAAgB,MAAM,eAAe,CAAC;AAUtH,QAAA,MAAM,UAAU;;;EAGd,CAAC;AAEH,MAAM,WAAW,eAAe;IAC/B,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B;;;;;;OAMG;IACH,IAAI,EAAE,CACL,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,OAAO,EAAE;QACR,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;QAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;KACjB,KACG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CAC1C;AAqFD,MAAM,WAAW,eAAe;IAC/B,oEAAoE;IACpE,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,oGAAoG;IACpG,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAiJnG;AAED,0EAA0E;AAC1E,eAAO,MAAM,QAAQ;;;QAAgC,CAAC","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { createWriteStream, existsSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AgentTool } from \"@mariozechner/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport { spawn } from \"child_process\";\nimport { getShellConfig, killProcessTree } from \"../../utils/shell.js\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateTail } from \"./truncate.js\";\n\n/**\n * Generate a unique temp file path for bash output\n */\nfunction getTempFilePath(): string {\n\tconst id = randomBytes(8).toString(\"hex\");\n\treturn join(tmpdir(), `pi-bash-${id}.log`);\n}\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (e.g., SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command - The command to execute\n\t * @param cwd - Working directory\n\t * @param options - Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t},\n\t) => Promise<{ exitCode: number | null }>;\n}\n\n/**\n * Default bash operations using local shell\n */\nconst defaultBashOperations: BashOperations = {\n\texec: (command, cwd, { onData, signal, timeout }) => {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst { shell, args } = getShellConfig();\n\n\t\t\tif (!existsSync(cwd)) {\n\t\t\t\treject(new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst child = spawn(shell, [...args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: true,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t});\n\n\t\t\tlet timedOut = false;\n\n\t\t\t// Set timeout if provided\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tif (timeout !== undefined && timeout > 0) {\n\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\ttimedOut = true;\n\t\t\t\t\tif (child.pid) {\n\t\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t\t}\n\t\t\t\t}, timeout * 1000);\n\t\t\t}\n\n\t\t\t// Stream stdout and stderr\n\t\t\tif (child.stdout) {\n\t\t\t\tchild.stdout.on(\"data\", onData);\n\t\t\t}\n\t\t\tif (child.stderr) {\n\t\t\t\tchild.stderr.on(\"data\", onData);\n\t\t\t}\n\n\t\t\t// Handle shell spawn errors\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t\treject(err);\n\t\t\t});\n\n\t\t\t// Handle abort signal - kill entire process tree\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) {\n\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tif (signal) {\n\t\t\t\tif (signal.aborted) {\n\t\t\t\t\tonAbort();\n\t\t\t\t} else {\n\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle process exit\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (timedOut) {\n\t\t\t\t\treject(new Error(`timeout:${timeout}`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tresolve({ exitCode: code });\n\t\t\t});\n\t\t});\n\t},\n};\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (e.g., \"shopt -s expand_aliases\" for alias support) */\n\tcommandPrefix?: string;\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\tconst ops = options?.operations ?? defaultBashOperations;\n\tconst commandPrefix = options?.commandPrefix;\n\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tparameters: bashSchema,\n\t\texecute: async (\n\t\t\t_toolCallId: string,\n\t\t\t{ command, timeout }: { command: string; timeout?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t) => {\n\t\t\t// Apply command prefix if configured (e.g., \"shopt -s expand_aliases\" for alias support)\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t// We'll stream to a temp file if output gets large\n\t\t\t\tlet tempFilePath: string | undefined;\n\t\t\t\tlet tempFileStream: ReturnType<typeof createWriteStream> | undefined;\n\t\t\t\tlet totalBytes = 0;\n\n\t\t\t\t// Keep a rolling buffer of the last chunk for tail truncation\n\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\tlet chunksBytes = 0;\n\t\t\t\t// Keep more than we need so we have enough for truncation\n\t\t\t\tconst maxChunksBytes = DEFAULT_MAX_BYTES * 2;\n\n\t\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\t\ttotalBytes += data.length;\n\n\t\t\t\t\t// Start writing to temp file once we exceed the threshold\n\t\t\t\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\t\t\t\ttempFilePath = getTempFilePath();\n\t\t\t\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\t\t\t\t// Write all buffered chunks to the file\n\t\t\t\t\t\tfor (const chunk of chunks) {\n\t\t\t\t\t\t\ttempFileStream.write(chunk);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Write to temp file if we have one\n\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\ttempFileStream.write(data);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Keep rolling buffer of recent data\n\t\t\t\t\tchunks.push(data);\n\t\t\t\t\tchunksBytes += data.length;\n\n\t\t\t\t\t// Trim old chunks if buffer is too large\n\t\t\t\t\twhile (chunksBytes > maxChunksBytes && chunks.length > 1) {\n\t\t\t\t\t\tconst removed = chunks.shift()!;\n\t\t\t\t\t\tchunksBytes -= removed.length;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Stream partial output to callback (truncated rolling buffer)\n\t\t\t\t\tif (onUpdate) {\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tconst fullText = fullBuffer.toString(\"utf-8\");\n\t\t\t\t\t\tconst truncation = truncateTail(fullText);\n\t\t\t\t\t\tonUpdate({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: truncation.content || \"\" }],\n\t\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\t\ttruncation: truncation.truncated ? truncation : undefined,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tops.exec(resolvedCommand, cwd, { onData: handleData, signal, timeout })\n\t\t\t\t\t.then(({ exitCode }) => {\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Combine all buffered chunks\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tconst fullOutput = fullBuffer.toString(\"utf-8\");\n\n\t\t\t\t\t\t// Apply tail truncation\n\t\t\t\t\t\tconst truncation = truncateTail(fullOutput);\n\t\t\t\t\t\tlet outputText = truncation.content || \"(no output)\";\n\n\t\t\t\t\t\t// Build details with truncation info\n\t\t\t\t\t\tlet details: BashToolDetails | undefined;\n\n\t\t\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\t\t\tdetails = {\n\t\t\t\t\t\t\t\ttruncation,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t// Build actionable notice\n\t\t\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\t\t\tconst endLine = truncation.totalLines;\n\n\t\t\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\t\t\t// Edge case: last line alone > 30KB\n\t\t\t\t\t\t\t\tconst lastLineSize = formatSize(Buffer.byteLength(fullOutput.split(\"\\n\").pop() || \"\", \"utf-8\"));\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\t\t\toutputText += `\\n\\nCommand exited with code ${exitCode}`;\n\t\t\t\t\t\t\treject(new Error(outputText));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: outputText }], details });\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err: Error) => {\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Combine all buffered chunks for error output\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tlet output = fullBuffer.toString(\"utf-8\");\n\n\t\t\t\t\t\tif (err.message === \"aborted\") {\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += \"Command aborted\";\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else if (err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += `Command timed out after ${timeoutSecs} seconds`;\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t});\n\t\t},\n\t};\n}\n\n/** Default bash tool using process.cwd() - for backwards compatibility */\nexport const bashTool = createBashTool(process.cwd());\n"]}
|
package/dist/core/tools/bash.js
CHANGED
|
@@ -94,12 +94,15 @@ const defaultBashOperations = {
|
|
|
94
94
|
};
|
|
95
95
|
export function createBashTool(cwd, options) {
|
|
96
96
|
const ops = options?.operations ?? defaultBashOperations;
|
|
97
|
+
const commandPrefix = options?.commandPrefix;
|
|
97
98
|
return {
|
|
98
99
|
name: "bash",
|
|
99
100
|
label: "bash",
|
|
100
101
|
description: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,
|
|
101
102
|
parameters: bashSchema,
|
|
102
103
|
execute: async (_toolCallId, { command, timeout }, signal, onUpdate) => {
|
|
104
|
+
// Apply command prefix if configured (e.g., "shopt -s expand_aliases" for alias support)
|
|
105
|
+
const resolvedCommand = commandPrefix ? `${commandPrefix}\n${command}` : command;
|
|
103
106
|
return new Promise((resolve, reject) => {
|
|
104
107
|
// We'll stream to a temp file if output gets large
|
|
105
108
|
let tempFilePath;
|
|
@@ -147,7 +150,7 @@ export function createBashTool(cwd, options) {
|
|
|
147
150
|
});
|
|
148
151
|
}
|
|
149
152
|
};
|
|
150
|
-
ops.exec(
|
|
153
|
+
ops.exec(resolvedCommand, cwd, { onData: handleData, signal, timeout })
|
|
151
154
|
.then(({ exitCode }) => {
|
|
152
155
|
// Close temp file stream
|
|
153
156
|
if (tempFileStream) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,UAAU,EAAyB,YAAY,EAAE,MAAM,eAAe,CAAC;AAEtH;;GAEG;AACH,SAAS,eAAe,GAAW;IAClC,MAAM,EAAE,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1C,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;AAAA,CAC3C;AAED,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC;IAC9B,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,yBAAyB,EAAE,CAAC;IAChE,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,mDAAmD,EAAE,CAAC,CAAC;CACzG,CAAC,CAAC;AA8BH;;GAEG;AACH,MAAM,qBAAqB,GAAmB;IAC7C,IAAI,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;QACpD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;YACvC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,cAAc,EAAE,CAAC;YAEzC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,CAAC,IAAI,KAAK,CAAC,qCAAqC,GAAG,iCAAiC,CAAC,CAAC,CAAC;gBAC7F,OAAO;YACR,CAAC;YAED,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE;gBAC9C,GAAG;gBACH,QAAQ,EAAE,IAAI;gBACd,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC;aACjC,CAAC,CAAC;YAEH,IAAI,QAAQ,GAAG,KAAK,CAAC;YAErB,0BAA0B;YAC1B,IAAI,aAAyC,CAAC;YAC9C,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;gBAC1C,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC;oBAChC,QAAQ,GAAG,IAAI,CAAC;oBAChB,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;wBACf,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBAC5B,CAAC;gBAAA,CACD,EAAE,OAAO,GAAG,IAAI,CAAC,CAAC;YACpB,CAAC;YAED,2BAA2B;YAC3B,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBAClB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACjC,CAAC;YACD,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBAClB,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACjC,CAAC;YAED,4BAA4B;YAC5B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;gBAC1B,IAAI,aAAa;oBAAE,YAAY,CAAC,aAAa,CAAC,CAAC;gBAC/C,IAAI,MAAM;oBAAE,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBACzD,MAAM,CAAC,GAAG,CAAC,CAAC;YAAA,CACZ,CAAC,CAAC;YAEH,iDAAiD;YACjD,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC;gBACrB,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;oBACf,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC5B,CAAC;YAAA,CACD,CAAC;YAEF,IAAI,MAAM,EAAE,CAAC;gBACZ,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACpB,OAAO,EAAE,CAAC;gBACX,CAAC;qBAAM,CAAC;oBACP,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC3D,CAAC;YACF,CAAC;YAED,sBAAsB;YACtB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;gBAC3B,IAAI,aAAa;oBAAE,YAAY,CAAC,aAAa,CAAC,CAAC;gBAC/C,IAAI,MAAM;oBAAE,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAEzD,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;oBACrB,MAAM,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;oBAC7B,OAAO;gBACR,CAAC;gBAED,IAAI,QAAQ,EAAE,CAAC;oBACd,MAAM,CAAC,IAAI,KAAK,CAAC,WAAW,OAAO,EAAE,CAAC,CAAC,CAAC;oBACxC,OAAO;gBACR,CAAC;gBAED,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;YAAA,CAC5B,CAAC,CAAC;QAAA,CACH,CAAC,CAAC;IAAA,CACH;CACD,CAAC;AAOF,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,OAAyB,EAAgC;IACpG,MAAM,GAAG,GAAG,OAAO,EAAE,UAAU,IAAI,qBAAqB,CAAC;IAEzD,OAAO;QACN,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM;QACb,WAAW,EAAE,mHAAmH,iBAAiB,aAAa,iBAAiB,GAAG,IAAI,0HAA0H;QAChT,UAAU,EAAE,UAAU;QACtB,OAAO,EAAE,KAAK,EACb,WAAmB,EACnB,EAAE,OAAO,EAAE,OAAO,EAAyC,EAC3D,MAAoB,EACpB,QAAS,EACR,EAAE,CAAC;YACJ,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;gBACvC,mDAAmD;gBACnD,IAAI,YAAgC,CAAC;gBACrC,IAAI,cAAgE,CAAC;gBACrE,IAAI,UAAU,GAAG,CAAC,CAAC;gBAEnB,8DAA8D;gBAC9D,MAAM,MAAM,GAAa,EAAE,CAAC;gBAC5B,IAAI,WAAW,GAAG,CAAC,CAAC;gBACpB,0DAA0D;gBAC1D,MAAM,cAAc,GAAG,iBAAiB,GAAG,CAAC,CAAC;gBAE7C,MAAM,UAAU,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC;oBACpC,UAAU,IAAI,IAAI,CAAC,MAAM,CAAC;oBAE1B,0DAA0D;oBAC1D,IAAI,UAAU,GAAG,iBAAiB,IAAI,CAAC,YAAY,EAAE,CAAC;wBACrD,YAAY,GAAG,eAAe,EAAE,CAAC;wBACjC,cAAc,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAC;wBACjD,wCAAwC;wBACxC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;4BAC5B,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;wBAC7B,CAAC;oBACF,CAAC;oBAED,oCAAoC;oBACpC,IAAI,cAAc,EAAE,CAAC;wBACpB,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;oBAC5B,CAAC;oBAED,qCAAqC;oBACrC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAClB,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC;oBAE3B,yCAAyC;oBACzC,OAAO,WAAW,GAAG,cAAc,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAC1D,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,EAAG,CAAC;wBAChC,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;oBAC/B,CAAC;oBAED,+DAA+D;oBAC/D,IAAI,QAAQ,EAAE,CAAC;wBACd,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;wBACzC,MAAM,QAAQ,GAAG,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;wBAC9C,MAAM,UAAU,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;wBAC1C,QAAQ,CAAC;4BACR,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;4BAC3D,OAAO,EAAE;gCACR,UAAU,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;gCACzD,cAAc,EAAE,YAAY;6BAC5B;yBACD,CAAC,CAAC;oBACJ,CAAC;gBAAA,CACD,CAAC;gBAEF,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;qBAC7D,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;oBACvB,yBAAyB;oBACzB,IAAI,cAAc,EAAE,CAAC;wBACpB,cAAc,CAAC,GAAG,EAAE,CAAC;oBACtB,CAAC;oBAED,8BAA8B;oBAC9B,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;oBACzC,MAAM,UAAU,GAAG,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;oBAEhD,wBAAwB;oBACxB,MAAM,UAAU,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;oBAC5C,IAAI,UAAU,GAAG,UAAU,CAAC,OAAO,IAAI,aAAa,CAAC;oBAErD,qCAAqC;oBACrC,IAAI,OAAoC,CAAC;oBAEzC,IAAI,UAAU,CAAC,SAAS,EAAE,CAAC;wBAC1B,OAAO,GAAG;4BACT,UAAU;4BACV,cAAc,EAAE,YAAY;yBAC5B,CAAC;wBAEF,0BAA0B;wBAC1B,MAAM,SAAS,GAAG,UAAU,CAAC,UAAU,GAAG,UAAU,CAAC,WAAW,GAAG,CAAC,CAAC;wBACrE,MAAM,OAAO,GAAG,UAAU,CAAC,UAAU,CAAC;wBAEtC,IAAI,UAAU,CAAC,eAAe,EAAE,CAAC;4BAChC,oCAAoC;4BACpC,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC,CAAC;4BAChG,UAAU,IAAI,qBAAqB,UAAU,CAAC,UAAU,CAAC,WAAW,CAAC,YAAY,OAAO,aAAa,YAAY,mBAAmB,YAAY,GAAG,CAAC;wBACrJ,CAAC;6BAAM,IAAI,UAAU,CAAC,WAAW,KAAK,OAAO,EAAE,CAAC;4BAC/C,UAAU,IAAI,sBAAsB,SAAS,IAAI,OAAO,OAAO,UAAU,CAAC,UAAU,kBAAkB,YAAY,GAAG,CAAC;wBACvH,CAAC;6BAAM,CAAC;4BACP,UAAU,IAAI,sBAAsB,SAAS,IAAI,OAAO,OAAO,UAAU,CAAC,UAAU,KAAK,UAAU,CAAC,iBAAiB,CAAC,yBAAyB,YAAY,GAAG,CAAC;wBAChK,CAAC;oBACF,CAAC;oBAED,IAAI,QAAQ,KAAK,CAAC,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;wBACzC,UAAU,IAAI,gCAAgC,QAAQ,EAAE,CAAC;wBACzD,MAAM,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;oBAC/B,CAAC;yBAAM,CAAC;wBACP,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;oBACrE,CAAC;gBAAA,CACD,CAAC;qBACD,KAAK,CAAC,CAAC,GAAU,EAAE,EAAE,CAAC;oBACtB,yBAAyB;oBACzB,IAAI,cAAc,EAAE,CAAC;wBACpB,cAAc,CAAC,GAAG,EAAE,CAAC;oBACtB,CAAC;oBAED,+CAA+C;oBAC/C,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;oBACzC,IAAI,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;oBAE1C,IAAI,GAAG,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;wBAC/B,IAAI,MAAM;4BAAE,MAAM,IAAI,MAAM,CAAC;wBAC7B,MAAM,IAAI,iBAAiB,CAAC;wBAC5B,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3B,CAAC;yBAAM,IAAI,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;wBAC/C,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;wBAC9C,IAAI,MAAM;4BAAE,MAAM,IAAI,MAAM,CAAC;wBAC7B,MAAM,IAAI,2BAA2B,WAAW,UAAU,CAAC;wBAC3D,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;oBAC3B,CAAC;yBAAM,CAAC;wBACP,MAAM,CAAC,GAAG,CAAC,CAAC;oBACb,CAAC;gBAAA,CACD,CAAC,CAAC;YAAA,CACJ,CAAC,CAAC;QAAA,CACH;KACD,CAAC;AAAA,CACF;AAED,0EAA0E;AAC1E,MAAM,CAAC,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { createWriteStream, existsSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AgentTool } from \"@mariozechner/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport { spawn } from \"child_process\";\nimport { getShellConfig, killProcessTree } from \"../../utils/shell.js\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateTail } from \"./truncate.js\";\n\n/**\n * Generate a unique temp file path for bash output\n */\nfunction getTempFilePath(): string {\n\tconst id = randomBytes(8).toString(\"hex\");\n\treturn join(tmpdir(), `pi-bash-${id}.log`);\n}\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (e.g., SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command - The command to execute\n\t * @param cwd - Working directory\n\t * @param options - Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t},\n\t) => Promise<{ exitCode: number | null }>;\n}\n\n/**\n * Default bash operations using local shell\n */\nconst defaultBashOperations: BashOperations = {\n\texec: (command, cwd, { onData, signal, timeout }) => {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst { shell, args } = getShellConfig();\n\n\t\t\tif (!existsSync(cwd)) {\n\t\t\t\treject(new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst child = spawn(shell, [...args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: true,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t});\n\n\t\t\tlet timedOut = false;\n\n\t\t\t// Set timeout if provided\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tif (timeout !== undefined && timeout > 0) {\n\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\ttimedOut = true;\n\t\t\t\t\tif (child.pid) {\n\t\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t\t}\n\t\t\t\t}, timeout * 1000);\n\t\t\t}\n\n\t\t\t// Stream stdout and stderr\n\t\t\tif (child.stdout) {\n\t\t\t\tchild.stdout.on(\"data\", onData);\n\t\t\t}\n\t\t\tif (child.stderr) {\n\t\t\t\tchild.stderr.on(\"data\", onData);\n\t\t\t}\n\n\t\t\t// Handle shell spawn errors\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t\treject(err);\n\t\t\t});\n\n\t\t\t// Handle abort signal - kill entire process tree\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) {\n\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tif (signal) {\n\t\t\t\tif (signal.aborted) {\n\t\t\t\t\tonAbort();\n\t\t\t\t} else {\n\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle process exit\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (timedOut) {\n\t\t\t\t\treject(new Error(`timeout:${timeout}`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tresolve({ exitCode: code });\n\t\t\t});\n\t\t});\n\t},\n};\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\tconst ops = options?.operations ?? defaultBashOperations;\n\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tparameters: bashSchema,\n\t\texecute: async (\n\t\t\t_toolCallId: string,\n\t\t\t{ command, timeout }: { command: string; timeout?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t) => {\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t// We'll stream to a temp file if output gets large\n\t\t\t\tlet tempFilePath: string | undefined;\n\t\t\t\tlet tempFileStream: ReturnType<typeof createWriteStream> | undefined;\n\t\t\t\tlet totalBytes = 0;\n\n\t\t\t\t// Keep a rolling buffer of the last chunk for tail truncation\n\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\tlet chunksBytes = 0;\n\t\t\t\t// Keep more than we need so we have enough for truncation\n\t\t\t\tconst maxChunksBytes = DEFAULT_MAX_BYTES * 2;\n\n\t\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\t\ttotalBytes += data.length;\n\n\t\t\t\t\t// Start writing to temp file once we exceed the threshold\n\t\t\t\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\t\t\t\ttempFilePath = getTempFilePath();\n\t\t\t\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\t\t\t\t// Write all buffered chunks to the file\n\t\t\t\t\t\tfor (const chunk of chunks) {\n\t\t\t\t\t\t\ttempFileStream.write(chunk);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Write to temp file if we have one\n\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\ttempFileStream.write(data);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Keep rolling buffer of recent data\n\t\t\t\t\tchunks.push(data);\n\t\t\t\t\tchunksBytes += data.length;\n\n\t\t\t\t\t// Trim old chunks if buffer is too large\n\t\t\t\t\twhile (chunksBytes > maxChunksBytes && chunks.length > 1) {\n\t\t\t\t\t\tconst removed = chunks.shift()!;\n\t\t\t\t\t\tchunksBytes -= removed.length;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Stream partial output to callback (truncated rolling buffer)\n\t\t\t\t\tif (onUpdate) {\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tconst fullText = fullBuffer.toString(\"utf-8\");\n\t\t\t\t\t\tconst truncation = truncateTail(fullText);\n\t\t\t\t\t\tonUpdate({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: truncation.content || \"\" }],\n\t\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\t\ttruncation: truncation.truncated ? truncation : undefined,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tops.exec(command, cwd, { onData: handleData, signal, timeout })\n\t\t\t\t\t.then(({ exitCode }) => {\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Combine all buffered chunks\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tconst fullOutput = fullBuffer.toString(\"utf-8\");\n\n\t\t\t\t\t\t// Apply tail truncation\n\t\t\t\t\t\tconst truncation = truncateTail(fullOutput);\n\t\t\t\t\t\tlet outputText = truncation.content || \"(no output)\";\n\n\t\t\t\t\t\t// Build details with truncation info\n\t\t\t\t\t\tlet details: BashToolDetails | undefined;\n\n\t\t\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\t\t\tdetails = {\n\t\t\t\t\t\t\t\ttruncation,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t// Build actionable notice\n\t\t\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\t\t\tconst endLine = truncation.totalLines;\n\n\t\t\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\t\t\t// Edge case: last line alone > 30KB\n\t\t\t\t\t\t\t\tconst lastLineSize = formatSize(Buffer.byteLength(fullOutput.split(\"\\n\").pop() || \"\", \"utf-8\"));\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\t\t\toutputText += `\\n\\nCommand exited with code ${exitCode}`;\n\t\t\t\t\t\t\treject(new Error(outputText));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: outputText }], details });\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err: Error) => {\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Combine all buffered chunks for error output\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tlet output = fullBuffer.toString(\"utf-8\");\n\n\t\t\t\t\t\tif (err.message === \"aborted\") {\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += \"Command aborted\";\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else if (err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += `Command timed out after ${timeoutSecs} seconds`;\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t});\n\t\t},\n\t};\n}\n\n/** Default bash tool using process.cwd() - for backwards compatibility */\nexport const bashTool = createBashTool(process.cwd());\n"]}
|
|
1
|
+
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../../src/core/tools/bash.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { createWriteStream, existsSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\nimport type { AgentTool } from \"@mariozechner/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport { spawn } from \"child_process\";\nimport { getShellConfig, killProcessTree } from \"../../utils/shell.js\";\nimport { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationResult, truncateTail } from \"./truncate.js\";\n\n/**\n * Generate a unique temp file path for bash output\n */\nfunction getTempFilePath(): string {\n\tconst id = randomBytes(8).toString(\"hex\");\n\treturn join(tmpdir(), `pi-bash-${id}.log`);\n}\n\nconst bashSchema = Type.Object({\n\tcommand: Type.String({ description: \"Bash command to execute\" }),\n\ttimeout: Type.Optional(Type.Number({ description: \"Timeout in seconds (optional, no default timeout)\" })),\n});\n\nexport interface BashToolDetails {\n\ttruncation?: TruncationResult;\n\tfullOutputPath?: string;\n}\n\n/**\n * Pluggable operations for the bash tool.\n * Override these to delegate command execution to remote systems (e.g., SSH).\n */\nexport interface BashOperations {\n\t/**\n\t * Execute a command and stream output.\n\t * @param command - The command to execute\n\t * @param cwd - Working directory\n\t * @param options - Execution options\n\t * @returns Promise resolving to exit code (null if killed)\n\t */\n\texec: (\n\t\tcommand: string,\n\t\tcwd: string,\n\t\toptions: {\n\t\t\tonData: (data: Buffer) => void;\n\t\t\tsignal?: AbortSignal;\n\t\t\ttimeout?: number;\n\t\t},\n\t) => Promise<{ exitCode: number | null }>;\n}\n\n/**\n * Default bash operations using local shell\n */\nconst defaultBashOperations: BashOperations = {\n\texec: (command, cwd, { onData, signal, timeout }) => {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst { shell, args } = getShellConfig();\n\n\t\t\tif (!existsSync(cwd)) {\n\t\t\t\treject(new Error(`Working directory does not exist: ${cwd}\\nCannot execute bash commands.`));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst child = spawn(shell, [...args, command], {\n\t\t\t\tcwd,\n\t\t\t\tdetached: true,\n\t\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\t});\n\n\t\t\tlet timedOut = false;\n\n\t\t\t// Set timeout if provided\n\t\t\tlet timeoutHandle: NodeJS.Timeout | undefined;\n\t\t\tif (timeout !== undefined && timeout > 0) {\n\t\t\t\ttimeoutHandle = setTimeout(() => {\n\t\t\t\t\ttimedOut = true;\n\t\t\t\t\tif (child.pid) {\n\t\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t\t}\n\t\t\t\t}, timeout * 1000);\n\t\t\t}\n\n\t\t\t// Stream stdout and stderr\n\t\t\tif (child.stdout) {\n\t\t\t\tchild.stdout.on(\"data\", onData);\n\t\t\t}\n\t\t\tif (child.stderr) {\n\t\t\t\tchild.stderr.on(\"data\", onData);\n\t\t\t}\n\n\t\t\t// Handle shell spawn errors\n\t\t\tchild.on(\"error\", (err) => {\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\t\t\t\treject(err);\n\t\t\t});\n\n\t\t\t// Handle abort signal - kill entire process tree\n\t\t\tconst onAbort = () => {\n\t\t\t\tif (child.pid) {\n\t\t\t\t\tkillProcessTree(child.pid);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tif (signal) {\n\t\t\t\tif (signal.aborted) {\n\t\t\t\t\tonAbort();\n\t\t\t\t} else {\n\t\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true });\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Handle process exit\n\t\t\tchild.on(\"close\", (code) => {\n\t\t\t\tif (timeoutHandle) clearTimeout(timeoutHandle);\n\t\t\t\tif (signal) signal.removeEventListener(\"abort\", onAbort);\n\n\t\t\t\tif (signal?.aborted) {\n\t\t\t\t\treject(new Error(\"aborted\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (timedOut) {\n\t\t\t\t\treject(new Error(`timeout:${timeout}`));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tresolve({ exitCode: code });\n\t\t\t});\n\t\t});\n\t},\n};\n\nexport interface BashToolOptions {\n\t/** Custom operations for command execution. Default: local shell */\n\toperations?: BashOperations;\n\t/** Command prefix prepended to every command (e.g., \"shopt -s expand_aliases\" for alias support) */\n\tcommandPrefix?: string;\n}\n\nexport function createBashTool(cwd: string, options?: BashToolOptions): AgentTool<typeof bashSchema> {\n\tconst ops = options?.operations ?? defaultBashOperations;\n\tconst commandPrefix = options?.commandPrefix;\n\n\treturn {\n\t\tname: \"bash\",\n\t\tlabel: \"bash\",\n\t\tdescription: `Execute a bash command in the current working directory. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Optionally provide a timeout in seconds.`,\n\t\tparameters: bashSchema,\n\t\texecute: async (\n\t\t\t_toolCallId: string,\n\t\t\t{ command, timeout }: { command: string; timeout?: number },\n\t\t\tsignal?: AbortSignal,\n\t\t\tonUpdate?,\n\t\t) => {\n\t\t\t// Apply command prefix if configured (e.g., \"shopt -s expand_aliases\" for alias support)\n\t\t\tconst resolvedCommand = commandPrefix ? `${commandPrefix}\\n${command}` : command;\n\n\t\t\treturn new Promise((resolve, reject) => {\n\t\t\t\t// We'll stream to a temp file if output gets large\n\t\t\t\tlet tempFilePath: string | undefined;\n\t\t\t\tlet tempFileStream: ReturnType<typeof createWriteStream> | undefined;\n\t\t\t\tlet totalBytes = 0;\n\n\t\t\t\t// Keep a rolling buffer of the last chunk for tail truncation\n\t\t\t\tconst chunks: Buffer[] = [];\n\t\t\t\tlet chunksBytes = 0;\n\t\t\t\t// Keep more than we need so we have enough for truncation\n\t\t\t\tconst maxChunksBytes = DEFAULT_MAX_BYTES * 2;\n\n\t\t\t\tconst handleData = (data: Buffer) => {\n\t\t\t\t\ttotalBytes += data.length;\n\n\t\t\t\t\t// Start writing to temp file once we exceed the threshold\n\t\t\t\t\tif (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {\n\t\t\t\t\t\ttempFilePath = getTempFilePath();\n\t\t\t\t\t\ttempFileStream = createWriteStream(tempFilePath);\n\t\t\t\t\t\t// Write all buffered chunks to the file\n\t\t\t\t\t\tfor (const chunk of chunks) {\n\t\t\t\t\t\t\ttempFileStream.write(chunk);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\t// Write to temp file if we have one\n\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\ttempFileStream.write(data);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Keep rolling buffer of recent data\n\t\t\t\t\tchunks.push(data);\n\t\t\t\t\tchunksBytes += data.length;\n\n\t\t\t\t\t// Trim old chunks if buffer is too large\n\t\t\t\t\twhile (chunksBytes > maxChunksBytes && chunks.length > 1) {\n\t\t\t\t\t\tconst removed = chunks.shift()!;\n\t\t\t\t\t\tchunksBytes -= removed.length;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Stream partial output to callback (truncated rolling buffer)\n\t\t\t\t\tif (onUpdate) {\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tconst fullText = fullBuffer.toString(\"utf-8\");\n\t\t\t\t\t\tconst truncation = truncateTail(fullText);\n\t\t\t\t\t\tonUpdate({\n\t\t\t\t\t\t\tcontent: [{ type: \"text\", text: truncation.content || \"\" }],\n\t\t\t\t\t\t\tdetails: {\n\t\t\t\t\t\t\t\ttruncation: truncation.truncated ? truncation : undefined,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tops.exec(resolvedCommand, cwd, { onData: handleData, signal, timeout })\n\t\t\t\t\t.then(({ exitCode }) => {\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Combine all buffered chunks\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tconst fullOutput = fullBuffer.toString(\"utf-8\");\n\n\t\t\t\t\t\t// Apply tail truncation\n\t\t\t\t\t\tconst truncation = truncateTail(fullOutput);\n\t\t\t\t\t\tlet outputText = truncation.content || \"(no output)\";\n\n\t\t\t\t\t\t// Build details with truncation info\n\t\t\t\t\t\tlet details: BashToolDetails | undefined;\n\n\t\t\t\t\t\tif (truncation.truncated) {\n\t\t\t\t\t\t\tdetails = {\n\t\t\t\t\t\t\t\ttruncation,\n\t\t\t\t\t\t\t\tfullOutputPath: tempFilePath,\n\t\t\t\t\t\t\t};\n\n\t\t\t\t\t\t\t// Build actionable notice\n\t\t\t\t\t\t\tconst startLine = truncation.totalLines - truncation.outputLines + 1;\n\t\t\t\t\t\t\tconst endLine = truncation.totalLines;\n\n\t\t\t\t\t\t\tif (truncation.lastLinePartial) {\n\t\t\t\t\t\t\t\t// Edge case: last line alone > 30KB\n\t\t\t\t\t\t\t\tconst lastLineSize = formatSize(Buffer.byteLength(fullOutput.split(\"\\n\").pop() || \"\", \"utf-8\"));\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else if (truncation.truncatedBy === \"lines\") {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\toutputText += `\\n\\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${tempFilePath}]`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (exitCode !== 0 && exitCode !== null) {\n\t\t\t\t\t\t\toutputText += `\\n\\nCommand exited with code ${exitCode}`;\n\t\t\t\t\t\t\treject(new Error(outputText));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresolve({ content: [{ type: \"text\", text: outputText }], details });\n\t\t\t\t\t\t}\n\t\t\t\t\t})\n\t\t\t\t\t.catch((err: Error) => {\n\t\t\t\t\t\t// Close temp file stream\n\t\t\t\t\t\tif (tempFileStream) {\n\t\t\t\t\t\t\ttempFileStream.end();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Combine all buffered chunks for error output\n\t\t\t\t\t\tconst fullBuffer = Buffer.concat(chunks);\n\t\t\t\t\t\tlet output = fullBuffer.toString(\"utf-8\");\n\n\t\t\t\t\t\tif (err.message === \"aborted\") {\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += \"Command aborted\";\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else if (err.message.startsWith(\"timeout:\")) {\n\t\t\t\t\t\t\tconst timeoutSecs = err.message.split(\":\")[1];\n\t\t\t\t\t\t\tif (output) output += \"\\n\\n\";\n\t\t\t\t\t\t\toutput += `Command timed out after ${timeoutSecs} seconds`;\n\t\t\t\t\t\t\treject(new Error(output));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\treject(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t});\n\t\t},\n\t};\n}\n\n/** Default bash tool using process.cwd() - for backwards compatibility */\nexport const bashTool = createBashTool(process.cwd());\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { type BashOperations, type BashToolDetails, type BashToolOptions, bashTool, createBashTool } from "./bash.js";
|
|
1
|
+
export { type BashOperations, type BashToolDetails, type BashToolOptions, bashTool, createBashTool, } from "./bash.js";
|
|
2
2
|
export { createEditTool, type EditOperations, type EditToolDetails, type EditToolOptions, editTool } from "./edit.js";
|
|
3
3
|
export { createFindTool, type FindOperations, type FindToolDetails, type FindToolOptions, findTool } from "./find.js";
|
|
4
4
|
export { createGrepTool, type GrepOperations, type GrepToolDetails, type GrepToolOptions, grepTool } from "./grep.js";
|
|
@@ -7,6 +7,7 @@ export { createReadTool, type ReadOperations, type ReadToolDetails, type ReadToo
|
|
|
7
7
|
export { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, type TruncationOptions, type TruncationResult, truncateHead, truncateLine, truncateTail, } from "./truncate.js";
|
|
8
8
|
export { createWriteTool, type WriteOperations, type WriteToolOptions, writeTool } from "./write.js";
|
|
9
9
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
|
10
|
+
import { type BashToolOptions } from "./bash.js";
|
|
10
11
|
import { type ReadToolOptions } from "./read.js";
|
|
11
12
|
/** Tool type (AgentTool from pi-ai) */
|
|
12
13
|
export type Tool = AgentTool<any>;
|
|
@@ -54,6 +55,8 @@ export type ToolName = keyof typeof allTools;
|
|
|
54
55
|
export interface ToolsOptions {
|
|
55
56
|
/** Options for the read tool */
|
|
56
57
|
read?: ReadToolOptions;
|
|
58
|
+
/** Options for the bash tool */
|
|
59
|
+
bash?: BashToolOptions;
|
|
57
60
|
}
|
|
58
61
|
/**
|
|
59
62
|
* Create coding tools configured for a specific working directory.
|