@vibecompass/vibecompass 0.10.4 → 0.10.6
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 +16 -0
- package/README.md +7 -2
- package/package.json +1 -1
- package/src/cli.js +101 -2
- package/src/docs-review.js +104 -6
- package/src/docs-update.js +538 -0
- package/src/index.js +1 -0
- package/src/project-memory.js +27 -0
- package/src/scaffold.js +29 -6
- package/src/session.js +134 -0
- package/src/status.js +91 -2
- package/src/workflow.js +1 -0
- package/src/workflows/registry.js +2 -1
package/src/scaffold.js
CHANGED
|
@@ -216,7 +216,7 @@ This workspace uses VibeCompass project memory rooted at \`${rootRelativePath}\`
|
|
|
216
216
|
## Canonical files
|
|
217
217
|
- \`${rootRelativePath}/project.yaml\` — machine-oriented project metadata
|
|
218
218
|
- \`${rootRelativePath}/architecture/\` — canonical architecture docs
|
|
219
|
-
- \`${rootRelativePath}/decisions/\` —
|
|
219
|
+
- \`${rootRelativePath}/decisions/\` — append-only decision log for this project-memory root
|
|
220
220
|
- \`${rootRelativePath}/sessions/\` — finalized session notes
|
|
221
221
|
|
|
222
222
|
## Derived and scratch files
|
|
@@ -233,7 +233,7 @@ This workspace uses VibeCompass project memory rooted at \`${rootRelativePath}\`
|
|
|
233
233
|
- The active lane scratch files live under \`${rootRelativePath}/sessions/active/<lane-id>/\`.
|
|
234
234
|
- \`${rootRelativePath}/sessions/active/index.yaml\` is the authoritative current lane pointer; the tool-specific Current session block is a human-readable continuity summary, not the lane-selection source of truth.
|
|
235
235
|
- Finalized sessions are append-only notes named \`${rootRelativePath}/sessions/YYYY-MM-DD-N-title.md\`; multiple sessions on the same day increment \`N\`.
|
|
236
|
-
- Decisions remain append-only and independent from session notes. A session note may reference decisions, but the decision entry in \`${rootRelativePath}/decisions/\` is the durable
|
|
236
|
+
- Decisions remain append-only and independent from session notes. A session note may reference decisions, but the decision entry in \`${rootRelativePath}/decisions/\` is the durable decision record for this root.
|
|
237
237
|
|
|
238
238
|
## Repos in scope
|
|
239
239
|
${repos}
|
|
@@ -248,7 +248,7 @@ ${repos}
|
|
|
248
248
|
${promptCommands}
|
|
249
249
|
|
|
250
250
|
These are prompt commands for agent behavior, not \`vibecompass\` CLI subcommands.
|
|
251
|
-
Reviewer handback is explicit: the reviewer ends the pass by updating the selected lane's \`wip.md\` + \`handoff.md\` and then stopping. The \`close session\` prompt runs builder close-out and ends with \`vibecompass close-session --session <lane-id
|
|
251
|
+
Reviewer handback is explicit: the reviewer ends the pass by updating the selected lane's \`wip.md\` + \`handoff.md\` and then stopping. The \`close session\` prompt runs builder close-out and ends with \`vibecompass close-session --session <lane-id>\` plus document-maintenance checkpoint statuses, which follows the workflow defaults recorded in \`${rootRelativePath}/project.yaml\`.
|
|
252
252
|
\`vibecompass end-session\` is also accepted as an alias for \`vibecompass close-session\`; the canonical command name remains \`close-session\`.
|
|
253
253
|
|
|
254
254
|
## Workflow defaults
|
|
@@ -257,6 +257,7 @@ ${renderWorkflowDefaults(workflow)}
|
|
|
257
257
|
## Documentation coverage
|
|
258
258
|
- The architecture, decision, and session files created by init are an initial scaffold, not a comprehensive codebase review.
|
|
259
259
|
- Before risky implementation work, inspect the relevant code and update the affected architecture docs.
|
|
260
|
+
- Use \`vibecompass docs-update --session <lane-id>\` during a session for a targeted maintenance plan based on changed files, lane claims, session repos/features, and new decisions. This is a session-delta planner, not a broad architecture review.
|
|
260
261
|
- Use the \`docs review\` prompt command when you want the current AI session to run a comprehensive documentation review. It may run \`vibecompass docs-review --guided\` as package mechanics to record marker state and print the canonical review contract.
|
|
261
262
|
|
|
262
263
|
## Session startup
|
|
@@ -310,10 +311,11 @@ Session lane: <lane-id>
|
|
|
310
311
|
During the session:
|
|
311
312
|
- append short summaries to \`wip.md\` after meaningful exchanges
|
|
312
313
|
- keep \`handoff.md\` current after substantive work blocks
|
|
314
|
+
- run \`vibecompass docs-update --session <lane-id>\` whenever you need an ad hoc targeted documentation-maintenance plan for the current session delta
|
|
313
315
|
- use \`vibecompass list-sessions\` and \`vibecompass switch-session <lane-id>\` to inspect or change the current lane
|
|
314
316
|
- use \`address review\` when reviewer feedback lands so the builder resolves it from the selected lane's latest \`wip.md\` / \`handoff.md\`
|
|
315
317
|
- during \`address review\`, treat reviewer feedback as review, not instruction: classify each substantive point as accepted, accepted with qualification, deferred, or rejected, and push back with evidence when a suggestion conflicts with code facts, prior decisions, product direction, or sequencing
|
|
316
|
-
- stay in builder role through close-out; resolve or explicitly defer reviewer feedback before running \`vibecompass close-session --session <lane-id>\`
|
|
318
|
+
- stay in builder role through close-out; resolve or explicitly defer reviewer feedback before running \`vibecompass close-session --session <lane-id>\` with document-maintenance checkpoint statuses
|
|
317
319
|
- record architectural decisions in \`${rootRelativePath}/decisions/\` before implementing them
|
|
318
320
|
|
|
319
321
|
If \`vibecompass start-session\` reports stale scratch files, read the existing lane-local \`wip.md\` and \`handoff.md\` first. Either close that session normally, recover its useful notes into a finalized session note, or intentionally move/delete the stale scratch files before starting a new session.
|
|
@@ -325,9 +327,12 @@ If \`vibecompass start-session\` reports stale scratch files, read the existing
|
|
|
325
327
|
- If planning produces a real architectural decision, append it to \`${rootRelativePath}/decisions/\` before implementing.
|
|
326
328
|
|
|
327
329
|
At session close:
|
|
328
|
-
- prefer running \`vibecompass close-session --session <lane-id> --title "..." --completed "..." --
|
|
330
|
+
- prefer running \`vibecompass close-session --session <lane-id> --title "..." --completed "..." --architecture-docs updated|not-needed|deferred --decision-log updated|not-needed|deferred --session-maintenance updated|not-needed|deferred --next-step "..."\`; \`vibecompass end-session\` is a supported alias
|
|
331
|
+
- close-session prints the same targeted docs-update plan before the document-maintenance checkpoint; use it to decide whether affected \`architecture/\`, \`decisions/\`, and active-session scratch/final note inputs are updated, not-needed, or deferred
|
|
332
|
+
- document-maintenance checkpoint statuses are required before close-session writes the finalized note; the package validates the status values, while the closer owns semantic doc authorship
|
|
329
333
|
- follow the stored close-session defaults from \`${rootRelativePath}/project.yaml\`
|
|
330
|
-
- finalize the lane-local \`wip.md\` into \`${rootRelativePath}/sessions/YYYY-MM-DD-N-title.md\`
|
|
334
|
+
- finalize the lane-local \`wip.md\` into \`${rootRelativePath}/sessions/YYYY-MM-DD-N-title.md\`; the permanent note distills decisions, completions, blockers, and next steps rather than preserving the full \`## Review log\`
|
|
335
|
+
- if a granular reviewer trail must remain durable, summarize it explicitly in the session note inputs or create a separate finalized session note before close-session deletes lane scratch files
|
|
331
336
|
- delete the closed lane directory under \`${rootRelativePath}/sessions/active/<lane-id>/\`
|
|
332
337
|
- refresh any affected architecture/decision docs
|
|
333
338
|
|
|
@@ -371,6 +376,10 @@ Initial project-memory scaffold for ${projectConfig.name}.
|
|
|
371
376
|
|
|
372
377
|
${projectConfig.description ? `${projectConfig.description}\n` : ''}This document is a starting map, not a comprehensive architecture review.
|
|
373
378
|
|
|
379
|
+
## Review metadata
|
|
380
|
+
- Evidence: \`project.yaml\`, declared repo descriptors, and the files created by \`vibecompass init\`
|
|
381
|
+
- Blindspots: Runtime architecture, feature flows, persistence, integrations, deployment, observability, and test strategy are not reviewed by the init scaffold.
|
|
382
|
+
|
|
374
383
|
## Details
|
|
375
384
|
- Mode: \`${projectConfig.mode}\`
|
|
376
385
|
- Documentation coverage: initial scaffold
|
|
@@ -394,6 +403,11 @@ Not yet documented:
|
|
|
394
403
|
|
|
395
404
|
Before changing any undocumented area, inspect the relevant code and update or add architecture docs.
|
|
396
405
|
|
|
406
|
+
## Retrieval guidance
|
|
407
|
+
- Use this doc to understand the project-memory root shape, declared repositories, and the fact that the initial coverage is scaffold-only.
|
|
408
|
+
- Do not use this doc as evidence for runtime behavior, feature ownership, data flow, external integrations, or deployment behavior.
|
|
409
|
+
- Prefer more specific domain/feature/component docs once they exist, and run \`vibecompass docs-update --session <lane-id>\` during active work to find targeted maintenance needs.
|
|
410
|
+
|
|
397
411
|
## Next steps
|
|
398
412
|
- Use the \`docs review\` prompt command for a comprehensive documentation review.
|
|
399
413
|
- Replace this placeholder with domain/feature/component docs as the project map becomes evidence-backed.
|
|
@@ -487,10 +501,18 @@ architecture/<domain-slug>/<feature-slug>/<component>.md
|
|
|
487
501
|
|
|
488
502
|
## Recommended sections
|
|
489
503
|
- \`## Description\`
|
|
504
|
+
- \`## Review metadata\`
|
|
490
505
|
- \`## Details\`
|
|
506
|
+
- \`## Retrieval guidance\`
|
|
491
507
|
- \`## Next steps\`
|
|
492
508
|
- \`## Involved files\`
|
|
493
509
|
|
|
510
|
+
## Shared quality bar
|
|
511
|
+
- Review metadata should record concrete evidence sources and blindspots.
|
|
512
|
+
- Retrieval guidance should say when to consult the doc, what it does not cover, and which related docs or decisions matter.
|
|
513
|
+
- Details should distinguish confirmed behavior from known gaps or open follow-up.
|
|
514
|
+
- Involved files should use concrete \`repo:path\` references that match the affected implementation surface.
|
|
515
|
+
|
|
494
516
|
Each component doc is canonical. This README is only a convenience guide.
|
|
495
517
|
`;
|
|
496
518
|
}
|
|
@@ -600,6 +622,7 @@ function buildScaffoldAgentContext(options) {
|
|
|
600
622
|
function renderWorkflowDefaults(workflow) {
|
|
601
623
|
const lines = [
|
|
602
624
|
`- reviewer handback: ${describeReviewerHandback(workflow)}`,
|
|
625
|
+
'- close-session: require document-maintenance checkpoint statuses for architecture docs, decision log, and session handoff/scratch',
|
|
603
626
|
'- close-session: refresh any relevant architecture docs before finalizing the session',
|
|
604
627
|
'- close-session: refresh any relevant decision files before finalizing the session',
|
|
605
628
|
];
|
package/src/session.js
CHANGED
|
@@ -5,11 +5,34 @@ 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
7
|
import { START_MARKER } from './generators/agent-files/markers.js';
|
|
8
|
+
import { planDocsUpdate } from './docs-update.js';
|
|
9
|
+
// This lazy-only cycle is intentional: manifest.js reads lane state from this
|
|
10
|
+
// module, and session command handlers rewrite the derived manifest after
|
|
11
|
+
// lane mutations. Keep both sides free of top-level calls into the other.
|
|
12
|
+
import { writeStateManifest } from './manifest.js';
|
|
8
13
|
|
|
9
14
|
const CURRENT_SESSION_REQUIRED_FIELDS = ['Date:', 'Working on:', 'Last thing completed:', 'Blockers:', 'Next session should:'];
|
|
10
15
|
const WIP_HEADER_PATTERN = /^# WIP — (\d{4}-\d{2}-\d{2}) \(session (\d+)\)$/m;
|
|
11
16
|
const SESSION_FILENAME_PATTERN = /^(\d{4}-\d{2}-\d{2})-(\d+)-([a-z0-9-]+)\.md$/i;
|
|
12
17
|
const LANE_ID_PATTERN = /^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/;
|
|
18
|
+
const DOCUMENT_MAINTENANCE_STATUSES = new Set(['updated', 'not-needed', 'deferred']);
|
|
19
|
+
const DOCUMENT_MAINTENANCE_FIELDS = [
|
|
20
|
+
{
|
|
21
|
+
key: 'architectureDocs',
|
|
22
|
+
label: 'Architecture docs',
|
|
23
|
+
flag: '--architecture-docs',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
key: 'decisionLog',
|
|
27
|
+
label: 'Decision log',
|
|
28
|
+
flag: '--decision-log',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
key: 'sessionMaintenance',
|
|
32
|
+
label: 'Session handoff/scratch',
|
|
33
|
+
flag: '--session-maintenance',
|
|
34
|
+
},
|
|
35
|
+
];
|
|
13
36
|
const RESERVED_LANE_IDS = new Set([
|
|
14
37
|
'active',
|
|
15
38
|
'current',
|
|
@@ -114,6 +137,7 @@ export async function startProjectSession(options) {
|
|
|
114
137
|
status: 'active',
|
|
115
138
|
workingOn,
|
|
116
139
|
}, { current: sessionId });
|
|
140
|
+
const manifestRefresh = await refreshStateManifestSafely(normalized.rootDir);
|
|
117
141
|
const auditWarnings = await buildDocsReviewWarnings(normalized);
|
|
118
142
|
const metadataWarnings = collectLaneMetadataWarnings(existingLanes);
|
|
119
143
|
const agentFileSync = await syncAgentInstructionFilesSafely({
|
|
@@ -134,8 +158,10 @@ export async function startProjectSession(options) {
|
|
|
134
158
|
warnings: [
|
|
135
159
|
...metadataWarnings,
|
|
136
160
|
...overlapWarnings,
|
|
161
|
+
...manifestRefresh.warnings,
|
|
137
162
|
...auditWarnings,
|
|
138
163
|
],
|
|
164
|
+
manifest: manifestRefresh.manifest,
|
|
139
165
|
agentFileSync,
|
|
140
166
|
};
|
|
141
167
|
}
|
|
@@ -162,6 +188,14 @@ export async function closeProjectSession(options) {
|
|
|
162
188
|
const workflowSettings = await readWorkflowSettings(normalized.projectFilePath);
|
|
163
189
|
const sessionId = await resolveExistingSessionId(normalized, options);
|
|
164
190
|
const lanePaths = sessionId ? getLanePaths(normalized, sessionId) : normalized;
|
|
191
|
+
const documentMaintenance = normalizeDocumentMaintenanceCheckpoint(options?.documentMaintenance);
|
|
192
|
+
const docsUpdate = await planDocsUpdateSafely({
|
|
193
|
+
rootDir: normalized.rootDir,
|
|
194
|
+
cwd: normalized.cwd,
|
|
195
|
+
sessionId,
|
|
196
|
+
// Test injection seam; production callers should let close-session use the default planner.
|
|
197
|
+
planner: options?.docsUpdatePlanner,
|
|
198
|
+
});
|
|
165
199
|
|
|
166
200
|
if (!(await fileExists(lanePaths.wipFilePath))) {
|
|
167
201
|
throw new Error(
|
|
@@ -205,6 +239,7 @@ export async function closeProjectSession(options) {
|
|
|
205
239
|
completed,
|
|
206
240
|
decisions,
|
|
207
241
|
models: models.length > 0 ? models : ['Not recorded.'],
|
|
242
|
+
documentMaintenance,
|
|
208
243
|
blockers,
|
|
209
244
|
nextSteps,
|
|
210
245
|
}),
|
|
@@ -241,6 +276,7 @@ export async function closeProjectSession(options) {
|
|
|
241
276
|
);
|
|
242
277
|
|
|
243
278
|
await writeFile(normalized.claudePath, updatedClaude, 'utf8');
|
|
279
|
+
const manifestRefresh = await refreshStateManifestSafely(normalized.rootDir);
|
|
244
280
|
const agentFileSync = await syncAgentInstructionFilesSafely({
|
|
245
281
|
rootDir: normalized.rootDir,
|
|
246
282
|
toolingRootDir: normalized.toolingRootDir,
|
|
@@ -256,7 +292,14 @@ export async function closeProjectSession(options) {
|
|
|
256
292
|
sessionRelativePath,
|
|
257
293
|
sessionDate: activeSession.sessionDate,
|
|
258
294
|
sessionNumber: activeSession.sessionNumber,
|
|
295
|
+
documentMaintenance,
|
|
296
|
+
docsUpdatePlan: docsUpdate.plan,
|
|
259
297
|
workflowGuidance: buildCloseSessionGuidance(workflowSettings),
|
|
298
|
+
warnings: [
|
|
299
|
+
...docsUpdate.warnings,
|
|
300
|
+
...manifestRefresh.warnings,
|
|
301
|
+
],
|
|
302
|
+
manifest: manifestRefresh.manifest,
|
|
260
303
|
agentFileSync,
|
|
261
304
|
removedScratchFiles: [
|
|
262
305
|
lanePaths.wipFilePath,
|
|
@@ -265,6 +308,46 @@ export async function closeProjectSession(options) {
|
|
|
265
308
|
};
|
|
266
309
|
}
|
|
267
310
|
|
|
311
|
+
function normalizeDocumentMaintenanceCheckpoint(value) {
|
|
312
|
+
const source = value && typeof value === 'object' && !Array.isArray(value)
|
|
313
|
+
? value
|
|
314
|
+
: {};
|
|
315
|
+
const missing = [];
|
|
316
|
+
const invalid = [];
|
|
317
|
+
const checkpoint = {};
|
|
318
|
+
|
|
319
|
+
for (const field of DOCUMENT_MAINTENANCE_FIELDS) {
|
|
320
|
+
const normalized = normalizeOptionalString(source[field.key]);
|
|
321
|
+
if (!normalized) {
|
|
322
|
+
missing.push(`${field.flag} <updated|not-needed|deferred>`);
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (!DOCUMENT_MAINTENANCE_STATUSES.has(normalized)) {
|
|
327
|
+
invalid.push(`${field.flag}=${normalized}`);
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
checkpoint[field.key] = normalized;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (missing.length > 0 || invalid.length > 0) {
|
|
335
|
+
const details = [
|
|
336
|
+
missing.length > 0
|
|
337
|
+
? `Missing document-maintenance checkpoint status: ${missing.join(', ')}.`
|
|
338
|
+
: null,
|
|
339
|
+
invalid.length > 0
|
|
340
|
+
? `Invalid document-maintenance checkpoint status: ${invalid.join(', ')}. Allowed values: updated, not-needed, deferred.`
|
|
341
|
+
: null,
|
|
342
|
+
'The package validates the close-session checkpoint, but the closer remains responsible for semantic doc updates.',
|
|
343
|
+
].filter(Boolean);
|
|
344
|
+
|
|
345
|
+
throw new Error(details.join(' '));
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return checkpoint;
|
|
349
|
+
}
|
|
350
|
+
|
|
268
351
|
export async function listProjectSessions(options = {}) {
|
|
269
352
|
const normalized = normalizeSessionPaths(options);
|
|
270
353
|
await ensureInitializedProjectMemory(normalized.rootDir, { allowMissingProjectFile: true });
|
|
@@ -300,6 +383,7 @@ export async function switchProjectSession(options = {}) {
|
|
|
300
383
|
),
|
|
301
384
|
'utf8',
|
|
302
385
|
);
|
|
386
|
+
const manifestRefresh = await refreshStateManifestSafely(normalized.rootDir);
|
|
303
387
|
const agentFileSync = await syncAgentInstructionFilesSafely({
|
|
304
388
|
rootDir: normalized.rootDir,
|
|
305
389
|
toolingRootDir: normalized.toolingRootDir,
|
|
@@ -310,10 +394,50 @@ export async function switchProjectSession(options = {}) {
|
|
|
310
394
|
claudePath: normalized.claudePath,
|
|
311
395
|
current: sessionId,
|
|
312
396
|
lanes,
|
|
397
|
+
warnings: manifestRefresh.warnings,
|
|
398
|
+
manifest: manifestRefresh.manifest,
|
|
313
399
|
agentFileSync,
|
|
314
400
|
};
|
|
315
401
|
}
|
|
316
402
|
|
|
403
|
+
async function refreshStateManifestSafely(rootDir) {
|
|
404
|
+
try {
|
|
405
|
+
const manifest = await writeStateManifest(rootDir);
|
|
406
|
+
return {
|
|
407
|
+
manifest,
|
|
408
|
+
warnings: [],
|
|
409
|
+
};
|
|
410
|
+
} catch (error) {
|
|
411
|
+
return {
|
|
412
|
+
manifest: null,
|
|
413
|
+
warnings: [
|
|
414
|
+
`State manifest refresh skipped: ${error instanceof Error ? error.message : String(error)}`,
|
|
415
|
+
],
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async function planDocsUpdateSafely(options) {
|
|
421
|
+
try {
|
|
422
|
+
const planner = typeof options.planner === 'function' ? options.planner : planDocsUpdate;
|
|
423
|
+
return {
|
|
424
|
+
plan: await planner({
|
|
425
|
+
rootDir: options.rootDir,
|
|
426
|
+
cwd: options.cwd,
|
|
427
|
+
sessionId: options.sessionId,
|
|
428
|
+
}),
|
|
429
|
+
warnings: [],
|
|
430
|
+
};
|
|
431
|
+
} catch (error) {
|
|
432
|
+
return {
|
|
433
|
+
plan: null,
|
|
434
|
+
warnings: [
|
|
435
|
+
`Docs-update plan skipped: ${error instanceof Error ? error.message : String(error)}`,
|
|
436
|
+
],
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
317
441
|
async function syncAgentInstructionFilesSafely(options) {
|
|
318
442
|
try {
|
|
319
443
|
return await syncAgentInstructionFiles(options);
|
|
@@ -364,6 +488,7 @@ function normalizeSessionPaths(options) {
|
|
|
364
488
|
: cwd;
|
|
365
489
|
|
|
366
490
|
return {
|
|
491
|
+
cwd,
|
|
367
492
|
rootDir,
|
|
368
493
|
toolingRootDir,
|
|
369
494
|
projectFilePath: path.resolve(rootDir, 'project.yaml'),
|
|
@@ -918,6 +1043,9 @@ ${renderBulletList(options.decisions, 'No new decisions were logged in this sess
|
|
|
918
1043
|
## Models used
|
|
919
1044
|
${renderBulletList(options.models)}
|
|
920
1045
|
|
|
1046
|
+
## Document maintenance checkpoint
|
|
1047
|
+
${renderDocumentMaintenanceCheckpoint(options.documentMaintenance)}
|
|
1048
|
+
|
|
921
1049
|
## Blockers / open questions
|
|
922
1050
|
${renderBulletList(options.blockers, 'No blocker remains.')}
|
|
923
1051
|
|
|
@@ -926,6 +1054,12 @@ ${renderOrderedList(options.nextSteps)}
|
|
|
926
1054
|
`;
|
|
927
1055
|
}
|
|
928
1056
|
|
|
1057
|
+
function renderDocumentMaintenanceCheckpoint(documentMaintenance) {
|
|
1058
|
+
return DOCUMENT_MAINTENANCE_FIELDS
|
|
1059
|
+
.map((field) => `- ${field.label}: ${documentMaintenance[field.key]}`)
|
|
1060
|
+
.join('\n');
|
|
1061
|
+
}
|
|
1062
|
+
|
|
929
1063
|
function renderBulletList(items, fallback) {
|
|
930
1064
|
if (items.length === 0) {
|
|
931
1065
|
return `- ${fallback}`;
|
package/src/status.js
CHANGED
|
@@ -5,6 +5,7 @@ import { sha256Text } from './hash.js';
|
|
|
5
5
|
import { parseSimpleYaml } from './simple-yaml.js';
|
|
6
6
|
import { listProjectSessions } from './session.js';
|
|
7
7
|
import { syncAgentInstructionFiles } from './generators/agent-files/index.js';
|
|
8
|
+
import { scanProjectMemory } from './project-memory.js';
|
|
8
9
|
|
|
9
10
|
export async function getProjectStatus(options = {}) {
|
|
10
11
|
const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
|
|
@@ -18,11 +19,12 @@ export async function getProjectStatus(options = {}) {
|
|
|
18
19
|
cwd,
|
|
19
20
|
...(options.packageVersion ? { packageVersion: options.packageVersion } : {}),
|
|
20
21
|
});
|
|
21
|
-
const [project, sessions, docsReview, agentFiles] = await Promise.all([
|
|
22
|
+
const [project, sessions, docsReview, agentFiles, projectMemory] = await Promise.all([
|
|
22
23
|
readProjectSummary(compatibility.projectFilePath),
|
|
23
24
|
readSessionsStatus(rootDir),
|
|
24
25
|
readDocsReviewStatus(rootDir),
|
|
25
26
|
readAgentFileStatus(rootDir, toolingRootDir),
|
|
27
|
+
readProjectMemoryStatus(rootDir),
|
|
26
28
|
]);
|
|
27
29
|
|
|
28
30
|
return {
|
|
@@ -39,12 +41,14 @@ export async function getProjectStatus(options = {}) {
|
|
|
39
41
|
sessions,
|
|
40
42
|
docsReview,
|
|
41
43
|
agentFiles,
|
|
44
|
+
projectMemory,
|
|
42
45
|
recommendations: buildRecommendations({
|
|
43
46
|
project,
|
|
44
47
|
compatibility,
|
|
45
48
|
sessions,
|
|
46
49
|
docsReview,
|
|
47
50
|
agentFiles,
|
|
51
|
+
projectMemory,
|
|
48
52
|
}),
|
|
49
53
|
};
|
|
50
54
|
}
|
|
@@ -99,6 +103,9 @@ export function renderStatusText(status) {
|
|
|
99
103
|
? [`- Output drift: ${status.docsReview.outputDrift.reasons.join('; ')}`]
|
|
100
104
|
: []),
|
|
101
105
|
'',
|
|
106
|
+
'Project memory:',
|
|
107
|
+
...formatProjectMemoryStatus(status.projectMemory),
|
|
108
|
+
'',
|
|
102
109
|
'Agent files:',
|
|
103
110
|
...formatAgentFileStatus(status.agentFiles),
|
|
104
111
|
);
|
|
@@ -125,6 +132,7 @@ export function toStatusJson(status) {
|
|
|
125
132
|
sessions: status.sessions,
|
|
126
133
|
docsReview: status.docsReview,
|
|
127
134
|
agentFiles: status.agentFiles,
|
|
135
|
+
projectMemory: status.projectMemory,
|
|
128
136
|
recommendations: status.recommendations,
|
|
129
137
|
};
|
|
130
138
|
}
|
|
@@ -297,7 +305,48 @@ async function readAgentFileStatus(rootDir, toolingRootDir) {
|
|
|
297
305
|
}
|
|
298
306
|
}
|
|
299
307
|
|
|
300
|
-
function
|
|
308
|
+
async function readProjectMemoryStatus(rootDir) {
|
|
309
|
+
try {
|
|
310
|
+
const scan = await scanProjectMemory(rootDir);
|
|
311
|
+
const architectureDocs = scan.documents.filter((document) => document.kind === 'architecture');
|
|
312
|
+
const architectureWarnings = scan.warnings
|
|
313
|
+
.filter((warning) => warning.path?.startsWith('architecture/'))
|
|
314
|
+
.map((warning) => ({
|
|
315
|
+
path: warning.path,
|
|
316
|
+
code: warning.code,
|
|
317
|
+
message: warning.message,
|
|
318
|
+
}));
|
|
319
|
+
|
|
320
|
+
return {
|
|
321
|
+
status: scan.errors.length > 0 ? 'errors' : 'ok',
|
|
322
|
+
architectureDocCount: architectureDocs.length,
|
|
323
|
+
architectureWarningCount: architectureWarnings.length,
|
|
324
|
+
warningSample: architectureWarnings.slice(0, 10),
|
|
325
|
+
truncatedWarningCount: Math.max(0, architectureWarnings.length - 10),
|
|
326
|
+
errorCount: scan.errors.length,
|
|
327
|
+
errorSample: scan.errors.slice(0, 10).map((error) => ({
|
|
328
|
+
path: error.path,
|
|
329
|
+
code: error.code,
|
|
330
|
+
message: error.message,
|
|
331
|
+
})),
|
|
332
|
+
truncatedErrorCount: Math.max(0, scan.errors.length - 10),
|
|
333
|
+
};
|
|
334
|
+
} catch (error) {
|
|
335
|
+
return {
|
|
336
|
+
status: 'unavailable',
|
|
337
|
+
architectureDocCount: 0,
|
|
338
|
+
architectureWarningCount: 0,
|
|
339
|
+
warningSample: [],
|
|
340
|
+
truncatedWarningCount: 0,
|
|
341
|
+
errorCount: 0,
|
|
342
|
+
errorSample: [],
|
|
343
|
+
truncatedErrorCount: 0,
|
|
344
|
+
errorMessage: error instanceof Error ? error.message : 'Unable to scan project memory.',
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
function buildRecommendations({ project, compatibility, sessions, docsReview, agentFiles, projectMemory }) {
|
|
301
350
|
const recommendations = [];
|
|
302
351
|
if (project.status === 'unreadable') {
|
|
303
352
|
recommendations.push('vibecompass init --guided');
|
|
@@ -320,6 +369,16 @@ function buildRecommendations({ project, compatibility, sessions, docsReview, ag
|
|
|
320
369
|
recommendations.push('vibecompass sync-agents --dry-run');
|
|
321
370
|
}
|
|
322
371
|
|
|
372
|
+
if (projectMemory.status === 'errors') {
|
|
373
|
+
recommendations.push('vibecompass status --json');
|
|
374
|
+
} else if (projectMemory.architectureWarningCount > 0) {
|
|
375
|
+
recommendations.push(
|
|
376
|
+
sessions.status === 'ok' && sessions.current
|
|
377
|
+
? `vibecompass docs-update --session ${sessions.current}`
|
|
378
|
+
: 'vibecompass docs-review --guided',
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
323
382
|
if (sessions.status === 'ok' && sessions.lanes.length === 0) {
|
|
324
383
|
recommendations.push('vibecompass start-session --id LANE_ID --working-on "TASK"');
|
|
325
384
|
} else if (sessions.status === 'ok' && sessions.lanes.length >= 2) {
|
|
@@ -334,6 +393,8 @@ function hasStatusDrift(status) {
|
|
|
334
393
|
status.compatibility.warnings.length > 0 ||
|
|
335
394
|
status.docsReview.status !== 'completed' ||
|
|
336
395
|
Boolean(status.docsReview.outputDrift?.hasDrift) ||
|
|
396
|
+
status.projectMemory.status !== 'ok' ||
|
|
397
|
+
status.projectMemory.architectureWarningCount > 0 ||
|
|
337
398
|
status.agentFiles.status !== 'ok' ||
|
|
338
399
|
status.agentFiles.results.some((item) => item.changed || item.warning)
|
|
339
400
|
);
|
|
@@ -390,6 +451,34 @@ function formatDocsReviewStatus(docsReview) {
|
|
|
390
451
|
return `${docsReview.status}${docsReview.completedAt ? ` at ${docsReview.completedAt}` : ''}`;
|
|
391
452
|
}
|
|
392
453
|
|
|
454
|
+
function formatProjectMemoryStatus(projectMemory) {
|
|
455
|
+
if (projectMemory.status === 'unavailable') {
|
|
456
|
+
return [`- unavailable (${projectMemory.errorMessage})`];
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const lines = [
|
|
460
|
+
`- Architecture docs: ${projectMemory.architectureDocCount}`,
|
|
461
|
+
`- Architecture warnings: ${projectMemory.architectureWarningCount}`,
|
|
462
|
+
];
|
|
463
|
+
|
|
464
|
+
for (const warning of projectMemory.warningSample) {
|
|
465
|
+
lines.push(`- ${warning.path}: ${warning.code} — ${warning.message}`);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (projectMemory.truncatedWarningCount > 0) {
|
|
469
|
+
lines.push(`- ${projectMemory.truncatedWarningCount} more architecture warning(s) omitted; use --json for the full status sample.`);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
if (projectMemory.errorCount > 0) {
|
|
473
|
+
lines.push(`- Scan errors: ${projectMemory.errorCount}`);
|
|
474
|
+
for (const error of projectMemory.errorSample) {
|
|
475
|
+
lines.push(`- ${error.path}: ${error.code} — ${error.message}`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
return lines;
|
|
480
|
+
}
|
|
481
|
+
|
|
393
482
|
function formatAgentFileStatus(agentFiles) {
|
|
394
483
|
if (agentFiles.status !== 'ok') {
|
|
395
484
|
return [`- unavailable (${agentFiles.errorMessage})`];
|
package/src/workflow.js
CHANGED
|
@@ -78,6 +78,7 @@ export function buildCloseSessionGuidance(workflowSettings) {
|
|
|
78
78
|
const guidance = [];
|
|
79
79
|
|
|
80
80
|
guidance.push('Reviewer handback stays in the selected sessions/active/<lane-id>/wip.md and handoff.md before close-session runs.');
|
|
81
|
+
guidance.push('Close-session requires document-maintenance checkpoint statuses for architecture docs, decision log, and session handoff/scratch: updated, not-needed, or deferred.');
|
|
81
82
|
|
|
82
83
|
if (workflowSettings.closeSession.refreshArchitectureDocs) {
|
|
83
84
|
guidance.push('Refresh any relevant architecture docs before finalizing the session.');
|
|
@@ -12,7 +12,8 @@ export const WORKFLOW_REGISTRY = [
|
|
|
12
12
|
{ trigger: 'planning mode', summary: 'optional prompt-level mode for scoping work before implementation' },
|
|
13
13
|
{ trigger: 'review handoff', summary: 'reviewer reads the selected lane handoff and appends findings' },
|
|
14
14
|
{ trigger: 'address review', summary: 'builder critically resolves reviewer feedback from the selected lane scratch files' },
|
|
15
|
-
{ trigger: '
|
|
15
|
+
{ trigger: 'docs update', summary: 'builder runs a targeted session-delta documentation maintenance plan with `vibecompass docs-update --session <lane-id>`' },
|
|
16
|
+
{ trigger: 'close session', summary: 'builder runs the close-out checklist and ends with `vibecompass close-session --session <lane-id>` plus required document-maintenance checkpoint statuses; `end session` and `vibecompass end-session` are accepted aliases' },
|
|
16
17
|
],
|
|
17
18
|
},
|
|
18
19
|
{
|