@aexol/spectral 0.7.7 → 0.8.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/dist/agent/agents.js +4 -4
- package/dist/agent/index.js +24 -148
- package/dist/cli.js +25 -220
- package/dist/commands/serve.js +1 -1
- package/dist/extensions/spectral-vision-fallback.js +225 -0
- package/dist/mcp/agent-dir.js +1 -1
- package/dist/mcp/config.js +3 -3
- package/dist/mcp/sampling-handler.js +1 -1
- package/dist/mcp/server-manager.js +5 -1
- package/dist/memory/commands/status.js +6 -6
- package/dist/memory/commands/view.js +16 -14
- package/dist/memory/compaction.js +33 -5
- package/dist/memory/config.js +3 -3
- package/dist/memory/debug-log.js +1 -1
- package/dist/memory/observer.js +2 -2
- package/dist/memory/prompts.js +5 -5
- package/dist/memory/tokens.js +1 -1
- package/dist/memory/tools/read-project-observations.js +2 -2
- package/dist/memory/tools/recall-observation.js +4 -4
- package/dist/relay/auto-research.js +23 -23
- package/dist/relay/dispatcher.js +28 -2
- package/dist/relay/models-fetch.js +15 -3
- package/dist/{pi → sdk}/coding-agent/cli/args.js +4 -4
- package/dist/{pi → sdk}/coding-agent/config.js +9 -20
- package/dist/{pi → sdk}/coding-agent/core/agent-session.js +5 -17
- package/dist/{pi → sdk}/coding-agent/core/compaction/compaction.js +161 -5
- package/dist/{pi → sdk}/coding-agent/core/extensions/loader.js +0 -6
- package/dist/{pi → sdk}/coding-agent/core/extensions/runner.js +7 -1
- package/dist/{pi → sdk}/coding-agent/core/keybindings.js +129 -2
- package/dist/{pi → sdk}/coding-agent/core/model-registry.js +11 -4
- package/dist/{pi → sdk}/coding-agent/core/package-manager.js +5 -5
- package/dist/{pi → sdk}/coding-agent/core/sdk.js +1 -1
- package/dist/{pi → sdk}/coding-agent/core/session-manager.js +4 -4
- package/dist/{pi → sdk}/coding-agent/core/settings-manager.js +20 -0
- package/dist/{pi → sdk}/coding-agent/core/telemetry.js +1 -1
- package/dist/{pi → sdk}/coding-agent/core/tools/bash.js +17 -63
- package/dist/{pi → sdk}/coding-agent/core/tools/edit.js +4 -141
- package/dist/{pi → sdk}/coding-agent/core/tools/find.js +0 -11
- package/dist/{pi → sdk}/coding-agent/core/tools/grep.js +0 -11
- package/dist/{pi → sdk}/coding-agent/core/tools/ls.js +0 -11
- package/dist/{pi → sdk}/coding-agent/core/tools/read.js +0 -12
- package/dist/{pi → sdk}/coding-agent/core/tools/render-utils.js +1 -14
- package/dist/{pi → sdk}/coding-agent/core/tools/write.js +2 -97
- package/dist/{pi → sdk}/coding-agent/migrations.js +3 -3
- package/dist/{pi → sdk}/coding-agent/modes/interactive/components/keybinding-hints.js +1 -1
- package/dist/sdk/coding-agent/modes/interactive/components/visual-truncate.js +26 -0
- package/dist/{pi → sdk}/coding-agent/modes/interactive/theme/theme.js +1 -2
- package/dist/{pi → sdk}/coding-agent/utils/tools-manager.js +1 -1
- package/dist/{pi → sdk}/coding-agent/utils/version-check.js +2 -2
- package/dist/{pi → sdk}/coding-agent/utils/windows-self-update.js +1 -1
- package/dist/server/{pi-bridge.js → agent-bridge.js} +158 -89
- package/dist/server/handlers/sessions.js +21 -0
- package/dist/server/session-stream.js +12 -6
- package/package.json +6 -3
- package/dist/pi/coding-agent/core/export-html/ansi-to-html.js +0 -248
- package/dist/pi/coding-agent/core/export-html/index.js +0 -225
- package/dist/pi/coding-agent/core/export-html/tool-renderer.js +0 -107
- package/dist/pi/coding-agent/modes/interactive/components/visual-truncate.js +0 -32
- package/dist/pi/tui/autocomplete.js +0 -631
- package/dist/pi/tui/components/box.js +0 -103
- package/dist/pi/tui/components/cancellable-loader.js +0 -34
- package/dist/pi/tui/components/editor.js +0 -1915
- package/dist/pi/tui/components/image.js +0 -88
- package/dist/pi/tui/components/input.js +0 -425
- package/dist/pi/tui/components/loader.js +0 -68
- package/dist/pi/tui/components/markdown.js +0 -633
- package/dist/pi/tui/components/select-list.js +0 -158
- package/dist/pi/tui/components/settings-list.js +0 -184
- package/dist/pi/tui/components/spacer.js +0 -22
- package/dist/pi/tui/components/text.js +0 -88
- package/dist/pi/tui/components/truncated-text.js +0 -50
- package/dist/pi/tui/editor-component.js +0 -1
- package/dist/pi/tui/fuzzy.js +0 -109
- package/dist/pi/tui/index.js +0 -31
- package/dist/pi/tui/keybindings.js +0 -173
- package/dist/pi/tui/keys.js +0 -1172
- package/dist/pi/tui/kill-ring.js +0 -43
- package/dist/pi/tui/stdin-buffer.js +0 -360
- package/dist/pi/tui/terminal-image.js +0 -335
- package/dist/pi/tui/terminal.js +0 -324
- package/dist/pi/tui/tui.js +0 -1076
- package/dist/pi/tui/undo-stack.js +0 -24
- package/dist/pi/tui/utils.js +0 -1016
- /package/dist/{pi → sdk}/agent-core/agent-loop.js +0 -0
- /package/dist/{pi → sdk}/agent-core/agent.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/agent-harness.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/compaction/branch-summarization.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/compaction/compaction.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/compaction/utils.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/env/nodejs.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/messages.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/prompt-templates.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/session/jsonl-repo.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/session/jsonl-storage.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/session/memory-repo.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/session/memory-storage.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/session/repo-utils.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/session/session.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/session/uuid.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/skills.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/system-prompt.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/types.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/utils/shell-output.js +0 -0
- /package/dist/{pi → sdk}/agent-core/harness/utils/truncate.js +0 -0
- /package/dist/{pi → sdk}/agent-core/index.js +0 -0
- /package/dist/{pi → sdk}/agent-core/node.js +0 -0
- /package/dist/{pi → sdk}/agent-core/proxy.js +0 -0
- /package/dist/{pi → sdk}/agent-core/types.js +0 -0
- /package/dist/{pi → sdk}/ai/api-registry.js +0 -0
- /package/dist/{pi → sdk}/ai/cli.js +0 -0
- /package/dist/{pi → sdk}/ai/env-api-keys.js +0 -0
- /package/dist/{pi → sdk}/ai/image-models.generated.js +0 -0
- /package/dist/{pi → sdk}/ai/image-models.js +0 -0
- /package/dist/{pi → sdk}/ai/images-api-registry.js +0 -0
- /package/dist/{pi → sdk}/ai/images.js +0 -0
- /package/dist/{pi → sdk}/ai/index.js +0 -0
- /package/dist/{pi → sdk}/ai/models.generated.js +0 -0
- /package/dist/{pi → sdk}/ai/models.js +0 -0
- /package/dist/{pi → sdk}/ai/oauth.js +0 -0
- /package/dist/{pi → sdk}/ai/providers/anthropic.js +0 -0
- /package/dist/{pi → sdk}/ai/providers/faux.js +0 -0
- /package/dist/{pi → sdk}/ai/providers/github-copilot-headers.js +0 -0
- /package/dist/{pi → sdk}/ai/providers/openai-completions.js +0 -0
- /package/dist/{pi → sdk}/ai/providers/openai-prompt-cache.js +0 -0
- /package/dist/{pi → sdk}/ai/providers/register-builtins.js +0 -0
- /package/dist/{pi → sdk}/ai/providers/simple-options.js +0 -0
- /package/dist/{pi → sdk}/ai/providers/transform-messages.js +0 -0
- /package/dist/{pi → sdk}/ai/session-resources.js +0 -0
- /package/dist/{pi → sdk}/ai/stream.js +0 -0
- /package/dist/{pi → sdk}/ai/types.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/diagnostics.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/event-stream.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/hash.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/headers.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/json-parse.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/node-http-proxy.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/oauth/anthropic.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/oauth/device-code.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/oauth/github-copilot.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/oauth/index.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/oauth/oauth-page.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/oauth/openai-codex.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/oauth/pkce.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/oauth/types.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/overflow.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/sanitize-unicode.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/typebox-helpers.js +0 -0
- /package/dist/{pi → sdk}/ai/utils/validation.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/bun/cli.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/bun/restore-sandbox-env.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/cli/file-processor.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/cli/initial-message.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/cli.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/agent-session-runtime.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/agent-session-services.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/auth-guidance.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/auth-storage.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/bash-executor.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/compaction/branch-summarization.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/compaction/index.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/compaction/utils.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/defaults.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/diagnostics.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/event-bus.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/exec.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/extensions/index.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/extensions/types.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/extensions/wrapper.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/footer-data-provider.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/http-dispatcher.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/index.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/messages.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/model-resolver.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/output-guard.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/prompt-templates.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/provider-display-names.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/resolve-config-value.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/resource-loader.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/session-cwd.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/skills.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/slash-commands.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/source-info.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/system-prompt.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/timings.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/tools/edit-diff.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/tools/file-mutation-queue.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/tools/index.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/tools/output-accumulator.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/tools/path-utils.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/tools/tool-definition-wrapper.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/core/tools/truncate.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/index.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/main.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/modes/index.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/modes/interactive/components/diff.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/modes/interactive/interactive-mode.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/modes/print-mode.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/modes/rpc/jsonl.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-client.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-mode.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/modes/rpc/rpc-types.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/ansi.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/changelog.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/child-process.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/clipboard-image.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/clipboard-native.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/clipboard.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/exif-orientation.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/frontmatter.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/fs-watch.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/git.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/html.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/image-convert.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/image-resize.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/mime.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/paths.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/photon.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/pi-user-agent.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/shell.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/sleep.js +0 -0
- /package/dist/{pi → sdk}/coding-agent/utils/syntax-highlight.js +0 -0
package/dist/agent/agents.js
CHANGED
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
* System prompt for the agent goes here.
|
|
13
13
|
*
|
|
14
14
|
* Locations:
|
|
15
|
-
* ~/.
|
|
16
|
-
* .
|
|
15
|
+
* ~/.spectral/agent/agents/*.md — User-level (always loaded)
|
|
16
|
+
* .spectral/agents/*.md — Project-level (opt-in via agentScope)
|
|
17
17
|
*/
|
|
18
18
|
import * as fs from "node:fs";
|
|
19
19
|
import * as path from "node:path";
|
|
20
|
-
import { getAgentDir, parseFrontmatter } from "../
|
|
20
|
+
import { getAgentDir, parseFrontmatter } from "../sdk/coding-agent/index.js";
|
|
21
21
|
function loadAgentsFromDir(dir, source) {
|
|
22
22
|
const agents = [];
|
|
23
23
|
if (!fs.existsSync(dir)) {
|
|
@@ -74,7 +74,7 @@ function isDirectory(p) {
|
|
|
74
74
|
function findNearestProjectAgentsDir(cwd) {
|
|
75
75
|
let currentDir = cwd;
|
|
76
76
|
while (true) {
|
|
77
|
-
const candidate = path.join(currentDir, ".
|
|
77
|
+
const candidate = path.join(currentDir, ".spectral", "agents");
|
|
78
78
|
if (isDirectory(candidate))
|
|
79
79
|
return candidate;
|
|
80
80
|
const parentDir = path.dirname(currentDir);
|
package/dist/agent/index.js
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
* - Chain: { chain: [{ agent, task: "... {previous} ..." }, ...] }
|
|
14
14
|
*
|
|
15
15
|
* Agent definitions live as markdown files with YAML frontmatter:
|
|
16
|
-
* - ~/.
|
|
17
|
-
* - .
|
|
16
|
+
* - ~/.spectral/agent/agents/*.md (user-level, always loaded)
|
|
17
|
+
* - .spectral/agents/*.md (project-level, opt-in via agentScope)
|
|
18
18
|
*
|
|
19
19
|
* Subagents use an in-process `agentLoop()` for:
|
|
20
20
|
* - Clean context isolation (fresh messages array)
|
|
@@ -24,16 +24,15 @@
|
|
|
24
24
|
* - Signal propagation (abort passthrough)
|
|
25
25
|
* - Access to observational memory via the recall tool
|
|
26
26
|
*/
|
|
27
|
-
import { agentLoop } from "../
|
|
28
|
-
import { StringEnum } from "../
|
|
29
|
-
import { Text } from "../pi/tui/index.js";
|
|
27
|
+
import { agentLoop } from "../sdk/agent-core/index.js";
|
|
28
|
+
import { StringEnum } from "../sdk/ai/index.js";
|
|
30
29
|
import { Type } from "typebox";
|
|
31
30
|
import { discoverAgents } from "./agents.js";
|
|
32
31
|
// ---------------------------------------------------------------------------
|
|
33
32
|
// Built-in tool resolution (no spawn needed — tools are registered in-process)
|
|
34
33
|
// ---------------------------------------------------------------------------
|
|
35
|
-
import { createTool, allToolNames } from "../
|
|
36
|
-
import { wrapToolDefinition } from "../
|
|
34
|
+
import { createTool, allToolNames } from "../sdk/coding-agent/core/tools/index.js";
|
|
35
|
+
import { wrapToolDefinition } from "../sdk/coding-agent/core/tools/tool-definition-wrapper.js";
|
|
37
36
|
// ---------------------------------------------------------------------------
|
|
38
37
|
// Constants
|
|
39
38
|
// ---------------------------------------------------------------------------
|
|
@@ -235,17 +234,19 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
|
|
|
235
234
|
agent: agentName,
|
|
236
235
|
agentSource: agent.source,
|
|
237
236
|
task,
|
|
238
|
-
exitCode: 0
|
|
237
|
+
exitCode: -1, // -1 = still running; 0 = success; 1 = error
|
|
239
238
|
messages: [],
|
|
240
239
|
stderr: "",
|
|
241
240
|
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
242
241
|
model: model.id,
|
|
243
242
|
step,
|
|
244
243
|
};
|
|
244
|
+
// Accumulated streaming text from text_delta events (reset per assistant turn).
|
|
245
|
+
let streamingText = "";
|
|
245
246
|
const emitUpdate = () => {
|
|
246
247
|
if (onUpdate) {
|
|
247
248
|
onUpdate({
|
|
248
|
-
content: [{ type: "text", text: getFinalOutput(currentResult.messages) || "(running...)" }],
|
|
249
|
+
content: [{ type: "text", text: getFinalOutput(currentResult.messages) || streamingText || "(running...)" }],
|
|
249
250
|
details: makeDetails([currentResult]),
|
|
250
251
|
});
|
|
251
252
|
}
|
|
@@ -271,10 +272,22 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
|
|
|
271
272
|
hadError = true;
|
|
272
273
|
break;
|
|
273
274
|
}
|
|
275
|
+
if (event.type === "message_update" && event.message?.role === "assistant") {
|
|
276
|
+
// Forward streaming text deltas so the UI can show the subagent's
|
|
277
|
+
// response as it's generated, not just the final result.
|
|
278
|
+
const inner = event.assistantMessageEvent;
|
|
279
|
+
if (inner?.type === "text_delta" && inner.delta) {
|
|
280
|
+
streamingText += inner.delta;
|
|
281
|
+
emitUpdate();
|
|
282
|
+
}
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
274
285
|
if (event.type === "message_end" && event.message) {
|
|
275
286
|
const msg = event.message;
|
|
276
287
|
collectedMessages.push(msg);
|
|
277
288
|
if (msg.role === "assistant") {
|
|
289
|
+
// Reset streaming accumulator — final text is now in messages
|
|
290
|
+
streamingText = "";
|
|
278
291
|
currentResult.usage.turns++;
|
|
279
292
|
const usage = msg.usage;
|
|
280
293
|
if (usage) {
|
|
@@ -373,8 +386,8 @@ export default function subagentExtension(pi) {
|
|
|
373
386
|
description: [
|
|
374
387
|
"Delegate tasks to specialized subagents with isolated context.",
|
|
375
388
|
"Modes: single (agent + task), parallel (tasks array), chain (sequential with {previous} placeholder).",
|
|
376
|
-
'Default agent scope is "user" (from ~/.
|
|
377
|
-
'To enable project-local agents in .
|
|
389
|
+
'Default agent scope is "user" (from ~/.spectral/agent/agents).',
|
|
390
|
+
'To enable project-local agents in .spectral/agents, set agentScope: "both" (or "project").',
|
|
378
391
|
].join(" "),
|
|
379
392
|
promptSnippet: "Run a subagent to investigate, plan, review, or implement code changes",
|
|
380
393
|
promptGuidelines: [
|
|
@@ -600,142 +613,5 @@ export default function subagentExtension(pi) {
|
|
|
600
613
|
details: makeDetails("single")([]),
|
|
601
614
|
};
|
|
602
615
|
},
|
|
603
|
-
// ── Rendering (headless-compatible: plain text strings, no TUI imports) ──
|
|
604
|
-
renderCall(args, _theme, _context) {
|
|
605
|
-
const scope = args.agentScope ?? "user";
|
|
606
|
-
if (args.chain && args.chain.length > 0) {
|
|
607
|
-
let text = `subagent chain (${args.chain.length} steps) [${scope}]`;
|
|
608
|
-
for (let i = 0; i < Math.min(args.chain.length, 3); i++) {
|
|
609
|
-
const step = args.chain[i];
|
|
610
|
-
const cleanTask = step.task.replace(/\{previous\}/g, "").trim();
|
|
611
|
-
const preview = cleanTask.length > 40 ? `${cleanTask.slice(0, 40)}...` : cleanTask;
|
|
612
|
-
text += `\n ${i + 1}. ${step.agent} ${preview}`;
|
|
613
|
-
}
|
|
614
|
-
if (args.chain.length > 3)
|
|
615
|
-
text += `\n ... +${args.chain.length - 3} more`;
|
|
616
|
-
return new Text(text);
|
|
617
|
-
}
|
|
618
|
-
if (args.tasks && args.tasks.length > 0) {
|
|
619
|
-
let text = `subagent parallel (${args.tasks.length} tasks) [${scope}]`;
|
|
620
|
-
for (const t of args.tasks.slice(0, 3)) {
|
|
621
|
-
const preview = t.task.length > 40 ? `${t.task.slice(0, 40)}...` : t.task;
|
|
622
|
-
text += `\n ${t.agent} ${preview}`;
|
|
623
|
-
}
|
|
624
|
-
if (args.tasks.length > 3)
|
|
625
|
-
text += `\n ... +${args.tasks.length - 3} more`;
|
|
626
|
-
return new Text(text);
|
|
627
|
-
}
|
|
628
|
-
const agentName = args.agent || "...";
|
|
629
|
-
const preview = args.task
|
|
630
|
-
? args.task.length > 60 ? `${args.task.slice(0, 60)}...` : args.task
|
|
631
|
-
: "...";
|
|
632
|
-
return new Text(`subagent ${agentName} [${scope}]\n ${preview}`);
|
|
633
|
-
},
|
|
634
|
-
renderResult(result, _opts, _theme, _context) {
|
|
635
|
-
const details = result.details;
|
|
636
|
-
if (!details || details.results.length === 0) {
|
|
637
|
-
const text = result.content[0];
|
|
638
|
-
return new Text(text?.type === "text" ? text.text : "(no output)");
|
|
639
|
-
}
|
|
640
|
-
const lines = [];
|
|
641
|
-
if (details.mode === "single" && details.results.length === 1) {
|
|
642
|
-
const r = details.results[0];
|
|
643
|
-
const isError = r.exitCode !== 0 || r.stopReason === "error" || r.stopReason === "aborted";
|
|
644
|
-
const icon = isError ? "✗" : "✓";
|
|
645
|
-
const finalOutput = getFinalOutput(r.messages);
|
|
646
|
-
lines.push(`${icon} ${r.agent} (${r.agentSource})${isError && r.stopReason ? ` [${r.stopReason}]` : ""}`);
|
|
647
|
-
if (isError && r.errorMessage)
|
|
648
|
-
lines.push(`Error: ${r.errorMessage}`);
|
|
649
|
-
lines.push("");
|
|
650
|
-
lines.push("─── Task ───");
|
|
651
|
-
lines.push(r.task);
|
|
652
|
-
lines.push("");
|
|
653
|
-
lines.push("─── Output ───");
|
|
654
|
-
if (!finalOutput) {
|
|
655
|
-
lines.push("(no output)");
|
|
656
|
-
}
|
|
657
|
-
else {
|
|
658
|
-
lines.push(finalOutput.trim());
|
|
659
|
-
}
|
|
660
|
-
const usageStr = formatUsageStats(r.usage, r.model);
|
|
661
|
-
if (usageStr) {
|
|
662
|
-
lines.push("");
|
|
663
|
-
lines.push(usageStr);
|
|
664
|
-
}
|
|
665
|
-
return new Text(lines.join("\n"));
|
|
666
|
-
}
|
|
667
|
-
const aggregateUsage = (results) => {
|
|
668
|
-
const total = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, turns: 0 };
|
|
669
|
-
for (const r of results) {
|
|
670
|
-
total.input += r.usage.input;
|
|
671
|
-
total.output += r.usage.output;
|
|
672
|
-
total.cacheRead += r.usage.cacheRead;
|
|
673
|
-
total.cacheWrite += r.usage.cacheWrite;
|
|
674
|
-
total.cost += r.usage.cost;
|
|
675
|
-
total.turns += r.usage.turns;
|
|
676
|
-
}
|
|
677
|
-
return total;
|
|
678
|
-
};
|
|
679
|
-
if (details.mode === "chain") {
|
|
680
|
-
const successCount = details.results.filter((r) => r.exitCode === 0).length;
|
|
681
|
-
const icon = successCount === details.results.length ? "✓" : "✗";
|
|
682
|
-
lines.push(`${icon} chain ${successCount}/${details.results.length} steps`);
|
|
683
|
-
for (const r of details.results) {
|
|
684
|
-
const rIcon = r.exitCode === 0 ? "✓" : "✗";
|
|
685
|
-
const finalOutput = getFinalOutput(r.messages);
|
|
686
|
-
lines.push("");
|
|
687
|
-
lines.push(`─── Step ${r.step}: ${r.agent} ${rIcon}`);
|
|
688
|
-
lines.push(`Task: ${r.task}`);
|
|
689
|
-
if (finalOutput)
|
|
690
|
-
lines.push(finalOutput.trim());
|
|
691
|
-
else
|
|
692
|
-
lines.push("(no output)");
|
|
693
|
-
const stepUsage = formatUsageStats(r.usage, r.model);
|
|
694
|
-
if (stepUsage)
|
|
695
|
-
lines.push(stepUsage);
|
|
696
|
-
}
|
|
697
|
-
const usageStr = formatUsageStats(aggregateUsage(details.results));
|
|
698
|
-
if (usageStr) {
|
|
699
|
-
lines.push("");
|
|
700
|
-
lines.push(`Total: ${usageStr}`);
|
|
701
|
-
}
|
|
702
|
-
return new Text(lines.join("\n"));
|
|
703
|
-
}
|
|
704
|
-
if (details.mode === "parallel") {
|
|
705
|
-
const running = details.results.filter((r) => r.exitCode === -1).length;
|
|
706
|
-
const successCount = details.results.filter((r) => r.exitCode === 0).length;
|
|
707
|
-
const failCount = details.results.filter((r) => r.exitCode > 0).length;
|
|
708
|
-
const isRunning = running > 0;
|
|
709
|
-
const icon = isRunning ? "⏳" : failCount > 0 ? "◐" : "✓";
|
|
710
|
-
const status = isRunning
|
|
711
|
-
? `${successCount + failCount}/${details.results.length} done, ${running} running`
|
|
712
|
-
: `${successCount}/${details.results.length} tasks`;
|
|
713
|
-
lines.push(`${icon} parallel ${status}`);
|
|
714
|
-
for (const r of details.results) {
|
|
715
|
-
const rIcon = r.exitCode === -1 ? "⏳" : r.exitCode === 0 ? "✓" : "✗";
|
|
716
|
-
const finalOutput = getFinalOutput(r.messages);
|
|
717
|
-
lines.push("");
|
|
718
|
-
lines.push(`─── ${r.agent} ${rIcon}`);
|
|
719
|
-
lines.push(`Task: ${r.task}`);
|
|
720
|
-
if (finalOutput)
|
|
721
|
-
lines.push(finalOutput.trim());
|
|
722
|
-
else
|
|
723
|
-
lines.push(r.exitCode === -1 ? "(running...)" : "(no output)");
|
|
724
|
-
const taskUsage = formatUsageStats(r.usage, r.model);
|
|
725
|
-
if (taskUsage)
|
|
726
|
-
lines.push(taskUsage);
|
|
727
|
-
}
|
|
728
|
-
if (!isRunning) {
|
|
729
|
-
const usageStr = formatUsageStats(aggregateUsage(details.results));
|
|
730
|
-
if (usageStr) {
|
|
731
|
-
lines.push("");
|
|
732
|
-
lines.push(`Total: ${usageStr}`);
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
|
-
return new Text(lines.join("\n"));
|
|
736
|
-
}
|
|
737
|
-
const text = result.content[0];
|
|
738
|
-
return new Text(text?.type === "text" ? text.text : "(no output)");
|
|
739
|
-
},
|
|
740
616
|
});
|
|
741
617
|
}
|
package/dist/cli.js
CHANGED
|
@@ -2,120 +2,26 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* @aexol/spectral — Coding agent that never sleeps
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Delegation strategy: SUBPROCESS spawn of pi's bin.
|
|
8
|
-
*
|
|
9
|
-
* Why subprocess and not in-process import?
|
|
10
|
-
* - pi's CLI entry has top-level side effects (TUI bootstrap, signal handlers,
|
|
11
|
-
* raw stdin mode). Running it in-process means our wrapper process owns those,
|
|
12
|
-
* and any future pre-processing we add (skills, system prompts) becomes
|
|
13
|
-
* entangled with pi's lifecycle.
|
|
14
|
-
* - A child process gives us clean exit-code propagation, clean SIGINT
|
|
15
|
-
* forwarding, and a stable boundary for future iterations to inject things
|
|
16
|
-
* like extra args, env vars, or post-run hooks without monkey-patching pi.
|
|
17
|
-
* - stdio: "inherit" gives pi direct TTY access, so its TUI renders correctly
|
|
18
|
-
* and raw stdin works as expected.
|
|
5
|
+
* Headless CLI wrapper — no TUI mode supported.
|
|
19
6
|
*
|
|
20
7
|
* Subcommand routing:
|
|
21
8
|
* - `spectral login` → interactive auth flow (writes ~/.spectral/config.json)
|
|
22
9
|
* - `spectral logout` → deletes ~/.spectral/config.json
|
|
10
|
+
* - `spectral serve` → start the Aexol relay WebSocket server (headless)
|
|
11
|
+
* - `spectral bind` → link directory to an Aexol Studio project
|
|
12
|
+
* - `spectral unbind` → remove Aexol Studio project binding
|
|
23
13
|
* - `spectral --version` / `--help` → branded short-circuits, no auth needed.
|
|
24
|
-
* - anything else →
|
|
25
|
-
* with the bundled Aexol MCP extension auto-loaded.
|
|
14
|
+
* - anything else → error with usage hint.
|
|
26
15
|
*/
|
|
27
|
-
import {
|
|
28
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
29
|
-
import { constants as osConstants } from "node:os";
|
|
16
|
+
import { readFileSync } from "node:fs";
|
|
30
17
|
import { dirname, resolve } from "node:path";
|
|
31
18
|
import { fileURLToPath } from "node:url";
|
|
32
|
-
import { requireLogin } from "./preflight.js";
|
|
33
19
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
34
20
|
// ---- Read our own version ----------------------------------------------------
|
|
35
21
|
const pkgPath = resolve(__dirname, "..", "package.json");
|
|
36
22
|
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
37
23
|
const VERSION = pkg.version;
|
|
38
24
|
const TAGLINE = "Coding agent that never sleeps";
|
|
39
|
-
// ---- Resolve pi's bin --------------------------------------------------------
|
|
40
|
-
// pi exports `.` as ESM only and does not export `./package.json`, so
|
|
41
|
-
// require.resolve(...) is unreliable across Node versions. Instead we walk
|
|
42
|
-
// upward from this file's location looking for a `node_modules/<pi>/package.json`.
|
|
43
|
-
function resolvePiBin() {
|
|
44
|
-
const piPkgRel = "node_modules/@mariozechner/pi-coding-agent/package.json";
|
|
45
|
-
let dir = __dirname;
|
|
46
|
-
let piPkgJsonPath;
|
|
47
|
-
for (let i = 0; i < 20; i++) {
|
|
48
|
-
const candidate = resolve(dir, piPkgRel);
|
|
49
|
-
try {
|
|
50
|
-
readFileSync(candidate, "utf8");
|
|
51
|
-
piPkgJsonPath = candidate;
|
|
52
|
-
break;
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
55
|
-
/* keep walking */
|
|
56
|
-
}
|
|
57
|
-
const parent = dirname(dir);
|
|
58
|
-
if (parent === dir)
|
|
59
|
-
break;
|
|
60
|
-
dir = parent;
|
|
61
|
-
}
|
|
62
|
-
if (!piPkgJsonPath) {
|
|
63
|
-
throw new Error("Unable to locate @mariozechner/pi-coding-agent in any ancestor node_modules.");
|
|
64
|
-
}
|
|
65
|
-
const piPkg = JSON.parse(readFileSync(piPkgJsonPath, "utf8"));
|
|
66
|
-
let binRel;
|
|
67
|
-
if (typeof piPkg.bin === "string") {
|
|
68
|
-
binRel = piPkg.bin;
|
|
69
|
-
}
|
|
70
|
-
else if (piPkg.bin && typeof piPkg.bin === "object") {
|
|
71
|
-
binRel = piPkg.bin.pi ?? Object.values(piPkg.bin)[0];
|
|
72
|
-
}
|
|
73
|
-
if (!binRel) {
|
|
74
|
-
throw new Error("Unable to locate pi bin in @mariozechner/pi-coding-agent package.json");
|
|
75
|
-
}
|
|
76
|
-
return resolve(dirname(piPkgJsonPath), binRel);
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Resolve an extension entry-point path, falling back from .js to .ts.
|
|
80
|
-
*
|
|
81
|
-
* In production __dirname is dist/ and compiled .js files exist. In dev
|
|
82
|
-
* (tsx src/cli.ts) __dirname is src/ and only .ts sources are present.
|
|
83
|
-
* pi's extension loader uses jiti which handles .ts transparently.
|
|
84
|
-
*/
|
|
85
|
-
function resolveExtensionEntry(relativePath, fileName) {
|
|
86
|
-
const dir = resolve(__dirname, relativePath);
|
|
87
|
-
const jsPath = resolve(dir, fileName + ".js");
|
|
88
|
-
if (existsSync(jsPath))
|
|
89
|
-
return jsPath;
|
|
90
|
-
const tsPath = resolve(dir, fileName + ".ts");
|
|
91
|
-
if (existsSync(tsPath))
|
|
92
|
-
return tsPath;
|
|
93
|
-
return jsPath; // let the missing-file check produce a clear error
|
|
94
|
-
}
|
|
95
|
-
/** Absolute path to the bundled aexol-mcp extension. */
|
|
96
|
-
function resolveAexolExtensionPath() {
|
|
97
|
-
return resolveExtensionEntry("extensions", "aexol-mcp");
|
|
98
|
-
}
|
|
99
|
-
/** Absolute path to the bundled observations-memory extension. */
|
|
100
|
-
function resolveObservationalMemoryPath() {
|
|
101
|
-
return resolveExtensionEntry("memory", "index");
|
|
102
|
-
}
|
|
103
|
-
/** Absolute path to the bundled pi-mcp-adapter extension. */
|
|
104
|
-
function resolveMcpExtensionPath() {
|
|
105
|
-
return resolveExtensionEntry("mcp", "index");
|
|
106
|
-
}
|
|
107
|
-
/** Absolute path to the bundled openrouter-attribution extension. */
|
|
108
|
-
function resolveOpenRouterAttributionPath() {
|
|
109
|
-
return resolveExtensionEntry("extensions", "openrouter-attribution");
|
|
110
|
-
}
|
|
111
|
-
/** Absolute path to the bundled agent subagent extension. */
|
|
112
|
-
function resolveAgentExtensionPath() {
|
|
113
|
-
return resolveExtensionEntry("agent", "index");
|
|
114
|
-
}
|
|
115
|
-
/** Absolute path to the bundled designer extension. */
|
|
116
|
-
function resolveDesignerExtensionPath() {
|
|
117
|
-
return resolveExtensionEntry("designer", "index");
|
|
118
|
-
}
|
|
119
25
|
// ---- Branded helpers ---------------------------------------------------------
|
|
120
26
|
function printVersion() {
|
|
121
27
|
process.stdout.write(`spectral ${VERSION} — ${TAGLINE}\n`);
|
|
@@ -128,77 +34,29 @@ function printHeader() {
|
|
|
128
34
|
"Subcommands:",
|
|
129
35
|
" spectral login Interactive authentication (team API key or OAuth)",
|
|
130
36
|
" spectral logout Remove stored Aexol credentials",
|
|
131
|
-
" spectral serve
|
|
132
|
-
" spectral bind
|
|
133
|
-
" spectral unbind
|
|
37
|
+
" spectral serve Connect this machine to the Aexol relay backend",
|
|
38
|
+
" spectral bind Link this directory to an Aexol Studio project",
|
|
39
|
+
" spectral unbind Remove the Aexol Studio project binding",
|
|
40
|
+
" spectral --version Print version and exit",
|
|
41
|
+
" spectral --help Print this help and exit",
|
|
134
42
|
"",
|
|
135
|
-
"
|
|
136
|
-
"
|
|
43
|
+
"TUI/interactive mode has been removed. Use `spectral serve` and connect",
|
|
44
|
+
"via the Aexol Studio web frontend or relay WebSocket.",
|
|
137
45
|
"",
|
|
138
46
|
].join("\n"));
|
|
139
47
|
}
|
|
140
|
-
// ---- Delegate to pi ----------------------------------------------------------
|
|
141
|
-
function delegateToPi(args) {
|
|
142
|
-
const piBin = resolvePiBin();
|
|
143
|
-
// The subprocess inherits a snapshot of process.env (which already has
|
|
144
|
-
// all interactive-editor vars disabled by disableInteractiveGitEditors()),
|
|
145
|
-
// but we pass it explicitly anyway as defense-in-depth in case the user
|
|
146
|
-
// overrides any of these in their shell rc files before launching spectral.
|
|
147
|
-
const child = spawn(process.execPath, [piBin, ...args], {
|
|
148
|
-
stdio: "inherit",
|
|
149
|
-
env: {
|
|
150
|
-
...process.env,
|
|
151
|
-
GIT_EDITOR: "true",
|
|
152
|
-
GIT_SEQUENCE_EDITOR: "true",
|
|
153
|
-
EDITOR: "true",
|
|
154
|
-
VISUAL: "true",
|
|
155
|
-
GIT_PAGER: "cat",
|
|
156
|
-
},
|
|
157
|
-
});
|
|
158
|
-
// Forward common termination signals to pi so its TUI can clean up.
|
|
159
|
-
const signals = ["SIGINT", "SIGTERM", "SIGHUP", "SIGQUIT"];
|
|
160
|
-
for (const sig of signals) {
|
|
161
|
-
process.on(sig, () => {
|
|
162
|
-
if (!child.killed) {
|
|
163
|
-
try {
|
|
164
|
-
child.kill(sig);
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
167
|
-
/* ignore */
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
child.on("exit", (code, signal) => {
|
|
173
|
-
if (signal) {
|
|
174
|
-
const sigNum = osConstants.signals[signal] ?? 0;
|
|
175
|
-
process.exit(128 + sigNum);
|
|
176
|
-
}
|
|
177
|
-
process.exit(code ?? 0);
|
|
178
|
-
});
|
|
179
|
-
child.on("error", (err) => {
|
|
180
|
-
process.stderr.write(`spectral: failed to launch pi: ${err.message}\n`);
|
|
181
|
-
process.exit(1);
|
|
182
|
-
});
|
|
183
|
-
// spawn returns; keep the type system happy.
|
|
184
|
-
// We never actually reach here because of the exit handlers above, but
|
|
185
|
-
// TypeScript needs a `never` terminator.
|
|
186
|
-
return undefined;
|
|
187
|
-
}
|
|
188
48
|
// ---- Disable interactive git editors ----------------------------------------
|
|
189
|
-
// Set at process level
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
49
|
+
// Set at process level for serve mode (AgentBridge in-process → getShellEnv()
|
|
50
|
+
// returns process.env). Without this, `git rebase --continue`, `git commit`
|
|
51
|
+
// (without -m), `git merge`, etc. open an editor that hangs forever — the
|
|
52
|
+
// agent's bash tool runs with `stdio: ["ignore", ...]`, so there is no TTY
|
|
53
|
+
// to interact with.
|
|
194
54
|
function disableInteractiveGitEditors() {
|
|
195
55
|
const editorVars = {
|
|
196
56
|
GIT_EDITOR: "true",
|
|
197
57
|
GIT_SEQUENCE_EDITOR: "true",
|
|
198
58
|
EDITOR: "true",
|
|
199
59
|
VISUAL: "true",
|
|
200
|
-
// cat pipes output straight through; prevents git's pager (less) from
|
|
201
|
-
// blocking on a non-existent TTY.
|
|
202
60
|
GIT_PAGER: "cat",
|
|
203
61
|
};
|
|
204
62
|
for (const [key, value] of Object.entries(editorVars)) {
|
|
@@ -209,7 +67,6 @@ function disableInteractiveGitEditors() {
|
|
|
209
67
|
}
|
|
210
68
|
// ---- Main --------------------------------------------------------------------
|
|
211
69
|
async function main() {
|
|
212
|
-
// Must be called before any pi process is spawned (direct or serve).
|
|
213
70
|
disableInteractiveGitEditors();
|
|
214
71
|
const args = process.argv.slice(2);
|
|
215
72
|
const first = args[0];
|
|
@@ -220,12 +77,7 @@ async function main() {
|
|
|
220
77
|
}
|
|
221
78
|
if (first === "--help" || first === "-h") {
|
|
222
79
|
printHeader();
|
|
223
|
-
|
|
224
|
-
// annotated `: never` because its child.on("exit") handler calls
|
|
225
|
-
// process.exit, but the function itself returns synchronously after
|
|
226
|
-
// spawn — so we must not fall through to the pre-flight check below.
|
|
227
|
-
delegateToPi(args);
|
|
228
|
-
return;
|
|
80
|
+
process.exit(0);
|
|
229
81
|
}
|
|
230
82
|
// Subcommands. Dynamic import keeps the cold-start path light when users
|
|
231
83
|
// are just running pi.
|
|
@@ -257,59 +109,12 @@ async function main() {
|
|
|
257
109
|
await runUnbind();
|
|
258
110
|
process.exit(0);
|
|
259
111
|
}
|
|
260
|
-
//
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
const aexolExtPath = resolveAexolExtensionPath();
|
|
267
|
-
if (!existsSync(aexolExtPath)) {
|
|
268
|
-
process.stderr.write(`spectral: bundled Aexol MCP extension not found at ${aexolExtPath}. This is a packaging bug.\n`);
|
|
269
|
-
process.exit(1);
|
|
270
|
-
}
|
|
271
|
-
// Bundled pi-mcp-adapter extension for standard MCP server support
|
|
272
|
-
// (stdio + SSE/HTTP transports, lazy loading, OAuth, /mcp panel).
|
|
273
|
-
const mcpExtPath = resolveMcpExtensionPath();
|
|
274
|
-
if (!existsSync(mcpExtPath)) {
|
|
275
|
-
process.stderr.write(`spectral: bundled MCP extension not found at ${mcpExtPath}. This is a packaging bug.\n`);
|
|
276
|
-
process.exit(1);
|
|
277
|
-
}
|
|
278
|
-
// Bundled observational memory extension for tiered agent memory
|
|
279
|
-
// (observer → reflector → pruner pipeline with /om-status, /om-view, recall tool).
|
|
280
|
-
const obsMemPath = resolveObservationalMemoryPath();
|
|
281
|
-
if (!existsSync(obsMemPath)) {
|
|
282
|
-
process.stderr.write(`spectral: bundled observational-memory extension not found at ${obsMemPath}. This is a packaging bug.\n`);
|
|
283
|
-
process.exit(1);
|
|
284
|
-
}
|
|
285
|
-
// OpenRouter attribution extension – adds headers so Aexol appears in OpenRouter rankings
|
|
286
|
-
const openrouterAttrPath = resolveOpenRouterAttributionPath();
|
|
287
|
-
if (!existsSync(openrouterAttrPath)) {
|
|
288
|
-
process.stderr.write(`spectral: bundled OpenRouter attribution extension not found at ${openrouterAttrPath}. This is a packaging bug.\n`);
|
|
289
|
-
process.exit(1);
|
|
290
|
-
}
|
|
291
|
-
// Bundled agent delegation extension for subagent task delegation
|
|
292
|
-
const agentExtPath = resolveAgentExtensionPath();
|
|
293
|
-
if (!existsSync(agentExtPath)) {
|
|
294
|
-
process.stderr.write(`spectral: bundled agent extension not found at ${agentExtPath}. This is a packaging bug.\n`);
|
|
295
|
-
process.exit(1);
|
|
296
|
-
}
|
|
297
|
-
// Bundled designer extension for design tasks (prototypes, slide decks, mobile apps, etc.)
|
|
298
|
-
const designerExtPath = resolveDesignerExtensionPath();
|
|
299
|
-
if (!existsSync(designerExtPath)) {
|
|
300
|
-
process.stderr.write(`spectral: bundled designer extension not found at ${designerExtPath}. This is a packaging bug.\n`);
|
|
301
|
-
process.exit(1);
|
|
302
|
-
}
|
|
303
|
-
const extFlags = [
|
|
304
|
-
"--extension", aexolExtPath,
|
|
305
|
-
"--extension", mcpExtPath,
|
|
306
|
-
"--extension", obsMemPath,
|
|
307
|
-
"--extension", openrouterAttrPath,
|
|
308
|
-
"--extension", agentExtPath,
|
|
309
|
-
"--extension", designerExtPath,
|
|
310
|
-
];
|
|
311
|
-
const finalArgs = [...extFlags, ...args];
|
|
312
|
-
delegateToPi(finalArgs);
|
|
112
|
+
// No TUI/interactive mode is supported. All other invocations must use
|
|
113
|
+
// explicit subcommands.
|
|
114
|
+
process.stderr.write(`spectral: unknown command "${first ?? ""}".\n` +
|
|
115
|
+
`Use "spectral --help" for available subcommands.\n` +
|
|
116
|
+
`To connect this machine, run: spectral serve\n`);
|
|
117
|
+
process.exit(1);
|
|
313
118
|
}
|
|
314
119
|
main().catch((err) => {
|
|
315
120
|
const msg = err instanceof Error ? err.message : String(err);
|
package/dist/commands/serve.js
CHANGED
|
@@ -146,7 +146,7 @@ export async function runServe(opts = {}) {
|
|
|
146
146
|
const backendUrl = opts.backendUrlOverride ?? process.env.SPECTRAL_BACKEND_URL ?? DEFAULT_BACKEND_URL;
|
|
147
147
|
const relayUrl = opts.relayUrlOverride ?? process.env.SPECTRAL_RELAY_URL ?? deriveRelayUrl(backendUrl);
|
|
148
148
|
// Register BEFORE constructing the SessionStreamManager. The manager
|
|
149
|
-
// (and every
|
|
149
|
+
// (and every AgentBridge it spawns) needs the machine JWT to authenticate
|
|
150
150
|
// backend-proxied inference calls, so registration must succeed first.
|
|
151
151
|
// Fail-fast on error — a clear message beats a hang.
|
|
152
152
|
let registration;
|