@vibecompass/vibecompass 0.10.8 → 0.11.1
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 +36 -1
- package/README.md +32 -0
- package/package.json +1 -1
- package/src/cli.js +602 -8
- package/src/compatibility.js +34 -1
- package/src/decision-index.js +386 -0
- package/src/decisions.js +195 -0
- package/src/docs-review.js +27 -27
- package/src/docs-update.js +459 -24
- package/src/generators/agent-files/index.js +8 -0
- package/src/generators/agent-files/template.js +6 -3
- package/src/git-binding.js +676 -0
- package/src/init.js +9 -0
- package/src/lane-marker.js +339 -0
- package/src/lane-runtime.js +254 -0
- package/src/manifest.js +19 -0
- package/src/project-memory.js +27 -1
- package/src/refresh-workflow.js +8 -0
- package/src/scaffold.js +19 -11
- package/src/serialization.js +244 -0
- package/src/session.js +1000 -75
- package/src/simple-yaml.js +24 -4
- package/src/status.js +43 -5
- package/src/sync.js +5 -0
- package/src/workflows/registry.js +1 -1
package/src/simple-yaml.js
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
const KEY_VALUE_PATTERN = /^([A-Za-z_][A-Za-z0-9_-]*)\s*:(?:\s+(.*)|\s*)$/;
|
|
2
|
+
const SAFE_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Whether a value can appear as a block-map key in this subset. Writers that
|
|
6
|
+
* derive map keys from user-supplied identifiers (e.g. repo ids in lane
|
|
7
|
+
* metadata) must check this and fail closed instead of emitting a key the
|
|
8
|
+
* parser will reject — a parse throw degrades the whole document for readers.
|
|
9
|
+
*/
|
|
10
|
+
export function isSimpleYamlKeySafe(value) {
|
|
11
|
+
return typeof value === 'string' && SAFE_KEY_PATTERN.test(value);
|
|
12
|
+
}
|
|
2
13
|
|
|
3
14
|
export function parseSimpleYaml(source, options = {}) {
|
|
4
15
|
// This intentionally supports only the YAML subset used by project.yaml and
|
|
@@ -208,10 +219,19 @@ function parseScalar(rawValue) {
|
|
|
208
219
|
return Number(rawValue);
|
|
209
220
|
}
|
|
210
221
|
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
(
|
|
214
|
-
|
|
222
|
+
if (rawValue.startsWith('"') && rawValue.endsWith('"')) {
|
|
223
|
+
// Writers quote free-text values with JSON.stringify; parse the token
|
|
224
|
+
// back symmetrically so escaped quotes and backslashes (e.g. Windows
|
|
225
|
+
// paths) round-trip. Hand-authored quoting that is not valid JSON keeps
|
|
226
|
+
// the historical naive slice so legacy files stay readable.
|
|
227
|
+
try {
|
|
228
|
+
return JSON.parse(rawValue);
|
|
229
|
+
} catch {
|
|
230
|
+
return rawValue.slice(1, -1);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (rawValue.startsWith("'") && rawValue.endsWith("'")) {
|
|
215
235
|
return rawValue.slice(1, -1);
|
|
216
236
|
}
|
|
217
237
|
|
package/src/status.js
CHANGED
|
@@ -2,17 +2,21 @@ import { readFile } from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { inspectProjectCompatibility } from './compatibility.js';
|
|
4
4
|
import { sha256Text } from './hash.js';
|
|
5
|
+
import { resolveLaneMarkerContext } from './lane-marker.js';
|
|
5
6
|
import { parseSimpleYaml } from './simple-yaml.js';
|
|
6
|
-
import { listProjectSessions } from './session.js';
|
|
7
|
+
import { inferToolingRootForMemoryRoot, listProjectSessions } from './session.js';
|
|
7
8
|
import { syncAgentInstructionFiles } from './generators/agent-files/index.js';
|
|
8
9
|
import { scanProjectMemory } from './project-memory.js';
|
|
9
10
|
|
|
10
11
|
export async function getProjectStatus(options = {}) {
|
|
11
12
|
const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
|
|
12
|
-
const
|
|
13
|
+
const rootContext = await resolveStatusRootContext(cwd, options.rootDir);
|
|
14
|
+
const rootDir = rootContext.rootDir;
|
|
13
15
|
const toolingRootDir = options.toolingRootDir
|
|
14
16
|
? path.resolve(cwd, options.toolingRootDir)
|
|
15
|
-
:
|
|
17
|
+
: rootContext.rootSource === 'marker'
|
|
18
|
+
? await inferToolingRootForMemoryRoot(rootDir)
|
|
19
|
+
: cwd;
|
|
16
20
|
|
|
17
21
|
const compatibility = await inspectProjectCompatibility({
|
|
18
22
|
rootDir,
|
|
@@ -36,7 +40,15 @@ export async function getProjectStatus(options = {}) {
|
|
|
36
40
|
cliPackageVersion: compatibility.cliPackageVersion,
|
|
37
41
|
package: compatibility.package,
|
|
38
42
|
state: compatibility.state,
|
|
39
|
-
warnings:
|
|
43
|
+
warnings: [
|
|
44
|
+
...(rootContext.markerError
|
|
45
|
+
? [{
|
|
46
|
+
code: 'lane-marker-unreadable',
|
|
47
|
+
message: `A lane marker near ${cwd} could not be resolved (${rootContext.markerError}); reporting on ${rootDir} instead. Lane-scoped commands will fail closed on it.`,
|
|
48
|
+
}]
|
|
49
|
+
: []),
|
|
50
|
+
...compatibility.warnings,
|
|
51
|
+
],
|
|
40
52
|
},
|
|
41
53
|
sessions,
|
|
42
54
|
docsReview,
|
|
@@ -53,6 +65,30 @@ export async function getProjectStatus(options = {}) {
|
|
|
53
65
|
};
|
|
54
66
|
}
|
|
55
67
|
|
|
68
|
+
/**
|
|
69
|
+
* D-280: status adopts the nearest valid marker's memory root when --root is
|
|
70
|
+
* omitted, matching lane-scoped commands, so `status` from a worktree cwd
|
|
71
|
+
* reports the bound root instead of a nonexistent `cwd/.compass`. Status is
|
|
72
|
+
* read-only and must not fail on a corrupt or foreign marker — resolution
|
|
73
|
+
* failures fall back to the cwd default.
|
|
74
|
+
*/
|
|
75
|
+
async function resolveStatusRootContext(cwd, explicitRootDir) {
|
|
76
|
+
if (explicitRootDir) {
|
|
77
|
+
return { rootDir: path.resolve(cwd, explicitRootDir), rootSource: 'flag', markerError: null };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const context = await resolveLaneMarkerContext({ cwd });
|
|
82
|
+
return { rootDir: context.rootDir, rootSource: context.rootSource, markerError: null };
|
|
83
|
+
} catch (error) {
|
|
84
|
+
return {
|
|
85
|
+
rootDir: path.resolve(cwd, '.compass'),
|
|
86
|
+
rootSource: 'default',
|
|
87
|
+
markerError: error instanceof Error ? error.message : String(error),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
56
92
|
export function renderStatusText(status) {
|
|
57
93
|
if (status.project.status === 'unreadable') {
|
|
58
94
|
return [
|
|
@@ -375,7 +411,9 @@ function buildRecommendations({ project, compatibility, sessions, docsReview, ag
|
|
|
375
411
|
recommendations.push(
|
|
376
412
|
sessions.status === 'ok' && sessions.current
|
|
377
413
|
? `vibecompass docs-update --session ${sessions.current}`
|
|
378
|
-
: '
|
|
414
|
+
: sessions.status === 'ok' && sessions.lanes.length >= 2
|
|
415
|
+
? 'vibecompass list-sessions'
|
|
416
|
+
: 'vibecompass docs-review --guided',
|
|
379
417
|
);
|
|
380
418
|
}
|
|
381
419
|
|
package/src/sync.js
CHANGED
|
@@ -2,6 +2,7 @@ import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { sha256Text } from './hash.js';
|
|
4
4
|
import { writeStateManifest } from './manifest.js';
|
|
5
|
+
import { withMemoryRootLock } from './serialization.js';
|
|
5
6
|
import { parseSimpleYaml } from './simple-yaml.js';
|
|
6
7
|
import {
|
|
7
8
|
buildSyncStateWithCursor,
|
|
@@ -143,6 +144,10 @@ export async function pullExportProjectMemory(options = {}, environment = {}) {
|
|
|
143
144
|
export async function applyPullExport(options = {}, environment = {}) {
|
|
144
145
|
const cwd = environment.cwd ? path.resolve(environment.cwd) : process.cwd();
|
|
145
146
|
const rootDir = path.resolve(cwd, options.rootDir ?? '.compass');
|
|
147
|
+
return withMemoryRootLock(rootDir, 'apply-export', () => applyPullExportLocked(options, rootDir));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async function applyPullExportLocked(options, rootDir) {
|
|
146
151
|
const outputPath = path.resolve(rootDir, options.outputPath ?? DEFAULT_EXPORT_PATH);
|
|
147
152
|
const bundle = await readJsonRequired(outputPath, 'pull-export bundle');
|
|
148
153
|
const operations = Array.isArray(bundle.operations) ? bundle.operations : [];
|
|
@@ -10,7 +10,7 @@ export const WORKFLOW_REGISTRY = [
|
|
|
10
10
|
{ trigger: 'start session', summary: 'builder role trigger' },
|
|
11
11
|
{ trigger: 'join as reviewer', summary: 'reviewer role trigger' },
|
|
12
12
|
{ trigger: 'planning mode', summary: 'optional prompt-level mode for scoping work before implementation' },
|
|
13
|
-
{ trigger: 'review handoff', summary: 'reviewer reads the selected lane handoff and appends findings' },
|
|
13
|
+
{ trigger: 'review handoff', summary: 'reviewer reads the selected lane handoff and appends findings; with multiple active lanes name the lane explicitly: `review handoff <lane-id>`' },
|
|
14
14
|
{ trigger: 'address review', summary: 'builder critically resolves reviewer feedback from the selected lane scratch files' },
|
|
15
15
|
{ trigger: 'docs update', summary: 'builder runs a targeted session-delta documentation maintenance plan with `vibecompass docs-update --session <lane-id>`' },
|
|
16
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' },
|