@oh-my-pi/pi-coding-agent 12.8.2 → 12.10.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 +57 -0
- package/package.json +7 -7
- package/src/capability/rule.ts +173 -5
- package/src/cli/args.ts +3 -0
- package/src/cli/update-cli.ts +1 -66
- package/src/commands/launch.ts +3 -0
- package/src/config/model-registry.ts +300 -21
- package/src/config/model-resolver.ts +4 -4
- package/src/config/settings-schema.ts +12 -0
- package/src/discovery/agents.ts +175 -12
- package/src/discovery/builtin.ts +3 -13
- package/src/discovery/cline.ts +4 -45
- package/src/discovery/cursor.ts +2 -29
- package/src/discovery/helpers.ts +43 -0
- package/src/discovery/index.ts +1 -0
- package/src/discovery/opencode.ts +394 -0
- package/src/discovery/windsurf.ts +5 -44
- package/src/export/ttsr.ts +324 -54
- package/src/extensibility/custom-tools/wrapper.ts +1 -11
- package/src/internal-urls/index.ts +4 -2
- package/src/internal-urls/memory-protocol.ts +133 -0
- package/src/internal-urls/router.ts +4 -2
- package/src/internal-urls/skill-protocol.ts +1 -1
- package/src/internal-urls/types.ts +6 -2
- package/src/main.ts +5 -0
- package/src/memories/index.ts +6 -13
- package/src/modes/components/settings-defs.ts +6 -0
- package/src/modes/components/status-line/segments.ts +3 -2
- package/src/modes/rpc/rpc-client.ts +16 -0
- package/src/prompts/memories/consolidation.md +1 -1
- package/src/prompts/memories/read_path.md +4 -4
- package/src/prompts/memories/stage_one_input.md +1 -2
- package/src/prompts/tools/bash.md +10 -23
- package/src/prompts/tools/read.md +2 -0
- package/src/sdk.ts +25 -10
- package/src/session/agent-session.ts +252 -44
- package/src/session/session-manager.ts +79 -36
- package/src/tools/bash-skill-urls.ts +177 -0
- package/src/tools/bash.ts +7 -1
- package/src/tools/fetch.ts +6 -2
- package/src/tools/index.ts +2 -2
- package/src/tools/output-meta.ts +49 -42
- package/src/tools/read.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,63 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [12.10.0] - 2026-02-18
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
- Changed keyless provider auth sentinel from `"<no-auth>"` to `kNoAuth` (`"N/A"`) for `ModelRegistry.getApiKey()` and `ModelRegistry.getApiKeyForProvider()`
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Added `--no-rules` CLI flag to disable rules discovery and loading
|
|
14
|
+
- Added `sessionDir` option to RpcClientOptions for specifying agent session directory
|
|
15
|
+
- Added `Symbol.dispose` method to RpcClient for resource cleanup support
|
|
16
|
+
- Added `rules` option to CreateAgentSessionOptions for explicit rule configuration
|
|
17
|
+
- Added `sessionDir` option to RpcClientOptions for specifying agent session directory
|
|
18
|
+
- Added `Symbol.dispose` method to RpcClient for resource cleanup support
|
|
19
|
+
- Added `condition` and `scope` fields to rule frontmatter for advanced TTSR matching and stream filtering
|
|
20
|
+
- Added `ttsr.interruptMode` setting to control when TTSR rules interrupt mid-stream vs inject warnings after completion
|
|
21
|
+
- Added support for loading rules, prompts, commands, context files (AGENTS.md), and system prompts (SYSTEM.md) from ~/.agent/ directory (with fallback to ~/.agents/)
|
|
22
|
+
- Added scoped stream buffering for TTSR matching to isolate prose, thinking, and tool argument streams
|
|
23
|
+
- Added file-path-aware TTSR scope matching for tool calls with glob patterns (e.g., `tool:edit(*.ts)`)
|
|
24
|
+
- Added legacy field support: `ttsr_trigger` and `ttsrTrigger` are accepted as fallback for `condition`
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Changed TTSR injection tracking to record all turns where rules were injected (instead of only the last turn) to support repeat-after-gap mode across resumed sessions
|
|
29
|
+
- Changed TTSR injection messages to use custom message type with metadata instead of synthetic user messages for better session tracking
|
|
30
|
+
- Changed TTSR rule injection to persist injected rule names in session state for restoration when resuming sessions
|
|
31
|
+
- Changed model discovery to automatically discover built-in provider models (Anthropic, OpenAI, Groq, Cerebras, Xai, Mistral, OpenCode, OpenRouter, Vercel AI Gateway, Kimi Code, GitHub Copilot, Google, Cursor, Google Antigravity, Google Gemini CLI, OpenAI Codex) when credentials are configured
|
|
32
|
+
- Changed `getModel()` and `getModels()` imports to `getBundledModel()` and `getBundledModels()` across test utilities
|
|
33
|
+
- Changed TTSR rule matching from single `ttsrTrigger` regex to multiple `condition` patterns with scope filtering
|
|
34
|
+
- Changed TTSR buffer management to use per-stream-key buffers instead of a single global buffer
|
|
35
|
+
- Changed rule discovery to use unified `buildRuleFromMarkdown` helper across all providers (builtin, cline, cursor, windsurf, agents)
|
|
36
|
+
- Changed TTSR injection to defer warnings until stream completion when `interruptMode` is not `always`
|
|
37
|
+
- Changed `TtsrManager.addRule()` to return boolean indicating successful registration instead of void
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- Fixed TTSR repeat-after-gap mode to correctly calculate gaps when rules are restored from previous sessions
|
|
42
|
+
- Fixed TTSR matching to respect tool-specific scope filters, preventing cross-tool rule contamination
|
|
43
|
+
- Fixed path normalization in TTSR glob matching to handle both relative and absolute path variants
|
|
44
|
+
|
|
45
|
+
## [12.9.0] - 2026-02-17
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- Added OpenCode discovery provider to load configuration from ~/.config/opencode/ and .opencode/ directories
|
|
50
|
+
- Added support for loading MCP servers from opencode.json mcp key
|
|
51
|
+
- Added support for loading skills from ~/.config/opencode/skills/ and .opencode/skills/
|
|
52
|
+
- Added support for loading slash commands from ~/.config/opencode/commands/ and .opencode/commands/
|
|
53
|
+
- Added support for loading extension modules (plugins) from ~/.config/opencode/plugins/ and .opencode/plugins/
|
|
54
|
+
- Added support for loading context files (AGENTS.md) from ~/.config/opencode/
|
|
55
|
+
- Added support for loading settings from opencode.json configuration files
|
|
56
|
+
|
|
57
|
+
### Changed
|
|
58
|
+
|
|
59
|
+
- Improved path display in status line to strip both `/work/` and `~/Projects/` prefixes when abbreviating paths
|
|
60
|
+
- Refactored session directory naming to use single-dash format for home-relative paths and double-dash format for absolute paths, with automatic migration of legacy session directories on first access
|
|
61
|
+
|
|
5
62
|
## [12.8.2] - 2026-02-17
|
|
6
63
|
### Changed
|
|
7
64
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.10.0",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -84,12 +84,12 @@
|
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
86
|
"@mozilla/readability": "0.6.0",
|
|
87
|
-
"@oh-my-pi/omp-stats": "12.
|
|
88
|
-
"@oh-my-pi/pi-agent-core": "12.
|
|
89
|
-
"@oh-my-pi/pi-ai": "12.
|
|
90
|
-
"@oh-my-pi/pi-natives": "12.
|
|
91
|
-
"@oh-my-pi/pi-tui": "12.
|
|
92
|
-
"@oh-my-pi/pi-utils": "12.
|
|
87
|
+
"@oh-my-pi/omp-stats": "12.10.0",
|
|
88
|
+
"@oh-my-pi/pi-agent-core": "12.10.0",
|
|
89
|
+
"@oh-my-pi/pi-ai": "12.10.0",
|
|
90
|
+
"@oh-my-pi/pi-natives": "12.10.0",
|
|
91
|
+
"@oh-my-pi/pi-tui": "12.10.0",
|
|
92
|
+
"@oh-my-pi/pi-utils": "12.10.0",
|
|
93
93
|
"@sinclair/typebox": "^0.34.48",
|
|
94
94
|
"@xterm/headless": "^6.0.0",
|
|
95
95
|
"ajv": "^8.18.0",
|
package/src/capability/rule.ts
CHANGED
|
@@ -7,15 +7,23 @@
|
|
|
7
7
|
import { defineCapability } from ".";
|
|
8
8
|
import type { SourceMeta } from "./types";
|
|
9
9
|
|
|
10
|
+
const CONDITION_GLOB_SCOPE_TOOLS = ["edit", "write"] as const;
|
|
11
|
+
|
|
10
12
|
/**
|
|
11
|
-
* Parsed frontmatter from
|
|
13
|
+
* Parsed frontmatter from rule files.
|
|
12
14
|
*/
|
|
13
15
|
export interface RuleFrontmatter {
|
|
14
16
|
description?: string;
|
|
15
17
|
globs?: string[];
|
|
16
18
|
alwaysApply?: boolean;
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
+
/** New key for TTSR match conditions. */
|
|
20
|
+
condition?: string | string[];
|
|
21
|
+
/** New key for TTSR stream scope. */
|
|
22
|
+
scope?: string | string[];
|
|
23
|
+
/** Legacy key accepted for backward compatibility with existing rules. */
|
|
24
|
+
ttsr_trigger?: string | string[];
|
|
25
|
+
/** Legacy camelCase key accepted for backward compatibility with existing rules. */
|
|
26
|
+
ttsrTrigger?: string | string[];
|
|
19
27
|
[key: string]: unknown;
|
|
20
28
|
}
|
|
21
29
|
|
|
@@ -35,12 +43,172 @@ export interface Rule {
|
|
|
35
43
|
alwaysApply?: boolean;
|
|
36
44
|
/** Description (for agent-requested rules) */
|
|
37
45
|
description?: string;
|
|
38
|
-
/** Regex
|
|
39
|
-
|
|
46
|
+
/** Regex condition(s) that can trigger TTSR interruption. */
|
|
47
|
+
condition?: string[];
|
|
48
|
+
/** Optional stream scope tokens (for example: text, thinking, tool:edit(*.ts)). */
|
|
49
|
+
scope?: string[];
|
|
40
50
|
/** Source metadata */
|
|
41
51
|
_source: SourceMeta;
|
|
42
52
|
}
|
|
43
53
|
|
|
54
|
+
function normalizeRuleField(value: unknown): string[] | undefined {
|
|
55
|
+
if (typeof value === "string") {
|
|
56
|
+
const token = value.trim();
|
|
57
|
+
return token.length > 0 ? [token] : undefined;
|
|
58
|
+
}
|
|
59
|
+
if (!Array.isArray(value)) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const tokens = value
|
|
64
|
+
.filter((item): item is string => typeof item === "string")
|
|
65
|
+
.map(item => item.trim())
|
|
66
|
+
.filter(item => item.length > 0);
|
|
67
|
+
if (tokens.length === 0) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return Array.from(new Set(tokens));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function splitScopeTokens(value: string): string[] {
|
|
75
|
+
const tokens: string[] = [];
|
|
76
|
+
let current = "";
|
|
77
|
+
let parenDepth = 0;
|
|
78
|
+
let bracketDepth = 0;
|
|
79
|
+
let braceDepth = 0;
|
|
80
|
+
let quote: '"' | "'" | undefined;
|
|
81
|
+
for (let i = 0; i < value.length; i++) {
|
|
82
|
+
const char = value[i];
|
|
83
|
+
if (quote) {
|
|
84
|
+
current += char;
|
|
85
|
+
if (char === quote && value[i - 1] !== "\\") {
|
|
86
|
+
quote = undefined;
|
|
87
|
+
}
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (char === '"' || char === "'") {
|
|
91
|
+
quote = char;
|
|
92
|
+
current += char;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (char === "(") {
|
|
96
|
+
parenDepth++;
|
|
97
|
+
current += char;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (char === ")") {
|
|
101
|
+
parenDepth = Math.max(0, parenDepth - 1);
|
|
102
|
+
current += char;
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (char === "[") {
|
|
106
|
+
bracketDepth++;
|
|
107
|
+
current += char;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (char === "]") {
|
|
111
|
+
bracketDepth = Math.max(0, bracketDepth - 1);
|
|
112
|
+
current += char;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (char === "{") {
|
|
116
|
+
braceDepth++;
|
|
117
|
+
current += char;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (char === "}") {
|
|
121
|
+
braceDepth = Math.max(0, braceDepth - 1);
|
|
122
|
+
current += char;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (char === "," && parenDepth === 0 && bracketDepth === 0 && braceDepth === 0) {
|
|
126
|
+
const token = current.trim();
|
|
127
|
+
if (token.length > 0) {
|
|
128
|
+
tokens.push(token);
|
|
129
|
+
}
|
|
130
|
+
current = "";
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
current += char;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const tail = current.trim();
|
|
137
|
+
if (tail.length > 0) {
|
|
138
|
+
tokens.push(tail);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return tokens;
|
|
142
|
+
}
|
|
143
|
+
function normalizeScopeField(value: unknown): string[] | undefined {
|
|
144
|
+
const normalized = normalizeRuleField(value);
|
|
145
|
+
if (!normalized) {
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const tokens = normalized.flatMap(splitScopeTokens).filter(item => item.length > 0);
|
|
150
|
+
if (tokens.length === 0) {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
return Array.from(new Set(tokens));
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Heuristic for condition shorthand that looks like a file glob (for example `*.rs`).
|
|
157
|
+
*/
|
|
158
|
+
function isLikelyFileGlob(value: string): boolean {
|
|
159
|
+
const token = value.trim();
|
|
160
|
+
if (token.length === 0) {
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
if (/[\\^$+|()]/.test(token)) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
if (!/[?*[\]{}]/.test(token)) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
if (token.includes("/")) {
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
return /^\*\.[^\s/]+$/.test(token);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Parse `condition` + `scope` from rule frontmatter.
|
|
177
|
+
*
|
|
178
|
+
* - `condition` accepts string or string[]
|
|
179
|
+
* - `scope` accepts string or string[]
|
|
180
|
+
* - legacy `ttsr_trigger` / `ttsrTrigger` are accepted as a `condition` fallback
|
|
181
|
+
* - condition tokens that look like file globs become scope shorthands:
|
|
182
|
+
* `*.rs` => `tool:edit(*.rs)`, `tool:write(*.rs)` and a catch-all condition `.*`
|
|
183
|
+
*/
|
|
184
|
+
export function parseRuleConditionAndScope(frontmatter: RuleFrontmatter): Pick<Rule, "condition" | "scope"> {
|
|
185
|
+
const rawCondition = frontmatter.condition ?? frontmatter.ttsr_trigger ?? frontmatter.ttsrTrigger;
|
|
186
|
+
const parsedCondition = normalizeRuleField(rawCondition);
|
|
187
|
+
const parsedScope = normalizeScopeField(frontmatter.scope);
|
|
188
|
+
|
|
189
|
+
const inferredScope: string[] = [];
|
|
190
|
+
const condition: string[] = [];
|
|
191
|
+
for (const token of parsedCondition ?? []) {
|
|
192
|
+
if (isLikelyFileGlob(token)) {
|
|
193
|
+
for (const toolName of CONDITION_GLOB_SCOPE_TOOLS) {
|
|
194
|
+
inferredScope.push(`tool:${toolName}(${token})`);
|
|
195
|
+
}
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
condition.push(token);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (condition.length === 0 && inferredScope.length > 0) {
|
|
202
|
+
condition.push(".*");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const scope = [...(parsedScope ?? []), ...inferredScope];
|
|
206
|
+
return {
|
|
207
|
+
condition: condition.length > 0 ? Array.from(new Set(condition)) : undefined,
|
|
208
|
+
scope: scope.length > 0 ? Array.from(new Set(scope)) : undefined,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
44
212
|
export const ruleCapability = defineCapability<Rule>({
|
|
45
213
|
id: "rules",
|
|
46
214
|
displayName: "Rules",
|
package/src/cli/args.ts
CHANGED
|
@@ -40,6 +40,7 @@ export interface Args {
|
|
|
40
40
|
export?: string;
|
|
41
41
|
noSkills?: boolean;
|
|
42
42
|
skills?: string[];
|
|
43
|
+
noRules?: boolean;
|
|
43
44
|
listModels?: string | true;
|
|
44
45
|
noTitle?: boolean;
|
|
45
46
|
messages: string[];
|
|
@@ -150,6 +151,8 @@ export function parseArgs(args: string[], extensionFlags?: Map<string, { type: "
|
|
|
150
151
|
result.noExtensions = true;
|
|
151
152
|
} else if (arg === "--no-skills") {
|
|
152
153
|
result.noSkills = true;
|
|
154
|
+
} else if (arg === "--no-rules") {
|
|
155
|
+
result.noRules = true;
|
|
153
156
|
} else if (arg === "--no-title") {
|
|
154
157
|
result.noTitle = true;
|
|
155
158
|
} else if (arg === "--skills" && i + 1 < args.length) {
|
package/src/cli/update-cli.ts
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { execSync, spawnSync } from "node:child_process";
|
|
8
8
|
import * as fs from "node:fs";
|
|
9
|
-
import * as path from "node:path";
|
|
10
9
|
import { pipeline } from "node:stream/promises";
|
|
11
10
|
import { isEnoent } from "@oh-my-pi/pi-utils";
|
|
12
11
|
import { APP_NAME, VERSION } from "@oh-my-pi/pi-utils/dirs";
|
|
@@ -80,7 +79,7 @@ async function getLatestRelease(): Promise<ReleaseInfo> {
|
|
|
80
79
|
return {
|
|
81
80
|
tag,
|
|
82
81
|
version,
|
|
83
|
-
assets: [makeAsset(getBinaryName())
|
|
82
|
+
assets: [makeAsset(getBinaryName())],
|
|
84
83
|
};
|
|
85
84
|
}
|
|
86
85
|
|
|
@@ -142,27 +141,6 @@ function getBinaryName(): string {
|
|
|
142
141
|
return `${APP_NAME}-${os}-${archName}`;
|
|
143
142
|
}
|
|
144
143
|
|
|
145
|
-
/**
|
|
146
|
-
* Get native addon names for this platform, ordered by preference.
|
|
147
|
-
*/
|
|
148
|
-
function getNativeAddonNames(): string[] {
|
|
149
|
-
const platform = process.platform;
|
|
150
|
-
const arch = process.arch;
|
|
151
|
-
if (!["linux", "darwin", "win32"].includes(platform)) {
|
|
152
|
-
throw new Error(`Unsupported platform: ${platform}`);
|
|
153
|
-
}
|
|
154
|
-
if (!["x64", "arm64"].includes(arch)) {
|
|
155
|
-
throw new Error(`Unsupported architecture: ${arch}`);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const baseName = `pi_natives.${platform}-${arch}.node`;
|
|
159
|
-
if (arch !== "x64") {
|
|
160
|
-
return [baseName];
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
return [`pi_natives.${platform}-${arch}-modern.node`, `pi_natives.${platform}-${arch}-baseline.node`];
|
|
164
|
-
}
|
|
165
|
-
|
|
166
144
|
/**
|
|
167
145
|
* Update via bun package manager.
|
|
168
146
|
*/
|
|
@@ -200,44 +178,13 @@ async function updateViaBun(expectedVersion: string): Promise<void> {
|
|
|
200
178
|
*/
|
|
201
179
|
async function updateViaBinary(release: ReleaseInfo): Promise<void> {
|
|
202
180
|
const binaryName = getBinaryName();
|
|
203
|
-
const nativeAddonNames = getNativeAddonNames();
|
|
204
181
|
const asset = release.assets.find(a => a.name === binaryName);
|
|
205
182
|
if (!asset) {
|
|
206
183
|
throw new Error(`No binary found for ${binaryName}`);
|
|
207
184
|
}
|
|
208
185
|
const execPath = process.execPath;
|
|
209
|
-
const execDir = path.dirname(execPath);
|
|
210
186
|
const tempPath = `${execPath}.new`;
|
|
211
187
|
const backupPath = `${execPath}.bak`;
|
|
212
|
-
const nativeDownloads: Array<{ name: string; tempPath: string; finalPath: string }> = [];
|
|
213
|
-
|
|
214
|
-
const downloadNativeAsset = async (name: string, required: boolean): Promise<boolean> => {
|
|
215
|
-
const nativeAsset = release.assets.find(assetEntry => assetEntry.name === name);
|
|
216
|
-
if (!nativeAsset) {
|
|
217
|
-
if (required) throw new Error(`No native addon found for ${name}`);
|
|
218
|
-
return false;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
console.log(chalk.dim(`Downloading ${name}…`));
|
|
222
|
-
try {
|
|
223
|
-
const nativeResponse = await fetch(nativeAsset.url, { redirect: "follow" });
|
|
224
|
-
if (!nativeResponse.ok || !nativeResponse.body) {
|
|
225
|
-
if (required) throw new Error(`Native addon download failed for ${name}: ${nativeResponse.statusText}`);
|
|
226
|
-
return false;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const nativeFinalPath = path.join(execDir, name);
|
|
230
|
-
const nativeTempPath = `${nativeFinalPath}.new`;
|
|
231
|
-
const nativeFileStream = fs.createWriteStream(nativeTempPath, { mode: 0o755 });
|
|
232
|
-
await pipeline(nativeResponse.body, nativeFileStream);
|
|
233
|
-
nativeDownloads.push({ name, tempPath: nativeTempPath, finalPath: nativeFinalPath });
|
|
234
|
-
return true;
|
|
235
|
-
} catch (err) {
|
|
236
|
-
if (required) throw err;
|
|
237
|
-
return false;
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
|
|
241
188
|
console.log(chalk.dim(`Downloading ${binaryName}…`));
|
|
242
189
|
|
|
243
190
|
// Download binary to temp file
|
|
@@ -247,9 +194,6 @@ async function updateViaBinary(release: ReleaseInfo): Promise<void> {
|
|
|
247
194
|
}
|
|
248
195
|
const fileStream = fs.createWriteStream(tempPath, { mode: 0o755 });
|
|
249
196
|
await pipeline(response.body, fileStream);
|
|
250
|
-
for (const nativeAddonName of nativeAddonNames) {
|
|
251
|
-
await downloadNativeAsset(nativeAddonName, true);
|
|
252
|
-
}
|
|
253
197
|
// Replace current binary
|
|
254
198
|
console.log(chalk.dim("Installing update..."));
|
|
255
199
|
try {
|
|
@@ -262,11 +206,7 @@ async function updateViaBinary(release: ReleaseInfo): Promise<void> {
|
|
|
262
206
|
await fs.promises.rename(tempPath, execPath);
|
|
263
207
|
await fs.promises.unlink(backupPath);
|
|
264
208
|
|
|
265
|
-
for (const nativeDownload of nativeDownloads) {
|
|
266
|
-
await fs.promises.rename(nativeDownload.tempPath, nativeDownload.finalPath);
|
|
267
|
-
}
|
|
268
209
|
console.log(chalk.green(`\n${theme.status.success} Updated to ${release.version}`));
|
|
269
|
-
console.log(chalk.dim(`Installed ${nativeDownloads.length} native addon file(s)`));
|
|
270
210
|
console.log(chalk.dim(`Restart ${APP_NAME} to use the new version`));
|
|
271
211
|
} catch (err) {
|
|
272
212
|
if (fs.existsSync(backupPath) && !fs.existsSync(execPath)) {
|
|
@@ -275,11 +215,6 @@ async function updateViaBinary(release: ReleaseInfo): Promise<void> {
|
|
|
275
215
|
if (fs.existsSync(tempPath)) {
|
|
276
216
|
await fs.promises.unlink(tempPath);
|
|
277
217
|
}
|
|
278
|
-
for (const nativeDownload of nativeDownloads) {
|
|
279
|
-
if (fs.existsSync(nativeDownload.tempPath)) {
|
|
280
|
-
await fs.promises.unlink(nativeDownload.tempPath);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
218
|
throw err;
|
|
284
219
|
}
|
|
285
220
|
}
|
package/src/commands/launch.ts
CHANGED
|
@@ -105,6 +105,9 @@ export default class Index extends Command {
|
|
|
105
105
|
skills: Flags.string({
|
|
106
106
|
description: "Comma-separated glob patterns to filter skills (e.g., git-*,docker)",
|
|
107
107
|
}),
|
|
108
|
+
"no-rules": Flags.boolean({
|
|
109
|
+
description: "Disable rules discovery and loading",
|
|
110
|
+
}),
|
|
108
111
|
export: Flags.string({
|
|
109
112
|
description: "Export session file to HTML and exit",
|
|
110
113
|
}),
|