@oh-my-pi/pi-coding-agent 15.10.0 → 15.10.2
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 +142 -1
- package/dist/types/cli/dry-balance-cli.d.ts +15 -1
- package/dist/types/cli/startup-cwd.d.ts +2 -0
- package/dist/types/commands/launch.d.ts +3 -0
- package/dist/types/commit/analysis/conventional.d.ts +2 -2
- package/dist/types/commit/analysis/summary.d.ts +2 -2
- package/dist/types/commit/changelog/generate.d.ts +2 -2
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/index.d.ts +3 -3
- package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
- package/dist/types/commit/model-selection.d.ts +10 -4
- package/dist/types/config/api-key-resolver.d.ts +34 -0
- package/dist/types/config/keybindings.d.ts +2 -2
- package/dist/types/config/model-provider-priority.d.ts +1 -0
- package/dist/types/config/model-registry.d.ts +17 -1
- package/dist/types/config/model-resolver.d.ts +4 -1
- package/dist/types/config/settings-schema.d.ts +9 -0
- package/dist/types/config/settings.d.ts +7 -2
- package/dist/types/dap/config.d.ts +14 -1
- package/dist/types/dap/types.d.ts +10 -0
- package/dist/types/debug/report-bundle.d.ts +3 -0
- package/dist/types/edit/file-snapshot-store.d.ts +18 -10
- package/dist/types/eval/py/__tests__/prelude.test.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +4 -1
- package/dist/types/lsp/client.d.ts +10 -0
- package/dist/types/lsp/utils.d.ts +3 -2
- package/dist/types/main.d.ts +3 -9
- package/dist/types/mcp/tool-bridge.d.ts +2 -0
- package/dist/types/modes/components/chat-block.d.ts +64 -0
- package/dist/types/modes/components/custom-editor.d.ts +4 -1
- package/dist/types/modes/components/overlay-box.d.ts +17 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
- package/dist/types/modes/components/plan-toc.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/status-line.d.ts +2 -0
- package/dist/types/modes/components/transcript-container.d.ts +11 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +17 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
- package/dist/types/modes/controllers/input-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
- package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +16 -5
- package/dist/types/modes/magic-keywords.d.ts +1 -1
- package/dist/types/modes/markdown-prose.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +21 -5
- package/dist/types/modes/utils/copy-targets.d.ts +21 -1
- package/dist/types/modes/workflow.d.ts +3 -3
- package/dist/types/plan-mode/approved-plan.d.ts +27 -8
- package/dist/types/plan-mode/plan-protection.d.ts +4 -4
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/session/messages.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +8 -3
- package/dist/types/slash-commands/types.d.ts +4 -6
- package/dist/types/task/executor.d.ts +17 -0
- package/dist/types/task/index.d.ts +1 -0
- package/dist/types/task/render.d.ts +3 -2
- package/dist/types/tools/archive-reader.d.ts +5 -0
- package/dist/types/tools/ast-edit.d.ts +3 -0
- package/dist/types/tools/ast-grep.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/eval.d.ts +8 -0
- package/dist/types/tools/find.d.ts +8 -4
- package/dist/types/tools/gh-cache-invalidation.d.ts +6 -0
- package/dist/types/tools/github-cache.d.ts +12 -0
- package/dist/types/tools/grouped-file-output.d.ts +95 -12
- package/dist/types/tools/memory-render.d.ts +4 -1
- package/dist/types/tools/path-utils.d.ts +8 -0
- package/dist/types/tools/plan-mode-guard.d.ts +8 -9
- package/dist/types/tools/render-utils.d.ts +5 -9
- package/dist/types/tools/search.d.ts +6 -2
- package/dist/types/tools/sqlite-reader.d.ts +1 -0
- package/dist/types/tools/todo.d.ts +3 -2
- package/dist/types/tools/write.d.ts +3 -0
- package/dist/types/tools/yield.d.ts +8 -0
- package/dist/types/tui/output-block.d.ts +16 -4
- package/dist/types/tui/status-line.d.ts +3 -0
- package/dist/types/utils/enhanced-paste.d.ts +20 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -1
- package/package.json +9 -9
- package/src/auto-thinking/classifier.ts +5 -1
- package/src/cli/args.ts +3 -1
- package/src/cli/dry-balance-cli.ts +54 -21
- package/src/cli/gallery-cli.ts +4 -1
- package/src/cli/gallery-fixtures/misc.ts +29 -0
- package/src/cli/startup-cwd.ts +68 -0
- package/src/commands/launch.ts +3 -0
- package/src/commit/analysis/conventional.ts +2 -2
- package/src/commit/analysis/summary.ts +2 -2
- package/src/commit/changelog/generate.ts +2 -2
- package/src/commit/changelog/index.ts +2 -2
- package/src/commit/map-reduce/index.ts +3 -3
- package/src/commit/map-reduce/map-phase.ts +2 -2
- package/src/commit/map-reduce/reduce-phase.ts +2 -2
- package/src/commit/model-selection.ts +36 -11
- package/src/commit/pipeline.ts +4 -4
- package/src/config/api-key-resolver.ts +58 -0
- package/src/config/model-provider-priority.ts +55 -0
- package/src/config/model-registry.ts +29 -24
- package/src/config/model-resolver.ts +39 -7
- package/src/config/settings-schema.ts +10 -0
- package/src/config/settings.ts +106 -43
- package/src/dap/config.ts +41 -2
- package/src/dap/defaults.json +1 -0
- package/src/dap/session.ts +1 -0
- package/src/dap/types.ts +10 -0
- package/src/debug/index.ts +47 -53
- package/src/debug/raw-sse-buffer.ts +7 -4
- package/src/debug/report-bundle.ts +9 -0
- package/src/edit/file-snapshot-store.ts +33 -1
- package/src/edit/hashline/filesystem.ts +2 -1
- package/src/edit/renderer.ts +82 -78
- package/src/eval/__tests__/llm-bridge.test.ts +110 -31
- package/src/eval/js/context-manager.ts +32 -15
- package/src/eval/llm-bridge.ts +22 -6
- package/src/eval/py/__tests__/prelude.test.ts +19 -0
- package/src/eval/py/executor.ts +23 -11
- package/src/eval/py/prelude.py +1 -1
- package/src/extensibility/extensions/types.ts +10 -1
- package/src/goals/tools/goal-tool.ts +36 -26
- package/src/internal-urls/docs-index.generated.ts +8 -8
- package/src/lsp/client.ts +23 -11
- package/src/lsp/config.ts +11 -1
- package/src/lsp/index.ts +61 -9
- package/src/lsp/utils.ts +3 -2
- package/src/main.ts +100 -72
- package/src/mcp/tool-bridge.ts +2 -0
- package/src/memories/index.ts +14 -7
- package/src/mnemopi/backend.ts +5 -1
- package/src/modes/acp/acp-agent.ts +33 -26
- package/src/modes/components/assistant-message.ts +2 -9
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/copy-selector.ts +1 -44
- package/src/modes/components/custom-editor.ts +164 -109
- package/src/modes/components/custom-message.ts +1 -3
- package/src/modes/components/execution-shared.ts +1 -2
- package/src/modes/components/hook-message.ts +1 -3
- package/src/modes/components/model-selector.ts +59 -13
- package/src/modes/components/oauth-selector.ts +33 -7
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +799 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/read-tool-group.ts +20 -4
- package/src/modes/components/skill-message.ts +0 -1
- package/src/modes/components/status-line.ts +19 -4
- package/src/modes/components/tips.txt +2 -1
- package/src/modes/components/todo-reminder.ts +0 -2
- package/src/modes/components/tool-execution.ts +68 -88
- package/src/modes/components/transcript-container.ts +84 -24
- package/src/modes/components/user-message.ts +2 -3
- package/src/modes/controllers/command-controller-shared.ts +7 -6
- package/src/modes/controllers/command-controller.ts +57 -55
- package/src/modes/controllers/event-controller.ts +67 -40
- package/src/modes/controllers/extension-ui-controller.ts +10 -73
- package/src/modes/controllers/input-controller.ts +170 -126
- package/src/modes/controllers/mcp-command-controller.ts +69 -60
- package/src/modes/controllers/selector-controller.ts +23 -25
- package/src/modes/controllers/streaming-reveal.ts +212 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/interactive-mode.ts +274 -112
- package/src/modes/magic-keywords.ts +1 -1
- package/src/modes/markdown-prose.ts +1 -1
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/shimmer.ts +20 -9
- package/src/modes/theme/theme-schema.json +1 -1
- package/src/modes/theme/theme.ts +8 -4
- package/src/modes/types.ts +21 -7
- package/src/modes/utils/copy-targets.ts +133 -27
- package/src/modes/utils/ui-helpers.ts +44 -46
- package/src/modes/workflow.ts +10 -10
- package/src/plan-mode/approved-plan.ts +66 -43
- package/src/plan-mode/plan-protection.ts +4 -4
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/plan-mode-active.md +67 -58
- package/src/prompts/system/plan-mode-approved.md +1 -1
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/bash.md +9 -0
- package/src/prompts/tools/browser.md +1 -1
- package/src/prompts/tools/eval.md +2 -1
- package/src/prompts/tools/read.md +2 -2
- package/src/sdk.ts +37 -46
- package/src/session/agent-session.ts +119 -18
- package/src/session/auth-storage.ts +2 -0
- package/src/session/messages.ts +26 -0
- package/src/session/session-manager.ts +109 -28
- package/src/slash-commands/builtin-registry.ts +36 -9
- package/src/slash-commands/types.ts +4 -6
- package/src/task/executor.ts +76 -38
- package/src/task/index.ts +4 -0
- package/src/task/render.ts +211 -147
- package/src/tools/archive-reader.ts +64 -0
- package/src/tools/ask.ts +119 -164
- package/src/tools/ast-edit.ts +98 -71
- package/src/tools/ast-grep.ts +37 -43
- package/src/tools/bash.ts +57 -6
- package/src/tools/browser/tab-supervisor.ts +13 -1
- package/src/tools/browser/tab-worker.ts +33 -4
- package/src/tools/debug.ts +20 -8
- package/src/tools/eval.ts +13 -2
- package/src/tools/fetch.ts +297 -7
- package/src/tools/find.ts +51 -30
- package/src/tools/gh-cache-invalidation.ts +200 -0
- package/src/tools/gh-renderer.ts +81 -42
- package/src/tools/github-cache.ts +25 -0
- package/src/tools/grouped-file-output.ts +272 -48
- package/src/tools/image-gen.ts +150 -103
- package/src/tools/inspect-image-renderer.ts +63 -41
- package/src/tools/inspect-image.ts +10 -3
- package/src/tools/job.ts +3 -4
- package/src/tools/memory-render.ts +4 -1
- package/src/tools/path-utils.ts +28 -2
- package/src/tools/plan-mode-guard.ts +66 -39
- package/src/tools/read.ts +48 -28
- package/src/tools/render-utils.ts +21 -37
- package/src/tools/resolve.ts +14 -0
- package/src/tools/search-tool-bm25.ts +36 -23
- package/src/tools/search.ts +118 -81
- package/src/tools/sqlite-reader.ts +9 -12
- package/src/tools/todo.ts +118 -52
- package/src/tools/write.ts +83 -64
- package/src/tools/yield.ts +10 -1
- package/src/tui/output-block.ts +60 -13
- package/src/tui/status-line.ts +5 -1
- package/src/utils/commit-message-generator.ts +11 -3
- package/src/utils/enhanced-paste.ts +230 -0
- package/src/utils/title-generator.ts +2 -1
- package/src/web/search/providers/anthropic.ts +25 -19
- package/src/web/search/providers/codex.ts +37 -8
- package/src/web/search/providers/exa.ts +11 -3
- package/src/web/search/providers/kimi.ts +28 -17
- package/src/web/search/providers/parallel.ts +35 -24
- package/src/web/search/providers/synthetic.ts +8 -6
- package/src/web/search/providers/tavily.ts +9 -8
- package/src/web/search/providers/zai.ts +8 -6
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect cache-mutating `gh` subcommands inside a bash invocation and drop
|
|
3
|
+
* the matching `github-cache` rows so a subsequent `issue://<n>` or
|
|
4
|
+
* `pr://<n>` read sees the post-mutation state instead of the stale
|
|
5
|
+
* pre-mutation snapshot.
|
|
6
|
+
*
|
|
7
|
+
* Triggered before the bash command runs: on success the cache is now
|
|
8
|
+
* empty and the next read fetches fresh; on failure the worst case is one
|
|
9
|
+
* extra `gh` round-trip on the following read. That cost is bounded and
|
|
10
|
+
* eliminates the much-worse "issue shows OPEN for up to softTtlSec after
|
|
11
|
+
* `gh issue close`" failure mode reported by users.
|
|
12
|
+
*
|
|
13
|
+
* Detector scope: ops that change visible issue/PR state — `close`,
|
|
14
|
+
* `reopen`, `merge`, `delete`, `ready`, `lock`, `unlock`, `pin`, `unpin`,
|
|
15
|
+
* `transfer`, plus the comment/review/edit ops that change the rendered
|
|
16
|
+
* body. We deliberately over-invalidate (e.g. all matching rows for the
|
|
17
|
+
* number, all auth_keys) because the upside of staleness elimination
|
|
18
|
+
* dwarfs the cost of one cache miss.
|
|
19
|
+
*/
|
|
20
|
+
import { invalidateAllForNumber } from "./github-cache";
|
|
21
|
+
|
|
22
|
+
const PR_URL_PATTERN = /^https:\/\/github\.com\/([^/\s]+\/[^/\s]+)\/pull\/(\d+)(?:[/?#].*)?$/i;
|
|
23
|
+
const ISSUE_URL_PATTERN = /^https:\/\/github\.com\/([^/\s]+\/[^/\s]+)\/issues\/(\d+)(?:[/?#].*)?$/i;
|
|
24
|
+
|
|
25
|
+
/** Subcommands that mutate the rendered issue/PR view in any meaningful way. */
|
|
26
|
+
const MUTATING_ISSUE_SUBCMDS: Record<string, true> = {
|
|
27
|
+
close: true,
|
|
28
|
+
reopen: true,
|
|
29
|
+
delete: true,
|
|
30
|
+
edit: true,
|
|
31
|
+
comment: true,
|
|
32
|
+
lock: true,
|
|
33
|
+
unlock: true,
|
|
34
|
+
pin: true,
|
|
35
|
+
unpin: true,
|
|
36
|
+
transfer: true,
|
|
37
|
+
develop: true,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const MUTATING_PR_SUBCMDS: Record<string, true> = {
|
|
41
|
+
close: true,
|
|
42
|
+
reopen: true,
|
|
43
|
+
merge: true,
|
|
44
|
+
ready: true,
|
|
45
|
+
edit: true,
|
|
46
|
+
comment: true,
|
|
47
|
+
review: true,
|
|
48
|
+
lock: true,
|
|
49
|
+
unlock: true,
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Walk a single shell command's token stream looking for a top-level
|
|
53
|
+
* `gh (issue|pr) <subcmd> <id-or-url>` invocation and return the
|
|
54
|
+
* invalidation key when one is found. Returns `null` for non-matching
|
|
55
|
+
* commands so the caller can iterate cheaply.
|
|
56
|
+
*/
|
|
57
|
+
function detectGhMutation(tokens: readonly string[]): { number: number; repo?: string } | null {
|
|
58
|
+
const ghIdx = tokens.indexOf("gh");
|
|
59
|
+
if (ghIdx === -1) return null;
|
|
60
|
+
const subject = tokens[ghIdx + 1];
|
|
61
|
+
if (subject !== "issue" && subject !== "pr") return null;
|
|
62
|
+
const subcmd = tokens[ghIdx + 2];
|
|
63
|
+
if (!subcmd) return null;
|
|
64
|
+
const expected = subject === "issue" ? MUTATING_ISSUE_SUBCMDS : MUTATING_PR_SUBCMDS;
|
|
65
|
+
if (!expected[subcmd]) return null;
|
|
66
|
+
|
|
67
|
+
let repo: string | undefined;
|
|
68
|
+
// First pass: scan for --repo so it wins regardless of position relative
|
|
69
|
+
// to the issue/PR identifier (gh accepts the flag both before and after
|
|
70
|
+
// the positional argument).
|
|
71
|
+
for (let i = ghIdx + 3; i < tokens.length; i++) {
|
|
72
|
+
const token = tokens[i];
|
|
73
|
+
if (token === "-R" || token === "--repo") {
|
|
74
|
+
const next = tokens[i + 1];
|
|
75
|
+
if (next) repo = next;
|
|
76
|
+
i++;
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
if (token.startsWith("--repo=")) {
|
|
80
|
+
repo = token.slice("--repo=".length);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (let i = ghIdx + 3; i < tokens.length; i++) {
|
|
84
|
+
const token = tokens[i];
|
|
85
|
+
if (token === "-R" || token === "--repo") {
|
|
86
|
+
i++;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (token.startsWith("-")) continue;
|
|
90
|
+
const direct = /^\d+$/.test(token) ? Number(token) : undefined;
|
|
91
|
+
if (direct !== undefined && Number.isSafeInteger(direct) && direct > 0) {
|
|
92
|
+
return repo !== undefined ? { number: direct, repo } : { number: direct };
|
|
93
|
+
}
|
|
94
|
+
const urlMatch = (subject === "pr" ? PR_URL_PATTERN : ISSUE_URL_PATTERN).exec(token);
|
|
95
|
+
if (urlMatch) {
|
|
96
|
+
const num = Number(urlMatch[2]);
|
|
97
|
+
if (Number.isSafeInteger(num) && num > 0) {
|
|
98
|
+
// URL carries its own repo and wins over a stray --repo flag.
|
|
99
|
+
return { number: num, repo: urlMatch[1] };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Conservative tokenizer that splits a bash command into individual word
|
|
108
|
+
* tokens. Handles single/double-quoted strings, backslash escapes, and
|
|
109
|
+
* standard operators (`;`, `&&`, `||`, `|`, `&`, newlines) as token
|
|
110
|
+
* boundaries that emit a sentinel `";"` so the caller treats the segments
|
|
111
|
+
* as independent command sequences. We do not attempt full POSIX shell
|
|
112
|
+
* parsing — heredocs, command substitution, and arithmetic expansion are
|
|
113
|
+
* out of scope; the detector simply falls through when it cannot find a
|
|
114
|
+
* clean `gh issue|pr <subcmd>` triple.
|
|
115
|
+
*/
|
|
116
|
+
function tokenize(command: string): string[][] {
|
|
117
|
+
const segments: string[][] = [];
|
|
118
|
+
let current: string[] = [];
|
|
119
|
+
let buffer = "";
|
|
120
|
+
let inSingle = false;
|
|
121
|
+
let inDouble = false;
|
|
122
|
+
const pushBuffer = () => {
|
|
123
|
+
if (buffer.length > 0) {
|
|
124
|
+
current.push(buffer);
|
|
125
|
+
buffer = "";
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const pushSegment = () => {
|
|
129
|
+
pushBuffer();
|
|
130
|
+
if (current.length > 0) segments.push(current);
|
|
131
|
+
current = [];
|
|
132
|
+
};
|
|
133
|
+
for (let i = 0; i < command.length; i++) {
|
|
134
|
+
const ch = command[i];
|
|
135
|
+
if (inSingle) {
|
|
136
|
+
if (ch === "'") {
|
|
137
|
+
inSingle = false;
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
buffer += ch;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (inDouble) {
|
|
144
|
+
if (ch === "\\" && i + 1 < command.length) {
|
|
145
|
+
const next = command[i + 1];
|
|
146
|
+
if (next === '"' || next === "\\" || next === "$" || next === "`") {
|
|
147
|
+
buffer += next;
|
|
148
|
+
i++;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
if (ch === '"') {
|
|
153
|
+
inDouble = false;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
buffer += ch;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (ch === "'") {
|
|
160
|
+
inSingle = true;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (ch === '"') {
|
|
164
|
+
inDouble = true;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (ch === "\\" && i + 1 < command.length) {
|
|
168
|
+
buffer += command[i + 1];
|
|
169
|
+
i++;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (ch === " " || ch === "\t") {
|
|
173
|
+
pushBuffer();
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (ch === "\n" || ch === ";" || ch === "&" || ch === "|" || ch === "(" || ch === ")") {
|
|
177
|
+
pushSegment();
|
|
178
|
+
// `&&`, `||` already collapsed by the segment break above.
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
buffer += ch;
|
|
182
|
+
}
|
|
183
|
+
pushSegment();
|
|
184
|
+
return segments;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Drop `github-cache` rows for any `gh issue|pr <mutating-subcmd>` call
|
|
189
|
+
* embedded in `command`. Safe to invoke unconditionally; no-op when the
|
|
190
|
+
* command does not touch GitHub state.
|
|
191
|
+
*/
|
|
192
|
+
export function invalidateGithubCacheForBashCommand(command: string): void {
|
|
193
|
+
if (!command?.includes("gh")) return;
|
|
194
|
+
const segments = tokenize(command);
|
|
195
|
+
for (const segment of segments) {
|
|
196
|
+
const hit = detectGhMutation(segment);
|
|
197
|
+
if (!hit) continue;
|
|
198
|
+
invalidateAllForNumber(hit.number, hit.repo);
|
|
199
|
+
}
|
|
200
|
+
}
|
package/src/tools/gh-renderer.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { type Component, padding, Text,
|
|
1
|
+
import { type Component, padding, Text, visibleWidth } from "@oh-my-pi/pi-tui";
|
|
2
2
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
3
3
|
import type { Theme, ThemeColor } from "../modes/theme/theme";
|
|
4
|
-
import { renderStatusLine } from "../tui";
|
|
4
|
+
import { framedBlock, renderStatusLine } from "../tui";
|
|
5
5
|
import type {
|
|
6
6
|
GhRunWatchFailedLogDetails,
|
|
7
7
|
GhRunWatchJobDetails,
|
|
@@ -239,7 +239,7 @@ function renderFailedLogs(
|
|
|
239
239
|
return [];
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
-
const lines = [
|
|
242
|
+
const lines: string[] = [];
|
|
243
243
|
for (const entry of failedLogs) {
|
|
244
244
|
const context = entry.workflowName ? `${entry.workflowName} #${entry.runId}` : `run #${entry.runId}`;
|
|
245
245
|
lines.push(
|
|
@@ -268,36 +268,45 @@ function renderFailedLogs(
|
|
|
268
268
|
return lines;
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
function
|
|
271
|
+
function buildWatchSections(
|
|
272
272
|
watch: GhRunWatchViewDetails,
|
|
273
273
|
theme: Theme,
|
|
274
274
|
options: RenderResultOptions,
|
|
275
275
|
width: number,
|
|
276
|
-
): string[] {
|
|
277
|
-
const
|
|
276
|
+
): Array<{ label?: string; lines: string[] }> {
|
|
277
|
+
const main: string[] = [];
|
|
278
278
|
|
|
279
279
|
if (watch.note) {
|
|
280
|
-
|
|
280
|
+
main.push(theme.fg("dim", replaceTabs(watch.note)));
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
if (watch.mode === "run" && watch.run) {
|
|
284
|
-
|
|
284
|
+
main.push(...renderRunBlock(watch.run, width, theme));
|
|
285
285
|
} else if (watch.mode === "commit") {
|
|
286
286
|
const runs = watch.runs ?? [];
|
|
287
287
|
if (runs.length === 0) {
|
|
288
|
-
|
|
288
|
+
main.push(theme.fg("dim", "waiting for workflow runs..."));
|
|
289
289
|
} else {
|
|
290
290
|
runs.forEach((run, index) => {
|
|
291
291
|
if (index > 0) {
|
|
292
|
-
|
|
292
|
+
main.push("");
|
|
293
293
|
}
|
|
294
|
-
|
|
294
|
+
main.push(...renderRunBlock(run, width, theme));
|
|
295
295
|
});
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
-
lines
|
|
300
|
-
|
|
299
|
+
const sections: Array<{ label?: string; lines: string[] }> = [];
|
|
300
|
+
if (main.length > 0) {
|
|
301
|
+
sections.push({ lines: main });
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const failed = renderFailedLogs(watch.failedLogs ?? [], width, theme, options.expanded);
|
|
305
|
+
if (failed.length > 0) {
|
|
306
|
+
sections.push({ label: "failed logs", lines: failed });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return sections;
|
|
301
310
|
}
|
|
302
311
|
|
|
303
312
|
function extractText(content: Array<{ type: string; text?: string }>): string {
|
|
@@ -335,29 +344,44 @@ function renderFallbackComponent(
|
|
|
335
344
|
}
|
|
336
345
|
|
|
337
346
|
const allLines = replaceTabs(text).split("\n");
|
|
347
|
+
while (allLines.length > 0 && allLines[0].trim() === "") allLines.shift();
|
|
348
|
+
while (allLines.length > 0 && allLines[allLines.length - 1].trim() === "") allLines.pop();
|
|
349
|
+
|
|
350
|
+
// Trivial one-line *success* result: a clean status line beats an almost-empty box.
|
|
351
|
+
// Errors always frame so the message reads as a structured block, never a raw red wrap.
|
|
352
|
+
if (allLines.length <= 1 && !isError) {
|
|
353
|
+
const body = allLines[0];
|
|
354
|
+
if (!body) return new Text(header, 0, 0);
|
|
355
|
+
const colored = isError ? theme.fg("error", body) : theme.fg("toolOutput", body);
|
|
356
|
+
return new Text(`${header}\n${colored}`, 0, 0);
|
|
357
|
+
}
|
|
338
358
|
|
|
339
|
-
return {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
359
|
+
return framedBlock(theme, width => {
|
|
360
|
+
const lineWidth = Math.max(1, (width || FALLBACK_WIDTH) - 3);
|
|
361
|
+
const expanded = options.expanded;
|
|
362
|
+
const limit = expanded ? allLines.length : Math.min(allLines.length, PREVIEW_LIMITS.OUTPUT_EXPANDED);
|
|
363
|
+
const visible = allLines.slice(0, limit);
|
|
364
|
+
const remaining = allLines.length - visible.length;
|
|
365
|
+
|
|
366
|
+
const out: string[] = [];
|
|
367
|
+
for (const line of visible) {
|
|
368
|
+
const colored = isError ? theme.fg("error", line) : theme.fg("toolOutput", line);
|
|
369
|
+
out.push(truncateVisualWidth(colored, lineWidth));
|
|
370
|
+
}
|
|
371
|
+
if (!expanded && remaining > 0) {
|
|
372
|
+
const hint = formatExpandHint(theme, expanded, true);
|
|
373
|
+
const more = `${formatMoreItems(remaining, "line")}${hint ? ` ${hint}` : ""}`;
|
|
374
|
+
out.push(theme.fg("dim", more));
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
header,
|
|
378
|
+
sections: out.length > 0 ? [{ lines: out }] : [],
|
|
379
|
+
state: isError ? "error" : "success",
|
|
380
|
+
borderColor: isError ? "error" : "borderMuted",
|
|
381
|
+
applyBg: false,
|
|
382
|
+
width,
|
|
383
|
+
};
|
|
384
|
+
});
|
|
361
385
|
}
|
|
362
386
|
|
|
363
387
|
function renderWatchCall(args: GithubToolRenderArgs, options: RenderResultOptions, theme: Theme): Component {
|
|
@@ -380,7 +404,7 @@ function renderWatchCall(args: GithubToolRenderArgs, options: RenderResultOption
|
|
|
380
404
|
}
|
|
381
405
|
|
|
382
406
|
const header = `${icon} ${titleText} ${metaText}`;
|
|
383
|
-
const wait = theme.fg("dim", "
|
|
407
|
+
const wait = theme.fg("dim", "waiting for workflow data...");
|
|
384
408
|
return new Text(`${header}\n${wait}`, 0, 0);
|
|
385
409
|
}
|
|
386
410
|
|
|
@@ -412,13 +436,28 @@ export const githubToolRenderer = {
|
|
|
412
436
|
): Component {
|
|
413
437
|
const watch = result.details?.watch;
|
|
414
438
|
if (watch) {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
439
|
+
const isError = result.isError === true;
|
|
440
|
+
const header = renderStatusLine(
|
|
441
|
+
{
|
|
442
|
+
icon: isError ? "error" : "success",
|
|
443
|
+
title: "GitHub Run Watch",
|
|
444
|
+
titleColor: isError ? "error" : "accent",
|
|
445
|
+
meta: [getWatchHeader(watch)],
|
|
419
446
|
},
|
|
420
|
-
|
|
421
|
-
|
|
447
|
+
uiTheme,
|
|
448
|
+
);
|
|
449
|
+
return framedBlock(uiTheme, width => {
|
|
450
|
+
const innerWidth = Math.max(1, (width || FALLBACK_WIDTH) - 3);
|
|
451
|
+
const sections = buildWatchSections(watch, uiTheme, options, innerWidth);
|
|
452
|
+
return {
|
|
453
|
+
header,
|
|
454
|
+
sections,
|
|
455
|
+
state: isError ? "error" : "success",
|
|
456
|
+
borderColor: isError ? "error" : "borderMuted",
|
|
457
|
+
applyBg: false,
|
|
458
|
+
width,
|
|
459
|
+
};
|
|
460
|
+
});
|
|
422
461
|
}
|
|
423
462
|
|
|
424
463
|
return renderFallbackComponent(result, options, uiTheme, args ?? {});
|
|
@@ -316,6 +316,31 @@ export function invalidate(
|
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
+
/**
|
|
320
|
+
* Drop every cached row for a given issue/PR number, regardless of repo,
|
|
321
|
+
* auth key, include_comments flag, or row kind ({@link CacheKind}). Best-effort:
|
|
322
|
+
* swallows DB failures the same way {@link invalidate} does.
|
|
323
|
+
*
|
|
324
|
+
* Used by the bash-side detector that reacts to `gh issue close` / `gh pr merge`
|
|
325
|
+
* style mutations. Repo + auth-key narrowing is intentionally skipped because
|
|
326
|
+
* the bash command often does not name the repo (defaults to cwd's `gh`
|
|
327
|
+
* config) and resolving the *current* repo from `cwd` for every bash call would
|
|
328
|
+
* be far more expensive than a write-amplified DELETE.
|
|
329
|
+
*/
|
|
330
|
+
export function invalidateAllForNumber(number: number, repo?: string): void {
|
|
331
|
+
const db = openDb();
|
|
332
|
+
if (!db) return;
|
|
333
|
+
try {
|
|
334
|
+
if (repo === undefined) {
|
|
335
|
+
db.prepare("DELETE FROM github_view_cache WHERE number = ?").run(number);
|
|
336
|
+
} else {
|
|
337
|
+
db.prepare("DELETE FROM github_view_cache WHERE number = ? AND repo = ?").run(number, normalizeRepo(repo));
|
|
338
|
+
}
|
|
339
|
+
} catch (err) {
|
|
340
|
+
logger.debug("github cache: invalidateAllForNumber failed", { err: String(err) });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
319
344
|
/** Drop every cached row. Test helper. */
|
|
320
345
|
export function clearAll(): void {
|
|
321
346
|
const db = openDb();
|