vibe-coding-master 0.3.32 → 0.4.0

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.
@@ -5,6 +5,7 @@ import { promisify } from "node:util";
5
5
  import { renderArchitectHarnessRules } from "../templates/harness/architect-agent.js";
6
6
  import { renderCoderHarnessRules } from "../templates/harness/coder-agent.js";
7
7
  import { renderGateReviewerAgentRules, renderRequestGateReviewTool, renderTranslatorAgentRules, renderVcmGateReviewSkillRules } from "../templates/harness/gate-review.js";
8
+ import { renderHarnessEngineerHarnessRules } from "../templates/harness/harness-engineer-agent.js";
8
9
  import { renderRootClaudeHarnessRules } from "../templates/harness/claude-root.js";
9
10
  import { renderGitignoreHarnessRules } from "../templates/harness/gitignore.js";
10
11
  import { renderProjectManagerHarnessRules } from "../templates/harness/project-manager-agent.js";
@@ -16,6 +17,7 @@ import { renderVcmLongRunningValidationSkillRules } from "../templates/harness/v
16
17
  import { renderVcmRouteMessageSkillRules } from "../templates/harness/vcm-route-message-skill.js";
17
18
  import { submitTerminalInput } from "../runtime/terminal-submit.js";
18
19
  import { VcmError } from "../errors.js";
20
+ import { bumpHarnessRevision, readHarnessRevisionState } from "./harness-revision.js";
19
21
  const execFileAsync = promisify(execFile);
20
22
  const BOOTSTRAP_TASK_SLUG = "__vcm-harness-bootstrap__";
21
23
  const BOOTSTRAP_RUNTIME_ROLE = "project-manager";
@@ -119,6 +121,13 @@ const HARNESS_FILES = [
119
121
  frontmatter: renderAgentFrontmatter("translator", "VCM project translation tool role for conversation translation, file translation, bootstrap, and memory updates."),
120
122
  renderRules: renderTranslatorAgentRules
121
123
  },
