kiro-spec-engine 1.38.0 → 1.40.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/CHANGELOG.md +38 -0
- package/README.md +14 -0
- package/README.zh.md +14 -0
- package/docs/command-reference.md +17 -0
- package/lib/commands/scene.js +380 -1
- package/lib/scene-runtime/moqui-adapter.js +581 -0
- package/lib/scene-runtime/moqui-client.js +522 -0
- package/lib/scene-runtime/moqui-extractor.js +1711 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.40.0] - 2026-02-10
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Moqui Scene Template Extractor**: Extract reusable scene templates from live Moqui ERP instances
|
|
14
|
+
- `MoquiExtractor` — Analyze discovered Moqui resources, identify business patterns (crud/query/workflow), generate scene template bundles
|
|
15
|
+
- Built-in YAML serializer for scene manifests (`kse.scene/v0.2` apiVersion)
|
|
16
|
+
- Entity grouping by Header/Item suffix patterns (e.g., OrderHeader + OrderItem → composite pattern)
|
|
17
|
+
- Pattern-based manifest generation with governance contracts (risk_level, approval, idempotency)
|
|
18
|
+
- Package contract generation (`kse.scene.package/v0.1` apiVersion) with template parameters
|
|
19
|
+
- Template bundle file writing with partial failure resilience
|
|
20
|
+
- `kse scene extract` — Extract scene templates from Moqui ERP instance
|
|
21
|
+
- `--config <path>` custom adapter config path
|
|
22
|
+
- `--type <type>` filter discovery by resource type (entities|services|screens)
|
|
23
|
+
- `--pattern <pattern>` filter by business pattern (crud|query|workflow)
|
|
24
|
+
- `--out <dir>` output directory for template bundles
|
|
25
|
+
- `--dry-run` preview extraction without writing files
|
|
26
|
+
- `--json` structured JSON output
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- **scene discover**: Fixed `response.body.data` → `response.data` property access for Moqui catalog endpoint responses
|
|
30
|
+
|
|
31
|
+
## [1.39.0] - 2026-02-10
|
|
32
|
+
|
|
33
|
+
### Added
|
|
34
|
+
- **Moqui ERP Adapter**: Integrate Moqui ERP instance into KSE scene runtime
|
|
35
|
+
- `MoquiClient` — HTTP client with JWT auth lifecycle (login, refresh, re-login, logout), retry logic using Node.js built-in `http`/`https`
|
|
36
|
+
- `MoquiAdapter` — Binding handler for `spec.erp.*` and `moqui.*` refs, entity CRUD, service invocation, screen discovery
|
|
37
|
+
- `kse scene connect` — Test connectivity and authentication to Moqui ERP instance
|
|
38
|
+
- `--config <path>` custom adapter config path
|
|
39
|
+
- `--json` structured JSON output
|
|
40
|
+
- `kse scene discover` — Discover available entities, services, and screens from Moqui ERP
|
|
41
|
+
- `--config <path>` custom adapter config path
|
|
42
|
+
- `--type <type>` filter by catalog type (entities|services|screens)
|
|
43
|
+
- `--json` structured JSON output
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
- **Jest forceExit**: Added `forceExit: true` to jest configs to prevent CI hang from leaked worker processes
|
|
47
|
+
|
|
10
48
|
## [1.38.0] - 2026-02-10
|
|
11
49
|
|
|
12
50
|
### Added
|
package/README.md
CHANGED
|
@@ -310,6 +310,15 @@ Structure your work with Requirements → Design → Tasks workflow
|
|
|
310
310
|
- **Cross-Platform**: Consistent path handling across Windows/Linux/macOS
|
|
311
311
|
- **Smart Exclusions**: Automatically skip common non-repository directories (node_modules, build, etc.)
|
|
312
312
|
|
|
313
|
+
### Moqui ERP Integration 🚀 NEW in v1.39.0
|
|
314
|
+
- **Moqui ERP Adapter**: Connect KSE scene runtime to live Moqui ERP instances
|
|
315
|
+
- `MoquiClient` — HTTP client with JWT auth lifecycle (login, refresh, re-login, logout) and retry logic
|
|
316
|
+
- `MoquiAdapter` — Binding handler for `spec.erp.*` and `moqui.*` refs, entity CRUD, service invocation, screen discovery
|
|
317
|
+
- **Scene Template Extractor** (v1.40.0): Analyze Moqui resources, identify business patterns, generate reusable scene templates
|
|
318
|
+
- Entity grouping by Header/Item suffix patterns (e.g., OrderHeader + OrderItem → composite)
|
|
319
|
+
- Pattern-based manifest generation with governance contracts
|
|
320
|
+
- **CLI Commands**: `scene connect`, `scene discover`, `scene extract` with `--json` support
|
|
321
|
+
|
|
313
322
|
### Scene Template Engine 🚀 NEW in v1.25.0
|
|
314
323
|
- **Template Variable Schema**: Define typed variables (string, number, boolean, enum, array) with validation rules in scene-package.json
|
|
315
324
|
- **Multi-File Rendering**: Recursive template processing with `{{variable}}` substitution, `{{#if}}` conditionals, `{{#each}}` loops
|
|
@@ -437,6 +446,11 @@ kse scene template-validate --package <path> # Validate template variable sche
|
|
|
437
446
|
kse scene template-resolve --package <name> # Resolve inheritance chain and merged schema
|
|
438
447
|
kse scene template-render --package <name> --values <json> --out <dir> # Render template files
|
|
439
448
|
|
|
449
|
+
# Moqui ERP integration (NEW in v1.39.0)
|
|
450
|
+
kse scene connect --config <path> # Test connectivity to Moqui ERP instance
|
|
451
|
+
kse scene discover --config <path> # Discover entities, services, screens from Moqui
|
|
452
|
+
kse scene extract --config <path> --out <dir> # Extract scene templates from Moqui (v1.40.0)
|
|
453
|
+
|
|
440
454
|
# DevOps operations
|
|
441
455
|
kse ops init <project-name> # Initialize operations specs
|
|
442
456
|
kse ops validate [<project>] # Validate operations completeness
|
package/README.zh.md
CHANGED
|
@@ -266,6 +266,15 @@ sequenceDiagram
|
|
|
266
266
|
- **命令执行**:在特定环境上下文中运行命令
|
|
267
267
|
- **跨平台**:在 Windows、Linux 和 macOS 上无缝工作
|
|
268
268
|
|
|
269
|
+
### Moqui ERP 集成 🚀 v1.39.0 新增
|
|
270
|
+
- **Moqui ERP 适配器**: 将 KSE 场景运行时连接到 Moqui ERP 实例
|
|
271
|
+
- `MoquiClient` — 支持 JWT 认证生命周期(登录、刷新、重登录、登出)和重试逻辑的 HTTP 客户端
|
|
272
|
+
- `MoquiAdapter` — 处理 `spec.erp.*` 和 `moqui.*` 引用的绑定处理器,支持实体 CRUD、服务调用、屏幕发现
|
|
273
|
+
- **场景模板提取器**(v1.40.0): 分析 Moqui 资源,识别业务模式,生成可复用场景模板
|
|
274
|
+
- 按 Header/Item 后缀模式分组实体(如 OrderHeader + OrderItem → 复合模式)
|
|
275
|
+
- 基于模式的清单生成,包含治理合约
|
|
276
|
+
- **CLI 命令**: `scene connect`、`scene discover`、`scene extract`,支持 `--json` 输出
|
|
277
|
+
|
|
269
278
|
### 场景模板引擎 🚀 v1.25.0 新增
|
|
270
279
|
- **模板变量 Schema**: 在 scene-package.json 中定义类型化变量(string, number, boolean, enum, array)及验证规则
|
|
271
280
|
- **多文件渲染**: 递归模板处理,支持 `{{variable}}` 替换、`{{#if}}` 条件、`{{#each}}` 循环
|
|
@@ -341,6 +350,11 @@ kse scene template-validate --package <path> # 验证模板变量 schema
|
|
|
341
350
|
kse scene template-resolve --package <name> # 解析继承链和合并 schema
|
|
342
351
|
kse scene template-render --package <name> --values <json> --out <dir> # 渲染模板文件
|
|
343
352
|
|
|
353
|
+
# Moqui ERP 集成 (v1.39.0 新增)
|
|
354
|
+
kse scene connect --config <path> # 测试 Moqui ERP 实例连接
|
|
355
|
+
kse scene discover --config <path> # 发现 Moqui 实体、服务、屏幕
|
|
356
|
+
kse scene extract --config <path> --out <dir> # 从 Moqui 提取场景模板 (v1.40.0)
|
|
357
|
+
|
|
344
358
|
# DevOps 运维
|
|
345
359
|
kse ops init <project-name> # 初始化运维 specs
|
|
346
360
|
kse ops validate [<project>] # 验证运维完整性
|
|
@@ -214,6 +214,23 @@ kse scene template-render --package <name> --values <json-or-path> --out <dir>
|
|
|
214
214
|
kse scene template-render --package scene-erp --values '{"entity_name":"Order"}' --out ./output --json
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
+
### Moqui ERP Integration
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
# Test connectivity and authentication to Moqui ERP instance
|
|
221
|
+
kse scene connect --config <path>
|
|
222
|
+
kse scene connect --config ./moqui-config.json --json
|
|
223
|
+
|
|
224
|
+
# Discover available entities, services, and screens from Moqui ERP
|
|
225
|
+
kse scene discover --config <path>
|
|
226
|
+
kse scene discover --config ./moqui-config.json --type entities --json
|
|
227
|
+
|
|
228
|
+
# Extract scene templates from Moqui ERP instance
|
|
229
|
+
kse scene extract --config <path> --out <dir>
|
|
230
|
+
kse scene extract --config ./moqui-config.json --type entities --pattern crud --out ./templates --json
|
|
231
|
+
kse scene extract --config ./moqui-config.json --dry-run --json
|
|
232
|
+
```
|
|
233
|
+
|
|
217
234
|
### Version & Upgrade
|
|
218
235
|
|
|
219
236
|
```bash
|
package/lib/commands/scene.js
CHANGED
|
@@ -11,6 +11,9 @@ const PlanCompiler = require('../scene-runtime/plan-compiler');
|
|
|
11
11
|
const PolicyGate = require('../scene-runtime/policy-gate');
|
|
12
12
|
const RuntimeExecutor = require('../scene-runtime/runtime-executor');
|
|
13
13
|
const EvalBridge = require('../scene-runtime/eval-bridge');
|
|
14
|
+
const MoquiClient = require('../scene-runtime/moqui-client');
|
|
15
|
+
const { loadAdapterConfig, validateAdapterConfig } = require('../scene-runtime/moqui-adapter');
|
|
16
|
+
const { runExtraction, SUPPORTED_PATTERNS: EXTRACTOR_SUPPORTED_PATTERNS } = require('../scene-runtime/moqui-extractor');
|
|
14
17
|
|
|
15
18
|
const RUN_MODES = new Set(['dry_run', 'commit']);
|
|
16
19
|
const SCAFFOLD_TYPES = new Set(['erp', 'hybrid']);
|
|
@@ -728,6 +731,42 @@ function registerSceneCommands(program) {
|
|
|
728
731
|
.action(async (options) => {
|
|
729
732
|
await runSceneLockCommand({ ...options, action: 'ls' });
|
|
730
733
|
});
|
|
734
|
+
|
|
735
|
+
// ── scene connect ──
|
|
736
|
+
sceneCmd
|
|
737
|
+
.command('connect')
|
|
738
|
+
.description('Test connectivity and authentication to a Moqui ERP instance')
|
|
739
|
+
.option('-c, --config <path>', 'Path to moqui-adapter.json config file')
|
|
740
|
+
.option('-r, --registry <path>', 'Registry root directory', '.kiro/registry')
|
|
741
|
+
.option('--json', 'Print result as JSON')
|
|
742
|
+
.action(async (options) => {
|
|
743
|
+
await runSceneConnectCommand(options);
|
|
744
|
+
});
|
|
745
|
+
|
|
746
|
+
// ── scene discover ──
|
|
747
|
+
sceneCmd
|
|
748
|
+
.command('discover')
|
|
749
|
+
.description('Discover available entities, services, and screens from Moqui ERP')
|
|
750
|
+
.option('-c, --config <path>', 'Path to moqui-adapter.json config file')
|
|
751
|
+
.option('-t, --type <type>', 'Catalog type to discover (entities|services|screens)')
|
|
752
|
+
.option('--json', 'Print result as JSON')
|
|
753
|
+
.action(async (options) => {
|
|
754
|
+
await runSceneDiscoverCommand(options);
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
// ── scene extract ──
|
|
758
|
+
sceneCmd
|
|
759
|
+
.command('extract')
|
|
760
|
+
.description('Extract scene templates from a Moqui ERP instance')
|
|
761
|
+
.option('-c, --config <path>', 'Path to moqui-adapter.json config file')
|
|
762
|
+
.option('-t, --type <type>', 'Resource type filter (entities|services|screens)')
|
|
763
|
+
.option('-p, --pattern <pattern>', 'Pattern filter (crud|query|workflow)')
|
|
764
|
+
.option('-o, --out <dir>', 'Output directory for template bundles')
|
|
765
|
+
.option('--dry-run', 'Preview extraction without writing files')
|
|
766
|
+
.option('--json', 'Print result as JSON')
|
|
767
|
+
.action(async (options) => {
|
|
768
|
+
await runSceneExtractCommand(options);
|
|
769
|
+
});
|
|
731
770
|
}
|
|
732
771
|
|
|
733
772
|
function normalizeSourceOptions(options = {}) {
|
|
@@ -11993,6 +12032,334 @@ function printSceneLockSummary(options, payload) {
|
|
|
11993
12032
|
}
|
|
11994
12033
|
}
|
|
11995
12034
|
|
|
12035
|
+
// ── Scene Connect ─────────────────────────────────────────────────────────
|
|
12036
|
+
|
|
12037
|
+
function normalizeSceneConnectOptions(options = {}) {
|
|
12038
|
+
return {
|
|
12039
|
+
config: options.config ? String(options.config).trim() : undefined,
|
|
12040
|
+
registry: options.registry ? String(options.registry).trim() : '.kiro/registry',
|
|
12041
|
+
json: options.json === true
|
|
12042
|
+
};
|
|
12043
|
+
}
|
|
12044
|
+
|
|
12045
|
+
function validateSceneConnectOptions(options) {
|
|
12046
|
+
return null;
|
|
12047
|
+
}
|
|
12048
|
+
|
|
12049
|
+
async function runSceneConnectCommand(rawOptions = {}, dependencies = {}) {
|
|
12050
|
+
const projectRoot = dependencies.projectRoot || process.cwd();
|
|
12051
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
12052
|
+
|
|
12053
|
+
const options = normalizeSceneConnectOptions(rawOptions);
|
|
12054
|
+
const validationError = validateSceneConnectOptions(options);
|
|
12055
|
+
|
|
12056
|
+
if (validationError) {
|
|
12057
|
+
console.error(chalk.red(`Scene connect failed: ${validationError}`));
|
|
12058
|
+
process.exitCode = 1;
|
|
12059
|
+
return null;
|
|
12060
|
+
}
|
|
12061
|
+
|
|
12062
|
+
let client = null;
|
|
12063
|
+
try {
|
|
12064
|
+
const configResult = loadAdapterConfig(options.config, projectRoot);
|
|
12065
|
+
if (configResult.error) {
|
|
12066
|
+
throw new Error(configResult.error);
|
|
12067
|
+
}
|
|
12068
|
+
|
|
12069
|
+
const validation = validateAdapterConfig(configResult.config);
|
|
12070
|
+
if (!validation.valid) {
|
|
12071
|
+
throw new Error(`config validation failed: ${validation.errors.join(', ')}`);
|
|
12072
|
+
}
|
|
12073
|
+
|
|
12074
|
+
const config = configResult.config;
|
|
12075
|
+
client = new MoquiClient(config);
|
|
12076
|
+
const loginResult = await client.login();
|
|
12077
|
+
|
|
12078
|
+
let payload;
|
|
12079
|
+
if (loginResult.success) {
|
|
12080
|
+
payload = {
|
|
12081
|
+
success: true,
|
|
12082
|
+
baseUrl: config.baseUrl,
|
|
12083
|
+
authStatus: 'authenticated'
|
|
12084
|
+
};
|
|
12085
|
+
} else {
|
|
12086
|
+
payload = {
|
|
12087
|
+
success: false,
|
|
12088
|
+
baseUrl: config.baseUrl,
|
|
12089
|
+
error: { code: 'AUTH_FAILED', message: loginResult.error || 'authentication failed' }
|
|
12090
|
+
};
|
|
12091
|
+
}
|
|
12092
|
+
|
|
12093
|
+
printSceneConnectSummary(options, payload);
|
|
12094
|
+
return payload;
|
|
12095
|
+
} catch (error) {
|
|
12096
|
+
const payload = {
|
|
12097
|
+
success: false,
|
|
12098
|
+
baseUrl: rawOptions.config || 'unknown',
|
|
12099
|
+
error: { code: 'CONNECT_FAILED', message: error.message }
|
|
12100
|
+
};
|
|
12101
|
+
printSceneConnectSummary(options, payload);
|
|
12102
|
+
process.exitCode = 1;
|
|
12103
|
+
return payload;
|
|
12104
|
+
} finally {
|
|
12105
|
+
if (client) {
|
|
12106
|
+
try { await client.dispose(); } catch (_) { /* ignore dispose errors */ }
|
|
12107
|
+
}
|
|
12108
|
+
}
|
|
12109
|
+
}
|
|
12110
|
+
|
|
12111
|
+
function printSceneConnectSummary(options, payload) {
|
|
12112
|
+
if (options.json) {
|
|
12113
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
12114
|
+
return;
|
|
12115
|
+
}
|
|
12116
|
+
|
|
12117
|
+
if (payload.success) {
|
|
12118
|
+
console.log(chalk.green(`Connected to Moqui ERP at ${payload.baseUrl}`));
|
|
12119
|
+
console.log(` Auth status: ${payload.authStatus}`);
|
|
12120
|
+
} else {
|
|
12121
|
+
console.error(chalk.red(`Connection failed: ${payload.error.message}`));
|
|
12122
|
+
if (payload.baseUrl && payload.baseUrl !== 'unknown') {
|
|
12123
|
+
console.error(` Target: ${payload.baseUrl}`);
|
|
12124
|
+
}
|
|
12125
|
+
}
|
|
12126
|
+
}
|
|
12127
|
+
|
|
12128
|
+
// ── Scene Discover ────────────────────────────────────────────────────────
|
|
12129
|
+
|
|
12130
|
+
function normalizeSceneDiscoverOptions(options = {}) {
|
|
12131
|
+
return {
|
|
12132
|
+
config: options.config ? String(options.config).trim() : undefined,
|
|
12133
|
+
type: options.type ? String(options.type).trim() : undefined,
|
|
12134
|
+
json: options.json === true
|
|
12135
|
+
};
|
|
12136
|
+
}
|
|
12137
|
+
|
|
12138
|
+
function validateSceneDiscoverOptions(options) {
|
|
12139
|
+
if (options.type && !['entities', 'services', 'screens'].includes(options.type)) {
|
|
12140
|
+
return `invalid --type "${options.type}", must be entities, services, or screens`;
|
|
12141
|
+
}
|
|
12142
|
+
return null;
|
|
12143
|
+
}
|
|
12144
|
+
|
|
12145
|
+
async function runSceneDiscoverCommand(rawOptions = {}, dependencies = {}) {
|
|
12146
|
+
const projectRoot = dependencies.projectRoot || process.cwd();
|
|
12147
|
+
const fileSystem = dependencies.fileSystem || fs;
|
|
12148
|
+
|
|
12149
|
+
const options = normalizeSceneDiscoverOptions(rawOptions);
|
|
12150
|
+
const validationError = validateSceneDiscoverOptions(options);
|
|
12151
|
+
|
|
12152
|
+
if (validationError) {
|
|
12153
|
+
console.error(chalk.red(`Scene discover failed: ${validationError}`));
|
|
12154
|
+
process.exitCode = 1;
|
|
12155
|
+
return null;
|
|
12156
|
+
}
|
|
12157
|
+
|
|
12158
|
+
let client = null;
|
|
12159
|
+
try {
|
|
12160
|
+
const configResult = loadAdapterConfig(options.config, projectRoot);
|
|
12161
|
+
if (configResult.error) {
|
|
12162
|
+
throw new Error(configResult.error);
|
|
12163
|
+
}
|
|
12164
|
+
|
|
12165
|
+
const validation = validateAdapterConfig(configResult.config);
|
|
12166
|
+
if (!validation.valid) {
|
|
12167
|
+
throw new Error(`config validation failed: ${validation.errors.join(', ')}`);
|
|
12168
|
+
}
|
|
12169
|
+
|
|
12170
|
+
const config = configResult.config;
|
|
12171
|
+
client = new MoquiClient(config);
|
|
12172
|
+
const loginResult = await client.login();
|
|
12173
|
+
|
|
12174
|
+
if (!loginResult.success) {
|
|
12175
|
+
throw new Error(loginResult.error || 'authentication failed');
|
|
12176
|
+
}
|
|
12177
|
+
|
|
12178
|
+
let payload;
|
|
12179
|
+
|
|
12180
|
+
if (options.type) {
|
|
12181
|
+
// Query specific catalog type
|
|
12182
|
+
const endpoint = options.type === 'entities'
|
|
12183
|
+
? '/api/v1/entities'
|
|
12184
|
+
: options.type === 'services'
|
|
12185
|
+
? '/api/v1/services'
|
|
12186
|
+
: '/api/v1/screens';
|
|
12187
|
+
|
|
12188
|
+
const response = await client.request('GET', endpoint);
|
|
12189
|
+
const rawData = (response && response.data) || {};
|
|
12190
|
+
const items = Array.isArray(rawData) ? rawData : (rawData[options.type] || rawData.items || []);
|
|
12191
|
+
payload = {
|
|
12192
|
+
success: true,
|
|
12193
|
+
type: options.type,
|
|
12194
|
+
[options.type]: Array.isArray(items) ? items : [],
|
|
12195
|
+
count: Array.isArray(items) ? items.length : 0
|
|
12196
|
+
};
|
|
12197
|
+
} else {
|
|
12198
|
+
// Query all catalog types for summary
|
|
12199
|
+
const [entitiesRes, servicesRes, screensRes] = await Promise.all([
|
|
12200
|
+
client.request('GET', '/api/v1/entities').catch(() => null),
|
|
12201
|
+
client.request('GET', '/api/v1/services').catch(() => null),
|
|
12202
|
+
client.request('GET', '/api/v1/screens').catch(() => null)
|
|
12203
|
+
]);
|
|
12204
|
+
|
|
12205
|
+
const entitiesRaw = (entitiesRes && entitiesRes.data) || {};
|
|
12206
|
+
const servicesRaw = (servicesRes && servicesRes.data) || {};
|
|
12207
|
+
const screensRaw = (screensRes && screensRes.data) || {};
|
|
12208
|
+
const entitiesData = Array.isArray(entitiesRaw) ? entitiesRaw : (entitiesRaw.entities || entitiesRaw.items || []);
|
|
12209
|
+
const servicesData = Array.isArray(servicesRaw) ? servicesRaw : (servicesRaw.services || servicesRaw.items || []);
|
|
12210
|
+
const screensData = Array.isArray(screensRaw) ? screensRaw : (screensRaw.screens || screensRaw.items || []);
|
|
12211
|
+
|
|
12212
|
+
payload = {
|
|
12213
|
+
success: true,
|
|
12214
|
+
summary: {
|
|
12215
|
+
entities: { count: Array.isArray(entitiesData) ? entitiesData.length : 0 },
|
|
12216
|
+
services: { count: Array.isArray(servicesData) ? servicesData.length : 0 },
|
|
12217
|
+
screens: { count: Array.isArray(screensData) ? screensData.length : 0 }
|
|
12218
|
+
}
|
|
12219
|
+
};
|
|
12220
|
+
}
|
|
12221
|
+
|
|
12222
|
+
printSceneDiscoverSummary(options, payload);
|
|
12223
|
+
return payload;
|
|
12224
|
+
} catch (error) {
|
|
12225
|
+
const payload = {
|
|
12226
|
+
success: false,
|
|
12227
|
+
error: { code: 'DISCOVER_FAILED', message: error.message }
|
|
12228
|
+
};
|
|
12229
|
+
printSceneDiscoverSummary(options, payload);
|
|
12230
|
+
process.exitCode = 1;
|
|
12231
|
+
return payload;
|
|
12232
|
+
} finally {
|
|
12233
|
+
if (client) {
|
|
12234
|
+
try { await client.dispose(); } catch (_) { /* ignore dispose errors */ }
|
|
12235
|
+
}
|
|
12236
|
+
}
|
|
12237
|
+
}
|
|
12238
|
+
|
|
12239
|
+
function printSceneDiscoverSummary(options, payload) {
|
|
12240
|
+
if (options.json) {
|
|
12241
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
12242
|
+
return;
|
|
12243
|
+
}
|
|
12244
|
+
|
|
12245
|
+
if (!payload.success) {
|
|
12246
|
+
console.error(chalk.red(`Discovery failed: ${payload.error.message}`));
|
|
12247
|
+
return;
|
|
12248
|
+
}
|
|
12249
|
+
|
|
12250
|
+
if (payload.summary) {
|
|
12251
|
+
console.log(chalk.green('Moqui ERP catalog summary:'));
|
|
12252
|
+
console.log(` Entities: ${payload.summary.entities.count}`);
|
|
12253
|
+
console.log(` Services: ${payload.summary.services.count}`);
|
|
12254
|
+
console.log(` Screens: ${payload.summary.screens.count}`);
|
|
12255
|
+
} else {
|
|
12256
|
+
console.log(chalk.green(`Discovered ${payload.count} ${payload.type}:`));
|
|
12257
|
+
const items = payload[payload.type] || [];
|
|
12258
|
+
for (const item of items.slice(0, 50)) {
|
|
12259
|
+
const name = typeof item === 'string' ? item : (item.name || item.entityName || JSON.stringify(item));
|
|
12260
|
+
console.log(` ${name}`);
|
|
12261
|
+
}
|
|
12262
|
+
if (items.length > 50) {
|
|
12263
|
+
console.log(` ... and ${items.length - 50} more`);
|
|
12264
|
+
}
|
|
12265
|
+
}
|
|
12266
|
+
}
|
|
12267
|
+
|
|
12268
|
+
// ─── Scene Extract CLI Functions ──────────────────────────────────
|
|
12269
|
+
|
|
12270
|
+
function normalizeSceneExtractOptions(options = {}) {
|
|
12271
|
+
return {
|
|
12272
|
+
config: options.config ? String(options.config).trim() : undefined,
|
|
12273
|
+
type: options.type ? String(options.type).trim() : undefined,
|
|
12274
|
+
pattern: options.pattern ? String(options.pattern).trim() : undefined,
|
|
12275
|
+
out: options.out ? String(options.out).trim() : '.kiro/templates/extracted',
|
|
12276
|
+
dryRun: options.dryRun === true,
|
|
12277
|
+
json: options.json === true
|
|
12278
|
+
};
|
|
12279
|
+
}
|
|
12280
|
+
|
|
12281
|
+
function validateSceneExtractOptions(options) {
|
|
12282
|
+
if (options.type && !['entities', 'services', 'screens'].includes(options.type)) {
|
|
12283
|
+
return `invalid --type "${options.type}", must be entities, services, or screens`;
|
|
12284
|
+
}
|
|
12285
|
+
if (options.pattern && !EXTRACTOR_SUPPORTED_PATTERNS.includes(options.pattern)) {
|
|
12286
|
+
return `invalid --pattern "${options.pattern}", must be one of: ${EXTRACTOR_SUPPORTED_PATTERNS.join(', ')}`;
|
|
12287
|
+
}
|
|
12288
|
+
return null;
|
|
12289
|
+
}
|
|
12290
|
+
|
|
12291
|
+
function printSceneExtractSummary(options, payload) {
|
|
12292
|
+
if (options.json) {
|
|
12293
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
12294
|
+
return;
|
|
12295
|
+
}
|
|
12296
|
+
|
|
12297
|
+
if (!payload.success) {
|
|
12298
|
+
console.error(chalk.red(`Extraction failed: ${payload.error.message}`));
|
|
12299
|
+
return;
|
|
12300
|
+
}
|
|
12301
|
+
|
|
12302
|
+
const summary = payload.summary || {};
|
|
12303
|
+
const patterns = summary.patterns || {};
|
|
12304
|
+
|
|
12305
|
+
console.log(chalk.green(`Extracted ${summary.totalTemplates || 0} template(s):`));
|
|
12306
|
+
console.log(` CRUD: ${patterns.crud || 0}`);
|
|
12307
|
+
console.log(` Query: ${patterns.query || 0}`);
|
|
12308
|
+
console.log(` Workflow: ${patterns.workflow || 0}`);
|
|
12309
|
+
|
|
12310
|
+
if (options.dryRun) {
|
|
12311
|
+
console.log(chalk.yellow(' (dry-run — no files written)'));
|
|
12312
|
+
} else {
|
|
12313
|
+
console.log(` Output: ${summary.outputDir || options.out}`);
|
|
12314
|
+
}
|
|
12315
|
+
|
|
12316
|
+
if (payload.warnings && payload.warnings.length > 0) {
|
|
12317
|
+
console.log(chalk.yellow(` Warnings: ${payload.warnings.length}`));
|
|
12318
|
+
for (const w of payload.warnings) {
|
|
12319
|
+
console.log(chalk.yellow(` - ${w}`));
|
|
12320
|
+
}
|
|
12321
|
+
}
|
|
12322
|
+
}
|
|
12323
|
+
|
|
12324
|
+
async function runSceneExtractCommand(rawOptions = {}, dependencies = {}) {
|
|
12325
|
+
const options = normalizeSceneExtractOptions(rawOptions);
|
|
12326
|
+
const validationError = validateSceneExtractOptions(options);
|
|
12327
|
+
|
|
12328
|
+
if (validationError) {
|
|
12329
|
+
console.error(chalk.red(validationError));
|
|
12330
|
+
process.exitCode = 1;
|
|
12331
|
+
return null;
|
|
12332
|
+
}
|
|
12333
|
+
|
|
12334
|
+
try {
|
|
12335
|
+
const payload = await runExtraction({
|
|
12336
|
+
config: options.config,
|
|
12337
|
+
type: options.type,
|
|
12338
|
+
pattern: options.pattern,
|
|
12339
|
+
out: options.out,
|
|
12340
|
+
dryRun: options.dryRun
|
|
12341
|
+
}, dependencies);
|
|
12342
|
+
|
|
12343
|
+
if (!payload.success) {
|
|
12344
|
+
process.exitCode = 1;
|
|
12345
|
+
}
|
|
12346
|
+
|
|
12347
|
+
printSceneExtractSummary(options, payload);
|
|
12348
|
+
return payload;
|
|
12349
|
+
} catch (error) {
|
|
12350
|
+
const payload = {
|
|
12351
|
+
success: false,
|
|
12352
|
+
templates: [],
|
|
12353
|
+
summary: { totalTemplates: 0, patterns: { crud: 0, query: 0, workflow: 0 }, outputDir: options.out },
|
|
12354
|
+
warnings: [],
|
|
12355
|
+
error: { code: 'EXTRACT_FAILED', message: error.message }
|
|
12356
|
+
};
|
|
12357
|
+
printSceneExtractSummary(options, payload);
|
|
12358
|
+
process.exitCode = 1;
|
|
12359
|
+
return payload;
|
|
12360
|
+
}
|
|
12361
|
+
}
|
|
12362
|
+
|
|
11996
12363
|
module.exports = {
|
|
11997
12364
|
RUN_MODES,
|
|
11998
12365
|
SCAFFOLD_TYPES,
|
|
@@ -12185,5 +12552,17 @@ module.exports = {
|
|
|
12185
12552
|
normalizeSceneLockOptions,
|
|
12186
12553
|
validateSceneLockOptions,
|
|
12187
12554
|
runSceneLockCommand,
|
|
12188
|
-
printSceneLockSummary
|
|
12555
|
+
printSceneLockSummary,
|
|
12556
|
+
normalizeSceneConnectOptions,
|
|
12557
|
+
validateSceneConnectOptions,
|
|
12558
|
+
runSceneConnectCommand,
|
|
12559
|
+
printSceneConnectSummary,
|
|
12560
|
+
normalizeSceneDiscoverOptions,
|
|
12561
|
+
validateSceneDiscoverOptions,
|
|
12562
|
+
runSceneDiscoverCommand,
|
|
12563
|
+
printSceneDiscoverSummary,
|
|
12564
|
+
normalizeSceneExtractOptions,
|
|
12565
|
+
validateSceneExtractOptions,
|
|
12566
|
+
runSceneExtractCommand,
|
|
12567
|
+
printSceneExtractSummary
|
|
12189
12568
|
};
|