@cratis/pi 3.3.0 → 3.3.1

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/README.md CHANGED
@@ -14,6 +14,12 @@ Cratis quality hooks. When no `.cratis/ai.json` exists, it exposes the complete
14
14
  packaged skill set. It therefore gives Pi the complete single-harness Cratis
15
15
  experience without requiring the Cratis CLI.
16
16
 
17
+ The `subagent` tool stands down for the session when another extension already
18
+ provides a delegation tool named `Agent` (such as pi-subagents, which lists the
19
+ same agents). It removes itself from the active tools at session start and
20
+ shows one notice when Pi has a UI, so the model is not offered two delegation
21
+ tools.
22
+
17
23
  The managed CLI path remains the choice when one repository must configure and
18
24
  synchronize several harnesses. It writes the resolved corpus to `.cratis/ai`,
19
25
  creates every harness integration, and records hashes for safe update and
@@ -5,7 +5,6 @@ description: >
5
5
  Creates the single slice file containing all backend artifacts:
6
6
  commands, events, validators, constraints, read models, projections,
7
7
  and reactors — all in strict compliance with the vertical slice architecture.
8
- model: claude-sonnet-4-5
9
8
  tools:
10
9
  - Read
11
10
  - Grep
@@ -4,7 +4,6 @@ description: >
4
4
  Quality gate agent for Cratis-based projects. Reviews code against all
5
5
  project instruction files, checking architecture conformance, C# and
6
6
  TypeScript conventions, and vertical slice correctness before merge.
7
- model: claude-sonnet-4-5
8
7
  tools:
9
8
  - Read
10
9
  - Grep
@@ -8,7 +8,6 @@ description: >
8
8
  Use this agent when a request spans multiple concerns (backend + frontend,
9
9
  multiple slices, mixed C#/TypeScript work, or requires both implementation
10
10
  and review).
11
- model: claude-sonnet-4-5
12
11
  tools:
13
12
  - Read
14
13
  - Grep
@@ -4,7 +4,6 @@ description: >
4
4
  Specialist for TypeScript/React frontend code within a vertical slice.
5
5
  Implements React components that consume auto-generated command and query
6
6
  proxies, following the project's component and styling conventions.
7
- model: claude-sonnet-4-5
8
7
  tools:
9
8
  - Read
10
9
  - Grep
@@ -8,7 +8,6 @@ description: >
8
8
  Use this agent as the entry point whenever multiple agents need to work together
9
9
  as a team: mixed implementation + documentation + review, multi-feature work,
10
10
  large refactors, or any goal that spans more than one concern.
11
- model: claude-sonnet-4-5
12
11
  tools:
13
12
  - Read
14
13
  - Grep
@@ -4,7 +4,6 @@ description: >
4
4
  Performance-focused review agent for Cratis-based projects. Analyzes changed
5
5
  files for projection efficiency, query patterns, unnecessary allocations,
6
6
  React render overhead, and Chronicle anti-patterns before merge.
7
- model: claude-sonnet-4-5
8
7
  tools:
9
8
  - Read
10
9
  - Grep
@@ -5,7 +5,6 @@ description: >
5
5
  Breaks the work into ordered, parallelisable tasks, delegates each task
6
6
  to the right specialist agent, and ensures quality gates are met before
7
7
  the work is considered done.
8
- model: claude-sonnet-4-5
9
8
  tools:
10
9
  - Read
11
10
  - Grep
@@ -4,7 +4,6 @@ description: >
4
4
  Independent, read-only reviewer for typed Cratis repository investigations.
5
5
  Reviews evidence and repository-mode reasoning without applying application
6
6
  conventions to framework or client-library repositories.
7
- model: claude-opus-5
8
7
  tools:
9
8
  - Read
10
9
  - Grep
@@ -4,7 +4,6 @@ description: >
4
4
  Read-only investigator for Cratis application and framework repositories.
5
5
  Produces typed, evidence-backed findings without changing source, invoking
