@evref-bl/pharo-agent 0.3.7 → 0.3.8
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/.claude-plugin/plugin.json +1 -1
- package/package.json +4 -4
- package/plugin.json +1 -1
- package/skills/pharo-setup/SKILL.md +1 -1
- package/src/cli.js +2 -2
- package/src/images.js +1 -2
- package/src/live.js +53 -2
- package/src/runtime.js +7 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evref-bl/pharo-agent",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Terminal-first Pharo integration for coding agents.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"test": "node --test"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@evref-bl/pharo-mcp": "1.5.
|
|
35
|
-
"@evref-bl/plexus": "0.4.
|
|
36
|
-
"@evref-bl/plexus-gateway": "0.4.
|
|
34
|
+
"@evref-bl/pharo-mcp": "1.5.1",
|
|
35
|
+
"@evref-bl/plexus": "0.4.7",
|
|
36
|
+
"@evref-bl/plexus-gateway": "0.4.7",
|
|
37
37
|
"@modelcontextprotocol/sdk": "1.32.1"
|
|
38
38
|
}
|
|
39
39
|
}
|
package/plugin.json
CHANGED
|
@@ -8,7 +8,7 @@ Run this only when the user asks to enable Pharo tooling in this project.
|
|
|
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.3.
|
|
11
|
+
npx --yes @evref-bl/pharo-agent@0.3.8 init --host codex
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
Use `--host claude` in Claude Code and `--host opencode` in OpenCode.
|
package/src/cli.js
CHANGED
|
@@ -32,7 +32,7 @@ function parseImageAdd(args) {
|
|
|
32
32
|
throw new Error("image add requires an image id");
|
|
33
33
|
}
|
|
34
34
|
const parsed = { id };
|
|
35
|
-
const options = { "--template": "template", "--
|
|
35
|
+
const options = { "--template": "template", "--profile": "profile" };
|
|
36
36
|
for (let index = 0; index < rest.length; index += 1) {
|
|
37
37
|
const option = rest[index];
|
|
38
38
|
const key = options[option];
|
|
@@ -67,7 +67,7 @@ function usage() {
|
|
|
67
67
|
"Usage:",
|
|
68
68
|
" pharo-agent init --host <codex|claude|opencode> [--mount-root <path>]",
|
|
69
69
|
" pharo-agent image list",
|
|
70
|
-
" pharo-agent image add <id> --template <name> [--
|
|
70
|
+
" pharo-agent image add <id> --template <name> [--profile <id>]",
|
|
71
71
|
" pharo-agent image use <id>",
|
|
72
72
|
" pharo-agent image remove <id>",
|
|
73
73
|
" pharo-agent open [--image <id>]",
|
package/src/images.js
CHANGED
|
@@ -37,7 +37,7 @@ export function listImages(projectRoot) {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
export function addImage(projectRoot, { id, template,
|
|
40
|
+
export function addImage(projectRoot, { id, template, profile }) {
|
|
41
41
|
const config = readProjectConfig(projectRoot);
|
|
42
42
|
const images = Array.isArray(config.images) ? config.images : [];
|
|
43
43
|
if (images.some((image) => image.id === id)) {
|
|
@@ -57,7 +57,6 @@ export function addImage(projectRoot, { id, template, category, profile }) {
|
|
|
57
57
|
kind: "template",
|
|
58
58
|
...(profile ? { profileId: profile } : {}),
|
|
59
59
|
templateName: template,
|
|
60
|
-
...(category ? { templateCategory: category } : {}),
|
|
61
60
|
cleanupPolicy: "workspace_cleanup_only",
|
|
62
61
|
},
|
|
63
62
|
};
|
package/src/live.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
resolvePlexusHomePath,
|
|
10
10
|
} from "@evref-bl/plexus";
|
|
11
11
|
import { callImageTool } from "./gateway.js";
|
|
12
|
-
import { readProjectConfig } from "./project.js";
|
|
12
|
+
import { readProjectConfig, writeProjectConfig } from "./project.js";
|
|
13
13
|
import {
|
|
14
14
|
defaultImageId,
|
|
15
15
|
readConfig,
|
|
@@ -62,6 +62,48 @@ export function needsImageCreation(listed, imageId) {
|
|
|
62
62
|
return summary.status === "declared";
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// The launcher resolves a template name against its default group only, so an
|
|
66
|
+
// image naming a template from any other group cannot be created until the
|
|
67
|
+
// project config says which group that is. Find it once, on first creation.
|
|
68
|
+
export async function resolveTemplateCategory(projectRoot, launcher, imageId) {
|
|
69
|
+
const config = readProjectConfig(projectRoot);
|
|
70
|
+
const images = Array.isArray(config.images) ? config.images : [];
|
|
71
|
+
const configured = images.find((image) => image.id === imageId);
|
|
72
|
+
const create = configured?.create;
|
|
73
|
+
if (!create?.templateName || create.templateCategory) {
|
|
74
|
+
return create?.templateCategory;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const listed = await launcher.listTemplates();
|
|
78
|
+
const templates = Array.isArray(listed?.templates) ? listed.templates : [];
|
|
79
|
+
const categories = [
|
|
80
|
+
...new Set(
|
|
81
|
+
templates
|
|
82
|
+
.filter((template) => template?.name === create.templateName)
|
|
83
|
+
.map((template) => template?.templateCategory)
|
|
84
|
+
.filter(Boolean),
|
|
85
|
+
),
|
|
86
|
+
];
|
|
87
|
+
if (categories.length === 0) {
|
|
88
|
+
throw new Error(`No template named ${create.templateName} in any category.`);
|
|
89
|
+
}
|
|
90
|
+
if (categories.length > 1) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Template ${create.templateName} exists in several categories: ${categories.join(", ")}.`
|
|
93
|
+
+ ` Set create.templateCategory for image ${imageId} to choose one.`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const [templateCategory] = categories;
|
|
98
|
+
writeProjectConfig(projectRoot, {
|
|
99
|
+
...config,
|
|
100
|
+
images: images.map((image) => (image.id === imageId
|
|
101
|
+
? { ...image, create: { ...image.create, templateCategory } }
|
|
102
|
+
: image)),
|
|
103
|
+
});
|
|
104
|
+
return templateCategory;
|
|
105
|
+
}
|
|
106
|
+
|
|
65
107
|
export function describeLifecycleFailure(failure) {
|
|
66
108
|
const stderr = failure.launcherResult?.raw?.stderr?.trim();
|
|
67
109
|
return [
|
|
@@ -116,13 +158,22 @@ export async function openAgentImage(projectRoot, imageId) {
|
|
|
116
158
|
const launcher = new ScopedPharoLauncher({ projectRoot });
|
|
117
159
|
const created = needsImageCreation(launcher.listImages(), resolved);
|
|
118
160
|
if (created) {
|
|
161
|
+
await resolveTemplateCategory(projectRoot, launcher, resolved);
|
|
119
162
|
await launcher.createImage(resolved);
|
|
120
163
|
}
|
|
121
164
|
const opened = await runLifecycle(
|
|
122
165
|
() => openProject({ projectRoot, imageIds: [resolved] }),
|
|
123
166
|
`PLexus could not open image ${resolved}.`,
|
|
124
167
|
);
|
|
125
|
-
|
|
168
|
+
let image;
|
|
169
|
+
let mcpServerId;
|
|
170
|
+
try {
|
|
171
|
+
({ image, mcpServerId } = selectRunningImage(opened.state, resolved));
|
|
172
|
+
} catch (error) {
|
|
173
|
+
// The image is running but unusable to us, so do not leave it behind.
|
|
174
|
+
await closeProject({ projectRoot, imageIds: [resolved] }).catch(() => {});
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
126
177
|
const mountPath = resolveMountPath(projectRoot, readConfig(projectRoot), image.id);
|
|
127
178
|
fs.mkdirSync(path.dirname(mountPath), { recursive: true });
|
|
128
179
|
const open = {
|
package/src/runtime.js
CHANGED
|
@@ -112,8 +112,14 @@ export function selectRunningImage(state, imageId) {
|
|
|
112
112
|
}
|
|
113
113
|
const image = images[0];
|
|
114
114
|
if (!Array.isArray(image.mcpServers) || image.mcpServers.length !== 1) {
|
|
115
|
+
// PLexus withholds the route when the image is outside the MCP contract,
|
|
116
|
+
// and records why. Say that rather than only the endpoint count.
|
|
117
|
+
const contract = image.pharoMcpContract;
|
|
118
|
+
const reason = contract?.status === "unsupported" && contract.reason
|
|
119
|
+
? ` ${contract.reason}`
|
|
120
|
+
: "";
|
|
115
121
|
throw new Error(
|
|
116
|
-
`Image ${image.id} needs exactly one MCP endpoint; PLexus reported ${image.mcpServers?.length ?? 0}
|
|
122
|
+
`Image ${image.id} needs exactly one MCP endpoint; PLexus reported ${image.mcpServers?.length ?? 0}.${reason}`,
|
|
117
123
|
);
|
|
118
124
|
}
|
|
119
125
|
return { image, mcpServerId: image.mcpServers[0].id };
|