@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,761 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
|
|
3
|
+
import { buildEpicWorkScopeSlice } from '../../../src/epicWorkScope.mjs';
|
|
4
|
+
import {
|
|
5
|
+
appendWorkItemAtomToIntentTree,
|
|
6
|
+
persistWorkItemUpdateToRepo,
|
|
7
|
+
persistWorkItemUpdatesToRepo,
|
|
8
|
+
readWorkItemAtomFromRepo,
|
|
9
|
+
readWorkItemsFromRepo,
|
|
10
|
+
} from '../../../src/intentTreeWorkItems.mjs';
|
|
11
|
+
import {
|
|
12
|
+
buildSnapshot,
|
|
13
|
+
claimNext,
|
|
14
|
+
claimWorkItemWithLease,
|
|
15
|
+
recordEvidence,
|
|
16
|
+
transitionStatus,
|
|
17
|
+
} from '../../../src/workGraphRuntime.mjs';
|
|
18
|
+
import { transitionWorkItemWithEpicCascade } from '../../../src/workItemEpicCascade.mjs';
|
|
19
|
+
import { executeSemanticSearchFromRepo } from '../../../src/semanticSearchWorkflow.mjs';
|
|
20
|
+
import {
|
|
21
|
+
buildWorkItemCreateAnalysisDecision,
|
|
22
|
+
} from '../../../src/workItemCreateAnalysis.mjs';
|
|
23
|
+
import { formatStepAtomDraft } from '../../../src/stepAtomFormatter.mjs';
|
|
24
|
+
import { buildArchitectureSnapshot } from '../../../src/architectureSnapshot.mjs';
|
|
25
|
+
import { buildIntentHierarchySnapshot } from '../../../src/intentHierarchy.mjs';
|
|
26
|
+
import { buildOperatorShellSnapshotV2 } from '../../../src/operatorShellProjection.mjs';
|
|
27
|
+
import { buildGraphRagContextForWorkerInput } from '../../../src/graphRagContextSlice.mjs';
|
|
28
|
+
import { buildEvidenceReadModelForTask, buildEvidenceReadModelFromItems } from '../../../src/evidenceReadModel.mjs';
|
|
29
|
+
import {
|
|
30
|
+
buildMemoryRecordCandidatesFromItems,
|
|
31
|
+
mergeMemoryJournalWithCandidates,
|
|
32
|
+
readMemoryRecordJournal,
|
|
33
|
+
} from '../../../src/memoryRecordWriter.mjs';
|
|
34
|
+
import { buildPvrgTaskScopeSlice } from '../../../src/pvrgTaskScope.mjs';
|
|
35
|
+
import { buildUnifiedLinkageProjectionV1 } from '../../../src/unifiedLinkageProjection.mjs';
|
|
36
|
+
import { buildPhasePromoteReadyQueue } from '../../../src/workGraphPhasePromoteReadyQueue.mjs';
|
|
37
|
+
import {
|
|
38
|
+
buildStepGraphProjectionFromRepo,
|
|
39
|
+
buildStepGraphSliceFromRepo,
|
|
40
|
+
} from '../../../src/stepGraphSlice.mjs';
|
|
41
|
+
import {
|
|
42
|
+
buildWorkItemPipelineView,
|
|
43
|
+
recordWorkItemAnalysis,
|
|
44
|
+
recordWorkItemDecision,
|
|
45
|
+
} from '../../../src/workItemDecisionPipeline.mjs';
|
|
46
|
+
import {
|
|
47
|
+
assertWorkItemExecutionAllowed,
|
|
48
|
+
statusChangeRequiresExecutionGate,
|
|
49
|
+
} from '../../../src/workItemExecutionGate.mjs';
|
|
50
|
+
import {
|
|
51
|
+
attachUiReference,
|
|
52
|
+
listUiReferences,
|
|
53
|
+
} from '../../../src/workItemUiReferences.mjs';
|
|
54
|
+
|
|
55
|
+
const DONE_STATUSES = new Set(['done', 'verified']);
|
|
56
|
+
|
|
57
|
+
export function resolveWorkGraphRoot(env = process.env) {
|
|
58
|
+
const fromProject = env.WG_PROJECT_ROOT?.trim();
|
|
59
|
+
if (fromProject) {
|
|
60
|
+
return resolve(fromProject);
|
|
61
|
+
}
|
|
62
|
+
const raw = env.WORKGRAPH_ROOT?.trim();
|
|
63
|
+
return resolve(raw || process.cwd());
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function listWorkItems(args = {}, options = {}) {
|
|
67
|
+
const items = await readItems(options);
|
|
68
|
+
const status = normalizeOptional(args.status);
|
|
69
|
+
const query = normalizeOptional(args.query)?.toLowerCase();
|
|
70
|
+
const limit = clampLimit(args.limit, 50);
|
|
71
|
+
|
|
72
|
+
return items
|
|
73
|
+
.filter((item) => !status || item.status === status)
|
|
74
|
+
.filter((item) => {
|
|
75
|
+
if (!query) return true;
|
|
76
|
+
return [
|
|
77
|
+
item.id,
|
|
78
|
+
item.title,
|
|
79
|
+
item.status,
|
|
80
|
+
item.ownerRole,
|
|
81
|
+
item.department,
|
|
82
|
+
item.priority,
|
|
83
|
+
item.risk,
|
|
84
|
+
item.nextAction,
|
|
85
|
+
...(item.targetFiles ?? []),
|
|
86
|
+
].join(' ').toLowerCase().includes(query);
|
|
87
|
+
})
|
|
88
|
+
.slice(0, limit)
|
|
89
|
+
.map(toWorkItemSummary);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function getWorkItem(args = {}, options = {}) {
|
|
93
|
+
const workId = requireWorkId(args);
|
|
94
|
+
const items = await readItems(options);
|
|
95
|
+
const item = items.find((candidate) => candidate.id === workId);
|
|
96
|
+
if (!item) {
|
|
97
|
+
throw new Error(`WorkItem not found: ${workId}`);
|
|
98
|
+
}
|
|
99
|
+
return item;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function getBacklogSnapshot(_args = {}, options = {}) {
|
|
103
|
+
const items = await readItems(options);
|
|
104
|
+
return buildSnapshot(items);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export async function getCurrentCycle(_args = {}, options = {}) {
|
|
108
|
+
const items = await readItems(options);
|
|
109
|
+
const snapshot = buildSnapshot(items);
|
|
110
|
+
const phase8PlusPromoteReadyQueue = buildPhasePromoteReadyQueue(items);
|
|
111
|
+
return {
|
|
112
|
+
schema: 'workgraph.current-cycle.v1',
|
|
113
|
+
source: 'intent/index.bvc',
|
|
114
|
+
statusCounts: snapshot.statusCounts,
|
|
115
|
+
readyQueue: snapshot.readyQueue,
|
|
116
|
+
current: snapshot.items.filter((item) => ['claimed', 'doing', 'verify'].includes(item.status)),
|
|
117
|
+
backlogCount: snapshot.items.filter((item) => item.status === 'backlog').length,
|
|
118
|
+
doneCount: snapshot.items.filter((item) => DONE_STATUSES.has(item.status)).length,
|
|
119
|
+
phase8PlusPromoteReadyQueue,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export async function getPromoteReadyQueue(args = {}, options = {}) {
|
|
124
|
+
const items = await readItems(options);
|
|
125
|
+
return buildPhasePromoteReadyQueue(items, {
|
|
126
|
+
minPhase: args.minPhase,
|
|
127
|
+
limit: args.limit,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function getIntentHierarchy(_args = {}, options = {}) {
|
|
132
|
+
const items = await readItems(options);
|
|
133
|
+
return buildIntentHierarchySnapshot(items.map((item) => ({ item })));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function getArchitectureSnapshot(args = {}, options = {}) {
|
|
137
|
+
const items = await readItems(options);
|
|
138
|
+
const snapshot = buildSnapshot(items);
|
|
139
|
+
const focusBlockId = normalizeOptional(args.focusBlockId ?? args.focus_block_id);
|
|
140
|
+
return buildArchitectureSnapshot(snapshot, {
|
|
141
|
+
repoRoot: resolveRoot(options),
|
|
142
|
+
...(focusBlockId ? { focusBlockId } : {}),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export async function getUnifiedLinkage(_args = {}, options = {}) {
|
|
147
|
+
const items = await readItems(options);
|
|
148
|
+
return buildUnifiedLinkageProjectionV1(items);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export async function getEpicWorkScope(args = {}, options = {}) {
|
|
152
|
+
const epicId = requireWorkId({ workId: args.epicId ?? args.epic_id ?? args.workId ?? args.id });
|
|
153
|
+
const items = await readItems(options);
|
|
154
|
+
return buildEpicWorkScopeSlice(items, epicId);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function getPvrgTaskScope(args = {}, options = {}) {
|
|
158
|
+
const workId = requireWorkId(args);
|
|
159
|
+
const items = await readItems(options);
|
|
160
|
+
const scopeOptions = {};
|
|
161
|
+
if (Number.isInteger(args.maxNodes) && args.maxNodes > 0) {
|
|
162
|
+
scopeOptions.maxNodes = args.maxNodes;
|
|
163
|
+
}
|
|
164
|
+
if (Number.isInteger(args.maxDepth) && args.maxDepth >= 0) {
|
|
165
|
+
scopeOptions.maxDepth = args.maxDepth;
|
|
166
|
+
}
|
|
167
|
+
return buildPvrgTaskScopeSlice(items, workId, scopeOptions);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function loadMergedMemoryRecords(options = {}) {
|
|
171
|
+
const items = await readItems(options);
|
|
172
|
+
const candidates = buildMemoryRecordCandidatesFromItems(items).records;
|
|
173
|
+
const journal = await readMemoryRecordJournal({ cwd: resolveRoot(options) });
|
|
174
|
+
return {
|
|
175
|
+
items,
|
|
176
|
+
records: mergeMemoryJournalWithCandidates(candidates, journal.records),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function filterMemoryRecords(records, args = {}) {
|
|
181
|
+
const sourceWorkItem = normalizeOptional(args.sourceWorkItem ?? args.workId);
|
|
182
|
+
const type = normalizeOptional(args.type);
|
|
183
|
+
const status = normalizeOptional(args.status);
|
|
184
|
+
const query = normalizeOptional(args.query)?.toLowerCase();
|
|
185
|
+
|
|
186
|
+
return records.filter((record) => {
|
|
187
|
+
if (sourceWorkItem) {
|
|
188
|
+
const related = record.sourceWorkItem === sourceWorkItem
|
|
189
|
+
|| (record.relatedTasks ?? []).includes(sourceWorkItem);
|
|
190
|
+
if (!related) {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (type && record.type !== type) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (status && record.status !== status) {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
if (query) {
|
|
204
|
+
const haystack = [
|
|
205
|
+
record.id,
|
|
206
|
+
record.type,
|
|
207
|
+
record.summary,
|
|
208
|
+
record.sourceWorkItem,
|
|
209
|
+
...(record.relatedFiles ?? []),
|
|
210
|
+
...(record.relatedTasks ?? []),
|
|
211
|
+
].join(' ').toLowerCase();
|
|
212
|
+
if (!haystack.includes(query)) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return record.status !== 'retired';
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function filterEvidenceRecords(records, args = {}) {
|
|
222
|
+
const type = normalizeOptional(args.type);
|
|
223
|
+
const status = normalizeOptional(args.status);
|
|
224
|
+
const query = normalizeOptional(args.query)?.toLowerCase();
|
|
225
|
+
|
|
226
|
+
return records.filter((record) => {
|
|
227
|
+
if (type && record.type !== type) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (status && record.status !== status) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (query) {
|
|
236
|
+
const haystack = [record.id, record.taskId, record.type, record.summary].join(' ').toLowerCase();
|
|
237
|
+
if (!haystack.includes(query)) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return true;
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export async function getGraphRagContext(args = {}, options = {}) {
|
|
247
|
+
const workId = requireWorkId(args);
|
|
248
|
+
const { items, records } = await loadMergedMemoryRecords(options);
|
|
249
|
+
findItem(items, workId);
|
|
250
|
+
|
|
251
|
+
const graphRagOptions = {
|
|
252
|
+
memoryWorker: { memoryRecords: records },
|
|
253
|
+
memoryRecords: records,
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
if (Number.isInteger(args.maxNodes) && args.maxNodes > 0) {
|
|
257
|
+
graphRagOptions.maxNodes = args.maxNodes;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (Number.isInteger(args.maxDepth) && args.maxDepth >= 0) {
|
|
261
|
+
graphRagOptions.maxDepth = args.maxDepth;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return buildGraphRagContextForWorkerInput(items, workId, graphRagOptions);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export async function listMemoryRecords(args = {}, options = {}) {
|
|
268
|
+
const { records } = await loadMergedMemoryRecords(options);
|
|
269
|
+
const filtered = filterMemoryRecords(records, args);
|
|
270
|
+
const limit = clampLimit(args.limit, 50);
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
schema: 'memory-record-list.v1',
|
|
274
|
+
count: filtered.length,
|
|
275
|
+
records: filtered.slice(0, limit),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export async function getMemoryRecord(args = {}, options = {}) {
|
|
280
|
+
const recordId = normalizeOptional(args.recordId ?? args.id);
|
|
281
|
+
if (!recordId) {
|
|
282
|
+
throw new Error('recordId is required');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const { records } = await loadMergedMemoryRecords(options);
|
|
286
|
+
const record = records.find((entry) => entry.id === recordId);
|
|
287
|
+
if (!record) {
|
|
288
|
+
throw new Error(`MemoryRecord not found: ${recordId}`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return record;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export async function listEvidenceRecords(args = {}, options = {}) {
|
|
295
|
+
const items = await readItems(options);
|
|
296
|
+
const taskId = normalizeOptional(args.taskId ?? args.workId);
|
|
297
|
+
const model = taskId
|
|
298
|
+
? buildEvidenceReadModelForTask(items, taskId)
|
|
299
|
+
: buildEvidenceReadModelFromItems(items);
|
|
300
|
+
const filtered = filterEvidenceRecords(model.records, args);
|
|
301
|
+
const limit = clampLimit(args.limit, 50);
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
schema: 'evidence-record-list.v1',
|
|
305
|
+
count: filtered.length,
|
|
306
|
+
records: filtered.slice(0, limit),
|
|
307
|
+
compatibility: model.compatibility,
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export async function getEvidenceRecord(args = {}, options = {}) {
|
|
312
|
+
const recordId = normalizeOptional(args.recordId ?? args.id);
|
|
313
|
+
if (!recordId) {
|
|
314
|
+
throw new Error('recordId is required');
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const items = await readItems(options);
|
|
318
|
+
const model = buildEvidenceReadModelFromItems(items);
|
|
319
|
+
const record = model.records.find((entry) => entry.id === recordId);
|
|
320
|
+
if (!record) {
|
|
321
|
+
throw new Error(`EvidenceRecord not found: ${recordId}`);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return record;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export async function getOperatorShellSnapshot(_args = {}, options = {}) {
|
|
328
|
+
const items = await readItems(options);
|
|
329
|
+
const snapshot = buildSnapshot(items);
|
|
330
|
+
return buildOperatorShellSnapshotV2(snapshot);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export async function getStepGraphProjection(args = {}, options = {}) {
|
|
334
|
+
return buildStepGraphProjectionFromRepo({
|
|
335
|
+
cwd: resolveRoot(options),
|
|
336
|
+
maxNodes: args.maxNodes,
|
|
337
|
+
roots: normalizeOptional(args.roots)?.split(',').map((entry) => entry.trim()).filter(Boolean),
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export async function getStepGraphSlice(args = {}, options = {}) {
|
|
342
|
+
const seedStepName = normalizeOptional(args.seedStepName ?? args.stepName);
|
|
343
|
+
const seedPath = normalizeOptional(args.seedPath ?? args.logicalPath);
|
|
344
|
+
const seedNodeId = normalizeOptional(args.seedNodeId ?? args.nodeId);
|
|
345
|
+
|
|
346
|
+
if (!seedStepName && !seedPath && !seedNodeId) {
|
|
347
|
+
throw new Error('seedStepName, seedPath or seedNodeId is required');
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return buildStepGraphSliceFromRepo({
|
|
351
|
+
cwd: resolveRoot(options),
|
|
352
|
+
...(seedStepName ? { seedStepName } : {}),
|
|
353
|
+
...(seedPath ? { seedPath } : {}),
|
|
354
|
+
...(seedNodeId ? { seedNodeId } : {}),
|
|
355
|
+
maxNodes: args.maxNodes,
|
|
356
|
+
maxDepth: args.maxDepth,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export async function updateWorkItemStatus(args = {}, options = {}) {
|
|
361
|
+
const workId = requireWorkId(args);
|
|
362
|
+
const status = String(args.status ?? '').trim();
|
|
363
|
+
if (!status) {
|
|
364
|
+
throw new Error('status is required');
|
|
365
|
+
}
|
|
366
|
+
const items = await readItems(options);
|
|
367
|
+
const item = findItem(items, workId);
|
|
368
|
+
if (statusChangeRequiresExecutionGate(status)) {
|
|
369
|
+
assertWorkItemExecutionAllowed(item);
|
|
370
|
+
}
|
|
371
|
+
const updated = transitionWorkItemWithEpicCascade(items, item, status, {
|
|
372
|
+
reason: args.reason,
|
|
373
|
+
blocker: args.reason,
|
|
374
|
+
evidence: args.evidence,
|
|
375
|
+
});
|
|
376
|
+
const persisted = await persistWorkItemUpdatesToRepo(updated.updatedItems, {
|
|
377
|
+
cwd: resolveRoot(options),
|
|
378
|
+
});
|
|
379
|
+
const primary = updated.updatedItems.find((entry) => entry.id === workId) ?? updated.updatedItems.at(-1);
|
|
380
|
+
return {
|
|
381
|
+
ok: true,
|
|
382
|
+
workId,
|
|
383
|
+
previousStatus: item.status,
|
|
384
|
+
newStatus: primary?.status ?? status,
|
|
385
|
+
cascadedChildIds: updated.cascadedChildIds,
|
|
386
|
+
paths: persisted.map((entry) => entry.path),
|
|
387
|
+
path: persisted.find((entry) => entry.workId === workId)?.path ?? persisted.at(-1)?.path,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export async function addWorkItemEvidence(args = {}, options = {}) {
|
|
392
|
+
const workId = requireWorkId(args);
|
|
393
|
+
const evidence = String(args.evidence ?? '').trim();
|
|
394
|
+
if (!evidence) {
|
|
395
|
+
throw new Error('evidence is required');
|
|
396
|
+
}
|
|
397
|
+
const items = await readItems(options);
|
|
398
|
+
const item = findItem(items, workId);
|
|
399
|
+
const updated = recordEvidence(item, evidence);
|
|
400
|
+
const persisted = await persistWorkItemUpdateToRepo({
|
|
401
|
+
cwd: resolveRoot(options),
|
|
402
|
+
item: updated,
|
|
403
|
+
});
|
|
404
|
+
return {
|
|
405
|
+
ok: true,
|
|
406
|
+
workId,
|
|
407
|
+
evidenceCount: updated.evidence.length,
|
|
408
|
+
path: persisted.path,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export async function claimWorkItem(args = {}, options = {}) {
|
|
413
|
+
const items = await readItems(options);
|
|
414
|
+
const workId = normalizeOptional(args.workId);
|
|
415
|
+
const item = workId ? findItem(items, workId) : claimNext(items);
|
|
416
|
+
if (!item) {
|
|
417
|
+
throw new Error('No claimable WorkItem found');
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
assertWorkItemExecutionAllowed(item);
|
|
421
|
+
|
|
422
|
+
const claimRunId = normalizeOptional(args.claimRunId ?? args.claim_run_id)
|
|
423
|
+
?? `mcp-claim-${item.id}-${Date.now()}`;
|
|
424
|
+
const claimResult = claimWorkItemWithLease(item, {
|
|
425
|
+
claimRunId,
|
|
426
|
+
targetStatus: 'doing',
|
|
427
|
+
evidence: args.evidence ?? `claim: ${item.id} via WorkGraph MCP`,
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
if (!claimResult.ok) {
|
|
431
|
+
if (claimResult.error === 'claim_lease_active') {
|
|
432
|
+
throw new Error(`Claim lease active for ${item.id}${claimResult.claimedBy ? ` (claimed_by=${claimResult.claimedBy})` : ''}`);
|
|
433
|
+
}
|
|
434
|
+
throw new Error(`WorkItem is not claimable: ${item.id} (${item.status})`);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (claimResult.idempotent) {
|
|
438
|
+
return {
|
|
439
|
+
ok: true,
|
|
440
|
+
workId: item.id,
|
|
441
|
+
previousStatus: claimResult.previousStatus,
|
|
442
|
+
newStatus: claimResult.newStatus,
|
|
443
|
+
idempotent: true,
|
|
444
|
+
claimRunId: claimResult.claimRunId,
|
|
445
|
+
leaseUntil: claimResult.leaseUntil,
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const persisted = await persistWorkItemUpdateToRepo({
|
|
450
|
+
cwd: resolveRoot(options),
|
|
451
|
+
item: claimResult.item,
|
|
452
|
+
});
|
|
453
|
+
return {
|
|
454
|
+
ok: true,
|
|
455
|
+
workId: item.id,
|
|
456
|
+
previousStatus: claimResult.previousStatus,
|
|
457
|
+
newStatus: claimResult.newStatus,
|
|
458
|
+
claimRunId: claimResult.claimRunId,
|
|
459
|
+
leaseUntil: claimResult.leaseUntil,
|
|
460
|
+
path: persisted.path,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export async function completeWorkItem(args = {}, options = {}) {
|
|
465
|
+
const workId = requireWorkId(args);
|
|
466
|
+
const evidence = String(args.evidence ?? '').trim();
|
|
467
|
+
if (!evidence) {
|
|
468
|
+
throw new Error('evidence is required to complete a WorkItem');
|
|
469
|
+
}
|
|
470
|
+
const items = await readItems(options);
|
|
471
|
+
const item = findItem(items, workId);
|
|
472
|
+
const updated = transitionWorkItemWithEpicCascade(items, item, 'done', { evidence });
|
|
473
|
+
const persisted = await persistWorkItemUpdatesToRepo(updated.updatedItems, {
|
|
474
|
+
cwd: resolveRoot(options),
|
|
475
|
+
});
|
|
476
|
+
const primary = updated.updatedItems.find((entry) => entry.id === workId) ?? updated.updatedItems.at(-1);
|
|
477
|
+
return {
|
|
478
|
+
ok: true,
|
|
479
|
+
workId,
|
|
480
|
+
previousStatus: item.status,
|
|
481
|
+
newStatus: primary?.status ?? 'done',
|
|
482
|
+
cascadedChildIds: updated.cascadedChildIds,
|
|
483
|
+
paths: persisted.map((entry) => entry.path),
|
|
484
|
+
path: persisted.find((entry) => entry.workId === workId)?.path ?? persisted.at(-1)?.path,
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export async function readWorkGraphResource(uri, options = {}) {
|
|
489
|
+
const value = String(uri ?? '').trim();
|
|
490
|
+
if (value === 'workgraph://backlog') {
|
|
491
|
+
return getBacklogSnapshot({}, options);
|
|
492
|
+
}
|
|
493
|
+
if (value === 'workgraph://cycle/current') {
|
|
494
|
+
return getCurrentCycle({}, options);
|
|
495
|
+
}
|
|
496
|
+
if (value === 'workgraph://intent/hierarchy') {
|
|
497
|
+
return getIntentHierarchy({}, options);
|
|
498
|
+
}
|
|
499
|
+
if (value === 'workgraph://architecture/snapshot') {
|
|
500
|
+
return getArchitectureSnapshot({}, options);
|
|
501
|
+
}
|
|
502
|
+
if (value === 'workgraph://linkage/projection') {
|
|
503
|
+
return getUnifiedLinkage({}, options);
|
|
504
|
+
}
|
|
505
|
+
if (value === 'workgraph://step-graph/projection') {
|
|
506
|
+
return getStepGraphProjection({}, options);
|
|
507
|
+
}
|
|
508
|
+
const stepGraphSliceMatch = value.match(/^workgraph:\/\/step-graph\/slice\/(.+)$/u);
|
|
509
|
+
if (stepGraphSliceMatch) {
|
|
510
|
+
const seed = decodeURIComponent(stepGraphSliceMatch[1]);
|
|
511
|
+
if (seed.includes('\u001f')) {
|
|
512
|
+
return getStepGraphSlice({ seedNodeId: seed }, options);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
return getStepGraphSlice({ seedStepName: seed }, options);
|
|
516
|
+
}
|
|
517
|
+
const epicScopeMatch = value.match(/^workgraph:\/\/epic\/(.+)\/scope$/u);
|
|
518
|
+
if (epicScopeMatch) {
|
|
519
|
+
return getEpicWorkScope({ epicId: decodeURIComponent(epicScopeMatch[1]) }, options);
|
|
520
|
+
}
|
|
521
|
+
const scopeMatch = value.match(/^workgraph:\/\/pvrg\/scope\/(.+)$/u);
|
|
522
|
+
if (scopeMatch) {
|
|
523
|
+
return getPvrgTaskScope({ workId: decodeURIComponent(scopeMatch[1]) }, options);
|
|
524
|
+
}
|
|
525
|
+
const graphRagMatch = value.match(/^workgraph:\/\/pvrg\/graph-rag\/(.+)$/u);
|
|
526
|
+
if (graphRagMatch) {
|
|
527
|
+
return getGraphRagContext({ workId: decodeURIComponent(graphRagMatch[1]) }, options);
|
|
528
|
+
}
|
|
529
|
+
if (value === 'workgraph://memory/records') {
|
|
530
|
+
return listMemoryRecords({ limit: 200 }, options);
|
|
531
|
+
}
|
|
532
|
+
const memoryRecordMatch = value.match(/^workgraph:\/\/memory\/record\/(.+)$/u);
|
|
533
|
+
if (memoryRecordMatch) {
|
|
534
|
+
return getMemoryRecord({ recordId: decodeURIComponent(memoryRecordMatch[1]) }, options);
|
|
535
|
+
}
|
|
536
|
+
if (value === 'workgraph://evidence/records') {
|
|
537
|
+
return listEvidenceRecords({ limit: 200 }, options);
|
|
538
|
+
}
|
|
539
|
+
const evidenceRecordMatch = value.match(/^workgraph:\/\/evidence\/record\/(.+)$/u);
|
|
540
|
+
if (evidenceRecordMatch) {
|
|
541
|
+
return getEvidenceRecord({ recordId: decodeURIComponent(evidenceRecordMatch[1]) }, options);
|
|
542
|
+
}
|
|
543
|
+
const itemMatch = value.match(/^workgraph:\/\/item\/(.+)$/u);
|
|
544
|
+
if (itemMatch) {
|
|
545
|
+
return getWorkItem({ workId: decodeURIComponent(itemMatch[1]) }, options);
|
|
546
|
+
}
|
|
547
|
+
throw new Error(`Unsupported WorkGraph resource URI: ${value}`);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
export async function readWorkItemAtomResource(workId, options = {}) {
|
|
551
|
+
const atom = await readWorkItemAtomFromRepo(workId, { cwd: resolveRoot(options) });
|
|
552
|
+
return atom.atomText;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
export async function semanticSearch(args = {}, options = {}) {
|
|
556
|
+
const query = String(args.query ?? args.q ?? '').trim();
|
|
557
|
+
if (!query) {
|
|
558
|
+
throw new Error('query is required');
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const limit = clampLimit(args.limit, 12);
|
|
562
|
+
const mode = String(args.mode ?? '').trim() || undefined;
|
|
563
|
+
return executeSemanticSearchFromRepo({
|
|
564
|
+
cwd: resolveRoot(options),
|
|
565
|
+
query,
|
|
566
|
+
limit,
|
|
567
|
+
...(mode ? { mode } : {}),
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function atomNameFromWorkId(workId) {
|
|
572
|
+
return `Задача_${String(workId).replace(/-/gu, '_')}`;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function normalizeTextList(value, fallback = []) {
|
|
576
|
+
if (Array.isArray(value)) {
|
|
577
|
+
return value.map((entry) => String(entry).trim()).filter(Boolean);
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const text = String(value ?? '').trim();
|
|
581
|
+
if (text === '') {
|
|
582
|
+
return [...fallback];
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return text.split(/\r?\n/u).map((line) => line.trim()).filter(Boolean);
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
export async function createWorkItem(args = {}, options = {}) {
|
|
589
|
+
const workId = normalizeOptional(args.workId ?? args.id);
|
|
590
|
+
if (!workId) {
|
|
591
|
+
throw new Error('workId is required');
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const title = String(args.title ?? workId).trim();
|
|
595
|
+
const department = String(args.department ?? 'agent-platform').trim();
|
|
596
|
+
const ownerRole = String(args.ownerRole ?? args.owner_role ?? 'integration_architect').trim();
|
|
597
|
+
const priority = String(args.priority ?? 'medium').trim();
|
|
598
|
+
const risk = String(args.risk ?? 'medium').trim();
|
|
599
|
+
const status = String(args.status ?? 'backlog').trim();
|
|
600
|
+
const nextAction = String(args.nextAction ?? args.next_action ?? 'просмотреть и перевести в ready').trim();
|
|
601
|
+
const dependsOn = normalizeTextList(args.dependsOn ?? args.depends_on);
|
|
602
|
+
const targetFiles = normalizeTextList(args.targetFiles ?? args.target_files);
|
|
603
|
+
const parentId = normalizeOptional(args.parentId ?? args.parent_id);
|
|
604
|
+
const itemKind = normalizeOptional(args.itemKind ?? args.item_kind);
|
|
605
|
+
const intentQuestionId = normalizeOptional(args.intentQuestionId ?? args.intent_question_id);
|
|
606
|
+
const intentOptionId = normalizeOptional(args.intentOptionId ?? args.intent_option_id);
|
|
607
|
+
const intentDecisionId = normalizeOptional(args.intentDecisionId ?? args.intent_decision_id);
|
|
608
|
+
const checks = normalizeTextList(args.checks, [
|
|
609
|
+
'Atom WorkItem проходит StepAtomDraft validation',
|
|
610
|
+
'Файл intent/**/*.work.bvc (новые items; legacy .work.bvc read-only)',
|
|
611
|
+
'intent/index.bvc актуален',
|
|
612
|
+
'Свидетельства записаны перед переводом в done',
|
|
613
|
+
]);
|
|
614
|
+
|
|
615
|
+
const { analysis, decision, pipelineLabels } = buildWorkItemCreateAnalysisDecision(args);
|
|
616
|
+
|
|
617
|
+
const draft = {
|
|
618
|
+
name: atomNameFromWorkId(workId),
|
|
619
|
+
profile: 'work_item',
|
|
620
|
+
basis: normalizeTextList(args.basis, [`WorkItem ${workId} создан через WorkGraph MCP.`]),
|
|
621
|
+
vector: normalizeTextList(args.vector, ['Выполнить задачу с evidence-driven workflow.']),
|
|
622
|
+
goal: normalizeTextList(args.goal, [title]),
|
|
623
|
+
...(analysis.length > 0 ? { analysis } : {}),
|
|
624
|
+
...(decision.length > 0 ? { decision } : {}),
|
|
625
|
+
checks,
|
|
626
|
+
labels: {
|
|
627
|
+
'atom.profile': 'work_item',
|
|
628
|
+
'work.id': workId,
|
|
629
|
+
'work.title': title,
|
|
630
|
+
'work.status': status,
|
|
631
|
+
'work.owner_role': ownerRole,
|
|
632
|
+
'work.department': department,
|
|
633
|
+
'work.priority': priority,
|
|
634
|
+
'work.risk': risk,
|
|
635
|
+
'work.next_action': nextAction,
|
|
636
|
+
...(dependsOn.length > 0 ? { 'work.depends_on': dependsOn.join(', ') } : {}),
|
|
637
|
+
...(targetFiles.length > 0 ? { 'work.target_files': targetFiles.join(', ') } : {}),
|
|
638
|
+
...(parentId ? { 'work.parent_id': parentId } : {}),
|
|
639
|
+
...(itemKind ? { 'work.item_kind': itemKind } : {}),
|
|
640
|
+
...(intentQuestionId ? { 'intent.question_id': intentQuestionId } : {}),
|
|
641
|
+
...(intentOptionId ? { 'intent.option_id': intentOptionId } : {}),
|
|
642
|
+
...(intentDecisionId ? { 'intent.decision_id': intentDecisionId } : {}),
|
|
643
|
+
'trace.status': 'pending',
|
|
644
|
+
'migration.strategy': String(args.migrationStrategy ?? 'rebuild').trim(),
|
|
645
|
+
...(args.intakeSourceKind ? { 'intake.source_kind': String(args.intakeSourceKind).trim() } : {}),
|
|
646
|
+
...pipelineLabels,
|
|
647
|
+
},
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
const atomText = formatStepAtomDraft(draft);
|
|
651
|
+
const persisted = await appendWorkItemAtomToIntentTree(atomText, {
|
|
652
|
+
cwd: resolveRoot(options),
|
|
653
|
+
path: args.path,
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
return {
|
|
657
|
+
ok: true,
|
|
658
|
+
workId,
|
|
659
|
+
status,
|
|
660
|
+
path: persisted.path,
|
|
661
|
+
indexPath: persisted.indexPath,
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export async function getWorkItemPipeline(args = {}, options = {}) {
|
|
666
|
+
const workId = requireWorkId(args);
|
|
667
|
+
const item = findItem(await readItems(options), workId);
|
|
668
|
+
return buildWorkItemPipelineView(item);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
export async function recordWorkItemAnalysisFromMcp(args = {}, options = {}) {
|
|
672
|
+
return recordWorkItemAnalysis({
|
|
673
|
+
cwd: resolveRoot(options),
|
|
674
|
+
workId: requireWorkId(args),
|
|
675
|
+
analysis: args.analysis,
|
|
676
|
+
analysisSource: args.analysisSource ?? 'cursor-mcp',
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
export async function recordWorkItemDecisionFromMcp(args = {}, options = {}) {
|
|
681
|
+
const verdict = String(args.verdict ?? '').trim();
|
|
682
|
+
if (verdict === '') {
|
|
683
|
+
throw new Error('verdict is required (useful | harmful | defer)');
|
|
684
|
+
}
|
|
685
|
+
return recordWorkItemDecision({
|
|
686
|
+
cwd: resolveRoot(options),
|
|
687
|
+
workId: requireWorkId(args),
|
|
688
|
+
verdict,
|
|
689
|
+
notes: args.notes ?? args.decision,
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
export async function attachWorkItemUiReference(args = {}, options = {}) {
|
|
694
|
+
return attachUiReference({
|
|
695
|
+
cwd: resolveRoot(options),
|
|
696
|
+
workId: requireWorkId(args),
|
|
697
|
+
filename: args.filename,
|
|
698
|
+
contentBase64: args.contentBase64,
|
|
699
|
+
caption: args.caption,
|
|
700
|
+
force: args.force === true,
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
export async function listWorkItemUiReferences(args = {}, options = {}) {
|
|
705
|
+
return listUiReferences({
|
|
706
|
+
cwd: resolveRoot(options),
|
|
707
|
+
workId: requireWorkId(args),
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
async function readItems(options) {
|
|
712
|
+
return readWorkItemsFromRepo({ cwd: resolveRoot(options) });
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function findItem(items, workId) {
|
|
716
|
+
const item = items.find((candidate) => candidate.id === workId);
|
|
717
|
+
if (!item) {
|
|
718
|
+
throw new Error(`WorkItem not found: ${workId}`);
|
|
719
|
+
}
|
|
720
|
+
return item;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
function requireWorkId(args) {
|
|
724
|
+
const workId = normalizeOptional(args.workId ?? args.id);
|
|
725
|
+
if (!workId) {
|
|
726
|
+
throw new Error('workId is required');
|
|
727
|
+
}
|
|
728
|
+
return workId;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function normalizeOptional(value) {
|
|
732
|
+
const text = String(value ?? '').trim();
|
|
733
|
+
return text === '' ? null : text;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function clampLimit(value, fallback) {
|
|
737
|
+
const number = Number(value ?? fallback);
|
|
738
|
+
if (!Number.isFinite(number)) return fallback;
|
|
739
|
+
return Math.max(1, Math.min(200, Math.floor(number)));
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
function resolveRoot(options) {
|
|
743
|
+
return resolve(options.root ?? resolveWorkGraphRoot(options.env));
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
function toWorkItemSummary(item) {
|
|
747
|
+
return {
|
|
748
|
+
id: item.id,
|
|
749
|
+
title: item.title,
|
|
750
|
+
status: item.status,
|
|
751
|
+
ownerRole: item.ownerRole,
|
|
752
|
+
department: item.department,
|
|
753
|
+
priority: item.priority,
|
|
754
|
+
risk: item.risk,
|
|
755
|
+
dependsOn: item.dependsOn,
|
|
756
|
+
targetFiles: item.targetFiles,
|
|
757
|
+
nextAction: item.nextAction,
|
|
758
|
+
evidenceCount: item.evidence?.length ?? 0,
|
|
759
|
+
blocker: item.blocker,
|
|
760
|
+
};
|
|
761
|
+
}
|