@vibecompass/vibecompass 0.10.2 → 0.10.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.
- package/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/src/cli.js +13 -0
- package/src/init.js +3 -1
- package/src/scaffold.js +10 -2
- package/src/session.js +41 -9
- package/src/setup.js +8 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.10.4 - 2026-06-19
|
|
4
|
+
|
|
5
|
+
- Add `vibecompass --version`, `vibecompass -v`, and `vibecompass version` CLI support.
|
|
6
|
+
- Make bare `vibecompass init` errors point users to `vibecompass init --guided`.
|
|
7
|
+
- Clarify guided first-session prompts so lane IDs are presented as short kebab-case slugs and reserved yes/no answers explain that pressing Enter accepts the suggested slug.
|
|
8
|
+
- Propagate declared project repo ids into the first lane opened by guided init.
|
|
9
|
+
|
|
10
|
+
## 0.10.3 - 2026-06-19
|
|
11
|
+
|
|
12
|
+
- Let `start-session` create a missing `## Current session` summary block in adopted existing `CLAUDE.md` files instead of failing after guided init.
|
|
13
|
+
- Use the local calendar date, not UTC, when naming the starter `project-memory-initialized` session note during init.
|
|
14
|
+
|
|
3
15
|
## 0.10.2 - 2026-06-19
|
|
4
16
|
|
|
5
17
|
- Add a package-owned docs-review source inventory scanner that writes `state/docs-review-source-inventory.json`, includes scanned subsystem inventory in the review prompt, and records `scanned_unaccounted` / `source_unavailable` reconciliation warnings without changing the primary coverage score basis.
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -10,6 +10,7 @@ import { syncAgentInstructionFiles } from './generators/agent-files/index.js';
|
|
|
10
10
|
import { getProjectStatus, renderStatusText, toStatusJson } from './status.js';
|
|
11
11
|
import { refreshWorkflow } from './refresh-workflow.js';
|
|
12
12
|
import { inspectProjectCompatibility, formatCompatibilityWarnings } from './compatibility.js';
|
|
13
|
+
import { PACKAGE_VERSION } from './version.js';
|
|
13
14
|
import {
|
|
14
15
|
applyPullExport,
|
|
15
16
|
pullExportProjectMemory,
|
|
@@ -25,6 +26,11 @@ export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
|
25
26
|
return 0;
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
if (parsed.command === 'version') {
|
|
30
|
+
io.stdout.write(`${PACKAGE_VERSION}\n`);
|
|
31
|
+
return 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
28
34
|
if (parsed.command === 'init') {
|
|
29
35
|
const initPlan = await resolveInitCliOptions(parsed.options, {
|
|
30
36
|
cwd: runtime.cwd,
|
|
@@ -99,6 +105,7 @@ export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
|
99
105
|
toolingRootDir: initPlan.initOptions.toolingRootDir,
|
|
100
106
|
sessionId: initPlan.sessionPlan.sessionId,
|
|
101
107
|
workingOn: initPlan.sessionPlan.workingOn,
|
|
108
|
+
repos: initPlan.sessionPlan.repos,
|
|
102
109
|
...(runtime.cwd ? { cwd: runtime.cwd } : {}),
|
|
103
110
|
});
|
|
104
111
|
io.stdout.write(`Started session ${sessionResult.sessionDate}-${sessionResult.sessionNumber}\n`);
|
|
@@ -425,6 +432,10 @@ export function parseCliArgs(argv) {
|
|
|
425
432
|
return { command: 'help' };
|
|
426
433
|
}
|
|
427
434
|
|
|
435
|
+
if (command === 'version' || command === '--version' || command === '-v') {
|
|
436
|
+
return { command: 'version' };
|
|
437
|
+
}
|
|
438
|
+
|
|
428
439
|
if (command !== 'init') {
|
|
429
440
|
if (command === 'start-session') {
|
|
430
441
|
return parseStartSessionArgs(rest);
|
|
@@ -1495,6 +1506,8 @@ function splitAssignment(value, flagName) {
|
|
|
1495
1506
|
function usageText() {
|
|
1496
1507
|
return [
|
|
1497
1508
|
'Usage:',
|
|
1509
|
+
' vibecompass --version',
|
|
1510
|
+
' vibecompass version',
|
|
1498
1511
|
' vibecompass init --name <project-name> --mode <local-only|local-primary|hosted-only> --repo <id=remote> [options]',
|
|
1499
1512
|
' vibecompass connect-hosted [options]',
|
|
1500
1513
|
' vibecompass sync-target [<name>] [options]',
|
package/src/init.js
CHANGED
|
@@ -231,7 +231,9 @@ function normalizeInitOptions(options) {
|
|
|
231
231
|
: cwd;
|
|
232
232
|
|
|
233
233
|
if (typeof preparedOptions.name !== 'string' || preparedOptions.name.trim() === '') {
|
|
234
|
-
throw new Error(
|
|
234
|
+
throw new Error(
|
|
235
|
+
'init requires a non-empty project name. Run guided setup with `vibecompass init --guided`, or provide `--name <project-name>`.',
|
|
236
|
+
);
|
|
235
237
|
}
|
|
236
238
|
|
|
237
239
|
if (typeof preparedOptions.mode !== 'string' || !VALID_MODES.has(preparedOptions.mode)) {
|
package/src/scaffold.js
CHANGED
|
@@ -627,10 +627,18 @@ function describeReviewerHandback(workflow) {
|
|
|
627
627
|
function sessionDateFromGeneratedAt(value) {
|
|
628
628
|
const date = value ? new Date(value) : new Date();
|
|
629
629
|
if (Number.isNaN(date.getTime())) {
|
|
630
|
-
return new Date()
|
|
630
|
+
return formatLocalDate(new Date());
|
|
631
631
|
}
|
|
632
632
|
|
|
633
|
-
return date
|
|
633
|
+
return formatLocalDate(date);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function formatLocalDate(date) {
|
|
637
|
+
return [
|
|
638
|
+
date.getFullYear(),
|
|
639
|
+
String(date.getMonth() + 1).padStart(2, '0'),
|
|
640
|
+
String(date.getDate()).padStart(2, '0'),
|
|
641
|
+
].join('-');
|
|
634
642
|
}
|
|
635
643
|
|
|
636
644
|
function quoteYamlString(value) {
|
package/src/session.js
CHANGED
|
@@ -4,6 +4,7 @@ import { scanProjectMemory } from './project-memory.js';
|
|
|
4
4
|
import { parseSimpleYaml } from './simple-yaml.js';
|
|
5
5
|
import { buildCloseSessionGuidance, resolveWorkflowSettings } from './workflow.js';
|
|
6
6
|
import { syncAgentInstructionFiles } from './generators/agent-files/index.js';
|
|
7
|
+
import { START_MARKER } from './generators/agent-files/markers.js';
|
|
7
8
|
|
|
8
9
|
const CURRENT_SESSION_REQUIRED_FIELDS = ['Date:', 'Working on:', 'Last thing completed:', 'Blockers:', 'Next session should:'];
|
|
9
10
|
const WIP_HEADER_PATTERN = /^# WIP — (\d{4}-\d{2}-\d{2}) \(session (\d+)\)$/m;
|
|
@@ -51,13 +52,13 @@ export async function startProjectSession(options) {
|
|
|
51
52
|
const highestDecisionId = await readHighestDecisionId(normalized.rootDir);
|
|
52
53
|
|
|
53
54
|
const nextSessionNumber = await getNextSessionNumber(normalized.sessionsDir, sessionDate);
|
|
54
|
-
const currentSession = parseCurrentSessionBlock(claude.content);
|
|
55
|
+
const currentSession = parseCurrentSessionBlock(claude.content, { optional: true });
|
|
55
56
|
const updatedClaude = replaceCurrentSessionBlock(claude.content, {
|
|
56
57
|
date: `${sessionDate} (session ${nextSessionNumber}, lane ${sessionId})`,
|
|
57
58
|
workingOn: `${workingOn} [${sessionId}]`,
|
|
58
59
|
lastThingCompleted:
|
|
59
60
|
normalizeOptionalString(options?.lastThingCompleted) ??
|
|
60
|
-
currentSession
|
|
61
|
+
currentSession?.lastThingCompleted ??
|
|
61
62
|
'Project memory bootstrap completed.',
|
|
62
63
|
blockers: normalizeOptionalString(options?.blockers) ?? 'No blocker yet.',
|
|
63
64
|
nextSessionShould:
|
|
@@ -170,11 +171,11 @@ export async function closeProjectSession(options) {
|
|
|
170
171
|
|
|
171
172
|
const wipContent = await readFile(lanePaths.wipFilePath, 'utf8');
|
|
172
173
|
const activeSession = parseActiveSession(wipContent, lanePaths.wipFilePath);
|
|
173
|
-
const currentSession = parseCurrentSessionBlock(claude.content);
|
|
174
|
+
const currentSession = parseCurrentSessionBlock(claude.content, { optional: true });
|
|
174
175
|
const workedOn =
|
|
175
176
|
normalizeOptionalString(options?.workedOn) ??
|
|
176
177
|
extractSectionBody(wipContent, 'Working on') ??
|
|
177
|
-
currentSession
|
|
178
|
+
currentSession?.workingOn;
|
|
178
179
|
|
|
179
180
|
if (!workedOn) {
|
|
180
181
|
throw new Error('session close could not determine what the session worked on. Pass workedOn explicitly.');
|
|
@@ -414,7 +415,7 @@ async function readWorkflowSettings(projectFilePath) {
|
|
|
414
415
|
async function readClaudeFile(claudePath) {
|
|
415
416
|
if (!(await fileExists(claudePath))) {
|
|
416
417
|
throw new Error(
|
|
417
|
-
`No CLAUDE.md was found at ${claudePath}. Re-run "vibecompass init --with-claude" or create
|
|
418
|
+
`No CLAUDE.md was found at ${claudePath}. Re-run "vibecompass init --with-claude" or create the file before using session commands.`,
|
|
418
419
|
);
|
|
419
420
|
}
|
|
420
421
|
|
|
@@ -424,9 +425,13 @@ async function readClaudeFile(claudePath) {
|
|
|
424
425
|
};
|
|
425
426
|
}
|
|
426
427
|
|
|
427
|
-
function parseCurrentSessionBlock(content) {
|
|
428
|
+
function parseCurrentSessionBlock(content, options = {}) {
|
|
428
429
|
const sessionFence = findCurrentSessionFence(content);
|
|
429
430
|
if (!sessionFence) {
|
|
431
|
+
if (options.optional) {
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
434
|
+
|
|
430
435
|
throw new Error('CLAUDE.md is missing a recognizable "## Current session" fenced block.');
|
|
431
436
|
}
|
|
432
437
|
|
|
@@ -453,19 +458,46 @@ function parseCurrentSessionBlock(content) {
|
|
|
453
458
|
|
|
454
459
|
function replaceCurrentSessionBlock(content, fields) {
|
|
455
460
|
const sessionFence = findCurrentSessionFence(content);
|
|
461
|
+
const blockBody = renderCurrentSessionBlockBody(fields);
|
|
462
|
+
|
|
456
463
|
if (!sessionFence) {
|
|
457
|
-
|
|
464
|
+
return insertCurrentSessionBlock(content, blockBody);
|
|
458
465
|
}
|
|
459
466
|
|
|
460
|
-
|
|
467
|
+
return `${content.slice(0, sessionFence.bodyStart)}${blockBody}${content.slice(sessionFence.bodyEnd)}`;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function renderCurrentSessionBlockBody(fields) {
|
|
471
|
+
return [
|
|
461
472
|
`Date: ${fields.date}`,
|
|
462
473
|
`Working on: ${fields.workingOn}`,
|
|
463
474
|
`Last thing completed: ${fields.lastThingCompleted}`,
|
|
464
475
|
`Blockers: ${fields.blockers}`,
|
|
465
476
|
`Next session should: ${fields.nextSessionShould}`,
|
|
466
477
|
].join('\n');
|
|
478
|
+
}
|
|
467
479
|
|
|
468
|
-
|
|
480
|
+
function insertCurrentSessionBlock(content, blockBody) {
|
|
481
|
+
const currentSessionBlock = [
|
|
482
|
+
'## Current session',
|
|
483
|
+
'',
|
|
484
|
+
'**Update this block at the start and end of every session.**',
|
|
485
|
+
'',
|
|
486
|
+
'```',
|
|
487
|
+
blockBody,
|
|
488
|
+
'```',
|
|
489
|
+
'',
|
|
490
|
+
].join('\n');
|
|
491
|
+
const markerIndex = content.indexOf(START_MARKER);
|
|
492
|
+
const normalizedContent = content.endsWith('\n') ? content : `${content}\n`;
|
|
493
|
+
|
|
494
|
+
if (markerIndex >= 0) {
|
|
495
|
+
const beforeMarker = content.slice(0, markerIndex).trimEnd();
|
|
496
|
+
const fromMarker = content.slice(markerIndex);
|
|
497
|
+
return `${beforeMarker}\n\n${currentSessionBlock}${fromMarker}`;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
return `${normalizedContent}\n${currentSessionBlock}`;
|
|
469
501
|
}
|
|
470
502
|
|
|
471
503
|
function buildCurrentSessionFieldsForLane(lane, overrides = {}) {
|
package/src/setup.js
CHANGED
|
@@ -104,6 +104,7 @@ export async function resolveInitCliOptions(options, environment = {}) {
|
|
|
104
104
|
? {
|
|
105
105
|
sessionId: resolved.sessionId,
|
|
106
106
|
workingOn: resolved.sessionWorkingOn,
|
|
107
|
+
repos: resolved.repos.map((repo) => repo.id),
|
|
107
108
|
}
|
|
108
109
|
: null,
|
|
109
110
|
};
|
|
@@ -608,14 +609,15 @@ async function completeGuidedInitOptions(options, environment) {
|
|
|
608
609
|
}
|
|
609
610
|
|
|
610
611
|
if (!resolved.sessionWorkingOn) {
|
|
611
|
-
resolved.sessionWorkingOn = await askInput(prompter, '
|
|
612
|
+
resolved.sessionWorkingOn = await askInput(prompter, 'Work summary for this first session');
|
|
612
613
|
}
|
|
613
614
|
|
|
614
615
|
if (!resolved.sessionId) {
|
|
615
|
-
|
|
616
|
-
|
|
616
|
+
const defaultSessionId = suggestLaneId(resolved.sessionWorkingOn);
|
|
617
|
+
resolved.sessionId = await askInput(prompter, 'Session lane id, short kebab-case slug', {
|
|
618
|
+
defaultValue: defaultSessionId,
|
|
617
619
|
validate(value) {
|
|
618
|
-
return validateLaneIdForPrompt(value);
|
|
620
|
+
return validateLaneIdForPrompt(value, defaultSessionId);
|
|
619
621
|
},
|
|
620
622
|
});
|
|
621
623
|
}
|
|
@@ -1245,7 +1247,7 @@ function suggestLaneId(value) {
|
|
|
1245
1247
|
return slug.length >= 3 ? slug : 'builder-session';
|
|
1246
1248
|
}
|
|
1247
1249
|
|
|
1248
|
-
function validateLaneIdForPrompt(value) {
|
|
1250
|
+
function validateLaneIdForPrompt(value, defaultValue) {
|
|
1249
1251
|
if (!/^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/.test(value)) {
|
|
1250
1252
|
return 'Lane id must be 3-64 lowercase letters, numbers, or hyphens, and must start and end with a letter or number.';
|
|
1251
1253
|
}
|
|
@@ -1268,7 +1270,7 @@ function validateLaneIdForPrompt(value) {
|
|
|
1268
1270
|
'wip',
|
|
1269
1271
|
'yes',
|
|
1270
1272
|
].includes(value)) {
|
|
1271
|
-
return `Lane id "${value}" is reserved.`;
|
|
1273
|
+
return `Lane id "${value}" is reserved. Enter a short kebab-case lane id, or press Enter to use "${defaultValue}".`;
|
|
1272
1274
|
}
|
|
1273
1275
|
|
|
1274
1276
|
return null;
|