scene-capability-engine 3.6.44 → 3.6.46

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.
Files changed (61) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/bin/scene-capability-engine.js +36 -2
  3. package/docs/command-reference.md +5 -0
  4. package/docs/releases/README.md +2 -0
  5. package/docs/releases/v3.6.45.md +18 -0
  6. package/docs/releases/v3.6.46.md +23 -0
  7. package/docs/zh/releases/README.md +2 -0
  8. package/docs/zh/releases/v3.6.45.md +18 -0
  9. package/docs/zh/releases/v3.6.46.md +23 -0
  10. package/lib/workspace/collab-governance-audit.js +575 -0
  11. package/package.json +4 -2
  12. package/scripts/auto-strategy-router.js +231 -0
  13. package/scripts/capability-mapping-report.js +339 -0
  14. package/scripts/check-branding-consistency.js +140 -0
  15. package/scripts/check-sce-tracking.js +54 -0
  16. package/scripts/check-skip-allowlist.js +94 -0
  17. package/scripts/errorbook-registry-health-gate.js +172 -0
  18. package/scripts/errorbook-release-gate.js +132 -0
  19. package/scripts/failure-attribution-repair.js +317 -0
  20. package/scripts/git-managed-gate.js +464 -0
  21. package/scripts/interactive-approval-event-projection.js +400 -0
  22. package/scripts/interactive-approval-workflow.js +829 -0
  23. package/scripts/interactive-authorization-tier-evaluate.js +413 -0
  24. package/scripts/interactive-change-plan-gate.js +225 -0
  25. package/scripts/interactive-context-bridge.js +617 -0
  26. package/scripts/interactive-customization-loop.js +1690 -0
  27. package/scripts/interactive-dialogue-governance.js +842 -0
  28. package/scripts/interactive-feedback-log.js +253 -0
  29. package/scripts/interactive-flow-smoke.js +238 -0
  30. package/scripts/interactive-flow.js +1059 -0
  31. package/scripts/interactive-governance-report.js +1112 -0
  32. package/scripts/interactive-intent-build.js +707 -0
  33. package/scripts/interactive-loop-smoke.js +215 -0
  34. package/scripts/interactive-moqui-adapter.js +304 -0
  35. package/scripts/interactive-plan-build.js +426 -0
  36. package/scripts/interactive-runtime-policy-evaluate.js +495 -0
  37. package/scripts/interactive-work-order-build.js +552 -0
  38. package/scripts/matrix-regression-gate.js +167 -0
  39. package/scripts/moqui-core-regression-suite.js +397 -0
  40. package/scripts/moqui-lexicon-audit.js +651 -0
  41. package/scripts/moqui-matrix-remediation-phased-runner.js +865 -0
  42. package/scripts/moqui-matrix-remediation-queue.js +852 -0
  43. package/scripts/moqui-metadata-extract.js +1340 -0
  44. package/scripts/moqui-rebuild-gate.js +167 -0
  45. package/scripts/moqui-release-summary.js +729 -0
  46. package/scripts/moqui-standard-rebuild.js +1370 -0
  47. package/scripts/moqui-template-baseline-report.js +682 -0
  48. package/scripts/npm-package-runtime-asset-check.js +221 -0
  49. package/scripts/problem-closure-gate.js +441 -0
  50. package/scripts/release-asset-integrity-check.js +216 -0
  51. package/scripts/release-asset-nonempty-normalize.js +166 -0
  52. package/scripts/release-drift-evaluate.js +223 -0
  53. package/scripts/release-drift-signals.js +255 -0
  54. package/scripts/release-governance-snapshot-export.js +132 -0
  55. package/scripts/release-ops-weekly-summary.js +934 -0
  56. package/scripts/release-risk-remediation-bundle.js +315 -0
  57. package/scripts/release-weekly-ops-gate.js +423 -0
  58. package/scripts/state-migration-reconciliation-gate.js +110 -0
  59. package/scripts/state-storage-tiering-audit.js +337 -0
  60. package/scripts/steering-content-audit.js +393 -0
  61. package/scripts/symbol-evidence-locate.js +366 -0
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const path = require('path');
5
+ const fs = require('fs-extra');
6
+ const crypto = require('crypto');
7
+ const { spawnSync } = require('child_process');
8
+
9
+ const DEFAULT_CONTEXT = 'docs/interactive-customization/page-context.sample.json';
10
+ const DEFAULT_GOAL = 'Adjust order screen field layout for clearer input flow';
11
+ const DEFAULT_APPROVAL_ROLE_POLICY = 'docs/interactive-customization/approval-role-policy-baseline.json';
12
+ const DEFAULT_APPROVAL_ACTOR_ROLE = 'workflow-operator';
13
+ const DEFAULT_APPROVER_ACTOR_ROLE = 'workflow-operator';
14
+ const DEFAULT_DIALOGUE_PROFILE = 'system-maintainer';
15
+ const DEFAULT_OUT = '.sce/reports/interactive-loop-smoke/interactive-loop-smoke.summary.json';
16
+
17
+ function parseArgs(argv) {
18
+ const options = {
19
+ context: DEFAULT_CONTEXT,
20
+ goal: DEFAULT_GOAL,
21
+ approvalRolePolicy: DEFAULT_APPROVAL_ROLE_POLICY,
22
+ approvalActorRole: DEFAULT_APPROVAL_ACTOR_ROLE,
23
+ approverActorRole: DEFAULT_APPROVER_ACTOR_ROLE,
24
+ dialogueProfile: DEFAULT_DIALOGUE_PROFILE,
25
+ out: DEFAULT_OUT,
26
+ json: false
27
+ };
28
+
29
+ for (let index = 0; index < argv.length; index += 1) {
30
+ const token = argv[index];
31
+ const next = argv[index + 1];
32
+ if (token === '--context' && next) {
33
+ options.context = next;
34
+ index += 1;
35
+ } else if (token === '--goal' && next) {
36
+ options.goal = next;
37
+ index += 1;
38
+ } else if (token === '--approval-role-policy' && next) {
39
+ options.approvalRolePolicy = next;
40
+ index += 1;
41
+ } else if (token === '--approval-actor-role' && next) {
42
+ options.approvalActorRole = next;
43
+ index += 1;
44
+ } else if (token === '--approver-actor-role' && next) {
45
+ options.approverActorRole = next;
46
+ index += 1;
47
+ } else if (token === '--dialogue-profile' && next) {
48
+ options.dialogueProfile = next;
49
+ index += 1;
50
+ } else if (token === '--out' && next) {
51
+ options.out = next;
52
+ index += 1;
53
+ } else if (token === '--json') {
54
+ options.json = true;
55
+ } else if (token === '--help' || token === '-h') {
56
+ printHelpAndExit(0);
57
+ }
58
+ }
59
+
60
+ return options;
61
+ }
62
+
63
+ function printHelpAndExit(code) {
64
+ const lines = [
65
+ 'Usage: node scripts/interactive-loop-smoke.js [options]',
66
+ '',
67
+ 'Options:',
68
+ ` --context <path> Context JSON path (default: ${DEFAULT_CONTEXT})`,
69
+ ` --goal <text> Smoke goal text (default: ${DEFAULT_GOAL})`,
70
+ ` --approval-role-policy <path> Role policy path (default: ${DEFAULT_APPROVAL_ROLE_POLICY})`,
71
+ ` --approval-actor-role <name> Approval actor role (default: ${DEFAULT_APPROVAL_ACTOR_ROLE})`,
72
+ ` --approver-actor-role <name> Approver actor role (default: ${DEFAULT_APPROVER_ACTOR_ROLE})`,
73
+ ` --dialogue-profile <name> Dialogue profile (default: ${DEFAULT_DIALOGUE_PROFILE})`,
74
+ ` --out <path> Loop summary output path (default: ${DEFAULT_OUT})`,
75
+ ' --json Print smoke payload as JSON',
76
+ ' -h, --help Show this help'
77
+ ];
78
+ console.log(lines.join('\n'));
79
+ process.exit(code);
80
+ }
81
+
82
+ function resolvePath(cwd, value) {
83
+ return path.isAbsolute(value) ? value : path.resolve(cwd, value);
84
+ }
85
+
86
+ function parseJson(text, label) {
87
+ const raw = `${text || ''}`.trim();
88
+ if (!raw) {
89
+ throw new Error(`${label} output is empty`);
90
+ }
91
+ try {
92
+ return JSON.parse(raw);
93
+ } catch (error) {
94
+ throw new Error(`${label} output is not valid JSON: ${error.message}`);
95
+ }
96
+ }
97
+
98
+ function assert(condition, message) {
99
+ if (!condition) {
100
+ throw new Error(message);
101
+ }
102
+ }
103
+
104
+ async function main() {
105
+ const options = parseArgs(process.argv.slice(2));
106
+ const cwd = process.cwd();
107
+ const loopScript = path.resolve(__dirname, 'interactive-customization-loop.js');
108
+ const contextPath = resolvePath(cwd, options.context);
109
+ const approvalRolePolicyPath = resolvePath(cwd, options.approvalRolePolicy);
110
+ const outPath = resolvePath(cwd, options.out);
111
+
112
+ if (!(await fs.pathExists(loopScript))) {
113
+ throw new Error(`interactive loop script not found: ${loopScript}`);
114
+ }
115
+ if (!(await fs.pathExists(contextPath))) {
116
+ throw new Error(`context file not found: ${contextPath}`);
117
+ }
118
+ if (!(await fs.pathExists(approvalRolePolicyPath))) {
119
+ throw new Error(`approval role policy file not found: ${approvalRolePolicyPath}`);
120
+ }
121
+
122
+ const args = [
123
+ loopScript,
124
+ '--context', contextPath,
125
+ '--goal', options.goal,
126
+ '--execution-mode', 'apply',
127
+ '--dialogue-profile', options.dialogueProfile,
128
+ '--approval-role-policy', approvalRolePolicyPath,
129
+ '--approval-actor-role', options.approvalActorRole,
130
+ '--approver-actor-role', options.approverActorRole,
131
+ '--auto-execute-low-risk',
132
+ '--auth-password-hash', crypto.createHash('sha256').update('smoke-pass').digest('hex'),
133
+ '--auth-password', 'smoke-pass',
134
+ '--feedback-score', '5',
135
+ '--feedback-comment', 'CI smoke feedback',
136
+ '--feedback-tags', 'ci,smoke',
137
+ '--out', outPath,
138
+ '--json'
139
+ ];
140
+
141
+ const runResult = spawnSync(process.execPath, args, {
142
+ cwd,
143
+ encoding: 'utf8'
144
+ });
145
+
146
+ const exitCode = Number.isInteger(runResult.status) ? runResult.status : 1;
147
+ if (exitCode !== 0) {
148
+ const stderr = `${runResult.stderr || ''}`.trim();
149
+ throw new Error(`interactive loop exited with ${exitCode}${stderr ? `: ${stderr}` : ''}`);
150
+ }
151
+
152
+ const payload = parseJson(runResult.stdout, 'interactive loop');
153
+ const artifactFeedback = resolvePath(cwd, payload && payload.artifacts ? payload.artifacts.feedback_jsonl : '');
154
+ const artifactFeedbackGlobal = resolvePath(cwd, payload && payload.artifacts ? payload.artifacts.feedback_global_jsonl : '');
155
+ const artifactSummary = resolvePath(cwd, payload && payload.artifacts ? payload.artifacts.summary_json : '');
156
+
157
+ assert(payload.mode === 'interactive-customization-loop', 'loop mode mismatch');
158
+ assert(payload.summary && payload.summary.status === 'completed', 'loop summary status must be completed');
159
+ assert(payload.execution && payload.execution.attempted === true, 'loop execution must be attempted');
160
+ assert(payload.execution && payload.execution.blocked === false, 'loop execution must not be blocked');
161
+ assert(payload.approval && payload.approval.authorization && payload.approval.authorization.role_requirements &&
162
+ Array.isArray(payload.approval.authorization.role_requirements.execute), 'loop role requirements must be present');
163
+ assert(payload.feedback && payload.feedback.logged === true, 'loop feedback must be logged');
164
+ assert(Array.isArray(payload.steps) && payload.steps.some(step => step && step.name === 'feedback_log'), 'feedback_log step is required');
165
+ assert(await fs.pathExists(artifactFeedback), `session feedback file missing: ${artifactFeedback}`);
166
+ assert(await fs.pathExists(artifactFeedbackGlobal), `global feedback file missing: ${artifactFeedbackGlobal}`);
167
+ assert(await fs.pathExists(artifactSummary), `loop summary file missing: ${artifactSummary}`);
168
+
169
+ const smokePayload = {
170
+ mode: 'interactive-loop-smoke',
171
+ generated_at: new Date().toISOString(),
172
+ status: 'passed',
173
+ checks: {
174
+ summary_status: payload.summary.status,
175
+ gate_decision: payload.gate ? payload.gate.decision : null,
176
+ execution_result: payload.execution ? payload.execution.result : null,
177
+ feedback_logged: payload.feedback ? payload.feedback.logged : false
178
+ },
179
+ artifacts: {
180
+ summary: path.relative(cwd, artifactSummary) || '.',
181
+ feedback_session: path.relative(cwd, artifactFeedback) || '.',
182
+ feedback_global: path.relative(cwd, artifactFeedbackGlobal) || '.'
183
+ }
184
+ };
185
+
186
+ if (options.json) {
187
+ process.stdout.write(`${JSON.stringify(smokePayload, null, 2)}\n`);
188
+ } else {
189
+ process.stdout.write('Interactive loop smoke passed.\n');
190
+ process.stdout.write(`- Summary: ${smokePayload.artifacts.summary}\n`);
191
+ process.stdout.write(`- Feedback (session): ${smokePayload.artifacts.feedback_session}\n`);
192
+ process.stdout.write(`- Feedback (global): ${smokePayload.artifacts.feedback_global}\n`);
193
+ }
194
+ }
195
+
196
+ if (require.main === module) {
197
+ main().catch((error) => {
198
+ console.error(`Interactive loop smoke failed: ${error.message}`);
199
+ process.exit(1);
200
+ });
201
+ }
202
+
203
+ module.exports = {
204
+ DEFAULT_CONTEXT,
205
+ DEFAULT_GOAL,
206
+ DEFAULT_APPROVAL_ROLE_POLICY,
207
+ DEFAULT_APPROVAL_ACTOR_ROLE,
208
+ DEFAULT_APPROVER_ACTOR_ROLE,
209
+ DEFAULT_DIALOGUE_PROFILE,
210
+ DEFAULT_OUT,
211
+ parseArgs,
212
+ resolvePath,
213
+ parseJson,
214
+ main
215
+ };
@@ -0,0 +1,304 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const path = require('path');
5
+ const fs = require('fs-extra');
6
+ const {
7
+ MoquiInteractiveAdapter
8
+ } = require('../lib/interactive-customization/moqui-interactive-adapter');
9
+
10
+ const DEFAULT_OUT_PLAN = '.sce/reports/interactive-change-plan.adapter.json';
11
+ const DEFAULT_OUTPUT = '.sce/reports/interactive-moqui-adapter.json';
12
+
13
+ function parseArgs(argv) {
14
+ const options = {
15
+ action: null,
16
+ intent: null,
17
+ context: null,
18
+ plan: null,
19
+ executionId: null,
20
+ executionMode: 'suggestion',
21
+ policy: null,
22
+ catalog: null,
23
+ moquiConfig: null,
24
+ outPlan: DEFAULT_OUT_PLAN,
25
+ out: DEFAULT_OUTPUT,
26
+ recordOut: null,
27
+ ledgerOut: null,
28
+ liveApply: false,
29
+ dryRun: true,
30
+ allowSuggestionApply: false,
31
+ json: false
32
+ };
33
+
34
+ for (let i = 0; i < argv.length; i += 1) {
35
+ const token = argv[i];
36
+ const next = argv[i + 1];
37
+
38
+ if (token === '--action' && next) {
39
+ options.action = next;
40
+ i += 1;
41
+ } else if (token === '--intent' && next) {
42
+ options.intent = next;
43
+ i += 1;
44
+ } else if (token === '--context' && next) {
45
+ options.context = next;
46
+ i += 1;
47
+ } else if (token === '--plan' && next) {
48
+ options.plan = next;
49
+ i += 1;
50
+ } else if (token === '--execution-id' && next) {
51
+ options.executionId = next;
52
+ i += 1;
53
+ } else if (token === '--execution-mode' && next) {
54
+ options.executionMode = next;
55
+ i += 1;
56
+ } else if (token === '--policy' && next) {
57
+ options.policy = next;
58
+ i += 1;
59
+ } else if (token === '--catalog' && next) {
60
+ options.catalog = next;
61
+ i += 1;
62
+ } else if (token === '--moqui-config' && next) {
63
+ options.moquiConfig = next;
64
+ i += 1;
65
+ } else if (token === '--out-plan' && next) {
66
+ options.outPlan = next;
67
+ i += 1;
68
+ } else if (token === '--out' && next) {
69
+ options.out = next;
70
+ i += 1;
71
+ } else if (token === '--record-out' && next) {
72
+ options.recordOut = next;
73
+ i += 1;
74
+ } else if (token === '--ledger-out' && next) {
75
+ options.ledgerOut = next;
76
+ i += 1;
77
+ } else if (token === '--live-apply') {
78
+ options.liveApply = true;
79
+ } else if (token === '--no-dry-run') {
80
+ options.dryRun = false;
81
+ } else if (token === '--allow-suggestion-apply') {
82
+ options.allowSuggestionApply = true;
83
+ } else if (token === '--json') {
84
+ options.json = true;
85
+ } else if (token === '--help' || token === '-h') {
86
+ printHelpAndExit(0);
87
+ }
88
+ }
89
+
90
+ const action = `${options.action || ''}`.trim().toLowerCase();
91
+ const allowed = new Set(['capabilities', 'plan', 'validate', 'apply', 'low-risk-apply', 'rollback']);
92
+ if (!allowed.has(action)) {
93
+ throw new Error('--action must be one of: capabilities, plan, validate, apply, low-risk-apply, rollback');
94
+ }
95
+
96
+ options.action = action;
97
+
98
+ if (action === 'plan' && !options.intent) {
99
+ throw new Error('--intent is required for --action plan');
100
+ }
101
+ if (['validate', 'apply', 'low-risk-apply'].includes(action) && !options.plan) {
102
+ throw new Error('--plan is required for --action validate/apply/low-risk-apply');
103
+ }
104
+ if (action === 'rollback' && !options.executionId) {
105
+ throw new Error('--execution-id is required for --action rollback');
106
+ }
107
+ if (!['suggestion', 'apply'].includes(`${options.executionMode || ''}`.trim())) {
108
+ throw new Error('--execution-mode must be one of: suggestion, apply');
109
+ }
110
+
111
+ return options;
112
+ }
113
+
114
+ function printHelpAndExit(code) {
115
+ const lines = [
116
+ 'Usage: node scripts/interactive-moqui-adapter.js --action <name> [options]',
117
+ '',
118
+ 'Actions:',
119
+ ' capabilities Show adapter capability contract',
120
+ ' plan Build Change_Plan from Change_Intent',
121
+ ' validate Validate Change_Plan against policy gate',
122
+ ' apply Run controlled apply pipeline and emit ExecutionRecord',
123
+ ' low-risk-apply One-click apply path (requires low-risk + allow)',
124
+ ' rollback Emit rollback ExecutionRecord for a previous execution',
125
+ '',
126
+ 'Options:',
127
+ ' --action <name> Action name (required)',
128
+ ' --intent <path> Change_Intent JSON (for plan)',
129
+ ' --context <path> Optional page context JSON (for plan)',
130
+ ' --plan <path> Change_Plan JSON (for validate/apply)',
131
+ ' --execution-id <id> Execution id (for rollback)',
132
+ ' --execution-mode <mode> suggestion|apply (for plan; default: suggestion)',
133
+ ' --policy <path> Guardrail policy path override',
134
+ ' --catalog <path> High-risk catalog path override',
135
+ ' --moqui-config <path> moqui-adapter.json override for live apply',
136
+ ` --out-plan <path> Generated plan output path (default: ${DEFAULT_OUT_PLAN})`,
137
+ ` --out <path> Command output summary JSON (default: ${DEFAULT_OUTPUT})`,
138
+ ' --record-out <path> Execution record output override',
139
+ ' --ledger-out <path> Execution ledger output override',
140
+ ' --live-apply Enable live Moqui execute mode (default: false)',
141
+ ' --no-dry-run Disable dry-run simulation when live apply is enabled',
142
+ ' --allow-suggestion-apply Allow applying plans with execution_mode=suggestion',
143
+ ' --json Print result JSON to stdout',
144
+ ' -h, --help Show this help'
145
+ ];
146
+ console.log(lines.join('\n'));
147
+ process.exit(code);
148
+ }
149
+
150
+ function resolvePath(cwd, value) {
151
+ return path.isAbsolute(value) ? value : path.resolve(cwd, value);
152
+ }
153
+
154
+ async function readJsonFile(filePath, label) {
155
+ if (!(await fs.pathExists(filePath))) {
156
+ throw new Error(`${label} not found: ${filePath}`);
157
+ }
158
+ const text = await fs.readFile(filePath, 'utf8');
159
+ try {
160
+ return JSON.parse(text);
161
+ } catch (error) {
162
+ throw new Error(`invalid JSON in ${label}: ${error.message}`);
163
+ }
164
+ }
165
+
166
+ async function writeJsonFile(filePath, payload) {
167
+ await fs.ensureDir(path.dirname(filePath));
168
+ await fs.writeJson(filePath, payload, { spaces: 2 });
169
+ }
170
+
171
+ function summarizeResult(payload, options, cwd) {
172
+ return {
173
+ mode: 'interactive-moqui-adapter',
174
+ generated_at: new Date().toISOString(),
175
+ action: options.action,
176
+ payload
177
+ };
178
+ }
179
+
180
+ async function main() {
181
+ const options = parseArgs(process.argv.slice(2));
182
+ const cwd = process.cwd();
183
+
184
+ const adapter = new MoquiInteractiveAdapter({
185
+ projectRoot: cwd,
186
+ policyPath: options.policy || undefined,
187
+ catalogPath: options.catalog || undefined,
188
+ executionRecordOut: options.recordOut || undefined,
189
+ executionLedgerOut: options.ledgerOut || undefined,
190
+ moquiConfigPath: options.moquiConfig || undefined,
191
+ liveApply: options.liveApply,
192
+ dryRun: options.dryRun
193
+ });
194
+
195
+ let payload;
196
+
197
+ if (options.action === 'capabilities') {
198
+ payload = {
199
+ capabilities: adapter.capabilities()
200
+ };
201
+ } else if (options.action === 'plan') {
202
+ const intentPath = resolvePath(cwd, options.intent);
203
+ const intent = await readJsonFile(intentPath, 'intent');
204
+ const context = options.context
205
+ ? await readJsonFile(resolvePath(cwd, options.context), 'context')
206
+ : {};
207
+ const plan = await adapter.plan(intent, context, { executionMode: options.executionMode });
208
+ const outPlanPath = resolvePath(cwd, options.outPlan);
209
+ await writeJsonFile(outPlanPath, plan);
210
+ payload = {
211
+ decision: 'planned',
212
+ plan,
213
+ output: {
214
+ plan: path.relative(cwd, outPlanPath) || '.'
215
+ }
216
+ };
217
+ } else if (options.action === 'validate') {
218
+ const planPath = resolvePath(cwd, options.plan);
219
+ const plan = await readJsonFile(planPath, 'plan');
220
+ const validation = await adapter.validate(plan);
221
+ payload = {
222
+ decision: validation.decision,
223
+ validation
224
+ };
225
+ } else if (options.action === 'apply') {
226
+ const planPath = resolvePath(cwd, options.plan);
227
+ const plan = await readJsonFile(planPath, 'plan');
228
+ const result = await adapter.apply(plan, {
229
+ liveApply: options.liveApply,
230
+ dryRun: options.dryRun,
231
+ allowSuggestionApply: options.allowSuggestionApply
232
+ });
233
+ payload = {
234
+ decision: result.record.result === 'success' ? 'applied' : 'blocked',
235
+ blocked: result.blocked,
236
+ reason: result.reason,
237
+ validation: result.validation,
238
+ execution_record: result.record
239
+ };
240
+ if (result.record.result !== 'success') {
241
+ process.exitCode = 2;
242
+ }
243
+ } else if (options.action === 'low-risk-apply') {
244
+ const planPath = resolvePath(cwd, options.plan);
245
+ const plan = await readJsonFile(planPath, 'plan');
246
+ const result = await adapter.applyLowRisk(plan, {
247
+ liveApply: options.liveApply,
248
+ dryRun: options.dryRun,
249
+ allowSuggestionApply: options.allowSuggestionApply
250
+ });
251
+ payload = {
252
+ decision: result.record.result === 'success' ? 'applied' : 'blocked',
253
+ blocked: result.blocked,
254
+ reason: result.reason,
255
+ validation: result.validation,
256
+ execution_record: result.record
257
+ };
258
+ if (result.record.result !== 'success') {
259
+ process.exitCode = 2;
260
+ }
261
+ } else {
262
+ const rollback = await adapter.rollback(options.executionId);
263
+ payload = {
264
+ decision: rollback.record.result === 'rolled-back' ? 'rolled-back' : 'failed',
265
+ found: rollback.found,
266
+ execution_record: rollback.record
267
+ };
268
+ if (rollback.record.result !== 'rolled-back') {
269
+ process.exitCode = 2;
270
+ }
271
+ }
272
+
273
+ const finalPayload = summarizeResult(payload, options, cwd);
274
+ const outPath = resolvePath(cwd, options.out);
275
+ await writeJsonFile(outPath, finalPayload);
276
+
277
+ if (options.json) {
278
+ process.stdout.write(`${JSON.stringify(finalPayload, null, 2)}\n`);
279
+ } else {
280
+ process.stdout.write(`Interactive moqui adapter action completed: ${options.action}\n`);
281
+ process.stdout.write(`- Output: ${path.relative(cwd, outPath) || '.'}\n`);
282
+ if (payload.execution_record && payload.execution_record.execution_id) {
283
+ process.stdout.write(`- Execution ID: ${payload.execution_record.execution_id}\n`);
284
+ process.stdout.write(`- Result: ${payload.execution_record.result}\n`);
285
+ }
286
+ }
287
+ }
288
+
289
+ if (require.main === module) {
290
+ main().catch((error) => {
291
+ console.error(`Interactive moqui adapter failed: ${error.message}`);
292
+ process.exit(1);
293
+ });
294
+ }
295
+
296
+ module.exports = {
297
+ DEFAULT_OUT_PLAN,
298
+ DEFAULT_OUTPUT,
299
+ parseArgs,
300
+ resolvePath,
301
+ readJsonFile,
302
+ writeJsonFile,
303
+ main
304
+ };