scene-capability-engine 3.3.26 → 3.4.5

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.
@@ -0,0 +1,446 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const { mergeConfigs } = require('../auto/config-schema');
4
+ const {
5
+ SteeringContract,
6
+ DEFAULT_LAYER_FILES,
7
+ MANIFEST_FILENAME,
8
+ SCE_STEERING_DIR,
9
+ } = require('../runtime/steering-contract');
10
+
11
+ const TAKEOVER_BASELINE_SCHEMA_VERSION = '1.0';
12
+
13
+ const SESSION_GOVERNANCE_DEFAULTS = Object.freeze({
14
+ schema_version: '1.0',
15
+ scene_primary_session_required: true,
16
+ one_scene_one_primary_session: true,
17
+ spec_runs_bind_child_session: true,
18
+ scene_completion_auto_rollover: true,
19
+ auto_archive_spec_sessions: true
20
+ });
21
+
22
+ const SPEC_DOMAIN_POLICY_DEFAULTS = Object.freeze({
23
+ schema_version: '1.0',
24
+ closed_loop_research_required: true,
25
+ coverage_validation_required: true,
26
+ fail_on_gap_default: true
27
+ });
28
+
29
+ const PROBLEM_EVAL_POLICY_DEFAULTS = Object.freeze({
30
+ schema_version: '1.0',
31
+ enabled: true,
32
+ mode: 'required',
33
+ enforce_on_stages: ['plan', 'generate', 'apply', 'verify', 'release'],
34
+ block_on_stages: ['apply', 'release'],
35
+ min_confidence_by_stage: {
36
+ plan: 20,
37
+ generate: 25,
38
+ apply: 30,
39
+ verify: 35,
40
+ release: 40
41
+ },
42
+ high_risk_requires_debug_evidence: true,
43
+ high_risk_keywords: [
44
+ 'auth',
45
+ 'payment',
46
+ 'security',
47
+ 'delete',
48
+ 'rollback',
49
+ 'production',
50
+ 'migrate',
51
+ 'compliance',
52
+ 'data-loss'
53
+ ],
54
+ recommendation_limit: 6
55
+ });
56
+
57
+ const TAKEOVER_DEFAULTS = Object.freeze({
58
+ autonomous: {
59
+ enabled: true,
60
+ mode: 'aggressive',
61
+ require_step_confirmation: false,
62
+ apply_all_work_by_default: true
63
+ },
64
+ session_governance: {
65
+ scene_primary_session_required: true,
66
+ one_scene_one_primary_session: true,
67
+ spec_runs_bind_child_session: true,
68
+ scene_completion_auto_rollover: true
69
+ },
70
+ spec_domain_policy: {
71
+ closed_loop_research_required: true,
72
+ coverage_validation_required: true,
73
+ fail_on_gap_default: true
74
+ },
75
+ problem_evaluation: {
76
+ enabled: true,
77
+ mode: 'required',
78
+ enforce_on_stages: ['plan', 'generate', 'apply', 'verify', 'release'],
79
+ block_on_stages: ['apply', 'release']
80
+ },
81
+ debug_policy: {
82
+ prioritize_root_cause_fix: true,
83
+ max_direct_fix_rounds_before_debug: 2,
84
+ forbid_bypass_workarounds: true
85
+ },
86
+ migration_policy: {
87
+ legacy_kiro_supported: false,
88
+ require_manual_legacy_migration_confirmation: true
89
+ }
90
+ });
91
+
92
+ function _toRelativePosix(projectPath, absolutePath) {
93
+ return path.relative(projectPath, absolutePath).replace(/\\/g, '/');
94
+ }
95
+
96
+ function _isObject(value) {
97
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
98
+ }
99
+
100
+ function _clone(value) {
101
+ return JSON.parse(JSON.stringify(value));
102
+ }
103
+
104
+ function _deepEqual(left, right) {
105
+ return JSON.stringify(left) === JSON.stringify(right);
106
+ }
107
+
108
+ function _deepMerge(base, patch) {
109
+ const output = _isObject(base) ? _clone(base) : {};
110
+ if (!_isObject(patch)) {
111
+ return output;
112
+ }
113
+
114
+ for (const [key, value] of Object.entries(patch)) {
115
+ if (_isObject(value)) {
116
+ output[key] = _deepMerge(output[key], value);
117
+ } else {
118
+ output[key] = value;
119
+ }
120
+ }
121
+ return output;
122
+ }
123
+
124
+ async function _readJsonSafe(filePath, fileSystem) {
125
+ if (!await fileSystem.pathExists(filePath)) {
126
+ return null;
127
+ }
128
+ try {
129
+ return await fileSystem.readJson(filePath);
130
+ } catch (_error) {
131
+ return null;
132
+ }
133
+ }
134
+
135
+ function _buildAutoConfig(existing) {
136
+ const merged = mergeConfigs({}, _isObject(existing) ? existing : {});
137
+ merged.mode = 'aggressive';
138
+ merged.checkpoints = {
139
+ ...(merged.checkpoints || {}),
140
+ requirementsReview: false,
141
+ designReview: false,
142
+ tasksReview: false,
143
+ phaseCompletion: false,
144
+ finalReview: false
145
+ };
146
+ merged.errorRecovery = {
147
+ ...(merged.errorRecovery || {}),
148
+ enabled: true,
149
+ maxAttempts: Math.max(3, Number(merged?.errorRecovery?.maxAttempts || 0) || 0)
150
+ };
151
+ merged.performance = {
152
+ ...(merged.performance || {}),
153
+ maxConcurrentTasks: Math.max(1, Number(merged?.performance?.maxConcurrentTasks || 0) || 1)
154
+ };
155
+ merged.takeover = {
156
+ managed: true,
157
+ require_step_confirmation: false,
158
+ apply_all_work_by_default: true
159
+ };
160
+ return merged;
161
+ }
162
+
163
+ function _buildAdoptionConfig(existing, nowIso, sceVersion) {
164
+ const base = _isObject(existing) ? _clone(existing) : {};
165
+ const adoptedAt = typeof base.adoptedAt === 'string' && base.adoptedAt.trim()
166
+ ? base.adoptedAt
167
+ : nowIso;
168
+
169
+ return {
170
+ ...base,
171
+ version: typeof base.version === 'string' && base.version.trim() ? base.version : '1.0.0',
172
+ adoptedAt,
173
+ steeringStrategy: typeof base.steeringStrategy === 'string' && base.steeringStrategy.trim()
174
+ ? base.steeringStrategy
175
+ : 'use-kse',
176
+ multiUserMode: base.multiUserMode === true,
177
+ runtimePolicy: {
178
+ agent_parity_permissions: true,
179
+ autonomous_default: true
180
+ },
181
+ takeover: {
182
+ managed: true,
183
+ schema_version: TAKEOVER_BASELINE_SCHEMA_VERSION,
184
+ auto_detect_on_startup: true,
185
+ legacy_kiro_supported: false
186
+ },
187
+ defaults: _clone(TAKEOVER_DEFAULTS),
188
+ lastAlignedSceVersion: sceVersion
189
+ };
190
+ }
191
+
192
+ function _buildTakeoverBaselineConfig(existing, sceVersion) {
193
+ const base = _isObject(existing) ? _clone(existing) : {};
194
+ return {
195
+ ...base,
196
+ schema_version: TAKEOVER_BASELINE_SCHEMA_VERSION,
197
+ engine: 'sce',
198
+ managed: true,
199
+ last_aligned_sce_version: sceVersion,
200
+ defaults: _clone(TAKEOVER_DEFAULTS)
201
+ };
202
+ }
203
+
204
+ async function _reconcileJsonFile(filePath, desired, options = {}) {
205
+ const {
206
+ projectPath,
207
+ apply,
208
+ fileSystem,
209
+ managedBy = 'takeover-baseline'
210
+ } = options;
211
+ const existing = await _readJsonSafe(filePath, fileSystem);
212
+ const existed = existing !== null;
213
+ const changed = !existed || !_deepEqual(existing, desired);
214
+
215
+ if (apply && changed) {
216
+ await fileSystem.ensureDir(path.dirname(filePath));
217
+ await fileSystem.writeJson(filePath, desired, { spaces: 2 });
218
+ }
219
+
220
+ return {
221
+ path: _toRelativePosix(projectPath, filePath),
222
+ existed,
223
+ changed,
224
+ status: existed ? (changed ? 'updated' : 'unchanged') : (changed ? 'created' : 'unchanged'),
225
+ managed_by: managedBy
226
+ };
227
+ }
228
+
229
+ async function _inspectSteeringState(projectPath, fileSystem) {
230
+ const steeringDir = path.join(projectPath, SCE_STEERING_DIR);
231
+ const manifestPath = path.join(steeringDir, MANIFEST_FILENAME);
232
+ const layers = Object.values(DEFAULT_LAYER_FILES).map((filename) => path.join(steeringDir, filename));
233
+ const files = [manifestPath, ...layers];
234
+ let missing = 0;
235
+ for (const filePath of files) {
236
+ if (!await fileSystem.pathExists(filePath)) {
237
+ missing += 1;
238
+ }
239
+ }
240
+ return {
241
+ steeringDir,
242
+ manifestPath,
243
+ layerFiles: layers,
244
+ missing
245
+ };
246
+ }
247
+
248
+ async function _reconcileSteeringContract(projectPath, options = {}) {
249
+ const { apply, fileSystem } = options;
250
+ const before = await _inspectSteeringState(projectPath, fileSystem);
251
+ let ensureResult = null;
252
+ if (apply) {
253
+ const contract = new SteeringContract(projectPath);
254
+ ensureResult = await contract.ensureContract();
255
+ }
256
+ const after = await _inspectSteeringState(projectPath, fileSystem);
257
+ const changed = before.missing !== after.missing;
258
+
259
+ return {
260
+ path: _toRelativePosix(projectPath, before.steeringDir),
261
+ changed,
262
+ status: changed ? 'updated' : 'unchanged',
263
+ managed_by: 'steering-contract',
264
+ details: {
265
+ missing_before: before.missing,
266
+ missing_after: after.missing,
267
+ ensure_result: ensureResult
268
+ }
269
+ };
270
+ }
271
+
272
+ function _summarize(items) {
273
+ const summary = {
274
+ created: 0,
275
+ updated: 0,
276
+ unchanged: 0,
277
+ pending: 0
278
+ };
279
+
280
+ for (const item of items) {
281
+ if (!item) {
282
+ continue;
283
+ }
284
+ if (item.status === 'created') {
285
+ summary.created += 1;
286
+ } else if (item.status === 'updated') {
287
+ summary.updated += 1;
288
+ } else if (item.status === 'pending') {
289
+ summary.pending += 1;
290
+ } else {
291
+ summary.unchanged += 1;
292
+ }
293
+ }
294
+ return summary;
295
+ }
296
+
297
+ function _toAuditStatus(items, apply) {
298
+ if (apply) {
299
+ return items.map((item) => item);
300
+ }
301
+ return items.map((item) => {
302
+ if (item.status === 'created' || item.status === 'updated') {
303
+ return {
304
+ ...item,
305
+ status: 'pending',
306
+ changed: true
307
+ };
308
+ }
309
+ return item;
310
+ });
311
+ }
312
+
313
+ async function applyTakeoverBaseline(projectPath = process.cwd(), options = {}) {
314
+ const fileSystem = options.fileSystem || fs;
315
+ const apply = options.apply !== false;
316
+ const writeReport = options.writeReport === true;
317
+ const now = options.now || new Date();
318
+ const nowIso = typeof now.toISOString === 'function' ? now.toISOString() : new Date().toISOString();
319
+ const sceVersion = typeof options.sceVersion === 'string' && options.sceVersion.trim()
320
+ ? options.sceVersion.trim()
321
+ : 'unknown';
322
+
323
+ const sceRoot = path.join(projectPath, '.sce');
324
+ if (!await fileSystem.pathExists(sceRoot)) {
325
+ return {
326
+ mode: 'workspace-takeover-baseline',
327
+ detected_project: false,
328
+ apply,
329
+ passed: true,
330
+ project_path: projectPath,
331
+ drift_count: 0,
332
+ files: [],
333
+ summary: {
334
+ created: 0,
335
+ updated: 0,
336
+ unchanged: 0,
337
+ pending: 0
338
+ },
339
+ message: 'No .sce directory found; takeover baseline skipped.'
340
+ };
341
+ }
342
+
343
+ const adoptionPath = path.join(sceRoot, 'adoption-config.json');
344
+ const autoConfigPath = path.join(sceRoot, 'auto', 'config.json');
345
+ const takeoverConfigPath = path.join(sceRoot, 'config', 'takeover-baseline.json');
346
+ const sessionGovernancePath = path.join(sceRoot, 'config', 'session-governance.json');
347
+ const specDomainPolicyPath = path.join(sceRoot, 'config', 'spec-domain-policy.json');
348
+ const problemEvalPolicyPath = path.join(sceRoot, 'config', 'problem-eval-policy.json');
349
+ const reportPath = path.join(sceRoot, 'reports', 'takeover-baseline-latest.json');
350
+
351
+ const existingAdoption = await _readJsonSafe(adoptionPath, fileSystem);
352
+ const existingAuto = await _readJsonSafe(autoConfigPath, fileSystem);
353
+ const existingTakeover = await _readJsonSafe(takeoverConfigPath, fileSystem);
354
+ const existingSessionGovernance = await _readJsonSafe(sessionGovernancePath, fileSystem);
355
+ const existingSpecDomainPolicy = await _readJsonSafe(specDomainPolicyPath, fileSystem);
356
+ const existingProblemEvalPolicy = await _readJsonSafe(problemEvalPolicyPath, fileSystem);
357
+
358
+ const desiredAdoption = _buildAdoptionConfig(existingAdoption, nowIso, sceVersion);
359
+ const desiredAutoConfig = _buildAutoConfig(existingAuto);
360
+ const desiredTakeover = _buildTakeoverBaselineConfig(existingTakeover, sceVersion);
361
+ const desiredSessionGovernance = _deepMerge(existingSessionGovernance || {}, SESSION_GOVERNANCE_DEFAULTS);
362
+ const desiredSpecDomainPolicy = _deepMerge(existingSpecDomainPolicy || {}, SPEC_DOMAIN_POLICY_DEFAULTS);
363
+ const desiredProblemEvalPolicy = _deepMerge(existingProblemEvalPolicy || {}, PROBLEM_EVAL_POLICY_DEFAULTS);
364
+
365
+ const fileResults = [];
366
+ fileResults.push(await _reconcileJsonFile(adoptionPath, desiredAdoption, {
367
+ projectPath,
368
+ apply,
369
+ fileSystem
370
+ }));
371
+ fileResults.push(await _reconcileJsonFile(autoConfigPath, desiredAutoConfig, {
372
+ projectPath,
373
+ apply,
374
+ fileSystem
375
+ }));
376
+ fileResults.push(await _reconcileJsonFile(takeoverConfigPath, desiredTakeover, {
377
+ projectPath,
378
+ apply,
379
+ fileSystem
380
+ }));
381
+ fileResults.push(await _reconcileJsonFile(sessionGovernancePath, desiredSessionGovernance, {
382
+ projectPath,
383
+ apply,
384
+ fileSystem
385
+ }));
386
+ fileResults.push(await _reconcileJsonFile(specDomainPolicyPath, desiredSpecDomainPolicy, {
387
+ projectPath,
388
+ apply,
389
+ fileSystem
390
+ }));
391
+ fileResults.push(await _reconcileJsonFile(problemEvalPolicyPath, desiredProblemEvalPolicy, {
392
+ projectPath,
393
+ apply,
394
+ fileSystem
395
+ }));
396
+ fileResults.push(await _reconcileSteeringContract(projectPath, {
397
+ apply,
398
+ fileSystem
399
+ }));
400
+
401
+ const auditFiles = _toAuditStatus(fileResults, apply);
402
+ const summary = _summarize(auditFiles);
403
+ const driftCount = summary.pending;
404
+ const passed = driftCount === 0;
405
+
406
+ const report = {
407
+ mode: 'workspace-takeover-baseline',
408
+ generated_at: nowIso,
409
+ detected_project: true,
410
+ apply,
411
+ passed,
412
+ project_path: projectPath,
413
+ sce_version: sceVersion,
414
+ drift_count: driftCount,
415
+ enforced_defaults: _clone(TAKEOVER_DEFAULTS),
416
+ files: auditFiles,
417
+ summary
418
+ };
419
+
420
+ if (apply && writeReport) {
421
+ const reportExists = await fileSystem.pathExists(reportPath);
422
+ const shouldWriteReport = options.forceWriteReport === true
423
+ || !reportExists
424
+ || summary.created > 0
425
+ || summary.updated > 0;
426
+
427
+ if (shouldWriteReport) {
428
+ await fileSystem.ensureDir(path.dirname(reportPath));
429
+ await fileSystem.writeJson(reportPath, report, { spaces: 2 });
430
+ }
431
+ if (reportExists || shouldWriteReport) {
432
+ report.report_file = _toRelativePosix(projectPath, reportPath);
433
+ }
434
+ }
435
+
436
+ return report;
437
+ }
438
+
439
+ module.exports = {
440
+ TAKEOVER_BASELINE_SCHEMA_VERSION,
441
+ TAKEOVER_DEFAULTS,
442
+ SESSION_GOVERNANCE_DEFAULTS,
443
+ SPEC_DOMAIN_POLICY_DEFAULTS,
444
+ PROBLEM_EVAL_POLICY_DEFAULTS,
445
+ applyTakeoverBaseline
446
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scene-capability-engine",
3
- "version": "3.3.26",
3
+ "version": "3.4.5",
4
4
  "description": "SCE (Scene Capability Engine) - A CLI tool and npm package for spec-driven development with AI coding assistants.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1,36 @@
1
+ {
2
+ "schema_version": "1.0",
3
+ "enabled": true,
4
+ "mode": "required",
5
+ "enforce_on_stages": [
6
+ "plan",
7
+ "generate",
8
+ "apply",
9
+ "verify",
10
+ "release"
11
+ ],
12
+ "block_on_stages": [
13
+ "apply",
14
+ "release"
15
+ ],
16
+ "min_confidence_by_stage": {
17
+ "plan": 20,
18
+ "generate": 25,
19
+ "apply": 30,
20
+ "verify": 35,
21
+ "release": 40
22
+ },
23
+ "high_risk_requires_debug_evidence": true,
24
+ "high_risk_keywords": [
25
+ "auth",
26
+ "payment",
27
+ "security",
28
+ "delete",
29
+ "rollback",
30
+ "production",
31
+ "migrate",
32
+ "compliance",
33
+ "data-loss"
34
+ ],
35
+ "recommendation_limit": 6
36
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "schema_version": "1.0",
3
+ "scene_primary_session_required": true,
4
+ "one_scene_one_primary_session": true,
5
+ "spec_runs_bind_child_session": true,
6
+ "scene_completion_auto_rollover": true,
7
+ "auto_archive_spec_sessions": true
8
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "schema_version": "1.0",
3
+ "closed_loop_research_required": true,
4
+ "coverage_validation_required": true,
5
+ "fail_on_gap_default": true
6
+ }
@@ -0,0 +1,33 @@
1
+ {
2
+ "schema_version": "1.0",
3
+ "engine": "sce",
4
+ "managed": true,
5
+ "defaults": {
6
+ "autonomous": {
7
+ "enabled": true,
8
+ "mode": "aggressive",
9
+ "require_step_confirmation": false,
10
+ "apply_all_work_by_default": true
11
+ },
12
+ "session_governance": {
13
+ "scene_primary_session_required": true,
14
+ "one_scene_one_primary_session": true,
15
+ "spec_runs_bind_child_session": true,
16
+ "scene_completion_auto_rollover": true
17
+ },
18
+ "spec_domain_policy": {
19
+ "closed_loop_research_required": true,
20
+ "coverage_validation_required": true,
21
+ "fail_on_gap_default": true
22
+ },
23
+ "debug_policy": {
24
+ "prioritize_root_cause_fix": true,
25
+ "max_direct_fix_rounds_before_debug": 2,
26
+ "forbid_bypass_workarounds": true
27
+ },
28
+ "migration_policy": {
29
+ "legacy_kiro_supported": false,
30
+ "require_manual_legacy_migration_confirmation": true
31
+ }
32
+ }
33
+ }