scene-capability-engine 3.6.28 → 3.6.32

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,269 @@
1
+ function buildStudioTaskKey(stageName = '') {
2
+ const normalizedStage = (typeof stageName === 'string' ? stageName.trim() : '') || 'task';
3
+ return 'studio:' + normalizedStage;
4
+ }
5
+
6
+ async function resolveTaskReference(mode, job, options = {}, dependencies = {}) {
7
+ const normalizeString = dependencies.normalizeString;
8
+ const resolveTaskStage = dependencies.resolveTaskStage;
9
+ const TaskRefRegistry = dependencies.TaskRefRegistry;
10
+ const fileSystem = dependencies.fileSystem;
11
+ const explicitTaskRef = normalizeString(options.taskRef);
12
+ if (explicitTaskRef) {
13
+ return explicitTaskRef;
14
+ }
15
+
16
+ const sceneId = normalizeString(job && job.scene && job.scene.id);
17
+ const specId = normalizeString(job && job.scene && job.scene.spec_id) || normalizeString(job && job.source && job.source.spec_id);
18
+ if (!sceneId || !specId) {
19
+ return null;
20
+ }
21
+
22
+ const stageName = resolveTaskStage(mode, job, options.stageName);
23
+ const taskKey = normalizeString(options.taskKey) || buildStudioTaskKey(stageName);
24
+ const projectPath = normalizeString(options.projectPath) || process.cwd();
25
+ const taskRefRegistry = options.taskRefRegistry || new TaskRefRegistry(projectPath, { fileSystem });
26
+
27
+ try {
28
+ const taskRef = await taskRefRegistry.resolveOrCreateRef({
29
+ sceneId,
30
+ specId,
31
+ taskKey,
32
+ source: 'studio-stage',
33
+ metadata: {
34
+ mode: normalizeString(mode) || null,
35
+ stage: stageName || null,
36
+ job_id: normalizeString(job && job.job_id) || null
37
+ }
38
+ });
39
+ return taskRef.task_ref;
40
+ } catch (_error) {
41
+ return null;
42
+ }
43
+ }
44
+
45
+ function collectTaskFileChanges(job = {}, stageName = '', stageMetadata = {}, dependencies = {}) {
46
+ const normalizeString = dependencies.normalizeString;
47
+ const normalizeTaskFileChanges = dependencies.normalizeTaskFileChanges;
48
+ const fileChanges = [];
49
+ if (Array.isArray(stageMetadata && stageMetadata.file_changes)) {
50
+ fileChanges.push(...stageMetadata.file_changes);
51
+ }
52
+
53
+ const createdSpec = job && job.source && job.source.intake && job.source.intake.created_spec;
54
+ const createdSpecId = createdSpec && createdSpec.created
55
+ ? normalizeString(createdSpec.spec_id)
56
+ : '';
57
+ if (stageName === 'plan' && createdSpecId) {
58
+ fileChanges.push(
59
+ { path: '.sce/specs/' + createdSpecId + '/requirements.md', line: 1 },
60
+ { path: '.sce/specs/' + createdSpecId + '/design.md', line: 1 },
61
+ { path: '.sce/specs/' + createdSpecId + '/tasks.md', line: 1 },
62
+ { path: '.sce/specs/' + createdSpecId + '/custom/problem-domain-chain.json', line: 1 },
63
+ { path: '.sce/specs/' + createdSpecId + '/custom/problem-contract.json', line: 1 }
64
+ );
65
+ }
66
+
67
+ const artifacts = job && job.artifacts ? job.artifacts : {};
68
+ if (stageName === 'plan') {
69
+ if (normalizeString(artifacts.spec_portfolio_report)) {
70
+ fileChanges.push({ path: artifacts.spec_portfolio_report, line: 1 });
71
+ }
72
+ if (normalizeString(artifacts.spec_scene_index)) {
73
+ fileChanges.push({ path: artifacts.spec_scene_index, line: 1 });
74
+ }
75
+ }
76
+ if (stageName === 'generate' && normalizeString(artifacts.generate_report)) {
77
+ fileChanges.push({ path: artifacts.generate_report, line: 1 });
78
+ }
79
+ if (stageName === 'verify' && normalizeString(artifacts.verify_report)) {
80
+ fileChanges.push({ path: artifacts.verify_report, line: 1 });
81
+ }
82
+ if (stageName === 'release' && normalizeString(artifacts.release_report)) {
83
+ fileChanges.push({ path: artifacts.release_report, line: 1 });
84
+ }
85
+ return normalizeTaskFileChanges(fileChanges);
86
+ }
87
+
88
+ function collectTaskEvidence(job = {}, stageName = '', stageMetadata = {}, dependencies = {}) {
89
+ const normalizeString = dependencies.normalizeString;
90
+ const normalizeTaskEvidence = dependencies.normalizeTaskEvidence;
91
+ const evidence = [];
92
+ const stageReport = normalizeString(stageMetadata && stageMetadata.report);
93
+ if (stageReport) {
94
+ evidence.push({ type: 'stage-report', ref: stageReport, detail: stageName });
95
+ }
96
+
97
+ const artifacts = job && job.artifacts ? job.artifacts : {};
98
+ if (stageName && artifacts.problem_eval_reports && artifacts.problem_eval_reports[stageName]) {
99
+ evidence.push({
100
+ type: 'problem-evaluation-report',
101
+ ref: artifacts.problem_eval_reports[stageName],
102
+ detail: stageName
103
+ });
104
+ }
105
+
106
+ if (normalizeString(job && job.source && job.source.domain_chain && job.source.domain_chain.chain_path)) {
107
+ evidence.push({
108
+ type: 'domain-chain',
109
+ ref: job.source.domain_chain.chain_path,
110
+ detail: stageName
111
+ });
112
+ }
113
+ if (normalizeString(job && job.source && job.source.problem_contract_path)) {
114
+ evidence.push({
115
+ type: 'problem-contract',
116
+ ref: job.source.problem_contract_path,
117
+ detail: stageName
118
+ });
119
+ }
120
+ if (Array.isArray(stageMetadata && stageMetadata.auto_errorbook_records)) {
121
+ for (const item of stageMetadata.auto_errorbook_records) {
122
+ const entryId = normalizeString(item && item.entry_id);
123
+ if (!entryId) {
124
+ continue;
125
+ }
126
+ evidence.push({
127
+ type: 'errorbook-entry',
128
+ ref: entryId,
129
+ detail: normalizeString(item && item.step_id) || null
130
+ });
131
+ }
132
+ }
133
+
134
+ if (normalizeString(job && job.job_id)) {
135
+ evidence.push({
136
+ type: 'event-log',
137
+ ref: '.sce/state/sce-state.sqlite',
138
+ detail: 'studio_event_stream:job_id=' + job.job_id
139
+ });
140
+ }
141
+
142
+ return normalizeTaskEvidence(evidence);
143
+ }
144
+
145
+ function buildTaskEnvelope(mode, job, options = {}, dependencies = {}) {
146
+ const resolveTaskStage = dependencies.resolveTaskStage;
147
+ const resolveNextAction = dependencies.resolveNextAction;
148
+ const normalizeString = dependencies.normalizeString;
149
+ const normalizeTaskCommands = dependencies.normalizeTaskCommands;
150
+ const normalizeTaskErrors = dependencies.normalizeTaskErrors;
151
+ const extractCommandsFromStageMetadata = dependencies.extractCommandsFromStageMetadata;
152
+ const extractErrorsFromStageMetadata = dependencies.extractErrorsFromStageMetadata;
153
+ const deriveTaskIntentShape = dependencies.deriveTaskIntentShape;
154
+ const buildTaskSummaryLines = dependencies.buildTaskSummaryLines;
155
+ const buildTaskAcceptanceCriteria = dependencies.buildTaskAcceptanceCriteria;
156
+
157
+ const stageName = resolveTaskStage(mode, job, options.stageName);
158
+ const stageState = stageName && job && job.stages && job.stages[stageName]
159
+ ? job.stages[stageName]
160
+ : {};
161
+ const stageMetadata = stageState && typeof stageState.metadata === 'object' && stageState.metadata
162
+ ? stageState.metadata
163
+ : {};
164
+ const nextAction = resolveNextAction(job);
165
+
166
+ const events = Array.isArray(options.events)
167
+ ? options.events
168
+ : (options.event ? [options.event] : []);
169
+ const latestEvent = events.length > 0 ? events[events.length - 1] : null;
170
+
171
+ const taskStatus = normalizeString(stageState && stageState.status)
172
+ || (stageName === 'rollback' && normalizeString(job && job.status) === 'rolled_back'
173
+ ? 'completed'
174
+ : normalizeString(job && job.status) || 'unknown');
175
+ const taskId = normalizeString(options.taskId)
176
+ || (normalizeString(job && job.job_id)
177
+ ? job.job_id + ':' + (stageName || 'task')
178
+ : null);
179
+ const rawRequest = normalizeString(job && job.source && job.source.goal);
180
+ const goal = rawRequest || ('Studio ' + (stageName || 'task') + ' execution');
181
+ const taskIntent = deriveTaskIntentShape(rawRequest, stageName);
182
+ const sessionId = normalizeString(job && job.session && job.session.scene_session_id) || null;
183
+ const sceneId = normalizeString(job && job.scene && job.scene.id) || null;
184
+ const specId = normalizeString(job && job.scene && job.scene.spec_id) || normalizeString(job && job.source && job.source.spec_id) || null;
185
+ const taskRef = normalizeString(options.taskRef) || null;
186
+
187
+ const commands = normalizeTaskCommands([
188
+ ...(Array.isArray(stageMetadata.commands) ? stageMetadata.commands : []),
189
+ ...extractCommandsFromStageMetadata(stageMetadata)
190
+ ]);
191
+ const errors = normalizeTaskErrors(
192
+ extractErrorsFromStageMetadata(stageState, stageMetadata)
193
+ );
194
+ const fileChanges = collectTaskFileChanges(job, stageName, stageMetadata, dependencies);
195
+ const evidence = collectTaskEvidence(job, stageName, stageMetadata, dependencies);
196
+
197
+ const handoff = stageMetadata.handoff && typeof stageMetadata.handoff === 'object'
198
+ ? stageMetadata.handoff
199
+ : {
200
+ stage: stageName,
201
+ status: taskStatus,
202
+ completed_at: normalizeString(stageState && stageState.completed_at) || null,
203
+ report: normalizeString(stageMetadata.report) || null,
204
+ release_ref: normalizeString(stageMetadata.release_ref) || normalizeString(job && job.artifacts && job.artifacts.release_ref) || null
205
+ };
206
+
207
+ const normalizedHandoff = {
208
+ ...handoff,
209
+ task_ref: taskRef
210
+ };
211
+
212
+ return {
213
+ sessionId,
214
+ sceneId,
215
+ specId,
216
+ taskId,
217
+ taskRef,
218
+ eventId: normalizeString(latestEvent && latestEvent.event_id) || null,
219
+ task: {
220
+ ref: taskRef,
221
+ task_ref: taskRef,
222
+ title_norm: taskIntent.title_norm,
223
+ raw_request: taskIntent.raw_request,
224
+ goal,
225
+ sub_goals: taskIntent.sub_goals,
226
+ acceptance_criteria: buildTaskAcceptanceCriteria(stageName, job, nextAction),
227
+ needs_split: taskIntent.needs_split,
228
+ confidence: taskIntent.confidence,
229
+ status: taskStatus,
230
+ summary: buildTaskSummaryLines(job, stageName, taskStatus, nextAction, taskRef, dependencies.buildProgress),
231
+ handoff: normalizedHandoff,
232
+ next_action: nextAction,
233
+ file_changes: fileChanges,
234
+ commands,
235
+ errors,
236
+ evidence
237
+ },
238
+ event: events
239
+ };
240
+ }
241
+
242
+ async function buildCommandPayload(mode, job, options = {}, dependencies = {}) {
243
+ const taskRef = await resolveTaskReference(mode, job, options, dependencies);
244
+ const base = {
245
+ mode,
246
+ success: true,
247
+ job_id: job.job_id,
248
+ status: job.status,
249
+ progress: dependencies.buildProgress(job),
250
+ next_action: dependencies.resolveNextAction(job),
251
+ artifacts: { ...job.artifacts }
252
+ };
253
+ return dependencies.attachTaskFeedbackModel({
254
+ ...base,
255
+ ...buildTaskEnvelope(mode, job, {
256
+ ...options,
257
+ taskRef
258
+ }, dependencies)
259
+ });
260
+ }
261
+
262
+ module.exports = {
263
+ buildStudioTaskKey,
264
+ resolveTaskReference,
265
+ collectTaskFileChanges,
266
+ collectTaskEvidence,
267
+ buildTaskEnvelope,
268
+ buildCommandPayload
269
+ };
@@ -0,0 +1,149 @@
1
+ function normalizeString(value) {
2
+ if (typeof value !== 'string') {
3
+ return '';
4
+ }
5
+ return value.trim();
6
+ }
7
+
8
+ function truncateTaskText(value = '', maxLength = 96) {
9
+ const normalized = normalizeString(value).replace(/\s+/g, ' ');
10
+ if (!normalized) {
11
+ return '';
12
+ }
13
+ if (normalized.length <= maxLength) {
14
+ return normalized;
15
+ }
16
+ return normalized.slice(0, Math.max(0, maxLength - 3)).trim() + '...';
17
+ }
18
+
19
+ function dedupeTaskList(items = [], limit = 3) {
20
+ const seen = new Set();
21
+ const result = [];
22
+ for (const item of items) {
23
+ const normalized = truncateTaskText(item, 120);
24
+ if (!normalized) {
25
+ continue;
26
+ }
27
+ const key = normalized.toLowerCase();
28
+ if (seen.has(key)) {
29
+ continue;
30
+ }
31
+ seen.add(key);
32
+ result.push(normalized);
33
+ if (result.length >= limit) {
34
+ break;
35
+ }
36
+ }
37
+ return result;
38
+ }
39
+
40
+ function splitTaskRawRequest(rawRequest = '') {
41
+ const normalized = normalizeString(rawRequest).replace(/\s+/g, ' ');
42
+ if (!normalized) {
43
+ return [];
44
+ }
45
+ const chunks = normalized
46
+ .split(/(?:\r?\n|[;;。!?!?]|(?:\s+\band\b\s+)|(?:\s+\bthen\b\s+)|(?:\s+\balso\b\s+)|(?:\s*并且\s*)|(?:\s*同时\s*)|(?:\s*以及\s*)|(?:\s*然后\s*))/gi)
47
+ .map((item) => normalizeString(item).replace(/^(?:and|then|also)\s+/i, ''))
48
+ .filter(Boolean);
49
+ return dedupeTaskList(chunks, 3);
50
+ }
51
+
52
+ function deriveTaskIntentShape(rawRequest = '', stageName = '') {
53
+ const normalizedRaw = normalizeString(rawRequest).replace(/\s+/g, ' ');
54
+ const clauses = splitTaskRawRequest(normalizedRaw);
55
+ const hasRaw = normalizedRaw.length > 0;
56
+ const inferredSubGoals = clauses.length > 1 ? clauses.slice(0, 3) : [];
57
+ const needsSplit = inferredSubGoals.length > 1;
58
+ const titleSource = clauses.length > 0
59
+ ? clauses[0]
60
+ : (hasRaw ? normalizedRaw : 'Studio ' + (stageName || 'task') + ' execution');
61
+
62
+ let confidence = hasRaw ? 0.9 : 0.6;
63
+ if (needsSplit) {
64
+ confidence = 0.72;
65
+ }
66
+ if (normalizeString(stageName) && normalizeString(stageName) !== 'plan') {
67
+ confidence = Math.min(0.95, confidence + 0.03);
68
+ }
69
+
70
+ return {
71
+ title_norm: truncateTaskText(titleSource, 96) || ('Studio ' + (stageName || 'task') + ' execution'),
72
+ raw_request: hasRaw ? normalizedRaw : null,
73
+ sub_goals: inferredSubGoals,
74
+ needs_split: needsSplit,
75
+ confidence: Number(confidence.toFixed(2))
76
+ };
77
+ }
78
+
79
+ function buildTaskSummaryLines(job = {}, stageName = '', taskStatus = '', nextAction = '', taskRef = '', buildProgress) {
80
+ const sceneId = normalizeString(job && job.scene && job.scene.id) || 'scene.n/a';
81
+ const specId = normalizeString(job && job.scene && job.scene.spec_id) || normalizeString(job && job.source && job.source.spec_id) || 'spec.n/a';
82
+ const progress = typeof buildProgress === 'function' ? buildProgress(job) : { completed: 0, total: 0 };
83
+ return [
84
+ 'Stage: ' + (stageName || 'plan') + ' | Status: ' + (taskStatus || 'unknown') + (taskRef ? (' | Ref: ' + taskRef) : ''),
85
+ 'Scene: ' + sceneId + ' | Spec: ' + specId + ' | Progress: ' + progress.completed + '/' + progress.total,
86
+ 'Next: ' + (nextAction || 'n/a')
87
+ ];
88
+ }
89
+
90
+ function buildTaskAcceptanceCriteria(stageName = '', job = {}, nextAction = '') {
91
+ const normalizedStage = normalizeString(stageName) || 'task';
92
+ const artifacts = job && job.artifacts ? job.artifacts : {};
93
+ const criteriaByStage = {
94
+ plan: [
95
+ 'Scene/spec binding is resolved and persisted in studio job metadata.',
96
+ 'Plan stage problem evaluation passes with no blockers.',
97
+ 'Next action is executable (' + (nextAction || 'sce studio generate --job <job-id>') + ').'
98
+ ],
99
+ generate: [
100
+ 'Patch bundle id is produced for downstream apply stage.',
101
+ 'Generate stage report is written to artifacts.',
102
+ 'Next action is executable (' + (nextAction || 'sce studio apply --patch-bundle <id> --job <job-id>') + ').'
103
+ ],
104
+ apply: [
105
+ 'Authorization requirements are satisfied for apply stage.',
106
+ 'Apply stage completes without policy blockers.',
107
+ 'Next action is executable (' + (nextAction || 'sce studio verify --job <job-id>') + ').'
108
+ ],
109
+ verify: [
110
+ 'Verification gates finish with no required-step failures.',
111
+ 'Verify report is available (' + (normalizeString(artifacts.verify_report) || 'artifact pending') + ').',
112
+ 'Next action is executable (' + (nextAction || 'sce studio release --job <job-id>') + ').'
113
+ ],
114
+ release: [
115
+ 'Release gates pass under configured release profile.',
116
+ 'Release reference is emitted (' + (normalizeString(artifacts.release_ref) || 'artifact pending') + ').',
117
+ 'Next action is executable (' + (nextAction || 'complete') + ').'
118
+ ],
119
+ rollback: [
120
+ 'Rollback stage transitions job status to rolled_back.',
121
+ 'Rollback evidence is appended to studio event stream.',
122
+ 'Recovery next action is executable (' + (nextAction || 'sce studio plan --scene <scene-id> --from-chat <session>') + ').'
123
+ ],
124
+ events: [
125
+ 'Events stream payload is available for task-level audit.',
126
+ 'Task envelope preserves normalized IDs and handoff fields.',
127
+ 'Next action is explicit (' + (nextAction || 'n/a') + ').'
128
+ ],
129
+ resume: [
130
+ 'Current job status and stage progress are restored deterministically.',
131
+ 'Task envelope remains schema-compatible for downstream UI.',
132
+ 'Next action is explicit (' + (nextAction || 'n/a') + ').'
133
+ ]
134
+ };
135
+ return criteriaByStage[normalizedStage] || [
136
+ 'Task envelope contains normalized identifiers and task contract fields.',
137
+ 'Task output preserves evidence, command logs, and error bundles.',
138
+ 'Next action is explicit (' + (nextAction || 'n/a') + ').'
139
+ ];
140
+ }
141
+
142
+ module.exports = {
143
+ deriveTaskIntentShape,
144
+ buildTaskSummaryLines,
145
+ buildTaskAcceptanceCriteria,
146
+ truncateTaskText,
147
+ dedupeTaskList,
148
+ splitTaskRawRequest
149
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "scene-capability-engine",
3
- "version": "3.6.28",
3
+ "version": "3.6.32",
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": {