vibe-coding-master 0.5.2 → 0.5.4

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.
@@ -135,15 +135,23 @@ export function createSessionService(deps) {
135
135
  });
136
136
  }
137
137
  await deps.fs.ensureDir(resolveRepoPath(repoRoot, TRANSLATION_DIR));
138
- const launchCwd = launchMode === "resume"
139
- ? persisted?.cwd ?? taskContext.taskRepoRoot
140
- : taskContext.taskRepoRoot;
138
+ // Project-level tool sessions always launch (and resume) from the base
139
+ // repoRoot. Claude anchors a session transcript to its first-launch cwd and
140
+ // `/cd` never relocates that transcript, so a constant repoRoot anchor keeps
141
+ // `claude --resume` valid even after the prior task worktree is deleted. The
142
+ // active task worktree is entered afterwards via `/cd`
143
+ // (migrateRunningProjectToolSessionCwd), and the task root is also exposed
144
+ // independently of pty cwd via VCM_TASK_REPO_ROOT.
145
+ const launchCwd = repoRoot;
146
+ // `claude --resume` restores the session's last working directory, so a resumed
147
+ // session is already at its persisted cwd (not the repoRoot spawn cwd). Track that
148
+ // restored cwd so the `/cd` migrate below fires only on an actual switch; a fresh
149
+ // session genuinely starts at repoRoot.
150
+ const sessionCwd = launchMode === "resume" ? persisted?.cwd ?? launchCwd : launchCwd;
141
151
  const claudeSessionId = resumeClaudeSessionId ?? "";
142
- const transcriptPath = launchMode === "resume" && persisted?.transcriptPath
143
- ? persisted.transcriptPath
144
- : resumeClaudeSessionId
145
- ? claudeTranscriptPath(launchCwd, resumeClaudeSessionId)
146
- : undefined;
152
+ const transcriptPath = resumeClaudeSessionId
153
+ ? claudeTranscriptPath(repoRoot, resumeClaudeSessionId)
154
+ : undefined;
147
155
  const startCommand = {
148
156
  ...deps.claude.buildRoleStartCommand(TRANSLATOR_ROLE, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort),
149
157
  cwd: launchCwd
@@ -158,7 +166,10 @@ export function createSessionService(deps) {
158
166
  VCM_API_URL: deps.apiUrl,
159
167
  VCM_BASE_REPO_ROOT: repoRoot,
160
168
  VCM_TASK_REPO_ROOT: taskContext.taskRepoRoot,
161
- VCM_TASK_SLUG: taskContext.taskSlug,
169
+ // Project-scoped sessions report their project sentinel as VCM_TASK_SLUG so
170
+ // hook payloads match this session's record and cannot be attributed to the
171
+ // active task; VCM_TASK_REPO_ROOT remains the active worktree.
172
+ VCM_TASK_SLUG: PROJECT_TRANSLATOR_SCOPE,
162
173
  VCM_ROLE: TRANSLATOR_ROLE,
163
174
  VCM_SESSION_ID: claudeSessionId || undefined
164
175
  },
@@ -179,7 +190,7 @@ export function createSessionService(deps) {
179
190
  permissionMode,
180
191
  model,
181
192
  effort,
182
- cwd: startCommand.cwd,
193
+ cwd: sessionCwd,
183
194
  terminalBackend: "node-pty",
184
195
  pid: runtimeSession.pid,
185
196
  startedAt: runtimeSession.startedAt,
@@ -215,15 +226,19 @@ export function createSessionService(deps) {
215
226
  });
216
227
  }
217
228
  await deps.fs.ensureDir(resolveRepoPath(repoRoot, HARNESS_ENGINEER_DIR));
218
- const launchCwd = launchMode === "resume"
219
- ? persisted?.cwd ?? taskContext.taskRepoRoot
220
- : taskContext.taskRepoRoot;
229
+ // See launchProjectTranslatorSession: project-level tool sessions launch and
230
+ // resume from the base repoRoot so the transcript anchor stays stable and
231
+ // resume never depends on a possibly-deleted task worktree. The active task
232
+ // worktree is entered afterwards via `/cd`.
233
+ const launchCwd = repoRoot;
234
+ // `claude --resume` restores the session's last working directory, so a resumed
235
+ // session is already at its persisted cwd (not the repoRoot spawn cwd). Track that
236
+ // restored cwd so the `/cd` migrate below fires only on an actual switch.
237
+ const sessionCwd = launchMode === "resume" ? persisted?.cwd ?? launchCwd : launchCwd;
221
238
  const claudeSessionId = resumeClaudeSessionId ?? "";
