@oh-my-pi/pi-coding-agent 17.0.8 → 17.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -1
- package/README.md +1 -1
- package/dist/cli.js +5495 -5021
- package/dist/types/async/job-manager.d.ts +37 -1
- package/dist/types/cli/args.d.ts +2 -0
- package/dist/types/cli/bench-cli.d.ts +41 -0
- package/dist/types/commands/bench.d.ts +15 -3
- package/dist/types/commands/launch.d.ts +4 -0
- package/dist/types/config/__tests__/model-registry.test.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +30 -1
- package/dist/types/config/models-config-schema.d.ts +18 -6
- package/dist/types/config/models-config.d.ts +14 -5
- package/dist/types/config/provider-globals.d.ts +2 -2
- package/dist/types/config/service-tier.d.ts +5 -1
- package/dist/types/config/settings-schema.d.ts +350 -50
- package/dist/types/config/settings.d.ts +7 -0
- package/dist/types/dap/client.d.ts +20 -0
- package/dist/types/exec/bash-executor.d.ts +38 -0
- package/dist/types/exec/direnv.d.ts +33 -0
- package/dist/types/export/html/args.d.ts +15 -0
- package/dist/types/export/html/index.d.ts +11 -26
- package/dist/types/export/html/web-palette.d.ts +55 -116
- package/dist/types/extensibility/extensions/loader.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +17 -2
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -0
- package/dist/types/extensibility/legacy-pi-tui-shim.d.ts +10 -0
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +14 -1
- package/dist/types/internal-urls/registry-helpers.d.ts +11 -0
- package/dist/types/live/controller.d.ts +40 -0
- package/dist/types/live/protocol.d.ts +85 -0
- package/dist/types/live/protocol.test.d.ts +1 -0
- package/dist/types/live/transport.d.ts +48 -0
- package/dist/types/live/visualizer.d.ts +33 -0
- package/dist/types/lsp/client.d.ts +18 -1
- package/dist/types/mcp/config-writer.test.d.ts +1 -0
- package/dist/types/mcp/manager.d.ts +6 -2
- package/dist/types/mcp/oauth-discovery.d.ts +2 -0
- package/dist/types/mcp/render.d.ts +2 -2
- package/dist/types/mcp/smithery-auth.d.ts +1 -1
- package/dist/types/mcp/smithery-auth.test.d.ts +1 -0
- package/dist/types/mcp/tool-bridge.d.ts +7 -3
- package/dist/types/mcp/types.d.ts +6 -0
- package/dist/types/memory-backend/tool-names.d.ts +2 -0
- package/dist/types/modes/components/login-dialog.d.ts +2 -2
- package/dist/types/modes/components/oauth-selector.d.ts +8 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +26 -0
- package/dist/types/modes/components/settings-defs.d.ts +8 -1
- package/dist/types/modes/components/status-line/component.jj-cache.test.d.ts +1 -0
- package/dist/types/modes/components/tool-execution.d.ts +9 -1
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/event-controller.d.ts +6 -1
- package/dist/types/modes/controllers/live-command-controller.d.ts +14 -0
- package/dist/types/modes/controllers/mcp-command-controller.d.ts +3 -0
- package/dist/types/modes/interactive-mode.d.ts +5 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +10 -1
- package/dist/types/modes/rpc/rpc-frame.d.ts +16 -0
- package/dist/types/modes/rpc/rpc-messages.d.ts +26 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +40 -0
- package/dist/types/modes/setup-wizard/scenes/sign-in.d.ts +1 -1
- package/dist/types/modes/setup-wizard/scenes/types.d.ts +9 -1
- package/dist/types/modes/setup-wizard/scenes/web-search.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +3 -1
- package/dist/types/modes/utils/context-usage.d.ts +12 -2
- package/dist/types/plan-mode/plan-files.d.ts +10 -0
- package/dist/types/registry/agent-lifecycle.d.ts +21 -9
- package/dist/types/registry/agent-registry.d.ts +11 -4
- package/dist/types/sdk.d.ts +15 -1
- package/dist/types/secrets/index.d.ts +15 -2
- package/dist/types/secrets/obfuscator.d.ts +113 -15
- package/dist/types/session/acp-permission-gate.d.ts +19 -0
- package/dist/types/session/agent-session-events.d.ts +84 -0
- package/dist/types/session/agent-session-types.d.ts +314 -0
- package/dist/types/session/agent-session.d.ts +120 -750
- package/dist/types/session/async-job-delivery.d.ts +28 -0
- package/dist/types/session/bash-runner.d.ts +75 -0
- package/dist/types/session/blob-store.d.ts +11 -1
- package/dist/types/session/blob-store.test.d.ts +1 -0
- package/dist/types/session/checkpoint-entries.d.ts +28 -0
- package/dist/types/session/eval-runner.d.ts +49 -0
- package/dist/types/session/irc-bridge.d.ts +46 -0
- package/dist/types/session/messages.d.ts +22 -0
- package/dist/types/session/model-controls.d.ts +180 -0
- package/dist/types/session/prewalk.d.ts +46 -0
- package/dist/types/session/queued-messages.d.ts +22 -0
- package/dist/types/session/redis-session-storage.d.ts +1 -0
- package/dist/types/session/retry-fallback-chains.d.ts +74 -0
- package/dist/types/session/role-models.d.ts +15 -0
- package/dist/types/session/session-advisors.d.ts +228 -0
- package/dist/types/session/session-entries.d.ts +8 -0
- package/dist/types/session/session-handoff.d.ts +70 -0
- package/dist/types/session/session-history-format.d.ts +6 -1
- package/dist/types/session/session-loader.d.ts +6 -4
- package/dist/types/session/session-maintenance.d.ts +246 -0
- package/dist/types/session/session-manager.d.ts +41 -0
- package/dist/types/session/session-memory.d.ts +56 -0
- package/dist/types/session/session-provider-boundary.d.ts +62 -0
- package/dist/types/session/session-stats.d.ts +48 -0
- package/dist/types/session/session-tools.d.ts +143 -0
- package/dist/types/session/session-workspace.d.ts +32 -0
- package/dist/types/session/stream-guards.d.ts +44 -0
- package/dist/types/session/todo-tracker.d.ts +56 -0
- package/dist/types/session/ttsr-coordinator.d.ts +51 -0
- package/dist/types/session/turn-recovery.d.ts +225 -0
- package/dist/types/system-prompt.d.ts +2 -0
- package/dist/types/task/executor.d.ts +4 -1
- package/dist/types/task/output-manager.d.ts +3 -1
- package/dist/types/task/types.d.ts +14 -0
- package/dist/types/tools/approval.d.ts +1 -0
- package/dist/types/tools/browser/launch.d.ts +5 -0
- package/dist/types/tools/hub/jobs.d.ts +1 -1
- package/dist/types/tools/image-gen.d.ts +4 -4
- package/dist/types/tools/image-providers.d.ts +38 -0
- package/dist/types/tools/index.d.ts +8 -0
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/report-tool-issue.d.ts +24 -9
- package/dist/types/tools/todo.d.ts +10 -4
- package/dist/types/tools/xdev.d.ts +5 -1
- package/dist/types/tts/streaming-player.d.ts +14 -0
- package/dist/types/tts/vocalizer.d.ts +5 -0
- package/dist/types/utils/jj.d.ts +29 -0
- package/dist/types/utils/lang-from-path.d.ts +1 -0
- package/dist/types/utils/title-generator.d.ts +30 -0
- package/dist/types/vibe/runtime.d.ts +36 -8
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/provider.d.ts +15 -5
- package/dist/types/web/search/providers/firecrawl.d.ts +9 -0
- package/dist/types/web/search/types.d.ts +95 -1
- package/package.json +13 -12
- package/scripts/generate-share-viewer.ts +4 -6
- package/scripts/legacy-pi-virtual-module.ts +1 -1
- package/src/advisor/__tests__/advisor.test.ts +643 -0
- package/src/advisor/runtime.ts +144 -43
- package/src/async/job-manager.ts +90 -2
- package/src/cli/args.ts +2 -0
- package/src/cli/auth-gateway-cli.ts +18 -3
- package/src/cli/bench-cli.ts +395 -29
- package/src/cli/flag-tables.ts +3 -0
- package/src/cli/gc-cli.ts +2 -2
- package/src/cli/grievances-cli.ts +2 -2
- package/src/cli/usage-cli.ts +11 -2
- package/src/collab/host.ts +3 -2
- package/src/commands/bench.ts +18 -4
- package/src/commands/launch.ts +4 -0
- package/src/config/__tests__/model-registry.test.ts +147 -0
- package/src/config/model-registry.ts +50 -2
- package/src/config/model-resolver.ts +0 -1
- package/src/config/models-config-schema.ts +15 -4
- package/src/config/provider-globals.ts +9 -9
- package/src/config/service-tier.ts +26 -1
- package/src/config/settings-schema.ts +250 -54
- package/src/config/settings.ts +81 -2
- package/src/dap/client.ts +37 -4
- package/src/edit/index.ts +49 -12
- package/src/eval/py/executor.ts +103 -33
- package/src/exec/bash-executor.ts +89 -6
- package/src/exec/direnv.ts +145 -0
- package/src/export/html/args.ts +20 -0
- package/src/export/html/index.ts +45 -47
- package/src/export/html/template.css +19 -1
- package/src/export/html/template.html +6 -0
- package/src/export/html/template.js +21 -0
- package/src/export/html/tool-views.generated.js +31 -31
- package/src/export/html/web-palette.ts +116 -132
- package/src/export/share.ts +249 -49
- package/src/extensibility/extensions/loader.ts +29 -1
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +33 -2
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +1 -0
- package/src/extensibility/legacy-pi-tui-shim.ts +10 -0
- package/src/extensibility/plugins/legacy-pi-compat.ts +870 -311
- package/src/hindsight/state.ts +64 -6
- package/src/internal-urls/local-protocol.ts +2 -1
- package/src/internal-urls/memory-protocol.ts +2 -2
- package/src/internal-urls/registry-helpers.ts +40 -0
- package/src/internal-urls/skill-protocol.ts +2 -2
- package/src/internal-urls/ssh-protocol.ts +2 -1
- package/src/internal-urls/vault-protocol.ts +2 -1
- package/src/live/audio-worklet.txt +59 -0
- package/src/live/browser-runtime.txt +221 -0
- package/src/live/controller.ts +464 -0
- package/src/live/prompts/agent-final-message.md +3 -0
- package/src/live/prompts/live-instructions.md +23 -0
- package/src/live/protocol.test.ts +140 -0
- package/src/live/protocol.ts +233 -0
- package/src/live/transport.ts +490 -0
- package/src/live/visualizer.ts +234 -0
- package/src/lsp/client.ts +52 -9
- package/src/lsp/clients/biome-client.ts +3 -4
- package/src/lsp/index.ts +44 -11
- package/src/main.ts +9 -1
- package/src/mcp/config-writer.test.ts +43 -0
- package/src/mcp/config-writer.ts +109 -82
- package/src/mcp/manager.ts +39 -8
- package/src/mcp/oauth-discovery.ts +10 -2
- package/src/mcp/render.ts +94 -35
- package/src/mcp/smithery-auth.test.ts +29 -0
- package/src/mcp/smithery-auth.ts +3 -2
- package/src/mcp/tool-bridge.ts +107 -11
- package/src/mcp/types.ts +7 -0
- package/src/memories/index.ts +40 -20
- package/src/memory-backend/tool-names.ts +2 -0
- package/src/mnemopi/backend.ts +24 -7
- package/src/modes/acp/acp-agent.ts +56 -10
- package/src/modes/acp/acp-event-mapper.ts +8 -1
- package/src/modes/components/agent-hub.ts +27 -10
- package/src/modes/components/login-dialog.ts +14 -4
- package/src/modes/components/oauth-selector.ts +24 -7
- package/src/modes/components/plan-review-overlay.ts +350 -35
- package/src/modes/components/settings-defs.ts +28 -3
- package/src/modes/components/settings-selector.ts +178 -6
- package/src/modes/components/status-line/component.jj-cache.test.ts +229 -0
- package/src/modes/components/status-line/component.ts +123 -2
- package/src/modes/components/tool-execution.test.ts +63 -2
- package/src/modes/components/tool-execution.ts +11 -2
- package/src/modes/controllers/command-controller.ts +70 -22
- package/src/modes/controllers/event-controller.ts +111 -16
- package/src/modes/controllers/extension-ui-controller.test.ts +1 -0
- package/src/modes/controllers/extension-ui-controller.ts +20 -4
- package/src/modes/controllers/live-command-controller.ts +178 -0
- package/src/modes/controllers/mcp-command-controller.ts +80 -41
- package/src/modes/controllers/omfg-controller.ts +44 -40
- package/src/modes/controllers/selector-controller.ts +18 -10
- package/src/modes/controllers/session-focus-controller.ts +6 -1
- package/src/modes/interactive-mode.ts +160 -38
- package/src/modes/rpc/rpc-client.ts +94 -3
- package/src/modes/rpc/rpc-frame.ts +164 -4
- package/src/modes/rpc/rpc-messages.ts +127 -0
- package/src/modes/rpc/rpc-mode.ts +79 -7
- package/src/modes/rpc/rpc-types.ts +34 -2
- package/src/modes/runtime-init.ts +2 -0
- package/src/modes/setup-wizard/scenes/model.ts +5 -7
- package/src/modes/setup-wizard/scenes/providers.ts +3 -2
- package/src/modes/setup-wizard/scenes/sign-in.ts +8 -2
- package/src/modes/setup-wizard/scenes/theme.ts +13 -3
- package/src/modes/setup-wizard/scenes/types.ts +9 -1
- package/src/modes/setup-wizard/scenes/web-search.ts +23 -10
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme.ts +1 -1
- package/src/modes/types.ts +3 -1
- package/src/modes/utils/context-usage.ts +23 -7
- package/src/modes/utils/ui-helpers.ts +10 -5
- package/src/plan-mode/plan-files.ts +40 -0
- package/src/prompts/bench/cache-prefix-chunk.md +1 -0
- package/src/prompts/bench/cache-prefix.md +3 -0
- package/src/prompts/bench/cache-suffix.md +1 -0
- package/src/prompts/goals/guided-goal-system.md +24 -3
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/project-prompt.md +8 -1
- package/src/prompts/system/subagent-async-pending.md +6 -0
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/prompts/system/workflow-notice.md +48 -40
- package/src/prompts/system/xdev-mount-notice.md +4 -0
- package/src/prompts/tools/task.md +12 -2
- package/src/prompts/tools/todo.md +3 -1
- package/src/registry/agent-lifecycle.ts +116 -46
- package/src/registry/agent-registry.ts +35 -8
- package/src/registry/persisted-agents.ts +26 -2
- package/src/sdk.ts +255 -139
- package/src/secrets/index.ts +127 -4
- package/src/secrets/obfuscator.ts +2328 -105
- package/src/session/acp-permission-gate.ts +165 -0
- package/src/session/agent-session-error-log.test.ts +1 -1
- package/src/session/agent-session-events.ts +66 -0
- package/src/session/agent-session-types.ts +334 -0
- package/src/session/agent-session.ts +1910 -12213
- package/src/session/artifacts.ts +6 -5
- package/src/session/async-job-delivery.ts +74 -0
- package/src/session/bash-runner.ts +326 -0
- package/src/session/blob-store.test.ts +56 -0
- package/src/session/blob-store.ts +18 -2
- package/src/session/checkpoint-entries.ts +81 -0
- package/src/session/eval-runner.ts +212 -0
- package/src/session/indexed-session-storage.ts +9 -2
- package/src/session/irc-bridge.ts +203 -0
- package/src/session/messages.ts +263 -1
- package/src/session/model-controls.ts +714 -0
- package/src/session/prewalk.ts +252 -0
- package/src/session/queued-messages.ts +93 -0
- package/src/session/redis-session-storage.ts +51 -11
- package/src/session/retry-fallback-chains.ts +455 -0
- package/src/session/role-models.ts +85 -0
- package/src/session/session-advisors.ts +1679 -0
- package/src/session/session-context.test.ts +224 -1
- package/src/session/session-context.ts +47 -5
- package/src/session/session-entries.ts +8 -0
- package/src/session/session-handoff.ts +308 -0
- package/src/session/session-history-format.ts +20 -9
- package/src/session/session-loader.ts +10 -51
- package/src/session/session-maintenance.ts +2983 -0
- package/src/session/session-manager.ts +390 -30
- package/src/session/session-memory.ts +222 -0
- package/src/session/session-provider-boundary.ts +307 -0
- package/src/session/session-stats.ts +293 -0
- package/src/session/session-tools.ts +943 -0
- package/src/session/session-workspace.ts +53 -0
- package/src/session/stream-guards.ts +417 -0
- package/src/session/todo-tracker.ts +380 -0
- package/src/session/ttsr-coordinator.ts +496 -0
- package/src/session/turn-recovery.ts +1629 -0
- package/src/slash-commands/builtin-registry.ts +92 -11
- package/src/slash-commands/helpers/usage-report.ts +25 -4
- package/src/system-prompt.ts +41 -17
- package/src/task/executor.ts +288 -70
- package/src/task/index.ts +148 -111
- package/src/task/output-manager.ts +25 -3
- package/src/task/persisted-revive.ts +4 -3
- package/src/task/structured-subagent.ts +4 -1
- package/src/task/types.ts +16 -0
- package/src/tools/approval.ts +55 -10
- package/src/tools/bash-interactive.ts +26 -0
- package/src/tools/bash-skill-urls.ts +28 -2
- package/src/tools/bash.ts +339 -123
- package/src/tools/browser/launch.ts +9 -1
- package/src/tools/browser/tab-supervisor.ts +8 -5
- package/src/tools/browser.ts +94 -47
- package/src/tools/hub/index.ts +1 -1
- package/src/tools/hub/jobs.ts +67 -8
- package/src/tools/image-gen.ts +20 -28
- package/src/tools/image-providers.ts +50 -0
- package/src/tools/index.ts +12 -0
- package/src/tools/path-utils.ts +49 -2
- package/src/tools/read.ts +156 -88
- package/src/tools/renderers.ts +7 -1
- package/src/tools/report-tool-issue.ts +79 -28
- package/src/tools/todo.ts +101 -11
- package/src/tools/vibe.ts +1 -2
- package/src/tools/write.ts +51 -1
- package/src/tools/xdev.ts +67 -3
- package/src/tts/streaming-player.ts +62 -4
- package/src/tts/vocalizer.ts +18 -1
- package/src/utils/jj.ts +125 -4
- package/src/utils/lang-from-path.ts +7 -0
- package/src/utils/title-generator.ts +137 -1
- package/src/vibe/runtime.ts +852 -72
- package/src/web/search/index.ts +3 -6
- package/src/web/search/provider.ts +31 -16
- package/src/web/search/providers/firecrawl.ts +46 -13
- package/src/web/search/providers/xai.ts +7 -1
- package/src/web/search/types.ts +8 -1
package/src/utils/jj.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as fs from "node:fs
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import { $which } from "@oh-my-pi/pi-utils";
|
|
4
4
|
import { LRUCache } from "lru-cache/raw";
|
|
@@ -63,6 +63,8 @@ export class JjCommandError extends Error {
|
|
|
63
63
|
// ════════════════════════════════════════════════════════════════════════════
|
|
64
64
|
// Internal: Core execution
|
|
65
65
|
// ════════════════════════════════════════════════════════════════════════════
|
|
66
|
+
const WORKING_COPY_LABEL_REVSET = "@ | heads(::@ & bookmarks())";
|
|
67
|
+
const WORKING_COPY_LABEL_TEMPLATE = 'change_id.shortest(8) ++ "|" ++ local_bookmarks ++ "\\n"';
|
|
66
68
|
|
|
67
69
|
function ensureAvailable(): void {
|
|
68
70
|
if (!$which("jj")) {
|
|
@@ -121,6 +123,19 @@ async function runText(cwd: string, args: readonly string[], options: CommandOpt
|
|
|
121
123
|
return (await runChecked(cwd, args, options)).stdout;
|
|
122
124
|
}
|
|
123
125
|
|
|
126
|
+
async function runOptionalText(
|
|
127
|
+
cwd: string,
|
|
128
|
+
args: readonly string[],
|
|
129
|
+
options: CommandOptions = {},
|
|
130
|
+
): Promise<string | null> {
|
|
131
|
+
try {
|
|
132
|
+
const result = await jj(cwd, args, options);
|
|
133
|
+
return result.exitCode === 0 ? result.stdout : null;
|
|
134
|
+
} catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
124
139
|
function splitLines(text: string): string[] {
|
|
125
140
|
return text
|
|
126
141
|
.split("\n")
|
|
@@ -135,6 +150,30 @@ function buildDiffArgs(options: DiffOptions): string[] {
|
|
|
135
150
|
return args;
|
|
136
151
|
}
|
|
137
152
|
|
|
153
|
+
function parseWorkingCopyLabel(raw: string): string | null {
|
|
154
|
+
let changeId: string | null = null;
|
|
155
|
+
for (const line of raw.split("\n")) {
|
|
156
|
+
const sep = line.indexOf("|");
|
|
157
|
+
const change = (sep === -1 ? line : line.slice(0, sep)).trim();
|
|
158
|
+
const bookmarks = sep === -1 ? "" : line.slice(sep + 1).trim();
|
|
159
|
+
if (changeId === null && change) changeId = change;
|
|
160
|
+
if (bookmarks) return bookmarks.replace(/\s+/g, " ");
|
|
161
|
+
}
|
|
162
|
+
return changeId;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function parseStatusSummary(raw: string): git.GitStatusSummary {
|
|
166
|
+
let unstaged = 0;
|
|
167
|
+
let untracked = 0;
|
|
168
|
+
for (const line of raw.split("\n")) {
|
|
169
|
+
const type = line.trim()[0];
|
|
170
|
+
if (!type) continue;
|
|
171
|
+
if (type === "A") untracked++;
|
|
172
|
+
else unstaged++;
|
|
173
|
+
}
|
|
174
|
+
return { staged: 0, unstaged, untracked };
|
|
175
|
+
}
|
|
176
|
+
|
|
138
177
|
// ════════════════════════════════════════════════════════════════════════════
|
|
139
178
|
// Internal: Repository resolution
|
|
140
179
|
// ════════════════════════════════════════════════════════════════════════════
|
|
@@ -153,7 +192,16 @@ async function hasJjWorkspaceMetadata(dir: string): Promise<boolean> {
|
|
|
153
192
|
// of the default workspace. Either form is a real workspace, so match on
|
|
154
193
|
// `.jj/repo` presence rather than the inner `store/` directory.
|
|
155
194
|
try {
|
|
156
|
-
await fs.stat(path.join(dir, ".jj", "repo"));
|
|
195
|
+
await fs.promises.stat(path.join(dir, ".jj", "repo"));
|
|
196
|
+
return true;
|
|
197
|
+
} catch {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function hasJjWorkspaceMetadataSync(dir: string): boolean {
|
|
203
|
+
try {
|
|
204
|
+
fs.statSync(path.join(dir, ".jj", "repo"));
|
|
157
205
|
return true;
|
|
158
206
|
} catch {
|
|
159
207
|
return false;
|
|
@@ -180,6 +228,21 @@ async function findWorkspaceRoot(cwd: string): Promise<string | undefined> {
|
|
|
180
228
|
return undefined;
|
|
181
229
|
}
|
|
182
230
|
|
|
231
|
+
function findWorkspaceRootSync(cwd: string): string | undefined {
|
|
232
|
+
const key = path.resolve(cwd);
|
|
233
|
+
if (workspaceRootCache.has(key)) return workspaceRootCache.get(key)?.root;
|
|
234
|
+
|
|
235
|
+
for (let dir: string | undefined = key; dir; dir = parentOf(dir)) {
|
|
236
|
+
if (hasJjWorkspaceMetadataSync(dir)) {
|
|
237
|
+
workspaceRootCache.set(key, { root: dir });
|
|
238
|
+
return dir;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
workspaceRootCache.set(key, {});
|
|
243
|
+
return undefined;
|
|
244
|
+
}
|
|
245
|
+
|
|
183
246
|
/**
|
|
184
247
|
* Resolve the `.jj/repo` directory backing a workspace root, following the file
|
|
185
248
|
* indirection used by non-default workspaces. `jj workspace add` writes a FILE at
|
|
@@ -190,8 +253,8 @@ async function findWorkspaceRoot(cwd: string): Promise<string | undefined> {
|
|
|
190
253
|
async function resolveRepoDir(root: string): Promise<string> {
|
|
191
254
|
const jjDir = path.join(root, ".jj");
|
|
192
255
|
const repoPath = path.join(jjDir, "repo");
|
|
193
|
-
if ((await fs.stat(repoPath)).isFile()) {
|
|
194
|
-
const target = (await fs.readFile(repoPath, "utf8")).trim();
|
|
256
|
+
if ((await fs.promises.stat(repoPath)).isFile()) {
|
|
257
|
+
const target = (await fs.promises.readFile(repoPath, "utf8")).trim();
|
|
195
258
|
return path.resolve(jjDir, target);
|
|
196
259
|
}
|
|
197
260
|
return repoPath;
|
|
@@ -221,6 +284,56 @@ export const diff = Object.assign(
|
|
|
221
284
|
},
|
|
222
285
|
);
|
|
223
286
|
|
|
287
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
288
|
+
// API: working copy
|
|
289
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
290
|
+
|
|
291
|
+
/** Jujutsu working-copy metadata used by status displays. */
|
|
292
|
+
export const workingCopy = {
|
|
293
|
+
/**
|
|
294
|
+
* Label `@` with its nearest bookmark, falling back to its short change ID.
|
|
295
|
+
* Returns `null` when `jj` is unavailable or the query fails.
|
|
296
|
+
*/
|
|
297
|
+
async label(cwd: string, signal?: AbortSignal): Promise<string | null> {
|
|
298
|
+
const raw = await runOptionalText(
|
|
299
|
+
cwd,
|
|
300
|
+
[
|
|
301
|
+
"log",
|
|
302
|
+
"--no-graph",
|
|
303
|
+
"--ignore-working-copy",
|
|
304
|
+
"-r",
|
|
305
|
+
WORKING_COPY_LABEL_REVSET,
|
|
306
|
+
"-T",
|
|
307
|
+
WORKING_COPY_LABEL_TEMPLATE,
|
|
308
|
+
],
|
|
309
|
+
{ signal },
|
|
310
|
+
);
|
|
311
|
+
return raw === null ? null : parseWorkingCopyLabel(raw);
|
|
312
|
+
},
|
|
313
|
+
|
|
314
|
+
/** Parse working-copy label query output. */
|
|
315
|
+
parseLabel: parseWorkingCopyLabel,
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
319
|
+
// API: status
|
|
320
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
321
|
+
|
|
322
|
+
/** Jujutsu working-copy status derived from the changes in `@`. */
|
|
323
|
+
export const status = {
|
|
324
|
+
/**
|
|
325
|
+
* Count changes in `@` relative to its parent using the Git status shape.
|
|
326
|
+
* Jujutsu has no index, so `staged` is always zero.
|
|
327
|
+
*/
|
|
328
|
+
async summary(cwd: string, signal?: AbortSignal): Promise<git.GitStatusSummary | null> {
|
|
329
|
+
const raw = await runOptionalText(cwd, ["diff", "-r", "@", "--summary", "--ignore-working-copy"], { signal });
|
|
330
|
+
return raw === null ? null : parseStatusSummary(raw);
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
/** Parse `jj diff --summary` output into status counts. */
|
|
334
|
+
parse: parseStatusSummary,
|
|
335
|
+
};
|
|
336
|
+
|
|
224
337
|
// ════════════════════════════════════════════════════════════════════════════
|
|
225
338
|
// API: repo
|
|
226
339
|
// ════════════════════════════════════════════════════════════════════════════
|
|
@@ -231,6 +344,14 @@ export const repo = {
|
|
|
231
344
|
workspaceRootCache.clear();
|
|
232
345
|
},
|
|
233
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Resolve the current workspace root synchronously from on-disk metadata.
|
|
349
|
+
* Intended for render paths that cannot await filesystem I/O.
|
|
350
|
+
*/
|
|
351
|
+
rootSync(cwd: string): string | null {
|
|
352
|
+
return findWorkspaceRootSync(cwd) ?? null;
|
|
353
|
+
},
|
|
354
|
+
|
|
234
355
|
/** Resolve the current Jujutsu workspace root, or `null` when `cwd` is not in a JJ repository. */
|
|
235
356
|
async root(cwd: string): Promise<string | null> {
|
|
236
357
|
return (await findWorkspaceRoot(cwd)) ?? null;
|
|
@@ -102,6 +102,9 @@ const EXTENSION_LANG: Record<string, readonly [string, string]> = {
|
|
|
102
102
|
md: ["markdown", "markdown"],
|
|
103
103
|
markdown: ["markdown", "markdown"],
|
|
104
104
|
mdx: ["markdown", "markdown"],
|
|
105
|
+
mdc: ["markdown", "markdown"],
|
|
106
|
+
mkd: ["markdown", "markdown"],
|
|
107
|
+
mdown: ["markdown", "markdown"],
|
|
105
108
|
rst: ["restructuredtext", "restructuredtext"],
|
|
106
109
|
adoc: ["asciidoc", "asciidoc"],
|
|
107
110
|
tex: ["latex", "latex"],
|
|
@@ -217,6 +220,10 @@ export function getLanguageFromPath(filePath: string): string | undefined {
|
|
|
217
220
|
return undefined;
|
|
218
221
|
}
|
|
219
222
|
|
|
223
|
+
export function isMarkdownPath(filePath: string): boolean {
|
|
224
|
+
return getLanguageFromPath(filePath) === "markdown";
|
|
225
|
+
}
|
|
226
|
+
|
|
220
227
|
/**
|
|
221
228
|
* LSP language identifier; falls back to `plaintext`.
|
|
222
229
|
*/
|
|
@@ -387,7 +387,143 @@ export function setTerminalTitle(title: string): void {
|
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
export function setSessionTerminalTitle(sessionName: string | undefined, cwd?: string): void {
|
|
390
|
-
|
|
390
|
+
// An authoritative session title (rename, new session, focus swap) supersedes
|
|
391
|
+
// any extension override so the base title tracks the real session again.
|
|
392
|
+
terminalTitleRuntime.extensionOverride = undefined;
|
|
393
|
+
terminalTitleRuntime.label = sanitizeTerminalTitlePart(sessionName) ?? getFallbackTerminalTitle(cwd);
|
|
394
|
+
emitTerminalTitle();
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Set a terminal title from an extension's `setTitle()`. Unlike the session base
|
|
399
|
+
* title, this owns the terminal verbatim: the run-state separator will not rewrite
|
|
400
|
+
* it on the next spinner tick or state change. Cleared when the app next sets an
|
|
401
|
+
* authoritative session title via {@link setSessionTerminalTitle}.
|
|
402
|
+
*/
|
|
403
|
+
export function setExtensionTerminalTitle(title: string): void {
|
|
404
|
+
terminalTitleRuntime.extensionOverride = title;
|
|
405
|
+
emitTerminalTitle();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export type TerminalTitleState = "idle" | "working" | "attention";
|
|
409
|
+
|
|
410
|
+
/** Separator glyphs carrying the run state between the `π` brand and the session
|
|
411
|
+
* label — the brand itself stays bare (no prefix glyph). Spinner frames animate
|
|
412
|
+
* the separator while working; they are self-contained (not the theme's symbol
|
|
413
|
+
* set) to avoid a utils→modes import cycle; OSC titles render in tab/window bars
|
|
414
|
+
* that handle Unicode. */
|
|
415
|
+
const TITLE_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] as const;
|
|
416
|
+
const TITLE_SPINNER_INTERVAL_MS = 80;
|
|
417
|
+
/** The user's turn: the title reads like a shell prompt awaiting input. */
|
|
418
|
+
const TITLE_IDLE_SEPARATOR = ">";
|
|
419
|
+
/** Agent blocked on the user (ask / approval prompt). */
|
|
420
|
+
const TITLE_ATTENTION_SEPARATOR = "!";
|
|
421
|
+
|
|
422
|
+
const terminalTitleRuntime: {
|
|
423
|
+
label: string | undefined;
|
|
424
|
+
state: TerminalTitleState;
|
|
425
|
+
frame: number;
|
|
426
|
+
enabled: boolean;
|
|
427
|
+
timer: NodeJS.Timeout | undefined;
|
|
428
|
+
lastEmitted: string | undefined;
|
|
429
|
+
/** A title an extension set via `setTitle()`. While set, it owns the terminal
|
|
430
|
+
* title verbatim: the run-state separator never rewrites it. Cleared when the
|
|
431
|
+
* app next establishes an authoritative session title (rename, new session,
|
|
432
|
+
* focus swap) via `setSessionTerminalTitle`. */
|
|
433
|
+
extensionOverride: string | undefined;
|
|
434
|
+
} = {
|
|
435
|
+
label: undefined,
|
|
436
|
+
state: "idle",
|
|
437
|
+
frame: 0,
|
|
438
|
+
enabled: true,
|
|
439
|
+
timer: undefined,
|
|
440
|
+
lastEmitted: undefined,
|
|
441
|
+
extensionOverride: undefined,
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Compose the terminal title from the `π` brand, a state-carrying SEPARATOR, and
|
|
446
|
+
* the session label. The brand never gains a prefix glyph — the separator slot
|
|
447
|
+
* expresses the run state instead. Pure (no I/O) so the state→separator contract
|
|
448
|
+
* is unit-testable:
|
|
449
|
+
* - `idle` (user's turn): `π > label` — reads like a prompt awaiting input;
|
|
450
|
+
* - `working`: `π ⠋ label` — spinner frames animate the separator;
|
|
451
|
+
* - `attention`: `π ! label` — agent blocked on the user;
|
|
452
|
+
* - disabled: `π: label` — the pre-state layout.
|
|
453
|
+
* Without a label the separator trails the brand (`π >`) so the state stays visible.
|
|
454
|
+
*/
|
|
455
|
+
export function buildTerminalTitleWithState(
|
|
456
|
+
label: string | undefined,
|
|
457
|
+
state: TerminalTitleState,
|
|
458
|
+
frame: number,
|
|
459
|
+
enabled: boolean,
|
|
460
|
+
): string {
|
|
461
|
+
if (!enabled) return label ? `${DEFAULT_TERMINAL_TITLE}: ${label}` : DEFAULT_TERMINAL_TITLE;
|
|
462
|
+
const separator =
|
|
463
|
+
state === "working"
|
|
464
|
+
? TITLE_SPINNER_FRAMES[frame % TITLE_SPINNER_FRAMES.length]
|
|
465
|
+
: state === "attention"
|
|
466
|
+
? TITLE_ATTENTION_SEPARATOR
|
|
467
|
+
: TITLE_IDLE_SEPARATOR;
|
|
468
|
+
return label ? `${DEFAULT_TERMINAL_TITLE} ${separator} ${label}` : `${DEFAULT_TERMINAL_TITLE} ${separator}`;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function emitTerminalTitle(): void {
|
|
472
|
+
// An extension override owns the terminal verbatim; the run-state separator and
|
|
473
|
+
// spinner ticks must not clobber it (still deduped via lastEmitted).
|
|
474
|
+
const next =
|
|
475
|
+
terminalTitleRuntime.extensionOverride ??
|
|
476
|
+
buildTerminalTitleWithState(
|
|
477
|
+
terminalTitleRuntime.label,
|
|
478
|
+
terminalTitleRuntime.state,
|
|
479
|
+
terminalTitleRuntime.frame,
|
|
480
|
+
terminalTitleRuntime.enabled,
|
|
481
|
+
);
|
|
482
|
+
if (next === terminalTitleRuntime.lastEmitted) return;
|
|
483
|
+
terminalTitleRuntime.lastEmitted = next;
|
|
484
|
+
setTerminalTitle(next);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
function stopTerminalTitleSpinner(): void {
|
|
488
|
+
if (terminalTitleRuntime.timer) {
|
|
489
|
+
clearInterval(terminalTitleRuntime.timer);
|
|
490
|
+
terminalTitleRuntime.timer = undefined;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function startTerminalTitleSpinner(): void {
|
|
495
|
+
if (terminalTitleRuntime.timer || !process.stdout.isTTY) return;
|
|
496
|
+
terminalTitleRuntime.timer = setInterval(() => {
|
|
497
|
+
terminalTitleRuntime.frame = (terminalTitleRuntime.frame + 1) % TITLE_SPINNER_FRAMES.length;
|
|
498
|
+
emitTerminalTitle();
|
|
499
|
+
}, TITLE_SPINNER_INTERVAL_MS);
|
|
500
|
+
// Never keep the event loop alive for a cosmetic animation.
|
|
501
|
+
terminalTitleRuntime.timer.unref?.();
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Reflect the agent run state in the terminal title's separator: `working`
|
|
506
|
+
* animates spinner frames in the separator slot, `idle` shows `>` (your turn),
|
|
507
|
+
* `attention` shows `!` (agent blocked on you). Gated off by `tui.titleState`.
|
|
508
|
+
*/
|
|
509
|
+
export function setTerminalTitleState(state: TerminalTitleState): void {
|
|
510
|
+
terminalTitleRuntime.state = state;
|
|
511
|
+
if (state === "working" && terminalTitleRuntime.enabled) startTerminalTitleSpinner();
|
|
512
|
+
else stopTerminalTitleSpinner();
|
|
513
|
+
emitTerminalTitle();
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/** Enable/disable the run-state separator (driven by the `tui.titleState` setting). */
|
|
517
|
+
export function setTerminalTitleStateEnabled(enabled: boolean): void {
|
|
518
|
+
terminalTitleRuntime.enabled = enabled;
|
|
519
|
+
if (enabled && terminalTitleRuntime.state === "working") startTerminalTitleSpinner();
|
|
520
|
+
else stopTerminalTitleSpinner();
|
|
521
|
+
emitTerminalTitle();
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/** Stop the spinner timer; call on session/UI teardown. */
|
|
525
|
+
export function disposeTerminalTitleState(): void {
|
|
526
|
+
stopTerminalTitleSpinner();
|
|
391
527
|
}
|
|
392
528
|
|
|
393
529
|
/**
|