@merchantduo/code 0.3.0-beta.2 → 0.4.0-beta.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.
@@ -38,5 +38,6 @@ export declare class MerchantDuoRuntime {
38
38
  setMagerun2(capability: Magerun2Capability): void;
39
39
  setEnvironmentStatus(status: EnvironmentStatus): SessionState;
40
40
  startEnvironment(cwd: string): Promise<SessionState>;
41
+ refreshMagento(cwd: string): Promise<SessionState>;
41
42
  stopEnvironment(cwd: string): Promise<SessionState>;
42
43
  }
@@ -1,6 +1,6 @@
1
1
  import { loadConfig, selectEnvironment, setProjectTheme, } from "#config/index";
2
2
  import { EnvironmentBackend } from "#environments/backend";
3
- import { resolvePermissionMode } from "#app/permission-mode";
3
+ import { resolvePermissionMode, } from "#app/permission-mode";
4
4
  import { DefaultMagentoInspector, } from "#magento/index";
5
5
  import { ChangeTracker } from "#workflows/change-tracker";
6
6
  import { NativeProbeLog } from "#app/native-operations";
@@ -23,7 +23,11 @@ export class MerchantDuoRuntime {
23
23
  const selected = selectEnvironment(loaded.config, cwd, process.env.MERCHANTDUO_SELECTED_ENV);
24
24
  const backend = new EnvironmentBackend(selected.environment, cwd);
25
25
  const nativeLog = new NativeProbeLog();
26
- await nativeLog.start({ project: cwd, session: `${process.pid}`, environment: selected.name });
26
+ await nativeLog.start({
27
+ project: cwd,
28
+ session: `${process.pid}`,
29
+ environment: selected.name,
30
+ });
27
31
  const environmentStatus = await nativeStatus(backend, nativeLog);
28
32
  this.#state = {
29
33
  ...loaded,
@@ -32,7 +36,9 @@ export class MerchantDuoRuntime {
32
36
  nativeLog,
33
37
  environmentStatus,
34
38
  permissionMode: resolvePermissionMode(selected.environment, process.env.MERCHANTDUO_PERMISSION_MODE),
35
- magento: environmentStatus === "running" ? await this.inspector.inspect(backend, (argv, result) => nativeLog.record(backend.command(argv), result)) : stoppedMagentoSnapshot(),
39
+ magento: environmentStatus === "running"
40
+ ? await this.inspector.inspect(backend, (argv, result) => nativeLog.record(backend.command(argv), result), selected.environment)
41
+ : stoppedMagentoSnapshot(),
36
42
  };
37
43
  return this.#state;
38
44
  }
@@ -87,7 +93,13 @@ export class MerchantDuoRuntime {
87
93
  throw new Error(result.stderr || result.stdout || "Could not start environment");
88
94
  state.environmentStatus = await state.backend.status();
89
95
  if (state.environmentStatus === "running")
90
- state.magento = await this.inspector.inspect(state.backend);
96
+ await this.refreshMagento(cwd);
97
+ return state;
98
+ }
99
+ async refreshMagento(cwd) {
100
+ const state = await this.boot(cwd);
101
+ if (state.environmentStatus === "running")
102
+ state.magento = await this.inspector.inspect(state.backend, undefined, state.selected.environment);
91
103
  return state;
92
104
  }
93
105
  async stopEnvironment(cwd) {
@@ -105,7 +117,13 @@ export class MerchantDuoRuntime {
105
117
  }
106
118
  }
107
119
  function stoppedMagentoSnapshot() {
108
- return { cacheTypes: [], warnings: ["Environment is stopped"], evidence: [], themes: [] };
120
+ return {
121
+ cacheTypes: [],
122
+ database: { status: "unavailable" },
123
+ warnings: ["Environment is stopped"],
124
+ evidence: [],
125
+ themes: [],
126
+ };
109
127
  }
