vibe-coding-master 0.8.4 → 0.8.6
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 +23 -8
- package/dist/backend/api/task-routes.js +6 -0
- package/dist/backend/gateway/gateway-service.js +18 -3
- package/dist/backend/server.js +5 -2
- package/dist/backend/services/artifact-service.js +15 -6
- package/dist/backend/services/auto-memory-service.js +114 -50
- package/dist/backend/services/claude-hook-service.js +17 -1
- package/dist/backend/services/gate-review-service.js +4 -1
- package/dist/backend/services/harness-feedback-service.js +14 -1
- package/dist/backend/services/memory-review-paths.js +3 -0
- package/dist/backend/services/translation-service.js +49 -1
- package/dist/backend/services/user-question-reply.js +10 -0
- package/dist/backend/services/workflow-control-service.js +47 -1
- package/dist/backend/templates/harness/architect-agent.js +2 -1
- package/dist/backend/templates/harness/coder-agent.js +2 -1
- package/dist/backend/templates/harness/project-manager-agent.js +1 -1
- package/dist/backend/templates/harness/tester-agent.js +2 -2
- package/dist/backend/templates/harness/vcm-ask-user-skill.js +2 -2
- package/dist/backend/templates/harness/vcm-workflow-review-skill.js +1 -1
- package/dist-frontend/assets/{index-ClaVB8jM.js → index-CqZm9xCA.js} +31 -31
- package/dist-frontend/assets/index-xgiQeUD5.css +32 -0
- package/dist-frontend/index.html +2 -2
- package/package.json +1 -1
- package/scripts/harness-tools/vcm-artifact +1 -1
- package/dist-frontend/assets/index-Dc0_KSf4.css +0 -32
|
@@ -5,6 +5,7 @@ import { VcmError } from "../errors.js";
|
|
|
5
5
|
import { submitTerminalInput } from "../runtime/terminal-submit.js";
|
|
6
6
|
import { readLatestRoleTurnReply } from "./claude-transcript-reply.js";
|
|
7
7
|
import { createTranslationQueueRegistry } from "./translation-queue.js";
|
|
8
|
+
import { findUserQuestionReply, isPendingUserQuestion } from "./user-question-reply.js";
|
|
8
9
|
const TRANSLATION_SOURCE_LANGUAGE = "auto";
|
|
9
10
|
const TRANSLATION_INPUT_MODE = "review-before-send";
|
|
10
11
|
const TRANSLATION_CONTEXT_ENABLED = false;
|
|
@@ -25,6 +26,7 @@ export function createTranslationService(deps) {
|
|
|
25
26
|
async function loadConfig() {
|
|
26
27
|
const preferences = await deps.appSettings.getPreferences();
|
|
27
28
|
return {
|
|
29
|
+
enabled: preferences.translationEnabled,
|
|
28
30
|
sourceLanguage: TRANSLATION_SOURCE_LANGUAGE,
|
|
29
31
|
targetLanguage: preferences.translationTargetLanguage,
|
|
30
32
|
inputMode: TRANSLATION_INPUT_MODE,
|
|
@@ -239,6 +241,22 @@ export function createTranslationService(deps) {
|
|
|
239
241
|
return;
|
|
240
242
|
}
|
|
241
243
|
const config = await loadConfig();
|
|
244
|
+
if (state.role === "project-manager" && event.kind === "text" && event.stopReason === "end_turn"
|
|
245
|
+
&& !event.isSidechain && state.repoRoot && state.taskSlug && deps.workflowControlService && deps.projectService) {
|
|
246
|
+
const projectConfig = await deps.projectService.loadConfig(state.baseRepoRoot ?? state.repoRoot);
|
|
247
|
+
const workflow = await deps.workflowControlService.getState({
|
|
248
|
+
taskRepoRoot: state.repoRoot, taskSlug: state.taskSlug,
|
|
249
|
+
stateRoot: projectConfig.stateRoot, handoffDir: projectConfig.handoffRoot
|
|
250
|
+
});
|
|
251
|
+
const reply = findUserQuestionReply(workflow, sessionId, event.timestamp);
|
|
252
|
+
if (reply) {
|
|
253
|
+
publishUserQuestionReply(reply, config);
|
|
254
|
+
state.seenTranscriptIds.add(event.id);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (isPendingUserQuestion(workflow, event.timestamp))
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
242
260
|
let displayed = false;
|
|
243
261
|
if (event.kind === "text") {
|
|
244
262
|
const shouldTranslate = shouldTranslateTextTranscriptEvent(state, event, config);
|
|
@@ -281,6 +299,20 @@ export function createTranslationService(deps) {
|
|
|
281
299
|
}
|
|
282
300
|
return true;
|
|
283
301
|
}
|
|
302
|
+
function publishUserQuestionReply(reply, config) {
|
|
303
|
+
const state = getState(reply.sessionId);
|
|
304
|
+
if (state.seenTranscriptIds.has(reply.id))
|
|
305
|
+
return;
|
|
306
|
+
const metadata = { transcriptStopReason: "end_turn", transcriptTimestamp: reply.completedAt };
|
|
307
|
+
// Claim before scheduling translation so Stop and transcript replay share one entry/job.
|
|
308
|
+
state.seenTranscriptIds.add(reply.id);
|
|
309
|
+
if (config.enabled === false || config.outputMode === "round-final") {
|
|
310
|
+
pushPreservedProseEntry(reply.sessionId, reply.id, reply.question, config, metadata);
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
processClaudeOutputText(reply.sessionId, reply.question, config, reply.id, { flushImmediately: true, metadata });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
284
316
|
function processClaudeOutputText(sessionId, rawText, config, entryId, options = {}) {
|
|
285
317
|
return startClaudeOutputTranslation(sessionId, rawText, config, {
|
|
286
318
|
entryId,
|
|
@@ -647,6 +679,12 @@ export function createTranslationService(deps) {
|
|
|
647
679
|
return state;
|
|
648
680
|
}
|
|
649
681
|
return {
|
|
682
|
+
async recordUserQuestionReply(input) {
|
|
683
|
+
await prepareCache({ repoRoot: input.taskRepoRoot, baseRepoRoot: input.repoRoot,
|
|
684
|
+
taskSlug: input.taskSlug, role: "project-manager", sessionId: input.reply.sessionId });
|
|
685
|
+
publishUserQuestionReply(input.reply, await loadConfig());
|
|
686
|
+
await persistEvents(getState(input.reply.sessionId));
|
|
687
|
+
},
|
|
650
688
|
async startSession(input) {
|
|
651
689
|
const roleSession = await deps.sessionService.getRoleSession(input.repoRoot, input.taskSlug, input.role);
|
|
652
690
|
if (!roleSession || roleSession.status !== "running") {
|
|
@@ -720,6 +758,16 @@ export function createTranslationService(deps) {
|
|
|
720
758
|
status: state.status
|
|
721
759
|
});
|
|
722
760
|
}
|
|
761
|
+
if (deps.workflowControlService && deps.projectService) {
|
|
762
|
+
const projectConfig = await deps.projectService.loadConfig(input.repoRoot);
|
|
763
|
+
const workflow = await deps.workflowControlService.getState({ ...input,
|
|
764
|
+
stateRoot: projectConfig.stateRoot, handoffDir: projectConfig.handoffRoot });
|
|
765
|
+
for (const reply of workflow.userQuestionReplies ?? []) {
|
|
766
|
+
if (feedSessions.some((session) => session.role === "project-manager" && session.sessionId === reply.sessionId)) {
|
|
767
|
+
publishUserQuestionReply(reply, config);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
723
771
|
if (config.outputMode === "round-final") {
|
|
724
772
|
await translateRoundFinalReplyIfReady(input, config);
|
|
725
773
|
}
|
|
@@ -1173,7 +1221,7 @@ export function createTranslationService(deps) {
|
|
|
1173
1221
|
}
|
|
1174
1222
|
};
|
|
1175
1223
|
async function translateRoundFinalReplyIfReady(input, config) {
|
|
1176
|
-
if (!deps.roundService || !deps.projectService) {
|
|
1224
|
+
if (config.enabled === false || !deps.roundService || !deps.projectService) {
|
|
1177
1225
|
return;
|
|
1178
1226
|
}
|
|
1179
1227
|
const projectConfig = await deps.projectService.loadConfig(input.repoRoot);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Use the recorded turn window, not the current wait flag: replies must survive
|
|
2
|
+
// the user's answer and transcript replay without becoming a second message.
|
|
3
|
+
export function findUserQuestionReply(state, sessionId, timestamp) {
|
|
4
|
+
return state.userQuestionReplies?.slice().reverse().find((reply) => reply.sessionId === sessionId
|
|
5
|
+
&& timestamp >= reply.turnStartedAt && timestamp <= reply.completedAt);
|
|
6
|
+
}
|
|
7
|
+
export function isPendingUserQuestion(state, timestamp) {
|
|
8
|
+
return Boolean(state.awaitingUser && timestamp >= state.awaitingUser.requestedAt
|
|
9
|
+
&& !state.userQuestionReplies?.some((reply) => reply.requestedAt === state.awaitingUser?.requestedAt));
|
|
10
|
+
}
|
|
@@ -317,6 +317,8 @@ export function createWorkflowControlService(deps) {
|
|
|
317
317
|
},
|
|
318
318
|
requestUserInput,
|
|
319
319
|
resolveUserInput,
|
|
320
|
+
completeUserQuestion,
|
|
321
|
+
runWhileNotAwaitingUser,
|
|
320
322
|
submitProgress,
|
|
321
323
|
assertDocsArtifactAllowed,
|
|
322
324
|
assertRouteAuthorized,
|
|
@@ -409,6 +411,37 @@ export function createWorkflowControlService(deps) {
|
|
|
409
411
|
return next;
|
|
410
412
|
});
|
|
411
413
|
}
|
|
414
|
+
async function runWhileNotAwaitingUser(input, run) {
|
|
415
|
+
return withLock(statePath(input), async () => {
|
|
416
|
+
const state = await getState(input);
|
|
417
|
+
failWhileAwaitingUser(state);
|
|
418
|
+
return run();
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
async function completeUserQuestion(input, turn) {
|
|
422
|
+
return withLock(statePath(input), async () => {
|
|
423
|
+
const state = await getState(input);
|
|
424
|
+
failOnStateWarnings(state);
|
|
425
|
+
const question = state.awaitingUser;
|
|
426
|
+
if (!question)
|
|
427
|
+
return undefined;
|
|
428
|
+
const replies = state.userQuestionReplies ?? [];
|
|
429
|
+
const existing = replies.find((reply) => reply.requestedAt === question.requestedAt);
|
|
430
|
+
if (existing)
|
|
431
|
+
return existing.sessionId === turn.sessionId ? existing : undefined;
|
|
432
|
+
if (question.requestedAt > turn.endedAt)
|
|
433
|
+
return undefined;
|
|
434
|
+
const reply = {
|
|
435
|
+
...question,
|
|
436
|
+
id: `user-question:${turn.sessionId}:${question.requestedAt}`,
|
|
437
|
+
sessionId: turn.sessionId,
|
|
438
|
+
turnStartedAt: turn.startedAt,
|
|
439
|
+
completedAt: turn.endedAt
|
|
440
|
+
};
|
|
441
|
+
await saveState(input, { ...state, userQuestionReplies: [...replies, reply], updatedAt: now() });
|
|
442
|
+
return reply;
|
|
443
|
+
});
|
|
444
|
+
}
|
|
412
445
|
async function saveState(input, state) {
|
|
413
446
|
await deps.fs.writeJsonAtomic(statePath(input), state);
|
|
414
447
|
}
|
|
@@ -1343,18 +1376,24 @@ function normalizeState(value, taskSlug, timestamp) {
|
|
|
1343
1376
|
const awaitingUser = value.awaitingUser === undefined || value.awaitingUser === null
|
|
1344
1377
|
? null
|
|
1345
1378
|
: isAwaitingUser(value.awaitingUser) ? value.awaitingUser : undefined;
|
|
1379
|
+
const validQuestionReplies = value.userQuestionReplies === undefined
|
|
1380
|
+
|| (Array.isArray(value.userQuestionReplies) && value.userQuestionReplies.every(isUserQuestionReply));
|
|
1346
1381
|
if (pendingDispatch === undefined
|
|
1347
1382
|
|| activeDispatch === undefined
|
|
1348
1383
|
|| flowRun === undefined
|
|
1349
1384
|
|| userAuthorizations === undefined
|
|
1350
1385
|
|| userApprovedFollowUps === undefined
|
|
1351
|
-
|| awaitingUser === undefined
|
|
1386
|
+
|| awaitingUser === undefined
|
|
1387
|
+
|| !validQuestionReplies) {
|
|
1352
1388
|
return { ...emptyState(taskSlug, timestamp), warnings: ["Workflow control state has an unsupported shape."] };
|
|
1353
1389
|
}
|
|
1354
1390
|
return {
|
|
1355
1391
|
version: 1,
|
|
1356
1392
|
taskSlug,
|
|
1357
1393
|
awaitingUser,
|
|
1394
|
+
...(Array.isArray(value.userQuestionReplies) ? {
|
|
1395
|
+
userQuestionReplies: value.userQuestionReplies
|
|
1396
|
+
} : {}),
|
|
1358
1397
|
pendingDispatch,
|
|
1359
1398
|
activeDispatch,
|
|
1360
1399
|
flowRun,
|
|
@@ -1364,6 +1403,13 @@ function normalizeState(value, taskSlug, timestamp) {
|
|
|
1364
1403
|
updatedAt: typeof value.updatedAt === "string" ? value.updatedAt : timestamp
|
|
1365
1404
|
};
|
|
1366
1405
|
}
|
|
1406
|
+
function isUserQuestionReply(value) {
|
|
1407
|
+
if (!value || typeof value !== "object")
|
|
1408
|
+
return false;
|
|
1409
|
+
const reply = value;
|
|
1410
|
+
return [reply.id, reply.sessionId, reply.question, reply.requestedAt, reply.turnStartedAt, reply.completedAt]
|
|
1411
|
+
.every((field) => typeof field === "string" && field.length > 0);
|
|
1412
|
+
}
|
|
1367
1413
|
function emptyState(taskSlug, timestamp) {
|
|
1368
1414
|
return {
|
|
1369
1415
|
version: 1,
|
|
@@ -301,7 +301,8 @@ Each rewritten \`architecture-diagnosis.md\` must be a complete, self-contained
|
|
|
301
301
|
- For a \`[VCM Durable Documentation Assignment]\`, update the assigned durable
|
|
302
302
|
document and directly related documentation, run applicable documentation
|
|
303
303
|
checks, commit the result, and submit \`docs-update-report.md\` with the exact
|
|
304
|
-
assigned Assignment ID.
|
|
304
|
+
assigned Assignment ID. Use \`vcm-artifact docs-update-report --path <assigned-report-path>\`;
|
|
305
|
+
do not use the shared Docs-Only report path. This backend assignment does not start Docs-Only Flow.
|
|
305
306
|
|
|
306
307
|
- In Docs-Only Flow, verify claims against current code and durable docs, update the PM-assigned project documents and related documentation needed for consistency, run applicable documentation checks, commit the changes, and submit \`.ai/vcm/handoffs/docs-update-report.md\` through \`vcm-artifact\` before reporting to PM.
|
|
307
308
|
- In Code-Change Flow, Architect Debug Flow, and a code-producing Architecture Diagnosis Flow, perform post-validation docs sync only when project-manager requests it after tester completes.
|
|
@@ -82,7 +82,8 @@ ${renderRoleMemoryRules("coder")}
|
|
|
82
82
|
- For a \`[VCM Durable Documentation Assignment]\`, verify and update the assigned
|
|
83
83
|
durable document and directly related documentation, run applicable checks,
|
|
84
84
|
commit the result, and submit \`docs-update-report.md\` with the exact assigned
|
|
85
|
-
Assignment ID.
|
|
85
|
+
Assignment ID. Use \`vcm-artifact docs-update-report --path <assigned-report-path>\`;
|
|
86
|
+
do not use the shared Docs-Only report path. This backend assignment does not start Docs-Only Flow.
|
|
86
87
|
|
|
87
88
|
- In Docs-Only Flow, commit the documentation changes and submit \`.ai/vcm/handoffs/docs-update-report.md\` through \`vcm-artifact\` with the decision, changed and reviewed documents, evidence, checks, commit, and remaining documentation issues. Do not submit \`coder-completion.md\` for Docs-Only work.
|
|
88
89
|
- Submit \`.ai/vcm/handoffs/coder-completion.md\` before routing back to project-manager: write a candidate outside \`.ai/vcm\`, then run \`.ai/tools/vcm-artifact coder-completion --file <candidate> --mode draft|final\`. This file is the complete, self-contained current implementation completion evidence, not a log. Each revision must restate every Scaffold Manifest disposition, changed file, helper, deviation, generated-context result, baseline-test change, L0/L1 command and result, worker result, commit, and objective failure still needed to review the current implementation without a prior revision. Replace stale content instead of appending history.
|
|
@@ -21,7 +21,7 @@ ${renderRoleMemoryRules("project-manager")}
|
|
|
21
21
|
- Assistant text emitted before a tool call or role dispatch while the current Round continues is not user communication. Do not use intermediate text to deliver status, findings, decisions, questions, or required actions to the user.
|
|
22
22
|
- The user is informed only by PM's Round Final Reply when the Round pauses for user input or reaches a terminal result.
|
|
23
23
|
- The Round Final Reply must be self-contained. Restate every result, blocker, decision, risk, and required user action needed to understand the current state. Never rely on earlier PM text in the same Round.
|
|
24
|
-
- When asking the user a question,
|
|
24
|
+
- When asking the user a question, submit the complete user-facing question and its necessary context through \`vcm-ask-user\`, then end the turn. VCM delivers the registered question as the Round Final Reply; do not rely on a separate final message to supply missing context.
|
|
25
25
|
- Explain task status, blockers, role results, and decisions in user-facing language.
|
|
26
26
|
- Prefer plain logic over code-level detail: describe what changed, why it matters, what risk remains, and what decision is needed.
|
|
27
27
|
- Exclude irrelevant implementation detail, but retain the technical facts needed to explain the cause, evidence, impact, and unresolved state. Plain language means translating technical detail, not deleting it.
|
|
@@ -42,8 +42,8 @@ ${renderRoleMemoryRules("tester")}
|
|
|
42
42
|
- For a \`[VCM Durable Documentation Assignment]\`, verify and update the assigned
|
|
43
43
|
durable document and directly related documentation, run applicable
|
|
44
44
|
documentation checks, commit the result, and submit \`docs-update-report.md\`
|
|
45
|
-
with the exact assigned Assignment ID.
|
|
46
|
-
Docs-Only Flow.
|
|
45
|
+
with the exact assigned Assignment ID. Use \`vcm-artifact docs-update-report --path <assigned-report-path>\`;
|
|
46
|
+
do not use the shared Docs-Only report path. This backend assignment does not start Docs-Only Flow.
|
|
47
47
|
|
|
48
48
|
- Verify the assigned documentation against current project evidence, update the required documentation, run applicable documentation checks, and commit the changes.
|
|
49
49
|
- Submit \`.ai/vcm/handoffs/docs-update-report.md\` through \`vcm-artifact\` with the decision, changed and reviewed documents, evidence, checks, commit, and remaining documentation issues.
|
|
@@ -5,13 +5,13 @@ Use this skill whenever Project Manager asks the user a question.
|
|
|
5
5
|
|
|
6
6
|
## Ask And Wait
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Submit the complete user-facing question, including the context needed to answer:
|
|
9
9
|
|
|
10
10
|
\`\`\`bash
|
|
11
11
|
.ai/tools/vcm-ask-user --question "<exact question>"
|
|
12
12
|
\`\`\`
|
|
13
13
|
|
|
14
|
-
After the tool returns \`awaiting_user\`,
|
|
14
|
+
After the tool returns \`awaiting_user\`, end the turn. VCM delivers the registered question as the Round Final Reply through the existing display, translation, and Gateway paths. Do not rely on a separate final message to supply missing context. Do not request Workflow Review, write a route message, run a Gate, or advance the workflow in the same turn.
|
|
15
15
|
|
|
16
16
|
Every question pauses the workflow. PM may defer a question by not asking it; once PM asks, only a new direct user message resumes the workflow. The previous workflow approval is canceled, so request a fresh approval before the next dispatch.
|
|
17
17
|
`;
|
|
@@ -52,7 +52,7 @@ Only the user's explicit instruction may authorize a rejected transition.
|
|
|
52
52
|
|
|
53
53
|
If the current user instruction already explicitly authorizes the exact rejected transition, reuse that instruction verbatim as \`Authorization Text\`. Do not ask the user to confirm it again.
|
|
54
54
|
|
|
55
|
-
If no such instruction exists, use \`vcm-ask-user\` with the
|
|
55
|
+
If no such instruction exists, use \`vcm-ask-user\` with the complete authorization question and end the turn.
|
|
56
56
|
|
|
57
57
|
Resubmit the unchanged transition with:
|
|
58
58
|
|