scene-capability-engine 3.6.38 → 3.6.44
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 +63 -0
- package/bin/scene-capability-engine.js +42 -2
- package/docs/command-reference.md +27 -0
- package/docs/developer-guide.md +1 -1
- package/docs/document-governance.md +22 -2
- package/docs/releases/README.md +6 -0
- package/docs/releases/v3.6.39.md +24 -0
- package/docs/releases/v3.6.40.md +19 -0
- package/docs/releases/v3.6.41.md +20 -0
- package/docs/releases/v3.6.42.md +19 -0
- package/docs/releases/v3.6.43.md +17 -0
- package/docs/releases/v3.6.44.md +17 -0
- package/docs/spec-collaboration-guide.md +1 -1
- package/docs/state-migration-reconciliation-runbook.md +76 -0
- package/docs/state-storage-tiering.md +104 -0
- package/docs/zh/releases/README.md +6 -0
- package/docs/zh/releases/v3.6.39.md +24 -0
- package/docs/zh/releases/v3.6.40.md +19 -0
- package/docs/zh/releases/v3.6.41.md +20 -0
- package/docs/zh/releases/v3.6.42.md +19 -0
- package/docs/zh/releases/v3.6.43.md +17 -0
- package/docs/zh/releases/v3.6.44.md +17 -0
- package/lib/adoption/adoption-logger.js +1 -1
- package/lib/adoption/adoption-strategy.js +29 -29
- package/lib/adoption/detection-engine.js +16 -13
- package/lib/adoption/smart-orchestrator.js +3 -3
- package/lib/adoption/strategy-selector.js +19 -15
- package/lib/adoption/template-sync.js +3 -3
- package/lib/auto/autonomous-engine.js +5 -5
- package/lib/auto/handoff-release-gate-history-loaders-service.js +24 -4
- package/lib/auto/handoff-run-service.js +37 -0
- package/lib/backup/backup-system.js +10 -10
- package/lib/collab/collab-manager.js +8 -5
- package/lib/collab/dependency-manager.js +1 -1
- package/lib/commands/adopt.js +2 -2
- package/lib/commands/auto.js +239 -97
- package/lib/commands/collab.js +10 -4
- package/lib/commands/docs.js +8 -2
- package/lib/commands/scene.js +78 -18
- package/lib/commands/status.js +3 -3
- package/lib/commands/studio.js +8 -0
- package/lib/commands/watch.js +10 -1
- package/lib/governance/config-manager.js +16 -0
- package/lib/governance/diagnostic-engine.js +2 -1
- package/lib/governance/validation-engine.js +3 -2
- package/lib/repo/config-manager.js +2 -2
- package/lib/runtime/session-store.js +8 -0
- package/lib/spec/bootstrap/context-collector.js +5 -4
- package/lib/spec-gate/rules/default-rules.js +8 -8
- package/lib/state/sce-state-store.js +265 -0
- package/lib/state/state-migration-manager.js +27 -2
- package/lib/state/state-storage-policy.js +179 -0
- package/lib/upgrade/migration-engine.js +5 -5
- package/lib/upgrade/migrations/1.0.0-to-1.1.0.js +3 -3
- package/lib/utils/tool-detector.js +4 -4
- package/lib/utils/validation.js +6 -6
- package/lib/watch/action-executor.js +10 -1
- package/lib/watch/event-debouncer.js +3 -0
- package/lib/watch/file-watcher.js +51 -10
- package/lib/watch/watch-manager.js +10 -1
- package/lib/workspace/multi/workspace-context-resolver.js +3 -3
- package/lib/workspace/multi/workspace-registry.js +3 -3
- package/lib/workspace/multi/workspace-state-manager.js +3 -3
- package/lib/workspace/spec-delivery-audit.js +553 -0
- package/lib/workspace/takeover-baseline.js +11 -0
- package/package.json +5 -1
- package/template/.sce/config/state-storage-policy.json +165 -0
package/lib/commands/collab.js
CHANGED
|
@@ -90,7 +90,10 @@ function registerCollabCommands(program) {
|
|
|
90
90
|
console.log(chalk.gray('Type:'), status.metadata.type);
|
|
91
91
|
console.log(chalk.gray('Status:'), status.metadata.status.current);
|
|
92
92
|
if (status.metadata.assignment) {
|
|
93
|
-
console.log(
|
|
93
|
+
console.log(
|
|
94
|
+
chalk.gray('Assigned to:'),
|
|
95
|
+
status.metadata.assignment.sceInstance || status.metadata.assignment.kiroInstance
|
|
96
|
+
);
|
|
94
97
|
}
|
|
95
98
|
if (status.metadata.dependencies && status.metadata.dependencies.length > 0) {
|
|
96
99
|
console.log(chalk.gray('Dependencies:'));
|
|
@@ -105,8 +108,11 @@ function registerCollabCommands(program) {
|
|
|
105
108
|
|
|
106
109
|
for (const { name, metadata } of status.specs) {
|
|
107
110
|
const symbol = getStatusSymbol(metadata.status.current);
|
|
111
|
+
const assignedInstance = metadata.assignment
|
|
112
|
+
? (metadata.assignment.sceInstance || metadata.assignment.kiroInstance)
|
|
113
|
+
: null;
|
|
108
114
|
const assignment = metadata.assignment
|
|
109
|
-
? chalk.gray(`(${
|
|
115
|
+
? chalk.gray(`(${assignedInstance})`)
|
|
110
116
|
: chalk.gray('(unassigned)');
|
|
111
117
|
console.log(`${symbol} ${name} ${assignment}`);
|
|
112
118
|
}
|
|
@@ -127,12 +133,12 @@ function registerCollabCommands(program) {
|
|
|
127
133
|
collab
|
|
128
134
|
.command('assign <spec-name> <SCE-instance>')
|
|
129
135
|
.description('Assign a spec to a SCE instance')
|
|
130
|
-
.action(async (specName,
|
|
136
|
+
.action(async (specName, sceInstance) => {
|
|
131
137
|
try {
|
|
132
138
|
const workspaceRoot = process.cwd();
|
|
133
139
|
const manager = new CollaborationManager(workspaceRoot);
|
|
134
140
|
|
|
135
|
-
const result = await manager.assignSpec(specName,
|
|
141
|
+
const result = await manager.assignSpec(specName, sceInstance);
|
|
136
142
|
|
|
137
143
|
if (result.success) {
|
|
138
144
|
console.log(chalk.green('✓'), result.message);
|
package/lib/commands/docs.js
CHANGED
|
@@ -301,6 +301,12 @@ async function handleConfigDisplay(configManager, reporter) {
|
|
|
301
301
|
console.log(` • ${dir}`);
|
|
302
302
|
});
|
|
303
303
|
console.log();
|
|
304
|
+
|
|
305
|
+
console.log(chalk.bold('Spec Allowed Root Files:'));
|
|
306
|
+
(config.specAllowedRootFiles || []).forEach(file => {
|
|
307
|
+
console.log(` • ${file}`);
|
|
308
|
+
});
|
|
309
|
+
console.log();
|
|
304
310
|
|
|
305
311
|
console.log(chalk.bold('Temporary Patterns:'));
|
|
306
312
|
config.temporaryPatterns.forEach(pattern => {
|
|
@@ -350,10 +356,10 @@ async function handleConfigSet(configManager, reporter, options) {
|
|
|
350
356
|
const camelKey = key.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
351
357
|
|
|
352
358
|
// Validate key
|
|
353
|
-
const validKeys = ['rootAllowedFiles', 'specSubdirs', 'temporaryPatterns'];
|
|
359
|
+
const validKeys = ['rootAllowedFiles', 'specAllowedRootFiles', 'specSubdirs', 'temporaryPatterns'];
|
|
354
360
|
if (!validKeys.includes(camelKey)) {
|
|
355
361
|
reporter.displayError(`Invalid configuration key: ${key}`);
|
|
356
|
-
console.log(chalk.gray('Valid keys: root-allowed-files, spec-subdirs, temporary-patterns\n'));
|
|
362
|
+
console.log(chalk.gray('Valid keys: root-allowed-files, spec-allowed-root-files, spec-subdirs, temporary-patterns\n'));
|
|
357
363
|
return 2;
|
|
358
364
|
}
|
|
359
365
|
|
package/lib/commands/scene.js
CHANGED
|
@@ -158,6 +158,45 @@ function createDoctorTraceId() {
|
|
|
158
158
|
return `doctor-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
function stringifyJsonForCli(value, indentSize = 2, currentIndent = 0) {
|
|
162
|
+
if (value === null) {
|
|
163
|
+
return 'null';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (typeof value === 'number') {
|
|
167
|
+
return Object.is(value, -0) ? '-0' : JSON.stringify(value);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (typeof value === 'string' || typeof value === 'boolean') {
|
|
171
|
+
return JSON.stringify(value);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (Array.isArray(value)) {
|
|
175
|
+
if (value.length === 0) {
|
|
176
|
+
return '[]';
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const nextIndent = currentIndent + indentSize;
|
|
180
|
+
const indent = ' '.repeat(currentIndent);
|
|
181
|
+
const childIndent = ' '.repeat(nextIndent);
|
|
182
|
+
const items = value.map((entry) => `${childIndent}${stringifyJsonForCli(entry, indentSize, nextIndent)}`);
|
|
183
|
+
return `[\n${items.join(',\n')}\n${indent}]`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const keys = Object.keys(value);
|
|
187
|
+
if (keys.length === 0) {
|
|
188
|
+
return '{}';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const nextIndent = currentIndent + indentSize;
|
|
192
|
+
const indent = ' '.repeat(currentIndent);
|
|
193
|
+
const childIndent = ' '.repeat(nextIndent);
|
|
194
|
+
const entries = keys.map((key) => (
|
|
195
|
+
`${childIndent}${JSON.stringify(key)}: ${stringifyJsonForCli(value[key], indentSize, nextIndent)}`
|
|
196
|
+
));
|
|
197
|
+
return `{\n${entries.join(',\n')}\n${indent}}`;
|
|
198
|
+
}
|
|
199
|
+
|
|
161
200
|
function registerSceneCommands(program) {
|
|
162
201
|
const sceneCmd = program
|
|
163
202
|
.command('scene')
|
|
@@ -4795,7 +4834,7 @@ function createSceneEvalConfigTemplateByProfile(profile = 'default') {
|
|
|
4795
4834
|
}
|
|
4796
4835
|
|
|
4797
4836
|
function normalizeRelativePath(targetPath = '') {
|
|
4798
|
-
return String(targetPath || '').replace(/\\/g, '/').replace(/\/+/g, '/').replace(/^\.\//, '');
|
|
4837
|
+
return String(targetPath || '').trim().replace(/\\/g, '/').replace(/\/+/g, '/').replace(/^\.\//, '');
|
|
4799
4838
|
}
|
|
4800
4839
|
|
|
4801
4840
|
function collectManifestDiscoveryCandidates(preferredPath = 'custom/scene.yaml') {
|
|
@@ -13639,6 +13678,35 @@ async function runSceneVersionCommand(rawOptions = {}, dependencies = {}) {
|
|
|
13639
13678
|
}
|
|
13640
13679
|
}
|
|
13641
13680
|
|
|
13681
|
+
function isBinaryContent(buffer) {
|
|
13682
|
+
if (!Buffer.isBuffer(buffer)) {
|
|
13683
|
+
return false;
|
|
13684
|
+
}
|
|
13685
|
+
|
|
13686
|
+
for (let index = 0; index < buffer.length; index++) {
|
|
13687
|
+
if (buffer[index] === 0) {
|
|
13688
|
+
return true;
|
|
13689
|
+
}
|
|
13690
|
+
}
|
|
13691
|
+
|
|
13692
|
+
return false;
|
|
13693
|
+
}
|
|
13694
|
+
|
|
13695
|
+
function countChangedLines(fromContent, toContent) {
|
|
13696
|
+
const oldLines = fromContent.toString('utf8').split('\n');
|
|
13697
|
+
const newLines = toContent.toString('utf8').split('\n');
|
|
13698
|
+
const maxLen = Math.max(oldLines.length, newLines.length);
|
|
13699
|
+
let changedLines = 0;
|
|
13700
|
+
|
|
13701
|
+
for (let index = 0; index < maxLen; index++) {
|
|
13702
|
+
if ((oldLines[index] || '') !== (newLines[index] || '')) {
|
|
13703
|
+
changedLines++;
|
|
13704
|
+
}
|
|
13705
|
+
}
|
|
13706
|
+
|
|
13707
|
+
return changedLines;
|
|
13708
|
+
}
|
|
13709
|
+
|
|
13642
13710
|
function buildPackageDiff(fromFiles, toFiles) {
|
|
13643
13711
|
const fromMap = new Map();
|
|
13644
13712
|
for (const f of (fromFiles || [])) {
|
|
@@ -13662,19 +13730,9 @@ function buildPackageDiff(fromFiles, toFiles) {
|
|
|
13662
13730
|
if (Buffer.compare(content, toContent) === 0) {
|
|
13663
13731
|
unchanged.push(filePath);
|
|
13664
13732
|
} else {
|
|
13665
|
-
|
|
13666
|
-
|
|
13667
|
-
|
|
13668
|
-
const newLines = toContent.toString('utf8').split('\n');
|
|
13669
|
-
const maxLen = Math.max(oldLines.length, newLines.length);
|
|
13670
|
-
for (let i = 0; i < maxLen; i++) {
|
|
13671
|
-
if ((oldLines[i] || '') !== (newLines[i] || '')) {
|
|
13672
|
-
changedLines++;
|
|
13673
|
-
}
|
|
13674
|
-
}
|
|
13675
|
-
} catch (_e) {
|
|
13676
|
-
changedLines = -1;
|
|
13677
|
-
}
|
|
13733
|
+
const changedLines = isBinaryContent(content) || isBinaryContent(toContent)
|
|
13734
|
+
? -1
|
|
13735
|
+
: countChangedLines(content, toContent);
|
|
13678
13736
|
modified.push({ path: filePath, changedLines });
|
|
13679
13737
|
}
|
|
13680
13738
|
}
|
|
@@ -13734,7 +13792,9 @@ function printSceneDiffSummary(options, payload, projectRoot = process.cwd()) {
|
|
|
13734
13792
|
console.log(chalk.red(` - ${f}`));
|
|
13735
13793
|
}
|
|
13736
13794
|
for (const f of payload.files.modified) {
|
|
13737
|
-
const detail =
|
|
13795
|
+
const detail = options.stat
|
|
13796
|
+
? ''
|
|
13797
|
+
: (f.changedLines >= 0 ? ` (${f.changedLines} lines changed)` : ' (binary content differs)');
|
|
13738
13798
|
console.log(chalk.yellow(` ~ ${f.path}${detail}`));
|
|
13739
13799
|
}
|
|
13740
13800
|
}
|
|
@@ -15248,7 +15308,7 @@ async function runSceneLintCommand(rawOptions = {}, dependencies = {}) {
|
|
|
15248
15308
|
|
|
15249
15309
|
function printSceneLintSummary(options, payload, projectRoot = process.cwd()) {
|
|
15250
15310
|
if (options.json) {
|
|
15251
|
-
console.log(
|
|
15311
|
+
console.log(stringifyJsonForCli(payload));
|
|
15252
15312
|
return;
|
|
15253
15313
|
}
|
|
15254
15314
|
|
|
@@ -15344,7 +15404,7 @@ async function runSceneScoreCommand(rawOptions = {}, dependencies = {}) {
|
|
|
15344
15404
|
|
|
15345
15405
|
function printSceneScoreSummary(options, payload, projectRoot = process.cwd()) {
|
|
15346
15406
|
if (options.json) {
|
|
15347
|
-
console.log(
|
|
15407
|
+
console.log(stringifyJsonForCli(payload));
|
|
15348
15408
|
return;
|
|
15349
15409
|
}
|
|
15350
15410
|
|
|
@@ -15524,7 +15584,7 @@ async function runSceneContributeCommand(rawOptions = {}, dependencies = {}) {
|
|
|
15524
15584
|
|
|
15525
15585
|
function printSceneContributeSummary(options, payload, projectRoot = process.cwd()) {
|
|
15526
15586
|
if (options.json) {
|
|
15527
|
-
console.log(
|
|
15587
|
+
console.log(stringifyJsonForCli(payload));
|
|
15528
15588
|
return;
|
|
15529
15589
|
}
|
|
15530
15590
|
|
package/lib/commands/status.js
CHANGED
|
@@ -30,10 +30,10 @@ async function statusCommand(options = {}) {
|
|
|
30
30
|
|
|
31
31
|
try {
|
|
32
32
|
// 1. Check if .sce/ exists
|
|
33
|
-
const
|
|
34
|
-
const
|
|
33
|
+
const scePath = path.join(projectPath, '.sce');
|
|
34
|
+
const sceExists = await fs.pathExists(scePath);
|
|
35
35
|
|
|
36
|
-
if (!
|
|
36
|
+
if (!sceExists) {
|
|
37
37
|
console.log(chalk.yellow('⚠️ No .sce/ directory found'));
|
|
38
38
|
console.log();
|
|
39
39
|
console.log('This project has not been adopted yet.');
|
package/lib/commands/studio.js
CHANGED
|
@@ -502,6 +502,14 @@ async function buildReleaseGateSteps(options = {}, dependencies = {}) {
|
|
|
502
502
|
required: true
|
|
503
503
|
});
|
|
504
504
|
|
|
505
|
+
steps.push({
|
|
506
|
+
id: 'spec-delivery-audit',
|
|
507
|
+
name: 'spec delivery sync audit',
|
|
508
|
+
command: 'node',
|
|
509
|
+
args: ['bin/sce.js', 'workspace', 'delivery-audit', '--json', '--strict'],
|
|
510
|
+
required: true
|
|
511
|
+
});
|
|
512
|
+
|
|
505
513
|
const gitManagedGateScript = path.join(projectPath, 'scripts', 'git-managed-gate.js');
|
|
506
514
|
const hasGitManagedGateScript = await fileSystem.pathExists(gitManagedGateScript);
|
|
507
515
|
steps.push({
|
package/lib/commands/watch.js
CHANGED
|
@@ -11,6 +11,15 @@ const inquirer = require('inquirer');
|
|
|
11
11
|
const WatchManager = require('../watch/watch-manager');
|
|
12
12
|
const { listPresets, getPreset, mergePreset, validatePreset } = require('../watch/presets');
|
|
13
13
|
|
|
14
|
+
function sleep(ms) {
|
|
15
|
+
return new Promise(resolve => {
|
|
16
|
+
const timer = setTimeout(resolve, ms);
|
|
17
|
+
if (typeof timer.unref === 'function') {
|
|
18
|
+
timer.unref();
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
/**
|
|
15
24
|
* Start watch mode
|
|
16
25
|
*
|
|
@@ -329,7 +338,7 @@ async function followLogStream(logPath, options = {}) {
|
|
|
329
338
|
break;
|
|
330
339
|
}
|
|
331
340
|
|
|
332
|
-
await
|
|
341
|
+
await sleep(pollIntervalMs);
|
|
333
342
|
}
|
|
334
343
|
} finally {
|
|
335
344
|
process.removeListener('SIGINT', onSigInt);
|
|
@@ -52,6 +52,12 @@ class ConfigManager {
|
|
|
52
52
|
'CHANGELOG.md',
|
|
53
53
|
'CONTRIBUTING.md'
|
|
54
54
|
],
|
|
55
|
+
specAllowedRootFiles: [
|
|
56
|
+
'requirements.md',
|
|
57
|
+
'design.md',
|
|
58
|
+
'tasks.md',
|
|
59
|
+
'collaboration.json'
|
|
60
|
+
],
|
|
55
61
|
specSubdirs: [
|
|
56
62
|
'reports',
|
|
57
63
|
'scripts',
|
|
@@ -152,6 +158,10 @@ class ConfigManager {
|
|
|
152
158
|
if (!config.specSubdirs || !Array.isArray(config.specSubdirs)) {
|
|
153
159
|
errors.push('specSubdirs must be an array');
|
|
154
160
|
}
|
|
161
|
+
|
|
162
|
+
if (!config.specAllowedRootFiles || !Array.isArray(config.specAllowedRootFiles)) {
|
|
163
|
+
errors.push('specAllowedRootFiles must be an array');
|
|
164
|
+
}
|
|
155
165
|
|
|
156
166
|
if (!config.temporaryPatterns || !Array.isArray(config.temporaryPatterns)) {
|
|
157
167
|
errors.push('temporaryPatterns must be an array');
|
|
@@ -169,6 +179,12 @@ class ConfigManager {
|
|
|
169
179
|
errors.push('specSubdirs must contain only strings');
|
|
170
180
|
}
|
|
171
181
|
}
|
|
182
|
+
|
|
183
|
+
if (config.specAllowedRootFiles && Array.isArray(config.specAllowedRootFiles)) {
|
|
184
|
+
if (config.specAllowedRootFiles.some(f => typeof f !== 'string')) {
|
|
185
|
+
errors.push('specAllowedRootFiles must contain only strings');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
172
188
|
|
|
173
189
|
if (config.temporaryPatterns && Array.isArray(config.temporaryPatterns)) {
|
|
174
190
|
if (config.temporaryPatterns.some(p => typeof p !== 'string')) {
|
|
@@ -81,6 +81,7 @@ class DiagnosticEngine {
|
|
|
81
81
|
async scanSpecDirectory(specPath) {
|
|
82
82
|
const specName = path.basename(specPath);
|
|
83
83
|
const requiredFiles = ['requirements.md', 'design.md', 'tasks.md'];
|
|
84
|
+
const allowedRootFiles = this.config.specAllowedRootFiles || requiredFiles;
|
|
84
85
|
|
|
85
86
|
// Check for missing required files
|
|
86
87
|
for (const requiredFile of requiredFiles) {
|
|
@@ -124,7 +125,7 @@ class DiagnosticEngine {
|
|
|
124
125
|
const basename = path.basename(filePath);
|
|
125
126
|
|
|
126
127
|
// Skip required files
|
|
127
|
-
if (
|
|
128
|
+
if (allowedRootFiles.includes(basename)) {
|
|
128
129
|
continue;
|
|
129
130
|
}
|
|
130
131
|
|
|
@@ -84,6 +84,8 @@ class ValidationEngine {
|
|
|
84
84
|
*/
|
|
85
85
|
async validateSpec(specName) {
|
|
86
86
|
const specPath = this.scanner.getSpecDirectory(specName);
|
|
87
|
+
const requiredFiles = ['requirements.md', 'design.md', 'tasks.md'];
|
|
88
|
+
const allowedRootFiles = this.config.specAllowedRootFiles || requiredFiles;
|
|
87
89
|
|
|
88
90
|
// Check if Spec directory exists
|
|
89
91
|
if (!await this.scanner.exists(specPath)) {
|
|
@@ -97,7 +99,6 @@ class ValidationEngine {
|
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
// Check required files
|
|
100
|
-
const requiredFiles = ['requirements.md', 'design.md', 'tasks.md'];
|
|
101
102
|
for (const file of requiredFiles) {
|
|
102
103
|
const filePath = path.join(specPath, file);
|
|
103
104
|
if (!await this.scanner.exists(filePath)) {
|
|
@@ -118,7 +119,7 @@ class ValidationEngine {
|
|
|
118
119
|
const basename = path.basename(filePath);
|
|
119
120
|
|
|
120
121
|
// Skip required files
|
|
121
|
-
if (
|
|
122
|
+
if (allowedRootFiles.includes(basename)) {
|
|
122
123
|
continue;
|
|
123
124
|
}
|
|
124
125
|
|
|
@@ -131,8 +131,8 @@ class ConfigManager {
|
|
|
131
131
|
|
|
132
132
|
try {
|
|
133
133
|
// Ensure .sce directory exists
|
|
134
|
-
const
|
|
135
|
-
await fs.mkdir(
|
|
134
|
+
const sceDir = path.dirname(configPath);
|
|
135
|
+
await fs.mkdir(sceDir, { recursive: true });
|
|
136
136
|
|
|
137
137
|
// Write configuration with pretty formatting
|
|
138
138
|
const jsonContent = JSON.stringify(config, null, 2);
|
|
@@ -568,26 +568,34 @@ class SessionStore {
|
|
|
568
568
|
|
|
569
569
|
const fileCount = fileRecords.length;
|
|
570
570
|
const sqliteCount = Array.isArray(sqliteRecords) ? sqliteRecords.length : null;
|
|
571
|
+
let readSource = 'file';
|
|
571
572
|
|
|
572
573
|
let status = 'file-only';
|
|
573
574
|
if (sqliteCount === null) {
|
|
574
575
|
status = 'sqlite-unavailable';
|
|
575
576
|
} else if (fileCount === 0 && sqliteCount === 0) {
|
|
576
577
|
status = 'empty';
|
|
578
|
+
readSource = 'empty';
|
|
577
579
|
} else if (fileCount === 0 && sqliteCount > 0) {
|
|
578
580
|
status = 'sqlite-only';
|
|
581
|
+
readSource = 'sqlite';
|
|
579
582
|
} else if (fileCount > 0 && sqliteCount === 0) {
|
|
580
583
|
status = 'file-only';
|
|
584
|
+
readSource = 'file';
|
|
581
585
|
} else if (fileCount === sqliteCount) {
|
|
582
586
|
status = 'aligned';
|
|
587
|
+
readSource = this._preferSqliteSceneReads ? 'sqlite' : 'file';
|
|
583
588
|
} else if (sqliteCount < fileCount) {
|
|
584
589
|
status = 'pending-sync';
|
|
590
|
+
readSource = 'file';
|
|
585
591
|
} else if (sqliteCount > fileCount) {
|
|
586
592
|
status = 'sqlite-ahead';
|
|
593
|
+
readSource = 'sqlite';
|
|
587
594
|
}
|
|
588
595
|
|
|
589
596
|
return {
|
|
590
597
|
read_preference: this._preferSqliteSceneReads ? 'sqlite' : 'file',
|
|
598
|
+
read_source: readSource,
|
|
591
599
|
file_scene_count: fileCount,
|
|
592
600
|
sqlite_scene_count: sqliteCount,
|
|
593
601
|
status
|
|
@@ -7,14 +7,15 @@ class ContextCollector {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
async collect() {
|
|
10
|
-
const
|
|
11
|
-
const specsDir = path.join(
|
|
12
|
-
const
|
|
10
|
+
const sceDir = path.join(this.projectPath, '.sce');
|
|
11
|
+
const specsDir = path.join(sceDir, 'specs');
|
|
12
|
+
const hasSceDir = await fs.pathExists(sceDir);
|
|
13
13
|
const existingSpecs = await this._listSpecs(specsDir);
|
|
14
14
|
|
|
15
15
|
return {
|
|
16
16
|
projectPath: this.projectPath,
|
|
17
|
-
|
|
17
|
+
hasSceDir,
|
|
18
|
+
hasKiro: hasSceDir,
|
|
18
19
|
specsDir,
|
|
19
20
|
existingSpecs,
|
|
20
21
|
totalSpecs: existingSpecs.length,
|
|
@@ -87,25 +87,25 @@ function createDefaultRules(projectPath = process.cwd()) {
|
|
|
87
87
|
id: 'config_consistency',
|
|
88
88
|
description: 'Verify project-level sce config baseline exists',
|
|
89
89
|
async execute() {
|
|
90
|
-
const
|
|
91
|
-
const configDir = path.join(
|
|
92
|
-
const
|
|
90
|
+
const sceDir = path.join(projectPath, '.sce');
|
|
91
|
+
const configDir = path.join(sceDir, 'config');
|
|
92
|
+
const hasSceDir = await fs.pathExists(sceDir);
|
|
93
93
|
const hasConfig = await fs.pathExists(configDir);
|
|
94
94
|
|
|
95
|
-
const ratio =
|
|
95
|
+
const ratio = hasSceDir && hasConfig ? 1 : hasSceDir ? 0.5 : 0;
|
|
96
96
|
const warnings = [];
|
|
97
|
-
if (!
|
|
97
|
+
if (!hasSceDir) {
|
|
98
98
|
warnings.push('.sce directory missing');
|
|
99
99
|
}
|
|
100
|
-
if (
|
|
100
|
+
if (hasSceDir && !hasConfig) {
|
|
101
101
|
warnings.push('.sce/config directory missing');
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
return {
|
|
105
|
-
passed:
|
|
105
|
+
passed: hasSceDir,
|
|
106
106
|
ratio,
|
|
107
107
|
details: {
|
|
108
|
-
|
|
108
|
+
hasSceDir,
|
|
109
109
|
hasConfig
|
|
110
110
|
},
|
|
111
111
|
warnings
|