@gajae-code/coding-agent 0.18.11 → 0.18.12
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 +293 -0
- package/dist/types/async/job-manager.d.ts +7 -3
- package/dist/types/async/result-formatting.d.ts +2 -0
- package/dist/types/autoresearch/git.d.ts +1 -1
- package/dist/types/capability/fs.d.ts +2 -2
- package/dist/types/cli/accounts-cli.d.ts +2 -2
- package/dist/types/cli/doctor/install-repairs.d.ts +4 -1
- package/dist/types/cli/install-activation.d.ts +10 -4
- package/dist/types/cli/public-command-errors.d.ts +1 -0
- package/dist/types/cli/update-cli.d.ts +14 -1
- package/dist/types/cli-ordinary.d.ts +2 -2
- package/dist/types/commands/accounts.d.ts +2 -1
- package/dist/types/commands/migrate.d.ts +1 -1
- package/dist/types/commands/sdk.d.ts +5 -0
- package/dist/types/commit/changelog/index.d.ts +1 -0
- package/dist/types/config/autorouting-tier-map.d.ts +30 -0
- package/dist/types/config/config-hot-reload-trust.d.ts +12 -0
- package/dist/types/config/config-hot-reload.d.ts +39 -0
- package/dist/types/config/file-lock.d.ts +8 -1
- package/dist/types/config/model-discovery-manager.d.ts +2 -0
- package/dist/types/config/model-profile-activation.d.ts +62 -6
- package/dist/types/config/model-registry.d.ts +56 -5
- package/dist/types/config/model-resolver.d.ts +2 -0
- package/dist/types/config/models-config-schema.d.ts +10 -0
- package/dist/types/config/settings-schema.d.ts +46 -3
- package/dist/types/config/settings.d.ts +33 -1
- package/dist/types/dap/config.d.ts +8 -4
- package/dist/types/debug/system-info.d.ts +6 -1
- package/dist/types/discovery/mcp-compat.d.ts +3 -1
- package/dist/types/edit/modes/patch.d.ts +5 -0
- package/dist/types/edit/modes/replace.d.ts +3 -0
- package/dist/types/exec/bash-executor.d.ts +12 -0
- package/dist/types/gjc-runtime/managed-owner-admission.d.ts +3 -0
- package/dist/types/gjc-runtime/session-state-lock.d.ts +2 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +0 -2
- package/dist/types/migrate/adapters/codex.d.ts +2 -1
- package/dist/types/migrate/adapters/index.d.ts +10 -1
- package/dist/types/migrate/executor.d.ts +3 -1
- package/dist/types/migrate/types.d.ts +15 -3
- package/dist/types/modes/components/settings-selector.d.ts +6 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -0
- package/dist/types/modes/theme/theme.d.ts +1 -0
- package/dist/types/runtime-mcp/content-limits.d.ts +4 -0
- package/dist/types/runtime-mcp/manager.d.ts +7 -0
- package/dist/types/runtime-mcp/plugin-mcp-name-filter.d.ts +22 -0
- package/dist/types/runtime-mcp/tool-bridge.d.ts +4 -0
- package/dist/types/sdk/broker/broker-exit.d.ts +2 -2
- package/dist/types/sdk/broker/broker.d.ts +7 -1
- package/dist/types/sdk/broker/daemon-entry.d.ts +11 -1
- package/dist/types/sdk/broker/doctor-restart.d.ts +1 -1
- package/dist/types/sdk/broker/ensure.d.ts +136 -7
- package/dist/types/sdk/broker/hop.d.ts +36 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +18 -10
- package/dist/types/sdk/broker/lock-host-identity.d.ts +5 -0
- package/dist/types/sdk/broker/session-index.d.ts +4 -1
- package/dist/types/sdk/bus/chat-command-policy.d.ts +2 -1
- package/dist/types/sdk/bus/index.d.ts +23 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +10 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
- package/dist/types/sdk/cli/session-cli.d.ts +0 -1
- package/dist/types/sdk/host/session-runtime.d.ts +32 -0
- package/dist/types/sdk/prompt-deadline-flush.d.ts +42 -4
- package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -0
- package/dist/types/sdk/protocol/adapter-validation.d.ts +10 -4
- package/dist/types/sdk/reconciliation-extensions.d.ts +1 -1
- package/dist/types/sdk/session-list.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +58 -2
- package/dist/types/session/internal/managed-session-scope.d.ts +2 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +1 -1
- package/dist/types/session/internal/native-publish-outcome.d.ts +1 -1
- package/dist/types/session/messages.d.ts +5 -1
- package/dist/types/session/session-manager.d.ts +13 -3
- package/dist/types/session/startup-auth-config.d.ts +24 -2
- package/dist/types/session/streaming-output.d.ts +11 -0
- package/dist/types/session/terminal-abort.d.ts +3 -1
- package/dist/types/session-import/service.d.ts +1 -1
- package/dist/types/skill-state/workflow-mutation-guard.d.ts +7 -3
- package/dist/types/slash-commands/helpers/provider-auth-status.d.ts +8 -0
- package/dist/types/task/executor.d.ts +19 -2
- package/dist/types/tools/ast-edit-write-path.d.ts +10 -0
- package/dist/types/tools/atomic-file-write.d.ts +9 -7
- package/dist/types/tools/bash-allowed-prefixes.d.ts +11 -0
- package/dist/types/tools/descriptors.d.ts +2 -0
- package/dist/types/tools/fetch.d.ts +1 -1
- package/dist/types/tools/index.d.ts +2 -0
- package/dist/types/tools/python.d.ts +1 -1
- package/dist/types/tools/report-issue.d.ts +41 -0
- package/dist/types/tools/subagent.d.ts +23 -0
- package/dist/types/web/scrapers/types.d.ts +8 -1
- package/dist/types/web/search/providers/insane.d.ts +7 -1
- package/dist/types/web/search/providers/searxng.d.ts +5 -0
- package/package.json +8 -8
- package/scripts/build-binary.ts +2 -1
- package/scripts/capture-subagent-compact-panel-showcase.ts +105 -0
- package/scripts/generate-docs-index.ts +8 -1
- package/scripts/generate-sdk-operation-inventory.ts +22 -4
- package/src/async/job-manager.ts +356 -56
- package/src/async/result-formatting.ts +40 -0
- package/src/autoresearch/git.ts +9 -2
- package/src/autoresearch/prompts/prompt-setup.md +1 -1
- package/src/capability/fs.ts +27 -19
- package/src/capability/index.ts +2 -2
- package/src/cli/accounts-cli.ts +176 -8
- package/src/cli/auth-broker-cli.ts +21 -11
- package/src/cli/doctor/install-repairs.ts +35 -5
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/install-activation.ts +47 -4
- package/src/cli/migrate-cli.ts +7 -5
- package/src/cli/public-command-errors.ts +4 -0
- package/src/cli/root-flags.ts +2 -2
- package/src/cli/stats-cli.ts +9 -5
- package/src/cli/update-cli.ts +224 -7
- package/src/cli-ordinary.ts +7 -2
- package/src/commands/accounts.ts +10 -5
- package/src/commands/migrate.ts +3 -3
- package/src/commands/sdk.ts +199 -42
- package/src/commands/ultragoal.ts +16 -8
- package/src/commit/changelog/index.ts +39 -1
- package/src/commit/message.ts +10 -3
- package/src/config/autorouting-tier-map.ts +30 -0
- package/src/config/config-hot-reload-trust.ts +148 -0
- package/src/config/config-hot-reload.ts +734 -0
- package/src/config/file-lock.ts +216 -56
- package/src/config/model-bindings-applier.ts +85 -47
- package/src/config/model-discovery-manager.ts +10 -0
- package/src/config/model-profile-activation.ts +716 -113
- package/src/config/model-registry.ts +1334 -158
- package/src/config/model-resolver.ts +3 -1
- package/src/config/models-config-schema.ts +6 -0
- package/src/config/settings-schema.ts +60 -3
- package/src/config/settings.ts +458 -24
- package/src/coordinator-mcp/policy.ts +26 -1
- package/src/crash/sanitize.ts +4 -0
- package/src/customization/import.ts +3 -1
- package/src/dap/config.ts +31 -11
- package/src/dap/defaults.json +1 -1
- package/src/debug/crash-diagnostics.ts +2 -1
- package/src/debug/report-bundle.ts +19 -0
- package/src/debug/system-info.ts +8 -3
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/shared/base-url.ts +21 -7
- package/src/discovery/agents-md.ts +1 -1
- package/src/discovery/helpers.ts +1 -2
- package/src/discovery/mcp-compat.ts +120 -17
- package/src/edit/apply-patch/index.ts +12 -1
- package/src/edit/index.ts +13 -17
- package/src/edit/modes/patch.ts +187 -8
- package/src/edit/modes/replace.ts +136 -0
- package/src/eval/js/context-manager.ts +29 -3
- package/src/eval/js/worker-entry.ts +51 -18
- package/src/eval/py/executor.ts +14 -4
- package/src/eval/py/runner.py +144 -5
- package/src/exec/bash-executor.ts +81 -4
- package/src/exec/bash-shell-guardian.ts +4 -1
- package/src/exec/bash-shell-supervisor.ts +8 -4
- package/src/exec/isolated-shell.ts +5 -1
- package/src/export/html/index.ts +46 -6
- package/src/extensibility/custom-tools/wrapper.ts +3 -1
- package/src/extensibility/extensions/loader.ts +8 -1
- package/src/extensibility/plugins/marketplace/fetcher.ts +2 -2
- package/src/gjc-runtime/gc-runtime.ts +33 -6
- package/src/gjc-runtime/launch-tmux.ts +7 -2
- package/src/gjc-runtime/launch-worktree.test.ts +148 -0
- package/src/gjc-runtime/launch-worktree.ts +101 -14
- package/src/gjc-runtime/managed-owner-admission.ts +46 -2
- package/src/gjc-runtime/session-resolution.ts +38 -6
- package/src/gjc-runtime/session-state-lock.ts +59 -14
- package/src/gjc-runtime/state-runtime.ts +23 -19
- package/src/hooks/native-skill-hook.ts +14 -11
- package/src/internal-urls/docs-index.generated.ts +14 -13
- package/src/internal-urls/local-protocol.ts +142 -8
- package/src/lsp/index.ts +29 -0
- package/src/lsp/workspace-path.ts +23 -0
- package/src/migrate/action-planner.ts +57 -0
- package/src/migrate/adapters/claude-code.ts +23 -4
- package/src/migrate/adapters/codex.ts +22 -4
- package/src/migrate/adapters/index.ts +22 -4
- package/src/migrate/adapters/opencode.ts +6 -1
- package/src/migrate/executor.ts +84 -7
- package/src/migrate/report.ts +19 -2
- package/src/migrate/types.ts +14 -4
- package/src/modes/DESIGN.md +51 -0
- package/src/modes/acp/permission-mode.ts +18 -1
- package/src/modes/components/assistant-message.ts +55 -1
- package/src/modes/components/settings-selector.ts +103 -37
- package/src/modes/controllers/command-controller.ts +38 -2
- package/src/modes/controllers/plan-mode-controller.ts +17 -7
- package/src/modes/controllers/selector-controller.ts +62 -6
- package/src/modes/interactive-mode.ts +68 -0
- package/src/modes/theme/theme.ts +15 -0
- package/src/prompts/tools/debug.md +1 -1
- package/src/prompts/tools/read.md +1 -1
- package/src/prompts/tools/report-issue.md +13 -0
- package/src/prompts/tools/task.md +1 -1
- package/src/runtime-credential-selector.ts +2 -2
- package/src/runtime-mcp/content-limits.ts +7 -0
- package/src/runtime-mcp/manager-pool.test.ts +5 -4
- package/src/runtime-mcp/manager.ts +32 -0
- package/src/runtime-mcp/oauth-flow.ts +32 -6
- package/src/runtime-mcp/plugin-mcp-name-filter.ts +62 -0
- package/src/runtime-mcp/render.ts +16 -1
- package/src/runtime-mcp/tool-bridge.ts +126 -17
- package/src/sdk/broker/broker-exit.ts +8 -5
- package/src/sdk/broker/broker.ts +342 -82
- package/src/sdk/broker/daemon-entry.ts +338 -116
- package/src/sdk/broker/doctor-restart.ts +2 -1
- package/src/sdk/broker/ensure.ts +1143 -265
- package/src/sdk/broker/hop.ts +274 -0
- package/src/sdk/broker/lifecycle-ledger.ts +35 -11
- package/src/sdk/broker/lock-host-identity.ts +29 -0
- package/src/sdk/broker/session-index.ts +62 -12
- package/src/sdk/bus/chat-command-policy.ts +16 -3
- package/src/sdk/bus/index.ts +52 -21
- package/src/sdk/bus/reconciliation-store.ts +201 -8
- package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
- package/src/sdk/cli/session-cli.ts +21 -6
- package/src/sdk/host/session-runtime.test.ts +118 -23
- package/src/sdk/host/session-runtime.ts +352 -47
- package/src/sdk/lifecycle/service.ts +1 -0
- package/src/sdk/prompt-deadline-flush.ts +78 -5
- package/src/sdk/prompt-deadline-manager.ts +68 -14
- package/src/sdk/protocol/adapter-validation.ts +53 -11
- package/src/sdk/protocol/operation-inventory.generated.json +101 -2
- package/src/sdk/reconciliation-extensions.ts +1 -0
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +114 -61
- package/src/session/agent-session.ts +1043 -81
- package/src/session/internal/managed-session-scope.ts +126 -39
- package/src/session/internal/managed-session-storage.ts +14 -11
- package/src/session/internal/native-publish-outcome.ts +5 -1
- package/src/session/messages.ts +47 -6
- package/src/session/session-manager.ts +236 -14
- package/src/session/startup-auth-config.ts +116 -8
- package/src/session/streaming-output.ts +262 -4
- package/src/session/terminal-abort.ts +14 -2
- package/src/session-import/service.ts +1 -0
- package/src/skill-state/workflow-mutation-guard.ts +491 -32
- package/src/slash-commands/builtin-registry.ts +15 -3
- package/src/slash-commands/helpers/credential-switch.ts +10 -5
- package/src/slash-commands/helpers/provider-auth-status.ts +456 -0
- package/src/task/executor.ts +103 -12
- package/src/task/index.ts +389 -175
- package/src/tools/ast-edit-write-path.ts +64 -0
- package/src/tools/ast-edit.ts +18 -1
- package/src/tools/atomic-file-write.ts +46 -15
- package/src/tools/auto-generated-guard.ts +3 -0
- package/src/tools/bash-allowed-prefixes.ts +153 -5
- package/src/tools/bash-interactive.ts +14 -2
- package/src/tools/bash.ts +14 -0
- package/src/tools/debug.ts +7 -6
- package/src/tools/descriptors.test.ts +44 -0
- package/src/tools/descriptors.ts +34 -2
- package/src/tools/fetch.ts +3 -2
- package/src/tools/index.ts +2 -0
- package/src/tools/output-meta.ts +23 -2
- package/src/tools/python.ts +1 -1
- package/src/tools/read.ts +17 -35
- package/src/tools/report-issue.ts +184 -0
- package/src/tools/subagent-render.ts +114 -12
- package/src/tools/subagent.ts +108 -1
- package/src/tools/todo-write.ts +41 -3
- package/src/tools/tool-catalog.generated.ts +31 -3
- package/src/tools/write.ts +35 -3
- package/src/utils/commit-message-generator.ts +9 -2
- package/src/utils/file-mentions.ts +29 -1
- package/src/utils/title-generator.ts +7 -2
- package/src/web/scrapers/index.ts +3 -1
- package/src/web/scrapers/types.ts +8 -0
- package/src/web/scrapers/youtube.ts +3 -2
- package/src/web/search/providers/insane.ts +91 -28
- package/src/web/search/providers/searxng.ts +62 -10
package/src/migrate/report.ts
CHANGED
|
@@ -27,7 +27,12 @@ export interface BuildReportInput {
|
|
|
27
27
|
|
|
28
28
|
export function buildReport(input: BuildReportInput): MigrateReport {
|
|
29
29
|
const total = emptyStatusCounts();
|
|
30
|
-
const byType = {
|
|
30
|
+
const byType = {
|
|
31
|
+
mcp: emptyStatusCounts(),
|
|
32
|
+
skill: emptyStatusCounts(),
|
|
33
|
+
instruction: emptyStatusCounts(),
|
|
34
|
+
source: emptyStatusCounts(),
|
|
35
|
+
};
|
|
31
36
|
const bySource = {} as Record<MigrateSource, StatusCounts>;
|
|
32
37
|
for (const source of input.sources) bySource[source] = emptyStatusCounts();
|
|
33
38
|
|
|
@@ -75,10 +80,22 @@ export function renderHuman(report: MigrateReport): string {
|
|
|
75
80
|
const mode = report.dryRun ? " (dry-run)" : "";
|
|
76
81
|
lines.push(`gjc migrate${mode}: ${report.ok ? "ok" : "completed with failures"}`);
|
|
77
82
|
lines.push(`Sources: ${report.sources.join(", ") || "none"}`);
|
|
78
|
-
lines.push(
|
|
83
|
+
lines.push(
|
|
84
|
+
`Destination: mcp=${report.destinations.mcpConfigPath} skills=${report.destinations.skillsDir} instructions=${report.destinations.instructionsPath}`,
|
|
85
|
+
);
|
|
79
86
|
lines.push("");
|
|
80
87
|
lines.push(`MCP: ${summarizeCounts(report.summary.byType.mcp)}`);
|
|
81
88
|
lines.push(`Skills: ${summarizeCounts(report.summary.byType.skill)}`);
|
|
89
|
+
lines.push(`Instructions: ${summarizeCounts(report.summary.byType.instruction)}`);
|
|
90
|
+
const instructionActions = report.actions.filter(action => action.type === "instruction");
|
|
91
|
+
if (instructionActions.length > 0) {
|
|
92
|
+
lines.push("Instruction files:");
|
|
93
|
+
for (const action of instructionActions) {
|
|
94
|
+
lines.push(
|
|
95
|
+
` - [${action.source}] ${action.name ?? "AGENTS.md"}: ${action.status}${action.reason ? ` (${action.reason})` : ""}`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
82
99
|
if (Object.values(report.summary.byType.source).some(n => n > 0)) {
|
|
83
100
|
lines.push(`Source: ${summarizeCounts(report.summary.byType.source)}`);
|
|
84
101
|
}
|
package/src/migrate/types.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared types for `gjc migrate`.
|
|
3
3
|
*
|
|
4
|
-
* Imports MCP servers and
|
|
5
|
-
* OpenCode) into native GJC config. See the consensus plan under
|
|
4
|
+
* Imports MCP servers, skills, and instruction files from other coding agents
|
|
5
|
+
* (Claude Code, Codex, OpenCode) into native GJC config. See the consensus plan under
|
|
6
6
|
* `.gjc/plans/ralplan/` for the full taxonomy and force/collision semantics.
|
|
7
7
|
*/
|
|
8
8
|
import type { MCPServerConfig } from "../runtime-mcp/types";
|
|
@@ -16,7 +16,7 @@ export const MIGRATE_SOURCES: readonly MigrateSource[] = ["claude-code", "codex"
|
|
|
16
16
|
export const CANONICAL_SOURCE_ORDER: readonly MigrateSource[] = MIGRATE_SOURCES;
|
|
17
17
|
|
|
18
18
|
/** What kind of thing an action/coverage row is about. */
|
|
19
|
-
export type MigrateItemType = "mcp" | "skill" | "source";
|
|
19
|
+
export type MigrateItemType = "mcp" | "skill" | "instruction" | "source";
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Per-item outcome taxonomy.
|
|
@@ -77,6 +77,13 @@ export interface SkillCandidate {
|
|
|
77
77
|
warnings: string[];
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/** An instructions file imported as the active GJC AGENTS.md for its scope. */
|
|
81
|
+
export interface InstructionCandidate {
|
|
82
|
+
source: MigrateSource;
|
|
83
|
+
name: string;
|
|
84
|
+
content: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
80
87
|
/**
|
|
81
88
|
* Source-level diagnostic for a single source/type pair (e.g. "codex mcp config
|
|
82
89
|
* was malformed"). Distinct from per-item actions so absent/unreadable sources
|
|
@@ -93,6 +100,7 @@ export interface SourceDiagnostic {
|
|
|
93
100
|
export interface AdapterResult {
|
|
94
101
|
mcpCandidates: McpCandidate[];
|
|
95
102
|
skillCandidates: SkillCandidate[];
|
|
103
|
+
instructionCandidates: InstructionCandidate[];
|
|
96
104
|
diagnostics: SourceDiagnostic[];
|
|
97
105
|
}
|
|
98
106
|
|
|
@@ -112,6 +120,7 @@ export interface MigrateAction {
|
|
|
112
120
|
/** Resolved payload the executor needs; never serialized to the report. */
|
|
113
121
|
mcp?: { config: MCPServerConfig; force: boolean };
|
|
114
122
|
skill?: { content: string };
|
|
123
|
+
instruction?: { content: string };
|
|
115
124
|
}
|
|
116
125
|
|
|
117
126
|
export interface MigrateWarning {
|
|
@@ -126,6 +135,7 @@ export type StatusCounts = Record<MigrationStatus, number>;
|
|
|
126
135
|
export interface MigrateDestinations {
|
|
127
136
|
mcpConfigPath: string;
|
|
128
137
|
skillsDir: string;
|
|
138
|
+
instructionsPath: string;
|
|
129
139
|
}
|
|
130
140
|
|
|
131
141
|
/** The full machine-readable report emitted with `--json`. */
|
|
@@ -138,7 +148,7 @@ export interface MigrateReport {
|
|
|
138
148
|
destinations: MigrateDestinations;
|
|
139
149
|
summary: {
|
|
140
150
|
total: StatusCounts;
|
|
141
|
-
byType: { mcp: StatusCounts; skill: StatusCounts; source: StatusCounts };
|
|
151
|
+
byType: { mcp: StatusCounts; skill: StatusCounts; instruction: StatusCounts; source: StatusCounts };
|
|
142
152
|
bySource: Record<MigrateSource, StatusCounts>;
|
|
143
153
|
};
|
|
144
154
|
actions: Array<{
|
package/src/modes/DESIGN.md
CHANGED
|
@@ -112,6 +112,11 @@ condition in prose. Error guidance states the safe recovery action without
|
|
|
112
112
|
showing credentials. Confirmations are explicit focused choices; destructive
|
|
113
113
|
remove/disable actions are never the default side effect of navigation.
|
|
114
114
|
|
|
115
|
+
Guidance the user must act on (for example the subagent await-timeout
|
|
116
|
+
"inspect before cancelling" instruction) wraps to the terminal width instead of
|
|
117
|
+
being clipped to one preview line, so the action survives narrow terminals. It
|
|
118
|
+
stays bounded: at most 12 wrapped rows, the last ending in `…` if cut.
|
|
119
|
+
|
|
115
120
|
A non-cancellable action visibly locks navigation and names the reason. A
|
|
116
121
|
cancellable action names cancellation while it is pending, aborts on exit, and
|
|
117
122
|
must not render a late completion after disposal. This follows the selector's
|
|
@@ -469,3 +474,49 @@ differently named bundles, and a stored `uri#ref` differs from a bare `uri`, so
|
|
|
469
474
|
locator-based refusal would refuse installs that should proceed. Refusing on a
|
|
470
475
|
guess is worse than reporting the real failure, so identity must be readable for
|
|
471
476
|
refusal to apply.
|
|
477
|
+
|
|
478
|
+
## Subagent compact await panel
|
|
479
|
+
|
|
480
|
+
Source: `tools/subagent-render.ts` (the `subagent` await panel), behind the
|
|
481
|
+
`task.compactSubagentPanel` setting (default off). This section extends the
|
|
482
|
+
existing tool-panel grammar; it adds no tokens.
|
|
483
|
+
|
|
484
|
+
- **Scope:** live await progress only. A final result (timeout guidance,
|
|
485
|
+
failure and completion receipts, status/steer/resume replies) always keeps the
|
|
486
|
+
full per-subagent rendering, so nothing the user must act on is summarized.
|
|
487
|
+
- **Anatomy (collapsed, setting on):** a height independent of the number of
|
|
488
|
+
subagents — the existing status header, one `dim` summary row of group counts
|
|
489
|
+
joined by ` · `, at most one `warning` row naming the most-degraded provider and
|
|
490
|
+
how many more providers degrade (the expanded view keeps one row per provider), and one `dim` hint row in parentheses
|
|
491
|
+
naming the effective `app.tools.expand` key and, while anything runs, the
|
|
492
|
+
existing `ctrl+s to observe sessions` hint.
|
|
493
|
+
- **Count order:** `N subagents running`, then `failed`, `retrying`,
|
|
494
|
+
`idle >30s`, `queued`, `paused`, `done`, `cancelled`. Zero counts are omitted.
|
|
495
|
+
Attention states come first because a narrow terminal truncates the row end;
|
|
496
|
+
truncation shows `…` so a dropped count is visible.
|
|
497
|
+
- **States:** collapsed → summary; expanded (the existing tool-output toggle)
|
|
498
|
+
→ the unchanged per-subagent list. With the setting off the panel is the
|
|
499
|
+
unchanged per-subagent list in both states. Status is carried by words and
|
|
500
|
+
counts, never by color or the spinner alone; ASCII/no-color keeps the same
|
|
501
|
+
text.
|
|
502
|
+
- **Motion:** no new animation. With the setting on, await progress re-emits
|
|
503
|
+
only when the summary counts or per-provider retry counts change (plus the
|
|
504
|
+
existing liveness interval).
|
|
505
|
+
- **International text:** the summary and hint are fixed English labels and
|
|
506
|
+
numbers; subagent descriptions (which may be CJK) appear only in the expanded
|
|
507
|
+
list, whose layout is unchanged.
|
|
508
|
+
|
|
509
|
+
Capture command (fixture-backed, real renderer, fixed clock):
|
|
510
|
+
|
|
511
|
+
```sh
|
|
512
|
+
bun packages/coding-agent/scripts/capture-subagent-compact-panel-showcase.ts --out .gjc/qa/subagent-compact-panel-<run>
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
The matrix is every state at `80x24`, `120x36`, and `160x48` in
|
|
516
|
+
`unicode-color` (including `compact-final-timeout`, a final timeout receipt
|
|
517
|
+
with the setting on), plus `compact-collapsed-15-live` and `compact-collapsed-mixed`
|
|
518
|
+
at `80x24/ascii-no-color`, `compact-collapsed-mixed/48x36/unicode-color`, and
|
|
519
|
+
`narrow-cjk/48x36` in both render modes: 26 entries. An entry whose panel fits
|
|
520
|
+
the viewport is captured once (`top`); an overflowing entry is captured at
|
|
521
|
+
`top`, `middle`, and `bottom` so no tail is hidden. Every capture has
|
|
522
|
+
`terminal.txt`, `terminal-ansi.txt`, `terminal.html`, and `metadata.json`.
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ClientCapabilities } from "@agentclientprotocol/sdk";
|
|
2
|
+
import { startupProjectEnvSnapshot } from "@gajae-code/utils/env";
|
|
3
|
+
import { canonicalEnvKey } from "@gajae-code/utils/env-file";
|
|
2
4
|
|
|
3
5
|
export type AcpPermissionMode = "auto" | "prompt" | "always-allow";
|
|
4
6
|
|
|
@@ -9,6 +11,21 @@ function parseAcpPermissionMode(value: unknown): AcpPermissionMode {
|
|
|
9
11
|
return "prompt";
|
|
10
12
|
}
|
|
11
13
|
|
|
14
|
+
/**
|
|
15
|
+
* The default process environment includes the cwd `.env` merge. A live value
|
|
16
|
+
* equal to that startup declaration is the overlay and fails safe. A different
|
|
17
|
+
* live value is the launching environment, including when the declaration is
|
|
18
|
+
* dynamic (`$` or backticks) and is not that value. An explicit env map is
|
|
19
|
+
* the caller's value. The raw string is parsed so padding still fails safe.
|
|
20
|
+
*/
|
|
21
|
+
function permissionModeFromEnv(env: NodeJS.ProcessEnv): unknown {
|
|
22
|
+
const raw = env[ACP_PERMISSION_MODE_ENV];
|
|
23
|
+
if (env !== process.env || typeof raw !== "string") return raw;
|
|
24
|
+
const declared = startupProjectEnvSnapshot().values[canonicalEnvKey(ACP_PERMISSION_MODE_ENV)];
|
|
25
|
+
if (declared !== undefined && raw.trim() === declared.trim()) return undefined;
|
|
26
|
+
return raw;
|
|
27
|
+
}
|
|
28
|
+
|
|
12
29
|
/** Client metadata is authoritative; the process environment is only a fallback when that field is absent. */
|
|
13
30
|
export function resolveAcpPermissionMode(
|
|
14
31
|
clientCapabilities: ClientCapabilities | undefined,
|
|
@@ -21,5 +38,5 @@ export function resolveAcpPermissionMode(
|
|
|
21
38
|
return parseAcpPermissionMode((gjc as { permissionHandling?: unknown }).permissionHandling);
|
|
22
39
|
}
|
|
23
40
|
}
|
|
24
|
-
return parseAcpPermissionMode(env
|
|
41
|
+
return parseAcpPermissionMode(permissionModeFromEnv(env));
|
|
25
42
|
}
|
|
@@ -4,13 +4,17 @@ import {
|
|
|
4
4
|
Container,
|
|
5
5
|
Image,
|
|
6
6
|
ImageProtocol,
|
|
7
|
+
isCursorNeutralImagePermittedInFallback,
|
|
8
|
+
isTerminalGraphicsFallbackActive,
|
|
7
9
|
isViewportAnchorSourceRenderer,
|
|
8
10
|
Markdown,
|
|
11
|
+
type MarkdownTheme,
|
|
9
12
|
Spacer,
|
|
10
13
|
TERMINAL,
|
|
11
14
|
Text,
|
|
12
15
|
type ViewportAnchorSource,
|
|
13
16
|
} from "@gajae-code/tui";
|
|
17
|
+
import { SvgFigure } from "@gajae-code/tui/chat/svg-figure";
|
|
14
18
|
import { formatNumber } from "@gajae-code/utils";
|
|
15
19
|
import { settings } from "../../config/settings";
|
|
16
20
|
import { renderDeepInterviewAssistantText } from "../../deep-interview/render-middleware";
|
|
@@ -27,6 +31,44 @@ const THINKING_REPETITION_ELIDE_MIN_RUN = 24;
|
|
|
27
31
|
const THINKING_REPETITION_VISIBLE_TOKENS = 3;
|
|
28
32
|
const THINKING_REPETITION_TOKEN_PATTERN = /[\p{L}\p{N}_'-]{1,32}/gu;
|
|
29
33
|
|
|
34
|
+
let assistantMarkdownThemeBase: MarkdownTheme | undefined;
|
|
35
|
+
let cachedAssistantMarkdownTheme: MarkdownTheme | undefined;
|
|
36
|
+
|
|
37
|
+
function getAssistantMarkdownTheme(): MarkdownTheme {
|
|
38
|
+
const base = getMarkdownTheme();
|
|
39
|
+
if (base === assistantMarkdownThemeBase && cachedAssistantMarkdownTheme) return cachedAssistantMarkdownTheme;
|
|
40
|
+
assistantMarkdownThemeBase = base;
|
|
41
|
+
cachedAssistantMarkdownTheme = {
|
|
42
|
+
...base,
|
|
43
|
+
resolveSvgFigure: (source, context) => {
|
|
44
|
+
if (!settings.get("terminal.showImages")) return null;
|
|
45
|
+
const graphicsSuppressed =
|
|
46
|
+
isTerminalGraphicsFallbackActive() &&
|
|
47
|
+
!(TERMINAL.imageProtocol === ImageProtocol.Kitty && isCursorNeutralImagePermittedInFallback());
|
|
48
|
+
if (!TERMINAL.imageProtocol) return null; // Stable environment: cache the code fallback.
|
|
49
|
+
if (graphicsSuppressed) return undefined; // The suppression scope can end; retry on repaint.
|
|
50
|
+
const palette = theme.getSvgPalette();
|
|
51
|
+
const previous = context.previous instanceof SvgFigure ? context.previous : undefined;
|
|
52
|
+
const figure =
|
|
53
|
+
previous ??
|
|
54
|
+
new SvgFigure({
|
|
55
|
+
theme: { fallbackColor: (text: string) => theme.fg("toolOutput", text) },
|
|
56
|
+
palette,
|
|
57
|
+
onChange: context.onChange,
|
|
58
|
+
});
|
|
59
|
+
figure.update(source, context.closed || !context.streaming, context.closed, palette);
|
|
60
|
+
if (figure.retryable) {
|
|
61
|
+
// A failed preview remains a code block, but must observe layout changes
|
|
62
|
+
// so a smaller terminal canvas can retry the same source.
|
|
63
|
+
figure.render(context.width);
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
return figure.failed ? null : figure;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
return cachedAssistantMarkdownTheme;
|
|
70
|
+
}
|
|
71
|
+
|
|
30
72
|
interface ThinkingRepetitionToken {
|
|
31
73
|
text: string;
|
|
32
74
|
normalized: string;
|
|
@@ -92,6 +134,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
92
134
|
#convertedKittyImages = new Map<string, ImageContent & { source: string }>();
|
|
93
135
|
#kittyConversionsInFlight = new Map<string, string>();
|
|
94
136
|
#toolImageGenerations = new Map<string, number>();
|
|
137
|
+
#unsubscribeSettings?: () => void;
|
|
95
138
|
|
|
96
139
|
#responseHeader = new Text(theme.bold(theme.fg("statusLineModel", "gajae")), 1, 0);
|
|
97
140
|
#contentBlocksCache = new WeakMap<object, { source: string; component: Component }>();
|
|
@@ -133,6 +176,15 @@ export class AssistantMessageComponent extends Container {
|
|
|
133
176
|
}
|
|
134
177
|
this.#visibleSignature = this.#visibleContentSignature();
|
|
135
178
|
this.#visibleSignatureInitialized = true;
|
|
179
|
+
this.#unsubscribeSettings = settings.onChanged(path => {
|
|
180
|
+
if (path !== "terminal.showImages") return;
|
|
181
|
+
for (const block of this.#lastMessage?.content ?? []) {
|
|
182
|
+
if (block.type !== "text") continue;
|
|
183
|
+
const cached = this.#contentBlocksCache.get(block);
|
|
184
|
+
if (cached?.component instanceof Markdown) cached.component.invalidate();
|
|
185
|
+
}
|
|
186
|
+
this.#requestRepaint?.();
|
|
187
|
+
});
|
|
136
188
|
}
|
|
137
189
|
|
|
138
190
|
#visibleContentSignature(): string {
|
|
@@ -339,7 +391,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
339
391
|
cached.source = content.text;
|
|
340
392
|
return cached.component;
|
|
341
393
|
}
|
|
342
|
-
const component = deepInterview ?? new Markdown(trimmed, 1, 0,
|
|
394
|
+
const component = deepInterview ?? new Markdown(trimmed, 1, 0, getAssistantMarkdownTheme());
|
|
343
395
|
if (component instanceof Markdown) {
|
|
344
396
|
component.setOnStaleThrottle(this.#requestRepaint);
|
|
345
397
|
component.setStreaming(streaming);
|
|
@@ -626,6 +678,8 @@ export class AssistantMessageComponent extends Container {
|
|
|
626
678
|
override dispose(): void {
|
|
627
679
|
if (this.#disposed) return;
|
|
628
680
|
this.#disposed = true;
|
|
681
|
+
this.#unsubscribeSettings?.();
|
|
682
|
+
this.#unsubscribeSettings = undefined;
|
|
629
683
|
this.#toolImageGenerations.clear();
|
|
630
684
|
this.#kittyConversionsInFlight.clear();
|
|
631
685
|
this.#convertedKittyImages.clear();
|
|
@@ -108,9 +108,9 @@ class SelectSubmenu extends Container {
|
|
|
108
108
|
description: string,
|
|
109
109
|
options: ReadonlyArray<SelectItem>,
|
|
110
110
|
currentValue: string,
|
|
111
|
-
onSelect: (value: string) => void,
|
|
112
|
-
onCancel: () => void,
|
|
113
|
-
onSelectionChange?: (value: string) => void | Promise<void>,
|
|
111
|
+
private readonly onSelect: (value: string) => void,
|
|
112
|
+
private readonly onCancel: () => void,
|
|
113
|
+
private readonly onSelectionChange?: (value: string) => void | Promise<void>,
|
|
114
114
|
private readonly getPreview?: () => string,
|
|
115
115
|
) {
|
|
116
116
|
super();
|
|
@@ -137,46 +137,54 @@ class SelectSubmenu extends Container {
|
|
|
137
137
|
// Spacer
|
|
138
138
|
this.addChild(new Spacer(1));
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
this.#selectList
|
|
142
|
-
|
|
143
|
-
// Pre-select current value
|
|
144
|
-
const currentIndex = options.findIndex(o => o.value === currentValue);
|
|
145
|
-
if (currentIndex !== -1) {
|
|
146
|
-
this.#selectList.setSelectedIndex(currentIndex);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
this.#selectList.onSelect = item => {
|
|
150
|
-
onSelect(item.value);
|
|
151
|
-
};
|
|
140
|
+
this.#selectList = this.#createSelectList(options, currentValue);
|
|
141
|
+
this.addChild(this.#selectList);
|
|
152
142
|
|
|
153
|
-
|
|
143
|
+
// Hint
|
|
144
|
+
this.addChild(new Spacer(1));
|
|
145
|
+
this.addChild(
|
|
146
|
+
new DynamicThemeText(() => theme.fg("dim", uiString(settings.get("ui.language"), "settings.selectHint"))),
|
|
147
|
+
);
|
|
148
|
+
}
|
|
154
149
|
|
|
155
|
-
|
|
156
|
-
|
|
150
|
+
#createSelectList(options: ReadonlyArray<SelectItem>, selectedValue?: string): SelectList {
|
|
151
|
+
const selectList = new SelectList(options, Math.min(options.length, 10), () => getSelectListTheme());
|
|
152
|
+
const selectedIndex = options.findIndex(option => option.value === selectedValue);
|
|
153
|
+
if (selectedIndex !== -1) selectList.setSelectedIndex(selectedIndex);
|
|
154
|
+
selectList.onSelect = item => this.onSelect(item.value);
|
|
155
|
+
selectList.onCancel = this.onCancel;
|
|
156
|
+
if (this.onSelectionChange) {
|
|
157
|
+
selectList.onSelectionChange = item => {
|
|
157
158
|
const requestId = ++this.#previewUpdateRequestId;
|
|
158
|
-
const result = onSelectionChange(item.value);
|
|
159
|
+
const result = this.onSelectionChange?.(item.value);
|
|
159
160
|
if (result && typeof (result as Promise<void>).then === "function") {
|
|
160
161
|
void (result as Promise<void>).finally(() => {
|
|
161
|
-
if (requestId === this.#previewUpdateRequestId)
|
|
162
|
-
this.#updatePreview();
|
|
163
|
-
}
|
|
162
|
+
if (requestId === this.#previewUpdateRequestId) this.#updatePreview();
|
|
164
163
|
});
|
|
165
164
|
return;
|
|
166
165
|
}
|
|
167
|
-
if (requestId === this.#previewUpdateRequestId)
|
|
168
|
-
this.#updatePreview();
|
|
169
|
-
}
|
|
166
|
+
if (requestId === this.#previewUpdateRequestId) this.#updatePreview();
|
|
170
167
|
};
|
|
171
168
|
}
|
|
169
|
+
return selectList;
|
|
170
|
+
}
|
|
172
171
|
|
|
173
|
-
|
|
172
|
+
refreshOptions(options: ReadonlyArray<SelectItem>, retainMissingSelection = false): void {
|
|
173
|
+
const previous = this.#selectList;
|
|
174
|
+
const selectedItem = previous.getSelectedItem();
|
|
175
|
+
if (retainMissingSelection && selectedItem && !options.some(option => option.value === selectedItem.value)) {
|
|
176
|
+
const label = selectedItem.label.endsWith(" (unavailable)")
|
|
177
|
+
? selectedItem.label
|
|
178
|
+
: `${selectedItem.label} (unavailable)`;
|
|
179
|
+
options = [...options, { ...selectedItem, label }];
|
|
180
|
+
}
|
|
181
|
+
const selectedValue = selectedItem?.value;
|
|
182
|
+
this.#selectList = this.#createSelectList(options, selectedValue);
|
|
183
|
+
this.replaceChildren(this.children.map(child => (child === previous ? this.#selectList : child)));
|
|
184
|
+
}
|
|
174
185
|
|
|
175
|
-
|
|
176
|
-
this
|
|
177
|
-
this.addChild(
|
|
178
|
-
new DynamicThemeText(() => theme.fg("dim", uiString(settings.get("ui.language"), "settings.selectHint"))),
|
|
179
|
-
);
|
|
186
|
+
refreshPreview(): void {
|
|
187
|
+
this.#updatePreview();
|
|
180
188
|
}
|
|
181
189
|
|
|
182
190
|
#updatePreview(): void {
|
|
@@ -1528,6 +1536,10 @@ export interface SettingsCallbacks {
|
|
|
1528
1536
|
onPetCommit?: (mode: string) => boolean;
|
|
1529
1537
|
/** Called for status line preview while configuring */
|
|
1530
1538
|
onStatusLinePreview?: (settings: StatusLinePreviewSettings) => void;
|
|
1539
|
+
/** Called when a status-line submenu begins an uncommitted preview. */
|
|
1540
|
+
onStatusLinePreviewStart?: () => void;
|
|
1541
|
+
/** Called when a status-line preview is committed, cancelled, or disposed. */
|
|
1542
|
+
onStatusLinePreviewEnd?: () => void;
|
|
1531
1543
|
/** Get current rendered status line for inline preview */
|
|
1532
1544
|
getStatusLinePreview?: (width?: number) => string;
|
|
1533
1545
|
/** Render a status-line preview for supplied draft settings without mutating the live status line. */
|
|
@@ -1582,6 +1594,8 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1582
1594
|
#currentTabId: SettingTab | "plugins" | "gjc-bundles" = "appearance";
|
|
1583
1595
|
#textInputActive = false;
|
|
1584
1596
|
#activeProviderOrderEditor: Container | null = null;
|
|
1597
|
+
#activeSelectSubmenu: SelectSubmenu | null = null;
|
|
1598
|
+
#activeModelProfileSubmenu: SelectSubmenu | null = null;
|
|
1585
1599
|
|
|
1586
1600
|
constructor(
|
|
1587
1601
|
private readonly context: SettingsRuntimeContext,
|
|
@@ -1634,9 +1648,15 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1634
1648
|
}
|
|
1635
1649
|
|
|
1636
1650
|
#switchToTab(tabId: SettingTab | "plugins" | "gjc-bundles"): void {
|
|
1651
|
+
if (this.#currentTabId === "appearance" && tabId !== "appearance") {
|
|
1652
|
+
this.callbacks.onStatusLinePreviewEnd?.();
|
|
1653
|
+
}
|
|
1637
1654
|
if (this.#currentTabId === "notifications" && tabId !== "notifications" && !this.#disposeNotificationsEditor()) {
|
|
1638
1655
|
return;
|
|
1639
1656
|
}
|
|
1657
|
+
this.#activeSelectSubmenu?.dispose();
|
|
1658
|
+
this.#activeSelectSubmenu = null;
|
|
1659
|
+
this.#activeModelProfileSubmenu = null;
|
|
1640
1660
|
// Release an open provider-order editor (and its context subscriptions)
|
|
1641
1661
|
// before switching tabs; the submenu's done() never runs on tab change,
|
|
1642
1662
|
// so without this the abandoned editor's listeners would survive.
|
|
@@ -1833,6 +1853,18 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1833
1853
|
// Preview handlers
|
|
1834
1854
|
let onPreview: ((value: string) => void | Promise<void>) | undefined;
|
|
1835
1855
|
let onPreviewCancel: (() => void) | undefined;
|
|
1856
|
+
let onPreviewEnd: (() => void) | undefined;
|
|
1857
|
+
let statusLinePreviewStarted = false;
|
|
1858
|
+
const startStatusLinePreview = (): void => {
|
|
1859
|
+
if (statusLinePreviewStarted) return;
|
|
1860
|
+
statusLinePreviewStarted = true;
|
|
1861
|
+
this.callbacks.onStatusLinePreviewStart?.();
|
|
1862
|
+
};
|
|
1863
|
+
const endStatusLinePreview = (): void => {
|
|
1864
|
+
if (!statusLinePreviewStarted) return;
|
|
1865
|
+
statusLinePreviewStarted = false;
|
|
1866
|
+
this.callbacks.onStatusLinePreviewEnd?.();
|
|
1867
|
+
};
|
|
1836
1868
|
|
|
1837
1869
|
if (def.path === "theme.dark" || def.path === "theme.light") {
|
|
1838
1870
|
const activeThemeBeforePreview = getCurrentThemeName() ?? currentValue;
|
|
@@ -1844,6 +1876,7 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1844
1876
|
};
|
|
1845
1877
|
} else if (def.path === "statusLine.preset") {
|
|
1846
1878
|
onPreview = value => {
|
|
1879
|
+
startStatusLinePreview();
|
|
1847
1880
|
const presetDef = getPreset(value as StatusLinePreset);
|
|
1848
1881
|
this.callbacks.onStatusLinePreview?.({
|
|
1849
1882
|
preset: value as StatusLinePreset,
|
|
@@ -1854,6 +1887,7 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1854
1887
|
});
|
|
1855
1888
|
this.#updateStatusPreview();
|
|
1856
1889
|
};
|
|
1890
|
+
onPreviewEnd = endStatusLinePreview;
|
|
1857
1891
|
onPreviewCancel = () => {
|
|
1858
1892
|
const currentPreset = settings.get("statusLine.preset");
|
|
1859
1893
|
const presetDef = getPreset(currentPreset);
|
|
@@ -1880,9 +1914,11 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1880
1914
|
};
|
|
1881
1915
|
} else if (def.path === "statusLine.separator") {
|
|
1882
1916
|
onPreview = value => {
|
|
1917
|
+
startStatusLinePreview();
|
|
1883
1918
|
this.callbacks.onStatusLinePreview?.({ separator: value as StatusLineSeparatorStyle });
|
|
1884
1919
|
this.#updateStatusPreview();
|
|
1885
1920
|
};
|
|
1921
|
+
onPreviewEnd = endStatusLinePreview;
|
|
1886
1922
|
onPreviewCancel = () => {
|
|
1887
1923
|
const separator = settings.get("statusLine.separator");
|
|
1888
1924
|
this.callbacks.onStatusLinePreview?.({ separator, previewHighlightSegment: undefined });
|
|
@@ -1890,9 +1926,11 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1890
1926
|
};
|
|
1891
1927
|
} else if (def.path === "statusLine.maxRows") {
|
|
1892
1928
|
onPreview = value => {
|
|
1929
|
+
startStatusLinePreview();
|
|
1893
1930
|
this.callbacks.onStatusLinePreview?.({ maxRows: Number(value) });
|
|
1894
1931
|
this.#updateStatusPreview();
|
|
1895
1932
|
};
|
|
1933
|
+
onPreviewEnd = endStatusLinePreview;
|
|
1896
1934
|
onPreviewCancel = () => {
|
|
1897
1935
|
this.callbacks.onStatusLinePreview?.({
|
|
1898
1936
|
maxRows: settings.get("statusLine.maxRows"),
|
|
@@ -1914,7 +1952,14 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1914
1952
|
const isThemeSetting = def.path === "theme.dark" || def.path === "theme.light";
|
|
1915
1953
|
const getPreview = isThemeSetting ? this.callbacks.getStatusLinePreview : undefined;
|
|
1916
1954
|
|
|
1917
|
-
|
|
1955
|
+
let submenu: SelectSubmenu;
|
|
1956
|
+
const finish = (value?: string): void => {
|
|
1957
|
+
if (this.#activeSelectSubmenu === submenu) this.#activeSelectSubmenu = null;
|
|
1958
|
+
if (this.#activeModelProfileSubmenu === submenu) this.#activeModelProfileSubmenu = null;
|
|
1959
|
+
submenu.dispose();
|
|
1960
|
+
done(value);
|
|
1961
|
+
};
|
|
1962
|
+
submenu = new SelectSubmenu(
|
|
1918
1963
|
title,
|
|
1919
1964
|
description,
|
|
1920
1965
|
options,
|
|
@@ -1922,7 +1967,7 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1922
1967
|
value => {
|
|
1923
1968
|
if (def.path === "modelProfile.default") {
|
|
1924
1969
|
this.callbacks.onChange(def.path, value);
|
|
1925
|
-
|
|
1970
|
+
finish(value);
|
|
1926
1971
|
return;
|
|
1927
1972
|
}
|
|
1928
1973
|
if (def.path === "theme.dark" || def.path === "theme.light") {
|
|
@@ -1937,7 +1982,7 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1937
1982
|
}
|
|
1938
1983
|
if (!this.callbacks.onThemeCommit) return;
|
|
1939
1984
|
void this.callbacks.onThemeCommit(def.path, value, currentValue).then(accepted => {
|
|
1940
|
-
if (accepted)
|
|
1985
|
+
if (accepted) finish(value);
|
|
1941
1986
|
});
|
|
1942
1987
|
return;
|
|
1943
1988
|
}
|
|
@@ -1953,21 +1998,26 @@ export class SettingsSelectorComponent extends Container {
|
|
|
1953
1998
|
) {
|
|
1954
1999
|
return;
|
|
1955
2000
|
}
|
|
1956
|
-
|
|
2001
|
+
finish(accepted ? value : undefined);
|
|
1957
2002
|
return;
|
|
1958
2003
|
}
|
|
1959
2004
|
if (!commitInteractiveSettings(this.callbacks, () => this.#setSettingValue(def.path, value))) return;
|
|
1960
2005
|
this.callbacks.onChange(def.path, value);
|
|
1961
2006
|
if (def.path === "ui.language") this.#refreshTabBarLanguage();
|
|
1962
|
-
|
|
2007
|
+
onPreviewEnd?.();
|
|
2008
|
+
finish(value);
|
|
1963
2009
|
},
|
|
1964
2010
|
() => {
|
|
1965
2011
|
onPreviewCancel?.();
|
|
1966
|
-
|
|
2012
|
+
onPreviewEnd?.();
|
|
2013
|
+
finish();
|
|
1967
2014
|
},
|
|
1968
2015
|
onPreview,
|
|
1969
2016
|
getPreview,
|
|
1970
2017
|
);
|
|
2018
|
+
this.#activeSelectSubmenu = submenu;
|
|
2019
|
+
if (def.path === "modelProfile.default") this.#activeModelProfileSubmenu = submenu;
|
|
2020
|
+
return submenu;
|
|
1971
2021
|
}
|
|
1972
2022
|
|
|
1973
2023
|
/**
|
|
@@ -2277,8 +2327,24 @@ export class SettingsSelectorComponent extends Container {
|
|
|
2277
2327
|
return (this.#currentList || this.#pluginComponent || this.#gjcBundleComponent || this.#notificationsEditor)!;
|
|
2278
2328
|
}
|
|
2279
2329
|
|
|
2330
|
+
/** Refresh saved rows and model-profile choices without replacing active editors. */
|
|
2331
|
+
refreshFromConfiguration(availableModelProfiles: readonly string[]): void {
|
|
2332
|
+
this.context.availableModelProfiles = [...availableModelProfiles];
|
|
2333
|
+
this.#refreshCurrentTabItems();
|
|
2334
|
+
this.#activeModelProfileSubmenu?.refreshOptions(
|
|
2335
|
+
this.context.availableModelProfiles.map(profile => ({ value: profile, label: profile })),
|
|
2336
|
+
true,
|
|
2337
|
+
);
|
|
2338
|
+
this.#activeSelectSubmenu?.refreshPreview();
|
|
2339
|
+
this.#updateStatusPreview();
|
|
2340
|
+
}
|
|
2341
|
+
|
|
2280
2342
|
override dispose(): void {
|
|
2281
2343
|
if (this.#notificationsEditor?.navigationLocked) return;
|
|
2344
|
+
this.callbacks.onStatusLinePreviewEnd?.();
|
|
2345
|
+
this.#activeSelectSubmenu?.dispose();
|
|
2346
|
+
this.#activeSelectSubmenu = null;
|
|
2347
|
+
this.#activeModelProfileSubmenu = null;
|
|
2282
2348
|
// Release an open provider-order editor (and its context subscriptions)
|
|
2283
2349
|
// when the whole selector is torn down without a normal close.
|
|
2284
2350
|
this.#activeProviderOrderEditor?.dispose();
|
|
@@ -6,6 +6,7 @@ import { getEnvApiKey, type ToolCall, type UsageLimit, type UsageReport } from "
|
|
|
6
6
|
import type { ProviderDetails } from "@gajae-code/ai/provider-details";
|
|
7
7
|
import { type Keybinding, Loader, Markdown, padding, Spacer, Text, visibleWidth } from "@gajae-code/tui";
|
|
8
8
|
import { formatBytes, formatDuration, Snowflake, setProjectDir } from "@gajae-code/utils";
|
|
9
|
+
import { canonicalEnvKey, projectEnvSnapshot } from "@gajae-code/utils/env-file";
|
|
9
10
|
import { resolveAppendOnlyMode } from "../../append-only-mode";
|
|
10
11
|
import { jobElapsedMs } from "../../async";
|
|
11
12
|
import { reset as resetCapabilities } from "../../capability";
|
|
@@ -107,6 +108,40 @@ export function buildHelpMarkdown(keybindings: Pick<KeybindingsManager, "getAcce
|
|
|
107
108
|
].join("\n");
|
|
108
109
|
}
|
|
109
110
|
|
|
111
|
+
const GH_SHARE_DESTINATION_KEYS = ["GH_HOST", "GH_CONFIG_DIR"] as const;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Project dotenv at import, before `/move` changes cwd. Those declarations are
|
|
115
|
+
* already in the process environment, and a later `setProjectDir` does not
|
|
116
|
+
* remove them.
|
|
117
|
+
*/
|
|
118
|
+
const ghShareProjectSnapshot = projectEnvSnapshot();
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Environment for the default `/share` `gh` children. A `GH_HOST` or
|
|
122
|
+
* `GH_CONFIG_DIR` that matches the project dotenv is omitted so the repository
|
|
123
|
+
* cannot retarget gist upload or point `gh` at a config directory it shipped.
|
|
124
|
+
* An operator value the project does not declare is kept.
|
|
125
|
+
*/
|
|
126
|
+
function ghShareChildEnv(env: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
|
|
127
|
+
const child: NodeJS.ProcessEnv = { ...env };
|
|
128
|
+
for (const name of GH_SHARE_DESTINATION_KEYS) {
|
|
129
|
+
const raw = child[name];
|
|
130
|
+
if (typeof raw !== "string") continue;
|
|
131
|
+
const key = canonicalEnvKey(name);
|
|
132
|
+
const declared = ghShareProjectSnapshot.values[key];
|
|
133
|
+
if (declared === undefined) continue;
|
|
134
|
+
if (!(ghShareProjectSnapshot.dynamic.has(key) || declared === raw || declared === raw.trim())) continue;
|
|
135
|
+
delete child[name];
|
|
136
|
+
if (process.platform === "win32") {
|
|
137
|
+
for (const present of Object.keys(child)) {
|
|
138
|
+
if (canonicalEnvKey(present) === key) delete child[present];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return child;
|
|
143
|
+
}
|
|
144
|
+
|
|
110
145
|
export class CommandController {
|
|
111
146
|
constructor(private readonly ctx: InteractiveModeContext) {}
|
|
112
147
|
|
|
@@ -242,9 +277,10 @@ export class CommandController {
|
|
|
242
277
|
return;
|
|
243
278
|
}
|
|
244
279
|
|
|
280
|
+
const ghEnv = ghShareChildEnv();
|
|
245
281
|
try {
|
|
246
282
|
const authProcess = Bun.spawn(["gh", "auth", "status"], {
|
|
247
|
-
env:
|
|
283
|
+
env: ghEnv,
|
|
248
284
|
stdin: "ignore",
|
|
249
285
|
stdout: "ignore",
|
|
250
286
|
stderr: "ignore",
|
|
@@ -278,7 +314,7 @@ export class CommandController {
|
|
|
278
314
|
let cancellationRequested = false;
|
|
279
315
|
try {
|
|
280
316
|
const gistProcess = Bun.spawn(["gh", "gist", "create", "--public=false", tmpFile], {
|
|
281
|
-
env:
|
|
317
|
+
env: ghEnv,
|
|
282
318
|
stdin: "ignore",
|
|
283
319
|
stdout: "pipe",
|
|
284
320
|
stderr: "pipe",
|