@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,55 @@
1
+ import { buildFileFactsEnvelope, createCapabilities } from './shared.mjs';
2
+
3
+ export const onebaseOsAdapter = {
4
+ id: 'onebase-os-v1',
5
+ languageId: 'onebase-os',
6
+ extensions: ['.os'],
7
+ artifactProfiles: ['onebase-posting-script'],
8
+ capabilities: createCapabilities({
9
+ syntax: 'structured',
10
+ symbols: true,
11
+ domainMetadata: true,
12
+ semanticChunks: true,
13
+ verificationHints: true,
14
+ }),
15
+ confidence: 'medium',
16
+ fallback: 'plaintext-v1',
17
+ owner: 'work-graph',
18
+ };
19
+
20
+ const PROCEDURE_PATTERN = /(?:Процедура|Procedure)\s+([\wА-Яа-яЁё]+)\s*\(/gu;
21
+ const MOVEMENT_PATTERN = /Движения\.([\wА-Яа-яЁё]+)/gu;
22
+ const FIELD_REF_PATTERN = /(?:Строка|this)\.([\wА-Яа-яЁё]+)/gu;
23
+
24
+ export function extractOnebaseOsFacts(content, context = {}) {
25
+ const filePath = context.filePath ?? '';
26
+ const text = String(content);
27
+ const procedures = [...text.matchAll(PROCEDURE_PATTERN)].map((match) => match[1]);
28
+ const registers = [...new Set([...text.matchAll(MOVEMENT_PATTERN)].map((match) => match[1]))];
29
+ const documentFields = [...new Set([...text.matchAll(FIELD_REF_PATTERN)].map((match) => match[1]))];
30
+
31
+ const symbols = procedures.map((name) => ({ kind: 'procedure', name }));
32
+ const domainMetadata = {
33
+ postingScript: true,
34
+ procedures,
35
+ registers,
36
+ documentFields,
37
+ clearsMovements: /Движения\.[\wА-Яа-яЁё]+\.Очистить\(\)/u.test(text),
38
+ };
39
+
40
+ const testHints = [{
41
+ kind: 'onebase-posting',
42
+ commandHint: 'npm run test:optional:onebase',
43
+ }];
44
+
45
+ return buildFileFactsEnvelope(onebaseOsAdapter, filePath, {
46
+ symbols,
47
+ domainMetadata,
48
+ testHints,
49
+ chunks: procedures.map((name) => ({
50
+ ref: `procedure:${name}`,
51
+ kind: 'onebase-procedure',
52
+ text: name,
53
+ })),
54
+ });
55
+ }
@@ -0,0 +1,36 @@
1
+ import {
2
+ basenameFromPath,
3
+ buildFileFactsEnvelope,
4
+ createCapabilities,
5
+ lexicalChunks,
6
+ } from './shared.mjs';
7
+
8
+ export const plaintextAdapter = {
9
+ id: 'plaintext-v1',
10
+ languageId: 'plaintext',
11
+ extensions: ['*'],
12
+ artifactProfiles: ['generic-file'],
13
+ capabilities: createCapabilities({
14
+ syntax: 'lexical',
15
+ semanticChunks: true,
16
+ }),
17
+ confidence: 'low',
18
+ fallback: null,
19
+ owner: 'work-graph',
20
+ };
21
+
22
+ export function extractPlaintextFacts(content, context = {}) {
23
+ const filePath = context.filePath ?? '';
24
+ const extension = context.extension ?? '';
25
+
26
+ return buildFileFactsEnvelope(plaintextAdapter, filePath, {
27
+ chunks: lexicalChunks(content),
28
+ domainMetadata: {
29
+ extension,
30
+ basename: basenameFromPath(filePath),
31
+ byteLength: Buffer.byteLength(String(content), 'utf8'),
32
+ },
33
+ }, {
34
+ confidence: 'low',
35
+ });
36
+ }
@@ -0,0 +1,68 @@
1
+ export const LANGUAGE_FILE_FACTS_SCHEMA = 'workgraph.language-file-facts.v1';
2
+
3
+ export function createCapabilities(partial = {}) {
4
+ return {
5
+ syntax: partial.syntax ?? 'none',
6
+ symbols: Boolean(partial.symbols),
7
+ imports: Boolean(partial.imports),
8
+ tests: Boolean(partial.tests),
9
+ semanticChunks: Boolean(partial.semanticChunks),
10
+ domainMetadata: Boolean(partial.domainMetadata),
11
+ traceLinks: Boolean(partial.traceLinks),
12
+ verificationHints: Boolean(partial.verificationHints),
13
+ };
14
+ }
15
+
16
+ export function buildFileFactsEnvelope(adapter, filePath, payload, options = {}) {
17
+ return {
18
+ schema: LANGUAGE_FILE_FACTS_SCHEMA,
19
+ filePath,
20
+ languageId: adapter.languageId,
21
+ adapterId: adapter.id,
22
+ status: options.status ?? 'ok',
23
+ confidence: options.confidence ?? adapter.confidence,
24
+ capabilities: { ...adapter.capabilities },
25
+ symbols: payload.symbols ?? [],
26
+ imports: payload.imports ?? [],
27
+ chunks: payload.chunks ?? [],
28
+ domainMetadata: payload.domainMetadata ?? {},
29
+ testHints: payload.testHints ?? [],
30
+ traceRefs: payload.traceRefs ?? [],
31
+ diagnostics: payload.diagnostics ?? [],
32
+ };
33
+ }
34
+
35
+ export function lexicalChunks(content, options = {}) {
36
+ const lines = String(content).split(/\r?\n/u);
37
+ const maxLines = options.maxLines ?? 40;
38
+ const chunks = [];
39
+
40
+ for (let index = 0; index < lines.length; index += maxLines) {
41
+ const slice = lines.slice(index, index + maxLines);
42
+ chunks.push({
43
+ ref: `lines:${index + 1}-${index + slice.length}`,
44
+ kind: 'lexical',
45
+ text: slice.join('\n'),
46
+ });
47
+ }
48
+
49
+ if (chunks.length === 0) {
50
+ chunks.push({ ref: 'lines:1-1', kind: 'lexical', text: '' });
51
+ }
52
+
53
+ return chunks;
54
+ }
55
+
56
+ export function extensionFromPath(filePath) {
57
+ const normalized = String(filePath ?? '').replace(/\\/gu, '/');
58
+ const index = normalized.lastIndexOf('.');
59
+ if (index === -1) {
60
+ return '';
61
+ }
62
+
63
+ return normalized.slice(index).toLowerCase();
64
+ }
65
+
66
+ export function basenameFromPath(filePath) {
67
+ return String(filePath ?? '').replace(/\\/gu, '/').split('/').pop() ?? '';
68
+ }
@@ -0,0 +1,81 @@
1
+ import { parseStepAtomDrafts } from '../stepAtomFormatter.mjs';
2
+ import { lintBvcFilePath } from '../bvcFileFormat.mjs';
3
+ import { buildFileFactsEnvelope, createCapabilities, lexicalChunks } from './shared.mjs';
4
+
5
+ export const stepAdapter = {
6
+ id: 'bvc-v1',
7
+ languageId: 'bvc',
8
+ legacyLanguageIds: ['step'],
9
+ extensions: ['.bvc', '.bvc'],
10
+ artifactProfiles: ['bvc-atom', 'step-atom', 'work-item', 'protocol'],
11
+ capabilities: createCapabilities({
12
+ syntax: 'structured',
13
+ symbols: true,
14
+ semanticChunks: true,
15
+ traceLinks: true,
16
+ }),
17
+ confidence: 'high',
18
+ fallback: 'plaintext-v1',
19
+ owner: 'work-graph',
20
+ };
21
+
22
+ export function extractStepFacts(content, context = {}) {
23
+ const filePath = context.filePath ?? '';
24
+ const parsedAtoms = parseStepAtomDrafts(String(content));
25
+ const pathLints = filePath ? lintBvcFilePath(filePath) : [];
26
+ const symbols = [];
27
+ const traceRefs = [];
28
+ const chunks = [];
29
+
30
+ for (const parsed of parsedAtoms) {
31
+ const labels = parsed.draft.labels ?? {};
32
+ symbols.push({
33
+ kind: labels['atom.profile'] ?? 'bvc-atom',
34
+ name: parsed.draft.name,
35
+ profile: parsed.draft.profile,
36
+ lang: parsed.draft.lang ?? null,
37
+ });
38
+
39
+ if (labels['work.id']) {
40
+ traceRefs.push({ kind: 'work.id', value: labels['work.id'] });
41
+ }
42
+
43
+ if (labels['protocol.id']) {
44
+ traceRefs.push({ kind: 'protocol.id', value: labels['protocol.id'] });
45
+ }
46
+
47
+ if (labels['rule.id']) {
48
+ traceRefs.push({ kind: 'rule.id', value: labels['rule.id'] });
49
+ }
50
+
51
+ chunks.push({
52
+ ref: `atom:${parsed.draft.name}`,
53
+ kind: 'step-atom',
54
+ text: [
55
+ parsed.draft.basis.join(' '),
56
+ parsed.draft.vector.join(' '),
57
+ parsed.draft.goal.join(' '),
58
+ ].filter(Boolean).join('\n'),
59
+ });
60
+ }
61
+
62
+ if (chunks.length === 0) {
63
+ chunks.push(...lexicalChunks(content, { maxLines: 24 }));
64
+ }
65
+
66
+ const diagnostics = [
67
+ ...pathLints.map((lint) => `${lint.code}: ${lint.message}`),
68
+ ...parsedAtoms.flatMap((parsed) => parsed.errors ?? []),
69
+ ...parsedAtoms.flatMap((parsed) => (parsed.lints ?? []).map((lint) => `${lint.code}: ${lint.message}`)),
70
+ ];
71
+
72
+ return buildFileFactsEnvelope(stepAdapter, filePath, {
73
+ symbols,
74
+ traceRefs,
75
+ chunks,
76
+ diagnostics,
77
+ }, {
78
+ status: diagnostics.length > 0 ? 'degraded' : 'ok',
79
+ confidence: diagnostics.length > 0 ? 'medium' : 'high',
80
+ });
81
+ }
@@ -0,0 +1,136 @@
1
+ import { readdir, readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+
4
+ import { readWorkItemsFromRepo } from './intentTreeWorkItems.mjs';
5
+
6
+ const PLAN_GLOB_PREFIX = 'plan-';
7
+ const WORK_ID_IN_LINE = /`([a-z][a-z0-9-]*)`/i;
8
+ const UNCHECKED_TODO = /^-\s+\[\s\]\s+/;
9
+
10
+ const PIPELINE_ORDER = ['intake', 'analyzed', 'decided', 'ready', 'executing', 'closed'];
11
+
12
+ export function lintPlanTodoLines(planText, filePath) {
13
+ const issues = [];
14
+ const lines = planText.split(/\r?\n/);
15
+
16
+ for (let index = 0; index < lines.length; index += 1) {
17
+ const line = lines[index];
18
+
19
+ if (!UNCHECKED_TODO.test(line)) {
20
+ continue;
21
+ }
22
+
23
+ if (WORK_ID_IN_LINE.test(line)) {
24
+ continue;
25
+ }
26
+
27
+ issues.push({
28
+ code: 'plan_todo_missing_work_id',
29
+ severity: 'warning',
30
+ filePath,
31
+ line: index + 1,
32
+ message: `unchecked todo без work.id в backticks: ${line.trim()}`,
33
+ });
34
+ }
35
+
36
+ return issues;
37
+ }
38
+
39
+ export function lintWorkItemDoingBeforeReady(workItems) {
40
+ const issues = [];
41
+
42
+ for (const item of workItems) {
43
+ const status = item.status ?? item.labels?.['work.status'];
44
+ const stage = item.labels?.['work.pipeline_stage'] ?? inferStageFromItem(item);
45
+
46
+ if (status !== 'doing') {
47
+ continue;
48
+ }
49
+
50
+ if (!stage || stage === 'executing' || stage === 'closed') {
51
+ continue;
52
+ }
53
+
54
+ const stageIndex = PIPELINE_ORDER.indexOf(stage);
55
+ const readyIndex = PIPELINE_ORDER.indexOf('ready');
56
+
57
+ if (stageIndex !== -1 && stageIndex < readyIndex) {
58
+ issues.push({
59
+ code: 'work_doing_before_ready',
60
+ severity: 'warning',
61
+ workId: item.id,
62
+ message: `work.status=doing при work.pipeline_stage=${stage} (< ready)`,
63
+ });
64
+ }
65
+ }
66
+
67
+ return issues;
68
+ }
69
+
70
+ function inferStageFromItem(item) {
71
+ if (item.labels?.['work.pipeline_stage']) {
72
+ return item.labels['work.pipeline_stage'];
73
+ }
74
+
75
+ if (item.decisionVerdict) {
76
+ return 'decided';
77
+ }
78
+
79
+ if (item.analysis) {
80
+ return 'analyzed';
81
+ }
82
+
83
+ return 'intake';
84
+ }
85
+
86
+ export async function lintPlanWorkAlignment(options = {}) {
87
+ const cwd = options.cwd ?? process.cwd();
88
+ const docsDir = join(cwd, 'docs');
89
+ const issues = [];
90
+
91
+ let planFiles = [];
92
+
93
+ try {
94
+ const entries = await readdir(docsDir);
95
+ planFiles = entries
96
+ .filter((name) => name.startsWith(PLAN_GLOB_PREFIX) && name.endsWith('.md'))
97
+ .map((name) => join('docs', name));
98
+ } catch {
99
+ planFiles = [];
100
+ }
101
+
102
+ for (const filePath of planFiles) {
103
+ const text = await readFile(join(cwd, filePath), 'utf8');
104
+ issues.push(...lintPlanTodoLines(text, filePath));
105
+ }
106
+
107
+ const workItems = options.workItems ?? await readWorkItemsFromRepo({ cwd });
108
+ issues.push(...lintWorkItemDoingBeforeReady(workItems));
109
+
110
+ const errors = issues.filter((issue) => issue.severity === 'error');
111
+ const warnings = issues.filter((issue) => issue.severity === 'warning');
112
+
113
+ return {
114
+ schema: 'workgraph.lint-plan-work-alignment.v1',
115
+ ok: errors.length === 0,
116
+ issueCount: issues.length,
117
+ errors,
118
+ warnings,
119
+ issues,
120
+ };
121
+ }
122
+
123
+ export function formatPlanWorkAlignmentReport(report) {
124
+ const lines = [
125
+ `plan-work alignment: ${report.ok ? 'ok' : 'fail'} (${report.errors.length} errors, ${report.warnings.length} warnings)`,
126
+ ];
127
+
128
+ for (const issue of report.issues) {
129
+ const location = issue.filePath
130
+ ? `${issue.filePath}:${issue.line ?? '?'}`
131
+ : issue.workId ?? '?';
132
+ lines.push(` [${issue.severity}] ${issue.code} @ ${location} — ${issue.message}`);
133
+ }
134
+
135
+ return lines.join('\n');
136
+ }
@@ -0,0 +1,153 @@
1
+ import { resolveOpenAiProviderEnv } from './agentWorkerOpenAiProvider.mjs';
2
+ import {
3
+ createAgentToolLoopTracker,
4
+ formatAgentToolDuplicateInvocationLoopHint,
5
+ } from './agentToolLoopGuard.mjs';
6
+
7
+ export const LOOP_HINT_REPEAT_TOOL_EVAL_SCHEMA = 'workgraph.loop-hint-repeat-tool.eval.v1';
8
+
9
+ const STOP_KEYWORDS = [
10
+ 'loop_hint',
11
+ 'loop hint',
12
+ 'change strategy',
13
+ 'different tool',
14
+ 'stop repeating',
15
+ 'не повторя',
16
+ 'смени стратег',
17
+ 'другой инструмент',
18
+ 'зацикл',
19
+ ];
20
+
21
+ export function buildRepeatToolCallFixture() {
22
+ return {
23
+ toolName: 'get_work_item',
24
+ args: { workId: 'ready-eval' },
25
+ repeats: 3,
26
+ };
27
+ }
28
+
29
+ export function evaluateRepeatToolCallLoopDeterministic(fixture = buildRepeatToolCallFixture()) {
30
+ const tracker = createAgentToolLoopTracker();
31
+ let lastRecord = null;
32
+
33
+ for (let index = 0; index < fixture.repeats; index += 1) {
34
+ lastRecord = tracker.record(fixture.toolName, fixture.args);
35
+ }
36
+
37
+ return {
38
+ ok: lastRecord?.loopAborted === true && String(lastRecord.loopHint ?? '').includes('LOOP_HINT'),
39
+ lastRecord,
40
+ counts: tracker.snapshot(),
41
+ };
42
+ }
43
+
44
+ export function buildLoopHintPrompt(loopHintText) {
45
+ return [
46
+ 'You are simulating a WorkGraph MCP agent workflow.',
47
+ 'The tool executor returned LOOP_HINT because you repeated the same tool call.',
48
+ 'Respond in plain text. Acknowledge LOOP_HINT and stop repeating the same tool with the same arguments.',
49
+ 'Do not call get_work_item again in this response.',
50
+ '',
51
+ 'Tool result:',
52
+ loopHintText,
53
+ ].join('\n');
54
+ }
55
+
56
+ export function evaluateLoopHintModelResponse(text, options = {}) {
57
+ const body = String(text ?? '');
58
+ const lower = body.toLowerCase();
59
+ const repeatedSameTool = /\bget_work_item\b/u.test(body)
60
+ && /\bready-eval\b/u.test(body)
61
+ && /\b(call|invoke|claim|execute|вызов)/iu.test(body);
62
+ const hasStopSignal = STOP_KEYWORDS.some((keyword) => lower.includes(keyword));
63
+
64
+ return {
65
+ ok: hasStopSignal && !repeatedSameTool,
66
+ hasStopSignal,
67
+ repeatedSameTool,
68
+ };
69
+ }
70
+
71
+ export async function fetchLoopHintModelResponse(prompt, options = {}) {
72
+ const env = resolveOpenAiProviderEnv(options);
73
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
74
+ const url = `${env.baseUrl}/chat/completions`;
75
+ const headers = { 'Content-Type': 'application/json' };
76
+ if (env.apiKey) {
77
+ headers.Authorization = `Bearer ${env.apiKey}`;
78
+ }
79
+
80
+ const response = await fetchImpl(url, {
81
+ method: 'POST',
82
+ headers,
83
+ body: JSON.stringify({
84
+ model: env.model,
85
+ messages: [
86
+ { role: 'system', content: 'Follow LOOP_HINT: stop repeating identical tool calls.' },
87
+ { role: 'user', content: prompt },
88
+ ],
89
+ temperature: 0,
90
+ }),
91
+ });
92
+
93
+ if (!response.ok) {
94
+ const errorText = await response.text();
95
+ throw new Error(`live LLM request failed (${response.status}): ${errorText.slice(0, 400)}`);
96
+ }
97
+
98
+ const payload = await response.json();
99
+ return String(payload?.choices?.[0]?.message?.content ?? '').trim();
100
+ }
101
+
102
+ export async function runLoopHintRepeatToolEval(options = {}) {
103
+ const fixture = options.fixture ?? buildRepeatToolCallFixture();
104
+ const deterministic = evaluateRepeatToolCallLoopDeterministic(fixture);
105
+
106
+ if (!deterministic.ok) {
107
+ return {
108
+ schema: LOOP_HINT_REPEAT_TOOL_EVAL_SCHEMA,
109
+ ok: false,
110
+ failureClass: 'code_failure',
111
+ deterministic,
112
+ live: null,
113
+ };
114
+ }
115
+
116
+ const env = resolveOpenAiProviderEnv(options);
117
+ if (env.liveEnabled !== true && options.requireLive !== false) {
118
+ return {
119
+ schema: LOOP_HINT_REPEAT_TOOL_EVAL_SCHEMA,
120
+ ok: true,
121
+ failureClass: 'skipped',
122
+ reason: 'IOHASC_E2E_REAL_LLM is not set; deterministic LOOP_HINT guard verified',
123
+ deterministic,
124
+ live: null,
125
+ rubric: {
126
+ deterministic: 'same tool+args >= threshold → loopAborted + LOOP_HINT text',
127
+ liveOptional: 'model acknowledges LOOP_HINT and does not repeat identical tool call',
128
+ },
129
+ };
130
+ }
131
+
132
+ const loopHintText = formatAgentToolDuplicateInvocationLoopHint(fixture.toolName, deterministic.lastRecord.loopStreak);
133
+ const prompt = buildLoopHintPrompt(loopHintText);
134
+ const responseText = await fetchLoopHintModelResponse(prompt, options);
135
+ const live = evaluateLoopHintModelResponse(responseText);
136
+
137
+ return {
138
+ schema: LOOP_HINT_REPEAT_TOOL_EVAL_SCHEMA,
139
+ ok: live.ok,
140
+ failureClass: live.ok ? null : 'model_failure',
141
+ deterministic,
142
+ live: {
143
+ ...live,
144
+ responsePreview: responseText.slice(0, 500),
145
+ model: env.model,
146
+ endpoint: env.baseUrl,
147
+ },
148
+ rubric: {
149
+ deterministic: 'same tool+args >= threshold → loopAborted + LOOP_HINT text',
150
+ liveOptional: 'model acknowledges LOOP_HINT and does not repeat identical tool call',
151
+ },
152
+ };
153
+ }