6
6
  mutating Chronicle operations, or assuming an application architecture.
7
- model: claude-opus-5
8
7
  tools:
9
8
  - Read
10
9
  - Grep
@@ -5,7 +5,6 @@ description: >
5
5
  security review of all changed files before merge, covering input validation,
6
6
  auth/authz, data exposure, secrets, event sourcing specifics, and frontend
7
7
  attack surface.
8
- model: claude-sonnet-4-5
9
8
  tools:
10
9
  - Read
11
10
  - Grep
@@ -4,7 +4,6 @@ description: >
4
4
  Implements a Cratis vertical slice end-to-end — all backend artifacts in one slice file, BDD specs
5
5
  in when_*/ folders, and the React surface (page and/or command dialog). Use for new slices and for
6
6
  non-trivial slice changes spanning backend and frontend.
7
- model: claude-opus-4-8
8
7
  tools:
9
8
  - Read
10
9
  - Grep
@@ -4,7 +4,6 @@ description: >
4
4
  Specialist for writing C# specs (the in-process scenario family) and
5
5
  TypeScript/React specs for vertical slices. Ensures every slice has
6
6
  comprehensive behavior coverage following the project's BDD conventions.
7
- model: claude-sonnet-4-5
8
7
  tools:
9
8
  - Read
10
9
  - Grep
@@ -4,7 +4,8 @@
4
4
  * The agent definitions are the SINGLE-SOURCE corpus files under `.cratis/ai/agents/*.md`,
5
5
  * surfaced to Pi through symlink adapters in `.pi/agents/*.md`. Those files are written
6
6
  * in the Claude/Copilot shape (Title-Case `name`, a YAML-list `tools:` using Claude tool
7
- * names such as `Read`/`Glob`/`Bash`, and a `model:` id). Pi's built-in tools are the
7
+ * names such as `Read`/`Glob`/`Bash`). Agents without a `model:` inherit the dispatching
8
+ * session's model. Pi's built-in tools are the
8
9
  * lowercase set `read, write, edit, bash, grep, find, ls`, so this module NORMALIZES the
9
10
  * shared shape to Pi semantics — the adapter layer absorbs the tool difference, exactly
10
11
  * like every other adapter in this corpus, so `.pi/agents/*.md` can stay pure symlinks.
@@ -0,0 +1,40 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ /**
5
+ * Deciding whether the Cratis `subagent` tool stands down for a session.
6
+ *
7
+ * Widely used delegation extensions already read `.pi/agents/*.md` — pi-subagents registers a tool named
8
+ * `Agent` that lists every Cratis corpus agent. Offering that tool and `subagent` side by side makes the
9
+ * model guess which to call and pay for both descriptions, so the Cratis tool yields to the one the user
10
+ * installed deliberately.
11
+ *
12
+ * The match is on the exact tool name, not a substring: `agent` is a common word in unrelated tool names,
13
+ * and silently withdrawing delegation against one of those would be a bug nobody could see.
14
+ *
15
+ * Kept free of host calls so the policy is testable from the tool list alone; the extension owns
16
+ * `getAllTools`, `setActiveTools` and the notice.
17
+ */
18
+
19
+ import type { ToolInfo } from "@earendil-works/pi-coding-agent";
20
+
21
+ /** The name the Cratis delegation tool registers under. */
22
+ export const SUBAGENT_TOOL_NAME = "subagent";
23
+
24
+ /** Tool names that mean "another extension already delegates to agents". */
25
+ export const FOREIGN_DELEGATION_TOOL_NAMES: ReadonlySet<string> = new Set(["Agent"]);
26
+
27
+ /** The registered delegation tool the Cratis tool yields to, or `undefined` when there is none. */
28
+ export function foreignDelegationTool<T extends Pick<ToolInfo, "name">>(tools: readonly T[]): T | undefined {
29
+ return tools.find((tool) => FOREIGN_DELEGATION_TOOL_NAMES.has(tool.name));
30
+ }
31
+
32
+ /** The one informational notice shown when the Cratis tool stands down. */
33
+ export function standDownNotice(tool: Pick<ToolInfo, "name"> & { sourceInfo?: Partial<ToolInfo["sourceInfo"]> }): string {
34
+ const source = tool.sourceInfo?.source ?? "another extension";
35
+ return (
36
+ `The "${tool.name}" tool from ${source} already delegates to agents, so the Cratis ` +
37
+ `"${SUBAGENT_TOOL_NAME}" tool is disabled for this session to avoid offering two delegation tools. ` +
38
+ `Remove that extension to use "${SUBAGENT_TOOL_NAME}" instead.`
39
+ );
40
+ }
@@ -12,6 +12,10 @@
12
12
  * - single: { agent, task }
