vibe-coding-master 0.4.26 → 0.4.27
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.
|
@@ -268,7 +268,6 @@ async function main() {
|
|
|
268
268
|
const operations = [];
|
|
269
269
|
await assertDirectory(projectRoot, "Project root");
|
|
270
270
|
const manifest = await buildManifest(projectRoot);
|
|
271
|
-
await installManifest({ projectRoot, manifest, dryRun, operations });
|
|
272
271
|
for (const definition of MANAGED_FILES) {
|
|
273
272
|
await installManagedFile({ projectRoot, definition, dryRun, operations });
|
|
274
273
|
}
|
|
@@ -284,6 +283,13 @@ async function main() {
|
|
|
284
283
|
}
|
|
285
284
|
await removeLegacyFlatSkillFiles({ projectRoot, dryRun, operations });
|
|
286
285
|
await removeLegacyCodexHarnessPaths({ projectRoot, dryRun, operations });
|
|
286
|
+
await installManifest({
|
|
287
|
+
projectRoot,
|
|
288
|
+
manifest,
|
|
289
|
+
dryRun,
|
|
290
|
+
operations,
|
|
291
|
+
forceWrite: hasRealHarnessChange(operations)
|
|
292
|
+
});
|
|
287
293
|
printReport({ projectRoot, dryRun, operations });
|
|
288
294
|
}
|
|
289
295
|
function parseArgs(argv) {
|
|
@@ -449,13 +455,17 @@ function directoryCategory(directory) {
|
|
|
449
455
|
}
|
|
450
456
|
return "harness-tool-directory";
|
|
451
457
|
}
|
|
452
|
-
async function installManifest({ projectRoot, manifest, dryRun, operations }) {
|
|
458
|
+
async function installManifest({ projectRoot, manifest, dryRun, operations, forceWrite = false }) {
|
|
453
459
|
const targetPath = path.join(projectRoot, MANIFEST_PATH);
|
|
454
460
|
const currentManifest = await readOptionalJson(targetPath);
|
|
455
461
|
if (currentManifest && manifestBodyEqual(currentManifest, manifest)) {
|
|
456
462
|
operations.push(skip(MANIFEST_PATH, "unchanged"));
|
|
457
463
|
return;
|
|
458
464
|
}
|
|
465
|
+
if (!forceWrite && currentManifest && manifestBodyEqual(currentManifest, manifest, { ignoreHarnessVersion: true })) {
|
|
466
|
+
operations.push(skip(MANIFEST_PATH, "version-only change ignored"));
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
459
469
|
await writeIfChanged({
|
|
460
470
|
targetPath,
|
|
461
471
|
relativePath: MANIFEST_PATH,
|
|
@@ -731,15 +741,23 @@ async function pathExists(absolutePath) {
|
|
|
731
741
|
throw error;
|
|
732
742
|
});
|
|
733
743
|
}
|
|
734
|
-
function manifestBodyEqual(left, right) {
|
|
744
|
+
function manifestBodyEqual(left, right, options = {}) {
|
|
735
745
|
const normalizedLeft = { ...left };
|
|
736
746
|
const normalizedRight = { ...right };
|
|
737
747
|
delete normalizedLeft.installedAt;
|
|
738
748
|
delete normalizedLeft.updatedAt;
|
|
739
749
|
delete normalizedRight.installedAt;
|
|
740
750
|
delete normalizedRight.updatedAt;
|
|
751
|
+
if (options.ignoreHarnessVersion) {
|
|
752
|
+
delete normalizedLeft.harnessVersion;
|
|
753
|
+
delete normalizedRight.harnessVersion;
|
|
754
|
+
}
|
|
741
755
|
return JSON.stringify(normalizedLeft) === JSON.stringify(normalizedRight);
|
|
742
756
|
}
|
|
757
|
+
function hasRealHarnessChange(operations) {
|
|
758
|
+
return operations.some((operation) => operation.path !== MANIFEST_PATH &&
|
|
759
|
+
(operation.status === "done" || operation.status === "plan"));
|
|
760
|
+
}
|
|
743
761
|
function resolveInside(root, relativePath) {
|
|
744
762
|
if (path.isAbsolute(relativePath)) {
|
|
745
763
|
fail(`Path must be relative: ${relativePath}`);
|
|
@@ -1284,11 +1284,7 @@ async function analyzeHarnessManifest(fs, repoRoot, vcmVersion) {
|
|
|
1284
1284
|
};
|
|
1285
1285
|
}
|
|
1286
1286
|
if (installedVersion !== vcmVersion) {
|
|
1287
|
-
return
|
|
1288
|
-
path: MANIFEST_PATH,
|
|
1289
|
-
action: "update",
|
|
1290
|
-
reason: `VCM fixed harness manifest version is ${installedVersion ?? "missing"}; current VCM version is ${vcmVersion}.`
|
|
1291
|
-
};
|
|
1287
|
+
return undefined;
|
|
1292
1288
|
}
|
|
1293
1289
|
return undefined;
|
|
1294
1290
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomUUID } from "node:crypto";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import { VCM_ROLE_NAMES, isDispatchableRole } from "../../shared/constants.js";
|
|
4
3
|
import { VcmError } from "../errors.js";
|
|
@@ -43,10 +42,10 @@ export function createSessionService(deps) {
|
|
|
43
42
|
const permissionMode = normalizeClaudePermissionMode(input.permissionMode ?? persisted?.permissionMode);
|
|
44
43
|
const model = normalizeClaudeModel(input.model ?? persisted?.model);
|
|
45
44
|
const effort = normalizeClaudeEffort(input.effort ?? persisted?.effort);
|
|
46
|
-
const
|
|
45
|
+
const resumeClaudeSessionId = launchMode === "resume"
|
|
47
46
|
? persisted?.claudeSessionId
|
|
48
|
-
:
|
|
49
|
-
if (!
|
|
47
|
+
: undefined;
|
|
48
|
+
if (launchMode === "resume" && !resumeClaudeSessionId) {
|
|
50
49
|
throw new VcmError({
|
|
51
50
|
code: "CLAUDE_SESSION_MISSING",
|
|
52
51
|
message: `${role} does not have a session id to resume.`,
|
|
@@ -54,11 +53,14 @@ export function createSessionService(deps) {
|
|
|
54
53
|
hint: "Start the role once before using Resume."
|
|
55
54
|
});
|
|
56
55
|
}
|
|
56
|
+
const claudeSessionId = resumeClaudeSessionId ?? "";
|
|
57
57
|
const transcriptPath = launchMode === "resume" && persisted?.transcriptPath
|
|
58
58
|
? persisted.transcriptPath
|
|
59
|
-
:
|
|
59
|
+
: resumeClaudeSessionId
|
|
60
|
+
? claudeTranscriptPath(taskRepoRoot, resumeClaudeSessionId)
|
|
61
|
+
: undefined;
|
|
60
62
|
const startCommand = {
|
|
61
|
-
...deps.claude.buildRoleStartCommand(role, config.claudeCommand, permissionMode,
|
|
63
|
+
...deps.claude.buildRoleStartCommand(role, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort),
|
|
62
64
|
cwd: taskRepoRoot
|
|
63
65
|
};
|
|
64
66
|
const runtimeSession = await deps.runtime.createSession({
|
|
@@ -73,7 +75,7 @@ export function createSessionService(deps) {
|
|
|
73
75
|
VCM_TASK_REPO_ROOT: taskRepoRoot,
|
|
74
76
|
VCM_TASK_SLUG: taskSlug,
|
|
75
77
|
VCM_ROLE: role,
|
|
76
|
-
VCM_SESSION_ID: claudeSessionId
|
|
78
|
+
VCM_SESSION_ID: claudeSessionId || undefined
|
|
77
79
|
},
|
|
78
80
|
cols: input.cols,
|
|
79
81
|
rows: input.rows
|
|
@@ -120,10 +122,10 @@ export function createSessionService(deps) {
|
|
|
120
122
|
const permissionMode = normalizeClaudePermissionMode(input.permissionMode ?? persisted?.permissionMode);
|
|
121
123
|
const model = normalizeClaudeModel(input.model ?? persisted?.model);
|
|
122
124
|
const effort = normalizeClaudeEffort(input.effort ?? persisted?.effort ?? "medium");
|
|
123
|
-
const
|
|
125
|
+
const resumeClaudeSessionId = launchMode === "resume"
|
|
124
126
|
? persisted?.claudeSessionId
|
|
125
|
-
:
|
|
126
|
-
if (!
|
|
127
|
+
: undefined;
|
|
128
|
+
if (launchMode === "resume" && !resumeClaudeSessionId) {
|
|
127
129
|
throw new VcmError({
|
|
128
130
|
code: "TRANSLATOR_SESSION_MISSING",
|
|
129
131
|
message: "Translator does not have a session id to resume.",
|
|
@@ -135,11 +137,14 @@ export function createSessionService(deps) {
|
|
|
135
137
|
const launchCwd = launchMode === "resume"
|
|
136
138
|
? persisted?.cwd ?? taskContext.taskRepoRoot
|
|
137
139
|
: taskContext.taskRepoRoot;
|
|
140
|
+
const claudeSessionId = resumeClaudeSessionId ?? "";
|
|
138
141
|
const transcriptPath = launchMode === "resume" && persisted?.transcriptPath
|
|
139
142
|
? persisted.transcriptPath
|
|
140
|
-
:
|
|
143
|
+
: resumeClaudeSessionId
|
|
144
|
+
? claudeTranscriptPath(launchCwd, resumeClaudeSessionId)
|
|
145
|
+
: undefined;
|
|
141
146
|
const startCommand = {
|
|
142
|
-
...deps.claude.buildRoleStartCommand(TRANSLATOR_ROLE, config.claudeCommand, permissionMode,
|
|
147
|
+
...deps.claude.buildRoleStartCommand(TRANSLATOR_ROLE, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort),
|
|
143
148
|
cwd: launchCwd
|
|
144
149
|
};
|
|
145
150
|
const runtimeSession = await deps.runtime.createSession({
|
|
@@ -154,7 +159,7 @@ export function createSessionService(deps) {
|
|
|
154
159
|
VCM_TASK_REPO_ROOT: taskContext.taskRepoRoot,
|
|
155
160
|
VCM_TASK_SLUG: taskContext.taskSlug,
|
|
156
161
|
VCM_ROLE: TRANSLATOR_ROLE,
|
|
157
|
-
VCM_SESSION_ID: claudeSessionId
|
|
162
|
+
VCM_SESSION_ID: claudeSessionId || undefined
|
|
158
163
|
},
|
|
159
164
|
cols: input.cols,
|
|
160
165
|
rows: input.rows
|
|
@@ -197,10 +202,10 @@ export function createSessionService(deps) {
|
|
|
197
202
|
const permissionMode = normalizeClaudePermissionMode(input.permissionMode ?? persisted?.permissionMode);
|
|
198
203
|
const model = normalizeClaudeModel(input.model ?? persisted?.model);
|
|
199
204
|
const effort = normalizeClaudeEffort(input.effort ?? persisted?.effort ?? "medium");
|
|
200
|
-
const
|
|
205
|
+
const resumeClaudeSessionId = launchMode === "resume"
|
|
201
206
|
? persisted?.claudeSessionId
|
|
202
|
-
:
|
|
203
|
-
if (!
|
|
207
|
+
: undefined;
|
|
208
|
+
if (launchMode === "resume" && !resumeClaudeSessionId) {
|
|
204
209
|
throw new VcmError({
|
|
205
210
|
code: "HARNESS_ENGINEER_SESSION_MISSING",
|
|
206
211
|
message: "Harness Engineer does not have a session id to resume.",
|
|
@@ -212,11 +217,14 @@ export function createSessionService(deps) {
|
|
|
212
217
|
const launchCwd = launchMode === "resume"
|
|
213
218
|
? persisted?.cwd ?? taskContext.taskRepoRoot
|
|
214
219
|
: taskContext.taskRepoRoot;
|
|
220
|
+
const claudeSessionId = resumeClaudeSessionId ?? "";
|
|
215
221
|
const transcriptPath = launchMode === "resume" && persisted?.transcriptPath
|
|
216
222
|
? persisted.transcriptPath
|
|
217
|
-
:
|
|
223
|
+
: resumeClaudeSessionId
|
|
224
|
+
? claudeTranscriptPath(launchCwd, resumeClaudeSessionId)
|
|
225
|
+
: undefined;
|
|
218
226
|
const startCommand = {
|
|
219
|
-
...deps.claude.buildRoleStartCommand(HARNESS_ENGINEER_ROLE, config.claudeCommand, permissionMode,
|
|
227
|
+
...deps.claude.buildRoleStartCommand(HARNESS_ENGINEER_ROLE, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort),
|
|
220
228
|
cwd: launchCwd
|
|
221
229
|
};
|
|
222
230
|
const runtimeSession = await deps.runtime.createSession({
|
|
@@ -231,7 +239,7 @@ export function createSessionService(deps) {
|
|
|
231
239
|
VCM_TASK_REPO_ROOT: taskContext.taskRepoRoot,
|
|
232
240
|
VCM_TASK_SLUG: taskContext.taskSlug,
|
|
233
241
|
VCM_ROLE: HARNESS_ENGINEER_ROLE,
|
|
234
|
-
VCM_SESSION_ID: claudeSessionId
|
|
242
|
+
VCM_SESSION_ID: claudeSessionId || undefined
|
|
235
243
|
},
|
|
236
244
|
cols: input.cols,
|
|
237
245
|
rows: input.rows
|
|
@@ -298,7 +306,9 @@ export function createSessionService(deps) {
|
|
|
298
306
|
...session,
|
|
299
307
|
cwd: targetCwd,
|
|
300
308
|
previousCwd: session.cwd,
|
|
301
|
-
transcriptPath:
|
|
309
|
+
transcriptPath: session.claudeSessionId
|
|
310
|
+
? claudeTranscriptPath(targetCwd, session.claudeSessionId)
|
|
311
|
+
: session.transcriptPath,
|
|
302
312
|
updatedAt: timestamp
|
|
303
313
|
};
|
|
304
314
|
deps.registry.upsert(normalizeProjectScopedRecordForPersistence(updated));
|
|
@@ -509,10 +519,11 @@ export function createSessionService(deps) {
|
|
|
509
519
|
const timestamp = now();
|
|
510
520
|
const isTurnEnd = isTurnEndHook(input.eventName);
|
|
511
521
|
const isCompact = isCompactHook(input.eventName);
|
|
522
|
+
const sessionIdentity = nextHookSessionIdentity(current, input);
|
|
512
523
|
const updated = {
|
|
513
524
|
...current,
|
|
514
|
-
claudeSessionId:
|
|
515
|
-
transcriptPath:
|
|
525
|
+
claudeSessionId: sessionIdentity.claudeSessionId,
|
|
526
|
+
transcriptPath: sessionIdentity.transcriptPath,
|
|
516
527
|
cwd: input.cwd ?? current.cwd,
|
|
517
528
|
activityStatus: isTurnEnd ? "idle" : isCompact ? current.activityStatus : "running",
|
|
518
529
|
lastHookEventAt: timestamp,
|
|
@@ -633,10 +644,11 @@ export function createSessionService(deps) {
|
|
|
633
644
|
const timestamp = now();
|
|
634
645
|
const isTurnEnd = isTurnEndHook(input.eventName);
|
|
635
646
|
const isCompact = isCompactHook(input.eventName);
|
|
647
|
+
const sessionIdentity = nextHookSessionIdentity(current, input);
|
|
636
648
|
const updated = {
|
|
637
649
|
...current,
|
|
638
|
-
claudeSessionId:
|
|
639
|
-
transcriptPath:
|
|
650
|
+
claudeSessionId: sessionIdentity.claudeSessionId,
|
|
651
|
+
transcriptPath: sessionIdentity.transcriptPath,
|
|
640
652
|
cwd: input.cwd ?? current.cwd,
|
|
641
653
|
activityStatus: isTurnEnd ? "idle" : isCompact ? current.activityStatus : "running",
|
|
642
654
|
lastHookEventAt: timestamp,
|
|
@@ -800,10 +812,11 @@ export function createSessionService(deps) {
|
|
|
800
812
|
const timestamp = now();
|
|
801
813
|
const isTurnEnd = isTurnEndHook(input.eventName);
|
|
802
814
|
const isCompact = isCompactHook(input.eventName);
|
|
815
|
+
const sessionIdentity = nextHookSessionIdentity(current, input);
|
|
803
816
|
const updated = {
|
|
804
817
|
...current,
|
|
805
|
-
claudeSessionId:
|
|
806
|
-
transcriptPath:
|
|
818
|
+
claudeSessionId: sessionIdentity.claudeSessionId,
|
|
819
|
+
transcriptPath: sessionIdentity.transcriptPath,
|
|
807
820
|
cwd: input.cwd ?? current.cwd,
|
|
808
821
|
activityStatus: isTurnEnd ? "idle" : isCompact ? current.activityStatus : "running",
|
|
809
822
|
lastHookEventAt: timestamp,
|
|
@@ -890,6 +903,9 @@ function toRoleSessionRecordView(record, runtime) {
|
|
|
890
903
|
};
|
|
891
904
|
}
|
|
892
905
|
function matchesRoleHookSession(record, input) {
|
|
906
|
+
if (!record.claudeSessionId && !record.transcriptPath) {
|
|
907
|
+
return input.eventName === "UserPromptSubmit";
|
|
908
|
+
}
|
|
893
909
|
if (input.sessionId && record.claudeSessionId === input.sessionId) {
|
|
894
910
|
return true;
|
|
895
911
|
}
|
|
@@ -901,6 +917,20 @@ function matchesRoleHookSession(record, input) {
|
|
|
901
917
|
}
|
|
902
918
|
return false;
|
|
903
919
|
}
|
|
920
|
+
function nextHookSessionIdentity(current, input) {
|
|
921
|
+
const canRecordFirstClaudeSessionId = Boolean(!current.claudeSessionId &&
|
|
922
|
+
input.eventName === "UserPromptSubmit" &&
|
|
923
|
+
input.sessionId);
|
|
924
|
+
const hasConfirmedClaudeSessionId = Boolean(current.claudeSessionId || canRecordFirstClaudeSessionId);
|
|
925
|
+
return {
|
|
926
|
+
claudeSessionId: canRecordFirstClaudeSessionId
|
|
927
|
+
? input.sessionId ?? current.claudeSessionId
|
|
928
|
+
: current.claudeSessionId,
|
|
929
|
+
transcriptPath: hasConfirmedClaudeSessionId
|
|
930
|
+
? input.transcriptPath ?? current.transcriptPath
|
|
931
|
+
: current.transcriptPath
|
|
932
|
+
};
|
|
933
|
+
}
|
|
904
934
|
function isTurnEndHook(eventName) {
|
|
905
935
|
return eventName === "Stop" || eventName === "StopFailure";
|
|
906
936
|
}
|
|
@@ -1068,6 +1098,9 @@ function buildHarnessRefreshPrompt(role) {
|
|
|
1068
1098
|
].join("\n");
|
|
1069
1099
|
}
|
|
1070
1100
|
async function persistTaskSession(fs, repoRoot, stateRoot, session) {
|
|
1101
|
+
if (!hasConfirmedClaudeSessionId(session)) {
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1071
1104
|
const sessionPath = getTaskSessionPath(repoRoot, stateRoot, session.taskSlug);
|
|
1072
1105
|
const empty = createEmptyTaskSessionRecord(session.taskSlug, session.updatedAt);
|
|
1073
1106
|
const current = await fs.pathExists(sessionPath)
|
|
@@ -1104,6 +1137,9 @@ async function persistRoleSessionRecord(fs, baseRepoRoot, taskRepoRoot, stateRoo
|
|
|
1104
1137
|
await persistTaskSession(fs, taskRepoRoot, stateRoot, session);
|
|
1105
1138
|
}
|
|
1106
1139
|
async function persistTranslatorSession(fs, repoRoot, session) {
|
|
1140
|
+
if (!hasConfirmedClaudeSessionId(session)) {
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1107
1143
|
await fs.writeJsonAtomic(resolveRepoPath(repoRoot, TRANSLATOR_SESSION_PATH), {
|
|
1108
1144
|
version: 1,
|
|
1109
1145
|
role: session.role,
|
|
@@ -1115,6 +1151,9 @@ async function persistTranslatorSession(fs, repoRoot, session) {
|
|
|
1115
1151
|
});
|
|
1116
1152
|
}
|
|
1117
1153
|
async function persistHarnessEngineerSession(fs, repoRoot, session) {
|
|
1154
|
+
if (!hasConfirmedClaudeSessionId(session)) {
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1118
1157
|
await fs.writeJsonAtomic(resolveRepoPath(repoRoot, HARNESS_ENGINEER_SESSION_PATH), {
|
|
1119
1158
|
version: 1,
|
|
1120
1159
|
role: session.role,
|
|
@@ -1128,6 +1167,9 @@ async function persistHarnessEngineerSession(fs, repoRoot, session) {
|
|
|
1128
1167
|
function isProjectRoleSessionFile(value) {
|
|
1129
1168
|
return "record" in value && typeof value.record === "object" && value.record !== null;
|
|
1130
1169
|
}
|
|
1170
|
+
function hasConfirmedClaudeSessionId(session) {
|
|
1171
|
+
return session.claudeSessionId.trim().length > 0;
|
|
1172
|
+
}
|
|
1131
1173
|
function createEmptyTaskSessionRecord(taskSlug, updatedAt) {
|
|
1132
1174
|
return {
|
|
1133
1175
|
version: 1,
|