@work-graph/cli 0.2.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/README.md +31 -0
- package/bin/work-graph.mjs +238 -0
- package/package.json +38 -0
- package/vendor/packages/design-tokens/generated/gripe-dark-default.css +67 -0
- package/vendor/packages/design-tokens/generated/marketplace-default.css +67 -0
- package/vendor/packages/design-tokens/generated/workgraph-dark.css +67 -0
- package/vendor/packages/workgraph-mcp/README.md +28 -0
- package/vendor/packages/workgraph-mcp/bin/workgraph-mcp.mjs +21 -0
- package/vendor/packages/workgraph-mcp/package.json +37 -0
- package/vendor/packages/workgraph-mcp/src/handlers.mjs +761 -0
- package/vendor/packages/workgraph-mcp/src/index.mjs +638 -0
- package/vendor/packages/workgraph-mcp/src/prompts.mjs +162 -0
- package/vendor/public/assets/workgraph-logo.svg +11 -0
- package/vendor/public/fonts/GraphikLCG/GraphikLCG-Medium.woff2 +0 -0
- package/vendor/public/fonts/GraphikLCG/GraphikLCG-Regular.woff2 +0 -0
- package/vendor/public/fonts/GraphikLCG/GraphikLCG-Semibold.woff2 +0 -0
- package/vendor/public/fonts/GraphikLCG/stylesheet.css +25 -0
- package/vendor/public/graph-canvas-lit-flow.css +154 -0
- package/vendor/public/graph-canvas-lit-flow.css.map +7 -0
- package/vendor/public/graph-canvas-lit-flow.js +8530 -0
- package/vendor/public/graph-canvas-lit-flow.js.map +7 -0
- package/vendor/src/agentBehaviorRulesAudit.mjs +168 -0
- package/vendor/src/agentBehaviorRulesBundle.mjs +144 -0
- package/vendor/src/agentRunApi.mjs +136 -0
- package/vendor/src/agentToolLoopGuard.mjs +88 -0
- package/vendor/src/agentWorkerClaudeProvider.mjs +288 -0
- package/vendor/src/agentWorkerCursorSdkProvider.mjs +156 -0
- package/vendor/src/agentWorkerLiveLoop.mjs +455 -0
- package/vendor/src/agentWorkerLocalCliProvider.mjs +217 -0
- package/vendor/src/agentWorkerLocalRunner.mjs +246 -0
- package/vendor/src/agentWorkerOpenAiProvider.mjs +459 -0
- package/vendor/src/analyticsPanelProjection.mjs +212 -0
- package/vendor/src/analyticsRecordStore.mjs +165 -0
- package/vendor/src/analyticsRecordWorkItems.mjs +104 -0
- package/vendor/src/architectureL1Canon.mjs +419 -0
- package/vendor/src/architectureLayout.mjs +229 -0
- package/vendor/src/architectureSnapshot.mjs +490 -0
- package/vendor/src/architectureViewsProjection.mjs +116 -0
- package/vendor/src/atomInspector.mjs +253 -0
- package/vendor/src/atomInspectorApi.mjs +130 -0
- package/vendor/src/auditGapMatrixRefresh.mjs +121 -0
- package/vendor/src/backlogSchemaLint.mjs +176 -0
- package/vendor/src/blockedOnebaseGoPreflightEval.mjs +100 -0
- package/vendor/src/bracketIrTraceSignal.mjs +93 -0
- package/vendor/src/bvcAtomParser.mjs +210 -0
- package/vendor/src/bvcDialectRegistry.mjs +86 -0
- package/vendor/src/bvcFileFormat.mjs +218 -0
- package/vendor/src/bvcFormatCli.mjs +55 -0
- package/vendor/src/bvcLintCli.mjs +48 -0
- package/vendor/src/bvcNewWritePolicy.mjs +70 -0
- package/vendor/src/charterPreflightPromoteGate.mjs +194 -0
- package/vendor/src/claimNoEligibleEval.mjs +205 -0
- package/vendor/src/closingAnalysisSuggest.mjs +59 -0
- package/vendor/src/codeGapAnalyzer.mjs +308 -0
- package/vendor/src/codeGapBacklogFeeder.mjs +82 -0
- package/vendor/src/codeGapDraftIntakeApi.mjs +307 -0
- package/vendor/src/codeGapOperatorProjection.mjs +60 -0
- package/vendor/src/codeSyntaxHighlight.mjs +123 -0
- package/vendor/src/codegenEvidence.mjs +187 -0
- package/vendor/src/compilerRoundTripCli.mjs +164 -0
- package/vendor/src/dagreGraphLayout.mjs +78 -0
- package/vendor/src/draftIntakePromotionRules.mjs +205 -0
- package/vendor/src/epicWorkScope.mjs +85 -0
- package/vendor/src/evalLiveLlmEnv.mjs +63 -0
- package/vendor/src/evidenceReadModel.mjs +167 -0
- package/vendor/src/gfsOverlayProjectPassport.mjs +235 -0
- package/vendor/src/globalStepPathToBvcReferences.mjs +196 -0
- package/vendor/src/goldenPath.mjs +69 -0
- package/vendor/src/graphCanvasLayout.mjs +464 -0
- package/vendor/src/graphCanvasLitFlow/client/graphCanvasMinimap.ts +261 -0
- package/vendor/src/graphCanvasLitFlow/client/graphCanvasSvgEdges.ts +259 -0
- package/vendor/src/graphCanvasLitFlow/client/graphCanvasTheme.css +152 -0
- package/vendor/src/graphCanvasLitFlow/client/graphCardNode.ts +328 -0
- package/vendor/src/graphCanvasLitFlow/client/mountGraphCanvasLitFlow.ts +322 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeLabels.mjs +58 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeRouter.mjs +142 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasLayoutProfile.mjs +32 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasNodeMetrics.mjs +45 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasProjection.mjs +115 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasProjectionToFlow.mjs +133 -0
- package/vendor/src/graphCanvasLitFlow/graphCanvasTraversal.mjs +77 -0
- package/vendor/src/graphCanvasLitFlow/layoutIntentRoadmapWorkStack.mjs +73 -0
- package/vendor/src/graphCanvasLitFlow/resolveGraphCanvasOverlaps.mjs +77 -0
- package/vendor/src/graphRagContextSlice.mjs +461 -0
- package/vendor/src/gvmVerifyWorkerGate.mjs +95 -0
- package/vendor/src/homeSnapshotApi.mjs +131 -0
- package/vendor/src/homeSnapshotProjection.mjs +275 -0
- package/vendor/src/inboxEventStream.mjs +140 -0
- package/vendor/src/intentComposerApi.mjs +245 -0
- package/vendor/src/intentGraphGbcSliceBoundary.mjs +258 -0
- package/vendor/src/intentGraphProjection.mjs +208 -0
- package/vendor/src/intentHierarchy.mjs +241 -0
- package/vendor/src/intentNodeLint.mjs +107 -0
- package/vendor/src/intentNodeRuntime.mjs +185 -0
- package/vendor/src/intentRoadmapCanvas.mjs +393 -0
- package/vendor/src/intentRoadmapEpicProjection.mjs +122 -0
- package/vendor/src/intentRoadmapMermaid.mjs +165 -0
- package/vendor/src/intentRoadmapProjection.mjs +85 -0
- package/vendor/src/intentTreeLint.mjs +114 -0
- package/vendor/src/intentTreeMigration.mjs +150 -0
- package/vendor/src/intentTreeWorkItems.mjs +227 -0
- package/vendor/src/kanbanBoardProjection.mjs +58 -0
- package/vendor/src/languageAdapterRegistry.mjs +180 -0
- package/vendor/src/languageAdapters/goAdapter.mjs +62 -0
- package/vendor/src/languageAdapters/jsTsAdapter.mjs +60 -0
- package/vendor/src/languageAdapters/jsonYamlAdapter.mjs +103 -0
- package/vendor/src/languageAdapters/onebaseOsAdapter.mjs +55 -0
- package/vendor/src/languageAdapters/plaintextAdapter.mjs +36 -0
- package/vendor/src/languageAdapters/shared.mjs +68 -0
- package/vendor/src/languageAdapters/stepAdapter.mjs +81 -0
- package/vendor/src/lintPlanWorkAlignment.mjs +136 -0
- package/vendor/src/loopHintRepeatToolEval.mjs +153 -0
- package/vendor/src/lowcodeScaffoldCli.mjs +386 -0
- package/vendor/src/markdownDocumentRender.mjs +208 -0
- package/vendor/src/memoryPanelProjection.mjs +116 -0
- package/vendor/src/memoryRecordWriter.mjs +243 -0
- package/vendor/src/memoryWorkerSlice.mjs +238 -0
- package/vendor/src/migrateStepToBvc.mjs +133 -0
- package/vendor/src/missionControlServerHandlers.mjs +195 -0
- package/vendor/src/missionControlUiClient.mjs +278 -0
- package/vendor/src/onebaseCliCapabilityProbe.mjs +107 -0
- package/vendor/src/onebaseCliRunner.mjs +145 -0
- package/vendor/src/onebaseGrossProfitStaticVerify.mjs +98 -0
- package/vendor/src/onebaseParityEvidenceSync.mjs +88 -0
- package/vendor/src/onebasePvrgGraphNodes.mjs +257 -0
- package/vendor/src/onebaseRestEvidenceAdapter.mjs +216 -0
- package/vendor/src/onebaseVectorDslCodegenReadiness.mjs +137 -0
- package/vendor/src/onebaseWorkItemTemplate.mjs +154 -0
- package/vendor/src/onebaseWorkerTools.mjs +586 -0
- package/vendor/src/operatorShellProjection.mjs +102 -0
- package/vendor/src/pipelineProseRender.mjs +180 -0
- package/vendor/src/pipelineStageLint.mjs +118 -0
- package/vendor/src/promptRulesEditorApi.mjs +174 -0
- package/vendor/src/promptRulesProjection.mjs +134 -0
- package/vendor/src/pvrg/bladeAdapter.mjs +40 -0
- package/vendor/src/pvrgTaskScope.mjs +152 -0
- package/vendor/src/releaseGateMatrix.mjs +188 -0
- package/vendor/src/schematicView.mjs +305 -0
- package/vendor/src/seedAnalyticsRecord.mjs +217 -0
- package/vendor/src/semanticSearchBm25.mjs +103 -0
- package/vendor/src/semanticSearchExcerpts.mjs +68 -0
- package/vendor/src/semanticSearchTfidfVector.mjs +86 -0
- package/vendor/src/semanticSearchWorkflow.mjs +366 -0
- package/vendor/src/stepAtomFormatter.mjs +413 -0
- package/vendor/src/stepGraphSlice.mjs +318 -0
- package/vendor/src/ui/atoms/badge.mjs +40 -0
- package/vendor/src/ui/atoms/badgeClient.mjs +32 -0
- package/vendor/src/ui/atoms/button.mjs +114 -0
- package/vendor/src/ui/atoms/buttonClient.mjs +49 -0
- package/vendor/src/ui/atoms/icon.mjs +23 -0
- package/vendor/src/ui/atoms/input.mjs +38 -0
- package/vendor/src/ui/atoms/modal.mjs +44 -0
- package/vendor/src/ui/atoms/select.mjs +98 -0
- package/vendor/src/ui/backlogShellButtons.mjs +238 -0
- package/vendor/src/ui/htmlEscape.mjs +11 -0
- package/vendor/src/ui/molecules/rating.mjs +48 -0
- package/vendor/src/ui/molecules/tabs.mjs +70 -0
- package/vendor/src/ui/organisms/modal.mjs +1 -0
- package/vendor/src/ui/pages/uiKitPage.mjs +147 -0
- package/vendor/src/ui/workItemStatusTone.mjs +36 -0
- package/vendor/src/unifiedLinkageProjection.mjs +264 -0
- package/vendor/src/verificationLoop.mjs +206 -0
- package/vendor/src/workGraphBacklogPersist.mjs +234 -0
- package/vendor/src/workGraphBacklogUiServer.mjs +9192 -0
- package/vendor/src/workGraphBoundedTargetFileRead.mjs +178 -0
- package/vendor/src/workGraphCycleSlice.mjs +184 -0
- package/vendor/src/workGraphDaemonTick.mjs +307 -0
- package/vendor/src/workGraphDaemonWatch.mjs +157 -0
- package/vendor/src/workGraphEngineRoot.mjs +136 -0
- package/vendor/src/workGraphInstallLayout.mjs +65 -0
- package/vendor/src/workGraphLlmUsefulnessEval.mjs +611 -0
- package/vendor/src/workGraphPhasePromoteReadyQueue.mjs +159 -0
- package/vendor/src/workGraphProjectHost.mjs +149 -0
- package/vendor/src/workGraphProjectInit.mjs +392 -0
- package/vendor/src/workGraphPromoteReadyApi.mjs +115 -0
- package/vendor/src/workGraphRecoveryPolicy.mjs +124 -0
- package/vendor/src/workGraphRunnerQueueProjection.mjs +187 -0
- package/vendor/src/workGraphRuntime.mjs +1008 -0
- package/vendor/src/workGraphToolSurfaceAudit.mjs +372 -0
- package/vendor/src/workGraphToolTransportRuntime.mjs +195 -0
- package/vendor/src/workGraphWorkerProvider.mjs +600 -0
- package/vendor/src/workItemBvcQuality.mjs +262 -0
- package/vendor/src/workItemCreateAnalysis.mjs +157 -0
- package/vendor/src/workItemDecisionPipeline.mjs +278 -0
- package/vendor/src/workItemEpicCascade.mjs +176 -0
- package/vendor/src/workItemExecutionGate.mjs +78 -0
- package/vendor/src/workItemHierarchy.mjs +226 -0
- package/vendor/src/workItemProseLint.mjs +133 -0
- package/vendor/src/workItemTextRusify.mjs +794 -0
- package/vendor/src/workItemTraceEnvelope.mjs +158 -0
- package/vendor/src/workItemUiReferences.mjs +272 -0
- package/vendor/src/workflowEpicGrouping.mjs +67 -0
- package/vendor/src/workflowTreeProjection.mjs +53 -0
- package/vendor/src/workspaceRegistry.mjs +150 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildSectionTitleToFieldMap,
|
|
3
|
+
detectDialectFromBvcSectionTitle,
|
|
4
|
+
getFieldSectionsForDialect,
|
|
5
|
+
normalizeDialectId,
|
|
6
|
+
parseBvcFilePragma,
|
|
7
|
+
REGISTERED_DIALECT_IDS,
|
|
8
|
+
} from './bvcDialectRegistry.mjs';
|
|
9
|
+
import {
|
|
10
|
+
buildBvcAtomAst,
|
|
11
|
+
lintBvcAtomDialect,
|
|
12
|
+
resolveAtomLang,
|
|
13
|
+
scanBvcSectionDialects,
|
|
14
|
+
stripBvcFilePragmaLine,
|
|
15
|
+
} from './bvcAtomParser.mjs';
|
|
16
|
+
|
|
17
|
+
const ALLOWED_PROFILES = new Set([
|
|
18
|
+
'charter',
|
|
19
|
+
'charter_section',
|
|
20
|
+
'work_item',
|
|
21
|
+
'plan',
|
|
22
|
+
'prompt_rule',
|
|
23
|
+
'compiler',
|
|
24
|
+
'trace',
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
const ATOM_NAME_PATTERN = /^[A-Za-zА-Яа-яЁё0-9_]+$/u;
|
|
28
|
+
const LABEL_KEY_PATTERN = /^[A-Za-zА-Яа-яЁё0-9_.-]+$/u;
|
|
29
|
+
|
|
30
|
+
const SECTION_BY_TITLE = buildSectionTitleToFieldMap();
|
|
31
|
+
|
|
32
|
+
const STEP_ATOM_PATTERN = /^#([^@\n<]+?)(?:@([a-z]{2}))?<\[\n([\s\S]*?)\n\]>/gmu;
|
|
33
|
+
|
|
34
|
+
const LEGACY_MACHINE_FIELDS = new Map([
|
|
35
|
+
['guid', 'guid'],
|
|
36
|
+
['id', 'atom.id'],
|
|
37
|
+
['mode', 'atom.mode'],
|
|
38
|
+
['статус', 'work.status'],
|
|
39
|
+
['status', 'work.status'],
|
|
40
|
+
['trace_status', 'trace.status'],
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
export class StepAtomDraftValidationError extends Error {
|
|
44
|
+
constructor(errors) {
|
|
45
|
+
super(`Invalid StepAtomDraft: ${errors.join('; ')}`);
|
|
46
|
+
this.name = 'StepAtomDraftValidationError';
|
|
47
|
+
this.errors = errors;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function validateStepAtomDraft(draft) {
|
|
52
|
+
const errors = [];
|
|
53
|
+
|
|
54
|
+
if (!isPlainObject(draft)) {
|
|
55
|
+
return ['draft must be an object'];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
validateProfile(draft.profile, errors);
|
|
59
|
+
validateName(draft.name, errors);
|
|
60
|
+
validateTextArray(draft.basis, 'basis', errors, { required: true });
|
|
61
|
+
validateTextArray(draft.vector, 'vector', errors, { required: true });
|
|
62
|
+
validateTextArray(draft.goal, 'goal', errors, { required: true });
|
|
63
|
+
validateTextArray(draft.checks, 'checks', errors, { required: false });
|
|
64
|
+
validateTextArray(draft.evidence, 'evidence', errors, { required: false });
|
|
65
|
+
validateLabels(draft.labels, errors);
|
|
66
|
+
validateLang(draft.lang, errors);
|
|
67
|
+
|
|
68
|
+
return errors;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function resolveDraftLang(draft) {
|
|
72
|
+
const fromField = typeof draft.lang === 'string' ? draft.lang.trim() : '';
|
|
73
|
+
if (fromField !== '') {
|
|
74
|
+
return normalizeDialectId(fromField);
|
|
75
|
+
}
|
|
76
|
+
const fromLabels = typeof draft.labels?.lang === 'string' ? draft.labels.lang.trim() : '';
|
|
77
|
+
if (fromLabels !== '') {
|
|
78
|
+
return normalizeDialectId(fromLabels);
|
|
79
|
+
}
|
|
80
|
+
return 'ru';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function validateLang(lang, errors) {
|
|
84
|
+
if (lang === undefined) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (typeof lang !== 'string' || lang.trim() === '') {
|
|
88
|
+
errors.push('lang must be a non-empty string when provided');
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const id = lang.trim().toLowerCase();
|
|
92
|
+
if (!REGISTERED_DIALECT_IDS.includes(id)) {
|
|
93
|
+
errors.push(`lang must be one of: ${REGISTERED_DIALECT_IDS.join(', ')}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function formatStepAtomDraft(draft) {
|
|
98
|
+
const errors = validateStepAtomDraft(draft);
|
|
99
|
+
if (errors.length > 0) {
|
|
100
|
+
throw new StepAtomDraftValidationError(errors);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const lang = resolveDraftLang(draft);
|
|
104
|
+
const headerSuffix = lang === 'en' ? '@en' : '';
|
|
105
|
+
const fieldSections = getFieldSectionsForDialect(lang);
|
|
106
|
+
const lines = [`#${draft.name.trim()}${headerSuffix}<[`];
|
|
107
|
+
|
|
108
|
+
for (const [field, title] of fieldSections) {
|
|
109
|
+
if (field === 'labels') {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (field === 'checks' || field === 'evidence' || field === 'analysis' || field === 'decision' || field === 'uiRefs') {
|
|
113
|
+
if (draft[field] === undefined || (Array.isArray(draft[field]) && draft[field].length === 0)) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
lines.push(`${title}:`);
|
|
119
|
+
for (const item of draft[field]) {
|
|
120
|
+
lines.push(` ${item.trim()}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
lines.push('');
|
|
125
|
+
lines.push(`${getFieldSectionsForDialect(lang).find(([field]) => field === 'labels')[1]}:`);
|
|
126
|
+
|
|
127
|
+
const labels = {
|
|
128
|
+
...(draft.labels ?? {}),
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
if (!Object.prototype.hasOwnProperty.call(labels, 'atom.profile')) {
|
|
132
|
+
labels['atom.profile'] = draft.profile;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
for (const key of Object.keys(labels).sort(compareLabelKeys)) {
|
|
136
|
+
lines.push(` ${key}: ${labels[key].trim()}`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
lines.push(']>');
|
|
140
|
+
return `${lines.join('\n')}\n`;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function parseStepAtomDrafts(text, options = {}) {
|
|
144
|
+
if (typeof text !== 'string') {
|
|
145
|
+
throw new TypeError('text must be a string');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const filePragmaLang = options.filePragmaLang ?? parseBvcFilePragma(text);
|
|
149
|
+
const searchText = options.stripFilePragma === false ? text : stripBvcFilePragmaLine(text);
|
|
150
|
+
|
|
151
|
+
return [...searchText.matchAll(STEP_ATOM_PATTERN)].map((match) =>
|
|
152
|
+
parseStepAtomMatch(match, { filePragmaLang }),
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function parseBvcAtoms(text, options = {}) {
|
|
157
|
+
const parsed = parseStepAtomDrafts(text, options);
|
|
158
|
+
return parsed.map((entry) => entry.ast).filter(Boolean);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function isInlineSectionHeading(line) {
|
|
162
|
+
return /^[A-Za-zА-Яа-яЁё0-9][^:\n]{0,80}:$/u.test(line)
|
|
163
|
+
|| /^[A-Za-zА-Яа-яЁё0-9][^:\n]{0,40}\s\/\s[^:\n]{0,40}:$/u.test(line);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function parseStepAtomMatch(match, context = {}) {
|
|
167
|
+
const [, rawHeader, headerLangToken, body] = match;
|
|
168
|
+
const atomName = rawHeader.trim();
|
|
169
|
+
const headerLang = headerLangToken ? normalizeDialectId(headerLangToken) : null;
|
|
170
|
+
const draft = {
|
|
171
|
+
profile: '',
|
|
172
|
+
name: atomName.trim(),
|
|
173
|
+
basis: [],
|
|
174
|
+
vector: [],
|
|
175
|
+
goal: [],
|
|
176
|
+
labels: {},
|
|
177
|
+
lang: undefined,
|
|
178
|
+
};
|
|
179
|
+
const warnings = [];
|
|
180
|
+
let section = '';
|
|
181
|
+
const { firstBvcDialect } = scanBvcSectionDialects(body);
|
|
182
|
+
let labelsLangDeclared = null;
|
|
183
|
+
|
|
184
|
+
for (const rawLine of body.split(/\r?\n/u)) {
|
|
185
|
+
const line = rawLine.trim();
|
|
186
|
+
if (line === '') {
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const title = line.endsWith(':') ? line.slice(0, -1) : '';
|
|
191
|
+
if (SECTION_BY_TITLE.has(title)) {
|
|
192
|
+
const nextField = SECTION_BY_TITLE.get(title);
|
|
193
|
+
const labelsTitleRu = getFieldSectionsForDialect('ru').find(([f]) => f === 'labels')[1];
|
|
194
|
+
const labelsTitleEn = getFieldSectionsForDialect('en').find(([f]) => f === 'labels')[1];
|
|
195
|
+
const isLabelsTitle = title === labelsTitleRu || title === labelsTitleEn;
|
|
196
|
+
|
|
197
|
+
if (section === 'analysis' && nextField === 'decision') {
|
|
198
|
+
section = 'decision';
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if ((section === 'analysis' || section === 'decision') && !isLabelsTitle) {
|
|
203
|
+
if (draft[section] === undefined) {
|
|
204
|
+
draft[section] = [];
|
|
205
|
+
}
|
|
206
|
+
draft[section].push(stripListMarker(line));
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
section = nextField;
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (line === 'критерии_готовности:') {
|
|
214
|
+
section = 'checks';
|
|
215
|
+
warnings.push('legacy section критерии_готовности imported as checks');
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const labelsTitleRu = getFieldSectionsForDialect('ru').find(([f]) => f === 'labels')[1];
|
|
220
|
+
const labelsTitleEn = getFieldSectionsForDialect('en').find(([f]) => f === 'labels')[1];
|
|
221
|
+
if (line === `${labelsTitleRu}:` || line === `${labelsTitleEn}:`) {
|
|
222
|
+
section = 'labels';
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (section === 'basis' || section === 'vector' || section === 'goal') {
|
|
227
|
+
if (isInlineSectionHeading(line)) {
|
|
228
|
+
section = '';
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
draft[section].push(stripListMarker(line));
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (section === 'checks' || section === 'evidence' || section === 'analysis' || section === 'decision' || section === 'uiRefs') {
|
|
236
|
+
if (draft[section] === undefined) {
|
|
237
|
+
draft[section] = [];
|
|
238
|
+
}
|
|
239
|
+
draft[section].push(stripListMarker(line));
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (section === 'labels') {
|
|
244
|
+
const separatorIndex = line.indexOf(':');
|
|
245
|
+
if (separatorIndex === -1) {
|
|
246
|
+
warnings.push(`ignored label line without separator: ${line}`);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const key = line.slice(0, separatorIndex).trim();
|
|
251
|
+
const value = line.slice(separatorIndex + 1).trim();
|
|
252
|
+
if (key === 'lang') {
|
|
253
|
+
labelsLangDeclared = value;
|
|
254
|
+
}
|
|
255
|
+
draft.labels[key] = value;
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const legacyField = parseLegacyMachineField(line);
|
|
260
|
+
if (section === '' && legacyField !== null) {
|
|
261
|
+
const { labelKey, value, sourceKey } = legacyField;
|
|
262
|
+
if (!Object.prototype.hasOwnProperty.call(draft.labels, labelKey)) {
|
|
263
|
+
draft.labels[labelKey] = value;
|
|
264
|
+
}
|
|
265
|
+
warnings.push(`legacy top-level field ${sourceKey} imported as labels.${labelKey}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
draft.profile = draft.labels['atom.profile'] ?? '';
|
|
270
|
+
|
|
271
|
+
const labelsLang = labelsLangDeclared ?? draft.labels.lang ?? null;
|
|
272
|
+
if (draft.labels.lang) {
|
|
273
|
+
delete draft.labels.lang;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const resolved = resolveAtomLang({
|
|
277
|
+
headerLang,
|
|
278
|
+
labelsLang,
|
|
279
|
+
filePragmaLang: context.filePragmaLang ?? null,
|
|
280
|
+
autoDetectLang: firstBvcDialect,
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
draft.lang = resolved.lang;
|
|
284
|
+
for (const code of resolved.warnings) {
|
|
285
|
+
warnings.push(code);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const dialectLints = lintBvcAtomDialect(body, resolved.lang);
|
|
289
|
+
const ast = buildBvcAtomAst(draft, resolved.lang, dialectLints);
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
draft,
|
|
293
|
+
ast,
|
|
294
|
+
langSource: resolved.source,
|
|
295
|
+
warnings,
|
|
296
|
+
lints: dialectLints,
|
|
297
|
+
errors: validateStepAtomDraft(draft),
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function parseLegacyMachineField(line) {
|
|
302
|
+
const separatorIndex = line.indexOf(':');
|
|
303
|
+
if (separatorIndex === -1) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const sourceKey = line.slice(0, separatorIndex).trim();
|
|
308
|
+
const value = line.slice(separatorIndex + 1).trim();
|
|
309
|
+
const labelKey = LEGACY_MACHINE_FIELDS.get(sourceKey);
|
|
310
|
+
|
|
311
|
+
if (labelKey === undefined || value === '') {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return { labelKey, value, sourceKey };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function validateProfile(profile, errors) {
|
|
319
|
+
if (typeof profile !== 'string' || profile.trim() === '') {
|
|
320
|
+
errors.push('profile is required and must be a non-empty string');
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (profile !== profile.trim()) {
|
|
325
|
+
errors.push('profile must not have leading or trailing whitespace');
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (!ALLOWED_PROFILES.has(profile)) {
|
|
330
|
+
errors.push(`profile must be one of: ${Array.from(ALLOWED_PROFILES).join(', ')}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function validateName(name, errors) {
|
|
335
|
+
if (typeof name !== 'string' || name.trim() === '') {
|
|
336
|
+
errors.push('name is required and must be a non-empty string');
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const trimmed = name.trim();
|
|
341
|
+
if (!ATOM_NAME_PATTERN.test(trimmed)) {
|
|
342
|
+
errors.push('name may contain only letters, digits, and underscore');
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function validateTextArray(value, fieldName, errors, options) {
|
|
347
|
+
if (value === undefined && !options.required) {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (!Array.isArray(value)) {
|
|
352
|
+
errors.push(`${fieldName} is required and must be a non-empty array of strings`);
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (value.length === 0) {
|
|
357
|
+
errors.push(`${fieldName} must contain at least one string`);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
value.forEach((item, index) => {
|
|
362
|
+
if (typeof item !== 'string' || item.trim() === '') {
|
|
363
|
+
errors.push(`${fieldName}[${index}] must be a non-empty string`);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (hasLineBreak(item)) {
|
|
368
|
+
errors.push(`${fieldName}[${index}] must be a single-line string`);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function validateLabels(labels, errors) {
|
|
374
|
+
if (labels === undefined) {
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (!isPlainObject(labels)) {
|
|
379
|
+
errors.push('labels must be an object with string values');
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
for (const [key, value] of Object.entries(labels)) {
|
|
384
|
+
if (!LABEL_KEY_PATTERN.test(key)) {
|
|
385
|
+
errors.push(`labels.${key} has an invalid key; use letters, digits, underscore, dot, or dash`);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (typeof value !== 'string' || value.trim() === '') {
|
|
389
|
+
errors.push(`labels.${key} must be a non-empty string`);
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
if (hasLineBreak(value)) {
|
|
394
|
+
errors.push(`labels.${key} must be a single-line string`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function compareLabelKeys(left, right) {
|
|
400
|
+
return left.localeCompare(right, 'en', { sensitivity: 'variant' });
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function hasLineBreak(value) {
|
|
404
|
+
return value.includes('\n') || value.includes('\r');
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function stripListMarker(line) {
|
|
408
|
+
return line.replace(/^-\s*/u, '').trim();
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function isPlainObject(value) {
|
|
412
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
413
|
+
}
|