@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.
Files changed (194) hide show
  1. package/README.md +31 -0
  2. package/bin/work-graph.mjs +238 -0
  3. package/package.json +38 -0
  4. package/vendor/packages/design-tokens/generated/gripe-dark-default.css +67 -0
  5. package/vendor/packages/design-tokens/generated/marketplace-default.css +67 -0
  6. package/vendor/packages/design-tokens/generated/workgraph-dark.css +67 -0
  7. package/vendor/packages/workgraph-mcp/README.md +28 -0
  8. package/vendor/packages/workgraph-mcp/bin/workgraph-mcp.mjs +21 -0
  9. package/vendor/packages/workgraph-mcp/package.json +37 -0
  10. package/vendor/packages/workgraph-mcp/src/handlers.mjs +761 -0
  11. package/vendor/packages/workgraph-mcp/src/index.mjs +638 -0
  12. package/vendor/packages/workgraph-mcp/src/prompts.mjs +162 -0
  13. package/vendor/public/assets/workgraph-logo.svg +11 -0
  14. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Medium.woff2 +0 -0
  15. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Regular.woff2 +0 -0
  16. package/vendor/public/fonts/GraphikLCG/GraphikLCG-Semibold.woff2 +0 -0
  17. package/vendor/public/fonts/GraphikLCG/stylesheet.css +25 -0
  18. package/vendor/public/graph-canvas-lit-flow.css +154 -0
  19. package/vendor/public/graph-canvas-lit-flow.css.map +7 -0
  20. package/vendor/public/graph-canvas-lit-flow.js +8530 -0
  21. package/vendor/public/graph-canvas-lit-flow.js.map +7 -0
  22. package/vendor/src/agentBehaviorRulesAudit.mjs +168 -0
  23. package/vendor/src/agentBehaviorRulesBundle.mjs +144 -0
  24. package/vendor/src/agentRunApi.mjs +136 -0
  25. package/vendor/src/agentToolLoopGuard.mjs +88 -0
  26. package/vendor/src/agentWorkerClaudeProvider.mjs +288 -0
  27. package/vendor/src/agentWorkerCursorSdkProvider.mjs +156 -0
  28. package/vendor/src/agentWorkerLiveLoop.mjs +455 -0
  29. package/vendor/src/agentWorkerLocalCliProvider.mjs +217 -0
  30. package/vendor/src/agentWorkerLocalRunner.mjs +246 -0
  31. package/vendor/src/agentWorkerOpenAiProvider.mjs +459 -0
  32. package/vendor/src/analyticsPanelProjection.mjs +212 -0
  33. package/vendor/src/analyticsRecordStore.mjs +165 -0
  34. package/vendor/src/analyticsRecordWorkItems.mjs +104 -0
  35. package/vendor/src/architectureL1Canon.mjs +419 -0
  36. package/vendor/src/architectureLayout.mjs +229 -0
  37. package/vendor/src/architectureSnapshot.mjs +490 -0
  38. package/vendor/src/architectureViewsProjection.mjs +116 -0
  39. package/vendor/src/atomInspector.mjs +253 -0
  40. package/vendor/src/atomInspectorApi.mjs +130 -0
  41. package/vendor/src/auditGapMatrixRefresh.mjs +121 -0
  42. package/vendor/src/backlogSchemaLint.mjs +176 -0
  43. package/vendor/src/blockedOnebaseGoPreflightEval.mjs +100 -0
  44. package/vendor/src/bracketIrTraceSignal.mjs +93 -0
  45. package/vendor/src/bvcAtomParser.mjs +210 -0
  46. package/vendor/src/bvcDialectRegistry.mjs +86 -0
  47. package/vendor/src/bvcFileFormat.mjs +218 -0
  48. package/vendor/src/bvcFormatCli.mjs +55 -0
  49. package/vendor/src/bvcLintCli.mjs +48 -0
  50. package/vendor/src/bvcNewWritePolicy.mjs +70 -0
  51. package/vendor/src/charterPreflightPromoteGate.mjs +194 -0
  52. package/vendor/src/claimNoEligibleEval.mjs +205 -0
  53. package/vendor/src/closingAnalysisSuggest.mjs +59 -0
  54. package/vendor/src/codeGapAnalyzer.mjs +308 -0
  55. package/vendor/src/codeGapBacklogFeeder.mjs +82 -0
  56. package/vendor/src/codeGapDraftIntakeApi.mjs +307 -0
  57. package/vendor/src/codeGapOperatorProjection.mjs +60 -0
  58. package/vendor/src/codeSyntaxHighlight.mjs +123 -0
  59. package/vendor/src/codegenEvidence.mjs +187 -0
  60. package/vendor/src/compilerRoundTripCli.mjs +164 -0
  61. package/vendor/src/dagreGraphLayout.mjs +78 -0
  62. package/vendor/src/draftIntakePromotionRules.mjs +205 -0
  63. package/vendor/src/epicWorkScope.mjs +85 -0
  64. package/vendor/src/evalLiveLlmEnv.mjs +63 -0
  65. package/vendor/src/evidenceReadModel.mjs +167 -0
  66. package/vendor/src/gfsOverlayProjectPassport.mjs +235 -0
  67. package/vendor/src/globalStepPathToBvcReferences.mjs +196 -0
  68. package/vendor/src/goldenPath.mjs +69 -0
  69. package/vendor/src/graphCanvasLayout.mjs +464 -0
  70. package/vendor/src/graphCanvasLitFlow/client/graphCanvasMinimap.ts +261 -0
  71. package/vendor/src/graphCanvasLitFlow/client/graphCanvasSvgEdges.ts +259 -0
  72. package/vendor/src/graphCanvasLitFlow/client/graphCanvasTheme.css +152 -0
  73. package/vendor/src/graphCanvasLitFlow/client/graphCardNode.ts +328 -0
  74. package/vendor/src/graphCanvasLitFlow/client/mountGraphCanvasLitFlow.ts +322 -0
  75. package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeLabels.mjs +58 -0
  76. package/vendor/src/graphCanvasLitFlow/graphCanvasEdgeRouter.mjs +142 -0
  77. package/vendor/src/graphCanvasLitFlow/graphCanvasLayoutProfile.mjs +32 -0
  78. package/vendor/src/graphCanvasLitFlow/graphCanvasNodeMetrics.mjs +45 -0
  79. package/vendor/src/graphCanvasLitFlow/graphCanvasProjection.mjs +115 -0
  80. package/vendor/src/graphCanvasLitFlow/graphCanvasProjectionToFlow.mjs +133 -0
  81. package/vendor/src/graphCanvasLitFlow/graphCanvasTraversal.mjs +77 -0
  82. package/vendor/src/graphCanvasLitFlow/layoutIntentRoadmapWorkStack.mjs +73 -0
  83. package/vendor/src/graphCanvasLitFlow/resolveGraphCanvasOverlaps.mjs +77 -0
  84. package/vendor/src/graphRagContextSlice.mjs +461 -0
  85. package/vendor/src/gvmVerifyWorkerGate.mjs +95 -0
  86. package/vendor/src/homeSnapshotApi.mjs +131 -0
  87. package/vendor/src/homeSnapshotProjection.mjs +275 -0
  88. package/vendor/src/inboxEventStream.mjs +140 -0
  89. package/vendor/src/intentComposerApi.mjs +245 -0
  90. package/vendor/src/intentGraphGbcSliceBoundary.mjs +258 -0
  91. package/vendor/src/intentGraphProjection.mjs +208 -0
  92. package/vendor/src/intentHierarchy.mjs +241 -0
  93. package/vendor/src/intentNodeLint.mjs +107 -0
  94. package/vendor/src/intentNodeRuntime.mjs +185 -0
  95. package/vendor/src/intentRoadmapCanvas.mjs +393 -0
  96. package/vendor/src/intentRoadmapEpicProjection.mjs +122 -0
  97. package/vendor/src/intentRoadmapMermaid.mjs +165 -0
  98. package/vendor/src/intentRoadmapProjection.mjs +85 -0
  99. package/vendor/src/intentTreeLint.mjs +114 -0
  100. package/vendor/src/intentTreeMigration.mjs +150 -0
  101. package/vendor/src/intentTreeWorkItems.mjs +227 -0
  102. package/vendor/src/kanbanBoardProjection.mjs +58 -0
  103. package/vendor/src/languageAdapterRegistry.mjs +180 -0
  104. package/vendor/src/languageAdapters/goAdapter.mjs +62 -0
  105. package/vendor/src/languageAdapters/jsTsAdapter.mjs +60 -0
  106. package/vendor/src/languageAdapters/jsonYamlAdapter.mjs +103 -0
  107. package/vendor/src/languageAdapters/onebaseOsAdapter.mjs +55 -0
  108. package/vendor/src/languageAdapters/plaintextAdapter.mjs +36 -0
  109. package/vendor/src/languageAdapters/shared.mjs +68 -0
  110. package/vendor/src/languageAdapters/stepAdapter.mjs +81 -0
  111. package/vendor/src/lintPlanWorkAlignment.mjs +136 -0
  112. package/vendor/src/loopHintRepeatToolEval.mjs +153 -0
  113. package/vendor/src/lowcodeScaffoldCli.mjs +386 -0
  114. package/vendor/src/markdownDocumentRender.mjs +208 -0
  115. package/vendor/src/memoryPanelProjection.mjs +116 -0
  116. package/vendor/src/memoryRecordWriter.mjs +243 -0
  117. package/vendor/src/memoryWorkerSlice.mjs +238 -0
  118. package/vendor/src/migrateStepToBvc.mjs +133 -0
  119. package/vendor/src/missionControlServerHandlers.mjs +195 -0
  120. package/vendor/src/missionControlUiClient.mjs +278 -0
  121. package/vendor/src/onebaseCliCapabilityProbe.mjs +107 -0
  122. package/vendor/src/onebaseCliRunner.mjs +145 -0
  123. package/vendor/src/onebaseGrossProfitStaticVerify.mjs +98 -0
  124. package/vendor/src/onebaseParityEvidenceSync.mjs +88 -0
  125. package/vendor/src/onebasePvrgGraphNodes.mjs +257 -0
  126. package/vendor/src/onebaseRestEvidenceAdapter.mjs +216 -0
  127. package/vendor/src/onebaseVectorDslCodegenReadiness.mjs +137 -0
  128. package/vendor/src/onebaseWorkItemTemplate.mjs +154 -0
  129. package/vendor/src/onebaseWorkerTools.mjs +586 -0
  130. package/vendor/src/operatorShellProjection.mjs +102 -0
  131. package/vendor/src/pipelineProseRender.mjs +180 -0
  132. package/vendor/src/pipelineStageLint.mjs +118 -0
  133. package/vendor/src/promptRulesEditorApi.mjs +174 -0
  134. package/vendor/src/promptRulesProjection.mjs +134 -0
  135. package/vendor/src/pvrg/bladeAdapter.mjs +40 -0
  136. package/vendor/src/pvrgTaskScope.mjs +152 -0
  137. package/vendor/src/releaseGateMatrix.mjs +188 -0
  138. package/vendor/src/schematicView.mjs +305 -0
  139. package/vendor/src/seedAnalyticsRecord.mjs +217 -0
  140. package/vendor/src/semanticSearchBm25.mjs +103 -0
  141. package/vendor/src/semanticSearchExcerpts.mjs +68 -0
  142. package/vendor/src/semanticSearchTfidfVector.mjs +86 -0
  143. package/vendor/src/semanticSearchWorkflow.mjs +366 -0
  144. package/vendor/src/stepAtomFormatter.mjs +413 -0
  145. package/vendor/src/stepGraphSlice.mjs +318 -0
  146. package/vendor/src/ui/atoms/badge.mjs +40 -0
  147. package/vendor/src/ui/atoms/badgeClient.mjs +32 -0
  148. package/vendor/src/ui/atoms/button.mjs +114 -0
  149. package/vendor/src/ui/atoms/buttonClient.mjs +49 -0
  150. package/vendor/src/ui/atoms/icon.mjs +23 -0
  151. package/vendor/src/ui/atoms/input.mjs +38 -0
  152. package/vendor/src/ui/atoms/modal.mjs +44 -0
  153. package/vendor/src/ui/atoms/select.mjs +98 -0
  154. package/vendor/src/ui/backlogShellButtons.mjs +238 -0
  155. package/vendor/src/ui/htmlEscape.mjs +11 -0
  156. package/vendor/src/ui/molecules/rating.mjs +48 -0
  157. package/vendor/src/ui/molecules/tabs.mjs +70 -0
  158. package/vendor/src/ui/organisms/modal.mjs +1 -0
  159. package/vendor/src/ui/pages/uiKitPage.mjs +147 -0
  160. package/vendor/src/ui/workItemStatusTone.mjs +36 -0
  161. package/vendor/src/unifiedLinkageProjection.mjs +264 -0
  162. package/vendor/src/verificationLoop.mjs +206 -0
  163. package/vendor/src/workGraphBacklogPersist.mjs +234 -0
  164. package/vendor/src/workGraphBacklogUiServer.mjs +9192 -0
  165. package/vendor/src/workGraphBoundedTargetFileRead.mjs +178 -0
  166. package/vendor/src/workGraphCycleSlice.mjs +184 -0
  167. package/vendor/src/workGraphDaemonTick.mjs +307 -0
  168. package/vendor/src/workGraphDaemonWatch.mjs +157 -0
  169. package/vendor/src/workGraphEngineRoot.mjs +136 -0
  170. package/vendor/src/workGraphInstallLayout.mjs +65 -0
  171. package/vendor/src/workGraphLlmUsefulnessEval.mjs +611 -0
  172. package/vendor/src/workGraphPhasePromoteReadyQueue.mjs +159 -0
  173. package/vendor/src/workGraphProjectHost.mjs +149 -0
  174. package/vendor/src/workGraphProjectInit.mjs +392 -0
  175. package/vendor/src/workGraphPromoteReadyApi.mjs +115 -0
  176. package/vendor/src/workGraphRecoveryPolicy.mjs +124 -0
  177. package/vendor/src/workGraphRunnerQueueProjection.mjs +187 -0
  178. package/vendor/src/workGraphRuntime.mjs +1008 -0
  179. package/vendor/src/workGraphToolSurfaceAudit.mjs +372 -0
  180. package/vendor/src/workGraphToolTransportRuntime.mjs +195 -0
  181. package/vendor/src/workGraphWorkerProvider.mjs +600 -0
  182. package/vendor/src/workItemBvcQuality.mjs +262 -0
  183. package/vendor/src/workItemCreateAnalysis.mjs +157 -0
  184. package/vendor/src/workItemDecisionPipeline.mjs +278 -0
  185. package/vendor/src/workItemEpicCascade.mjs +176 -0
  186. package/vendor/src/workItemExecutionGate.mjs +78 -0
  187. package/vendor/src/workItemHierarchy.mjs +226 -0
  188. package/vendor/src/workItemProseLint.mjs +133 -0
  189. package/vendor/src/workItemTextRusify.mjs +794 -0
  190. package/vendor/src/workItemTraceEnvelope.mjs +158 -0
  191. package/vendor/src/workItemUiReferences.mjs +272 -0
  192. package/vendor/src/workflowEpicGrouping.mjs +67 -0
  193. package/vendor/src/workflowTreeProjection.mjs +53 -0
  194. package/vendor/src/workspaceRegistry.mjs +150 -0
