editmamei 0.17.5 → 0.18.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/NOTICES.md +43 -0
- package/README.md +91 -8
- package/dist/api/extendscript/_helpers.js +8 -0
- package/dist/bin/editmamei-core-darwin-arm64 +0 -0
- package/dist/bin/editmamei-core-darwin-x64 +0 -0
- package/dist/bin/editmamei-core-win-x64.exe +0 -0
- package/dist/cli/clients/json-config.js +1 -1
- package/dist/core/server.js +92 -24
- package/dist/core/tool-groups.js +178 -0
- package/dist/core/tool-tiers.js +34 -48
- package/dist/delivery/bundle.js +8 -0
- package/dist/delivery/config.js +23 -0
- package/dist/delivery/provision.js +30 -2
- package/dist/delivery/store.js +40 -1
- package/dist/detection/detect-active-doc.js +84 -0
- package/dist/detection/detection-client.js +62 -0
- package/dist/detection/face-contour-geometry.js +137 -0
- package/dist/detection/face-detector.js +34 -0
- package/dist/detection/face-feature-geometry.js +180 -0
- package/dist/detection/geometry.js +35 -0
- package/dist/detection/landmark-detection-client.js +38 -0
- package/dist/detection/landmark-detector.js +63 -0
- package/dist/detection/landmark-spec.js +29 -0
- package/dist/detection/mesh-face.js +36 -0
- package/dist/detection/object-detector.js +135 -0
- package/dist/detection/runtime.js +46 -0
- package/dist/models/dfine-s-coco-quant.onnx +0 -0
- package/dist/models/ultraface-rfb-320.onnx +0 -0
- package/dist/modules/ce/index.js +23 -5
- package/dist/perception/facets.js +148 -0
- package/dist/perception/region-precompute.js +122 -0
- package/dist/perception/region-scorer.js +151 -0
- package/dist/perception/scene-model.js +156 -0
- package/dist/perception/select-recipes.js +493 -0
- package/dist/perception/sky-ground-flood.js +234 -0
- package/dist/perception/sky-mask-transfer.js +77 -0
- package/dist/platform/macos-executor.js +0 -1
- package/dist/platform/windows-executor.js +6 -1
- package/dist/preview/coordinate-grid.js +250 -0
- package/dist/skills/editmamei-skill.zip +0 -0
- package/dist/telemetry/events.js +2 -1
- package/dist/telemetry/sanitize.js +15 -1
- package/dist/tools/adjustment-tools.js +46 -51
- package/dist/tools/detection-tools.js +209 -0
- package/dist/tools/document-tools.js +41 -30
- package/dist/tools/filter-tools.js +616 -101
- package/dist/tools/guide-tools.js +186 -0
- package/dist/tools/history-tools.js +1 -39
- package/dist/tools/image-tools.js +88 -0
- package/dist/tools/inspect-tools.js +86 -0
- package/dist/tools/layer-properties-tools.js +195 -106
- package/dist/tools/layer-tools.js +55 -14
- package/dist/tools/layer-transform-tools.js +236 -73
- package/dist/tools/metadata-tools.js +2 -32
- package/dist/tools/object-tools.js +193 -0
- package/dist/tools/overview-tools.js +69 -43
- package/dist/tools/path-tools.js +274 -0
- package/dist/tools/portrait-tools.js +146 -0
- package/dist/tools/preview-tools.js +78 -3
- package/dist/tools/retouch-tools.js +45 -33
- package/dist/tools/scene-tools.js +401 -0
- package/dist/tools/selection-tools.js +766 -228
- package/dist/tools/text-on-object-tools.js +225 -0
- package/dist/tools/text-tools.js +47 -56
- package/dist/tools/transform-canvas-tools.js +138 -0
- package/dist/tools/vector-mask-tools.js +116 -0
- package/dist/version.js +1 -1
- package/package.json +4 -2
package/NOTICES.md
CHANGED
|
@@ -46,6 +46,49 @@ artifact rather than by inlining the text here.
|
|
|
46
46
|
|
|
47
47
|
---
|
|
48
48
|
|
|
49
|
+
## Bundled Machine-Learning Models
|
|
50
|
+
|
|
51
|
+
Unlike the npm dependencies above, the local-vision ONNX model weights are
|
|
52
|
+
**bundled** in Editmamei's tarball (under `dist/models/`) and redistributed with
|
|
53
|
+
it, so their notices are reproduced here. The models run entirely on-device; no
|
|
54
|
+
image data is transmitted. The ONNX runtime that executes them
|
|
55
|
+
(`onnxruntime-web`, MIT) is a resolved npm dependency covered by the section
|
|
56
|
+
above.
|
|
57
|
+
|
|
58
|
+
### Ultraface (`ultraface-rfb-320.onnx`) — face detection
|
|
59
|
+
|
|
60
|
+
- **Upstream:** Ultra-Light-Fast-Generic-Face-Detector-1MB (version-RFB-320), by
|
|
61
|
+
Linzaer (Fei Yu); distributed via the ONNX Model Zoo.
|
|
62
|
+
- **License:** MIT.
|
|
63
|
+
- Used to locate forward-facing faces. Returns bounding boxes only.
|
|
64
|
+
|
|
65
|
+
### D-FINE-S (`dfine-s-coco-quant.onnx`) — object detection
|
|
66
|
+
|
|
67
|
+
- **Upstream:** D-FINE ("Redefine Regression Task of DETRs as Fine-grained
|
|
68
|
+
Distribution Refinement", Peng et al.), COCO-trained S variant; ONNX export +
|
|
69
|
+
int8 quantization via the Hugging Face `onnx-community/dfine_s_coco-ONNX`
|
|
70
|
+
repository.
|
|
71
|
+
- **License:** Apache-2.0. The upstream D-FINE repository's `NOTICE`/attribution
|
|
72
|
+
terms (Apache-2.0 §4(d)) are preserved by reference to that source.
|
|
73
|
+
- Used to detect the 80 COCO object classes.
|
|
74
|
+
|
|
75
|
+
### MediaPipe FaceMesh (`models/pro/face_mesh_468.onnx`) — facial landmarks (Pro)
|
|
76
|
+
|
|
77
|
+
- **Upstream:** Google MediaPipe Face Landmark / FaceMesh model (the
|
|
78
|
+
`face_landmarks_detector` weight from `face_landmarker.task`), trained by Google
|
|
79
|
+
on its own data. Converted to ONNX (468-point variant) and redistributed via the
|
|
80
|
+
Hugging Face `Heliosoph/mediapipe-face-onnx` repository.
|
|
81
|
+
- **License:** Apache-2.0 (the model is permissively licensed by Google;
|
|
82
|
+
commercial-safe — see `docs/product/pro-vision-models.md` for the weights-license
|
|
83
|
+
diligence). Any upstream `NOTICE`/attribution terms (Apache-2.0 §4(d)) are
|
|
84
|
+
preserved by reference to those sources.
|
|
85
|
+
- Runs on-device via the same `onnxruntime-web` backend; the image never leaves the
|
|
86
|
+
machine. Used by the Pro `photoshop_detect_landmarks` tool to locate the 468-point
|
|
87
|
+
face mesh (eyes, brows, lips, nose, jaw). Ships in the **Pro module**
|
|
88
|
+
(`models/pro/`), not the CE tarball.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
49
92
|
## Trademarks
|
|
50
93
|
|
|
51
94
|
"Adobe" and "Photoshop" are registered trademarks of Adobe Inc. This product
|
package/README.md
CHANGED
|
@@ -10,9 +10,31 @@
|
|
|
10
10
|
[](https://editmamei.com/license)
|
|
11
11
|
[]()
|
|
12
12
|
|
|
13
|
-
Editmamei is a Model Context Protocol (MCP) server that drives the Adobe Photoshop you already have. You describe the edit in plain words
|
|
13
|
+
Editmamei is a Model Context Protocol (MCP) server that drives the Adobe Photoshop you already have. You describe the edit in plain words, your AI assistant plans the steps, and your own copy of Photoshop carries them out with its standard adjustment layers, masks, selections, and filters. The AI directs and Photoshop edits. No generative model touches your pixels, and no image is uploaded to perform an edit.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
It serves photographers and retouchers who want to edit by conversation just as much as developers wiring Photoshop into an AI pipeline. To learn more or get started, visit **[editmamei.com](https://editmamei.com)**.
|
|
16
|
+
|
|
17
|
+
**[editmamei.com](https://editmamei.com)** · [Docs & wiki](https://github.com/editmamei/editmamei-wiki) · [Report a bug](https://github.com/editmamei/editmamei-wiki/issues)
|
|
18
|
+
|
|
19
|
+
## How it works
|
|
20
|
+
|
|
21
|
+
Editmamei is a pure MCP **stdio** server written in Node. Your AI client launches it as a subprocess and calls its tools over stdio. Each tool builds a small Photoshop script and runs it in your local Photoshop over the platform's native automation channel:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
AI client ──stdio──▶ Editmamei (Node MCP server)
|
|
25
|
+
│ builds a Photoshop script per tool call
|
|
26
|
+
▼
|
|
27
|
+
COM (Windows) / AppleScript (macOS)
|
|
28
|
+
│
|
|
29
|
+
▼
|
|
30
|
+
Your local Adobe Photoshop
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- **Windows** drives Photoshop through COM (the `Photoshop.Application` object).
|
|
34
|
+
- **macOS** drives it through AppleScript / OSA.
|
|
35
|
+
- Perception (face, object, and scene detection) runs **on-device** with local ONNX computer vision, so the model gets real document coordinates without your image leaving the machine.
|
|
36
|
+
|
|
37
|
+
Photoshop does the pixel work with its own engine. Editmamei is the conductor, not a renderer.
|
|
16
38
|
|
|
17
39
|
## Install
|
|
18
40
|
|
|
@@ -24,19 +46,80 @@ editmamei status # confirms install state + Photoshop detection
|
|
|
24
46
|
|
|
25
47
|
Then restart your AI client and ask it: _"Is Photoshop connected?"_
|
|
26
48
|
|
|
27
|
-
Prefer not to use a terminal? Claude Desktop users can install the one-click `.mcpb` extension from [the latest release](https://github.com/editmamei/editmamei-wiki/releases/latest). Full walkthrough in the [install guide](https://github.com/editmamei/editmamei-wiki/blob/main/docs/installation.md).
|
|
49
|
+
Prefer not to use a terminal? Claude Desktop users can install the one-click `.mcpb` extension from [the latest release](https://github.com/editmamei/editmamei-wiki/releases/latest) (no system Node required, Claude Desktop ships its own runtime). Full walkthrough in the [install guide](https://github.com/editmamei/editmamei-wiki/blob/main/docs/installation.md).
|
|
50
|
+
|
|
51
|
+
**Requirements:** Adobe Photoshop 2026 (v27.x), Windows 10/11 or macOS 12+, Node.js 20+ (for the npm path), and an MCP-compatible AI client (Claude Desktop, Cursor, Claude Code).
|
|
52
|
+
|
|
53
|
+
## A session, end to end
|
|
54
|
+
|
|
55
|
+
The discovery chain is `photoshop_ping` (liveness), then `photoshop_overview` (the working method and capability map), then `tools/list` (the full schema-typed inventory). After that you just talk to your assistant:
|
|
56
|
+
|
|
57
|
+
> _"Open my vacation photo."_
|
|
58
|
+
> _"Make the sky more dramatic but keep the foreground natural."_
|
|
59
|
+
> _"Sharpen the eyes, soften the skin, leave everything else alone."_
|
|
60
|
+
> _"Isolate the subject onto its own layer with a clean mask."_
|
|
61
|
+
> _"Export a 2048px web JPEG."_
|
|
28
62
|
|
|
29
|
-
|
|
63
|
+
Your assistant composes those into tool calls, checks the result with previews and measurements, and iterates.
|
|
30
64
|
|
|
31
|
-
##
|
|
65
|
+
## Design principles
|
|
32
66
|
|
|
33
|
-
|
|
67
|
+
These are the choices that shape the surface, and the reason an AI assistant can drive it reliably rather than hopefully.
|
|
68
|
+
|
|
69
|
+
- **Structured results, not prose.** Every tool declares an `outputSchema` and returns a typed JSON payload. Any tool that changes what is active reports the current document and layer back, so the model keeps an accurate picture of Photoshop's state between calls instead of re-deriving it.
|
|
70
|
+
- **Guidance lives in the surface.** `photoshop_overview` returns the working method (assess, plan, enact, check, iterate) and `photoshop_list_capabilities` returns a live map of what exists. The model orients itself from the server rather than from guesswork or a stale prompt.
|
|
71
|
+
- **Measure, don't eyeball.** A dedicated set of verification tools (histogram, region compare, layer-bounds diff, selection preview) lets the model check its own work numerically instead of trusting a thumbnail.
|
|
72
|
+
- **On-device perception.** Local computer vision (ONNX) detects faces, objects, and scene regions and returns real document coordinates. The image stays on your machine for this step and is never sent to a cloud vision model.
|
|
73
|
+
- **Non-destructive by default.** Adjustments are adjustment layers, and destructive pixel operations run on an auto-created duplicate. An edit is reversible by construction.
|
|
74
|
+
- **Verified against real Photoshop.** Every tool is built on Photoshop's own native scripting functions and verified in live Photoshop sessions before it ships. A new tool stays disabled until it has passed live verification, so the shipped surface is the verified surface.
|
|
75
|
+
- **Built for modern Photoshop.** Editmamei targets Photoshop 2026 (v27.x) so it can rely on current, stable scripting descriptors rather than carrying a decade of drifted behavior.
|
|
76
|
+
|
|
77
|
+
## Tool surface
|
|
78
|
+
|
|
79
|
+
**75 tools across 16 capability groups** (58 Community, 17 Pro). Every tool is namespaced `photoshop_*` and discoverable at runtime via `tools/list`. Community tools ship in both editions; Pro tools unlock with a license.
|
|
80
|
+
|
|
81
|
+
| Group | Edition | Tools |
|
|
82
|
+
| --- | --- | --- |
|
|
83
|
+
| **Core** | Community | `ping` · `overview` · `list_capabilities` · `undo` · `redo` |
|
|
84
|
+
| **Inspect** | Community | `inspect` · `get_preview` |
|
|
85
|
+
| **Verify** | Community | `get_histogram` · `compare_regions` · `get_layer_bounds_diff` · `get_selection_preview` |
|
|
86
|
+
| **Document & canvas** | Community | `create_document` · `open_document` · `close_document` · `save_psd` · `export` · `place_image` · `resize_image` · `crop_document` · `convert_image_mode` · `transform_canvas` · `guides` |
|
|
87
|
+
| **Selection** | Community | `select` · `modify_selection` · `selection_channel` |
|
|
88
|
+
| **Adjustments** | Community | `add_adjustment_layer` · `apply_adjustment` |
|
|
89
|
+
| **Filters** | Community | `apply_filter` |
|
|
90
|
+
| **Retouch** | Community | `retouch` |
|
|
91
|
+
| **Layers** | Community | `create_layer` · `delete_layer` · `fill_layer` · `add_fill_layer` · `select_layer` · `move_layer_to_position` · `duplicate_layer` · `layer_via_copy` · `convert_to_smart_object` · `rasterize_layer` · `set_layer` · `merge` · `bake_layer` · `add_layer_style` · `transform_layer` · `create_group` · `move_layer_to_group` · `set_group_blend_mode` · `ungroup` · `delete_group` |
|
|
92
|
+
| **Masks & paths** | Community | `layer_mask` · `create_clipping_mask` · `path` · `vector_mask` |
|
|
93
|
+
| **Type** | Community | `create_text_layer` · `set_text` |
|
|
94
|
+
| **Perception** | Community | `detect` · `scene` · `select_by_reference` |
|
|
95
|
+
| **AI selection** | Pro | `select_subject` · `select_sky` · `select_subject_instance` |
|
|
96
|
+
| **Layers (warp)** | Pro | `warp_layer` · `warp_layer_mesh` |
|
|
97
|
+
| **Face mesh** | Pro | `detect_landmarks` · `select_face_feature` |
|
|
98
|
+
| **Templates** | Pro | `template_create_evidence` · `template_save` · `template_list` · `template_apply` · `template_verify` · `template_recall` · `template_delete` |
|
|
99
|
+
| **Automation** | Pro | `list_actions` · `play_action` · `execute_script` |
|
|
100
|
+
|
|
101
|
+
## Editions
|
|
102
|
+
|
|
103
|
+
- **Community** is free and covers the everyday editing surface: documents, layers, selections, non-destructive adjustments, filters, masks, type, retouching, on-device perception, and the inspect/verify primitives.
|
|
104
|
+
- **Pro** adds AI selection (Sensei-backed Select Subject and Select Sky), face-mesh perception, the reproducible-template system, custom mesh warp, and Photoshop Actions plus the scripting escape hatch.
|
|
105
|
+
|
|
106
|
+
The split is detailed in [pro-features.md](https://github.com/editmamei/editmamei-wiki/blob/main/docs/pro-features.md). Pricing is at [editmamei.com](https://editmamei.com).
|
|
107
|
+
|
|
108
|
+
## Configuration
|
|
109
|
+
|
|
110
|
+
Settings live in `~/.editmamei/settings.json`, managed by the `editmamei config` CLI:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
editmamei config list
|
|
114
|
+
editmamei config set telemetry.usage false # turn off anonymous usage telemetry
|
|
115
|
+
editmamei config set photoshop_path "/path/to/Photoshop"
|
|
116
|
+
```
|
|
34
117
|
|
|
35
|
-
|
|
118
|
+
`PHOTOSHOP_PATH` (env var) overrides Photoshop auto-detection for a single run.
|
|
36
119
|
|
|
37
120
|
## Privacy
|
|
38
121
|
|
|
39
|
-
Editmamei runs on your computer and edits in your own Photoshop. No image content, document data, or file paths are sent to Editmamei's servers. It reports anonymous, content-free usage telemetry (on by default
|
|
122
|
+
Editmamei runs on your computer and edits in your own Photoshop. No image content, document data, or file paths are sent to Editmamei's servers. It reports anonymous, content-free usage telemetry (on by default, turn it off with `editmamei config set telemetry.usage false`). When your AI assistant needs to see a result, Editmamei sends it a downscaled preview, the same as dropping a photo into a chat with that assistant. Every field is documented in [privacy.md](https://github.com/editmamei/editmamei-wiki/blob/main/docs/privacy.md).
|
|
40
123
|
|
|
41
124
|
## Docs and support
|
|
42
125
|
|
|
@@ -49,6 +49,14 @@ function saveSelectionToTempChannel(doc) {
|
|
|
49
49
|
if (!hasActiveSelection(doc)) return null;
|
|
50
50
|
var ch = doc.channels.add();
|
|
51
51
|
doc.selection.store(ch, SelectionType.REPLACE);
|
|
52
|
+
// channels.add() makes the new alpha the ACTIVE channel. A pixel-sampling
|
|
53
|
+
// selection op that runs next (magic wand / color range / grow / similar)
|
|
54
|
+
// would then sample THIS alpha — uniform inside the stored selection — and
|
|
55
|
+
// flood the whole region, so a subsequent add/subtract/intersect collapses
|
|
56
|
+
// to empty/full. Geometric ops (rectangle/ellipse) are immune. Restore the
|
|
57
|
+
// composite so the sampler reads RGB. (See restoreCompositeChannel for the
|
|
58
|
+
// sibling cleanup-time case.)
|
|
59
|
+
doc.activeChannels = doc.componentChannels;
|
|
52
60
|
return ch;
|
|
53
61
|
}
|
|
54
62
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -27,7 +27,7 @@ export async function writeJsonMcpConfig(configPath, config) {
|
|
|
27
27
|
const dir = dirname(configPath);
|
|
28
28
|
const tmpPath = join(dir, `.editmamei.json-config.${process.pid}.tmp`);
|
|
29
29
|
const body = JSON.stringify(config, null, 2) + '\n';
|
|
30
|
-
await writeFile(tmpPath, body, 'utf8');
|
|
30
|
+
await writeFile(tmpPath, body, { encoding: 'utf8', mode: 0o600 });
|
|
31
31
|
try {
|
|
32
32
|
await rename(tmpPath, configPath);
|
|
33
33
|
}
|
package/dist/core/server.js
CHANGED
|
@@ -4,6 +4,7 @@ import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprot
|
|
|
4
4
|
import { Logger } from '../utils/logger.js';
|
|
5
5
|
import { ToolRegistry } from './tool-registry.js';
|
|
6
6
|
import { tierOf } from './tool-tiers.js';
|
|
7
|
+
import { groupOf, GROUPS } from './tool-groups.js';
|
|
7
8
|
import { EDITION } from '../edition.js';
|
|
8
9
|
import { VERSION } from '../version.js';
|
|
9
10
|
import { Session } from './session.js';
|
|
@@ -14,7 +15,7 @@ import { join, dirname } from 'node:path';
|
|
|
14
15
|
import { pathToFileURL } from 'node:url';
|
|
15
16
|
import { GoSnippetClient, resolveProBinaryPath, coreBinaryName } from '../api/snippet-client.js';
|
|
16
17
|
import { isProEntitled } from '../license/entitlement.js';
|
|
17
|
-
import {
|
|
18
|
+
import { loadVerifiedModule, PRO_SKU } from '../delivery/store.js';
|
|
18
19
|
import { runScript } from '../utils/run-script.js';
|
|
19
20
|
import { listTemplates } from '../utils/template-storage.js';
|
|
20
21
|
import { Kernel } from '../kernel/kernel.js';
|
|
@@ -133,6 +134,38 @@ export class EditmameiServer {
|
|
|
133
134
|
},
|
|
134
135
|
handler: async () => await this.pingPhotoshop(),
|
|
135
136
|
});
|
|
137
|
+
this.toolRegistry.register('photoshop_list_capabilities', {
|
|
138
|
+
tool: {
|
|
139
|
+
name: 'photoshop_list_capabilities',
|
|
140
|
+
description: 'Live map of every tool this MCP exposes, grouped by capability — each group is a one-line purpose plus the tool names in it. Read-only and cheap. Reach for it to re-orient mid-session (what exists, what to reach for) when the overview brief has scrolled out of context, or to learn which tool names to look up. photoshop_overview tells you HOW to combine tools; this tells you WHAT exists, currently; tools/list has the full schema for any one you pick.',
|
|
141
|
+
inputSchema: { type: 'object', properties: {} },
|
|
142
|
+
outputSchema: {
|
|
143
|
+
type: 'object',
|
|
144
|
+
properties: {
|
|
145
|
+
tool_count: { type: 'number' },
|
|
146
|
+
group_count: { type: 'number' },
|
|
147
|
+
groups: {
|
|
148
|
+
type: 'array',
|
|
149
|
+
items: {
|
|
150
|
+
type: 'object',
|
|
151
|
+
properties: {
|
|
152
|
+
id: { type: 'string' },
|
|
153
|
+
label: { type: 'string' },
|
|
154
|
+
purpose: { type: 'string' },
|
|
155
|
+
tools: { type: 'array', items: { type: 'string' } },
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
annotations: {
|
|
162
|
+
title: 'List Capabilities',
|
|
163
|
+
readOnlyHint: true,
|
|
164
|
+
idempotentHint: true,
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
handler: async () => this.listCapabilities(),
|
|
168
|
+
});
|
|
136
169
|
this.proModule = this.resolveProModule();
|
|
137
170
|
this.kernel = new Kernel({
|
|
138
171
|
registry: this.toolRegistry,
|
|
@@ -150,12 +183,11 @@ export class EditmameiServer {
|
|
|
150
183
|
}
|
|
151
184
|
resolveProModule() {
|
|
152
185
|
if (isProEntitled()) {
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
155
|
-
const entry = moduleHandlersPath(PRO_SKU, installed.version);
|
|
186
|
+
const verified = loadVerifiedModule(PRO_SKU);
|
|
187
|
+
if (verified) {
|
|
156
188
|
return {
|
|
157
|
-
importer: () => import(pathToFileURL(
|
|
158
|
-
binDir:
|
|
189
|
+
importer: () => import(pathToFileURL(verified.handlersPath).href),
|
|
190
|
+
binDir: verified.binDir,
|
|
159
191
|
};
|
|
160
192
|
}
|
|
161
193
|
}
|
|
@@ -182,7 +214,40 @@ export class EditmameiServer {
|
|
|
182
214
|
assertToolsClassified() {
|
|
183
215
|
for (const tool of this.toolRegistry.list()) {
|
|
184
216
|
tierOf(tool.name);
|
|
217
|
+
groupOf(tool.name);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
listCapabilities() {
|
|
221
|
+
const registered = this.toolRegistry.list();
|
|
222
|
+
const toolsByGroup = new Map();
|
|
223
|
+
for (const tool of registered) {
|
|
224
|
+
const g = groupOf(tool.name);
|
|
225
|
+
const arr = toolsByGroup.get(g) ?? [];
|
|
226
|
+
arr.push(tool.name);
|
|
227
|
+
toolsByGroup.set(g, arr);
|
|
185
228
|
}
|
|
229
|
+
const groups = Object.keys(GROUPS)
|
|
230
|
+
.map((id) => ({
|
|
231
|
+
id,
|
|
232
|
+
label: GROUPS[id].label,
|
|
233
|
+
purpose: GROUPS[id].purpose,
|
|
234
|
+
tools: (toolsByGroup.get(id) ?? []).sort(),
|
|
235
|
+
}))
|
|
236
|
+
.filter((g) => g.tools.length > 0);
|
|
237
|
+
const text = `Editmamei exposes ${registered.length} tools across ${groups.length} capability groups. ` +
|
|
238
|
+
`This is the live map of WHAT exists; photoshop_overview is HOW to combine them, and ` +
|
|
239
|
+
`tools/list has the full schema for any one tool.\n\n` +
|
|
240
|
+
groups
|
|
241
|
+
.map((g) => `${g.label} (${g.tools.length}) — ${g.purpose}\n ${g.tools.join(', ')}`)
|
|
242
|
+
.join('\n');
|
|
243
|
+
return {
|
|
244
|
+
content: [{ type: 'text', text }],
|
|
245
|
+
structuredContent: {
|
|
246
|
+
tool_count: registered.length,
|
|
247
|
+
group_count: groups.length,
|
|
248
|
+
groups,
|
|
249
|
+
},
|
|
250
|
+
};
|
|
186
251
|
}
|
|
187
252
|
setupHandlers() {
|
|
188
253
|
this.server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
@@ -193,26 +258,29 @@ export class EditmameiServer {
|
|
|
193
258
|
});
|
|
194
259
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
195
260
|
this.logger.debug(`Tool called: ${request.params.name}`);
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const result = await this.toolRegistry.execute(request.params.name, args);
|
|
199
|
-
this.session.updateActivity();
|
|
200
|
-
return result;
|
|
201
|
-
}
|
|
202
|
-
catch (error) {
|
|
203
|
-
this.logger.error(`Tool execution failed: ${request.params.name}`, error);
|
|
204
|
-
return {
|
|
205
|
-
content: [
|
|
206
|
-
{
|
|
207
|
-
type: 'text',
|
|
208
|
-
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
209
|
-
},
|
|
210
|
-
],
|
|
211
|
-
isError: true,
|
|
212
|
-
};
|
|
213
|
-
}
|
|
261
|
+
const args = request.params.arguments || {};
|
|
262
|
+
return this.handleToolCall(request.params.name, args);
|
|
214
263
|
});
|
|
215
264
|
}
|
|
265
|
+
async handleToolCall(name, args) {
|
|
266
|
+
try {
|
|
267
|
+
const result = await this.toolRegistry.execute(name, args);
|
|
268
|
+
this.session.updateActivity();
|
|
269
|
+
return result;
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
this.logger.error(`Tool execution failed: ${name}`, error);
|
|
273
|
+
return {
|
|
274
|
+
content: [
|
|
275
|
+
{
|
|
276
|
+
type: 'text',
|
|
277
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
278
|
+
},
|
|
279
|
+
],
|
|
280
|
+
isError: true,
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
}
|
|
216
284
|
async pingPhotoshop() {
|
|
217
285
|
const connection = this.session.getConnection();
|
|
218
286
|
const isConnected = await connection.ping();
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
export const GROUPS = {
|
|
2
|
+
core: {
|
|
3
|
+
id: 'core',
|
|
4
|
+
label: 'Core',
|
|
5
|
+
purpose: 'Session liveness, orientation, and undo/redo — always available.',
|
|
6
|
+
},
|
|
7
|
+
inspect: {
|
|
8
|
+
id: 'inspect',
|
|
9
|
+
label: 'Inspect',
|
|
10
|
+
purpose: 'Read document/layer/selection/history state and render previews.',
|
|
11
|
+
},
|
|
12
|
+
verify: {
|
|
13
|
+
id: 'verify',
|
|
14
|
+
label: 'Verify',
|
|
15
|
+
purpose: 'Measurement primitives — histogram, region compare, bounds diff — to check work instead of eyeballing.',
|
|
16
|
+
},
|
|
17
|
+
document: {
|
|
18
|
+
id: 'document',
|
|
19
|
+
label: 'Document & canvas',
|
|
20
|
+
purpose: 'Open/create/save/export documents; resize/crop/convert/rotate the canvas; place images; guides.',
|
|
21
|
+
},
|
|
22
|
+
select: {
|
|
23
|
+
id: 'select',
|
|
24
|
+
label: 'Selection',
|
|
25
|
+
purpose: 'Manual selections, selection edits, and alpha-channel save/load.',
|
|
26
|
+
},
|
|
27
|
+
select_ai: {
|
|
28
|
+
id: 'select_ai',
|
|
29
|
+
label: 'AI selection',
|
|
30
|
+
purpose: 'Sensei/CV-backed subject & sky selection (Pro).',
|
|
31
|
+
},
|
|
32
|
+
adjust: {
|
|
33
|
+
id: 'adjust',
|
|
34
|
+
label: 'Adjustments',
|
|
35
|
+
purpose: 'Non-destructive adjustment layers + the destructive tonal bakes with no layer equivalent.',
|
|
36
|
+
},
|
|
37
|
+
filter: {
|
|
38
|
+
id: 'filter',
|
|
39
|
+
label: 'Filters',
|
|
40
|
+
purpose: 'Blur/sharpen/noise/stylize filters (auto-duplicate-first by default).',
|
|
41
|
+
},
|
|
42
|
+
retouch: {
|
|
43
|
+
id: 'retouch',
|
|
44
|
+
label: 'Retouch',
|
|
45
|
+
purpose: 'Content-aware fill/patch/move and brush strokes.',
|
|
46
|
+
},
|
|
47
|
+
layers: {
|
|
48
|
+
id: 'layers',
|
|
49
|
+
label: 'Layers',
|
|
50
|
+
purpose: 'Layer lifecycle, properties, transforms, ordering, groups, and styles.',
|
|
51
|
+
},
|
|
52
|
+
masks: {
|
|
53
|
+
id: 'masks',
|
|
54
|
+
label: 'Masks & paths',
|
|
55
|
+
purpose: 'Layer masks, clipping masks, vector masks, and work paths.',
|
|
56
|
+
},
|
|
57
|
+
type: {
|
|
58
|
+
id: 'type',
|
|
59
|
+
label: 'Type',
|
|
60
|
+
purpose: 'Create and style text layers.',
|
|
61
|
+
},
|
|
62
|
+
perception: {
|
|
63
|
+
id: 'perception',
|
|
64
|
+
label: 'Perception',
|
|
65
|
+
purpose: 'On-device CV that gives the model real coordinates/selections — detection, scene read, and detection-driven edits.',
|
|
66
|
+
},
|
|
67
|
+
face: {
|
|
68
|
+
id: 'face',
|
|
69
|
+
label: 'Face mesh (Pro)',
|
|
70
|
+
purpose: 'Face-landmark perception and mesh-aimed feature selection / contour strokes (Pro).',
|
|
71
|
+
},
|
|
72
|
+
templates: {
|
|
73
|
+
id: 'templates',
|
|
74
|
+
label: 'Templates (Pro)',
|
|
75
|
+
purpose: 'Save, recall, apply, and verify reproducible aesthetic recipes (Pro).',
|
|
76
|
+
},
|
|
77
|
+
automation: {
|
|
78
|
+
id: 'automation',
|
|
79
|
+
label: 'Automation (Pro)',
|
|
80
|
+
purpose: 'Play recorded Actions and the ExtendScript escape hatch (Pro).',
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
export const TOOL_GROUPS = {
|
|
84
|
+
photoshop_ping: 'core',
|
|
85
|
+
photoshop_overview: 'core',
|
|
86
|
+
photoshop_undo: 'core',
|
|
87
|
+
photoshop_redo: 'core',
|
|
88
|
+
photoshop_list_capabilities: 'core',
|
|
89
|
+
photoshop_inspect: 'inspect',
|
|
90
|
+
photoshop_get_preview: 'inspect',
|
|
91
|
+
photoshop_get_histogram: 'verify',
|
|
92
|
+
photoshop_compare_regions: 'verify',
|
|
93
|
+
photoshop_get_layer_bounds_diff: 'verify',
|
|
94
|
+
photoshop_get_selection_preview: 'verify',
|
|
95
|
+
photoshop_create_document: 'document',
|
|
96
|
+
photoshop_open_document: 'document',
|
|
97
|
+
photoshop_close_document: 'document',
|
|
98
|
+
photoshop_save_psd: 'document',
|
|
99
|
+
photoshop_export: 'document',
|
|
100
|
+
photoshop_place_image: 'document',
|
|
101
|
+
photoshop_resize_image: 'document',
|
|
102
|
+
photoshop_crop_document: 'document',
|
|
103
|
+
photoshop_convert_image_mode: 'document',
|
|
104
|
+
photoshop_transform_canvas: 'document',
|
|
105
|
+
photoshop_guides: 'document',
|
|
106
|
+
photoshop_select: 'select',
|
|
107
|
+
photoshop_modify_selection: 'select',
|
|
108
|
+
photoshop_selection_channel: 'select',
|
|
109
|
+
photoshop_select_subject: 'select_ai',
|
|
110
|
+
photoshop_select_sky: 'select_ai',
|
|
111
|
+
photoshop_select_subject_instance: 'select_ai',
|
|
112
|
+
photoshop_add_adjustment_layer: 'adjust',
|
|
113
|
+
photoshop_apply_adjustment: 'adjust',
|
|
114
|
+
photoshop_apply_filter: 'filter',
|
|
115
|
+
photoshop_retouch: 'retouch',
|
|
116
|
+
photoshop_apply_brush_stroke: 'retouch',
|
|
117
|
+
photoshop_create_layer: 'layers',
|
|
118
|
+
photoshop_delete_layer: 'layers',
|
|
119
|
+
photoshop_fill_layer: 'layers',
|
|
120
|
+
photoshop_add_fill_layer: 'layers',
|
|
121
|
+
photoshop_select_layer: 'layers',
|
|
122
|
+
photoshop_move_layer_to_position: 'layers',
|
|
123
|
+
photoshop_duplicate_layer: 'layers',
|
|
124
|
+
photoshop_layer_via_copy: 'layers',
|
|
125
|
+
photoshop_convert_to_smart_object: 'layers',
|
|
126
|
+
photoshop_rasterize_layer: 'layers',
|
|
127
|
+
photoshop_set_layer: 'layers',
|
|
128
|
+
photoshop_merge: 'layers',
|
|
129
|
+
photoshop_bake_layer: 'layers',
|
|
130
|
+
photoshop_add_layer_style: 'layers',
|
|
131
|
+
photoshop_transform_layer: 'layers',
|
|
132
|
+
photoshop_warp_layer: 'layers',
|
|
133
|
+
photoshop_warp_layer_mesh: 'layers',
|
|
134
|
+
photoshop_create_group: 'layers',
|
|
135
|
+
photoshop_move_layer_to_group: 'layers',
|
|
136
|
+
photoshop_set_group_blend_mode: 'layers',
|
|
137
|
+
photoshop_ungroup: 'layers',
|
|
138
|
+
photoshop_delete_group: 'layers',
|
|
139
|
+
photoshop_layer_mask: 'masks',
|
|
140
|
+
photoshop_create_clipping_mask: 'masks',
|
|
141
|
+
photoshop_release_clipping_mask: 'masks',
|
|
142
|
+
photoshop_path: 'masks',
|
|
143
|
+
photoshop_vector_mask: 'masks',
|
|
144
|
+
photoshop_create_text_layer: 'type',
|
|
145
|
+
photoshop_set_text: 'type',
|
|
146
|
+
photoshop_detect: 'perception',
|
|
147
|
+
photoshop_scene: 'perception',
|
|
148
|
+
photoshop_select_by_reference: 'perception',
|
|
149
|
+
photoshop_object: 'perception',
|
|
150
|
+
photoshop_portrait_touchup: 'perception',
|
|
151
|
+
photoshop_text_on_object: 'perception',
|
|
152
|
+
photoshop_detect_landmarks: 'face',
|
|
153
|
+
photoshop_select_face_feature: 'face',
|
|
154
|
+
photoshop_stroke_face_contour: 'face',
|
|
155
|
+
photoshop_template_create_evidence: 'templates',
|
|
156
|
+
photoshop_template_save: 'templates',
|
|
157
|
+
photoshop_template_list: 'templates',
|
|
158
|
+
photoshop_template_apply: 'templates',
|
|
159
|
+
photoshop_template_verify: 'templates',
|
|
160
|
+
photoshop_template_recall: 'templates',
|
|
161
|
+
photoshop_template_delete: 'templates',
|
|
162
|
+
photoshop_list_actions: 'automation',
|
|
163
|
+
photoshop_play_action: 'automation',
|
|
164
|
+
photoshop_execute_script: 'automation',
|
|
165
|
+
};
|
|
166
|
+
export function groupOf(toolName) {
|
|
167
|
+
const group = TOOL_GROUPS[toolName];
|
|
168
|
+
if (!group) {
|
|
169
|
+
throw new Error(`Tool '${toolName}' has no entry in src/core/tool-groups.ts. ` +
|
|
170
|
+
`Every registered tool must be assigned a capability group.`);
|
|
171
|
+
}
|
|
172
|
+
return group;
|
|
173
|
+
}
|
|
174
|
+
export function toolsInGroup(group) {
|
|
175
|
+
return Object.entries(TOOL_GROUPS)
|
|
176
|
+
.filter(([, g]) => g === group)
|
|
177
|
+
.map(([name]) => name);
|
|
178
|
+
}
|