vibe-coding-master 0.7.42 → 0.7.44
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 +49 -17
- package/dist/backend/api/artifact-routes.js +3 -0
- package/dist/backend/api/harness-routes.js +16 -0
- package/dist/backend/api/task-routes.js +32 -2
- package/dist/backend/api/workflow-control-routes.js +7 -26
- package/dist/backend/cli/install-vcm-harness.js +61 -6
- package/dist/backend/role-tool-policy.js +1 -1
- package/dist/backend/server.js +14 -5
- package/dist/backend/services/artifact-service.js +10 -32
- package/dist/backend/services/auto-memory-service.js +642 -14
- package/dist/backend/services/claude-hook-service.js +183 -6
- package/dist/backend/services/gate-review-service.js +173 -74
- package/dist/backend/services/harness-feedback-service.js +180 -80
- package/dist/backend/services/harness-service.js +65 -9
- package/dist/backend/services/memory-review-paths.js +13 -0
- package/dist/backend/services/role-stall-detector-service.js +322 -0
- package/dist/backend/services/round-service.js +25 -0
- package/dist/backend/services/runtime-coordinator-service.js +12 -1
- package/dist/backend/services/session-service.js +70 -3
- package/dist/backend/services/status-service.js +1 -0
- package/dist/backend/services/translation-worker-service.js +19 -4
- package/dist/backend/services/workflow-control-service.js +524 -204
- package/dist/backend/templates/handoff.js +46 -5
- package/dist/backend/templates/harness/architect-agent.js +12 -6
- package/dist/backend/templates/harness/architect-scaffold-worker-agent.js +1 -1
- package/dist/backend/templates/harness/check-scaffold-ledger.js +234 -10
- package/dist/backend/templates/harness/claude-root.js +3 -2
- package/dist/backend/templates/harness/coder-agent.js +12 -5
- package/dist/backend/templates/harness/gate-review.js +150 -57
- package/dist/backend/templates/harness/harness-engineer-agent.js +38 -13
- package/dist/backend/templates/harness/project-manager-agent.js +18 -12
- package/dist/backend/templates/harness/resolve-durable-doc-assignment.js +60 -0
- package/dist/backend/templates/harness/tester-agent.js +13 -0
- package/dist/backend/templates/harness/vcm-ask-user-skill.js +82 -0
- package/dist/backend/templates/harness/vcm-code-navigation-skill.js +7 -6
- package/dist/backend/templates/harness/vcm-task-state-skill.js +2 -2
- package/dist/backend/templates/harness/vcm-workflow-review-skill.js +7 -9
- package/dist/shared/types/role-stall.js +1 -0
- package/dist/shared/types/workflow.js +15 -0
- package/dist/shared/validation/artifact-check.js +3 -3
- package/dist/shared/validation/artifact-contract.js +1 -1
- package/dist/shared/validation/artifact-registry.js +17 -1
- package/dist-frontend/assets/{index-C_XHGNBD.css → index-B0d4Z6ny.css} +1 -1
- package/dist-frontend/assets/index-BvCmrFlN.js +97 -0
- package/dist-frontend/index.html +2 -2
- package/package.json +1 -1
- package/scripts/claude-plugins/vcm-lsp-bridge/.claude-plugin/plugin.json +21 -6
- package/scripts/harness-tools/vcm-artifact +1 -2
- package/scripts/harness-tools/vcm-bash-guard +204 -14
- package/dist-frontend/assets/index-Bocc2DWF.js +0 -97
|
@@ -80,7 +80,7 @@ export function createClaudeHookService(deps) {
|
|
|
80
80
|
return { project };
|
|
81
81
|
}
|
|
82
82
|
async function processTranslatorHook(input) {
|
|
83
|
-
const eventName =
|
|
83
|
+
const eventName = parseBusinessHookEvent(input.event.hook_event_name);
|
|
84
84
|
const context = await getTranslatorHookContext();
|
|
85
85
|
const session = input.taskSlug === "__project__"
|
|
86
86
|
? await deps.sessionService.recordProjectTranslatorHookEvent(context.project.repoRoot, {
|
|
@@ -114,7 +114,7 @@ export function createClaudeHookService(deps) {
|
|
|
114
114
|
};
|
|
115
115
|
}
|
|
116
116
|
async function processHarnessEngineerHook(input) {
|
|
117
|
-
const eventName =
|
|
117
|
+
const eventName = parseBusinessHookEvent(input.event.hook_event_name);
|
|
118
118
|
const context = await getProjectToolHookContext("Harness Engineer");
|
|
119
119
|
const projectScoped = input.taskSlug === "__project_harness_engineer__";
|
|
120
120
|
const session = projectScoped
|
|
@@ -158,7 +158,7 @@ export function createClaudeHookService(deps) {
|
|
|
158
158
|
? await deps.harnessFeedbackService?.handleTaskRetrospectiveHook(context.project.repoRoot, {
|
|
159
159
|
taskSlug: activeTask.taskSlug,
|
|
160
160
|
eventName,
|
|
161
|
-
|
|
161
|
+
memoryReviewStatus: memoryState?.status ?? "idle"
|
|
162
162
|
})
|
|
163
163
|
: false;
|
|
164
164
|
if (memoryHandled || retrospectiveHandled) {
|
|
@@ -212,6 +212,16 @@ export function createClaudeHookService(deps) {
|
|
|
212
212
|
if (memoryResult) {
|
|
213
213
|
return memoryResult;
|
|
214
214
|
}
|
|
215
|
+
const prompt = stringOrUndefined(input.event.prompt);
|
|
216
|
+
if (input.role === "project-manager"
|
|
217
|
+
&& prompt
|
|
218
|
+
&& isDirectUserPrompt(prompt)
|
|
219
|
+
&& deps.workflowControlService) {
|
|
220
|
+
const workflowState = await deps.workflowControlService.getState(createWorkflowControlContext(context));
|
|
221
|
+
if (workflowState.awaitingUser) {
|
|
222
|
+
await deps.workflowControlService.resolveUserInput(createWorkflowControlContext(context));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
215
225
|
const session = await deps.sessionService.recordClaudeHookEvent(context.project.repoRoot, {
|
|
216
226
|
taskSlug: context.taskSlug,
|
|
217
227
|
role: input.role,
|
|
@@ -240,6 +250,12 @@ export function createClaudeHookService(deps) {
|
|
|
240
250
|
role: input.role,
|
|
241
251
|
eventName
|
|
242
252
|
});
|
|
253
|
+
await deps.roleStallDetector?.recordHook({
|
|
254
|
+
...createStallContext(context),
|
|
255
|
+
role: input.role,
|
|
256
|
+
eventName,
|
|
257
|
+
event: input.event
|
|
258
|
+
});
|
|
243
259
|
}
|
|
244
260
|
if (session) {
|
|
245
261
|
await deps.translationService.recordConversationBoundary({
|
|
@@ -260,7 +276,7 @@ export function createClaudeHookService(deps) {
|
|
|
260
276
|
handoffDir: context.task.handoffDir,
|
|
261
277
|
taskSlug: context.taskSlug,
|
|
262
278
|
role: input.role,
|
|
263
|
-
prompt
|
|
279
|
+
prompt
|
|
264
280
|
});
|
|
265
281
|
if (submitted) {
|
|
266
282
|
await deps.architectRestartService?.recordRouteAccepted(context.project.repoRoot, context.taskSlug, submitted);
|
|
@@ -292,6 +308,7 @@ export function createClaudeHookService(deps) {
|
|
|
292
308
|
return completedHookResult(input, eventName);
|
|
293
309
|
}
|
|
294
310
|
await clearStopFailureRecoveryState(context, input.role);
|
|
311
|
+
let pmAwaitingUser = false;
|
|
295
312
|
if (options.allowBlock && deps.jobGuard) {
|
|
296
313
|
const verdict = await deps.jobGuard.evaluateStop({
|
|
297
314
|
repoRoot: context.project.repoRoot,
|
|
@@ -313,6 +330,42 @@ export function createClaudeHookService(deps) {
|
|
|
313
330
|
};
|
|
314
331
|
}
|
|
315
332
|
}
|
|
333
|
+
if (input.role === "project-manager" && deps.workflowControlService) {
|
|
334
|
+
const workflowState = await deps.workflowControlService.getState(createWorkflowControlContext(context));
|
|
335
|
+
if (workflowState.awaitingUser) {
|
|
336
|
+
pmAwaitingUser = true;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
const lastAssistantMessage = stringOrUndefined(input.event.last_assistant_message);
|
|
340
|
+
if (options.allowBlock && lastAssistantMessage && asksUserQuestion(lastAssistantMessage)) {
|
|
341
|
+
return {
|
|
342
|
+
ok: true,
|
|
343
|
+
eventName,
|
|
344
|
+
taskSlug: context.taskSlug,
|
|
345
|
+
role: input.role,
|
|
346
|
+
sessionUpdated: false,
|
|
347
|
+
dispatchedCount: 0,
|
|
348
|
+
stopDecision: {
|
|
349
|
+
behavior: "block",
|
|
350
|
+
reason: "You asked the user a question without registering the wait. Use vcm-ask-user with the exact question, then ask that question and stop. Do not advance or route the workflow."
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
await deps.roleStallDetector?.recordHook({
|
|
357
|
+
...createStallContext(context),
|
|
358
|
+
role: input.role,
|
|
359
|
+
eventName,
|
|
360
|
+
event: input.event
|
|
361
|
+
});
|
|
362
|
+
if (pmAwaitingUser) {
|
|
363
|
+
return recordTurnEnd(input, context, eventName, {
|
|
364
|
+
dispatchRouteFiles: false,
|
|
365
|
+
notifyGateway: true,
|
|
366
|
+
settleGuard: false
|
|
367
|
+
});
|
|
368
|
+
}
|
|
316
369
|
return recordTurnEnd(input, context, eventName, {
|
|
317
370
|
dispatchRouteFiles: !isReviewerRoleName(input.role),
|
|
318
371
|
notifyGateway: true,
|
|
@@ -357,6 +410,12 @@ export function createClaudeHookService(deps) {
|
|
|
357
410
|
if (memoryResult) {
|
|
358
411
|
return memoryResult;
|
|
359
412
|
}
|
|
413
|
+
await deps.roleStallDetector?.recordHook({
|
|
414
|
+
...createStallContext(context),
|
|
415
|
+
role: input.role,
|
|
416
|
+
eventName,
|
|
417
|
+
event: input.event
|
|
418
|
+
});
|
|
360
419
|
const routeDispatchInput = createRouteDispatchInput(input, context);
|
|
361
420
|
const pending = await deps.messageService.listPendingRouteFiles(routeDispatchInput);
|
|
362
421
|
const hasCompletionEvidence = pending.some((routeFile) => routeFile.fromRole === input.role);
|
|
@@ -430,6 +489,14 @@ export function createClaudeHookService(deps) {
|
|
|
430
489
|
runtimeSessionId: stringOrUndefined(input.event.vcm_runtime_session_id),
|
|
431
490
|
runtimeSessionToken: input.runtimeSessionToken
|
|
432
491
|
});
|
|
492
|
+
if (session) {
|
|
493
|
+
await deps.roleStallDetector?.recordHook({
|
|
494
|
+
...createStallContext(context),
|
|
495
|
+
role: input.role,
|
|
496
|
+
eventName,
|
|
497
|
+
event: input.event
|
|
498
|
+
});
|
|
499
|
+
}
|
|
433
500
|
return {
|
|
434
501
|
ok: true,
|
|
435
502
|
eventName,
|
|
@@ -528,6 +595,17 @@ export function createClaudeHookService(deps) {
|
|
|
528
595
|
if (!deps.autoMemoryService || !(await deps.autoMemoryService.isRoleMemoryTurn(context.taskRepoRoot, input.role))) {
|
|
529
596
|
return undefined;
|
|
530
597
|
}
|
|
598
|
+
const prompt = stringOrUndefined(input.event.prompt);
|
|
599
|
+
if (eventName === "UserPromptSubmit"
|
|
600
|
+
&& input.role === "project-manager"
|
|
601
|
+
&& prompt
|
|
602
|
+
&& isDirectUserPrompt(prompt)
|
|
603
|
+
&& deps.workflowControlService) {
|
|
604
|
+
const workflowState = await deps.workflowControlService.getState(createWorkflowControlContext(context));
|
|
605
|
+
if (workflowState.awaitingUser) {
|
|
606
|
+
await deps.workflowControlService.resolveUserInput(createWorkflowControlContext(context));
|
|
607
|
+
}
|
|
608
|
+
}
|
|
531
609
|
const session = await deps.sessionService.recordClaudeHookEvent(context.project.repoRoot, {
|
|
532
610
|
taskSlug: context.taskSlug,
|
|
533
611
|
role: input.role,
|
|
@@ -552,6 +630,14 @@ export function createClaudeHookService(deps) {
|
|
|
552
630
|
eventName
|
|
553
631
|
});
|
|
554
632
|
}
|
|
633
|
+
if (boundToTask) {
|
|
634
|
+
await deps.roleStallDetector?.recordHook({
|
|
635
|
+
...createStallContext(context),
|
|
636
|
+
role: input.role,
|
|
637
|
+
eventName,
|
|
638
|
+
event: input.event
|
|
639
|
+
});
|
|
640
|
+
}
|
|
555
641
|
await deps.autoMemoryService.handleRoleHook({
|
|
556
642
|
baseRepoRoot: context.project.repoRoot,
|
|
557
643
|
taskRepoRoot: context.taskRepoRoot,
|
|
@@ -559,6 +645,10 @@ export function createClaudeHookService(deps) {
|
|
|
559
645
|
role: input.role,
|
|
560
646
|
eventName
|
|
561
647
|
});
|
|
648
|
+
if (eventName === "Stop") {
|
|
649
|
+
const memoryState = await deps.autoMemoryService.getState(context.project.repoRoot, context.taskRepoRoot);
|
|
650
|
+
await deps.harnessFeedbackService?.completeWaitingTaskRetrospective(context.project.repoRoot, context.taskSlug, memoryState.status);
|
|
651
|
+
}
|
|
562
652
|
return {
|
|
563
653
|
ok: true,
|
|
564
654
|
eventName,
|
|
@@ -579,6 +669,14 @@ export function createClaudeHookService(deps) {
|
|
|
579
669
|
...(stoppedRole ? { stoppedRole } : {})
|
|
580
670
|
};
|
|
581
671
|
}
|
|
672
|
+
function createWorkflowControlContext(context) {
|
|
673
|
+
return {
|
|
674
|
+
taskRepoRoot: context.taskRepoRoot,
|
|
675
|
+
stateRoot: context.config.stateRoot,
|
|
676
|
+
handoffDir: context.task.handoffDir,
|
|
677
|
+
taskSlug: context.taskSlug
|
|
678
|
+
};
|
|
679
|
+
}
|
|
582
680
|
async function isCurrentRoleHook(context, input, eventName) {
|
|
583
681
|
const current = await deps.sessionService.getRoleSession(context.project.repoRoot, context.taskSlug, input.role);
|
|
584
682
|
return Boolean(current && matchesRoleHookSession(current, {
|
|
@@ -764,6 +862,31 @@ export function createClaudeHookService(deps) {
|
|
|
764
862
|
taskSlug: context.taskSlug
|
|
765
863
|
};
|
|
766
864
|
}
|
|
865
|
+
function createStallContext(context) {
|
|
866
|
+
return {
|
|
867
|
+
repoRoot: context.project.repoRoot,
|
|
868
|
+
taskRepoRoot: context.taskRepoRoot,
|
|
869
|
+
stateRoot: context.config.stateRoot,
|
|
870
|
+
taskSlug: context.taskSlug
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
async function processProgressHook(input, eventName) {
|
|
874
|
+
const role = input.role;
|
|
875
|
+
if (!isVcmRoleName(role)) {
|
|
876
|
+
return completedHookResult(input, eventName);
|
|
877
|
+
}
|
|
878
|
+
const context = await getHookContext(input);
|
|
879
|
+
if (!(await isCurrentRoleHook(context, input, eventName))) {
|
|
880
|
+
return completedHookResult(input, eventName);
|
|
881
|
+
}
|
|
882
|
+
await deps.roleStallDetector?.recordHook({
|
|
883
|
+
...createStallContext(context),
|
|
884
|
+
role,
|
|
885
|
+
eventName,
|
|
886
|
+
event: input.event
|
|
887
|
+
});
|
|
888
|
+
return completedHookResult(input, eventName);
|
|
889
|
+
}
|
|
767
890
|
function renderStopFailureRecoveryPrompt() {
|
|
768
891
|
return [
|
|
769
892
|
"[VCM Recovery]",
|
|
@@ -806,6 +929,17 @@ export function createClaudeHookService(deps) {
|
|
|
806
929
|
});
|
|
807
930
|
}
|
|
808
931
|
const preferences = await deps.appSettings.getPreferences();
|
|
932
|
+
if (deps.roleStallDetector) {
|
|
933
|
+
const context = await getHookContext(input);
|
|
934
|
+
if (await isCurrentRoleHook(context, input, "PermissionRequest")) {
|
|
935
|
+
await deps.roleStallDetector.recordHook({
|
|
936
|
+
...createStallContext(context),
|
|
937
|
+
role: input.role,
|
|
938
|
+
eventName: "PermissionRequest",
|
|
939
|
+
event: input.event
|
|
940
|
+
});
|
|
941
|
+
}
|
|
942
|
+
}
|
|
809
943
|
if (preferences.permissionRequestMode !== "allowAll") {
|
|
810
944
|
return undefined;
|
|
811
945
|
}
|
|
@@ -821,13 +955,16 @@ export function createClaudeHookService(deps) {
|
|
|
821
955
|
return {
|
|
822
956
|
async handleHook(input) {
|
|
823
957
|
return withRoleHookLock(input, async () => {
|
|
958
|
+
const eventName = parseHookEvent(input.event.hook_event_name);
|
|
959
|
+
if (isProgressHook(eventName)) {
|
|
960
|
+
return processProgressHook(input, eventName);
|
|
961
|
+
}
|
|
824
962
|
if (isTranslatorToolRoleName(input.role)) {
|
|
825
963
|
return processTranslatorHook(input);
|
|
826
964
|
}
|
|
827
965
|
if (isHarnessEngineerToolRoleName(input.role)) {
|
|
828
966
|
return processHarnessEngineerHook(input);
|
|
829
967
|
}
|
|
830
|
-
const eventName = parseHookEvent(input.event.hook_event_name);
|
|
831
968
|
if (eventName === "UserPromptSubmit") {
|
|
832
969
|
return handleUserPromptSubmitHook(input);
|
|
833
970
|
}
|
|
@@ -860,6 +997,13 @@ function parseHookEvent(value) {
|
|
|
860
997
|
if (value === "UserPromptSubmit"
|
|
861
998
|
|| value === "Stop"
|
|
862
999
|
|| value === "StopFailure"
|
|
1000
|
+
|| value === "PreToolUse"
|
|
1001
|
+
|| value === "PostToolUse"
|
|
1002
|
+
|| value === "PostToolUseFailure"
|
|
1003
|
+
|| value === "PostToolBatch"
|
|
1004
|
+
|| value === "SubagentStart"
|
|
1005
|
+
|| value === "SubagentStop"
|
|
1006
|
+
|| value === "PreCompact"
|
|
863
1007
|
|| value === "PostCompact") {
|
|
864
1008
|
return value;
|
|
865
1009
|
}
|
|
@@ -867,9 +1011,28 @@ function parseHookEvent(value) {
|
|
|
867
1011
|
code: "HOOK_EVENT_UNSUPPORTED",
|
|
868
1012
|
message: `Unsupported Claude Code hook event: ${String(value)}`,
|
|
869
1013
|
statusCode: 400,
|
|
870
|
-
hint: "
|
|
1014
|
+
hint: "Use a Claude Code event installed by the VCM Harness."
|
|
871
1015
|
});
|
|
872
1016
|
}
|
|
1017
|
+
function isProgressHook(eventName) {
|
|
1018
|
+
return eventName === "PreToolUse"
|
|
1019
|
+
|| eventName === "PostToolUse"
|
|
1020
|
+
|| eventName === "PostToolUseFailure"
|
|
1021
|
+
|| eventName === "PostToolBatch"
|
|
1022
|
+
|| eventName === "SubagentStart"
|
|
1023
|
+
|| eventName === "SubagentStop"
|
|
1024
|
+
|| eventName === "PreCompact";
|
|
1025
|
+
}
|
|
1026
|
+
function parseBusinessHookEvent(value) {
|
|
1027
|
+
const eventName = parseHookEvent(value);
|
|
1028
|
+
if (eventName === "UserPromptSubmit"
|
|
1029
|
+
|| eventName === "Stop"
|
|
1030
|
+
|| eventName === "StopFailure"
|
|
1031
|
+
|| eventName === "PostCompact") {
|
|
1032
|
+
return eventName;
|
|
1033
|
+
}
|
|
1034
|
+
throwUnsupportedEvent(eventName);
|
|
1035
|
+
}
|
|
873
1036
|
function throwUnsupportedEvent(eventName) {
|
|
874
1037
|
throw new VcmError({
|
|
875
1038
|
code: "HOOK_EVENT_UNSUPPORTED",
|
|
@@ -918,3 +1081,17 @@ function normalizeDiagnosticString(value, maxLength) {
|
|
|
918
1081
|
function stringOrUndefined(value) {
|
|
919
1082
|
return typeof value === "string" ? value : undefined;
|
|
920
1083
|
}
|
|
1084
|
+
function isDirectUserPrompt(prompt) {
|
|
1085
|
+
const normalized = prompt.trim();
|
|
1086
|
+
return normalized.length > 0 && !/^\[VCM(?:\s|\])/i.test(normalized);
|
|
1087
|
+
}
|
|
1088
|
+
function asksUserQuestion(message) {
|
|
1089
|
+
const withoutCodeBlocks = message.replace(/```[\s\S]*?```/g, "");
|
|
1090
|
+
const lines = withoutCodeBlocks
|
|
1091
|
+
.split(/\r?\n/)
|
|
1092
|
+
.map((line) => line.trim())
|
|
1093
|
+
.filter(Boolean);
|
|
1094
|
+
return lines.some((line) => /[??]["')\]}】”’]*$/.test(line)
|
|
1095
|
+
|| /^(?:please\s+(?:answer|choose|confirm|decide|provide|select|tell)|(?:can|could|do|does|is|are|should|will|would)\s+you\b)/i.test(line)
|
|
1096
|
+
|| /^(?:请(?:回答|选择|确认|决定|提供|告知)|你(?:是否|能否|要不要|可否)|是否需要你|需要你(?:选择|确认|决定|提供|告知))/.test(line));
|
|
1097
|
+
}
|