vibe-coding-master 0.7.39 → 0.7.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +11 -6
  2. package/dist/backend/adapters/claude-adapter.js +1 -6
  3. package/dist/backend/api/artifact-routes.js +72 -1
  4. package/dist/backend/api/runtime-state-routes.js +17 -5
  5. package/dist/backend/api/task-routes.js +6 -0
  6. package/dist/backend/api/workflow-control-routes.js +56 -0
  7. package/dist/backend/cli/install-vcm-harness.js +21 -31
  8. package/dist/backend/server.js +21 -5
  9. package/dist/backend/services/artifact-service.js +309 -5
  10. package/dist/backend/services/auto-memory-service.js +2 -2
  11. package/dist/backend/services/gate-review-service.js +81 -33
  12. package/dist/backend/services/harness-feedback-service.js +11 -4
  13. package/dist/backend/services/harness-service.js +17 -31
  14. package/dist/backend/services/message-service.js +105 -37
  15. package/dist/backend/services/status-service.js +6 -0
  16. package/dist/backend/services/workflow-control-service.js +854 -0
  17. package/dist/backend/templates/handoff.js +158 -3
  18. package/dist/backend/templates/harness/architect-agent.js +2 -2
  19. package/dist/backend/templates/harness/check-scaffold-ledger.js +105 -39
  20. package/dist/backend/templates/harness/claude-root.js +9 -1
  21. package/dist/backend/templates/harness/coder-agent.js +1 -2
  22. package/dist/backend/templates/harness/coder-worker-agent.js +1 -1
  23. package/dist/backend/templates/harness/gate-review.js +3 -4
  24. package/dist/backend/templates/harness/harness-engineer-agent.js +18 -1
  25. package/dist/backend/templates/harness/project-manager-agent.js +3 -2
  26. package/dist/backend/templates/harness/tester-agent.js +1 -0
  27. package/dist/backend/templates/harness/vcm-architecture-interview-skill.js +5 -0
  28. package/dist/backend/templates/harness/vcm-code-navigation-skill.js +1 -2
  29. package/dist/backend/templates/harness/vcm-final-acceptance-skill.js +2 -2
  30. package/dist/backend/templates/harness/vcm-long-running-validation-skill.js +4 -2
  31. package/dist/backend/templates/harness/vcm-propose-memory-skill.js +4 -2
  32. package/dist/backend/templates/harness/vcm-report-harness-issue-skill.js +2 -1
  33. package/dist/backend/templates/harness/vcm-route-message-skill.js +4 -7
  34. package/dist/backend/templates/harness/vcm-task-state-skill.js +2 -13
  35. package/dist/backend/templates/harness/vcm-workflow-review-skill.js +62 -0
  36. package/dist/backend/templates/message-envelope.js +4 -4
  37. package/dist/shared/types/workflow.js +7 -1
  38. package/dist/shared/validation/artifact-check.js +99 -95
  39. package/dist/shared/validation/artifact-contract.js +9 -0
  40. package/dist/shared/validation/artifact-registry.js +211 -0
  41. package/dist-frontend/assets/{index-DkM0mnQD.js → index-Bocc2DWF.js} +34 -34
  42. package/dist-frontend/assets/{index-CXSOe-NN.css → index-C_XHGNBD.css} +1 -1
  43. package/dist-frontend/index.html +2 -2
  44. package/package.json +1 -1
  45. package/scripts/harness-tools/run-long-check +38 -8
  46. package/scripts/harness-tools/vcm-artifact +148 -0
  47. package/scripts/harness-tools/vcm-bash-guard +42 -47
  48. package/scripts/harness-tools/watch-job +58 -4
@@ -25,6 +25,7 @@ import { renderVcmProposeMemorySkillRules } from "../templates/harness/vcm-propo
25
25
  import { renderVcmReportHarnessIssueSkillRules } from "../templates/harness/vcm-report-harness-issue-skill.js";
26
26
  import { renderVcmRouteMessageSkillRules } from "../templates/harness/vcm-route-message-skill.js";
27
27
  import { renderUpdateTaskStateTool, renderVcmTaskStateSkillRules } from "../templates/harness/vcm-task-state-skill.js";
