gsd-pi 2.58.0-dev.778d6ac → 2.58.0-dev.e002a57
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/dist/cli.js +11 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +11 -8
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +9 -16
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +22 -1
- package/dist/resources/extensions/gsd/codebase-generator.js +279 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +10 -1
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +5 -0
- package/dist/resources/extensions/gsd/commands-codebase.js +115 -0
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +41 -4
- package/dist/resources/extensions/gsd/complexity-classifier.js +8 -6
- package/dist/resources/extensions/gsd/doctor-git-checks.js +48 -1
- package/dist/resources/extensions/gsd/doctor-proactive.js +34 -1
- package/dist/resources/extensions/gsd/error-classifier.js +3 -4
- package/dist/resources/extensions/gsd/git-service.js +82 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +22 -0
- package/dist/resources/extensions/gsd/paths.js +2 -0
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/watch/header-renderer.js +241 -0
- package/dist/resources/extensions/search-the-web/url-utils.js +17 -0
- package/dist/security-overrides.d.ts +11 -0
- package/dist/security-overrides.js +41 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +16 -16
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +16 -16
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/welcome-screen.d.ts +1 -0
- package/dist/welcome-screen.js +32 -6
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/resolve-config-value.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.js +23 -2
- package/packages/pi-coding-agent/dist/core/resolve-config-value.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +89 -2
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager-security.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/settings-manager-security.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager-security.test.js +83 -0
- package/packages/pi-coding-agent/dist/core/settings-manager-security.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +14 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +36 -3
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -0
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/armin.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/armin.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/armin.js +9 -8
- package/packages/pi-coding-agent/dist/modes/interactive/components/armin.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +0 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js +2 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bordered-loader.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/config-selector.js +5 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/countdown-timer.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/countdown-timer.js +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/countdown-timer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/custom-message.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/daxnuts.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/daxnuts.js +4 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/daxnuts.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/diff.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js +8 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.js +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js +26 -12
- package/packages/pi-coding-agent/dist/modes/interactive/components/footer.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/oauth-selector.js +4 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js +46 -14
- package/packages/pi-coding-agent/dist/modes/interactive/components/provider-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js +2 -8
- package/packages/pi-coding-agent/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js +4 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js +2 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +8 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message-selector.js +3 -2
- package/packages/pi-coding-agent/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +15 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +16 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +27 -4
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +111 -1
- package/packages/pi-coding-agent/src/core/resolve-config-value.ts +26 -2
- package/packages/pi-coding-agent/src/core/settings-manager-security.test.ts +102 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +44 -3
- package/packages/pi-coding-agent/src/index.ts +5 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/armin.ts +9 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +0 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/bash-execution.ts +3 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/bordered-loader.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/branch-summary-message.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/compaction-summary-message.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/config-selector.ts +7 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/countdown-timer.ts +3 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/custom-message.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/daxnuts.ts +4 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/diff.ts +2 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/dynamic-border.ts +3 -1
- package/packages/pi-coding-agent/src/modes/interactive/components/extension-input.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/extension-selector.ts +4 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/footer.ts +27 -13
- package/packages/pi-coding-agent/src/modes/interactive/components/oauth-selector.ts +4 -4
- package/packages/pi-coding-agent/src/modes/interactive/components/provider-manager.ts +45 -14
- package/packages/pi-coding-agent/src/modes/interactive/components/scoped-models-selector.ts +2 -7
- package/packages/pi-coding-agent/src/modes/interactive/components/session-selector.ts +4 -4
- package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts +2 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +8 -3
- package/packages/pi-coding-agent/src/modes/interactive/components/user-message-selector.ts +3 -2
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +17 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +14 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +35 -3
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +7 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +1 -1
- package/pkg/dist/modes/interactive/theme/themes.js +1 -1
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/src/resources/extensions/gsd/auto-worktree.ts +10 -7
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +10 -16
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +22 -1
- package/src/resources/extensions/gsd/codebase-generator.ts +351 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +10 -1
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +5 -0
- package/src/resources/extensions/gsd/commands-codebase.ts +164 -0
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +46 -4
- package/src/resources/extensions/gsd/complexity-classifier.ts +8 -6
- package/src/resources/extensions/gsd/doctor-git-checks.ts +49 -1
- package/src/resources/extensions/gsd/doctor-proactive.ts +35 -1
- package/src/resources/extensions/gsd/doctor-types.ts +2 -0
- package/src/resources/extensions/gsd/error-classifier.ts +3 -4
- package/src/resources/extensions/gsd/git-service.ts +93 -0
- package/src/resources/extensions/gsd/native-git-bridge.ts +24 -0
- package/src/resources/extensions/gsd/paths.ts +2 -0
- package/src/resources/extensions/gsd/preferences-types.ts +8 -0
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +488 -0
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/integration/doctor-git.test.ts +72 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +44 -0
- package/src/resources/extensions/gsd/watch/header-renderer.ts +275 -0
- package/src/resources/extensions/search-the-web/url-utils.ts +19 -0
- /package/dist/web/standalone/.next/static/{R0D4xaIPl5kg93edN7Oo0 → nUA6d2OJrDSVq9RNb-c8b}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{R0D4xaIPl5kg93edN7Oo0 → nUA6d2OJrDSVq9RNb-c8b}/_ssgManifest.js +0 -0
|
@@ -152,6 +152,23 @@ export interface Settings {
|
|
|
152
152
|
modelDiscovery?: ModelDiscoverySettings;
|
|
153
153
|
editMode?: "standard" | "hashline"; // Edit tool mode: "standard" (text match) or "hashline" (LINE#ID anchors). Default: "standard"
|
|
154
154
|
timestampFormat?: "date-time-iso" | "date-time-us"; // Timestamp display format for messages. Default: "date-time-iso"
|
|
155
|
+
allowedCommandPrefixes?: string[]; // Override built-in SAFE_COMMAND_PREFIXES for !command resolution (global-only — ignored in project settings)
|
|
156
|
+
fetchAllowedUrls?: string[]; // Hostnames exempted from SSRF blocklist in fetch_page (global-only — ignored in project settings)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Settings keys that are only respected from global config — project settings cannot override these. */
|
|
160
|
+
const GLOBAL_ONLY_KEYS: ReadonlySet<keyof Settings> = new Set([
|
|
161
|
+
"allowedCommandPrefixes",
|
|
162
|
+
"fetchAllowedUrls",
|
|
163
|
+
]);
|
|
164
|
+
|
|
165
|
+
/** Remove global-only keys from a settings object. Applied once at load time. */
|
|
166
|
+
function stripGlobalOnlyKeys(settings: Settings): Settings {
|
|
167
|
+
const result = { ...settings };
|
|
168
|
+
for (const key of GLOBAL_ONLY_KEYS) {
|
|
169
|
+
delete (result as Record<string, unknown>)[key];
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
155
172
|
}
|
|
156
173
|
|
|
157
174
|
/** Deep merge settings: project/overrides take precedence, nested objects merge recursively */
|
|
@@ -304,7 +321,7 @@ export class SettingsManager {
|
|
|
304
321
|
) {
|
|
305
322
|
this.storage = storage;
|
|
306
323
|
this.globalSettings = initialGlobal;
|
|
307
|
-
this.projectSettings = initialProject;
|
|
324
|
+
this.projectSettings = stripGlobalOnlyKeys(initialProject);
|
|
308
325
|
this.globalSettingsLoadError = globalLoadError;
|
|
309
326
|
this.projectSettingsLoadError = projectLoadError;
|
|
310
327
|
this.errors = [...initialErrors];
|
|
@@ -441,7 +458,7 @@ export class SettingsManager {
|
|
|
441
458
|
|
|
442
459
|
const projectLoad = SettingsManager.tryLoadFromStorage(this.storage, "project");
|
|
443
460
|
if (!projectLoad.error) {
|
|
444
|
-
this.projectSettings = projectLoad.settings;
|
|
461
|
+
this.projectSettings = stripGlobalOnlyKeys(projectLoad.settings);
|
|
445
462
|
this.projectSettingsLoadError = null;
|
|
446
463
|
} else {
|
|
447
464
|
this.projectSettingsLoadError = projectLoad.error;
|
|
@@ -571,7 +588,7 @@ export class SettingsManager {
|
|
|
571
588
|
}
|
|
572
589
|
|
|
573
590
|
private saveProjectSettings(settings: Settings): void {
|
|
574
|
-
this.projectSettings = structuredClone(settings);
|
|
591
|
+
this.projectSettings = stripGlobalOnlyKeys(structuredClone(settings));
|
|
575
592
|
this.settings = deepMergeSettings(this.globalSettings, this.projectSettings);
|
|
576
593
|
|
|
577
594
|
if (this.projectSettingsLoadError) {
|
|
@@ -1096,4 +1113,28 @@ export class SettingsManager {
|
|
|
1096
1113
|
setTimestampFormat(format: "date-time-iso" | "date-time-us"): void {
|
|
1097
1114
|
this.setGlobalSetting("timestampFormat", format);
|
|
1098
1115
|
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Get the allowed command prefixes from global settings only.
|
|
1119
|
+
* Returns undefined if not configured (caller should use built-in defaults).
|
|
1120
|
+
*/
|
|
1121
|
+
getAllowedCommandPrefixes(): string[] | undefined {
|
|
1122
|
+
return this.globalSettings.allowedCommandPrefixes;
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
setAllowedCommandPrefixes(prefixes: string[]): void {
|
|
1126
|
+
this.setGlobalSetting("allowedCommandPrefixes", prefixes);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
* Get the fetch URL allowlist from global settings only.
|
|
1131
|
+
* Returns undefined if not configured (caller should use empty allowlist).
|
|
1132
|
+
*/
|
|
1133
|
+
getFetchAllowedUrls(): string[] | undefined {
|
|
1134
|
+
return this.globalSettings.fetchAllowedUrls;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
setFetchAllowedUrls(urls: string[]): void {
|
|
1138
|
+
this.setGlobalSetting("fetchAllowedUrls", urls);
|
|
1139
|
+
}
|
|
1099
1140
|
}
|
|
@@ -225,6 +225,11 @@ export {
|
|
|
225
225
|
SettingsManager,
|
|
226
226
|
type TaskIsolationSettings,
|
|
227
227
|
} from "./core/settings-manager.js";
|
|
228
|
+
export {
|
|
229
|
+
SAFE_COMMAND_PREFIXES,
|
|
230
|
+
setAllowedCommandPrefixes,
|
|
231
|
+
getAllowedCommandPrefixes,
|
|
232
|
+
} from "./core/resolve-config-value.js";
|
|
228
233
|
// Skills
|
|
229
234
|
export {
|
|
230
235
|
ECOSYSTEM_SKILLS_DIR,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Armin says hi! A fun easter egg with animated XBM art.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type
|
|
5
|
+
import { type Component, type TUI, visibleWidth } from "@gsd/pi-tui";
|
|
6
6
|
import { theme } from "../theme/theme.js";
|
|
7
7
|
|
|
8
8
|
// XBM image: 31x36 pixels, LSB first, 1=background, 0=foreground
|
|
@@ -88,20 +88,20 @@ export class ArminComponent implements Component {
|
|
|
88
88
|
return this.cachedLines;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
const
|
|
92
|
-
|
|
91
|
+
const center = (s: string) => {
|
|
92
|
+
const visible = visibleWidth(s);
|
|
93
|
+
const left = Math.max(0, Math.floor((width - visible) / 2));
|
|
94
|
+
return " ".repeat(left) + s;
|
|
95
|
+
};
|
|
93
96
|
|
|
94
97
|
this.cachedLines = this.currentGrid.map((row) => {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
const padRight = Math.max(0, width - padding - clipped.length);
|
|
98
|
-
return ` ${theme.fg("accent", clipped)}${" ".repeat(padRight)}`;
|
|
98
|
+
const clipped = row.slice(0, width).join("");
|
|
99
|
+
return center(theme.fg("accent", clipped));
|
|
99
100
|
});
|
|
100
101
|
|
|
101
102
|
// Add "ARMIN SAYS HI" at the end
|
|
102
103
|
const message = "ARMIN SAYS HI";
|
|
103
|
-
|
|
104
|
-
this.cachedLines.push(` ${theme.fg("accent", message)}${" ".repeat(msgPadRight)}`);
|
|
104
|
+
this.cachedLines.push(center(theme.fg("accent", message)));
|
|
105
105
|
|
|
106
106
|
this.cachedWidth = width;
|
|
107
107
|
this.cachedVersion = this.gridVersion;
|
|
@@ -105,8 +105,6 @@ export class AssistantMessageComponent extends Container {
|
|
|
105
105
|
: "Operation aborted";
|
|
106
106
|
if (hasVisibleContent) {
|
|
107
107
|
this.contentContainer.addChild(new Spacer(1));
|
|
108
|
-
} else {
|
|
109
|
-
this.contentContainer.addChild(new Spacer(1));
|
|
110
108
|
}
|
|
111
109
|
this.contentContainer.addChild(new Text(theme.fg("error", abortMessage), 1, 0));
|
|
112
110
|
} else if (message.stopReason === "error") {
|
|
@@ -29,6 +29,7 @@ export class BashExecutionComponent extends Container {
|
|
|
29
29
|
private expanded = false;
|
|
30
30
|
private contentContainer: Container;
|
|
31
31
|
private ui: TUI;
|
|
32
|
+
private _borderColorKey: "dim" | "bashMode";
|
|
32
33
|
|
|
33
34
|
constructor(command: string, ui: TUI, excludeFromContext = false) {
|
|
34
35
|
super();
|
|
@@ -37,6 +38,7 @@ export class BashExecutionComponent extends Container {
|
|
|
37
38
|
|
|
38
39
|
// Use dim border for excluded-from-context commands (!! prefix)
|
|
39
40
|
const colorKey = excludeFromContext ? "dim" : "bashMode";
|
|
41
|
+
this._borderColorKey = colorKey;
|
|
40
42
|
const borderColor = (str: string) => theme.fg(colorKey, str);
|
|
41
43
|
|
|
42
44
|
// Add spacer
|
|
@@ -137,7 +139,7 @@ export class BashExecutionComponent extends Container {
|
|
|
137
139
|
this.contentContainer.clear();
|
|
138
140
|
|
|
139
141
|
// Command header
|
|
140
|
-
const header = new Text(theme.fg(
|
|
142
|
+
const header = new Text(theme.fg(this._borderColorKey, theme.bold(`$ ${this.command}`)), 1, 0);
|
|
141
143
|
this.contentContainer.addChild(header);
|
|
142
144
|
|
|
143
145
|
// Output
|
|
@@ -34,8 +34,8 @@ export class BorderedLoader extends Container {
|
|
|
34
34
|
if (this.cancellable) {
|
|
35
35
|
this.addChild(new Spacer(1));
|
|
36
36
|
this.addChild(new Text(keyHint("selectCancel", "cancel"), 1, 0));
|
|
37
|
+
this.addChild(new Spacer(1));
|
|
37
38
|
}
|
|
38
|
-
this.addChild(new Spacer(1));
|
|
39
39
|
this.addChild(new DynamicBorder(borderColor));
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -32,7 +32,7 @@ export class BranchSummaryMessageComponent extends Box {
|
|
|
32
32
|
private updateDisplay(): void {
|
|
33
33
|
this.clear();
|
|
34
34
|
|
|
35
|
-
const label = theme.fg("customMessageLabel",
|
|
35
|
+
const label = theme.fg("customMessageLabel", theme.bold("[branch]"));
|
|
36
36
|
this.addChild(new Text(label, 0, 0));
|
|
37
37
|
this.addChild(new Spacer(1));
|
|
38
38
|
|
package/packages/pi-coding-agent/src/modes/interactive/components/compaction-summary-message.ts
CHANGED
|
@@ -33,7 +33,7 @@ export class CompactionSummaryMessageComponent extends Box {
|
|
|
33
33
|
this.clear();
|
|
34
34
|
|
|
35
35
|
const tokenStr = this.message.tokensBefore.toLocaleString();
|
|
36
|
-
const label = theme.fg("customMessageLabel",
|
|
36
|
+
const label = theme.fg("customMessageLabel", theme.bold("[compaction]"));
|
|
37
37
|
this.addChild(new Text(label, 0, 0));
|
|
38
38
|
this.addChild(new Spacer(1));
|
|
39
39
|
|
|
@@ -346,9 +346,14 @@ class ResourceList implements Component, Focusable {
|
|
|
346
346
|
}
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
-
// Scroll indicator
|
|
349
|
+
// Scroll indicator — count only selectable items (exclude group/subgroup headers)
|
|
350
350
|
if (startIndex > 0 || endIndex < this.filteredItems.length) {
|
|
351
|
-
|
|
351
|
+
const selectableItems = this.filteredItems.filter((e) => e.type === "item");
|
|
352
|
+
const selectableTotal = selectableItems.length;
|
|
353
|
+
const selectablePosition = selectableItems.findIndex(
|
|
354
|
+
(e) => this.filteredItems.indexOf(e) === this.selectedIndex,
|
|
355
|
+
);
|
|
356
|
+
lines.push(theme.fg("dim", ` (${selectablePosition + 1}/${selectableTotal})`));
|
|
352
357
|
}
|
|
353
358
|
|
|
354
359
|
return lines;
|
|
@@ -7,6 +7,7 @@ import type { TUI } from "@gsd/pi-tui";
|
|
|
7
7
|
export class CountdownTimer {
|
|
8
8
|
private intervalId: ReturnType<typeof setInterval> | undefined;
|
|
9
9
|
private remainingSeconds: number;
|
|
10
|
+
private _disposed = false;
|
|
10
11
|
|
|
11
12
|
constructor(
|
|
12
13
|
timeoutMs: number,
|
|
@@ -18,6 +19,7 @@ export class CountdownTimer {
|
|
|
18
19
|
this.onTick(this.remainingSeconds);
|
|
19
20
|
|
|
20
21
|
this.intervalId = setInterval(() => {
|
|
22
|
+
if (this._disposed) return;
|
|
21
23
|
this.remainingSeconds--;
|
|
22
24
|
this.onTick(this.remainingSeconds);
|
|
23
25
|
this.tui?.requestRender();
|
|
@@ -30,6 +32,7 @@ export class CountdownTimer {
|
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
dispose(): void {
|
|
35
|
+
this._disposed = true;
|
|
33
36
|
if (this.intervalId) {
|
|
34
37
|
clearInterval(this.intervalId);
|
|
35
38
|
this.intervalId = undefined;
|
|
@@ -75,7 +75,7 @@ export class CustomMessageComponent extends Container {
|
|
|
75
75
|
this.box.clear();
|
|
76
76
|
|
|
77
77
|
// Default rendering: label + content
|
|
78
|
-
const label = theme.fg("customMessageLabel",
|
|
78
|
+
const label = theme.fg("customMessageLabel", theme.bold(`[${this.message.customType}]`));
|
|
79
79
|
this.box.addChild(new Text(label, 0, 0));
|
|
80
80
|
this.box.addChild(new Spacer(1));
|
|
81
81
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* A heartfelt tribute to dax (@thdxr) for providing free Kimi K2.5 access via OpenCode.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type
|
|
7
|
+
import { type Component, type TUI, visibleWidth } from "@gsd/pi-tui";
|
|
8
8
|
import { theme } from "../theme/theme.js";
|
|
9
9
|
|
|
10
10
|
// 32x32 RGB image of dax, hex encoded (3 bytes per pixel)
|
|
@@ -101,7 +101,7 @@ export class DaxnutsComponent implements Component {
|
|
|
101
101
|
const lines: string[] = [];
|
|
102
102
|
|
|
103
103
|
const center = (s: string) => {
|
|
104
|
-
const visible = s
|
|
104
|
+
const visible = visibleWidth(s);
|
|
105
105
|
const left = Math.max(0, Math.floor((width - visible) / 2));
|
|
106
106
|
return " ".repeat(left) + s;
|
|
107
107
|
};
|
|
@@ -145,7 +145,8 @@ export class DaxnutsComponent implements Component {
|
|
|
145
145
|
lines.push("");
|
|
146
146
|
if (textPhase > 2 || this.tick >= this.maxTicks) {
|
|
147
147
|
lines.push(center(t.fg("dim", "Try OpenCode")));
|
|
148
|
-
|
|
148
|
+
// URL removed — was pointing to an incorrect destination
|
|
149
|
+
lines.push(center(t.fg("mdLink", "opencode.ai")));
|
|
149
150
|
} else {
|
|
150
151
|
lines.push("");
|
|
151
152
|
lines.push("");
|
|
@@ -6,7 +6,7 @@ import { theme } from "../theme/theme.js";
|
|
|
6
6
|
* Format: "+123 content" or "-123 content" or " 123 content" or " ..."
|
|
7
7
|
*/
|
|
8
8
|
function parseDiffLine(line: string): { prefix: string; lineNum: string; content: string } | null {
|
|
9
|
-
const match = line.match(/^([
|
|
9
|
+
const match = line.match(/^([+\- ])(\s*\d*)\s(.*)$/);
|
|
10
10
|
if (!match) return null;
|
|
11
11
|
return { prefix: match[1], lineNum: match[2], content: match[3] };
|
|
12
12
|
}
|
|
@@ -15,7 +15,7 @@ function parseDiffLine(line: string): { prefix: string; lineNum: string; content
|
|
|
15
15
|
* Replace tabs with spaces for consistent rendering.
|
|
16
16
|
*/
|
|
17
17
|
function replaceTabs(text: string): string {
|
|
18
|
-
return text.replace(/\t/g, "
|
|
18
|
+
return text.replace(/\t/g, " ");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
@@ -11,7 +11,9 @@ import { theme } from "../theme/theme.js";
|
|
|
11
11
|
export class DynamicBorder implements Component {
|
|
12
12
|
private color: (str: string) => string;
|
|
13
13
|
|
|
14
|
-
constructor(color: (str: string) => string = (str) =>
|
|
14
|
+
constructor(color: (str: string) => string = (str) => {
|
|
15
|
+
try { return theme.fg("border", str); } catch { return str; }
|
|
16
|
+
}) {
|
|
15
17
|
this.color = color;
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -74,6 +74,7 @@ export class ExtensionInputComponent extends Container implements Focusable {
|
|
|
74
74
|
handleInput(keyData: string): void {
|
|
75
75
|
const kb = getEditorKeybindings();
|
|
76
76
|
if (kb.matches(keyData, "selectConfirm") || keyData === "\n") {
|
|
77
|
+
if (this.input.getValue().trim() === "") return;
|
|
77
78
|
this.onSubmitCallback(this.input.getValue());
|
|
78
79
|
} else if (kb.matches(keyData, "selectCancel")) {
|
|
79
80
|
this.onCancelCallback();
|
|
@@ -96,6 +96,10 @@ export class ExtensionSelectorComponent extends Container {
|
|
|
96
96
|
if (idx < 0 || idx >= this.options.length) {
|
|
97
97
|
return Math.max(0, Math.min(from, this.options.length - 1));
|
|
98
98
|
}
|
|
99
|
+
// If all items are separators, idx may still point to one — fall back to original index
|
|
100
|
+
if (this.isSeparator(idx)) {
|
|
101
|
+
return Math.max(0, Math.min(from, this.options.length - 1));
|
|
102
|
+
}
|
|
99
103
|
return idx;
|
|
100
104
|
}
|
|
101
105
|
|
|
@@ -110,29 +110,36 @@ export class FooterComponent implements Component {
|
|
|
110
110
|
pwd = `${pwd} • ${sessionName}`;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
// Build stats line
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
// Build stats line as separate groups joined by a dim middle-dot separator
|
|
114
|
+
const sep = ` ${theme.fg("dim", "\u00B7")} `;
|
|
115
|
+
|
|
116
|
+
// Group 1: token I/O
|
|
117
|
+
const tokenGroup: string[] = [];
|
|
118
|
+
if (totalInput) tokenGroup.push(`↑${formatTokens(totalInput)}`);
|
|
119
|
+
if (totalOutput) tokenGroup.push(`↓${formatTokens(totalOutput)}`);
|
|
120
|
+
|
|
121
|
+
// Group 2: cache metrics
|
|
122
|
+
const cacheGroup: string[] = [];
|
|
123
|
+
if (totalCacheRead) cacheGroup.push(`cr:${formatTokens(totalCacheRead)}`);
|
|
124
|
+
if (totalCacheWrite) cacheGroup.push(`cw:${formatTokens(totalCacheWrite)}`);
|
|
125
|
+
|
|
126
|
+
// Group 3: cost
|
|
127
|
+
const costGroup: string[] = [];
|
|
121
128
|
const usingSubscription = displayModel ? this.session.modelRegistry.isUsingOAuth(displayModel) : false;
|
|
122
129
|
if (totalCost || usingSubscription) {
|
|
123
130
|
const costStr = `$${totalCost.toFixed(3)}${usingSubscription ? " (sub)" : ""}`;
|
|
124
|
-
|
|
131
|
+
costGroup.push(costStr);
|
|
125
132
|
}
|
|
126
133
|
|
|
127
134
|
// Per-prompt cost annotation (opt-in via show_token_cost preference, #1515)
|
|
128
135
|
if (process.env.GSD_SHOW_TOKEN_COST === "1") {
|
|
129
136
|
const lastTurnCost = this.session.getLastTurnCost();
|
|
130
137
|
if (lastTurnCost > 0) {
|
|
131
|
-
|
|
138
|
+
costGroup.push(`(last: ${formatPromptCost(lastTurnCost)})`);
|
|
132
139
|
}
|
|
133
140
|
}
|
|
134
141
|
|
|
135
|
-
//
|
|
142
|
+
// Group 4: context percentage (colorized)
|
|
136
143
|
let contextPercentStr: string;
|
|
137
144
|
const autoIndicator = this.autoCompactEnabled ? " (auto)" : "";
|
|
138
145
|
const contextPercentDisplay =
|
|
@@ -146,9 +153,16 @@ export class FooterComponent implements Component {
|
|
|
146
153
|
} else {
|
|
147
154
|
contextPercentStr = contextPercentDisplay;
|
|
148
155
|
}
|
|
149
|
-
statsParts.push(contextPercentStr);
|
|
150
156
|
|
|
151
|
-
|
|
157
|
+
// Assemble groups: items within a group are space-separated,
|
|
158
|
+
// groups are separated by a dim middle-dot
|
|
159
|
+
const groups: string[] = [];
|
|
160
|
+
if (tokenGroup.length > 0) groups.push(tokenGroup.join(" "));
|
|
161
|
+
if (cacheGroup.length > 0) groups.push(cacheGroup.join(" "));
|
|
162
|
+
if (costGroup.length > 0) groups.push(costGroup.join(" "));
|
|
163
|
+
groups.push(contextPercentStr);
|
|
164
|
+
|
|
165
|
+
let statsLeft = groups.join(sep);
|
|
152
166
|
|
|
153
167
|
// Add model name on the right side, plus thinking level if model supports it
|
|
154
168
|
const modelName = displayModel?.id || "no-model";
|
|
@@ -96,14 +96,14 @@ export class OAuthSelectorComponent extends Container {
|
|
|
96
96
|
|
|
97
97
|
handleInput(keyData: string): void {
|
|
98
98
|
const kb = getEditorKeybindings();
|
|
99
|
-
// Up arrow
|
|
99
|
+
// Up arrow (wrap)
|
|
100
100
|
if (kb.matches(keyData, "selectUp")) {
|
|
101
|
-
this.selectedIndex =
|
|
101
|
+
this.selectedIndex = this.selectedIndex === 0 ? this.allProviders.length - 1 : this.selectedIndex - 1;
|
|
102
102
|
this.updateList();
|
|
103
103
|
}
|
|
104
|
-
// Down arrow
|
|
104
|
+
// Down arrow (wrap)
|
|
105
105
|
else if (kb.matches(keyData, "selectDown")) {
|
|
106
|
-
this.selectedIndex =
|
|
106
|
+
this.selectedIndex = this.selectedIndex === this.allProviders.length - 1 ? 0 : this.selectedIndex + 1;
|
|
107
107
|
this.updateList();
|
|
108
108
|
}
|
|
109
109
|
// Enter
|
|
@@ -43,6 +43,8 @@ export class ProviderManagerComponent extends Container implements Focusable {
|
|
|
43
43
|
private modelsJsonWriter: ModelsJsonWriter;
|
|
44
44
|
private onDone: () => void;
|
|
45
45
|
private onDiscover: (provider: string) => void;
|
|
46
|
+
private confirmingRemove = false;
|
|
47
|
+
private hintsContainer: Container;
|
|
46
48
|
|
|
47
49
|
constructor(
|
|
48
50
|
tui: TUI,
|
|
@@ -65,12 +67,9 @@ export class ProviderManagerComponent extends Container implements Focusable {
|
|
|
65
67
|
this.addChild(new Spacer(1));
|
|
66
68
|
|
|
67
69
|
// Hints
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
rawKeyHint("esc", "close"),
|
|
72
|
-
].join(" ");
|
|
73
|
-
this.addChild(new Text(hints, 0, 0));
|
|
70
|
+
this.hintsContainer = new Container();
|
|
71
|
+
this.addChild(this.hintsContainer);
|
|
72
|
+
this.updateHints();
|
|
74
73
|
this.addChild(new Spacer(1));
|
|
75
74
|
|
|
76
75
|
// List
|
|
@@ -116,6 +115,24 @@ export class ProviderManagerComponent extends Container implements Focusable {
|
|
|
116
115
|
this.selectedIndex = Math.min(this.selectedIndex, this.providers.length - 1);
|
|
117
116
|
}
|
|
118
117
|
|
|
118
|
+
private updateHints(): void {
|
|
119
|
+
this.hintsContainer.clear();
|
|
120
|
+
if (this.confirmingRemove) {
|
|
121
|
+
const hints = [
|
|
122
|
+
rawKeyHint("r", "confirm removal"),
|
|
123
|
+
rawKeyHint("esc", "cancel"),
|
|
124
|
+
].join(" ");
|
|
125
|
+
this.hintsContainer.addChild(new Text(hints, 0, 0));
|
|
126
|
+
} else {
|
|
127
|
+
const hints = [
|
|
128
|
+
rawKeyHint("d", "discover"),
|
|
129
|
+
rawKeyHint("r", "remove auth"),
|
|
130
|
+
rawKeyHint("esc", "close"),
|
|
131
|
+
].join(" ");
|
|
132
|
+
this.hintsContainer.addChild(new Text(hints, 0, 0));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
119
136
|
private updateList(): void {
|
|
120
137
|
this.listContainer.clear();
|
|
121
138
|
|
|
@@ -156,7 +173,13 @@ export class ProviderManagerComponent extends Container implements Focusable {
|
|
|
156
173
|
this.updateList();
|
|
157
174
|
this.tui.requestRender();
|
|
158
175
|
} else if (kb.matches(keyData, "selectCancel")) {
|
|
159
|
-
this.
|
|
176
|
+
if (this.confirmingRemove) {
|
|
177
|
+
this.confirmingRemove = false;
|
|
178
|
+
this.updateHints();
|
|
179
|
+
this.tui.requestRender();
|
|
180
|
+
} else {
|
|
181
|
+
this.onDone();
|
|
182
|
+
}
|
|
160
183
|
} else if (keyData === "d" || keyData === "D") {
|
|
161
184
|
const provider = this.providers[this.selectedIndex];
|
|
162
185
|
if (provider?.supportsDiscovery) {
|
|
@@ -164,13 +187,21 @@ export class ProviderManagerComponent extends Container implements Focusable {
|
|
|
164
187
|
}
|
|
165
188
|
} else if (keyData === "r" || keyData === "R") {
|
|
166
189
|
const provider = this.providers[this.selectedIndex];
|
|
167
|
-
if (provider) {
|
|
168
|
-
this.
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
190
|
+
if (provider?.hasAuth) {
|
|
191
|
+
if (this.confirmingRemove) {
|
|
192
|
+
this.confirmingRemove = false;
|
|
193
|
+
this.authStorage.remove(provider.name);
|
|
194
|
+
this.modelsJsonWriter.removeProvider(provider.name);
|
|
195
|
+
this.modelRegistry.refresh();
|
|
196
|
+
this.loadProviders();
|
|
197
|
+
this.updateHints();
|
|
198
|
+
this.updateList();
|
|
199
|
+
this.tui.requestRender();
|
|
200
|
+
} else {
|
|
201
|
+
this.confirmingRemove = true;
|
|
202
|
+
this.updateHints();
|
|
203
|
+
this.tui.requestRender();
|
|
204
|
+
}
|
|
174
205
|
}
|
|
175
206
|
}
|
|
176
207
|
}
|
|
@@ -318,14 +318,9 @@ export class ScopedModelsSelectorComponent extends Container implements Focusabl
|
|
|
318
318
|
return;
|
|
319
319
|
}
|
|
320
320
|
|
|
321
|
-
// Ctrl+C -
|
|
321
|
+
// Ctrl+C - always cancel immediately
|
|
322
322
|
if (matchesKey(data, Key.ctrl("c"))) {
|
|
323
|
-
|
|
324
|
-
this.searchInput.setValue("");
|
|
325
|
-
this.refresh();
|
|
326
|
-
} else {
|
|
327
|
-
this.callbacks.onCancel();
|
|
328
|
-
}
|
|
323
|
+
this.callbacks.onCancel();
|
|
329
324
|
return;
|
|
330
325
|
}
|
|
331
326
|
|
|
@@ -570,13 +570,13 @@ class SessionList implements Component, Focusable {
|
|
|
570
570
|
return;
|
|
571
571
|
}
|
|
572
572
|
|
|
573
|
-
// Up arrow
|
|
573
|
+
// Up arrow (wrap)
|
|
574
574
|
if (kb.matches(keyData, "selectUp")) {
|
|
575
|
-
this.selectedIndex =
|
|
575
|
+
this.selectedIndex = this.selectedIndex === 0 ? this.filteredSessions.length - 1 : this.selectedIndex - 1;
|
|
576
576
|
}
|
|
577
|
-
// Down arrow
|
|
577
|
+
// Down arrow (wrap)
|
|
578
578
|
else if (kb.matches(keyData, "selectDown")) {
|
|
579
|
-
this.selectedIndex =
|
|
579
|
+
this.selectedIndex = this.selectedIndex === this.filteredSessions.length - 1 ? 0 : this.selectedIndex + 1;
|
|
580
580
|
}
|
|
581
581
|
// Page up - jump up by maxVisible items
|
|
582
582
|
else if (kb.matches(keyData, "selectPageUp")) {
|
package/packages/pi-coding-agent/src/modes/interactive/components/skill-invocation-message.ts
CHANGED
|
@@ -35,7 +35,7 @@ export class SkillInvocationMessageComponent extends Box {
|
|
|
35
35
|
|
|
36
36
|
if (this.expanded) {
|
|
37
37
|
// Expanded: label + skill name header + full content
|
|
38
|
-
const label = theme.fg("customMessageLabel",
|
|
38
|
+
const label = theme.fg("customMessageLabel", theme.bold("[skill]"));
|
|
39
39
|
this.addChild(new Text(label, 0, 0));
|
|
40
40
|
const header = `**${this.skillBlock.name}**\n\n`;
|
|
41
41
|
this.addChild(
|
|
@@ -46,7 +46,7 @@ export class SkillInvocationMessageComponent extends Box {
|
|
|
46
46
|
} else {
|
|
47
47
|
// Collapsed: single line - [skill] name (hint to expand)
|
|
48
48
|
const line =
|
|
49
|
-
theme.fg("customMessageLabel",
|
|
49
|
+
theme.fg("customMessageLabel", theme.bold("[skill]") + " ") +
|
|
50
50
|
theme.fg("customMessageText", this.skillBlock.name) +
|
|
51
51
|
theme.fg("dim", ` (${editorKey("expandTools")} to expand)`);
|
|
52
52
|
this.addChild(new Text(line, 0, 0));
|
|
@@ -32,7 +32,7 @@ const WRITE_PARTIAL_FULL_HIGHLIGHT_LINES = 50;
|
|
|
32
32
|
* Replace tabs with spaces for consistent rendering
|
|
33
33
|
*/
|
|
34
34
|
function replaceTabs(text: string): string {
|
|
35
|
-
return text.replace(/\t/g, "
|
|
35
|
+
return text.replace(/\t/g, " ");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
/**
|
|
@@ -915,8 +915,13 @@ export class ToolExecutionComponent extends Container {
|
|
|
915
915
|
// Generic tool (shouldn't reach here for custom tools)
|
|
916
916
|
text = theme.fg("toolTitle", theme.bold(this.toolName));
|
|
917
917
|
|
|
918
|
-
const
|
|
919
|
-
|
|
918
|
+
const contentLines = JSON.stringify(this.args, null, 2).split("\n");
|
|
919
|
+
const maxContentLines = 20;
|
|
920
|
+
const truncatedContent = contentLines.slice(0, maxContentLines);
|
|
921
|
+
if (contentLines.length > maxContentLines) {
|
|
922
|
+
truncatedContent.push("...");
|
|
923
|
+
}
|
|
924
|
+
text += `\n\n${truncatedContent.join("\n")}`;
|
|
920
925
|
const output = this.getTextOutput();
|
|
921
926
|
if (output) {
|
|
922
927
|
text += `\n${output}`;
|
|
@@ -131,9 +131,10 @@ export class UserMessageSelectorComponent extends Container {
|
|
|
131
131
|
this.addChild(new Spacer(1));
|
|
132
132
|
this.addChild(new DynamicBorder());
|
|
133
133
|
|
|
134
|
-
// Auto-cancel if no messages
|
|
134
|
+
// Auto-cancel if no messages — invoke synchronously via microtask
|
|
135
|
+
// to avoid the 100ms visual flicker from setTimeout
|
|
135
136
|
if (messages.length === 0) {
|
|
136
|
-
|
|
137
|
+
Promise.resolve().then(() => onCancel());
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
|
|
@@ -6,6 +6,9 @@ import { AssistantMessageComponent } from "../components/assistant-message.js";
|
|
|
6
6
|
import { ToolExecutionComponent } from "../components/tool-execution.js";
|
|
7
7
|
import { appKey } from "../components/keybinding-hints.js";
|
|
8
8
|
|
|
9
|
+
// Tracks the last processed content index to avoid re-scanning all blocks on every message_update
|
|
10
|
+
let lastProcessedContentIndex = 0;
|
|
11
|
+
|
|
9
12
|
export async function handleAgentEvent(host: InteractiveModeStateHost & {
|
|
10
13
|
init: () => Promise<void>;
|
|
11
14
|
getMarkdownThemeWithSettings: () => any;
|
|
@@ -28,6 +31,11 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
|
|
|
28
31
|
|
|
29
32
|
host.footer.invalidate();
|
|
30
33
|
|
|
34
|
+
// Reset content index tracker when a new assistant message starts
|
|
35
|
+
if (event.type === "message_start" && event.message.role === "assistant") {
|
|
36
|
+
lastProcessedContentIndex = 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
31
39
|
switch (event.type) {
|
|
32
40
|
case "session_state_changed":
|
|
33
41
|
switch (event.reason) {
|
|
@@ -113,7 +121,9 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
|
|
|
113
121
|
if (host.streamingComponent && event.message.role === "assistant") {
|
|
114
122
|
host.streamingMessage = event.message;
|
|
115
123
|
host.streamingComponent.updateContent(host.streamingMessage);
|
|
116
|
-
|
|
124
|
+
const contentBlocks = host.streamingMessage.content;
|
|
125
|
+
for (let i = lastProcessedContentIndex; i < contentBlocks.length; i++) {
|
|
126
|
+
const content = contentBlocks[i];
|
|
117
127
|
if (content.type === "toolCall") {
|
|
118
128
|
if (!host.pendingTools.has(content.id)) {
|
|
119
129
|
const component = new ToolExecutionComponent(
|
|
@@ -161,6 +171,12 @@ export async function handleAgentEvent(host: InteractiveModeStateHost & {
|
|
|
161
171
|
}
|
|
162
172
|
}
|
|
163
173
|
}
|
|
174
|
+
// Update index: fully processed blocks won't need re-scanning.
|
|
175
|
+
// Keep the last block's index (it may still be accumulating data),
|
|
176
|
+
// so we re-check it next time but skip all earlier ones.
|
|
177
|
+
if (contentBlocks.length > 0) {
|
|
178
|
+
lastProcessedContentIndex = Math.max(0, contentBlocks.length - 1);
|
|
179
|
+
}
|
|
164
180
|
host.ui.requestRender();
|
|
165
181
|
}
|
|
166
182
|
break;
|