@aexol/spectral 0.9.20 → 0.9.24

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.
@@ -6,6 +6,7 @@
6
6
  * ---
7
7
  * name: my-agent
8
8
  * description: What this agent does
9
+ * mode: subagent # or "primary" to inject into the main system prompt
9
10
  * tools: read, grep, find, ls
10
11
  * model: claude-haiku-4-5
11
12
  * ---
@@ -16,9 +17,11 @@
16
17
  * .spectral/agents/*.md — Project-level (opt-in via agentScope)
17
18
  */
18
19
  export type AgentScope = "user" | "project" | "both";
20
+ export type AgentMode = "primary" | "subagent";
19
21
  export interface AgentConfig {
20
22
  name: string;
21
23
  description: string;
24
+ mode: AgentMode;
22
25
  tools?: string[];
23
26
  model?: string;
24
27
  systemPrompt: string;
@@ -29,7 +32,20 @@ export interface AgentDiscoveryResult {
29
32
  agents: AgentConfig[];
30
33
  projectAgentsDir: string | null;
31
34
  }
32
- export declare function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryResult;
35
+ export declare function discoverAgents(cwd: string, scope: AgentScope, agentDir?: string): AgentDiscoveryResult;
36
+ export declare function discoverPrimaryAgent(cwd: string, scope?: AgentScope, agentDir?: string): AgentConfig | null;
37
+ /**
38
+ * Prepend the primary agent's system prompt above the base system prompt.
39
+ *
40
+ * Used by the per-turn `before_agent_start` hook so the primary-agent
41
+ * toggle takes effect mid-session (on the next user message) rather than
42
+ * only at session start.
43
+ *
44
+ * - Returns the base unchanged when no primary agent is provided.
45
+ * - Idempotent: if the base already starts with the primary agent header,
46
+ * the base is returned unchanged (guards against double-injection).
47
+ */
48
+ export declare function injectPrimaryAgentPrompt(base: string | undefined, primaryAgent: Pick<AgentConfig, "name" | "description" | "systemPrompt"> | null): string | undefined;
33
49
  export declare function formatAgentList(agents: AgentConfig[], maxItems: number): {
34
50
  text: string;
35
51
  remaining: number;
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/agent/agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAMH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAyED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,oBAAoB,CAoBnF;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,WAAW,EAAE,EACrB,QAAQ,EAAE,MAAM,GACf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAQrC"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/agent/agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AACrD,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAE/C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AA2ED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,UAAU,EACjB,QAAQ,CAAC,EAAE,MAAM,GAChB,oBAAoB,CAoBtB;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,KAAK,GAAE,UAAmB,EAC1B,QAAQ,CAAC,EAAE,MAAM,GAChB,WAAW,GAAG,IAAI,CAgBpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,YAAY,EAAE,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,cAAc,CAAC,GAAG,IAAI,GAC9E,MAAM,GAAG,SAAS,CAOpB;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,WAAW,EAAE,EACrB,QAAQ,EAAE,MAAM,GACf;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAQrC"}
@@ -6,6 +6,7 @@
6
6
  * ---
7
7
  * name: my-agent
8
8
  * description: What this agent does
9
+ * mode: subagent # or "primary" to inject into the main system prompt
9
10
  * tools: read, grep, find, ls
10
11
  * model: claude-haiku-4-5
11
12
  * ---
@@ -17,6 +18,7 @@
17
18
  */
18
19
  import * as fs from "node:fs";
19
20
  import * as path from "node:path";
21
+ import chalk from "chalk";
20
22
  import { getAgentDir, parseFrontmatter } from "../sdk/coding-agent/index.js";
21
23
  function loadAgentsFromDir(dir, source) {
22
24
  const agents = [];
@@ -51,9 +53,11 @@ function loadAgentsFromDir(dir, source) {
51
53
  ?.split(",")
52
54
  .map((t) => t.trim())
53
55
  .filter(Boolean);
56
+ const mode = frontmatter.mode === "primary" ? "primary" : "subagent";
54
57
  agents.push({
55
58
  name: frontmatter.name,
56
59
  description: frontmatter.description,
60
+ mode,
57
61
  tools: tools && tools.length > 0 ? tools : undefined,
58
62
  model: frontmatter.model,
59
63
  systemPrompt: body,
@@ -83,8 +87,8 @@ function findNearestProjectAgentsDir(cwd) {
83
87
  currentDir = parentDir;
84
88
  }
85
89
  }
86
- export function discoverAgents(cwd, scope) {
87
- const userDir = path.join(getAgentDir(), "agents");
90
+ export function discoverAgents(cwd, scope, agentDir) {
91
+ const userDir = path.join(agentDir ?? getAgentDir(), "agents");
88
92
  const projectAgentsDir = findNearestProjectAgentsDir(cwd);
89
93
  const userAgents = scope === "project" ? [] : loadAgentsFromDir(userDir, "user");
90
94
  const projectAgents = scope === "user" || !projectAgentsDir ? [] : loadAgentsFromDir(projectAgentsDir, "project");
@@ -105,6 +109,42 @@ export function discoverAgents(cwd, scope) {
105
109
  }
106
110
  return { agents: Array.from(agentMap.values()), projectAgentsDir };
107
111
  }
112
+ export function discoverPrimaryAgent(cwd, scope = "both", agentDir) {
113
+ const { agents } = discoverAgents(cwd, scope, agentDir);
114
+ const primaries = agents.filter((a) => a.mode === "primary");
115
+ if (primaries.length === 0)
116
+ return null;
117
+ if (primaries.length > 1) {
118
+ // User-level primary wins over project-level primary; otherwise first wins.
119
+ const userPrimary = primaries.find((a) => a.source === "user");
120
+ const chosen = userPrimary ?? primaries[0];
121
+ console.warn(chalk.yellow(`Multiple primary agents found (${primaries.map((a) => a.name).join(", ")}). Using "${chosen.name}".`));
122
+ return chosen;
123
+ }
124
+ return primaries[0];
125
+ }
126
+ /**
127
+ * Prepend the primary agent's system prompt above the base system prompt.
128
+ *
129
+ * Used by the per-turn `before_agent_start` hook so the primary-agent
130
+ * toggle takes effect mid-session (on the next user message) rather than
131
+ * only at session start.
132
+ *
133
+ * - Returns the base unchanged when no primary agent is provided.
134
+ * - Idempotent: if the base already starts with the primary agent header,
135
+ * the base is returned unchanged (guards against double-injection).
136
+ */
137
+ export function injectPrimaryAgentPrompt(base, primaryAgent) {
138
+ if (!primaryAgent)
139
+ return base;
140
+ const header = `# Primary Agent: ${primaryAgent.name}\n\n${primaryAgent.description}`;
141
+ const block = `${header}\n\n${primaryAgent.systemPrompt}`;
142
+ if (!base)
143
+ return block;
144
+ if (base.startsWith(header))
145
+ return base;
146
+ return `${block}\n\n---\n\n${base}`;
147
+ }
108
148
  export function formatAgentList(agents, maxItems) {
109
149
  if (agents.length === 0)
110
150
  return { text: "none", remaining: 0 };
@@ -25,5 +25,7 @@
25
25
  * - Access to observational memory via the recall tool
26
26
  */
27
27
  import { type ExtensionAPI } from "../sdk/coding-agent/index.js";
28
- export default function subagentExtension(ext: ExtensionAPI): void;
28
+ export default function subagentExtension(ext: ExtensionAPI, opts?: {
29
+ disabledAgents?: string[];
30
+ }): void;
29
31
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAWH,OAAO,EACN,KAAK,YAAY,EAEjB,MAAM,8BAA8B,CAAC;AAqetC,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,GAAG,EAAE,YAAY,GAAG,IAAI,CAqTjE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/agent/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAWH,OAAO,EACN,KAAK,YAAY,EAEjB,MAAM,8BAA8B,CAAC;AAuetC,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CA+UvG"}
@@ -28,6 +28,7 @@ import { agentLoop } from "../sdk/agent-core/index.js";
28
28
  import { StringEnum } from "../sdk/ai/index.js";
29
29
  import { Type } from "typebox";
30
30
  import { discoverAgents } from "./agents.js";
31
+ import { loadAgentSettings } from "../server/handlers/agent-settings.js";
31
32
  // ---------------------------------------------------------------------------
32
33
  // Built-in tool resolution (no spawn needed — tools are registered in-process)
33
34
  // ---------------------------------------------------------------------------
@@ -175,7 +176,8 @@ async function mapWithConcurrencyLimit(items, concurrency, fn) {
175
176
  // Core — run a single subagent in-process via agentLoop
176
177
  // ---------------------------------------------------------------------------
177
178
  async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails, ext, ctx) {
178
- const agent = agents.find((a) => a.name === agentName);
179
+ const normalizedAgentName = agentName.replace(/^@/, "");
180
+ const agent = agents.find((a) => a.name === normalizedAgentName);
179
181
  if (!agent) {
180
182
  const available = agents.map((a) => `"${a.name}"`).join(", ") || "none";
181
183
  return {
@@ -355,8 +357,8 @@ const ChainItem = Type.Object({
355
357
  cwd: Type.Optional(Type.String({ description: "Working directory for the agent process" })),
356
358
  });
357
359
  const AgentScopeSchema = StringEnum(["user", "project", "both"], {
358
- description: 'Which agent directories to use. Default: "user". Use "both" to include project-local agents.',
359
- default: "user",
360
+ description: 'Which agent directories to use. Default: "both" (user + project agents).',
361
+ default: "both",
360
362
  });
361
363
  const SubagentParams = Type.Object({
362
364
  agent: Type.Optional(Type.String({ description: "Name of the agent to invoke (for single mode)" })),
@@ -379,15 +381,15 @@ const SubagentParams = Type.Object({
379
381
  // ---------------------------------------------------------------------------
380
382
  // Extension entry point
381
383
  // ---------------------------------------------------------------------------
382
- export default function subagentExtension(ext) {
384
+ export default function subagentExtension(ext, opts) {
383
385
  ext.registerTool({
384
386
  name: "subagent",
385
387
  label: "Subagent",
386
388
  description: [
387
389
  "Delegate tasks to specialized subagents with isolated context.",
388
390
  "Modes: single (agent + task), parallel (tasks array), chain (sequential with {previous} placeholder).",
389
- 'Default agent scope is "user" (from ~/.spectral/agent/agents).',
390
- 'To enable project-local agents in .spectral/agents, set agentScope: "both" (or "project").',
391
+ 'Default agent scope is "both" (user agents from ~/.spectral/agent/agents + project agents from .spectral/agents).',
392
+ 'Set agentScope: "user" or "project" to limit scope.'
391
393
  ].join(" "),
392
394
  promptSnippet: "Run a subagent to investigate, plan, review, or implement code changes",
393
395
  promptGuidelines: [
@@ -395,10 +397,36 @@ export default function subagentExtension(ext) {
395
397
  ],
396
398
  parameters: SubagentParams,
397
399
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
398
- const agentScope = params.agentScope ?? "user";
400
+ // Re-load agent settings from disk on every call so that UI toggles
401
+ // (subagentsEnabled / disabledSubagents) take effect immediately on
402
+ // running sessions, not just on the next session start.
403
+ const liveSettings = loadAgentSettings(ctx.cwd);
404
+ if (!liveSettings.subagentsEnabled) {
405
+ return {
406
+ content: [
407
+ {
408
+ type: "text",
409
+ text: "Subagents are disabled in agent settings. Enable subagents via the agent settings panel to delegate tasks.",
410
+ },
411
+ ],
412
+ details: {
413
+ mode: "single",
414
+ agentScope: (params.agentScope ?? "both"),
415
+ projectAgentsDir: null,
416
+ results: [],
417
+ },
418
+ };
419
+ }
420
+ const agentScope = params.agentScope ?? "both";
399
421
  const discovery = discoverAgents(ctx.cwd, agentScope);
400
- const agents = discovery.agents;
401
- const confirmProjectAgents = params.confirmProjectAgents ?? true;
422
+ // Merge registration-time disabled list (from session start) with the
423
+ // live disabled list (just read from disk) so both are honored.
424
+ const disabledSet = new Set([
425
+ ...(opts?.disabledAgents ?? []),
426
+ ...(liveSettings.disabledSubagents ?? []),
427
+ ]);
428
+ const agents = discovery.agents.filter((a) => !disabledSet.has(a.name));
429
+ const confirmProjectAgents = params.confirmProjectAgents ?? false;
402
430
  const hasChain = (params.chain?.length ?? 0) > 0;
403
431
  const hasTasks = (params.tasks?.length ?? 0) > 0;
404
432
  const hasSingle = Boolean(params.agent && params.task);
@@ -430,12 +458,12 @@ export default function subagentExtension(ext) {
430
458
  const requestedAgentNames = new Set();
431
459
  if (params.chain)
432
460
  for (const step of params.chain)
433
- requestedAgentNames.add(step.agent);
461
+ requestedAgentNames.add(step.agent.replace(/^@/, ""));
434
462
  if (params.tasks)
435
463
  for (const t of params.tasks)
436
- requestedAgentNames.add(t.agent);
464
+ requestedAgentNames.add(t.agent.replace(/^@/, ""));
437
465
  if (params.agent)
438
- requestedAgentNames.add(params.agent);
466
+ requestedAgentNames.add(params.agent.replace(/^@/, ""));
439
467
  const projectAgentsRequested = Array.from(requestedAgentNames)
440
468
  .map((name) => agents.find((a) => a.name === name))
441
469
  .filter((a) => a?.source === "project");
package/dist/cli.js CHANGED
@@ -18,9 +18,15 @@ import { dirname, resolve } from "node:path";
18
18
  import { fileURLToPath } from "node:url";
19
19
  const __dirname = dirname(fileURLToPath(import.meta.url));
20
20
  // ---- Read our own version ----------------------------------------------------
21
- const pkgPath = resolve(__dirname, "..", "package.json");
22
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
23
- const VERSION = pkg.version;
21
+ let VERSION = process.env.npm_package_version ?? "0.0.0";
22
+ try {
23
+ const pkgPath = resolve(__dirname, "..", "package.json");
24
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
25
+ VERSION = pkg.version;
26
+ }
27
+ catch {
28
+ // Standalone Bun binaries do not carry the source package.json.
29
+ }
24
30
  const TAGLINE = "Coding agent that never sleeps";
25
31
  // ---- Branded helpers ---------------------------------------------------------
26
32
  function printVersion() {
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAsBH,OAAO,EAAE,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AASlE,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,EAAmB,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAerE;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAazD;AAED,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CA6B9D;AAED,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,uCAAuC;IACvC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4CAA4C;IAC5C,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,qBAAqB,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,cAAc;IAC7B,qCAAqC;IACrC,KAAK,EAAE,YAAY,CAAC;IACpB,wDAAwD;IACxD,OAAO,EAAE,oBAAoB,CAAC;IAC9B,4CAA4C;IAC5C,KAAK,EAAE,WAAW,CAAC;IACnB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAsB,QAAQ,CAAC,IAAI,GAAE,eAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,CAqWlF;AAiED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAa5E"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../src/commands/serve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAsBH,OAAO,EAAE,WAAW,EAAmB,MAAM,oBAAoB,CAAC;AASlE,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,EAAmB,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAsBrE;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAazD;AAED,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CA6B9D;AAED,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,uCAAuC;IACvC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4CAA4C;IAC5C,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,qBAAqB,CAAC,OAAO,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;CAC/E;AAED,MAAM,WAAW,cAAc;IAC7B,qCAAqC;IACrC,KAAK,EAAE,YAAY,CAAC;IACpB,wDAAwD;IACxD,OAAO,EAAE,oBAAoB,CAAC;IAC9B,4CAA4C;IAC5C,KAAK,EAAE,WAAW,CAAC;IACnB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAsB,QAAQ,CAAC,IAAI,GAAE,eAAoB,GAAG,OAAO,CAAC,cAAc,CAAC,CAsWlF;AAiED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAa5E"}
@@ -28,7 +28,7 @@
28
28
  * in-process fake backend.
29
29
  */
30
30
  import { readFileSync } from "node:fs";
31
- import { homedir, hostname } from "node:os";
31
+ import { hostname } from "node:os";
32
32
  import { dirname, join, resolve } from "node:path";
33
33
  import { fileURLToPath } from "node:url";
34
34
  import { getConfigDir } from "../config.js";
@@ -49,9 +49,17 @@ const DEFAULT_BACKEND_URL = "https://api.aexol.ai";
49
49
  function readPackageVersion() {
50
50
  // dist/commands/serve.js → ../../package.json
51
51
  // src/commands/serve.ts → ../../package.json (tsx / vitest)
52
- const pkgPath = resolve(__dirname, "..", "..", "package.json");
53
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
54
- return pkg.version;
52
+ // In a Bun standalone binary the source tree is not present, so the
53
+ // package.json lookup below fails. Return a fallback version instead of
54
+ // crashing the serve process.
55
+ try {
56
+ const pkgPath = resolve(__dirname, "..", "..", "package.json");
57
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
58
+ return pkg.version;
59
+ }
60
+ catch {
61
+ return process.env.npm_package_version ?? "0.0.0";
62
+ }
55
63
  }
56
64
  /**
57
65
  * Translate a backend HTTP(S) base URL to the relay WS(S) URL.
@@ -118,7 +126,7 @@ export function parseServeArgs(args) {
118
126
  }
119
127
  export async function runServe(opts = {}) {
120
128
  const cfg = await requireLogin();
121
- const cwd = opts.cwd ?? homedir();
129
+ const cwd = opts.cwd ?? process.cwd();
122
130
  const version = readPackageVersion();
123
131
  const installSignalHandlers = opts.installSignalHandlers ?? true;
124
132
  const silent = opts.silent ?? false;
@@ -227,6 +235,7 @@ export async function runServe(opts = {}) {
227
235
  store,
228
236
  manager,
229
237
  cwd,
238
+ agentDir: getAgentDir(),
230
239
  publishMetaEvent: deferredPublishMetaEvent,
231
240
  mcpState: getLiveMcpState() ?? undefined,
232
241
  }).then((response) => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/memory/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAcjE,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,GAAG,EAAE,YAAY,QAmB5D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/memory/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAejE,MAAM,CAAC,OAAO,UAAU,mBAAmB,CAAC,GAAG,EAAE,YAAY,QAoB5D"}
@@ -5,6 +5,7 @@ import { registerCompactionTrigger } from "./hooks/compaction-trigger.js";
5
5
  import { registerInterAgentReceiver } from "./hooks/inter-agent-receiver.js";
6
6
  import { registerObserverTrigger } from "./hooks/observer-trigger.js";
7
7
  import { Runtime } from "./runtime.js";
8
+ import { registerToolOutputCompressor } from "./tool-output-compressor.js";
8
9
  import { registerRecallTool } from "./tools/recall-observation.js";
9
10
  import { registerReadProjectObservationsTool } from "./tools/read-project-observations.js";
10
11
  import { registerReceiveAgentObservationsTool } from "./tools/receive-agent-observations.js";
@@ -17,6 +18,7 @@ export default function observationalMemory(ext) {
17
18
  registerObserverTrigger(ext, runtime);
18
19
  registerCompactionTrigger(ext, runtime);
19
20
  registerCompactionHook(ext, runtime);
21
+ registerToolOutputCompressor(ext, runtime);
20
22
  registerStatusCommand(ext, runtime);
21
23
  registerViewCommand(ext, runtime);
22
24
  registerRecallTool(ext);
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Tool Output Compressor — RTK-inspired pre-processing that intercepts
3
+ * tool results and compresses raw output before it enters the LLM context.
4
+ *
5
+ * This is complementary to the conversation-level compaction pipeline:
6
+ * - Compressor: compresses raw tool output (pre-context)
7
+ * - Compaction: summarizes + prunes conversation history (post-context)
8
+ * - Unified compaction: narrative summary + observation extraction (at compaction boundary)
9
+ *
10
+ * Strategies (mirroring RTK's four-pronged approach):
11
+ *
12
+ * 1. SMART FILTERING — strip noise from structured outputs
13
+ * - Code: remove comments, collapse whitespace, skip blank lines
14
+ * - Git: strip progress meters, hints, verbose enumerations
15
+ * - Shell: remove ANSI escape codes, carriage returns
16
+ *
17
+ * 2. GROUPING — aggregate similar items
18
+ * - ls/find: group files by directory
19
+ * - grep/rg: group matches by file, collapse repeated patterns
20
+ * - Lint errors: group by rule + file
21
+ *
22
+ * 3. TRUNCATION — keep relevant context, cut redundancy
23
+ * - Read: keep N lines with line-number markers
24
+ * - Bash: keep first + last N lines of long output
25
+ * - Test runners: keep only failures + summary
26
+ *
27
+ * 4. DEDUPLICATION — collapse repeated lines with counts
28
+ * - Log output: deduplicate repeated lines
29
+ * - Docker/build output: collapse progress lines
30
+ */
31
+ import type { ExtensionAPI } from "../sdk/coding-agent/index.js";
32
+ import type { Runtime } from "./runtime.js";
33
+ export interface CompressorConfig {
34
+ /** Enable compression (default: true) */
35
+ enabled: boolean;
36
+ /** Maximum characters for any single tool result before truncation */
37
+ maxResultChars: number;
38
+ /** Maximum lines for read results before truncation */
39
+ maxReadLines: number;
40
+ /** Maximum lines for directory listings */
41
+ maxLsLines: number;
42
+ /** Maximum lines for grep/find results before grouping */
43
+ maxSearchLines: number;
44
+ /** Enable ANSI escape code stripping */
45
+ stripAnsi: boolean;
46
+ /** Enable comment stripping for code files */
47
+ stripComments: boolean;
48
+ /** Enable test-runner failure-only filtering */
49
+ testFailuresOnly: boolean;
50
+ /** Enable git output compacting */
51
+ compactGit: boolean;
52
+ }
53
+ export declare const DEFAULT_COMPRESSOR_CONFIG: CompressorConfig;
54
+ export interface CompressionStats {
55
+ originalChars: number;
56
+ compressedChars: number;
57
+ savingsPercent: number;
58
+ toolName: string;
59
+ }
60
+ export declare function registerToolOutputCompressor(ext: ExtensionAPI, runtime: Runtime): void;
61
+ //# sourceMappingURL=tool-output-compressor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-output-compressor.d.ts","sourceRoot":"","sources":["../../src/memory/tool-output-compressor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAUjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAO5C,MAAM,WAAW,gBAAgB;IAChC,yCAAyC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,sEAAsE;IACtE,cAAc,EAAE,MAAM,CAAC;IACvB,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,cAAc,EAAE,MAAM,CAAC;IACvB,wCAAwC;IACxC,SAAS,EAAE,OAAO,CAAC;IACnB,8CAA8C;IAC9C,aAAa,EAAE,OAAO,CAAC;IACvB,gDAAgD;IAChD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,mCAAmC;IACnC,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,eAAO,MAAM,yBAAyB,EAAE,gBAUvC,CAAC;AAghBF,MAAM,WAAW,gBAAgB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAED,wBAAgB,4BAA4B,CAC3C,GAAG,EAAE,YAAY,EACjB,OAAO,EAAE,OAAO,GACd,IAAI,CA4HN"}