vibe-coding-master 0.4.19 → 0.4.20
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/dist/backend/adapters/git-adapter.js +15 -0
- package/dist/backend/api/harness-routes.js +17 -0
- package/dist/backend/api/task-routes.js +2 -2
- package/dist/backend/gateway/gateway-service.js +2 -5
- package/dist/backend/services/app-settings-service.js +2 -0
- package/dist/backend/services/claude-hook-service.js +188 -36
- package/dist/backend/services/harness-feedback-service.js +149 -2
- package/dist/backend/services/harness-service.js +88 -0
- package/dist/backend/services/round-service.js +58 -0
- package/dist/backend/services/session-service.js +6 -301
- package/dist/backend/templates/harness/architect-agent.js +37 -3
- package/dist/backend/templates/harness/coder-agent.js +6 -1
- package/dist/backend/templates/harness/harness-engineer-agent.js +31 -0
- package/dist/backend/templates/harness/project-manager-agent.js +7 -0
- package/dist/backend/templates/harness/reviewer-agent.js +10 -0
- package/dist-frontend/assets/index-B3sODrcw.css +32 -0
- package/dist-frontend/assets/index-_aOTGZCq.js +96 -0
- package/dist-frontend/index.html +2 -2
- package/docs/full-harness-baseline.md +1 -2
- package/docs/gate-review-gates.md +8 -17
- package/docs/product-design.md +5 -6
- package/docs/v0.4-harness-optimization-plan.md +3 -2
- package/package.json +1 -1
- package/dist-frontend/assets/index-BrY-xd6U.js +0 -95
- package/dist-frontend/assets/index-D1LTJ-sY.css +0 -32
|
@@ -258,6 +258,9 @@ export function createHarnessService(deps) {
|
|
|
258
258
|
async getRepositoryDiff(repoRoot, input = {}) {
|
|
259
259
|
return getRepositoryDiffReport(requireRepositoryDiffGit(deps.git), repoRoot, input, now());
|
|
260
260
|
},
|
|
261
|
+
async mergeRepositoryDiffToCurrentBranch(baseRepoRoot, input) {
|
|
262
|
+
return mergeRepositoryDiffToCurrentBranch(requireRepositoryMergeGit(deps.git), baseRepoRoot, input, now());
|
|
263
|
+
},
|
|
261
264
|
async getBootstrapStatus(repoRoot, targetRepoRoot = repoRoot) {
|
|
262
265
|
return getHarnessBootstrapStatus(deps, repoRoot, targetRepoRoot, now);
|
|
263
266
|
},
|
|
@@ -454,6 +457,7 @@ function requireRepositoryDiffGit(git) {
|
|
|
454
457
|
if (!git?.getCommitDiff ||
|
|
455
458
|
!git.getCommitInfo ||
|
|
456
459
|
!git.getCommitList ||
|
|
460
|
+
!git.getCurrentBranch ||
|
|
457
461
|
!git.getHeadCommit ||
|
|
458
462
|
!git.getMergeBase) {
|
|
459
463
|
throw new VcmError({
|
|
@@ -464,7 +468,22 @@ function requireRepositoryDiffGit(git) {
|
|
|
464
468
|
}
|
|
465
469
|
return git;
|
|
466
470
|
}
|
|
471
|
+
function requireRepositoryMergeGit(git) {
|
|
472
|
+
if (!git?.branchExists ||
|
|
473
|
+
!git.getCurrentBranch ||
|
|
474
|
+
!git.getHeadCommit ||
|
|
475
|
+
!git.mergeBranchFastForward) {
|
|
476
|
+
throw new VcmError({
|
|
477
|
+
code: "HARNESS_GIT_UNAVAILABLE",
|
|
478
|
+
message: "Git-backed repository merge is not available in this VCM runtime.",
|
|
479
|
+
statusCode: 501
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
return git;
|
|
483
|
+
}
|
|
467
484
|
async function getRepositoryDiffReport(git, repoRoot, input, generatedAt) {
|
|
485
|
+
const sourceBranch = await git.getCurrentBranch(repoRoot);
|
|
486
|
+
const targetBranch = input.baseRepoRoot ? await git.getCurrentBranch(input.baseRepoRoot) : sourceBranch;
|
|
468
487
|
const baseRef = input.baseRepoRoot
|
|
469
488
|
? await git.getHeadCommit(input.baseRepoRoot)
|
|
470
489
|
: "HEAD^";
|
|
@@ -476,6 +495,8 @@ async function getRepositoryDiffReport(git, repoRoot, input, generatedAt) {
|
|
|
476
495
|
return {
|
|
477
496
|
version: 1,
|
|
478
497
|
repoRoot,
|
|
498
|
+
sourceBranch,
|
|
499
|
+
targetBranch,
|
|
479
500
|
generatedAt,
|
|
480
501
|
commits,
|
|
481
502
|
summary: createEmptyRepositoryDiffSummary(),
|
|
@@ -497,6 +518,8 @@ async function getRepositoryDiffReport(git, repoRoot, input, generatedAt) {
|
|
|
497
518
|
return {
|
|
498
519
|
version: 1,
|
|
499
520
|
repoRoot,
|
|
521
|
+
sourceBranch,
|
|
522
|
+
targetBranch,
|
|
500
523
|
generatedAt,
|
|
501
524
|
commits,
|
|
502
525
|
commit: {
|
|
@@ -522,6 +545,71 @@ async function getRepositoryDiffReport(git, repoRoot, input, generatedAt) {
|
|
|
522
545
|
warnings
|
|
523
546
|
};
|
|
524
547
|
}
|
|
548
|
+
async function mergeRepositoryDiffToCurrentBranch(git, baseRepoRoot, input, mergedAt) {
|
|
549
|
+
const taskBranch = input.taskBranch.trim();
|
|
550
|
+
if (!taskBranch) {
|
|
551
|
+
throw new VcmError({
|
|
552
|
+
code: "HARNESS_MERGE_SOURCE_BRANCH_MISSING",
|
|
553
|
+
message: "Task branch is missing.",
|
|
554
|
+
statusCode: 400
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
if (!(await git.branchExists(baseRepoRoot, taskBranch))) {
|
|
558
|
+
throw new VcmError({
|
|
559
|
+
code: "HARNESS_MERGE_SOURCE_BRANCH_MISSING",
|
|
560
|
+
message: `Task branch does not exist locally: ${taskBranch}`,
|
|
561
|
+
statusCode: 409,
|
|
562
|
+
hint: "Create or restore the task branch before merging it into the connected repository branch."
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
const targetBranch = await git.getCurrentBranch(baseRepoRoot);
|
|
566
|
+
if (targetBranch === "detached") {
|
|
567
|
+
throw new VcmError({
|
|
568
|
+
code: "HARNESS_MERGE_BASE_DETACHED",
|
|
569
|
+
message: "The connected repository is in detached HEAD state.",
|
|
570
|
+
statusCode: 409,
|
|
571
|
+
hint: "Checkout the intended target branch in the connected repository before merging the task branch."
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
if (taskBranch === targetBranch) {
|
|
575
|
+
throw new VcmError({
|
|
576
|
+
code: "HARNESS_MERGE_BRANCH_INVALID",
|
|
577
|
+
message: "Task branch is already the connected repository branch.",
|
|
578
|
+
statusCode: 409,
|
|
579
|
+
hint: `Source and target are both ${targetBranch}.`
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
await assertVisibleWorktreeClean(git, input.taskRepoRoot, "The active task worktree", "HARNESS_MERGE_TASK_DIRTY");
|
|
583
|
+
await assertVisibleWorktreeClean(git, baseRepoRoot, "The connected repository", "HARNESS_MERGE_BASE_DIRTY");
|
|
584
|
+
const beforeSha = await git.getHeadCommit(baseRepoRoot);
|
|
585
|
+
const mergeResult = await git.mergeBranchFastForward(baseRepoRoot, taskBranch);
|
|
586
|
+
const afterSha = await git.getHeadCommit(baseRepoRoot);
|
|
587
|
+
return {
|
|
588
|
+
version: 1,
|
|
589
|
+
baseRepoRoot,
|
|
590
|
+
taskRepoRoot: input.taskRepoRoot,
|
|
591
|
+
sourceBranch: taskBranch,
|
|
592
|
+
targetBranch,
|
|
593
|
+
beforeSha,
|
|
594
|
+
afterSha,
|
|
595
|
+
changed: beforeSha !== afterSha,
|
|
596
|
+
stdout: mergeResult.stdout,
|
|
597
|
+
stderr: mergeResult.stderr,
|
|
598
|
+
mergedAt
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
async function assertVisibleWorktreeClean(git, repoRoot, label, code) {
|
|
602
|
+
const entries = await getVisibleGitStatusEntries(git, repoRoot);
|
|
603
|
+
if (entries.length === 0) {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
throw new VcmError({
|
|
607
|
+
code,
|
|
608
|
+
message: `${label} has uncommitted Git-visible changes.`,
|
|
609
|
+
statusCode: 409,
|
|
610
|
+
hint: `Commit or revert these files before merging to main: ${entries.map((entry) => entry.path).slice(0, 12).join(", ")}`
|
|
611
|
+
});
|
|
612
|
+
}
|
|
525
613
|
function toRepositoryDiffCommit(commit) {
|
|
526
614
|
return {
|
|
527
615
|
sha: commit.sha,
|
|
@@ -241,6 +241,36 @@ export function createRoundService(deps) {
|
|
|
241
241
|
recordClaudeHookEvent(input) {
|
|
242
242
|
return recordRoleTurnEvent(input);
|
|
243
243
|
},
|
|
244
|
+
async setRoleRecovery(input) {
|
|
245
|
+
return withTaskLock(input, async () => {
|
|
246
|
+
const timestamp = now();
|
|
247
|
+
const state = await load(input);
|
|
248
|
+
const next = {
|
|
249
|
+
...state,
|
|
250
|
+
roleRecovery: normalizeRoleRecovery(input.recovery),
|
|
251
|
+
updatedAt: timestamp
|
|
252
|
+
};
|
|
253
|
+
await save(input, next);
|
|
254
|
+
await updateSessionStatus(input, "running");
|
|
255
|
+
return toSessionRoundState(next, timestamp);
|
|
256
|
+
});
|
|
257
|
+
},
|
|
258
|
+
async clearRoleRecovery(input) {
|
|
259
|
+
return withTaskLock(input, async () => {
|
|
260
|
+
const timestamp = now();
|
|
261
|
+
const state = await load(input);
|
|
262
|
+
if (!state.roleRecovery || (input.role && state.roleRecovery.role !== input.role)) {
|
|
263
|
+
return toSessionRoundState(state, timestamp);
|
|
264
|
+
}
|
|
265
|
+
const next = {
|
|
266
|
+
...state,
|
|
267
|
+
roleRecovery: undefined,
|
|
268
|
+
updatedAt: timestamp
|
|
269
|
+
};
|
|
270
|
+
await save(input, next);
|
|
271
|
+
return toSessionRoundState(next, timestamp);
|
|
272
|
+
});
|
|
273
|
+
},
|
|
244
274
|
stopSession() { },
|
|
245
275
|
stopTask(taskSlug) {
|
|
246
276
|
clearSettleTimersForTask(taskSlug);
|
|
@@ -352,6 +382,7 @@ function toSessionRoundState(state, updatedAt) {
|
|
|
352
382
|
totalCompletedTurnCount: state.totalCompletedTurnCount,
|
|
353
383
|
totalCcActiveMs: state.totalCcActiveMs,
|
|
354
384
|
currentRoundCcActiveMs: 0,
|
|
385
|
+
roleRecovery: state.roleRecovery,
|
|
355
386
|
roles: [],
|
|
356
387
|
updatedAt
|
|
357
388
|
};
|
|
@@ -379,6 +410,7 @@ function toSessionRoundState(state, updatedAt) {
|
|
|
379
410
|
totalCompletedTurnCount: state.totalCompletedTurnCount,
|
|
380
411
|
totalCcActiveMs: state.totalCcActiveMs + activeDurationMs,
|
|
381
412
|
currentRoundCcActiveMs,
|
|
413
|
+
roleRecovery: state.roleRecovery,
|
|
382
414
|
roles: current.roles,
|
|
383
415
|
updatedAt
|
|
384
416
|
};
|
|
@@ -394,9 +426,35 @@ function normalizeRoundFile(input, taskSlug, updatedAt) {
|
|
|
394
426
|
totalTurnCount: normalizeNumber(input.totalTurnCount ?? legacy.totalPromptSubmitCount),
|
|
395
427
|
totalCompletedTurnCount: normalizeNumber(input.totalCompletedTurnCount ?? legacy.totalStopCount),
|
|
396
428
|
totalCcActiveMs: normalizeNumber(input.totalCcActiveMs),
|
|
429
|
+
roleRecovery: normalizeRoleRecovery(input.roleRecovery),
|
|
397
430
|
updatedAt: typeof input.updatedAt === "string" ? input.updatedAt : updatedAt
|
|
398
431
|
};
|
|
399
432
|
}
|
|
433
|
+
function normalizeRoleRecovery(input) {
|
|
434
|
+
if (!input || typeof input !== "object") {
|
|
435
|
+
return undefined;
|
|
436
|
+
}
|
|
437
|
+
const record = input;
|
|
438
|
+
if (typeof record.role !== "string" ||
|
|
439
|
+
(record.status !== "waiting" && record.status !== "retrying" && record.status !== "failed") ||
|
|
440
|
+
typeof record.lastFailureAt !== "string") {
|
|
441
|
+
return undefined;
|
|
442
|
+
}
|
|
443
|
+
return {
|
|
444
|
+
role: record.role,
|
|
445
|
+
status: record.status,
|
|
446
|
+
attempt: normalizeNumber(record.attempt),
|
|
447
|
+
maxAttempts: normalizeNumber(record.maxAttempts),
|
|
448
|
+
lastFailureAt: record.lastFailureAt,
|
|
449
|
+
error: typeof record.error === "string" ? record.error : undefined,
|
|
450
|
+
errorDetails: typeof record.errorDetails === "string" ? record.errorDetails : undefined,
|
|
451
|
+
lastAssistantMessage: typeof record.lastAssistantMessage === "string" ? record.lastAssistantMessage : undefined,
|
|
452
|
+
retryable: typeof record.retryable === "boolean" ? record.retryable : undefined,
|
|
453
|
+
nextRetryAt: typeof record.nextRetryAt === "string" ? record.nextRetryAt : undefined,
|
|
454
|
+
lastRetryAt: typeof record.lastRetryAt === "string" ? record.lastRetryAt : undefined,
|
|
455
|
+
failedAt: typeof record.failedAt === "string" ? record.failedAt : undefined
|
|
456
|
+
};
|
|
457
|
+
}
|
|
400
458
|
function normalizeRound(input) {
|
|
401
459
|
if (!input || typeof input.id !== "string") {
|
|
402
460
|
return undefined;
|