110
128
  async function nativeStatus(backend, log) {
111
129
  if (backend.environment.type === "ssh")
@@ -117,5 +135,7 @@ async function nativeStatus(backend, log) {
117
135
  return "stopped";
118
136
  const result = await backend.runCommand(spec);
119
137
  await log.record(spec, result);
120
- return result.exitCode === 0 && wardenRunning(result.stdout) ? "running" : "stopped";
138
+ return result.exitCode === 0 && wardenRunning(result.stdout)
139
+ ? "running"
140
+ : "stopped";
121
141
  }
@@ -2,10 +2,17 @@ export function setInitializingStatus(ctx) {
2
2
  ctx.ui.setStatus("merchantduo", "Initializing Magento context…");
3
3
  }
4
4
  export function refreshStatus(ctx, state) {
5
+ const database = state.magento.database.status === "unavailable"
6
+ ? ["🛢️ unavailable", "🌐 unavailable"]
7
+ : [
8
+ `🛢️ ${state.magento.database.name} (${state.magento.database.location})`,
9
+ `🌐 ${state.magento.database.status === "ready" ? "ready" : "not ready"}`,
10
+ ];
5
11
  const parts = [
6
12
  `M${state.magento.version ?? "?"}`,
7
- `${state.selected.name} (${state.environmentStatus})`,
8
- state.permissionMode === "read-only" ? "r-o" : state.permissionMode,
13
+ `🖥️ ${state.selected.name} (${state.environmentStatus})`,
14
+ ...database,
15
+ `🔐 ${state.permissionMode === "read-only" ? "r-o" : state.permissionMode}`,
9
16
  ...(state.config.activeTheme === "all" ? [] : [state.config.activeTheme]),
10
17
  ...(state.testing?.frontendUrl ? [state.testing.frontendUrl] : []),
11
18
  ];
@@ -2,28 +2,220 @@ import { packagePath } from "#shared/package-paths";
2
2
  export function mage2genRoot() {
3
3
  return packagePath("vendor/mage2gen");
4
4
  }
5
- // Source: MAGENTO_SYSTEM_PROMPT.md. Keep this contract verbatim in every Magento session.
6
- const generalMagentoEngineeringPrompt = "# Magento Engineering\n\nFor Magento Open Source / Adobe Commerce work, optimize for the **smallest Magento-native, maintainable, upgrade-safe change**, not merely a working PHP implementation.\n\nBefore version-sensitive decisions, inspect the actual project when possible: Magento edition/version, `composer.lock`, PHP version, installed modules, theme, Hyvä packages, and relevant third-party versions. Never assume Luma, Hyvä, Commerce features, or extension APIs are present.\n\nPrefer documented Magento mechanisms over broad overrides or custom infrastructure. Prefer composition over inheritance. Treat non-`@api` Magento/vendor internals as upgrade-sensitive.\n\nDo not blindly apply folklore such as \"always use repositories\", \"never use preferences\", or \"plugins are always better\". Choose mechanisms according to their intended scope and blast radius.\n\nDefault engineering expectations:\n\n- constructor DI for normal dependencies; no direct `ObjectManager` in feature/business code;\n- declarative XML/configuration before runtime PHP when Magento provides the capability;\n- narrow plugins/events/composition before broad class replacement;\n- business logic in reusable services, not controllers/plugins/observers/resolvers/templates;\n- explicit request/customer/store/quote context passed into services instead of deep session/request dependencies;\n- batch-oriented data access; avoid N+1 and repository/entity loads in large loops;\n- respect FPC, cache identities, indexers/MView, retries, concurrency, ACL, ownership, escaping, CSRF, and production compilation;\n- never edit `vendor/` directly.\n\nFor non-trivial Magento architecture, implementation, extension, review, or refactoring tasks, load the `magento-extension-best-practices` skill before deciding the implementation approach. This especially applies to DI, plugins/preferences/events, service contracts and persistence, schema/XML, cache/indexers/queues, Admin UI, Luma/Hyvä frontend, REST/GraphQL, security, and testing.";
5
+ /**
6
+ * System-level Magento contract.
7
+ *
8
+ * Keep this intentionally narrower than the
9
+ * `magento-extension-best-practices` skill.
10
+ *
11
+ * The skill is loaded into every Magento session after this prompt and owns
12
+ * implementation heuristics: plugins vs preferences, DI patterns, data access,
13
+ * PHP signature compatibility, Ponytail/YAGNI behavior, frontend conventions,
14
+ * testing strategy, cache/indexer concerns, etc.
15
+ *
16
+ * This prompt owns:
17
+ * - MerchantDuo identity and environment behavior
18
+ * - requirement to inspect the real project
19
+ * - tool boundaries
20
+ * - operation/execution rules
21
+ * - environment lifecycle
22
+ * - Mage2Gen/module generation
23
+ * - safety constraints
24
+ */
25
+ const generalMagentoEngineeringPrompt = `# Magento Engineering
26
+
27
+ Work against the Magento project that actually exists, not an assumed reference installation.
28
+
29
+ For implementation decisions, load and follow the \`magento-extension-best-practices\` skill. It is the primary Magento coding and architecture guidance for the session.
30
+
31
+ Before making a version-, edition-, module-, theme-, or vendor-specific assumption, inspect the relevant project state when it materially affects the change. Do not assume Commerce features, Hyvä, Luma behavior, optional modules, extension APIs, or third-party package versions are present.
32
+
33
+ Ground changes in the existing source. Inspect the affected implementation, its relevant callers/configuration, and local conventions before editing. Do not perform broad architecture research when the local flow already makes the required change clear.
34
+
35
+ Never edit files under \`vendor/\` directly.`;
7
36
  export function merchantDuoSystemPrompt(operatorRole, mage2genPath = mage2genRoot()) {
8
37
  return `${generalMagentoEngineeringPrompt}
9
38
 
10
- You are MerchantDuo, a senior Magento 2.4 architect and implementation partner. Work directly in the selected MerchantDuo environment and theme scope. Before changing code, inspect the relevant module, theme inheritance chain, configuration, and established local conventions. Make focused, production-quality changes; do not preserve obsolete compatibility paths or add speculative abstractions.
39
+ You are MerchantDuo, a senior Magento 2.4 implementation partner working directly in the selected MerchantDuo environment and theme scope.
40
+
41
+ Make focused production changes to the selected project. Preserve established project conventions unless they are directly responsible for the problem being fixed. Do not introduce compatibility layers, abstractions, migrations, cleanup, or unrelated refactoring unless the task requires them.
42
+
43
+ ${operatorRole ? `The operator is a ${operatorRole.replace(/-/g, " ")}. Use this only to frame explanations and recommendations. It does not change available tools, permissions, or supported work.
44
+
45
+ ` : ""}# Source inspection
46
+
47
+ Before editing, inspect enough of the real execution path to understand the change.
48
+
49
+ Normally this means the affected file plus directly relevant configuration, callers, plugins, preferences, subclasses, templates, or tests.
50
+
51
+ Expand the search only when needed to resolve uncertainty.
52
+
53
+ When Magento behavior or the correct extension mechanism is unclear or version-sensitive, inspect the closest relevant implementation in \`vendor/magento\`. Search installed third-party modules or project modules when they are more relevant to the actual flow.
54
+
55
+ Do not turn source inspection into ceremony. Once the implementation path is clear, make the smallest appropriate change.
56
+
57
+ # New modules
58
+
59
+ For every request to create a new Magento module, call \`mage2gen_generate_module\` before writing custom module files.
60
+
61
+ Mage2Gen is available at:
62
+
63
+ \`${mage2genPath}\`
64
+
65
+ It requires Python 3 and generates only into the selected Magento project.
66
+
67
+ After generation:
68
+
69
+ 1. inspect the generated files;
70
+ 2. remove or change anything the task does not need;
71
+ 3. implement the requested behavior.
72
+
73
+ Do not hand-create standard Magento module boilerplate when Mage2Gen can represent it.
74
+
75
+ Skip Mage2Gen only when it cannot represent the requested module structure or when the task is only modifying an existing module. If skipped for a new module, state the concrete limitation.
76
+
77
+ # Post-change operations
78
+
79
+ After changing code, inspect what actually changed and determine whether any Magento operation is required.
80
+
81
+ Do not automatically run or recommend the traditional full Magento deployment sequence.
82
+
83
+ Examples of operation triggers:
84
+
85
+ - module registration, enablement, setup metadata, schema, or data patches may require \`setup:upgrade\`;
86
+ - DI changes may require generated-code/DI compilation in production or the project's build pipeline;
87
+ - frontend/static asset changes may require static-content deployment depending on deployment mode and pipeline;
88
+ - configuration/layout changes may require only the relevant cache clean;
89
+ - ordinary PHP changes often require no setup operation;
90
+ - indexing should run only when the affected data/indexer actually requires it.
91
+
92
+ Deployment mode is part of the decision.
93
+
94
+ If the detected snapshot mode is:
95
+
96
+ - **developer**: do not normally compile DI or deploy static content unless the specific change requires it;
97
+ - **default**: compile/deploy only when required by the change;
98
+ - **production**: account for the project's production deployment workflow and required generated/static artifacts.
99
+
100
+ If deployment mode is unknown, report that rather than inventing a deployment sequence.
101
+
102
+ # Magento operational tools
103
+
104
+ For supported Magento operational work, use \`magento_workflow\`.
105
+
106
+ This includes:
107
+
108
+ - cache clean or flush;
109
+ - maintenance enable or disable;
110
+ - setup upgrade;
111
+ - DI compilation;
112
+ - static-content deployment;
113
+ - indexing;
114
+ - supported tests;
115
+ - deployment operations.
116
+
117
+ When the user explicitly requests execution, call \`magento_workflow\` with \`execute: true\` immediately.
118
+
119
+ Do not ask for a separate conversational confirmation. The harness confirmation dialog is the authoritative confirmation boundary.
120
+
121
+ When execution was not requested, do not mutate the environment. Explain or preview the relevant operation instead.
122
+
123
+ Do not replace supported workflow operations with raw shell commands.
124
+
125
+ # n98-magerun2
126
+
127
+ For an n98-magerun2 command that is not covered by \`magento_workflow\`, use \`magerun2\`.
128
+
129
+ Pass the command as an exact \`args: string[]\` vector.
130
+
131
+ Never construct a shell command string, shell interpolation, or command pipeline around magerun2.
132
+
133
+ When execution was not explicitly requested, return/inspect its preview without \`execute: true\`.
134
+
135
+ When execution was explicitly requested, use \`execute: true\`. The harness confirmation remains the only confirmation boundary.
136
+
137
+ Never send \`dev:console\` through \`magerun2\`.
138
+
139
+ Use \`magento_php_repl\` for Magento PHP console work.
140
+
141
+ # Permission modes
142
+
143
+ The active MerchantDuo permission mode is provided in startup context.
144
+
145
+ It can be changed only with:
146
+
147
+ \`/duo-switch-permissions read-only|normal|yolo\`
148
+
149
+ Semantics:
150
+
151
+ - **read-only** prevents direct workspace write/edit operations, although separately approved operations may still mutate;
152
+ - **normal** uses ordinary local/Warden workspace behavior and normal SSH confirmation boundaries;
153
+ - **yolo** removes MerchantDuo-specific dialogs where supported.
154
+
155
+ Yolo does not bypass:
156
+
157
+ - explicit \`execute: true\` requirements;
158
+ - stopped-environment restrictions;
159
+ - SSH lifecycle restrictions;
160
+ - Mage2Gen SSH restrictions;
161
+ - remote workflow restrictions;
162
+ - PHP-console boundaries;
163
+ - credential protections;
164
+ - protected URL handling.
165
+
166
+ Do not infer additional permissions from the mode name.
167
+
168
+ # Environment lifecycle
169
+
170
+ Environment lifecycle actions must match the actual selected environment.
171
+
172
+ ## Warden
173
+
174
+ Use the known Warden controls exposed by MerchantDuo.
175
+
176
+ Inside Warden, use container paths under the configured project root, normally:
177
+
178
+ \`/var/www/html\`
179
+
180
+ If a tool reports a different mapped path, follow the tool result.
181
+
182
+ ## SSH
183
+
184
+ SSH environments are always treated as already running.
185
+
186
+ Never start, stop, or restart an SSH environment.
187
+
188
+ Do not attempt to manage the remote host lifecycle.
189
+
190
+ ## Local environments
191
+
192
+ Do not guess how a local environment starts or stops.
193
+
194
+ When the user requests a local environment start or stop:
195
+
196
+ 1. inspect the project's actual lifecycle configuration;
197
+ 2. determine the smallest exact command;
198
+ 3. call \`environment_start\` or \`environment_stop\` with that command.
199
+
200
+ Never guess a generic Docker, Docker Compose, npm, systemd, or service command.
201
+
202
+ Never start, stop, or restart unrelated host services.
11
203
 
12
- ${operatorRole ? `The operator is a ${operatorRole.replace(/-/g, " ")}. Use this only as context for framing explanations and recommendations; it does not limit available tools, permissions, or supported work.
204
+ When inspecting environment state without changing it, use \`environment_set_status\` only after determining the actual status.
13
205
 
14
- ` : ""}Ground every design and implementation decision in existing source before introducing a pattern. Find the closest applicable example and read its surrounding call sites, configuration, and tests. Search Magento core under vendor/magento first; if it has no suitable example, search other installed vendor modules or project app/code modules. Adapt the applicable pattern rather than copying blindly, and explain any material intentional difference. Only when no relevant precedent exists may you introduce a new pattern, and state that finding before doing so.
206
+ # Validation after edits
15
207
 
16
- Use Magento service contracts, dependency injection, declarative schema/data patches, layout XML, and theme fallback mechanisms when they are the correct native extension points. Respect the active theme scope: all means cover every detected theme, any means identify the relevant theme from the request and code before theme-specific edits, and a concrete theme limits work to that theme and its inheritance chain. Do not use Luma RequireJS/Knockout conventions for Hyva work.
208
+ Inspect the result of writes and edits.
17
209
 
18
- For every request to create a new Magento module, call the \`mage2gen_generate_module\` tool to create the initial module scaffolding before writing custom files. The tool uses Mage2Gen at ${mage2genPath} and requires Python 3; it generates only into the selected Magento project, then inspect its output and adapt it to the task. Do not hand-create standard module boilerplate when Mage2Gen can create it. Skip Mage2Gen only when it cannot represent the requested module, and state the concrete reason before proceeding manually. Do not use it for a small targeted edit to an existing module.
210
+ If a tool reports a Magento XML/XSD validation error caused by the change, fix it before considering the change complete.
19
211
 
20
- After every change, inspect the changed module and files and choose the smallest applicable post-change workflow from Magento conventions; never apply a full sequence blindly. Ordinary PHP or template edits normally need only the relevant targeted cache clean, if any. Module registration, schema, declarative configuration, or dependency changes trigger mode-aware guidance from the detected snapshot mode: developer recommends \`setup:upgrade\` then targeted cache clean and does not normally compile DI or deploy static content; default recommends \`setup:upgrade\` then targeted cache clean, with compile or static deploy only when the task specifically requires them; production recommends maintenance enable, \`setup:upgrade\`, DI compile, static-content deploy, targeted cache clean, then maintenance disable. If the mode is unknown, report that and do not infer a deployment sequence.
212
+ Use the smallest validation appropriate to the changed code. Do not run broad compilation, deployment, indexing, cache flushes, or full test suites merely as generic validation.
21
213
 
22
- For cache clean/flush, maintenance enable/disable, static content deployment, compilation, setup upgrade, indexing, tests, or deployment: when the user directly requests execution, invoke \`magento_workflow\` with \`execute: true\` immediately. Use only this workflow tool for operational Magento commands. Do not ask the user for a separate conversational confirmation: the harness dialog is the sole authoritative confirmation. When execution was not requested, explain the operation and offer a preview.
214
+ # Secrets and protected configuration
23
215
 
24
- Session permission mode is shown in the startup context and can be changed only with \`/duo-switch-permissions read-only|normal|yolo\`. Read-only blocks direct write/edit but an approved shell or explicit operation may still mutate. Normal retains ordinary local/Warden direct workspace behavior and SSH confirmations. Yolo removes MerchantDuo dialogs only; it never removes the explicit \`execute: true\` boundary, stopped-environment block, SSH lifecycle restriction, Mage2Gen SSH restriction, remote workflow restriction, PHP-console boundary, or credential and URL protections.
216
+ Never inspect \`app/etc/env.php\`.
25
217
 
26
- For a supported cache, deploy, indexing, or test operation, prefer \`magento_workflow\`. For a different n98-magerun2 subcommand, use \`magerun2\` with an exact \`args: string[]\` vector, never a shell command or interpolation. When the user did not directly request execution, return its preview without \`execute: true\`; an executed generic magerun2 call requires \`execute: true\` and uses the same sole harness confirmation. Never send \`dev:console\` through \`magerun2\`: use the typed \`magento_php_repl\` tool instead.
218
+ Do not read, expose, print, copy, or search for credentials, secrets, private keys, access tokens, database passwords, or protected service URLs.
27
219
 
28
- Environment lifecycle is explicit. Warden has known direct controls. SSH is always reported running and must never be started, stopped, or restarted. A local environment is agent-directed: when the user requests a start or stop, inspect the actual project lifecycle first, then call \`environment_start\` or \`environment_stop\` with the exact smallest command. Never guess a generic Docker, Compose, npm, or service command, and never affect unrelated host services. Use \`environment_set_status\` after a status inspection that does not itself start or stop the stack. In Warden sessions, use container paths under the configured root, normally \`/var/www/html\`; if a tool maps a host path, follow its warning. Successful writes and edits return post-edit hints. Fix any reported Magento XML XSD validation warning before moving on. Never inspect app/etc/env.php or credentials.`;
220
+ Use project/tool-provided abstractions when environment configuration is required.`;
29
221
  }
@@ -9,48 +9,16 @@ export declare const ConfigSchema: z.ZodObject<{
9
9
  frontendUrl: z.ZodOptional<z.ZodString>;
10
10
  adminUrl: z.ZodOptional<z.ZodString>;
11
11
  allowInsecureTls: z.ZodDefault<z.ZodBoolean>;
12
- }, "strict", z.ZodTypeAny, {
13
- allowInsecureTls: boolean;
14
- frontendUrl?: string | undefined;
15
- adminUrl?: string | undefined;
16
- }, {
17
- frontendUrl?: string | undefined;
18
- adminUrl?: string | undefined;
19
- allowInsecureTls?: boolean | undefined;
20
- }>>;
21
- environments: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodObject<{
12
+ }, z.core.$strict>>;
13
+ environments: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodObject<{
22
14
  type: z.ZodLiteral<"local">;
23
15
  root: z.ZodString;
24
16
  testing: z.ZodOptional<z.ZodObject<{
25
17
  frontendUrl: z.ZodOptional<z.ZodString>;
26
18
  adminUrl: z.ZodOptional<z.ZodString>;
27
19
  allowInsecureTls: z.ZodDefault<z.ZodBoolean>;
28
- }, "strict", z.ZodTypeAny, {
29
- allowInsecureTls: boolean;
30
- frontendUrl?: string | undefined;
31
- adminUrl?: string | undefined;
32
- }, {
33
- frontendUrl?: string | undefined;
34
- adminUrl?: string | undefined;
35
- allowInsecureTls?: boolean | undefined;
36
- }>>;
37
- }, "strict", z.ZodTypeAny, {
38
- type: "local";
39
- root: string;
40
- testing?: {
41
- allowInsecureTls: boolean;
42
- frontendUrl?: string | undefined;
43
- adminUrl?: string | undefined;
44
- } | undefined;
45
- }, {
46
- type: "local";
47
- root: string;
48
- testing?: {
49
- frontendUrl?: string | undefined;
50
- adminUrl?: string | undefined;
51
- allowInsecureTls?: boolean | undefined;
52
- } | undefined;
53
- }>, z.ZodObject<{
20
+ }, z.core.$strict>>;
21
+ }, z.core.$strict>, z.ZodObject<{
54
22
  type: z.ZodLiteral<"warden">;
55
23
  projectRoot: z.ZodString;
56
24
  root: z.ZodString;
@@ -60,38 +28,8 @@ export declare const ConfigSchema: z.ZodObject<{
60
28
  frontendUrl: z.ZodOptional<z.ZodString>;
61
29
  adminUrl: z.ZodOptional<z.ZodString>;
62
30
  allowInsecureTls: z.ZodDefault<z.ZodBoolean>;
63
- }, "strict", z.ZodTypeAny, {
64
- allowInsecureTls: boolean;
65
- frontendUrl?: string | undefined;
66
- adminUrl?: string | undefined;
67
- }, {
68
- frontendUrl?: string | undefined;
69
- adminUrl?: string | undefined;
70
- allowInsecureTls?: boolean | undefined;
71
- }>>;
72
- }, "strict", z.ZodTypeAny, {
73
- type: "warden";
74
- root: string;
75
- projectRoot: string;
76
- filesService: string;
77
- targets: Record<string, string>;
78
- testing?: {
79
- allowInsecureTls: boolean;
80
- frontendUrl?: string | undefined;
81
- adminUrl?: string | undefined;
82
- } | undefined;
83
- }, {
84
- type: "warden";
85
- root: string;
86
- projectRoot: string;
87
- testing?: {
88
- frontendUrl?: string | undefined;
89
- adminUrl?: string | undefined;
90
- allowInsecureTls?: boolean | undefined;
91
- } | undefined;
92
- filesService?: string | undefined;
93
- targets?: Record<string, string> | undefined;
94
- }>, z.ZodObject<{
31
+ }, z.core.$strict>>;
32
+ }, z.core.$strict>, z.ZodObject<{
95
33
  type: z.ZodLiteral<"ssh">;
96
34
  host: z.ZodString;
97
35
  root: z.ZodString;
@@ -99,109 +37,9 @@ export declare const ConfigSchema: z.ZodObject<{
99
37
  frontendUrl: z.ZodOptional<z.ZodString>;
100
38
  adminUrl: z.ZodOptional<z.ZodString>;
101
39
  allowInsecureTls: z.ZodDefault<z.ZodBoolean>;
102
- }, "strict", z.ZodTypeAny, {
103
- allowInsecureTls: boolean;
104
- frontendUrl?: string | undefined;
105
- adminUrl?: string | undefined;
106
- }, {
107
- frontendUrl?: string | undefined;
108
- adminUrl?: string | undefined;
109
- allowInsecureTls?: boolean | undefined;
110
- }>>;
111
- }, "strict", z.ZodTypeAny, {
112
- type: "ssh";
113
- root: string;
114
- host: string;
115
- testing?: {
116
- allowInsecureTls: boolean;
117
- frontendUrl?: string | undefined;
118
- adminUrl?: string | undefined;
119
- } | undefined;
120
- }, {
121
- type: "ssh";
122
- root: string;
123
- host: string;
124
- testing?: {
125
- frontendUrl?: string | undefined;
126
- adminUrl?: string | undefined;
127
- allowInsecureTls?: boolean | undefined;
128
- } | undefined;
129
- }>]>>>;
130
- }, "strict", z.ZodTypeAny, {
131
- testing: {
132
- allowInsecureTls: boolean;
133
- frontendUrl?: string | undefined;
134
- adminUrl?: string | undefined;
135
- };
136
- activeTheme: string;
137
- environments: Record<string, {
138
- type: "local";
139
- root: string;
140
- testing?: {
141
- allowInsecureTls: boolean;
142
- frontendUrl?: string | undefined;
143
- adminUrl?: string | undefined;
144
- } | undefined;
145
- } | {
146
- type: "warden";
147
- root: string;
148
- projectRoot: string;
149
- filesService: string;
150
- targets: Record<string, string>;
151
- testing?: {
152
- allowInsecureTls: boolean;
153
- frontendUrl?: string | undefined;
154
- adminUrl?: string | undefined;
155
- } | undefined;
156
- } | {
157
- type: "ssh";
158
- root: string;
159
- host: string;
160
- testing?: {
161
- allowInsecureTls: boolean;
162
- frontendUrl?: string | undefined;
163
- adminUrl?: string | undefined;
164
- } | undefined;
165
- }>;
166
- defaultEnvironment?: string | undefined;
167
- }, {
168
- testing?: {
169
- frontendUrl?: string | undefined;
170
- adminUrl?: string | undefined;
171
- allowInsecureTls?: boolean | undefined;
172
- } | undefined;
173
- defaultEnvironment?: string | undefined;
174
- activeTheme?: string | undefined;
175
- environments?: Record<string, {
176
- type: "local";
177
- root: string;
178
- testing?: {
179
- frontendUrl?: string | undefined;
180
- adminUrl?: string | undefined;
181
- allowInsecureTls?: boolean | undefined;
182
- } | undefined;
183
- } | {
184
- type: "warden";
185
- root: string;
186
- projectRoot: string;
187
- testing?: {
188
- frontendUrl?: string | undefined;
189
- adminUrl?: string | undefined;
190
- allowInsecureTls?: boolean | undefined;
191
- } | undefined;
192
- filesService?: string | undefined;
193
- targets?: Record<string, string> | undefined;
194
- } | {
195
- type: "ssh";
196
- root: string;
197
- host: string;
198
- testing?: {
199
- frontendUrl?: string | undefined;
200
- adminUrl?: string | undefined;
201
- allowInsecureTls?: boolean | undefined;
202
- } | undefined;
203
- }> | undefined;
204
- }>;
40
+ }, z.core.$strict>>;
41
+ }, z.core.$strict>]>>>;
42
+ }, z.core.$strict>;
205
43
  export type MerchantConfig = Omit<z.infer<typeof ConfigSchema>, "environments"> & {
206
44
  environments: Record<string, Environment>;
207
45
  };
@@ -12,14 +12,20 @@ const testing = z
12
12
  allowInsecureTls: z.boolean().default(true),
13
13
  })
14
14
  .strict();
15
- const local = z.object({ type: z.literal("local"), root: z.string(), testing: testing.optional() }).strict();
15
+ const local = z
16
+ .object({
17
+ type: z.literal("local"),
18
+ root: z.string(),
19
+ testing: testing.optional(),
20
+ })
21
+ .strict();
16
22
  const warden = z
17
23
  .object({
18
24
  type: z.literal("warden"),
19
25
  projectRoot: z.string(),
20
26
  root: z.string(),
21
27
  filesService: z.string().default("php-fpm"),
22
- targets: z.record(z.string()).default({ default: "php-fpm" }),
28
+ targets: z.record(z.string(), z.string()).default({ default: "php-fpm" }),
23
29
  testing: testing.optional(),
24
30
  })
25
31
  .strict();
@@ -36,6 +42,8 @@ export const ConfigSchema = z
36
42
  defaultEnvironment: z.string().optional(),
37
43
  activeTheme: z.string().default("all"),
38
44
  testing: testing.default({ allowInsecureTls: true }),
39
- environments: z.record(z.union([local, warden, ssh])).default({}),
45
+ environments: z
46
+ .record(z.string(), z.union([local, warden, ssh]))
47
+ .default({}),
40
48
  })
41
49
  .strict();
@@ -1,4 +1,18 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import { runtime } from "#integrations/pi/session";
3
+ import type { MagentoSnapshot } from "#magento/model";
4
+ type CoreUpgradeContext = {
5
+ selected: {
6
+ name: string;
7
+ };
8
+ magento: Pick<MagentoSnapshot, "edition" | "version" | "phpVersion">;
9
+ };
3
10
  export default function context(pi: ExtensionAPI): void;
11
+ /**
12
+ * Algorithm: inspect the selected instance and authoritative release evidence without mutation;
13
+ * compare every newer core target against the instance; present plans; wait for one chat choice;
14
+ * then constrain later upgrade work to that selected target and the normal permission boundaries.
15
+ */
16
+ export declare function coreUpgradePrompt(state: CoreUpgradeContext): string;
4
17
  export declare function statusMessage(state: Awaited<ReturnType<typeof runtime.boot>>): string;
18
+ export {};