13
13
  * - parallel: { tasks: [{ agent, task }, ...] } (max 8, 4 concurrent)
14
14
  * - chain: { chain: [{ agent, task }, ...] } (sequential, {previous} placeholder)
15
+ *
16
+ * Stand-down: when another extension already provides a delegation tool named `Agent` (pi-subagents,
17
+ * which reads the same `.pi/agents/*.md`), this tool removes itself from the session's active tools at
18
+ * session start and says so once — see `./delegation.ts`. Without such a tool it behaves unchanged.
15
19
  */
16
20
 
17
21
  import { spawn } from "node:child_process";
@@ -23,6 +27,7 @@ import { CONFIG_DIR_NAME, type ExtensionAPI, getAgentDir } from "@earendil-works
23
27
  import { StringEnum } from "@earendil-works/pi-ai";
24
28
  import { Type } from "typebox";
25
29
  import { type AgentConfig, type AgentScope, discoverAgents } from "./agents.ts";
30
+ import { foreignDelegationTool, standDownNotice, SUBAGENT_TOOL_NAME } from "./delegation.ts";
26
31
 
27
32
  const extensionPath = fileURLToPath(import.meta.url);
28
33
  const isPackagedExtension = extensionPath.includes(`${path.sep}package${path.sep}corpus${path.sep}`);
@@ -257,8 +262,28 @@ export default function (pi: ExtensionAPI) {
257
262
  } catch {
258
263
  knownAgents = "";
259
264
  }
265
+ // Yield to another extension's delegation tool. This can only happen at session start: getAllTools
266
+ // cannot be called while extensions load, and an extension loaded after this one is not visible yet.
267
+ // setActiveTools rebuilds the system prompt before the first turn, so the model never sees this tool.
268
+ let standDownNoticeShown = false;
269
+ pi.on("session_start", (_event, ctx) => {
270
+ try {
271
+ const foreign = foreignDelegationTool(pi.getAllTools());
272
+ if (!foreign) return;
273
+ const active = pi.getActiveTools();
274
+ if (!active.includes(SUBAGENT_TOOL_NAME)) return;
275
+ pi.setActiveTools(active.filter((name) => name !== SUBAGENT_TOOL_NAME));
276
+ if (ctx.hasUI && !standDownNoticeShown) {
277
+ standDownNoticeShown = true;
278
+ ctx.ui.notify(standDownNotice(foreign), "info");
279
+ }
280
+ } catch {
281
+ // Some hosts cannot list or switch tools; being unable to check is no reason to fail the session.
282
+ }
283
+ });
284
+
260
285
  pi.registerTool({
261
- name: "subagent",
286
+ name: SUBAGENT_TOOL_NAME,
262
287
  label: "Subagent",
263
288
  description: [
264
289
  "Delegate a task to a specialized Cratis agent in an isolated context (separate pi process).",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cratis/pi",
3
- "version": "3.3.0",
3
+ "version": "3.3.1",
4
4
  "description": "Configuration-aware Cratis AI integration for Pi",
5
5
  "type": "module",
6
6
  "files": [