@@ -0,0 +1,386 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { dirname, relative, resolve } from 'node:path';
3
+
4
+ export const LOWCODE_SCAFFOLD_MANIFEST_SCHEMA = 'workgraph.lowcode.scaffold.manifest.v1';
5
+ export const LOWCODE_SCAFFOLD_SUMMARY_SCHEMA = 'workgraph.lowcode.scaffold.summary.v1';
6
+
7
+ export const DEFAULT_ARCH_RULES_PATH = 'tests/fixtures/low-code-charter/arch-rules.bvc';
8
+ export const DEFAULT_SCAFFOLD_OUTPUT_DIR = 'generated/arch-scaffold';
9
+
10
+ const ARCH_RULE_BLOCK_PATTERN = /^#([^\n<]+)<\[\n([\s\S]*?)\n\]>/gmu;
11
+ const METADATA_TAG_PATTERN = /\/\*([^:]+):\s*([^*]+)\*\//gu;
12
+
13
+ export function parseArchRulesStepText(text) {
14
+ if (typeof text !== 'string') {
15
+ throw new TypeError('text must be a string');
16
+ }
17
+
18
+ const rules = [];
19
+ const errors = [];
20
+
21
+ for (const match of text.matchAll(ARCH_RULE_BLOCK_PATTERN)) {
22
+ const [, rawName, body] = match;
23
+ const name = rawName.trim();
24
+
25
+ if (name === 'ioHascSync' || name.startsWith('ioHasc')) {
26
+ continue;
27
+ }
28
+
29
+ const parsed = parseArchRuleBody(name, body);
30
+ if (parsed.errors.length > 0) {
31
+ errors.push(...parsed.errors.map((detail) => `${name}: ${detail}`));
32
+ continue;
33
+ }
34
+
35
+ if (parsed.rule) {
36
+ rules.push(parsed.rule);
37
+ }
38
+ }
39
+
40
+ if (rules.length === 0) {
41
+ errors.push('no arch rules found in charter text');
42
+ }
43
+
44
+ return {
45
+ ok: errors.length === 0,
46
+ rules,
47
+ errors,
48
+ };
49
+ }
50
+
51
+ function parseArchRuleBody(name, body) {
52
+ const errors = [];
53
+ const rule = {
54
+ id: name,
55
+ name,
56
+ basis: '',
57
+ vector: '',
58
+ goal: '',
59
+ metadata: {},
60
+ metadataRaw: '',
61
+ };
62
+
63
+ for (const rawLine of body.split(/\r?\n/u)) {
64
+ const line = rawLine.trim();
65
+ if (line === '') {
66
+ continue;
67
+ }
68
+
69
+ const separatorIndex = line.indexOf(':');
70
+ if (separatorIndex === -1) {
71
+ errors.push(`unsupported line: ${line}`);
72
+ continue;
73
+ }
74
+
75
+ const key = line.slice(0, separatorIndex).trim();
76
+ const value = line.slice(separatorIndex + 1).trim();
77
+
78
+ if (key === 'Базис') {
79
+ rule.basis = value;
80
+ continue;
81
+ }
82
+
83
+ if (key === 'Вектор') {
84
+ rule.vector = value;
85
+ continue;
86
+ }
87
+
88
+ if (key === 'Цель') {
89
+ rule.goal = value;
90
+ continue;
91
+ }
92
+
93
+ if (key === 'Метаданные') {
94
+ rule.metadataRaw = value;
95
+ rule.metadata = parseMetadataTags(value);
96
+ continue;
97
+ }
98
+
99
+ errors.push(`unknown field ${key}`);
100
+ }
101
+
102
+ if (!rule.basis || !rule.vector || !rule.goal) {
103
+ errors.push('missing basis/vector/goal');
104
+ return { rule: null, errors };
105
+ }
106
+
107
+ return { rule, errors };
108
+ }
109
+
110
+ export function parseMetadataTags(value) {
111
+ const metadata = {};
112
+
113
+ for (const match of String(value).matchAll(METADATA_TAG_PATTERN)) {
114
+ metadata[match[1].trim()] = match[2].trim();
115
+ }
116
+
117
+ return metadata;
118
+ }
119
+
120
+ export function buildArchRulesScaffoldSummary(options = {}) {
121
+ const charterPath = normalizeRepoPath(options.charterPath ?? DEFAULT_ARCH_RULES_PATH);
122
+ const outputDir = normalizeRepoPath(options.outputDir ?? DEFAULT_SCAFFOLD_OUTPUT_DIR);
123
+ const parsed = options.parsed ?? parseArchRulesStepText(options.charterText ?? '');
124
+
125
+ return {
126
+ schema: LOWCODE_SCAFFOLD_SUMMARY_SCHEMA,
127
+ ok: parsed.ok,
128
+ charterPath,
129
+ outputDir,
130
+ ruleCount: parsed.rules.length,
131
+ rules: parsed.rules.map((rule) => ({
132
+ id: rule.id,
133
+ domain: rule.metadata.domain ?? null,
134
+ severity: rule.metadata.severity ?? null,
135
+ pattern: rule.metadata.pattern ?? null,
136
+ })),
137
+ errors: parsed.errors,
138
+ generatedAt: options.generatedAt ?? new Date().toISOString(),
139
+ };
140
+ }
141
+
142
+ export function buildArchRuleStubModule(rule) {
143
+ const domain = rule.metadata.domain ?? 'unknown';
144
+ const severity = rule.metadata.severity ?? 'unknown';
145
+ const pattern = rule.metadata.pattern ?? null;
146
+
147
+ return `// Generated arch guard stub for ${rule.id}
148
+ export const ruleId = ${JSON.stringify(rule.id)};
149
+ export const domain = ${JSON.stringify(domain)};
150
+ export const severity = ${JSON.stringify(severity)};
151
+ export const pattern = ${pattern ? JSON.stringify(pattern) : 'null'};
152
+ export const summary = ${JSON.stringify({
153
+ basis: rule.basis,
154
+ vector: rule.vector,
155
+ goal: rule.goal,
156
+ }, null, 2)};
157
+
158
+ export function guardPlaceholder() {
159
+ return {
160
+ status: 'stub',
161
+ ruleId,
162
+ message: 'Wire arch guard scanner in sidecar or CI optional gate.',
163
+ };
164
+ }
165
+ `;
166
+ }
167
+
168
+ export function buildScaffoldManifest(summary, filesWritten) {
169
+ return {
170
+ schema: LOWCODE_SCAFFOLD_MANIFEST_SCHEMA,
171
+ charterPath: summary.charterPath,
172
+ outputDir: summary.outputDir,
173
+ generatedAt: summary.generatedAt,
174
+ ruleCount: summary.ruleCount,
175
+ rules: summary.rules,
176
+ filesWritten,
177
+ };
178
+ }
179
+
180
+ export async function writeArchRulesScaffold(options = {}) {
181
+ const cwd = options.cwd ?? process.cwd();
182
+ const charterPath = resolve(cwd, options.charterPath ?? DEFAULT_ARCH_RULES_PATH);
183
+ const outputDir = resolve(cwd, options.outputDir ?? DEFAULT_SCAFFOLD_OUTPUT_DIR);
184
+ const dryRun = options.dryRun === true;
185
+ const charterText = options.charterText ?? await readFile(charterPath, 'utf8');
186
+ const parsed = parseArchRulesStepText(charterText);
187
+
188
+ const summary = buildArchRulesScaffoldSummary({
189
+ charterPath: relative(cwd, charterPath).replace(/\\/g, '/'),
190
+ outputDir: relative(cwd, outputDir).replace(/\\/g, '/'),
191
+ parsed,
192
+ generatedAt: options.generatedAt,
193
+ });
194
+
195
+ if (!parsed.ok) {
196
+ return {
197
+ ok: false,
198
+ summary,
199
+ manifest: null,
200
+ filesWritten: [],
201
+ errors: parsed.errors,
202
+ };
203
+ }
204
+
205
+ const filesWritten = [];
206
+ const plannedFiles = [];
207
+
208
+ for (const rule of parsed.rules) {
209
+ const stubName = `${rule.id}.guard.stub.mjs`;
210
+ plannedFiles.push({
211
+ relativePath: `rules/${stubName}`,
212
+ contents: buildArchRuleStubModule(rule),
213
+ });
214
+ }
215
+
216
+ plannedFiles.push({
217
+ relativePath: 'README.md',
218
+ contents: buildScaffoldReadme(summary, parsed.rules),
219
+ });
220
+
221
+ if (dryRun) {
222
+ return {
223
+ ok: true,
224
+ summary,
225
+ manifest: buildScaffoldManifest(summary, plannedFiles.map((file) => file.relativePath)),
226
+ filesWritten: plannedFiles.map((file) => file.relativePath),
227
+ errors: [],
228
+ dryRun: true,
229
+ };
230
+ }
231
+
232
+ await mkdir(outputDir, { recursive: true });
233
+ await mkdir(resolve(outputDir, 'rules'), { recursive: true });
234
+
235
+ for (const file of plannedFiles) {
236
+ const absolutePath = resolve(outputDir, file.relativePath);
237
+ await mkdir(dirname(absolutePath), { recursive: true });
238
+ await writeFile(absolutePath, file.contents, 'utf8');
239
+ filesWritten.push(relative(cwd, absolutePath).replace(/\\/g, '/'));
240
+ }
241
+
242
+ const manifest = buildScaffoldManifest(summary, filesWritten);
243
+ const manifestPath = resolve(outputDir, 'manifest.json');
244
+ await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
245
+ filesWritten.push(relative(cwd, manifestPath).replace(/\\/g, '/'));
246
+
247
+ return {
248
+ ok: true,
249
+ summary,
250
+ manifest,
251
+ filesWritten,
252
+ errors: [],
253
+ dryRun: false,
254
+ };
255
+ }
256
+
257
+ function buildScaffoldReadme(summary, rules) {
258
+ const lines = [
259
+ '# Arch rules scaffold (Work Graph low-code MVP)',
260
+ '',
261
+ `Charter: \`${summary.charterPath}\``,
262
+ `Generated: ${summary.generatedAt}`,
263
+ `Rules: ${summary.ruleCount}`,
264
+ '',
265
+ 'This directory is a deterministic stub scaffold. Full TurIr/Handlebars generation remains deferred to ../project until sidecar boundary closes.',
266
+ '',
267
+ '## Rules',
268
+ '',
269
+ ...rules.map((rule) => `- \`${rule.id}\` (${rule.metadata.domain ?? 'unknown'} / ${rule.metadata.severity ?? 'unknown'})`),
270
+ '',
271
+ ];
272
+
273
+ return `${lines.join('\n')}\n`;
274
+ }
275
+
276
+ export async function validateArchRulesCharter(options = {}) {
277
+ const cwd = options.cwd ?? process.cwd();
278
+ const charterPath = resolve(cwd, options.charterPath ?? DEFAULT_ARCH_RULES_PATH);
279
+
280
+ try {
281
+ const charterText = await readFile(charterPath, 'utf8');
282
+ const parsed = parseArchRulesStepText(charterText);
283
+
284
+ return {
285
+ ok: parsed.ok,
286
+ charterPath: relative(cwd, charterPath).replace(/\\/g, '/'),
287
+ ruleCount: parsed.rules.length,
288
+ errors: parsed.errors,
289
+ };
290
+ } catch (error) {
291
+ return {
292
+ ok: false,
293
+ charterPath: relative(cwd, charterPath).replace(/\\/g, '/'),
294
+ ruleCount: 0,
295
+ errors: [error instanceof Error ? error.message : String(error)],
296
+ };
297
+ }
298
+ }
299
+
300
+ export async function runLowcodeVerify(options = {}) {
301
+ const cwd = options.cwd ?? process.cwd();
302
+ const charterPath = options.charterPath ?? DEFAULT_ARCH_RULES_PATH;
303
+ const charterValidation = await validateArchRulesCharter({ cwd, charterPath });
304
+
305
+ if (!charterValidation.ok) {
306
+ return {
307
+ ok: false,
308
+ schema: 'workgraph.lowcode.verify.v1',
309
+ charter: charterValidation,
310
+ scaffoldDryRun: null,
311
+ errors: charterValidation.errors,
312
+ };
313
+ }
314
+
315
+ const charterText = await readFile(resolve(cwd, charterPath), 'utf8');
316
+ const scaffoldDryRun = await writeArchRulesScaffold({
317
+ cwd,
318
+ charterPath,
319
+ charterText,
320
+ dryRun: true,
321
+ outputDir: options.outputDir ?? DEFAULT_SCAFFOLD_OUTPUT_DIR,
322
+ generatedAt: options.generatedAt,
323
+ });
324
+
325
+ const ok = scaffoldDryRun.ok;
326
+ const errors = ok ? [] : [...charterValidation.errors, ...scaffoldDryRun.errors];
327
+
328
+ return {
329
+ ok,
330
+ schema: 'workgraph.lowcode.verify.v1',
331
+ charter: charterValidation,
332
+ scaffoldDryRun: {
333
+ ok: scaffoldDryRun.ok,
334
+ ruleCount: scaffoldDryRun.summary.ruleCount,
335
+ filesPlanned: scaffoldDryRun.filesWritten,
336
+ },
337
+ errors,
338
+ };
339
+ }
340
+
341
+ export function parseLowcodeCliArgs(argv) {
342
+ const options = {
343
+ charterPath: DEFAULT_ARCH_RULES_PATH,
344
+ outputDir: DEFAULT_SCAFFOLD_OUTPUT_DIR,
345
+ dryRun: false,
346
+ json: false,
347
+ command: 'scaffold',
348
+ };
349
+
350
+ const args = [...argv];
351
+
352
+ if (args[0] === 'verify') {
353
+ options.command = 'verify';
354
+ args.shift();
355
+ }
356
+
357
+ for (let index = 0; index < args.length; index += 1) {
358
+ const token = args[index];
359
+
360
+ if (token === '--charter' && args[index + 1]) {
361
+ options.charterPath = args[++index];
362
+ continue;
363
+ }
364
+
365
+ if (token === '--output' && args[index + 1]) {
366
+ options.outputDir = args[++index];
367
+ continue;
368
+ }
369
+
370
+ if (token === '--dry-run') {
371
+ options.dryRun = true;
372
+ continue;
373
+ }
374
+
375
+ if (token === '--json') {
376
+ options.json = true;
377
+ continue;
378
+ }
379
+ }
380
+
381
+ return options;
382
+ }
383
+
384
+ function normalizeRepoPath(value) {
385
+ return String(value).replace(/\\/g, '/');
386
+ }
@@ -0,0 +1,208 @@
1
+ import { highlightCodeBlock, normalizeCodeLanguage } from './codeSyntaxHighlight.mjs';
2
+
3
+ function escapeHtml(value) {
4
+ return String(value ?? '')
5
+ .replace(/&/g, '&amp;')
6
+ .replace(/</g, '&lt;')
7
+ .replace(/>/g, '&gt;')
8
+ .replace(/"/g, '&quot;');
9
+ }
10
+
11
+ export function renderInlineMarkdown(text) {
12
+ const tickParts = String(text ?? '').split('`');
13
+
14
+ return tickParts.map((chunk, index) => {
15
+ if (index % 2 === 1) {
16
+ return `<code class="inline-term">${escapeHtml(chunk)}</code>`;
17
+ }
18
+
19
+ const boldParts = chunk.split('**');
20
+ return boldParts.map((part, boldIndex) => {
21
+ if (boldIndex % 2 === 1) {
22
+ return `<strong>${escapeHtml(part)}</strong>`;
23
+ }
24
+
25
+ return escapeHtml(part);
26
+ }).join('');
27
+ }).join('');
28
+ }
29
+
30
+ function splitTableRow(line) {
31
+ return line
32
+ .trim()
33
+ .replace(/^\|/u, '')
34
+ .replace(/\|$/u, '')
35
+ .split('|')
36
+ .map((cell) => cell.trim());
37
+ }
38
+
39
+ function isTableSeparator(line) {
40
+ return /^\|?(?:\s*:?-{3,}:?\s*\|)+\s*:?-{3,}:?\s*\|?$/u.test(String(line ?? '').trim());
41
+ }
42
+
43
+ function renderMarkdownTable(headerCells, bodyRows) {
44
+ const head = headerCells.map((cell) => `<th>${renderInlineMarkdown(cell)}</th>`).join('');
45
+ const body = bodyRows.map((row) =>
46
+ `<tr>${row.map((cell) => `<td>${renderInlineMarkdown(cell)}</td>`).join('')}</tr>`,
47
+ ).join('');
48
+
49
+ return `<div class="markdown-table-wrap"><table class="markdown-table"><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table></div>`;
50
+ }
51
+
52
+ export function stripAnalyticsBodyPreamble(text) {
53
+ const lines = String(text ?? '').split(/\r?\n/u);
54
+ let index = 0;
55
+
56
+ if (/^#\s+/u.test(lines[index]?.trim() ?? '')) {
57
+ index += 1;
58
+ }
59
+
60
+ while (index < lines.length && lines[index].trim() === '') {
61
+ index += 1;
62
+ }
63
+
64
+ while (index < lines.length) {
65
+ const trimmed = lines[index].trim();
66
+
67
+ if (trimmed === '') {
68
+ index += 1;
69
+ continue;
70
+ }
71
+
72
+ if (/^\*\*Запрос:\*\*/u.test(trimmed)) {
73
+ index += 1;
74
+ continue;
75
+ }
76
+
77
+ if (/^\*\*Тема:\*\*/u.test(trimmed)) {
78
+ index += 1;
79
+ continue;
80
+ }
81
+
82
+ if (/^\*\*Связанные файлы:\*\*/u.test(trimmed)) {
83
+ index += 1;
84
+ continue;
85
+ }
86
+
87
+ if (/^---+$|^___+$|^\*\*\*+$/u.test(trimmed)) {
88
+ index += 1;
89
+ continue;
90
+ }
91
+
92
+ break;
93
+ }
94
+
95
+ while (index < lines.length && lines[index].trim() === '') {
96
+ index += 1;
97
+ }
98
+
99
+ return lines.slice(index).join('\n').trim();
100
+ }
101
+
102
+ function escapeMermaidSource(text) {
103
+ return String(text ?? '')
104
+ .replace(/&/g, '&amp;')
105
+ .replace(/</g, '&lt;');
106
+ }
107
+
108
+ export function renderMarkdownDocument(text) {
109
+ const lines = String(text ?? '').split(/\r?\n/u);
110
+ const parts = [];
111
+ let index = 0;
112
+
113
+ while (index < lines.length) {
114
+ const rawLine = lines[index];
115
+ const trimmed = rawLine.trim();
116
+
117
+ if (trimmed === '') {
118
+ index += 1;
119
+ continue;
120
+ }
121
+
122
+ if (trimmed.startsWith('```')) {
123
+ const language = trimmed.slice(3).trim();
124
+ index += 1;
125
+ const codeLines = [];
126
+
127
+ while (index < lines.length && !lines[index].trim().startsWith('```')) {
128
+ codeLines.push(lines[index]);
129
+ index += 1;
130
+ }
131
+
132
+ if (index < lines.length) {
133
+ index += 1;
134
+ }
135
+
136
+ if (language === 'mermaid') {
137
+ const source = codeLines.join('\n');
138
+ parts.push(`<div class="markdown-mermaid-wrap" data-testid="markdown-mermaid"><div class="mermaid">${escapeMermaidSource(source)}</div></div>`);
139
+ continue;
140
+ }
141
+
142
+ const normalizedLanguage = normalizeCodeLanguage(language);
143
+ const highlighted = highlightCodeBlock(codeLines.join('\n'), normalizedLanguage);
144
+ const languageClass = normalizedLanguage === 'plaintext' ? '' : ` language-${normalizedLanguage}`;
145
+ parts.push(`<pre class="markdown-code-block"><code class="code-block${languageClass}">${highlighted}</code></pre>`);
146
+
147
+ continue;
148
+ }
149
+
150
+ if (/^---+$|^___+$|^\*\*\*+$/u.test(trimmed)) {
151
+ parts.push('<hr class="markdown-hr" />');
152
+ index += 1;
153
+ continue;
154
+ }
155
+
156
+ if (trimmed.includes('|') && index + 1 < lines.length && isTableSeparator(lines[index + 1])) {
157
+ const headerCells = splitTableRow(trimmed);
158
+ index += 2;
159
+ const bodyRows = [];
160
+
161
+ while (index < lines.length && lines[index].trim() !== '' && lines[index].trim().includes('|')) {
162
+ bodyRows.push(splitTableRow(lines[index]));
163
+ index += 1;
164
+ }
165
+
166
+ parts.push(renderMarkdownTable(headerCells, bodyRows));
167
+ continue;
168
+ }
169
+
170
+ const headingMatch = trimmed.match(/^(#{1,3})\s+(.+)$/u);
171
+ if (headingMatch) {
172
+ const level = headingMatch[1].length;
173
+ const tag = level === 1 ? 'h1' : level === 2 ? 'h2' : 'h3';
174
+ parts.push(`<${tag} class="markdown-${tag}">${renderInlineMarkdown(headingMatch[2])}</${tag}>`);
175
+ index += 1;
176
+ continue;
177
+ }
178
+
179
+ if (/^[-*]\s+/u.test(trimmed)) {
180
+ const items = [];
181
+
182
+ while (index < lines.length && /^[-*]\s+/u.test(lines[index].trim())) {
183
+ items.push(lines[index].trim().replace(/^[-*]\s+/u, ''));
184
+ index += 1;
185
+ }
186
+
187
+ parts.push(`<ul class="markdown-list">${items.map((item) => `<li>${renderInlineMarkdown(item)}</li>`).join('')}</ul>`);
188
+ continue;
189
+ }
190
+
191
+ if (/^\d+\.\s+/u.test(trimmed)) {
192
+ const items = [];
193
+
194
+ while (index < lines.length && /^\d+\.\s+/u.test(lines[index].trim())) {
195
+ items.push(lines[index].trim().replace(/^\d+\.\s+/u, ''));
196
+ index += 1;
197
+ }
198
+
199
+ parts.push(`<ol class="markdown-list markdown-list--ordered">${items.map((item) => `<li>${renderInlineMarkdown(item)}</li>`).join('')}</ol>`);
200
+ continue;
201
+ }
202
+
203
+ parts.push(`<p class="markdown-p">${renderInlineMarkdown(trimmed)}</p>`);
204
+ index += 1;
205
+ }
206
+
207
+ return `<div class="markdown-doc">${parts.join('')}</div>`;
208
+ }
@@ -0,0 +1,116 @@
1
+ import { readWorkItemsFromRepo } from './intentTreeWorkItems.mjs';
2
+ import {
3
+ buildMemoryRecordCandidatesFromItems,
4
+ mergeMemoryJournalWithCandidates,
5
+ readMemoryRecordJournal,
6
+ } from './memoryRecordWriter.mjs';
7
+
8
+ export const MEMORY_PANEL_PROJECTION_SCHEMA = 'memory-panel.projection.v1';
9
+ export const MEMORY_RECORDS_API_SCHEMA = 'memory-records.api.v1';
10
+ export const DEFAULT_MEMORY_RECORDS_LIMIT = 50;
11
+
12
+ function summarizeRecords(records) {
13
+ const byType = {};
14
+ const byStatus = {};
15
+
16
+ for (const record of records) {
17
+ byType[record.type] = (byType[record.type] ?? 0) + 1;
18
+ byStatus[record.status] = (byStatus[record.status] ?? 0) + 1;
19
+ }
20
+
21
+ return {
22
+ total: records.length,
23
+ byType,
24
+ byStatus,
25
+ reviewRequired: records.filter((record) => record.reviewRequired).length,
26
+ };
27
+ }
28
+
29
+ export async function loadMergedMemoryRecords(options = {}) {
30
+ const items = options.items ?? await readWorkItemsFromRepo(options);
31
+ const candidates = buildMemoryRecordCandidatesFromItems(items, options).records;
32
+ let journalRecords = options.journalRecords;
33
+
34
+ if (journalRecords === undefined && options.includeJournal !== false) {
35
+ const journal = await readMemoryRecordJournal(options);
36
+ journalRecords = journal.records;
37
+ }
38
+
39
+ return mergeMemoryJournalWithCandidates(candidates, journalRecords ?? []);
40
+ }
41
+
42
+ export function filterMemoryRecords(records, options = {}) {
43
+ if (!Array.isArray(records)) {
44
+ throw new TypeError('records must be an array');
45
+ }
46
+
47
+ let filtered = records;
48
+ const workId = String(options.workId ?? '').trim();
49
+ if (workId !== '') {
50
+ filtered = filtered.filter((record) => record.sourceWorkItem === workId);
51
+ }
52
+
53
+ const limit = Number.isInteger(options.limit) && options.limit > 0 ? options.limit : null;
54
+ const truncated = limit !== null && filtered.length > limit;
55
+ if (limit !== null) {
56
+ filtered = filtered.slice(0, limit);
57
+ }
58
+
59
+ return {
60
+ records: filtered,
61
+ truncated,
62
+ limit: limit ?? filtered.length,
63
+ workId: workId || null,
64
+ };
65
+ }
66
+
67
+ export function buildMemoryPanelProjectionFromRecords(records, options = {}) {
68
+ const summary = summarizeRecords(records);
69
+
70
+ return {
71
+ schema: MEMORY_PANEL_PROJECTION_SCHEMA,
72
+ readOnly: true,
73
+ reviewActionsEnabled: false,
74
+ source: options.source ?? 'merged-memory-records',
75
+ summary,
76
+ records,
77
+ filters: {
78
+ types: Object.keys(summary.byType).sort(),
79
+ statuses: Object.keys(summary.byStatus).sort(),
80
+ },
81
+ };
82
+ }
83
+
84
+ export function buildMemoryPanelProjectionFromItems(items, options = {}) {
85
+ if (!Array.isArray(items)) {
86
+ throw new TypeError('items must be an array');
87
+ }
88
+
89
+ const candidates = buildMemoryRecordCandidatesFromItems(items, options);
90
+ return buildMemoryPanelProjectionFromRecords(candidates.records, {
91
+ ...options,
92
+ source: 'done-work-items',
93
+ });
94
+ }
95
+
96
+ export async function buildMemoryRecordsApiResponse(options = {}) {
97
+ const records = await loadMergedMemoryRecords(options);
98
+ const filtered = filterMemoryRecords(records, {
99
+ workId: options.workId,
100
+ limit: options.limit ?? DEFAULT_MEMORY_RECORDS_LIMIT,
101
+ });
102
+
103
+ return {
104
+ schema: MEMORY_RECORDS_API_SCHEMA,
105
+ count: filtered.records.length,
106
+ truncated: filtered.truncated,
107
+ workId: filtered.workId,
108
+ limit: filtered.limit,
109
+ records: filtered.records,
110
+ };
111
+ }
112
+
113
+ export async function buildMemoryPanelProjection(options = {}) {
114
+ const records = await loadMergedMemoryRecords(options);
115
+ return buildMemoryPanelProjectionFromRecords(records, options);
116
+ }