@aexol/spectral 0.8.0 → 0.8.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/dist/extensions/kanban-bridge.js +668 -0
- package/dist/extensions/spectral-vision-fallback.js +3 -2
- package/dist/mcp/init.js +1 -9
- package/dist/memory/index.js +2 -0
- package/dist/memory/tools/write-project-observation.js +60 -0
- package/dist/relay/auto-research.js +34 -0
- package/dist/sdk/ai/env-api-keys.js +9 -49
- package/dist/sdk/ai/utils/oauth/anthropic.js +1 -1
- package/dist/sdk/ai/utils/oauth/openai-codex.js +1 -1
- package/dist/sdk/coding-agent/config.js +2 -69
- package/dist/sdk/coding-agent/core/extensions/loader.js +2 -35
- package/dist/sdk/coding-agent/core/extensions/runner.js +1 -2
- package/dist/sdk/coding-agent/core/model-resolver-utils.js +8 -0
- package/dist/sdk/coding-agent/core/model-resolver.js +1 -1
- package/dist/sdk/coding-agent/core/resource-loader.js +1 -1
- package/dist/sdk/coding-agent/core/settings-manager.js +1 -170
- package/dist/sdk/coding-agent/core/system-prompt.js +3 -1
- package/dist/sdk/coding-agent/core/theme.js +202 -0
- package/dist/sdk/coding-agent/core/tools/bash.js +17 -18
- package/dist/sdk/coding-agent/core/tools/edit.js +7 -8
- package/dist/sdk/coding-agent/core/tools/find.js +9 -13
- package/dist/sdk/coding-agent/core/tools/grep.js +10 -14
- package/dist/sdk/coding-agent/core/tools/ls.js +9 -10
- package/dist/sdk/coding-agent/core/tools/read.js +15 -25
- package/dist/sdk/coding-agent/{modes/interactive/components/diff.js → core/tools/render-diff.js} +18 -31
- package/dist/sdk/coding-agent/core/tools/write.js +10 -11
- package/dist/sdk/coding-agent/index.js +7 -5
- package/dist/sdk/coding-agent/modes/index.js +0 -1
- package/dist/sdk/coding-agent/modes/rpc/rpc-mode.js +2 -2
- package/dist/sdk/coding-agent/utils/photon.js +2 -10
- package/dist/sdk/coding-agent/utils/pi-user-agent.js +1 -2
- package/dist/server/agent-bridge.js +2 -1
- package/package.json +1 -1
- package/dist/sdk/coding-agent/bun/cli.js +0 -7
- package/dist/sdk/coding-agent/bun/restore-sandbox-env.js +0 -31
- package/dist/sdk/coding-agent/cli/args.js +0 -340
- package/dist/sdk/coding-agent/cli/file-processor.js +0 -82
- package/dist/sdk/coding-agent/cli/initial-message.js +0 -21
- package/dist/sdk/coding-agent/core/footer-data-provider.js +0 -309
- package/dist/sdk/coding-agent/modes/interactive/components/keybinding-hints.js +0 -35
- package/dist/sdk/coding-agent/modes/interactive/components/visual-truncate.js +0 -26
- package/dist/sdk/coding-agent/modes/interactive/interactive-mode.js +0 -3
- package/dist/sdk/coding-agent/modes/interactive/theme/theme.js +0 -1022
package/dist/mcp/init.js
CHANGED
|
@@ -208,15 +208,7 @@ export function flushMetadataCache(state) {
|
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
|
-
function safeFg(
|
|
212
|
-
try {
|
|
213
|
-
const styled = ui?.theme?.fg?.(color, text);
|
|
214
|
-
if (styled)
|
|
215
|
-
return styled;
|
|
216
|
-
}
|
|
217
|
-
catch {
|
|
218
|
-
// fall through to plain text
|
|
219
|
-
}
|
|
211
|
+
function safeFg(_ui, _color, text) {
|
|
220
212
|
return text;
|
|
221
213
|
}
|
|
222
214
|
export function updateStatusBar(state) {
|
package/dist/memory/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { registerObserverTrigger } from "./hooks/observer-trigger.js";
|
|
|
6
6
|
import { Runtime } from "./runtime.js";
|
|
7
7
|
import { registerRecallTool } from "./tools/recall-observation.js";
|
|
8
8
|
import { registerReadProjectObservationsTool } from "./tools/read-project-observations.js";
|
|
9
|
+
import { registerWriteProjectObservationTool } from "./tools/write-project-observation.js";
|
|
9
10
|
export default function observationalMemory(pi) {
|
|
10
11
|
const runtime = new Runtime();
|
|
11
12
|
// Log extension load so we can confirm it's running in serve mode.
|
|
@@ -17,4 +18,5 @@ export default function observationalMemory(pi) {
|
|
|
17
18
|
registerViewCommand(pi, runtime);
|
|
18
19
|
registerRecallTool(pi);
|
|
19
20
|
registerReadProjectObservationsTool(pi);
|
|
21
|
+
registerWriteProjectObservationTool(pi);
|
|
20
22
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Type } from "../../sdk/ai/index.js";
|
|
2
|
+
import { defineTool } from "../../sdk/coding-agent/index.js";
|
|
3
|
+
import { getProjectObsStore } from "../project-observations-store.js";
|
|
4
|
+
import { hashId } from "../ids.js";
|
|
5
|
+
export const WRITE_PROJECT_OBSERVATION_TOOL_NAME = "write_project_observation";
|
|
6
|
+
export const writeProjectObservationTool = defineTool({
|
|
7
|
+
name: WRITE_PROJECT_OBSERVATION_TOOL_NAME,
|
|
8
|
+
label: "Write project observation",
|
|
9
|
+
description: "Write a durable, cross-session observation about this project. " +
|
|
10
|
+
"Use this to persist discovered conventions, architectural rules, " +
|
|
11
|
+
"gotchas, and directory purposes. Observations are deduplicated by " +
|
|
12
|
+
"content hash — writing the same fact twice is harmless (idempotent). " +
|
|
13
|
+
"Future sessions can discover these via read_project_observations.",
|
|
14
|
+
promptSnippet: "Use write_project_observation(content, relevance) to persist project-level knowledge.",
|
|
15
|
+
promptGuidelines: [
|
|
16
|
+
"Use for STABLE facts: conventions, architecture decisions, gotchas, file roles.",
|
|
17
|
+
"Use relevance='critical' for NEVER-EDIT rules (auto-generated files, invariant constraints).",
|
|
18
|
+
"Use relevance='high' for important conventions the agent must follow.",
|
|
19
|
+
"Use relevance='medium' for useful patterns and directory purposes.",
|
|
20
|
+
"Content must be a single, self-contained sentence — no markdown, no lists.",
|
|
21
|
+
"Deduplication is automatic — same content = same ID → INSERT OR REPLACE.",
|
|
22
|
+
],
|
|
23
|
+
parameters: Type.Object({
|
|
24
|
+
content: Type.String({
|
|
25
|
+
minLength: 1,
|
|
26
|
+
description: "A single self-contained sentence describing the fact. No markdown, no lists.",
|
|
27
|
+
}),
|
|
28
|
+
relevance: Type.Union([
|
|
29
|
+
Type.Literal("low"),
|
|
30
|
+
Type.Literal("medium"),
|
|
31
|
+
Type.Literal("high"),
|
|
32
|
+
Type.Literal("critical"),
|
|
33
|
+
], { description: "Importance: critical > high > medium > low" }),
|
|
34
|
+
}),
|
|
35
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
36
|
+
const store = getProjectObsStore();
|
|
37
|
+
if (!store) {
|
|
38
|
+
return {
|
|
39
|
+
content: [{ type: "text", text: "Project observations store is not available." }],
|
|
40
|
+
details: { status: "store_unavailable" },
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const projectId = store.getProjectByCwd(ctx.cwd);
|
|
44
|
+
if (!projectId) {
|
|
45
|
+
return {
|
|
46
|
+
content: [{ type: "text", text: `No project found for current working directory: ${ctx.cwd}` }],
|
|
47
|
+
details: { status: "no_project" },
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const id = hashId(params.content);
|
|
51
|
+
store.insertProjectObservations(projectId, "auto-research", [{ id, content: params.content, relevance: params.relevance }], Date.now());
|
|
52
|
+
return {
|
|
53
|
+
content: [{ type: "text", text: `Observation [${id}] recorded (relevance: ${params.relevance}).` }],
|
|
54
|
+
details: { id, status: "ok" },
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
export function registerWriteProjectObservationTool(pi) {
|
|
59
|
+
pi.registerTool(writeProjectObservationTool);
|
|
60
|
+
}
|
|
@@ -532,6 +532,40 @@ function buildAutoResearchTask(projectPath, projectName, preRunContext = null) {
|
|
|
532
532
|
"",
|
|
533
533
|
"Start by collecting project context, then generate the most impactful extensions",
|
|
534
534
|
"and update AGENTS.md when done.",
|
|
535
|
+
"",
|
|
536
|
+
"### 6. Persist Key Discoveries as Durable Observations",
|
|
537
|
+
"",
|
|
538
|
+
"As you analyze the project, you will discover conventions, rules, and patterns",
|
|
539
|
+
"that future agent sessions should know. Write these as durable cross-session",
|
|
540
|
+
"observations using the `write_project_observation` tool:",
|
|
541
|
+
"",
|
|
542
|
+
"```",
|
|
543
|
+
"write_project_observation({",
|
|
544
|
+
" content: \"backend/src/models.ts is auto-generated by Axolotl — never edit by hand\",",
|
|
545
|
+
" relevance: \"critical\"",
|
|
546
|
+
"})",
|
|
547
|
+
"```",
|
|
548
|
+
"",
|
|
549
|
+
"**What to capture:**",
|
|
550
|
+
"",
|
|
551
|
+
"- **NEVER-EDIT markers** (relevance: critical): Auto-generated files that must",
|
|
552
|
+
" be regenerated instead of edited. Seen a comment like 'DO NOT EDIT'? Persist it.",
|
|
553
|
+
"- **Import/macro conventions** (relevance: high): Rules like \"all Deno imports",
|
|
554
|
+
" MUST use .ts extension\" or \"use workspace imports, not relative paths\".",
|
|
555
|
+
"- **Code-generation commands** (relevance: high): The exact shell commands to",
|
|
556
|
+
" regenerate generated files (e.g. 'cd backend && deno task axolotl:build').",
|
|
557
|
+
"- **Directory roles** (relevance: medium): What each top-level directory contains",
|
|
558
|
+
" (e.g. 'packages/parser/ is zero-dependency — never add imports here').",
|
|
559
|
+
"- **Runtime boundaries** (relevance: medium): Which packages use Deno vs Node.",
|
|
560
|
+
"- **Database/API patterns** (relevance: medium): How the backend is structured.",
|
|
561
|
+
"",
|
|
562
|
+
"**Guidelines:**",
|
|
563
|
+
"",
|
|
564
|
+
"- Write 5–10 observations covering the most important discoveries.",
|
|
565
|
+
"- Each observation is a single self-contained sentence — no markdown, no lists.",
|
|
566
|
+
"- Focus on facts that would PREVENT MISTAKES or SAVE TIME in future sessions.",
|
|
567
|
+
"- Deduplication is automatic by content hash — writing the same fact twice is harmless.",
|
|
568
|
+
"- Future sessions retrieve these via `read_project_observations(\"query\")`.",
|
|
535
569
|
].join("\n");
|
|
536
570
|
}
|
|
537
571
|
// ---------------------------------------------------------------------------
|
|
@@ -6,8 +6,8 @@ const dynamicImport = (specifier) => import(specifier);
|
|
|
6
6
|
const NODE_FS_SPECIFIER = "node:" + "fs";
|
|
7
7
|
const NODE_OS_SPECIFIER = "node:" + "os";
|
|
8
8
|
const NODE_PATH_SPECIFIER = "node:" + "path";
|
|
9
|
-
// Eagerly load in Node.js
|
|
10
|
-
if (typeof process !== "undefined" &&
|
|
9
|
+
// Eagerly load in Node.js environment only
|
|
10
|
+
if (typeof process !== "undefined" && process.versions?.node) {
|
|
11
11
|
dynamicImport(NODE_FS_SPECIFIER).then((m) => {
|
|
12
12
|
_existsSync = m.existsSync;
|
|
13
13
|
});
|
|
@@ -18,38 +18,6 @@ if (typeof process !== "undefined" && (process.versions?.node || process.version
|
|
|
18
18
|
_join = m.join;
|
|
19
19
|
});
|
|
20
20
|
}
|
|
21
|
-
let _procEnvCache = null;
|
|
22
|
-
/**
|
|
23
|
-
* Fallback for https://github.com/oven-sh/bun/issues/27802
|
|
24
|
-
* Bun compiled binaries have an empty `process.env` inside sandbox
|
|
25
|
-
* environments on Linux. We can recover the env from `/proc/self/environ`.
|
|
26
|
-
*/
|
|
27
|
-
function getProcEnv(key) {
|
|
28
|
-
if (!process.versions?.bun)
|
|
29
|
-
return undefined;
|
|
30
|
-
if (typeof process === "undefined")
|
|
31
|
-
return undefined;
|
|
32
|
-
// If process.env already has entries, the bug is not triggered.
|
|
33
|
-
if (Object.keys(process.env).length > 0)
|
|
34
|
-
return undefined;
|
|
35
|
-
if (_procEnvCache === null) {
|
|
36
|
-
_procEnvCache = new Map();
|
|
37
|
-
try {
|
|
38
|
-
const { readFileSync } = require("node:fs");
|
|
39
|
-
const data = readFileSync("/proc/self/environ", "utf-8");
|
|
40
|
-
for (const entry of data.split("\0")) {
|
|
41
|
-
const idx = entry.indexOf("=");
|
|
42
|
-
if (idx > 0) {
|
|
43
|
-
_procEnvCache.set(entry.slice(0, idx), entry.slice(idx + 1));
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
// /proc/self/environ may not be readable.
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return _procEnvCache.get(key);
|
|
52
|
-
}
|
|
53
21
|
let cachedVertexAdcCredentialsExists = null;
|
|
54
22
|
function hasVertexAdcCredentials() {
|
|
55
23
|
if (cachedVertexAdcCredentialsExists === null) {
|
|
@@ -57,7 +25,7 @@ function hasVertexAdcCredentials() {
|
|
|
57
25
|
// return false WITHOUT caching so the next call retries once they're ready.
|
|
58
26
|
// Only cache false permanently in a browser environment where fs is never available.
|
|
59
27
|
if (!_existsSync || !_homedir || !_join) {
|
|
60
|
-
const isNode = typeof process !== "undefined" &&
|
|
28
|
+
const isNode = typeof process !== "undefined" && process.versions?.node;
|
|
61
29
|
if (!isNode) {
|
|
62
30
|
// Definitively in a browser — safe to cache false permanently
|
|
63
31
|
cachedVertexAdcCredentialsExists = false;
|
|
@@ -65,7 +33,7 @@ function hasVertexAdcCredentials() {
|
|
|
65
33
|
return false;
|
|
66
34
|
}
|
|
67
35
|
// Check GOOGLE_APPLICATION_CREDENTIALS env var first (standard way)
|
|
68
|
-
const gacPath = process.env.GOOGLE_APPLICATION_CREDENTIALS
|
|
36
|
+
const gacPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
|
|
69
37
|
if (gacPath) {
|
|
70
38
|
cachedVertexAdcCredentialsExists = _existsSync(gacPath);
|
|
71
39
|
}
|
|
@@ -121,23 +89,21 @@ export function findEnvKeys(provider) {
|
|
|
121
89
|
const envVars = getApiKeyEnvVars(provider);
|
|
122
90
|
if (!envVars)
|
|
123
91
|
return undefined;
|
|
124
|
-
const found = envVars.filter((envVar) => !!process.env[envVar]
|
|
92
|
+
const found = envVars.filter((envVar) => !!process.env[envVar]);
|
|
125
93
|
return found.length > 0 ? found : undefined;
|
|
126
94
|
}
|
|
127
95
|
export function getEnvApiKey(provider) {
|
|
128
96
|
const envKeys = findEnvKeys(provider);
|
|
129
97
|
if (envKeys?.[0]) {
|
|
130
|
-
return process.env[envKeys[0]]
|
|
98
|
+
return process.env[envKeys[0]];
|
|
131
99
|
}
|
|
132
100
|
// Vertex AI supports either an explicit API key or Application Default Credentials.
|
|
133
101
|
// Auth is configured via `gcloud auth application-default login`.
|
|
134
102
|
if (provider === "google-vertex") {
|
|
135
103
|
const hasCredentials = hasVertexAdcCredentials();
|
|
136
104
|
const hasProject = !!(process.env.GOOGLE_CLOUD_PROJECT ||
|
|
137
|
-
process.env.GCLOUD_PROJECT
|
|
138
|
-
|
|
139
|
-
getProcEnv("GCLOUD_PROJECT"));
|
|
140
|
-
const hasLocation = !!(process.env.GOOGLE_CLOUD_LOCATION || getProcEnv("GOOGLE_CLOUD_LOCATION"));
|
|
105
|
+
process.env.GCLOUD_PROJECT);
|
|
106
|
+
const hasLocation = !!(process.env.GOOGLE_CLOUD_LOCATION);
|
|
141
107
|
if (hasCredentials && hasProject && hasLocation) {
|
|
142
108
|
return "<authenticated>";
|
|
143
109
|
}
|
|
@@ -155,13 +121,7 @@ export function getEnvApiKey(provider) {
|
|
|
155
121
|
process.env.AWS_BEARER_TOKEN_BEDROCK ||
|
|
156
122
|
process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ||
|
|
157
123
|
process.env.AWS_CONTAINER_CREDENTIALS_FULL_URI ||
|
|
158
|
-
process.env.AWS_WEB_IDENTITY_TOKEN_FILE
|
|
159
|
-
getProcEnv("AWS_PROFILE") ||
|
|
160
|
-
(getProcEnv("AWS_ACCESS_KEY_ID") && getProcEnv("AWS_SECRET_ACCESS_KEY")) ||
|
|
161
|
-
getProcEnv("AWS_BEARER_TOKEN_BEDROCK") ||
|
|
162
|
-
getProcEnv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI") ||
|
|
163
|
-
getProcEnv("AWS_CONTAINER_CREDENTIALS_FULL_URI") ||
|
|
164
|
-
getProcEnv("AWS_WEB_IDENTITY_TOKEN_FILE")) {
|
|
124
|
+
process.env.AWS_WEB_IDENTITY_TOKEN_FILE) {
|
|
165
125
|
return "<authenticated>";
|
|
166
126
|
}
|
|
167
127
|
}
|
|
@@ -21,7 +21,7 @@ async function getNodeApis() {
|
|
|
21
21
|
if (nodeApis)
|
|
22
22
|
return nodeApis;
|
|
23
23
|
if (!nodeApisPromise) {
|
|
24
|
-
if (typeof process === "undefined" ||
|
|
24
|
+
if (typeof process === "undefined" || !process.versions?.node) {
|
|
25
25
|
throw new Error("Anthropic OAuth is only available in Node.js environments");
|
|
26
26
|
}
|
|
27
27
|
nodeApisPromise = import("node:http").then((httpModule) => ({
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// NEVER convert to top-level imports - breaks browser/Vite builds
|
|
8
8
|
let _randomBytes = null;
|
|
9
9
|
let _http = null;
|
|
10
|
-
if (typeof process !== "undefined" &&
|
|
10
|
+
if (typeof process !== "undefined" && process.versions?.node) {
|
|
11
11
|
import("node:crypto").then((m) => {
|
|
12
12
|
_randomBytes = m.randomBytes;
|
|
13
13
|
});
|
|
@@ -9,13 +9,6 @@ import { normalizePath } from "./utils/paths.js";
|
|
|
9
9
|
// =============================================================================
|
|
10
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
11
|
const __dirname = dirname(__filename);
|
|
12
|
-
/**
|
|
13
|
-
* Detect if we're running as a Bun compiled binary.
|
|
14
|
-
* Bun binaries have import.meta.url containing "$bunfs", "~BUN", or "%7EBUN" (Bun's virtual filesystem path)
|
|
15
|
-
*/
|
|
16
|
-
export const isBunBinary = import.meta.url.includes("$bunfs") || import.meta.url.includes("~BUN") || import.meta.url.includes("%7EBUN");
|
|
17
|
-
/** Detect if Bun is the runtime (compiled binary or bun run) */
|
|
18
|
-
export const isBunRuntime = !!process.versions.bun;
|
|
19
12
|
function makeSelfUpdateCommand(installStep, uninstallStep) {
|
|
20
13
|
if (!uninstallStep)
|
|
21
14
|
return installStep;
|
|
@@ -33,9 +26,6 @@ function makeSelfUpdateCommandStep(command, args) {
|
|
|
33
26
|
};
|
|
34
27
|
}
|
|
35
28
|
export function detectInstallMethod() {
|
|
36
|
-
if (isBunBinary) {
|
|
37
|
-
return "bun-binary";
|
|
38
|
-
}
|
|
39
29
|
const resolvedPath = `${__dirname}\0${process.execPath || ""}`.toLowerCase().replace(/\\/g, "/");
|
|
40
30
|
if (resolvedPath.includes("/pnpm/") || resolvedPath.includes("/.pnpm/")) {
|
|
41
31
|
return "pnpm";
|
|
@@ -43,9 +33,6 @@ export function detectInstallMethod() {
|
|
|
43
33
|
if (resolvedPath.includes("/yarn/") || resolvedPath.includes("/.yarn/")) {
|
|
44
34
|
return "yarn";
|
|
45
35
|
}
|
|
46
|
-
if (isBunRuntime || resolvedPath.includes("/install/global/node_modules/")) {
|
|
47
|
-
return "bun";
|
|
48
|
-
}
|
|
49
36
|
if (resolvedPath.includes("/npm/") || resolvedPath.includes("/node_modules/")) {
|
|
50
37
|
return "npm";
|
|
51
38
|
}
|
|
@@ -74,8 +61,6 @@ function getInferredNpmInstall() {
|
|
|
74
61
|
}
|
|
75
62
|
function getSelfUpdateCommandForMethod(method, installedPackageName, updatePackageName = installedPackageName, npmCommand) {
|
|
76
63
|
switch (method) {
|
|
77
|
-
case "bun-binary":
|
|
78
|
-
return undefined;
|
|
79
64
|
case "pnpm":
|
|
80
65
|
return makeSelfUpdateCommand(makeSelfUpdateCommandStep("pnpm", ["install", "-g", "--ignore-scripts", updatePackageName]), updatePackageName === installedPackageName
|
|
81
66
|
? undefined
|
|
@@ -84,10 +69,6 @@ function getSelfUpdateCommandForMethod(method, installedPackageName, updatePacka
|
|
|
84
69
|
return makeSelfUpdateCommand(makeSelfUpdateCommandStep("yarn", ["global", "add", "--ignore-scripts", updatePackageName]), updatePackageName === installedPackageName
|
|
85
70
|
? undefined
|
|
86
71
|
: makeSelfUpdateCommandStep("yarn", ["global", "remove", installedPackageName]));
|
|
87
|
-
case "bun":
|
|
88
|
-
return makeSelfUpdateCommand(makeSelfUpdateCommandStep("bun", ["install", "-g", "--ignore-scripts", updatePackageName]), updatePackageName === installedPackageName
|
|
89
|
-
? undefined
|
|
90
|
-
: makeSelfUpdateCommandStep("bun", ["uninstall", "-g", installedPackageName]));
|
|
91
72
|
case "npm": {
|
|
92
73
|
const [command = "npm", ...npmArgs] = npmCommand ?? [];
|
|
93
74
|
const inferred = npmCommand?.length ? undefined : getInferredNpmInstall();
|
|
@@ -126,16 +107,6 @@ function getGlobalPackageRoots(method, _packageName, npmCommand) {
|
|
|
126
107
|
case "npm": {
|
|
127
108
|
const configured = !!npmCommand?.length;
|
|
128
109
|
const [command = "npm", ...npmArgs] = npmCommand ?? [];
|
|
129
|
-
if (configured && command === "bun") {
|
|
130
|
-
const bunBin = readCommandOutput(command, [...npmArgs, "pm", "bin", "-g"], {
|
|
131
|
-
requireSuccess: true,
|
|
132
|
-
});
|
|
133
|
-
const roots = [join(homedir(), ".bun", "install", "global", "node_modules")];
|
|
134
|
-
if (bunBin) {
|
|
135
|
-
roots.push(join(dirname(bunBin), "install", "global", "node_modules"));
|
|
136
|
-
}
|
|
137
|
-
return roots;
|
|
138
|
-
}
|
|
139
110
|
const root = readCommandOutput(command, [...npmArgs, "root", "-g"], {
|
|
140
111
|
requireSuccess: configured,
|
|
141
112
|
});
|
|
@@ -150,15 +121,6 @@ function getGlobalPackageRoots(method, _packageName, npmCommand) {
|
|
|
150
121
|
const dir = readCommandOutput("yarn", ["global", "dir"]);
|
|
151
122
|
return dir ? [dir, join(dir, "node_modules")] : [];
|
|
152
123
|
}
|
|
153
|
-
case "bun": {
|
|
154
|
-
const bunBin = readCommandOutput("bun", ["pm", "bin", "-g"]);
|
|
155
|
-
const roots = [join(homedir(), ".bun", "install", "global", "node_modules")];
|
|
156
|
-
if (bunBin) {
|
|
157
|
-
roots.push(join(dirname(bunBin), "install", "global", "node_modules"));
|
|
158
|
-
}
|
|
159
|
-
return roots;
|
|
160
|
-
}
|
|
161
|
-
case "bun-binary":
|
|
162
124
|
case "unknown":
|
|
163
125
|
return [];
|
|
164
126
|
}
|
|
@@ -229,9 +191,6 @@ export function getSelfUpdateCommand(packageName, npmCommand, updatePackageName
|
|
|
229
191
|
}
|
|
230
192
|
export function getSelfUpdateUnavailableInstruction(packageName, npmCommand, updatePackageName = packageName) {
|
|
231
193
|
const method = detectInstallMethod();
|
|
232
|
-
if (method === "bun-binary") {
|
|
233
|
-
return `Download from: https://github.com/earendil-works/pi-mono/releases/latest`;
|
|
234
|
-
}
|
|
235
194
|
const command = getSelfUpdateCommandForMethod(method, packageName, updatePackageName, npmCommand);
|
|
236
195
|
if (command) {
|
|
237
196
|
if (isManagedByGlobalPackageManager(method, packageName, npmCommand) && !isSelfUpdatePathWritable()) {
|
|
@@ -254,7 +213,6 @@ export function getUpdateInstruction(packageName) {
|
|
|
254
213
|
// =============================================================================
|
|
255
214
|
/**
|
|
256
215
|
* Get the base directory for resolving package assets (themes, package.json, README.md, CHANGELOG.md).
|
|
257
|
-
* - For Bun binary: returns the directory containing the executable
|
|
258
216
|
* - For Node.js (dist/): returns __dirname (the dist/ directory)
|
|
259
217
|
* - For tsx (src/): returns parent directory (the package root)
|
|
260
218
|
*/
|
|
@@ -264,10 +222,6 @@ export function getPackageDir() {
|
|
|
264
222
|
if (envDir) {
|
|
265
223
|
return normalizePath(envDir);
|
|
266
224
|
}
|
|
267
|
-
if (isBunBinary) {
|
|
268
|
-
// Bun binary: process.execPath points to the compiled executable
|
|
269
|
-
return dirname(process.execPath);
|
|
270
|
-
}
|
|
271
225
|
// Node.js: walk up from __dirname until we find package.json
|
|
272
226
|
let dir = __dirname;
|
|
273
227
|
while (dir !== dirname(dir)) {
|
|
@@ -281,14 +235,10 @@ export function getPackageDir() {
|
|
|
281
235
|
}
|
|
282
236
|
/**
|
|
283
237
|
* Get path to built-in themes directory (shipped with package)
|
|
284
|
-
* - For Bun binary: theme/ next to executable
|
|
285
238
|
* - For Node.js (dist/): dist/modes/interactive/theme/
|
|
286
239
|
* - For tsx (src/): src/modes/interactive/theme/
|
|
287
240
|
*/
|
|
288
241
|
export function getThemesDir() {
|
|
289
|
-
if (isBunBinary) {
|
|
290
|
-
return join(getPackageDir(), "theme");
|
|
291
|
-
}
|
|
292
242
|
// Theme is in modes/interactive/theme/ relative to src/ or dist/
|
|
293
243
|
const packageDir = getPackageDir();
|
|
294
244
|
const srcOrDist = existsSync(join(packageDir, "src")) ? "src" : "dist";
|
|
@@ -296,7 +246,8 @@ export function getThemesDir() {
|
|
|
296
246
|
}
|
|
297
247
|
/**
|
|
298
248
|
* Get path to HTML export template directory (shipped with package)
|
|
299
|
-
* - For
|
|
249
|
+
* - For Node.js (dist/): returns __dirname (the dist/ directory)
|
|
250
|
+
* - For tsx (src/): returns parent directory (the package root)
|
|
300
251
|
/** Get path to package.json */
|
|
301
252
|
export function getPackageJsonPath() {
|
|
302
253
|
return join(getPackageDir(), "package.json");
|
|
@@ -317,24 +268,6 @@ export function getExamplesPath() {
|
|
|
317
268
|
export function getChangelogPath() {
|
|
318
269
|
return resolve(join(getPackageDir(), "CHANGELOG.md"));
|
|
319
270
|
}
|
|
320
|
-
/**
|
|
321
|
-
* Get path to built-in interactive assets directory.
|
|
322
|
-
* - For Bun binary: assets/ next to executable
|
|
323
|
-
* - For Node.js (dist/): dist/modes/interactive/assets/
|
|
324
|
-
* - For tsx (src/): src/modes/interactive/assets/
|
|
325
|
-
*/
|
|
326
|
-
export function getInteractiveAssetsDir() {
|
|
327
|
-
if (isBunBinary) {
|
|
328
|
-
return join(getPackageDir(), "assets");
|
|
329
|
-
}
|
|
330
|
-
const packageDir = getPackageDir();
|
|
331
|
-
const srcOrDist = existsSync(join(packageDir, "src")) ? "src" : "dist";
|
|
332
|
-
return join(packageDir, srcOrDist, "modes", "interactive", "assets");
|
|
333
|
-
}
|
|
334
|
-
/** Get path to a bundled interactive asset */
|
|
335
|
-
export function getBundledInteractiveAssetPath(name) {
|
|
336
|
-
return join(getInteractiveAssetsDir(), name);
|
|
337
|
-
}
|
|
338
271
|
const pkg = JSON.parse(readFileSync(getPackageJsonPath(), "utf-8"));
|
|
339
272
|
const spectralConfigName = pkg.spectralConfig?.name;
|
|
340
273
|
export const PACKAGE_NAME = pkg.name || "index.ts";
|
|
@@ -6,45 +6,15 @@ import * as fs from "node:fs";
|
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
-
import * as _bundledPiAgentCore from "../../../agent-core/index.js";
|
|
10
|
-
import * as _bundledPiAi from "../../../ai/index.js";
|
|
11
|
-
import * as _bundledPiAiOauth from "../../../ai/oauth.js";
|
|
12
9
|
import { createJiti } from "@mariozechner/jiti";
|
|
13
|
-
|
|
14
|
-
// These MUST be static so Bun bundles them into the compiled binary.
|
|
15
|
-
// The virtualModules option then makes them available to extensions.
|
|
16
|
-
import * as _bundledTypebox from "typebox";
|
|
17
|
-
import * as _bundledTypeboxCompile from "typebox/compile";
|
|
18
|
-
import * as _bundledTypeboxValue from "typebox/value";
|
|
19
|
-
import { CONFIG_DIR_NAME, getAgentDir, isBunBinary } from "../../config.js";
|
|
20
|
-
// NOTE: This import works because loader.ts exports are NOT re-exported from index.ts,
|
|
21
|
-
// avoiding a circular dependency. Extensions can import from ../../index.ts.
|
|
22
|
-
import * as _bundledPiCodingAgent from "../../index.js";
|
|
10
|
+
import { CONFIG_DIR_NAME, getAgentDir } from "../../config.js";
|
|
23
11
|
import { resolvePath } from "../../utils/paths.js";
|
|
24
12
|
import { createEventBus } from "../event-bus.js";
|
|
25
13
|
import { execCommand } from "../exec.js";
|
|
26
14
|
import { createSyntheticSourceInfo } from "../source-info.js";
|
|
27
|
-
/** Modules available to extensions via virtualModules (for compiled Bun binary) */
|
|
28
|
-
const VIRTUAL_MODULES = {
|
|
29
|
-
typebox: _bundledTypebox,
|
|
30
|
-
"typebox/compile": _bundledTypeboxCompile,
|
|
31
|
-
"typebox/value": _bundledTypeboxValue,
|
|
32
|
-
"@sinclair/typebox": _bundledTypebox,
|
|
33
|
-
"@sinclair/typebox/compile": _bundledTypeboxCompile,
|
|
34
|
-
"@sinclair/typebox/value": _bundledTypeboxValue,
|
|
35
|
-
"../../../agent-core/index.ts": _bundledPiAgentCore,
|
|
36
|
-
"../../../ai/index.ts": _bundledPiAi,
|
|
37
|
-
"../../../ai/oauth.ts": _bundledPiAiOauth,
|
|
38
|
-
"../../index.ts": _bundledPiCodingAgent,
|
|
39
|
-
"@mariozechner/pi-agent": _bundledPiAgentCore,
|
|
40
|
-
"@mariozechner/pi-ai": _bundledPiAi,
|
|
41
|
-
"@mariozechner/pi-ai/oauth": _bundledPiAiOauth,
|
|
42
|
-
"@mariozechner/pi-coding-agent": _bundledPiCodingAgent,
|
|
43
|
-
};
|
|
44
15
|
const require = createRequire(import.meta.url);
|
|
45
16
|
/**
|
|
46
17
|
* Get aliases for jiti (used in Node.js/development mode).
|
|
47
|
-
* In Bun binary mode, virtualModules is used instead.
|
|
48
18
|
*/
|
|
49
19
|
let _aliases = null;
|
|
50
20
|
function getAliases() {
|
|
@@ -263,10 +233,7 @@ function createExtensionAPI(extension, runtime, cwd, eventBus) {
|
|
|
263
233
|
async function loadExtensionModule(extensionPath) {
|
|
264
234
|
const jiti = createJiti(import.meta.url, {
|
|
265
235
|
moduleCache: false,
|
|
266
|
-
|
|
267
|
-
// Also disable tryNative so jiti handles ALL imports (not just the entry point)
|
|
268
|
-
// In Node.js/dev: use aliases to resolve to node_modules paths
|
|
269
|
-
...(isBunBinary ? { virtualModules: VIRTUAL_MODULES, tryNative: false } : { alias: getAliases() }),
|
|
236
|
+
alias: getAliases(),
|
|
270
237
|
});
|
|
271
238
|
const module = await jiti.import(extensionPath, { default: true });
|
|
272
239
|
const factory = module;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Extension runner - executes extensions and manages their lifecycle.
|
|
3
3
|
*/
|
|
4
|
-
import { theme } from "../../modes/interactive/theme/theme.js";
|
|
5
4
|
// Extension shortcuts compete with canonical keybinding ids from keybindings.json.
|
|
6
5
|
// Only editor-global shortcuts are reserved here. Picker-specific bindings are not.
|
|
7
6
|
const RESERVED_KEYBINDINGS_FOR_EXTENSION_CONFLICTS = [
|
|
@@ -80,7 +79,7 @@ const noOpUIContext = {
|
|
|
80
79
|
setEditorComponent: () => { },
|
|
81
80
|
getEditorComponent: () => undefined,
|
|
82
81
|
get theme() {
|
|
83
|
-
return
|
|
82
|
+
return {};
|
|
84
83
|
},
|
|
85
84
|
getAllThemes: () => [],
|
|
86
85
|
getTheme: () => undefined,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utility for validating thinking level strings.
|
|
3
|
+
* Extracted from cli/args.ts (TUI-only, now deleted).
|
|
4
|
+
*/
|
|
5
|
+
const VALID_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"];
|
|
6
|
+
export function isValidThinkingLevel(level) {
|
|
7
|
+
return VALID_THINKING_LEVELS.includes(level);
|
|
8
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { modelsAreEqual } from "../../ai/index.js";
|
|
5
5
|
import chalk from "chalk";
|
|
6
6
|
import { minimatch } from "minimatch";
|
|
7
|
-
import { isValidThinkingLevel } from "
|
|
7
|
+
import { isValidThinkingLevel } from "./model-resolver-utils.js";
|
|
8
8
|
import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
|
|
9
9
|
/** Default model IDs for each known provider */
|
|
10
10
|
export const defaultModelPerProvider = {
|
|
@@ -2,7 +2,7 @@ import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
2
2
|
import { join, resolve, sep } from "node:path";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { CONFIG_DIR_NAME } from "../config.js";
|
|
5
|
-
import { loadThemeFromPath } from "
|
|
5
|
+
import { loadThemeFromPath } from "./theme.js";
|
|
6
6
|
import { canonicalizePath, isLocalPath, resolvePath } from "../utils/paths.js";
|
|
7
7
|
import { createEventBus } from "./event-bus.js";
|
|
8
8
|
import { createExtensionRuntime, loadExtensionFromFactory, loadExtensions } from "./extensions/loader.js";
|