@evref-bl/pharo-agent 0.7.0 → 0.9.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pharo-agent",
3
3
  "displayName": "Pharo Agent",
4
- "version": "0.7.0",
4
+ "version": "0.9.0",
5
5
  "description": "Terminal-first Pharo image setup for coding agents.",
6
6
  "author": {
7
7
  "name": "Evref-BL",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evref-bl/pharo-agent",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "private": false,
5
5
  "description": "Terminal-first Pharo integration for coding agents.",
6
6
  "license": "Apache-2.0",
@@ -31,9 +31,8 @@
31
31
  "test": "node --test"
32
32
  },
33
33
  "dependencies": {
34
- "@evref-bl/pharo-mcp": "3.0.0",
35
- "@evref-bl/plexus": "0.6.0",
36
- "@evref-bl/plexus-gateway": "0.6.0",
34
+ "@evref-bl/pharo-mcp": "3.2.0",
35
+ "@evref-bl/plexus": "0.8.0",
37
36
  "@modelcontextprotocol/sdk": "1.32.1"
38
37
  }
39
38
  }
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "pharo-agent",
4
- "version": "0.7.0",
4
+ "version": "0.9.0",
5
5
  "description": "Terminal-first Pharo image setup for coding agents.",
6
6
  "author": {
7
7
  "name": "Evref-BL",
@@ -8,7 +8,7 @@ Run this only when the user asks to enable or refresh Pharo tooling in this proj
8
8
  From the project root, run the released CLI that matches this plugin:
9
9
 
10
10
  ```sh
11
- npx --yes @evref-bl/pharo-agent@0.7.0 init --host claude
11
+ npx --yes @evref-bl/pharo-agent@0.9.0 init --host claude
12
12
  ```
13
13
 
14
14
  Name the host this chat runs in: `--host claude` in Claude Code,
package/src/gateway.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
- import { PlexusGateway } from "@evref-bl/plexus-gateway";
2
+ import { PlexusGateway } from "@evref-bl/plexus/gateway";
3
3
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
  import {
package/src/init.js CHANGED
@@ -71,6 +71,42 @@ function guidance() {
71
71
  return `# Pharo work\n\nWhen the task uses Pharo, read \`pharo.AGENTS.md\` before acting.\n`;
72
72
  }
73
73
 
74
+ // MCP ships the references and skills, so which one answers what is its to
75
+ // state. Repeating the list here went stale the first time a template was
76
+ // renamed, and sent readers to files that are no longer installed. Only the
77
+ // paths differ, because init copies the two template directories under
78
+ // `.pharo-agent`.
79
+ function pharoRouting() {
80
+ const templates = mcpTemplateDirectory();
81
+ const guide = fs.readFileSync(path.join(templates, "pharo.AGENTS.md"), "utf8");
82
+ const guidance = guide.indexOf("## Read First");
83
+ if (guidance === -1) {
84
+ throw new Error("The MCP agent guide no longer opens its guidance with ## Read First");
85
+ }
86
+ const routing = guide
87
+ .slice(guidance)
88
+ .replaceAll("pharo-references/", ".pharo-agent/references/")
89
+ .replaceAll("pharo-skills/", ".pharo-agent/skills/")
90
+ .replaceAll("skills/", ".pharo-agent/skills/")
91
+ .replaceAll(".pharo-agent/.pharo-agent/", ".pharo-agent/")
92
+ .trimEnd();
93
+ assertRoutingResolves(routing, templates);
94
+ return routing;
95
+ }
96
+
97
+ // A path the routing names must be a file MCP actually provides, so a renamed
98
+ // template stops init here rather than pointing a reader at nothing.
99
+ function assertRoutingResolves(routing, templates) {
100
+ const directoryNames = { references: "pharo-references", skills: "pharo-skills" };
101
+ for (const [target, directory, relative] of routing.matchAll(
102
+ /\.pharo-agent\/(references|skills)\/([\w./-]+\.md)/g,
103
+ )) {
104
+ if (!fs.existsSync(path.join(templates, directoryNames[directory], relative))) {
105
+ throw new Error(`The MCP agent guide names ${target}, which no template provides`);
106
+ }
107
+ }
108
+ }
109
+
74
110
  function pharoGuide() {
75
111
  return `# Pharo project guide
76
112
 
@@ -124,32 +160,7 @@ everything this project opened.
124
160
 
125
161
  Image, Tonel, Iceberg, disk Git, and host processes are separate states.
126
162
 
127
- ## Read first
128
-
129
- - Before Pharo source changes, read the project guidance. If absent, read the
130
- local [coding rules](.pharo-agent/references/pharo-coding-rules.md) and
131
- [coding style](.pharo-agent/references/pharo-coding-style.md).
132
- - Before loading a project, read
133
- [.pharo-agent/skills/pharo-project-load/SKILL.md](.pharo-agent/skills/pharo-project-load/SKILL.md).
134
- - Before cross-version work, read
135
- [.pharo-agent/skills/pharo-version-compat/SKILL.md](.pharo-agent/skills/pharo-version-compat/SKILL.md).
136
- - Before exporting image changes or changing Git state, read
137
- [.pharo-agent/skills/pharo-image-git-handoff/SKILL.md](.pharo-agent/skills/pharo-image-git-handoff/SKILL.md).
138
- - Before reproducing smalltalkCI or GitHub failures, read
139
- [.pharo-agent/skills/pharo-ci-repro/SKILL.md](.pharo-agent/skills/pharo-ci-repro/SKILL.md).
140
- - Before recovering a stopped standalone image, read
141
- [.pharo-agent/skills/pharo-mcp-recovery/SKILL.md](.pharo-agent/skills/pharo-mcp-recovery/SKILL.md).
142
- - Before debugger sessions or repairs, read the local
143
- [debugging guide](.pharo-agent/references/debugging.md).
144
-
145
- ## Workflow
146
-
147
- 1. Inspect the tool schema before acting; do not rely on remembered names.
148
- 2. Prefer the least-mutating dedicated tool; use \`image_evaluate\` only when
149
- no dedicated tool fits.
150
- 3. Verify identity before mutation, make the narrowest change, then run focused
151
- tests.
152
- 4. Use the image-to-Git handoff skill before export or commit.
163
+ ${pharoRouting()}
153
164
  `;
154
165
  }
155
166
 
package/src/live.js CHANGED
@@ -3,10 +3,10 @@ import fs from "node:fs";
3
3
  import path from "node:path";
4
4
  import {
5
5
  ScopedPharoLauncher,
6
- closeProject,
7
6
  homeDependencyRepositoryCachePath,
8
- openProject,
9
7
  resolvePlexusHomePath,
8
+ startProjectImage,
9
+ stopProjectImage,
10
10
  } from "@evref-bl/plexus";
11
11
  import { callImageTool } from "./gateway.js";
12
12
  import { readProjectConfig, writeProjectConfig } from "./project.js";
@@ -23,7 +23,7 @@ import {
23
23
  writeOpenImages,
24
24
  } from "./runtime.js";
25
25
 
26
- export const pifsRelease = "v0.7.0";
26
+ export const pifsRelease = "v0.9.0";
27
27
 
28
28
  // The class-side PIFSServer messages this plugin sends into the image. The
29
29
  // release automation checks that a published PIFS still implements them: a pin
@@ -153,7 +153,7 @@ export function describeLifecycleFailure(failure) {
153
153
  .join("\n ");
154
154
  }
155
155
 
156
- // openProject and closeProject throw on failure, carrying the failures on the error.
156
+ // startProjectImage and stopProjectImage throw on failure, carrying the failures on the error.
157
157
  export async function runLifecycle(action, fallback) {
158
158
  try {
159
159
  return await action();
@@ -210,7 +210,7 @@ export async function openAgentImage(projectRoot, imageId) {
210
210
  await launcher.createImage(resolved);
211
211
  }
212
212
  const opened = await runLifecycle(
213
- () => openProject({ projectRoot, imageIds: [resolved] }),
213
+ () => startProjectImage({ projectRoot, imageId: resolved }),
214
214
  `PLexus could not open image ${resolved}.`,
215
215
  );
216
216
  let image;
@@ -224,7 +224,7 @@ export async function openAgentImage(projectRoot, imageId) {
224
224
  const withoutMcp = unsupportedRunningImage(opened.state, resolved);
225
225
  if (!withoutMcp) {
226
226
  // The image is running but unusable to us, so do not leave it behind.
227
- await closeProject({ projectRoot, imageIds: [resolved] }).catch(() => {});
227
+ await stopProjectImage({ projectRoot, imageId: resolved }).catch(() => {});
228
228
  throw error;
229
229
  }
230
230
  const open = { statePath: opened.statePath, imageId: resolved, mcp: false };
@@ -245,7 +245,7 @@ export async function openAgentImage(projectRoot, imageId) {
245
245
  } catch (error) {
246
246
  // The projection is half-started at best and nothing records it yet, so
247
247
  // close the image rather than leave an image and a mount nothing owns.
248
- await closeProject({ projectRoot, imageIds: [resolved] }).catch(() => {});
248
+ await stopProjectImage({ projectRoot, imageId: resolved }).catch(() => {});
249
249
  throw error;
250
250
  }
251
251
  writeOpenImages(projectRoot, { ...openImages, [resolved]: { ...open, projectionPort } });
@@ -302,7 +302,7 @@ async function closeOneImage(projectRoot, openImages, imageId) {
302
302
  }
303
303
  }
304
304
  const closed = await runLifecycle(
305
- () => closeProject({ projectRoot, imageIds: [imageId] }),
305
+ () => stopProjectImage({ projectRoot, imageId }),
306
306
  `PLexus could not close image ${imageId}.`,
307
307
  );
308
308
  return { stoppedImages: closed.stoppedImages.map((image) => image.id), projectionError };