222
- const transcriptPath = launchMode === "resume" && persisted?.transcriptPath
223
- ? persisted.transcriptPath
224
- : resumeClaudeSessionId
225
- ? claudeTranscriptPath(launchCwd, resumeClaudeSessionId)
226
- : undefined;
239
+ const transcriptPath = resumeClaudeSessionId
240
+ ? claudeTranscriptPath(repoRoot, resumeClaudeSessionId)
241
+ : undefined;
227
242
  const startCommand = {
228
243
  ...deps.claude.buildRoleStartCommand(HARNESS_ENGINEER_ROLE, config.claudeCommand, permissionMode, resumeClaudeSessionId, launchMode === "resume", model, effort),
229
244
  cwd: launchCwd
@@ -238,7 +253,10 @@ export function createSessionService(deps) {
238
253
  VCM_API_URL: deps.apiUrl,
239
254
  VCM_BASE_REPO_ROOT: repoRoot,
240
255
  VCM_TASK_REPO_ROOT: taskContext.taskRepoRoot,
241
- VCM_TASK_SLUG: taskContext.taskSlug,
256
+ // Project-scoped sessions report their project sentinel as VCM_TASK_SLUG so
257
+ // hook payloads match this session's record and cannot be attributed to the
258
+ // active task; VCM_TASK_REPO_ROOT remains the active worktree.
259
+ VCM_TASK_SLUG: PROJECT_HARNESS_ENGINEER_SCOPE,
242
260
  VCM_ROLE: HARNESS_ENGINEER_ROLE,
243
261
  VCM_SESSION_ID: claudeSessionId || undefined
244
262
  },
@@ -259,7 +277,7 @@ export function createSessionService(deps) {
259
277
  permissionMode,
260
278
  model,
261
279
  effort,
262
- cwd: startCommand.cwd,
280
+ cwd: sessionCwd,
263
281
  terminalBackend: "node-pty",
264
282
  pid: runtimeSession.pid,
265
283
  startedAt: runtimeSession.startedAt,
@@ -305,13 +323,13 @@ export function createSessionService(deps) {
305
323
  await submitTerminalInput(deps.runtime, session.id, formatClaudeCdCommand(targetCwd), {
306
324
  enterDelayMs: PROJECT_TOOL_CD_ENTER_DELAY_MS
307
325
  });
326
+ // `cwd` tracks the logical `/cd` target only. The transcript stays anchored at
327
+ // the first-launch cwd (repoRoot for project tools), so transcriptPath must
328
+ // not be recomputed from targetCwd here.
308
329
  const updated = {
309
330
  ...session,
310
331
  cwd: targetCwd,
311
332
  previousCwd: session.cwd,
312
- transcriptPath: session.claudeSessionId
313
- ? claudeTranscriptPath(targetCwd, session.claudeSessionId)
314
- : session.transcriptPath,
315
333
  updatedAt: timestamp
316
334
  };
317
335
  deps.registry.upsert(normalizeProjectScopedRecordForPersistence(updated));
@@ -329,7 +347,11 @@ export function createSessionService(deps) {
329
347
  const permissionMode = normalizeClaudePermissionMode(session.permissionMode);
330
348
  const model = normalizeClaudeModel(session.model);
331
349
  const effort = normalizeClaudeEffort(session.effort);
332
- const launchCwd = session.cwd || targetCwd;
350
+ // Spawn (`claude --resume`) always anchors at the base repoRoot so resume works
351
+ // even if the persisted task cwd was deleted. `--resume` then restores the
352
+ // session's own last cwd (tracked on `session.cwd`), so the `/cd` migrate below
353
+ // fires only when that restored cwd differs from the target worktree.
354
+ const launchCwd = repoRoot;
333
355
  const startCommand = {
334
356
  ...deps.claude.buildRoleStartCommand(session.role, config.claudeCommand, permissionMode, session.claudeSessionId, true, model, effort),
335
357
  cwd: launchCwd
@@ -359,13 +381,14 @@ export function createSessionService(deps) {
359
381
  permissionMode,
360
382
  model,
361
383
  effort,
362
- cwd: launchCwd,
363
384
  pid: runtimeSession.pid,
364
385
  startedAt: runtimeSession.startedAt,
365
386
  updatedAt: timestamp,
366
387
  lastOutputAt: runtimeSession.lastOutputAt,
367
388
  exitCode: runtimeSession.exitCode,
368
- transcriptPath: session.transcriptPath ?? claudeTranscriptPath(launchCwd, session.claudeSessionId)
389
+ transcriptPath: session.claudeSessionId
390
+ ? claudeTranscriptPath(repoRoot, session.claudeSessionId)
391
+ : session.transcriptPath
369
392
  };
370
393
  deps.registry.upsert(normalizeProjectScopedRecordForPersistence(resumed));
371
394
  await persistProjectScopedToolSession(repoRoot, resumed);
@@ -1264,5 +1287,9 @@ function normalizeClaudeEffort(value) {
1264
1287
  return "default";
1265
1288
  }
1266
1289
  function formatClaudeCdCommand(targetCwd) {
1267
- return `/cd ${JSON.stringify(targetCwd)}`;
1290
+ // Claude Code's `/cd` slash command takes the literal remainder of the line as
1291
+ // the path, so the target must NOT be wrapped in quotes (quotes are taken as part
1292
+ // of the path and the cd fails). Paths with spaces are still fine unquoted; a
1293
+ // newline is the only unsafe character and is rejected by assertSafeCwdTarget.
1294
+ return `/cd ${targetCwd}`;
1268
1295
  }
@@ -84,6 +84,16 @@ export function isHarnessEngineerToolRoleName(value) {
84
84
  export function isDispatchableRole(value) {
85
85
  return DISPATCHABLE_ROLES.includes(value);
86
86
  }
87
+ /**
88
+ * Roles that address the human operator. When a role in this set ends its turn
89
+ * with no onward route message, the flow is treated as awaiting a user decision
90
+ * (see the `awaiting-user` flow-pause reason). Single source of truth for that
91
+ * predicate.
92
+ */
93
+ export const USER_FACING_ROLES = ["project-manager"];
94
+ export function isUserFacingRole(value) {
95
+ return USER_FACING_ROLES.includes(value);
96
+ }
87
97
  export function getRoleDefinition(role) {
88
98
  const definition = ROLE_DEFINITIONS.find((candidate) => candidate.name === role);
89
99
  if (!definition) {