@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,195 @@
|
|
|
1
|
+
import { readWorkItemsFromRepo } from './intentTreeWorkItems.mjs';
|
|
2
|
+
import { readWorkerRunJournal } from './agentWorkerLiveLoop.mjs';
|
|
3
|
+
import { readDaemonAuditJournal } from './workGraphDaemonTick.mjs';
|
|
4
|
+
import { readHomeSnapshot } from './homeSnapshotApi.mjs';
|
|
5
|
+
import {
|
|
6
|
+
markInboxEventsRead,
|
|
7
|
+
readInboxEventsResponse,
|
|
8
|
+
} from './inboxEventStream.mjs';
|
|
9
|
+
|
|
10
|
+
async function loadInboxSources(options = {}) {
|
|
11
|
+
const cwd = options.cwd ?? process.cwd();
|
|
12
|
+
const [workerRuns, daemonAudit, items] = await Promise.all([
|
|
13
|
+
readWorkerRunJournal({ cwd, journalPath: options.journalPath }),
|
|
14
|
+
readDaemonAuditJournal({ cwd, auditPath: options.auditPath }),
|
|
15
|
+
readWorkItemsFromRepo({ ...options, cwd }),
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
workerRuns,
|
|
20
|
+
daemonAudit,
|
|
21
|
+
verifyItems: items.filter((item) => item.status === 'verify'),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function handleHomeSnapshotRequest(options = {}) {
|
|
26
|
+
return readHomeSnapshot(options);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function handleInboxEventsRequest(options = {}) {
|
|
30
|
+
const cwd = options.cwd ?? process.cwd();
|
|
31
|
+
const sources = await loadInboxSources(options);
|
|
32
|
+
const analyticsRecords = [];
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const { readFile } = await import('node:fs/promises');
|
|
36
|
+
const { join } = await import('node:path');
|
|
37
|
+
const text = await readFile(join(cwd, 'work/analytics-records.jsonl'), 'utf8');
|
|
38
|
+
for (const line of text.split(/\r?\n/).filter(Boolean).slice(-20)) {
|
|
39
|
+
try {
|
|
40
|
+
const envelope = JSON.parse(line);
|
|
41
|
+
analyticsRecords.unshift(envelope.record ?? envelope);
|
|
42
|
+
} catch {
|
|
43
|
+
// skip
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
} catch {
|
|
47
|
+
// optional
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return readInboxEventsResponse({
|
|
51
|
+
cwd,
|
|
52
|
+
limit: Number(options.limit) || 50,
|
|
53
|
+
sources: {
|
|
54
|
+
workerRuns: sources.workerRuns.slice(-30).reverse(),
|
|
55
|
+
daemonAudit: sources.daemonAudit.slice(-20).reverse(),
|
|
56
|
+
analyticsRecords,
|
|
57
|
+
verifyItems: sources.verifyItems.slice(0, 10),
|
|
58
|
+
},
|
|
59
|
+
statePath: options.inboxStatePath,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function handleInboxEventsReadRequest(body, options = {}) {
|
|
64
|
+
const parsed = typeof body === 'string' ? JSON.parse(body) : body;
|
|
65
|
+
const eventIds = Array.isArray(parsed?.eventIds) ? parsed.eventIds : [];
|
|
66
|
+
const state = await markInboxEventsRead(eventIds, options);
|
|
67
|
+
const inbox = await handleInboxEventsRequest(options);
|
|
68
|
+
return { ok: true, readState: state, unreadCount: inbox.unreadCount };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const MISSION_CONTROL_CSS = `
|
|
72
|
+
.layout-root.has-agent-dock .content { margin-right: var(--agent-dock-width, 360px); }
|
|
73
|
+
#home-view { display: grid; gap: 16px; color: var(--text); }
|
|
74
|
+
.home-mission-control { display: grid; gap: 16px; }
|
|
75
|
+
.home-kpi-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 10px; }
|
|
76
|
+
.home-kpi-tile {
|
|
77
|
+
background: var(--panel);
|
|
78
|
+
border: 1px solid var(--border);
|
|
79
|
+
border-radius: 8px;
|
|
80
|
+
padding: 12px;
|
|
81
|
+
box-shadow: var(--shadow-card);
|
|
82
|
+
color: var(--text);
|
|
83
|
+
}
|
|
84
|
+
.home-kpi-tile .label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
|
|
85
|
+
.home-kpi-tile .value { font-size: 20px; font-weight: 600; margin-top: 4px; color: var(--text); }
|
|
86
|
+
.home-section {
|
|
87
|
+
background: var(--panel);
|
|
88
|
+
border: 1px solid var(--border);
|
|
89
|
+
border-radius: 8px;
|
|
90
|
+
padding: 12px;
|
|
91
|
+
box-shadow: var(--shadow-card);
|
|
92
|
+
color: var(--text);
|
|
93
|
+
}
|
|
94
|
+
.home-section h3 { margin: 0 0 8px; font-size: 14px; font-weight: 600; color: var(--text); }
|
|
95
|
+
.home-list { list-style: none; margin: 0; padding: 0; }
|
|
96
|
+
.home-list li { padding: 8px 0; border-bottom: 1px solid var(--border); cursor: pointer; color: var(--text); }
|
|
97
|
+
.home-list li:last-child { border-bottom: none; }
|
|
98
|
+
.home-list li .muted { color: var(--muted); font-size: var(--font-size-sm, 14px); margin-top: 2px; }
|
|
99
|
+
.home-list li.empty {
|
|
100
|
+
cursor: default;
|
|
101
|
+
color: var(--muted);
|
|
102
|
+
border: 1px dashed var(--border);
|
|
103
|
+
border-radius: 6px;
|
|
104
|
+
padding: 16px;
|
|
105
|
+
text-align: center;
|
|
106
|
+
background: var(--panel-2);
|
|
107
|
+
}
|
|
108
|
+
#agent-run-dock {
|
|
109
|
+
position: fixed; top: 0; right: 0;
|
|
110
|
+
width: var(--agent-dock-width, 360px); height: 100vh;
|
|
111
|
+
background: var(--panel);
|
|
112
|
+
border-left: 1px solid var(--border);
|
|
113
|
+
display: flex; flex-direction: column; z-index: 20;
|
|
114
|
+
transform: translateX(100%); transition: transform .2s ease;
|
|
115
|
+
color: var(--text);
|
|
116
|
+
}
|
|
117
|
+
#agent-run-dock.is-open { transform: translateX(0); }
|
|
118
|
+
.agent-dock-header { display: flex; justify-content: space-between; align-items: center; padding: 10px 12px; border-bottom: 1px solid var(--border); }
|
|
119
|
+
.agent-dock-close { background: transparent; border: 0; color: var(--muted); cursor: pointer; font: inherit; font-size: 18px; line-height: 1; padding: 4px 8px; }
|
|
120
|
+
.agent-dock-close:hover { color: var(--text); }
|
|
121
|
+
.agent-scope-panel {
|
|
122
|
+
border-bottom: 1px solid var(--border);
|
|
123
|
+
max-height: 42vh;
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
min-height: 0;
|
|
127
|
+
background: var(--panel-2);
|
|
128
|
+
}
|
|
129
|
+
.agent-scope-panel-header {
|
|
130
|
+
padding: 8px 12px;
|
|
131
|
+
font-size: 11px;
|
|
132
|
+
font-weight: 600;
|
|
133
|
+
text-transform: uppercase;
|
|
134
|
+
letter-spacing: .04em;
|
|
135
|
+
color: var(--muted);
|
|
136
|
+
border-bottom: 1px solid var(--border);
|
|
137
|
+
}
|
|
138
|
+
.agent-scope-summary { padding: 8px 12px 4px; font-size: 13px; }
|
|
139
|
+
.agent-scope-summary .muted { font-size: 11px; margin-top: 2px; color: var(--muted); }
|
|
140
|
+
.agent-scope-list {
|
|
141
|
+
list-style: none;
|
|
142
|
+
margin: 0;
|
|
143
|
+
padding: 0 0 8px;
|
|
144
|
+
overflow: auto;
|
|
145
|
+
flex: 1;
|
|
146
|
+
min-height: 0;
|
|
147
|
+
}
|
|
148
|
+
.agent-scope-list li {
|
|
149
|
+
display: grid;
|
|
150
|
+
grid-template-columns: auto 1fr auto;
|
|
151
|
+
gap: 6px;
|
|
152
|
+
align-items: baseline;
|
|
153
|
+
padding: 6px 12px;
|
|
154
|
+
cursor: pointer;
|
|
155
|
+
font-size: 12px;
|
|
156
|
+
border-bottom: 1px solid var(--border);
|
|
157
|
+
color: var(--text);
|
|
158
|
+
}
|
|
159
|
+
.agent-scope-list li:last-child { border-bottom: none; }
|
|
160
|
+
.agent-scope-list li:hover { background: var(--accent-soft); }
|
|
161
|
+
body[data-theme="dark"] .agent-scope-list li:hover { background: rgba(0, 102, 255, 0.12); }
|
|
162
|
+
.agent-scope-list li.empty { cursor: default; color: var(--muted); justify-content: center; display: block; text-align: center; }
|
|
163
|
+
.agent-scope-mark { font-family: ui-monospace, monospace; color: var(--muted); }
|
|
164
|
+
.agent-scope-status { font-size: 11px; }
|
|
165
|
+
.agent-scope-empty { padding: 12px; color: var(--muted); font-size: 12px; }
|
|
166
|
+
.agent-dock-log-label {
|
|
167
|
+
padding: 6px 12px;
|
|
168
|
+
font-size: 11px;
|
|
169
|
+
font-weight: 600;
|
|
170
|
+
text-transform: uppercase;
|
|
171
|
+
letter-spacing: .04em;
|
|
172
|
+
color: var(--muted);
|
|
173
|
+
border-bottom: 1px solid var(--border);
|
|
174
|
+
}
|
|
175
|
+
.agent-dock-body { flex: 1; overflow: auto; padding: 10px 12px; font-family: ui-monospace, monospace; font-size: 12px; white-space: pre-wrap; color: var(--text); min-height: 0; }
|
|
176
|
+
.agent-dock-footer { padding: 10px 12px; border-top: 1px solid var(--border); display: flex; gap: 8px; flex-shrink: 0; }
|
|
177
|
+
#cmd-k-overlay { position: fixed; inset: 0; background: rgba(9, 30, 66, 0.45); z-index: 50; display: none; align-items: flex-start; justify-content: center; padding-top: 12vh; }
|
|
178
|
+
body[data-theme="dark"] #cmd-k-overlay { background: rgba(0, 0, 0, 0.55); }
|
|
179
|
+
#cmd-k-overlay.is-open { display: flex; }
|
|
180
|
+
#cmd-k-panel {
|
|
181
|
+
width: min(560px, 92vw);
|
|
182
|
+
background: var(--panel);
|
|
183
|
+
border: 1px solid var(--border);
|
|
184
|
+
border-radius: 10px;
|
|
185
|
+
overflow: hidden;
|
|
186
|
+
box-shadow: var(--shadow-card);
|
|
187
|
+
color: var(--text);
|
|
188
|
+
}
|
|
189
|
+
#cmd-k-input { width: 100%; box-sizing: border-box; border: none; background: transparent; color: var(--text); padding: 14px 16px; font-size: 15px; outline: none; }
|
|
190
|
+
#cmd-k-results { max-height: 320px; overflow: auto; border-top: 1px solid var(--border); }
|
|
191
|
+
.cmd-k-row { padding: 10px 16px; cursor: pointer; color: var(--text); }
|
|
192
|
+
.cmd-k-row.is-active, .cmd-k-row:hover { background: var(--accent-soft); }
|
|
193
|
+
body[data-theme="dark"] .cmd-k-row.is-active,
|
|
194
|
+
body[data-theme="dark"] .cmd-k-row:hover { background: rgba(0, 102, 255, 0.16); }
|
|
195
|
+
`;
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
export function formatHomeKpiValue(value, fallback = '—') {
|
|
2
|
+
if (value === null || value === undefined || value === '') {
|
|
3
|
+
return fallback;
|
|
4
|
+
}
|
|
5
|
+
return String(value);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function renderHomeMissionControl(root, homeSnapshot) {
|
|
9
|
+
if (!root || !homeSnapshot) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const kpi = homeSnapshot.kpi ?? {};
|
|
14
|
+
const verifyRate = kpi.verifyPassRate?.rate;
|
|
15
|
+
const throughput = kpi.throughput?.perDay;
|
|
16
|
+
const runsToday = kpi.agentRunsToday?.count;
|
|
17
|
+
|
|
18
|
+
root.innerHTML = `
|
|
19
|
+
<div class="home-kpi-grid" data-testid="home-kpi-grid">
|
|
20
|
+
<article class="home-kpi-tile"><div class="label">Цикл</div><div class="value">${formatHomeKpiValue(kpi.cycleProgress?.percent, '0')}%</div></article>
|
|
21
|
+
<article class="home-kpi-tile"><div class="label">Готовы</div><div class="value">${formatHomeKpiValue(kpi.ready)}</div></article>
|
|
22
|
+
<article class="home-kpi-tile"><div class="label">Заблок.</div><div class="value">${formatHomeKpiValue(kpi.blocked)}</div></article>
|
|
23
|
+
<article class="home-kpi-tile"><div class="label">Доля verify</div><div class="value">${verifyRate === null ? '—' : Math.round(verifyRate * 100) + '%'}</div></article>
|
|
24
|
+
<article class="home-kpi-tile"><div class="label">Пропуск/д</div><div class="value">${throughput === null ? '—' : throughput.toFixed(1)}</div></article>
|
|
25
|
+
<article class="home-kpi-tile"><div class="label">Запуски сегодня</div><div class="value">${formatHomeKpiValue(runsToday)}</div></article>
|
|
26
|
+
</div>
|
|
27
|
+
<div class="home-section"><h3>Входящие</h3><ul class="home-list" data-testid="home-inbox-list">${renderHomeList(homeSnapshot.inboxPreview?.items, 'inbox')}</ul></div>
|
|
28
|
+
<div class="home-section"><h3>Моя очередь</h3><ul class="home-list" data-testid="home-queue-list">${renderHomeList(homeSnapshot.myQueue?.items, 'queue')}</ul></div>
|
|
29
|
+
<div class="home-section"><h3>Активные запуски</h3><ul class="home-list" data-testid="home-runs-list">${renderHomeList(homeSnapshot.activeRuns, 'run')}</ul></div>
|
|
30
|
+
`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function renderHomeList(items, kind) {
|
|
34
|
+
if (!Array.isArray(items) || items.length === 0) {
|
|
35
|
+
return '<li class="empty">Нет записей</li>';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return items.map((item) => {
|
|
39
|
+
const title = item.title ?? item.workId ?? item.id ?? '';
|
|
40
|
+
const meta = item.status ?? item.kind ?? '';
|
|
41
|
+
const routing = resolveHomeListRouting(item, kind);
|
|
42
|
+
const eventId = item.id ?? '';
|
|
43
|
+
const attrs = [
|
|
44
|
+
`data-home-kind="${kind}"`,
|
|
45
|
+
`data-event-id="${escapeHtmlAttr(eventId)}"`,
|
|
46
|
+
];
|
|
47
|
+
if (routing.workId) {
|
|
48
|
+
attrs.push(`data-work-id="${escapeHtmlAttr(routing.workId)}"`);
|
|
49
|
+
}
|
|
50
|
+
if (routing.analyticsKey) {
|
|
51
|
+
attrs.push(`data-analytics-key="${escapeHtmlAttr(routing.analyticsKey)}"`);
|
|
52
|
+
}
|
|
53
|
+
if (!routing.workId && !routing.analyticsKey) {
|
|
54
|
+
attrs.push('aria-disabled="true"');
|
|
55
|
+
}
|
|
56
|
+
return `<li ${attrs.join(' ')}><strong>${escapeHtml(title)}</strong><div class="muted">${escapeHtml(meta)}</div></li>`;
|
|
57
|
+
}).join('');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function resolveHomeListRouting(item, kind) {
|
|
61
|
+
if (kind === 'inbox') {
|
|
62
|
+
const link = item?.link;
|
|
63
|
+
if (link && typeof link === 'object') {
|
|
64
|
+
if (link.type === 'work' && link.workId) {
|
|
65
|
+
return { workId: String(link.workId), analyticsKey: null };
|
|
66
|
+
}
|
|
67
|
+
if (link.type === 'analytics' && (link.key || link.recordKey)) {
|
|
68
|
+
return { workId: null, analyticsKey: String(link.key ?? link.recordKey) };
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { workId: null, analyticsKey: null };
|
|
72
|
+
}
|
|
73
|
+
const workId = item?.workId ?? item?.id ?? null;
|
|
74
|
+
return {
|
|
75
|
+
workId: workId ? String(workId) : null,
|
|
76
|
+
analyticsKey: null,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function escapeHtml(value) {
|
|
81
|
+
return String(value)
|
|
82
|
+
.replace(/&/g, '&')
|
|
83
|
+
.replace(/</g, '<')
|
|
84
|
+
.replace(/>/g, '>');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function escapeHtmlAttr(value) {
|
|
88
|
+
return escapeHtml(value).replace(/"/g, '"');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function buildCommandPaletteIndex(snapshot, analyticsProjection) {
|
|
92
|
+
const rows = [];
|
|
93
|
+
for (const item of snapshot?.items ?? []) {
|
|
94
|
+
rows.push({
|
|
95
|
+
id: `task:${item.id}`,
|
|
96
|
+
scope: 'task',
|
|
97
|
+
label: item.title ?? item.id,
|
|
98
|
+
workId: item.id,
|
|
99
|
+
keywords: `${item.id} ${item.title ?? ''} ${item.status ?? ''}`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
for (const record of analyticsProjection?.records ?? []) {
|
|
103
|
+
rows.push({
|
|
104
|
+
id: `an:${record.key ?? record.id}`,
|
|
105
|
+
scope: 'an',
|
|
106
|
+
label: record.title ?? record.key,
|
|
107
|
+
analyticsKey: record.key,
|
|
108
|
+
keywords: `${record.key ?? ''} ${record.title ?? ''}`,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
rows.push({ id: 'cmd:home', scope: 'cmd', label: 'Открыть «Главная»', view: 'home', keywords: 'home mission control главная центр управления' });
|
|
112
|
+
rows.push({ id: 'cmd:board', scope: 'cmd', label: 'Открыть «Доска»', view: 'board', keywords: 'board kanban доска' });
|
|
113
|
+
rows.push({ id: 'cmd:run-ready', scope: 'run', label: 'Запустить первую ready-задачу', keywords: 'run agent worker запуск агент' });
|
|
114
|
+
return rows;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function filterCommandPaletteRows(rows, query) {
|
|
118
|
+
const needle = String(query ?? '').trim().toLowerCase();
|
|
119
|
+
if (!needle) {
|
|
120
|
+
return rows.slice(0, 12);
|
|
121
|
+
}
|
|
122
|
+
return rows
|
|
123
|
+
.filter((row) => row.keywords.toLowerCase().includes(needle) || row.label.toLowerCase().includes(needle))
|
|
124
|
+
.slice(0, 12);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const DONE_SCOPE_STATUSES = new Set(['done', 'verified']);
|
|
128
|
+
const ACTIVE_SCOPE_STATUSES = new Set(['claimed', 'doing', 'in_progress', 'verify']);
|
|
129
|
+
|
|
130
|
+
export function scopePanelCheckMark(status) {
|
|
131
|
+
const normalized = String(status ?? '').trim().toLowerCase();
|
|
132
|
+
if (DONE_SCOPE_STATUSES.has(normalized)) {
|
|
133
|
+
return 'x';
|
|
134
|
+
}
|
|
135
|
+
if (ACTIVE_SCOPE_STATUSES.has(normalized)) {
|
|
136
|
+
return '~';
|
|
137
|
+
}
|
|
138
|
+
return ' ';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function readItemKind(item) {
|
|
142
|
+
return String(item?.itemKind ?? item?.labels?.['work.item_kind'] ?? '').trim().toLowerCase();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function readItemParentId(item) {
|
|
146
|
+
return String(item?.parentId ?? item?.labels?.['work.parent_id'] ?? '').trim();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Walk parent chain from a work item to its containing epic id.
|
|
151
|
+
* @param {Array<object>} items
|
|
152
|
+
* @param {string} workId
|
|
153
|
+
* @returns {string | null}
|
|
154
|
+
*/
|
|
155
|
+
export function resolveEpicIdForWorkItem(items, workId) {
|
|
156
|
+
const normalizedWorkId = String(workId ?? '').trim();
|
|
157
|
+
if (normalizedWorkId === '' || !Array.isArray(items)) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const byId = new Map(items.map((item) => [item.id, item]));
|
|
162
|
+
let current = byId.get(normalizedWorkId);
|
|
163
|
+
while (current) {
|
|
164
|
+
if (readItemKind(current) === 'epic') {
|
|
165
|
+
return current.id;
|
|
166
|
+
}
|
|
167
|
+
const parentId = readItemParentId(current);
|
|
168
|
+
current = parentId ? byId.get(parentId) : null;
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Pick epic id for agent scope panel: focus task, else first active run, else in-progress epic.
|
|
175
|
+
* @param {Array<object>} items
|
|
176
|
+
* @param {{ focusTaskId?: string | null, activeRunIds?: string[] }} [options]
|
|
177
|
+
*/
|
|
178
|
+
export function resolveSessionEpicId(items, options = {}) {
|
|
179
|
+
const focusTaskId = String(options.focusTaskId ?? '').trim();
|
|
180
|
+
if (focusTaskId) {
|
|
181
|
+
const fromFocus = resolveEpicIdForWorkItem(items, focusTaskId);
|
|
182
|
+
if (fromFocus) {
|
|
183
|
+
return fromFocus;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
for (const runId of options.activeRunIds ?? []) {
|
|
188
|
+
const fromRun = resolveEpicIdForWorkItem(items, runId);
|
|
189
|
+
if (fromRun) {
|
|
190
|
+
return fromRun;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const inProgressEpic = items.find((item) => {
|
|
195
|
+
if (readItemKind(item) !== 'epic') {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
const status = String(item.status ?? '').trim().toLowerCase();
|
|
199
|
+
return status === 'in_progress' || status === 'doing' || status === 'claimed';
|
|
200
|
+
});
|
|
201
|
+
return inProgressEpic?.id ?? null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function renderAgentScopePanelHtml(scopeSlice) {
|
|
205
|
+
if (!scopeSlice || typeof scopeSlice !== 'object') {
|
|
206
|
+
return {
|
|
207
|
+
summaryHtml: '<div class="agent-scope-empty">Нет данных scope</div>',
|
|
208
|
+
listHtml: '',
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const rollup = scopeSlice.rollup ?? {};
|
|
213
|
+
const closed = rollup.closed ?? scopeSlice.doneChildCount ?? 0;
|
|
214
|
+
const total = rollup.total ?? scopeSlice.childCount ?? 0;
|
|
215
|
+
const title = scopeSlice.title ?? scopeSlice.epicId ?? 'Эпик';
|
|
216
|
+
const epicStatus = scopeSlice.status ?? '';
|
|
217
|
+
|
|
218
|
+
const summaryHtml =
|
|
219
|
+
'<div class="agent-scope-summary">' +
|
|
220
|
+
'<strong>' + escapeHtml(title) + '</strong>' +
|
|
221
|
+
'<div class="muted">' + escapeHtml(scopeSlice.epicId ?? '') + ' · ' + escapeHtml(epicStatus) +
|
|
222
|
+
' · ' + closed + '/' + total + '</div>' +
|
|
223
|
+
'</div>';
|
|
224
|
+
|
|
225
|
+
const children = Array.isArray(scopeSlice.children) ? scopeSlice.children : [];
|
|
226
|
+
if (children.length === 0) {
|
|
227
|
+
return {
|
|
228
|
+
summaryHtml,
|
|
229
|
+
listHtml: '<li class="empty">Нет subtasks</li>',
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const listHtml = children.map((child) => {
|
|
234
|
+
const mark = scopePanelCheckMark(child.status);
|
|
235
|
+
const label = child.title ? escapeHtml(child.title) : escapeHtml(child.id);
|
|
236
|
+
return (
|
|
237
|
+
'<li data-work-id="' + escapeHtmlAttr(child.id) + '" data-scope-status="' + escapeHtmlAttr(child.status ?? '') + '">' +
|
|
238
|
+
'<span class="agent-scope-mark" aria-hidden="true">[' + mark + ']</span> ' +
|
|
239
|
+
'<span class="agent-scope-label">' + label + '</span>' +
|
|
240
|
+
'<span class="agent-scope-status muted">' + escapeHtml(child.status ?? '') + '</span>' +
|
|
241
|
+
'</li>'
|
|
242
|
+
);
|
|
243
|
+
}).join('');
|
|
244
|
+
|
|
245
|
+
return { summaryHtml, listHtml };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export function renderAgentScopePanel(panelEl, scopeSlice) {
|
|
249
|
+
if (!panelEl) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const summaryEl = panelEl.querySelector('[data-testid="agent-scope-summary"]');
|
|
253
|
+
const listEl = panelEl.querySelector('[data-testid="agent-scope-list"]');
|
|
254
|
+
const { summaryHtml, listHtml } = renderAgentScopePanelHtml(scopeSlice);
|
|
255
|
+
if (summaryEl) {
|
|
256
|
+
summaryEl.innerHTML = summaryHtml;
|
|
257
|
+
}
|
|
258
|
+
if (listEl) {
|
|
259
|
+
listEl.innerHTML = listHtml;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export function renderAgentRunDockLog(bodyEl, journal) {
|
|
264
|
+
if (!bodyEl) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
const entries = Array.isArray(journal?.entries) ? journal.entries : [];
|
|
268
|
+
if (entries.length === 0) {
|
|
269
|
+
bodyEl.textContent = 'Нет записей в журнале agent run.';
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
bodyEl.textContent = entries.slice(0, 8).map((entry) => {
|
|
273
|
+
const at = entry.recordedAt ?? '';
|
|
274
|
+
const taskId = entry.taskId ?? entry.runId ?? '?';
|
|
275
|
+
const status = entry.ok === false ? 'FAIL' : 'OK';
|
|
276
|
+
return `[${at}] ${taskId} ${status} ${entry.summary ?? entry.failureReason ?? ''}`;
|
|
277
|
+
}).join('\n');
|
|
278
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { resolveOnebaseCliBinary } from './onebaseCliRunner.mjs';
|
|
6
|
+
|
|
7
|
+
export const ONEBASE_AI_CLI_COMMANDS = ['init', 'check', 'describe', 'ai-guide'];
|
|
8
|
+
|
|
9
|
+
const HELP_COMMAND_PATTERN = /^\s{2,}([a-z][a-z0-9-]*)\s+/gmu;
|
|
10
|
+
|
|
11
|
+
export function parseOnebaseHelpCommands(helpText) {
|
|
12
|
+
const text = String(helpText ?? '');
|
|
13
|
+
const commands = new Set();
|
|
14
|
+
const availableSection = text.split(/Available Commands:/iu)[1] ?? text;
|
|
15
|
+
|
|
16
|
+
for (const match of availableSection.matchAll(HELP_COMMAND_PATTERN)) {
|
|
17
|
+
commands.add(match[1]);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return [...commands].sort();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function probeOnebaseCliCapabilities(options = {}) {
|
|
24
|
+
const repoRoot = options.repoRoot ?? process.cwd();
|
|
25
|
+
const binary = resolveOnebaseCliBinary(options.env, { repoRoot });
|
|
26
|
+
const probedAt = options.probedAt ?? new Date().toISOString();
|
|
27
|
+
|
|
28
|
+
let helpResult;
|
|
29
|
+
try {
|
|
30
|
+
helpResult = (options.spawnSyncImpl ?? spawnSync)(binary, ['--help'], {
|
|
31
|
+
cwd: options.cwd ?? repoRoot,
|
|
32
|
+
env: options.env ?? process.env,
|
|
33
|
+
encoding: 'utf8',
|
|
34
|
+
shell: false,
|
|
35
|
+
timeout: options.timeoutMs ?? 30_000,
|
|
36
|
+
});
|
|
37
|
+
} catch (error) {
|
|
38
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
39
|
+
return {
|
|
40
|
+
schema: 'onebase.cli-capabilities.v1',
|
|
41
|
+
probedAt,
|
|
42
|
+
binary,
|
|
43
|
+
ok: false,
|
|
44
|
+
failureClass: message.includes('ENOENT') ? 'cli_missing' : 'cli_error',
|
|
45
|
+
message,
|
|
46
|
+
commands: Object.fromEntries(ONEBASE_AI_CLI_COMMANDS.map((name) => [name, false])),
|
|
47
|
+
availableCommands: [],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const stdout = String(helpResult.stdout ?? '');
|
|
52
|
+
const stderr = String(helpResult.stderr ?? '');
|
|
53
|
+
const combined = `${stdout}\n${stderr}`;
|
|
54
|
+
const availableCommands = parseOnebaseHelpCommands(combined);
|
|
55
|
+
const commands = Object.fromEntries(
|
|
56
|
+
ONEBASE_AI_CLI_COMMANDS.map((name) => [name, availableCommands.includes(name)]),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
schema: 'onebase.cli-capabilities.v1',
|
|
61
|
+
probedAt,
|
|
62
|
+
binary,
|
|
63
|
+
ok: (helpResult.status ?? 1) === 0 || availableCommands.length > 0,
|
|
64
|
+
failureClass: null,
|
|
65
|
+
message: null,
|
|
66
|
+
commands,
|
|
67
|
+
availableCommands,
|
|
68
|
+
helpExitCode: helpResult.status ?? 1,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function resolveDefaultCapabilitiesPath(options = {}) {
|
|
73
|
+
return resolve(options.repoRoot ?? process.cwd(), options.path ?? 'work/onebase-cli-capabilities.v1.json');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const ONEBASE_PVRG_METADATA_SCAN_CANDIDATES = [
|
|
77
|
+
'tests/fixtures/onebase',
|
|
78
|
+
'../onebase/examples/trade',
|
|
79
|
+
'domains/onebase/examples/trade',
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
export function resolveOnebaseMetadataScanRoot(options = {}) {
|
|
83
|
+
const repoRoot = resolve(options.repoRoot ?? process.cwd());
|
|
84
|
+
const candidates = [
|
|
85
|
+
options.relativeRoot,
|
|
86
|
+
...ONEBASE_PVRG_METADATA_SCAN_CANDIDATES,
|
|
87
|
+
].filter(Boolean);
|
|
88
|
+
|
|
89
|
+
for (const relativeRoot of candidates) {
|
|
90
|
+
const candidate = resolve(repoRoot, relativeRoot);
|
|
91
|
+
if (existsSync(candidate)) {
|
|
92
|
+
return candidate;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function binaryExistsForProbe(options = {}) {
|
|
100
|
+
const repoRoot = options.repoRoot ?? process.cwd();
|
|
101
|
+
const binary = resolveOnebaseCliBinary(options.env, { repoRoot });
|
|
102
|
+
if (binary.includes('/') || binary.includes('\\')) {
|
|
103
|
+
return existsSync(binary);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return true;
|
|
107
|
+
}
|