28
+ import { renderVcmWorkflowReviewSkillRules } from "../templates/harness/vcm-workflow-review-skill.js";
28
29
  import { renderCheckScaffoldLedgerTool } from "../templates/harness/check-scaffold-ledger.js";
29
30
  import { renderRequestArchitectRestartTool, renderRestartArchitectSkillRules } from "../templates/harness/restart-architect-skill.js";
30
31
  import { submitTerminalInput } from "../runtime/terminal-submit.js";
@@ -53,7 +54,7 @@ const VCM_BASH_DEFAULT_TIMEOUT_MS = "600000";
53
54
  const VCM_AUTO_MEMORY_ENABLED = false;
54
55
  const VCM_HOOK_DEFINITIONS = [
55
56
  { eventName: "PreToolUse", matcher: "Bash", command: VCM_BASH_GUARD_HOOK_COMMAND, timeout: 10 },
56
- { eventName: "PreToolUse", matcher: "Grep", command: VCM_BASH_GUARD_HOOK_COMMAND, timeout: 10 },
57
+ { eventName: "PreToolUse", matcher: "Write|Edit", command: VCM_BASH_GUARD_HOOK_COMMAND, timeout: 10 },
57
58
  { eventName: "UserPromptSubmit", command: VCM_HOOK_COMMAND, timeout: 5 },
58
59
  { eventName: "Stop", command: VCM_STOP_HOOK_COMMAND, timeout: 10 },
59
60
  { eventName: "StopFailure", command: VCM_HOOK_COMMAND, timeout: 5 },
@@ -137,6 +138,14 @@ const HARNESS_FILES = [
137
138
  ownership: "whole-file",
138
139
  renderRules: renderVcmTaskStateSkillRules
139
140
  },
141
+ {
142
+ kind: "skill-vcm-workflow-review",
143
+ path: ".claude/skills/vcm-workflow-review/SKILL.md",
144
+ title: "VCM Workflow Review Skill",
145
+ frontmatter: renderSkillFrontmatter("vcm-workflow-review", "Use before every project-manager dispatch to Architect, Coder, or Tester so VCM can approve the workflow transition."),
146
+ ownership: "whole-file",
147
+ renderRules: renderVcmWorkflowReviewSkillRules
148
+ },
140
149
  {
141
150
  kind: "skill-vcm-final-acceptance",
142
151
  path: ".claude/skills/vcm-final-acceptance/SKILL.md",
@@ -198,10 +207,9 @@ const HARNESS_FILES = [
198
207
  path: ".claude/agents/reviewer.md",
199
208
  title: "Reviewer Agent",
200
209
  memoryBlock: true,
201
- requiredTools: ["LSP"],
202
- forbiddenTools: ["Grep"],
210
+ requiredTools: ["Grep", "LSP"],
203
211
  requiredSkills: ["vcm-code-navigation"],
204
- frontmatter: renderAgentFrontmatter("reviewer", "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.", { tools: "Read, Glob, Bash, Write, LSP", skills: ["vcm-code-navigation"] }),
212
+ frontmatter: renderAgentFrontmatter("reviewer", "VCM independent gate review role for architecture plans, validation adequacy, and code diffs.", { tools: "Read, Grep, Glob, Bash, Write, LSP", skills: ["vcm-code-navigation"] }),
205
213
  renderRules: renderReviewerAgentRules
206
214
  },
207
215
  {
@@ -274,11 +282,10 @@ const HARNESS_FILES = [
274
282
  path: ".claude/agents/architect.md",
275
283
  title: "Architect Agent",
276
284
  memoryBlock: true,
277
- requiredTools: ["Agent", "LSP"],
278
- forbiddenTools: ["Grep"],
285
+ requiredTools: ["Grep", "Agent", "LSP"],
279
286
  requiredSkills: ["vcm-code-navigation"],
280
287
  blankLineBeforeEnd: true,
281
- frontmatter: renderAgentFrontmatter("architect", "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync.", { tools: "Read, Glob, Bash, Edit, Write, Agent, LSP", skills: ["vcm-code-navigation"] }),
288
+ frontmatter: renderAgentFrontmatter("architect", "VCM architecture role for plans, module boundaries, public contracts, verifiable behavior, and docs sync.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP", skills: ["vcm-code-navigation"] }),
282
289
  renderRules: renderArchitectHarnessRules
283
290
  },
284
291
  {
@@ -286,10 +293,9 @@ const HARNESS_FILES = [
286
293
  path: ".claude/agents/coder.md",
287
294
  title: "Coder Agent",
288
295
  memoryBlock: true,
289
- requiredTools: ["Agent", "LSP"],
290
- forbiddenTools: ["Grep"],
296
+ requiredTools: ["Grep", "Agent", "LSP"],
291
297
  requiredSkills: ["vcm-code-navigation"],
292
- frontmatter: renderAgentFrontmatter("coder", "VCM implementation role for scoped code changes and focused tests.", { tools: "Read, Glob, Bash, Edit, Write, Agent, LSP", skills: ["vcm-code-navigation"] }),
298
+ frontmatter: renderAgentFrontmatter("coder", "VCM implementation role for scoped code changes and focused tests.", { tools: "Read, Grep, Glob, Bash, Edit, Write, Agent, LSP", skills: ["vcm-code-navigation"] }),
293
299
  renderRules: renderCoderHarnessRules
294
300
  },
295
301
  {
@@ -1399,28 +1405,8 @@ function ensureAgentTool(content, requiredTool) {
1399
1405
  function ensureAgentTools(content, requiredTools) {
1400
1406
  return (requiredTools ?? []).reduce(ensureAgentTool, content);
1401
1407
  }
1402
- function removeAgentTool(content, forbiddenTool) {
1403
- const frontmatterMatch = content.match(/^---\r?\n[\s\S]*?\r?\n---/);
1404
- if (!frontmatterMatch) {
1405
- return content;
1406
- }
1407
- const toolsMatch = frontmatterMatch[0].match(/^tools:\s*(.*)$/m);
1408
- if (!toolsMatch) {
1409
- return content;
1410
- }
1411
- const tools = toolsMatch[1].split(",").map((tool) => tool.trim()).filter(Boolean);
1412
- const nextTools = tools.filter((tool) => tool !== forbiddenTool);
1413
- if (nextTools.length === tools.length) {
1414
- return content;
1415
- }
1416
- return content.replace(frontmatterMatch[0], frontmatterMatch[0].replace(toolsMatch[0], `tools: ${nextTools.join(", ")}`));
1417
- }
1418
- function normalizeAgentTools(content, requiredTools, forbiddenTools) {
1419
- const required = ensureAgentTools(content, requiredTools);
1420
- return (forbiddenTools ?? []).reduce(removeAgentTool, required);
1421
- }
1422
1408
  function normalizeAgentFrontmatter(content, definition) {
1423
- const toolsUpdated = normalizeAgentTools(content, definition.requiredTools, definition.forbiddenTools);
1409
+ const toolsUpdated = ensureAgentTools(content, definition.requiredTools);
1424
1410
  return (definition.requiredSkills ?? []).reduce(ensureAgentSkill, toolsUpdated);
1425
1411
  }
1426
1412
  function ensureAgentSkill(content, requiredSkill) {
@@ -1,5 +1,5 @@
1
1
  import path from "node:path";
2
- import { randomUUID } from "node:crypto";
2
+ import { createHash, randomUUID } from "node:crypto";
3
3
  import { CORE_VCM_ROLE_NAMES } from "../../shared/constants.js";
4
4
  import { resolveRepoPath } from "../adapters/filesystem.js";
5
5
  import { submitTerminalInput } from "../runtime/terminal-submit.js";
@@ -94,8 +94,28 @@ export function createMessageService(deps) {
94
94
  failureReason: `${routeFile.toRole} is still running.`
95
95
  };
96
96
  }
97
+ const messageId = id();
98
+ if (routeFile.fromRole === PM_ROLE && deps.workflowControlService) {
99
+ try {
100
+ await deps.workflowControlService.claimDispatch({
101
+ ...toWorkflowContext(input),
102
+ routePath: routeFile.path,
103
+ targetRole: routeFile.toRole,
104
+ routeContentHash: routeFile.contentHash ?? "",
105
+ messageId
106
+ });
107
+ }
108
+ catch (error) {
109
+ return {
110
+ delivered: false,
111
+ requiresUserApproval: false,
112
+ clearedRouteFile: false,
113
+ failureReason: errorMessage(error)
114
+ };
115
+ }
116
+ }
97
117
  const message = {
98
- id: id(),
118
+ id: messageId,
99
119
  taskSlug: input.taskSlug,
100
120
  createdAt: timestamp,
101
121
  fromRole: routeFile.fromRole,
@@ -103,7 +123,6 @@ export function createMessageService(deps) {
103
123
  type: routeFile.type,
104
124
  body: routeFile.body,
105
125
  artifactRefs: routeFile.artifactRefs,
106
- workflow: routeFile.fromRole === PM_ROLE ? routeFile.workflow : undefined,
107
126
  bodyPath: routeFile.path,
108
127
  routePath: routeFile.path,
109
128
  dispatchingAt: timestamp,
@@ -115,16 +134,24 @@ export function createMessageService(deps) {
115
134
  ...message,
116
135
  deliveredAt: timestamp
117
136
  };
118
- await submitTerminalInput(deps.runtime, session.id, renderMessageEnvelope(delivered), {
119
- enterDelayMs: autoDispatchEnterDelayMs
120
- });
137
+ try {
138
+ await submitTerminalInput(deps.runtime, session.id, renderMessageEnvelope(delivered), {
139
+ enterDelayMs: autoDispatchEnterDelayMs
140
+ });
141
+ }
142
+ catch (error) {
143
+ if (routeFile.fromRole === PM_ROLE) {
144
+ await deps.workflowControlService?.releaseDispatch(toWorkflowContext(input), messageId);
145
+ }
146
+ throw error;
147
+ }
121
148
  await deps.sessionService.markRoleActivityRunning(input.repoRoot, input.taskSlug, routeFile.toRole, session.id);
122
149
  if (routeFile.fromRole === PM_ROLE) {
123
150
  await deps.taskWorkflowService?.recordPmDispatch({
124
151
  taskRepoRoot: input.taskRepoRoot ?? input.repoRoot,
125
152
  stateRoot: input.stateRoot,
126
153
  taskSlug: input.taskSlug
127
- }, routeFile.workflow, {
154
+ }, undefined, {
128
155
  messageId: delivered.id,
129
156
  toRole: delivered.toRole
130
157
  }).catch(() => undefined);
@@ -158,7 +185,7 @@ export function createMessageService(deps) {
158
185
  const messages = await readLatestMessages(deps.fs, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug));
159
186
  const messageId = extractVcmMessageId(input.prompt);
160
187
  if (!messageId) {
161
- return undefined;
188
+ return confirmManualRoute(input, timestamp);
162
189
  }
163
190
  const message = findDeliveredMessageForPrompt(messages, input.role, messageId);
164
191
  if (!message) {
@@ -169,6 +196,9 @@ export function createMessageService(deps) {
169
196
  acceptedAt: timestamp,
170
197
  failureReason: undefined
171
198
  };
199
+ if (accepted.fromRole === PM_ROLE) {
200
+ await deps.workflowControlService?.confirmDispatch(toWorkflowContext(input), accepted.id);
201
+ }
172
202
  await appendMessageSnapshot(deps.fs, input, accepted);
173
203
  await clearRouteFileIfStillMatchesMessage(deps.fs, input, accepted);
174
204
  return accepted;
@@ -251,6 +281,58 @@ export function createMessageService(deps) {
251
281
  return messages.find((message) => message.id === messageId);
252
282
  });
253
283
  }
284
+ async function confirmManualRoute(input, timestamp) {
285
+ if (!input.prompt?.trim() || input.role === PM_ROLE)
286
+ return undefined;
287
+ const routes = await listRouteFiles(deps.fs, toRouteContext(input));
288
+ const routeFile = routes.find((candidate) => candidate.pending
289
+ && candidate.fromRole === PM_ROLE
290
+ && candidate.toRole === input.role
291
+ && candidate.body.trim() === input.prompt.trim());
292
+ if (!routeFile)
293
+ return undefined;
294
+ const messageId = id();
295
+ await deps.workflowControlService?.claimDispatch({
296
+ ...toWorkflowContext(input),
297
+ routePath: routeFile.path,
298
+ targetRole: routeFile.toRole,
299
+ routeContentHash: routeFile.contentHash ?? "",
300
+ messageId
301
+ });
302
+ const accepted = {
303
+ id: messageId,
304
+ taskSlug: input.taskSlug,
305
+ createdAt: timestamp,
306
+ fromRole: routeFile.fromRole,
307
+ toRole: routeFile.toRole,
308
+ type: routeFile.type,
309
+ body: routeFile.body,
310
+ artifactRefs: routeFile.artifactRefs,
311
+ bodyPath: routeFile.path,
312
+ routePath: routeFile.path,
313
+ dispatchingAt: timestamp,
314
+ deliveredAt: timestamp,
315
+ acceptedAt: timestamp
316
+ };
317
+ try {
318
+ await deps.workflowControlService?.confirmDispatch(toWorkflowContext(input), messageId);
319
+ }
320
+ catch (error) {
321
+ await deps.workflowControlService?.releaseDispatch(toWorkflowContext(input), messageId);
322
+ throw error;
323
+ }
324
+ await appendMessageSnapshot(deps.fs, input, accepted);
325
+ await clearRouteFileIfStillMatchesMessage(deps.fs, input, accepted);
326
+ await deps.taskWorkflowService?.recordPmDispatch({
327
+ taskRepoRoot: input.taskRepoRoot ?? input.repoRoot,
328
+ stateRoot: input.stateRoot,
329
+ taskSlug: input.taskSlug
330
+ }, undefined, {
331
+ messageId: accepted.id,
332
+ toRole: accepted.toRole
333
+ }).catch(() => undefined);
334
+ return accepted;
335
+ }
254
336
  async function markDispatchConfirmationFailure(input, messageId, failureReason) {
255
337
  await withTaskLock(taskLocks, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug), async () => {
256
338
  const messages = await readLatestMessages(deps.fs, getMessagesPath(getStateRepoRoot(input), input.stateRoot, input.taskSlug));
@@ -258,6 +340,9 @@ export function createMessageService(deps) {
258
340
  if (!current || current.acceptedAt) {
259
341
  return;
260
342
  }
343
+ if (current.fromRole === PM_ROLE) {
344
+ await deps.workflowControlService?.releaseDispatch(toWorkflowContext(input), messageId);
345
+ }
261
346
  await appendMessageSnapshot(deps.fs, input, {
262
347
  ...current,
263
348
  failureReason
@@ -272,6 +357,14 @@ function toRouteContext(input) {
272
357
  handoffDir: input.handoffDir
273
358
  };
274
359
  }
360
+ function toWorkflowContext(input) {
361
+ return {
362
+ taskRepoRoot: input.taskRepoRoot ?? input.repoRoot,
363
+ stateRoot: input.stateRoot,
364
+ handoffDir: input.handoffDir,
365
+ taskSlug: input.taskSlug
366
+ };
367
+ }
275
368
  async function listRouteFiles(fs, input) {
276
369
  const repoRoot = input.taskRepoRoot ?? input.repoRoot;
277
370
  const routeDir = path.posix.join(input.handoffDir, "messages");
@@ -296,9 +389,9 @@ async function listRouteFiles(fs, input) {
296
389
  type: parsed.type,
297
390
  body: parsed.body,
298
391
  artifactRefs: parsed.artifactRefs,
299
- workflow: parsed.workflow,
300
392
  exists: true,
301
- pending: parsed.body.trim().length > 0
393
+ pending: parsed.body.trim().length > 0,
394
+ contentHash: createHash("sha256").update(content).digest("hex")
302
395
  });
303
396
  }
304
397
  return routeFiles;
@@ -320,12 +413,10 @@ function parseRouteFileContent(content, fromRole, toRole) {
320
413
  const { frontmatter, body } = splitFrontmatter(content);
321
414
  const type = parseMessageType(frontmatter.type) ?? getDefaultMessageType(fromRole, toRole);
322
415
  const artifactRefs = parseArtifactRefs(frontmatter);
323
- const workflow = fromRole === PM_ROLE ? parseWorkflowDeclaration(frontmatter) : undefined;
324
416
  return {
325
417
  type,
326
418
  body: body.trim(),
327
- artifactRefs,
328
- workflow
419
+ artifactRefs
329
420
  };
330
421
  }
331
422
  function splitFrontmatter(content) {
@@ -376,25 +467,6 @@ function parseArtifactRefs(frontmatter) {
376
467
  .map((entry) => entry.trim())
377
468
  .filter(Boolean);
378
469
  }
379
- function parseWorkflowDeclaration(frontmatter) {
380
- const declaration = {
381
- flow: frontmatter.workflow_flow,
382
- step: frontmatter.workflow_step,
383
- branch: frontmatter.workflow_branch,
384
- resumePoint: frontmatter.workflow_resume_point,
385
- status: frontmatter.workflow_status,
386
- evidenceRefs: parseCommaSeparated(frontmatter.workflow_evidence_refs)
387
- };
388
- return Object.values(declaration).some((value) => Array.isArray(value) ? value.length > 0 : value !== undefined)
389
- ? declaration
390
- : undefined;
391
- }
392
- function parseCommaSeparated(value) {
393
- if (value === undefined) {
394
- return undefined;
395
- }
396
- return value.split(",").map((entry) => entry.trim()).filter(Boolean);
397
- }
398
470
  function getDefaultMessageType(fromRole, toRole) {
399
471
  if (fromRole === PM_ROLE && toRole !== PM_ROLE) {
400
472
  return "task";
@@ -454,14 +526,10 @@ async function clearRouteFileIfStillMatchesMessage(fs, input, message) {
454
526
  const parsed = parseRouteFileContent(routeContent, fromRole, message.toRole);
455
527
  if (parsed.body.trim() === message.body.trim() &&
456
528
  parsed.type === message.type &&
457
- arraysEqual(parsed.artifactRefs, message.artifactRefs) &&
458
- workflowDeclarationsEqual(parsed.workflow, message.workflow)) {
529
+ arraysEqual(parsed.artifactRefs, message.artifactRefs)) {
459
530
  await fs.writeText(absolutePath, "");
460
531
  }
461
532
  }
462
- function workflowDeclarationsEqual(left, right) {
463
- return JSON.stringify(left ?? null) === JSON.stringify(right ?? null);
464
- }
465
533
  function isCoreRouteRole(role) {
466
534
  return CORE_VCM_ROLE_NAMES.some((candidate) => candidate === role);
467
535
  }
@@ -51,10 +51,16 @@ function degradedArtifactSummary(handoffDir) {
51
51
  roleCommandPaths: Object.fromEntries(DISPATCHABLE_ROLES.map((role) => [role, `${roleCommandsDir}/${role}.md`])),
52
52
  messageRoutePaths: {},
53
53
  architectureBriefPath: `${handoffDir}/architecture-brief.md`,
54
+ architectureEvidencePath: `${handoffDir}/architecture-evidence.md`,
55
+ planningProgressPath: `${handoffDir}/planning-progress.md`,
54
56
  architecturePlanPath: `${handoffDir}/architecture-plan.md`,
55
57
  knownIssuesPath: `${handoffDir}/known-issues.md`,
58
+ coderCompletionPath: `${handoffDir}/coder-completion.md`,
59
+ architectDebugPath: `${handoffDir}/architect-debug.md`,
60
+ architectureDiagnosisPath: `${handoffDir}/architecture-diagnosis.md`,
56
61
  testReportPath: `${handoffDir}/test-report.md`,
57
62
  docsSyncReportPath: `${handoffDir}/docs-sync-report.md`,
63
+ workflowProgressPath: `${handoffDir}/workflow-progress.md`,
58
64
  finalAcceptancePath: `${handoffDir}/final-acceptance.md`
59
65
  },
60
66
  checks: []