124
+ {
125
+ kind: "agent-harness-engineer",
126
+ path: ".claude/agents/harness-engineer.md",
127
+ title: "Harness Engineer Agent",
128
+ frontmatter: renderAgentFrontmatter("harness-engineer", "VCM project-scoped harness maintenance role for harness diagnosis, diff proposals, and VCM issue drafts."),
129
+ renderRules: renderHarnessEngineerHarnessRules
130
+ },
122
131
  {
123
132
  kind: "tool-request-gate-review",
124
133
  path: ".ai/tools/request-gate-review",
@@ -162,11 +171,46 @@ export function createHarnessService(deps) {
162
171
  async getHarnessStatus(repoRoot) {
163
172
  const analyses = await analyzeHarnessFiles(deps.fs, repoRoot);
164
173
  const legacyChanges = await analyzeLegacyCodexHarnessPaths(deps.fs, repoRoot);
165
- return renderHarnessStatus(analyses, legacyChanges);
174
+ return renderHarnessStatus(await readHarnessRevisionValue(deps.fs, repoRoot), analyses, legacyChanges);
175
+ },
176
+ async getHarnessFileContent(repoRoot, filePath) {
177
+ return readHarnessFileContent(deps.fs, repoRoot, filePath);
178
+ },
179
+ async updateHarnessFileContent(repoRoot, filePath, content) {
180
+ const definition = getHarnessFileDefinition(filePath);
181
+ if (!isProjectEditableHarnessFile(definition)) {
182
+ throw new VcmError({
183
+ code: "HARNESS_FILE_READONLY",
184
+ message: "This harness file is managed by VCM and cannot be edited directly.",
185
+ statusCode: 409,
186
+ hint: getReadonlyHarnessReason(definition)
187
+ });
188
+ }
189
+ const absolutePath = resolveHarnessPath(repoRoot, definition.path);
190
+ const currentContent = await deps.fs.pathExists(absolutePath)
191
+ ? await deps.fs.readText(absolutePath)
192
+ : "";
193
+ assertManagedBlockUnchanged(definition, currentContent, content);
194
+ const nextContent = ensureTrailingNewline(content);
195
+ await deps.fs.writeText(absolutePath, nextContent);
196
+ if (nextContent !== currentContent) {
197
+ await bumpHarnessRevision(deps.fs, repoRoot, now());
198
+ }
199
+ const file = await readHarnessFileContent(deps.fs, repoRoot, definition.path);
200
+ const analyses = await analyzeHarnessFiles(deps.fs, repoRoot);
201
+ const legacyChanges = await analyzeLegacyCodexHarnessPaths(deps.fs, repoRoot);
202
+ return {
203
+ file,
204
+ status: renderHarnessStatus(await readHarnessRevisionValue(deps.fs, repoRoot), analyses, legacyChanges)
205
+ };
166
206
  },
167
207
  async applyHarness(repoRoot) {
168
208
  if (deps.runFixedInstaller) {
169
- return deps.runFixedInstaller(repoRoot);
209
+ const result = await deps.runFixedInstaller(repoRoot);
210
+ if (result.changedFiles.length > 0) {
211
+ await bumpHarnessRevision(deps.fs, repoRoot, now());
212
+ }
213
+ return result;
170
214
  }
171
215
  const analyses = await analyzeHarnessFiles(deps.fs, repoRoot);
172
216
  const changedFiles = [];
@@ -181,6 +225,9 @@ export function createHarnessService(deps) {
181
225
  }
182
226
  const legacyChanges = await removeLegacyCodexHarnessPaths(deps.fs, repoRoot);
183
227
  changedFiles.push(...legacyChanges);
228
+ if (changedFiles.length > 0) {
229
+ await bumpHarnessRevision(deps.fs, repoRoot, now());
230
+ }
184
231
  return {
185
232
  version: VCM_HARNESS_VERSION,
186
233
  changedFiles,
@@ -260,64 +307,71 @@ export function createHarnessService(deps) {
260
307
  return getHarnessBootstrapStatus(deps, repoRoot, now);
261
308
  },
262
309
  async startHarnessBootstrap(repoRoot, input = {}) {
263
- if (!deps.runtime || !deps.projectService) {
310
+ const session = await launchHarnessBootstrapSession(deps, repoRoot, now, input, { forceFresh: false });
311
+ const nextStatus = await getHarnessBootstrapStatus(deps, repoRoot, now);
312
+ return {
313
+ status: {
314
+ ...nextStatus,
315
+ session
316
+ },
317
+ session,
318
+ prompt: buildHarnessBootstrapPrompt(repoRoot)
319
+ };
320
+ },
321
+ async restartHarnessBootstrap(repoRoot, input = {}) {
322
+ const existing = await getCurrentHarnessBootstrapSession(deps, repoRoot, now);
323
+ if (existing && deps.runtime?.getSession(existing.id)) {
324
+ await deps.runtime.stop(existing.id);
325
+ }
326
+ const session = await launchHarnessBootstrapSession(deps, repoRoot, now, input, { forceFresh: true });
327
+ const nextStatus = await getHarnessBootstrapStatus(deps, repoRoot, now);
328
+ return {
329
+ status: {
330
+ ...nextStatus,
331
+ session
332
+ },
333
+ session,
334
+ prompt: buildHarnessBootstrapPrompt(repoRoot)
335
+ };
336
+ },
337
+ async stopHarnessBootstrap(repoRoot) {
338
+ const existing = await getCurrentHarnessBootstrapSession(deps, repoRoot, now);
339
+ if (!existing) {
340
+ throw new VcmError({
341
+ code: "HARNESS_BOOTSTRAP_SESSION_MISSING",
342
+ message: "Harness bootstrap terminal has not been started.",
343
+ statusCode: 404
344
+ });
345
+ }
346
+ if (deps.runtime?.getSession(existing.id)) {
347
+ await deps.runtime.stop(existing.id);
348
+ }
349
+ await persistHarnessBootstrapSession(deps.fs, repoRoot, {
350
+ ...existing,
351
+ status: "exited",
352
+ updatedAt: now()
353
+ });
354
+ return getHarnessBootstrapStatus(deps, repoRoot, now);
355
+ },
356
+ async runHarnessBootstrap(repoRoot) {
357
+ if (!deps.runtime) {
264
358
  throw new VcmError({
265
359
  code: "HARNESS_BOOTSTRAP_UNAVAILABLE",
266
360
  message: "Harness bootstrap sessions are not available in this VCM runtime.",
267
361
  statusCode: 501
268
362
  });
269
363
  }
270
- const currentStatus = await getHarnessBootstrapStatus(deps, repoRoot, now);
271
- if (currentStatus.session?.status === "running") {
272
- return {
273
- status: currentStatus,
274
- session: currentStatus.session,
275
- prompt: buildHarnessBootstrapPrompt(repoRoot)
276
- };
277
- }
278
- if (!currentStatus.canStart) {
364
+ const status = await getHarnessBootstrapStatus(deps, repoRoot, now);
365
+ const session = status.session;
366
+ if (!session || session.status !== "running" || !deps.runtime.getSession(session.id)) {
279
367
  throw new VcmError({
280
- code: "HARNESS_BOOTSTRAP_NOT_READY",
281
- message: "Install the fixed VCM harness before running harness bootstrap.",
368
+ code: "HARNESS_BOOTSTRAP_SESSION_NOT_RUNNING",
369
+ message: "Start the Harness Bootstrap terminal before running bootstrap.",
282
370
  statusCode: 409
283
371
  });
284
372
  }
285
- const config = await deps.projectService.loadConfig(repoRoot);
286
- const claudeSessionId = randomUUID();
287
- const command = buildClaudeStartCommand(config.claudeCommand, "default", claudeSessionId);
288
- const logPath = path.join(repoRoot, BOOTSTRAP_LOG_PATH);
289
- const runtimeSession = await deps.runtime.createSession({
290
- taskSlug: BOOTSTRAP_TASK_SLUG,
291
- role: BOOTSTRAP_RUNTIME_ROLE,
292
- command: command.command,
293
- args: command.args,
294
- cwd: repoRoot,
295
- env: {
296
- VCM_API_URL: deps.apiUrl,
297
- VCM_TASK_REPO_ROOT: repoRoot,
298
- VCM_HARNESS_BOOTSTRAP: "1",
299
- VCM_SESSION_ID: claudeSessionId
300
- },
301
- cols: input.cols,
302
- rows: input.rows,
303
- logPath
304
- });
305
- const timestamp = now();
306
- const session = {
307
- id: runtimeSession.id,
308
- claudeSessionId,
309
- status: runtimeSession.status === "crashed" ? "crashed" : runtimeSession.status === "exited" ? "exited" : "running",
310
- command: command.display,
311
- cwd: repoRoot,
312
- logPath: BOOTSTRAP_LOG_PATH,
313
- startedAt: runtimeSession.startedAt,
314
- updatedAt: timestamp,
315
- lastOutputAt: runtimeSession.lastOutputAt,
316
- exitCode: runtimeSession.exitCode
317
- };
318
- await persistHarnessBootstrapSession(deps.fs, repoRoot, session);
319
373
  const prompt = buildHarnessBootstrapPrompt(repoRoot);
320
- await submitTerminalInput(deps.runtime, runtimeSession.id, prompt);
374
+ await submitTerminalInput(deps.runtime, session.id, prompt);
321
375
  const nextStatus = await getHarnessBootstrapStatus(deps, repoRoot, now);
322
376
  return {
323
377
  status: {
@@ -346,6 +400,66 @@ function dedupeHarnessChanges(changedFiles) {
346
400
  }
347
401
  return changes;
348
402
  }
403
+ async function launchHarnessBootstrapSession(deps, repoRoot, now, input, options) {
404
+ if (!deps.runtime || !deps.projectService) {
405
+ throw new VcmError({
406
+ code: "HARNESS_BOOTSTRAP_UNAVAILABLE",
407
+ message: "Harness bootstrap sessions are not available in this VCM runtime.",
408
+ statusCode: 501
409
+ });
410
+ }
411
+ const currentStatus = await getHarnessBootstrapStatus(deps, repoRoot, now);
412
+ if (!options.forceFresh && currentStatus.session?.status === "running") {
413
+ return currentStatus.session;
414
+ }
415
+ if (!currentStatus.canStart && !(options.forceFresh && currentStatus.session?.status === "running")) {
416
+ throw new VcmError({
417
+ code: "HARNESS_BOOTSTRAP_NOT_READY",
418
+ message: "Install the fixed VCM harness before starting harness bootstrap.",
419
+ statusCode: 409
420
+ });
421
+ }
422
+ const config = await deps.projectService.loadConfig(repoRoot);
423
+ const claudeSessionId = randomUUID();
424
+ const permissionMode = normalizeClaudePermissionMode(input.permissionMode);
425
+ const model = normalizeClaudeModel(input.model);
426
+ const effort = normalizeClaudeEffort(input.effort);
427
+ const command = buildClaudeStartCommand(config.claudeCommand, permissionMode, claudeSessionId, model, effort);
428
+ const logPath = path.join(repoRoot, BOOTSTRAP_LOG_PATH);
429
+ const runtimeSession = await deps.runtime.createSession({
430
+ taskSlug: BOOTSTRAP_TASK_SLUG,
431
+ role: BOOTSTRAP_RUNTIME_ROLE,
432
+ command: command.command,
433
+ args: command.args,
434
+ cwd: repoRoot,
435
+ env: {
436
+ VCM_API_URL: deps.apiUrl,
437
+ VCM_TASK_REPO_ROOT: repoRoot,
438
+ VCM_HARNESS_BOOTSTRAP: "1",
439
+ VCM_SESSION_ID: claudeSessionId
440
+ },
441
+ cols: input.cols,
442
+ rows: input.rows,
443
+ logPath
444
+ });
445
+ const session = {
446
+ id: runtimeSession.id,
447
+ claudeSessionId,
448
+ status: runtimeSession.status === "crashed" ? "crashed" : runtimeSession.status === "exited" ? "exited" : "running",
449
+ command: command.display,
450
+ permissionMode,
451
+ model,
452
+ effort,
453
+ cwd: repoRoot,
454
+ logPath: BOOTSTRAP_LOG_PATH,
455
+ startedAt: runtimeSession.startedAt,
456
+ updatedAt: now(),
457
+ lastOutputAt: runtimeSession.lastOutputAt,
458
+ exitCode: runtimeSession.exitCode
459
+ };
460
+ await persistHarnessBootstrapSession(deps.fs, repoRoot, session);
461
+ return session;
462
+ }
349
463
  function normalizeHarnessGitPath(value) {
350
464
  if (!value || value.includes("\0") || path.posix.isAbsolute(value)) {
351
465
  throw new VcmError({
@@ -369,6 +483,104 @@ function normalizeHarnessGitPath(value) {
369
483
  function shortCommit(commit) {
370
484
  return commit.slice(0, 7);
371
485
  }
486
+ async function readHarnessFileContent(fs, repoRoot, filePath) {
487
+ const definition = getHarnessFileDefinition(filePath);
488
+ const absolutePath = resolveHarnessPath(repoRoot, definition.path);
489
+ const exists = await fs.pathExists(absolutePath);
490
+ const content = exists ? await fs.readText(absolutePath) : "";
491
+ const editable = isProjectEditableHarnessFile(definition);
492
+ return {
493
+ path: definition.path,
494
+ kind: definition.kind,
495
+ title: definition.title,
496
+ content,
497
+ editable,
498
+ readonlyReason: editable ? undefined : getReadonlyHarnessReason(definition)
499
+ };
500
+ }
501
+ function getHarnessFileDefinition(filePath) {
502
+ const normalizedPath = normalizeHarnessFilePath(filePath);
503
+ const definition = HARNESS_FILES.find((file) => file.path === normalizedPath) ?? getSpecialHarnessFileDefinition(normalizedPath);
504
+ if (!definition) {
505
+ throw new VcmError({
506
+ code: "HARNESS_FILE_UNKNOWN",
507
+ message: "This path is not a known VCM harness file.",
508
+ statusCode: 404,
509
+ hint: normalizedPath
510
+ });
511
+ }
512
+ return definition;
513
+ }
514
+ function getSpecialHarnessFileDefinition(filePath) {
515
+ if (filePath !== CLAUDE_SETTINGS_PATH) {
516
+ return undefined;
517
+ }
518
+ return {
519
+ kind: "claude-settings",
520
+ path: CLAUDE_SETTINGS_PATH,
521
+ title: "Claude Code Settings",
522
+ ownership: "raw-file",
523
+ renderRules: () => ""
524
+ };
525
+ }
526
+ function normalizeHarnessFilePath(filePath) {
527
+ if (!filePath || filePath.includes("\0") || path.posix.isAbsolute(filePath)) {
528
+ throw new VcmError({
529
+ code: "HARNESS_FILE_PATH_INVALID",
530
+ message: "Harness file path is invalid.",
531
+ statusCode: 400,
532
+ hint: filePath
533
+ });
534
+ }
535
+ const normalized = path.posix.normalize(filePath).replace(/^\.\//, "");
536
+ if (normalized === "." || normalized.startsWith("../")) {
537
+ throw new VcmError({
538
+ code: "HARNESS_FILE_PATH_INVALID",
539
+ message: "Harness file path must stay inside the repository.",
540
+ statusCode: 400,
541
+ hint: filePath
542
+ });
543
+ }
544
+ return normalized;
545
+ }
546
+ function isProjectEditableHarnessFile(definition) {
547
+ return definition.ownership !== "whole-file" && definition.ownership !== "raw-file";
548
+ }
549
+ function getReadonlyHarnessReason(definition) {
550
+ if (definition.path === CLAUDE_SETTINGS_PATH) {
551
+ return "Claude Code hooks are generated by VCM fixed harness install.";
552
+ }
553
+ if (definition.ownership === "whole-file" || definition.ownership === "raw-file") {
554
+ return "This file is VCM-owned whole-file template content. Use fixed harness update instead.";
555
+ }
556
+ return "This harness file is not editable from Harness Studio.";
557
+ }
558
+ function assertManagedBlockUnchanged(definition, currentContent, nextContent) {
559
+ const currentBlock = extractManagedBlock(definition, currentContent);
560
+ const nextBlock = extractManagedBlock(definition, nextContent);
561
+ if (currentBlock && nextBlock !== currentBlock) {
562
+ throw new VcmError({
563
+ code: "HARNESS_MANAGED_BLOCK_PROTECTED",
564
+ message: "VCM fixed managed blocks cannot be edited from Harness Studio.",
565
+ statusCode: 409,
566
+ hint: "Edit project-specific content outside the VCM:BEGIN / VCM:END block, or use fixed harness update."
567
+ });
568
+ }
569
+ if (!currentBlock && nextBlock) {
570
+ throw new VcmError({
571
+ code: "HARNESS_MANAGED_BLOCK_PROTECTED",
572
+ message: "VCM fixed managed blocks must be installed by VCM.",
573
+ statusCode: 409,
574
+ hint: "Use fixed harness update instead of adding a VCM managed block manually."
575
+ });
576
+ }
577
+ }
578
+ function extractManagedBlock(definition, content) {
579
+ if (definition.ownership === "whole-file" || definition.ownership === "raw-file") {
580
+ return undefined;
581
+ }
582
+ return content.match(getManagedBlockPattern(definition))?.[0];
583
+ }
372
584
  async function analyzeHarnessFiles(fs, repoRoot) {
373
585
  const analyses = [];
374
586
  for (const definition of HARNESS_FILES) {
@@ -508,7 +720,7 @@ async function removeLegacyCodexHarnessPaths(fs, repoRoot) {
508
720
  }
509
721
  return changes;
510
722
  }
511
- function renderHarnessStatus(analyses, legacyChanges = []) {
723
+ function renderHarnessStatus(harnessRevision, analyses, legacyChanges = []) {
512
724
  const files = analyses.map((analysis) => analysis.status);
513
725
  const plannedChanges = analyses
514
726
  .map((analysis) => analysis.plannedChange)
@@ -528,6 +740,7 @@ function renderHarnessStatus(analyses, legacyChanges = []) {
528
740
  analysis.status.exists));
529
741
  return {
530
742
  version: VCM_HARNESS_VERSION,
743
+ harnessRevision,
531
744
  initialized,
532
745
  files,
533
746
  needsApply: plannedChanges.length > 0,
@@ -537,6 +750,9 @@ function renderHarnessStatus(analyses, legacyChanges = []) {
537
750
  : []
538
751
  };
539
752
  }
753
+ async function readHarnessRevisionValue(fs, repoRoot) {
754
+ return (await readHarnessRevisionState(fs, repoRoot)).revision;
755
+ }
540
756
  function renderManagedBlock(definition, rules) {
541
757
  const endSpacing = definition.blankLineBeforeEnd ? "\n\n" : "\n";
542
758
  if (definition.commentStyle === "hash") {
@@ -924,6 +1140,9 @@ async function getCurrentHarnessBootstrapSession(deps, repoRoot, now) {
924
1140
  claudeSessionId: persisted?.claudeSessionId ?? runtimeSession.id,
925
1141
  status: runtimeSession.status === "crashed" ? "crashed" : runtimeSession.status === "exited" ? "exited" : "running",
926
1142
  command: persisted?.command ?? "claude",
1143
+ permissionMode: persisted?.permissionMode,
1144
+ model: persisted?.model,
1145
+ effort: persisted?.effort,
927
1146
  cwd: persisted?.cwd ?? repoRoot,
928
1147
  logPath: persisted?.logPath ?? BOOTSTRAP_LOG_PATH,
929
1148
  startedAt: runtimeSession.startedAt,
@@ -965,6 +1184,9 @@ async function loadPersistedHarnessBootstrapSession(fs, repoRoot) {
965
1184
  claudeSessionId: payload.claudeSessionId,
966
1185
  status,
967
1186
  command: payload.command,
1187
+ permissionMode: normalizeClaudePermissionMode(payload.permissionMode),
1188
+ model: normalizeClaudeModel(payload.model),
1189
+ effort: normalizeClaudeEffort(payload.effort),
968
1190
  cwd: payload.cwd,
969
1191
  logPath: payload.logPath,
970
1192
  startedAt: typeof payload.startedAt === "string" ? payload.startedAt : undefined,
@@ -1012,17 +1234,56 @@ function bootstrapWarnings(status, checks, session) {
1012
1234
  }
1013
1235
  return warnings;
1014
1236
  }
1015
- function buildClaudeStartCommand(command = "claude", permissionMode = "default", claudeSessionId) {
1016
- const args = ["--session-id", claudeSessionId];
1237
+ function buildClaudeStartCommand(command = "claude", permissionMode = "default", claudeSessionId, model = "default", effort = "default") {
1238
+ const args = ["--session-id", claudeSessionId, "--model", model];
1239
+ if (effort === "ultracode") {
1240
+ args.push("--settings", JSON.stringify({ ultracode: true }));
1241
+ }
1242
+ else if (effort !== "default") {
1243
+ args.push("--effort", effort);
1244
+ }
1017
1245
  if (permissionMode === "bypassPermissions") {
1018
1246
  args.push("--permission-mode", "bypassPermissions");
1019
1247
  }
1020
1248
  return {
1021
1249
  command,
1022
1250
  args,
1023
- display: `${command} ${args.join(" ")}`
1251
+ display: [command, ...args].map(formatDisplayArg).join(" ")
1024
1252
  };
1025
1253
  }
1254
+ function formatDisplayArg(value) {
1255
+ if (/^[A-Za-z0-9_./:=@+-]+$/.test(value)) {
1256
+ return value;
1257
+ }
1258
+ return `'${value.replaceAll("'", "'\\''")}'`;
1259
+ }
1260
+ function normalizeClaudePermissionMode(value) {
1261
+ return value === "bypassPermissions" || value === "dangerously-skip-permissions"
1262
+ ? "bypassPermissions"
1263
+ : "default";
1264
+ }
1265
+ function normalizeClaudeModel(value) {
1266
+ if (value === "best"
1267
+ || value === "fable"
1268
+ || value === "opus"
1269
+ || value === "opus[1m]"
1270
+ || value === "claude-opus-4-8"
1271
+ || value === "claude-opus-4-8[1m]") {
1272
+ return value;
1273
+ }
1274
+ return "default";
1275
+ }
1276
+ function normalizeClaudeEffort(value) {
1277
+ if (value === "low"
1278
+ || value === "medium"
1279
+ || value === "high"
1280
+ || value === "xhigh"
1281
+ || value === "max"
1282
+ || value === "ultracode") {
1283
+ return value;
1284
+ }
1285
+ return "default";
1286
+ }
1026
1287
  function buildHarnessBootstrapPrompt(repoRoot) {
1027
1288
  return `Use the vcm-harness-bootstrap skill to finish the VCM harness bootstrap for this repository.
1028
1289