@vibecompass/vibecompass 0.7.0 → 0.9.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.
- package/CHANGELOG.md +17 -0
- package/README.md +32 -1
- package/package.json +1 -1
- package/src/cli.js +132 -4
- package/src/docs-review.js +70 -32
- package/src/init.js +171 -4
- package/src/project-memory.js +24 -5
- package/src/read-model.js +8 -4
- package/src/scaffold.js +12 -4
- package/src/setup.js +8 -6
- package/src/status.js +16 -0
- package/src/sync-binding.js +307 -0
- package/src/sync.js +31 -24
- package/src/workflows/registry.js +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.0 - 2026-06-14
|
|
4
|
+
|
|
5
|
+
- Add explicit local folder sources for non-Git projects: `project.yaml.repos[]` may use `source: local` plus `path`, and `init` accepts `--repo-local <id=path>`.
|
|
6
|
+
- Keep blank Git remotes invalid, but report an actionable `--repo-local` hint when `--repo <id=remote>` resolves to an empty value.
|
|
7
|
+
- Update guided init, starter docs, status, scanning, and read-model output so local-source roots validate and render cleanly.
|
|
8
|
+
- Bump docs-review to `VibeCompass Docs Review Prompt v3`, requiring first-pass docs to include a user journey, project/system map, and core journey-facing feature summaries.
|
|
9
|
+
- Add `vibecompass-project-map version=1` support inside `architecture/overview/project-shape.md`, including nested-fence parsing so generated overview docs can round-trip the machine-readable journey map block.
|
|
10
|
+
|
|
11
|
+
## 0.8.0 - 2026-06-12
|
|
12
|
+
|
|
13
|
+
- Add named sync targets (D-236): `connect-hosted --target <name>` binds one project root to multiple hosted environments (for example `dev` on localhost and `prod` on vibecompass.dev).
|
|
14
|
+
- Add a `sync-target` command to list named targets and switch the default; flat `sync` fields in project.yaml always mirror the default target so older CLIs keep working.
|
|
15
|
+
- Add `--sync-target <name>` to `push`, `pull-preview`, `pull-export`, and `docs-review` for per-command environment selection; unknown target names error instead of falling back.
|
|
16
|
+
- Partition local sync cursor state per target (D-237): each target keeps its own last-pushed revision and pull previews, identity-checked against its URL and project id, so one environment's history can never become another environment's baseline.
|
|
17
|
+
- Carry the existing sync history over when a flat binding is converted into its first named target with the same URL and project id.
|
|
18
|
+
- Record the submitted sync target in the docs-review marker so `--poll-hosted` stays pinned to the environment the run was submitted to.
|
|
19
|
+
|
|
3
20
|
## 0.7.0 - 2026-05-30
|
|
4
21
|
|
|
5
22
|
- Add `docs-review --rebuild` with dry-run preview, scoped architecture paths, and archive-based stale-doc preparation.
|
package/README.md
CHANGED
|
@@ -38,6 +38,15 @@ Create project memory:
|
|
|
38
38
|
npx -y @vibecompass/vibecompass init --guided
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
For a folder that is not a Git repo, use an explicit local source:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx -y @vibecompass/vibecompass init \
|
|
45
|
+
--name "My Project" \
|
|
46
|
+
--mode local-primary \
|
|
47
|
+
--repo-local app=.
|
|
48
|
+
```
|
|
49
|
+
|
|
41
50
|
Check what to do next:
|
|
42
51
|
|
|
43
52
|
```bash
|
|
@@ -125,7 +134,7 @@ After setup, use these phrases inside your AI coding tool:
|
|
|
125
134
|
- `planning mode`
|
|
126
135
|
- `review handoff`
|
|
127
136
|
- `address review`
|
|
128
|
-
- `close session`
|
|
137
|
+
- `close session` / `end session`
|
|
129
138
|
- `docs review`
|
|
130
139
|
|
|
131
140
|
The npm package owns the file mechanics. Your AI session owns reading the
|
|
@@ -146,6 +155,28 @@ vibecompass connect-hosted \
|
|
|
146
155
|
vibecompass push --root .compass
|
|
147
156
|
```
|
|
148
157
|
|
|
158
|
+
### Multiple environments
|
|
159
|
+
|
|
160
|
+
One project can have named sync targets (for example a local dev server and
|
|
161
|
+
production) so you never have to rebind:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# add targets once — the first one becomes the default
|
|
165
|
+
vibecompass connect-hosted --root .compass --target dev \
|
|
166
|
+
--sync-api-url http://localhost:3000 --sync-project-id vc_proj_dev \
|
|
167
|
+
--sync-credential-env-var VIBECOMPASS_SYNC_TOKEN
|
|
168
|
+
vibecompass connect-hosted --root .compass --target prod \
|
|
169
|
+
--sync-api-url https://vibecompass.dev --sync-project-id vc_proj_prod \
|
|
170
|
+
--sync-credential-env-var VIBECOMPASS_SYNC_TOKEN_PROD
|
|
171
|
+
|
|
172
|
+
# then pick per command, or switch the default
|
|
173
|
+
vibecompass push --root .compass --sync-target prod
|
|
174
|
+
vibecompass sync-target prod --root .compass
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Tip: if you already have a single (flat) binding, add it as your first named
|
|
178
|
+
target using the same URL and project id — its sync history carries over.
|
|
179
|
+
|
|
149
180
|
See https://vibecompass.dev/developers for the full local-primary workflow.
|
|
150
181
|
|
|
151
182
|
## JavaScript API
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { realpathSync } from 'node:fs';
|
|
4
4
|
import { pathToFileURL } from 'node:url';
|
|
5
|
-
import { connectHostedProjectMemory, initializeProjectMemory } from './init.js';
|
|
5
|
+
import { connectHostedProjectMemory, initializeProjectMemory, setDefaultSyncTarget } from './init.js';
|
|
6
6
|
import { preflightDocsReview } from './docs-review.js';
|
|
7
7
|
import { resolveConnectHostedCliOptions, resolveInitCliOptions } from './setup.js';
|
|
8
8
|
import { closeProjectSession, listProjectSessions, startProjectSession, switchProjectSession } from './session.js';
|
|
@@ -140,6 +140,10 @@ export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
|
140
140
|
});
|
|
141
141
|
io.stdout.write(`Connected hosted VibeCompass for ${result.mode}\n`);
|
|
142
142
|
io.stdout.write(`Updated ${result.projectFilePath}\n`);
|
|
143
|
+
if (result.syncTarget) {
|
|
144
|
+
io.stdout.write(`Sync target: ${result.syncTarget.name} (default: ${result.syncTarget.defaultTarget})\n`);
|
|
145
|
+
io.stdout.write(`Select per command with --sync-target ${result.syncTarget.name}, or switch the default with: vibecompass sync-target ${result.syncTarget.name}\n`);
|
|
146
|
+
}
|
|
143
147
|
io.stdout.write(`Next step: set ${result.syncEnvVar} locally before your first hosted command.\n`);
|
|
144
148
|
if (result.mode === 'local-primary') {
|
|
145
149
|
io.stdout.write('Then run: vibecompass push\n');
|
|
@@ -150,6 +154,28 @@ export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
|
150
154
|
return 0;
|
|
151
155
|
}
|
|
152
156
|
|
|
157
|
+
if (parsed.command === 'sync-target') {
|
|
158
|
+
const result = await setDefaultSyncTarget({
|
|
159
|
+
...parsed.options,
|
|
160
|
+
...(runtime.cwd ? { cwd: runtime.cwd } : {}),
|
|
161
|
+
});
|
|
162
|
+
if (parsed.options.targetName) {
|
|
163
|
+
io.stdout.write(
|
|
164
|
+
result.changed
|
|
165
|
+
? `Default sync target set to ${result.defaultTarget}\n`
|
|
166
|
+
: `Default sync target already ${result.defaultTarget}\n`,
|
|
167
|
+
);
|
|
168
|
+
io.stdout.write(`Updated ${result.projectFilePath}\n`);
|
|
169
|
+
} else {
|
|
170
|
+
io.stdout.write(`Default sync target: ${result.defaultTarget ?? '(none)'}\n`);
|
|
171
|
+
}
|
|
172
|
+
for (const [name, target] of Object.entries(result.targets)) {
|
|
173
|
+
const marker = name === result.defaultTarget ? '*' : ' ';
|
|
174
|
+
io.stdout.write(`${marker} ${name}: ${target.api_url} (project ${target.project_id}, env ${target.credential_env_var})\n`);
|
|
175
|
+
}
|
|
176
|
+
return 0;
|
|
177
|
+
}
|
|
178
|
+
|
|
153
179
|
if (parsed.command === 'status') {
|
|
154
180
|
const result = await getProjectStatus({
|
|
155
181
|
...parsed.options,
|
|
@@ -401,6 +427,10 @@ export function parseCliArgs(argv) {
|
|
|
401
427
|
return parseConnectHostedArgs(rest);
|
|
402
428
|
}
|
|
403
429
|
|
|
430
|
+
if (command === 'sync-target') {
|
|
431
|
+
return parseSyncTargetArgs(rest);
|
|
432
|
+
}
|
|
433
|
+
|
|
404
434
|
if (command === 'status') {
|
|
405
435
|
return parseStatusArgs(rest);
|
|
406
436
|
}
|
|
@@ -538,6 +568,11 @@ export function parseCliArgs(argv) {
|
|
|
538
568
|
parsed.repos.push({ id, remote });
|
|
539
569
|
break;
|
|
540
570
|
}
|
|
571
|
+
case '--repo-local': {
|
|
572
|
+
const [id, repoPath] = splitAssignment(value, '--repo-local');
|
|
573
|
+
parsed.repos.push({ id, source: 'local', path: repoPath });
|
|
574
|
+
break;
|
|
575
|
+
}
|
|
541
576
|
case '--repo-branch': {
|
|
542
577
|
const [id, defaultBranch] = splitAssignment(value, '--repo-branch');
|
|
543
578
|
parsed.repoBranches.set(id, defaultBranch);
|
|
@@ -702,6 +737,9 @@ function parseConnectHostedArgs(argv) {
|
|
|
702
737
|
case '--root':
|
|
703
738
|
parsed.rootDir = value;
|
|
704
739
|
break;
|
|
740
|
+
case '--target':
|
|
741
|
+
parsed.targetName = value;
|
|
742
|
+
break;
|
|
705
743
|
case '--sync-api-url':
|
|
706
744
|
parsed.syncApiUrl = value;
|
|
707
745
|
break;
|
|
@@ -726,6 +764,7 @@ function parseConnectHostedArgs(argv) {
|
|
|
726
764
|
command: 'connect-hosted',
|
|
727
765
|
options: {
|
|
728
766
|
rootDir: parsed.rootDir,
|
|
767
|
+
...(parsed.targetName ? { targetName: parsed.targetName } : {}),
|
|
729
768
|
...(syncValues.length > 0
|
|
730
769
|
? {
|
|
731
770
|
sync: {
|
|
@@ -1128,6 +1167,9 @@ function parseDocsReviewArgs(argv) {
|
|
|
1128
1167
|
case '--artifact':
|
|
1129
1168
|
parsed.artifactId = value;
|
|
1130
1169
|
break;
|
|
1170
|
+
case '--sync-target':
|
|
1171
|
+
parsed.syncTarget = value;
|
|
1172
|
+
break;
|
|
1131
1173
|
case '--max-tokens':
|
|
1132
1174
|
parsed.maxTokens = value;
|
|
1133
1175
|
break;
|
|
@@ -1145,7 +1187,72 @@ function parseDocsReviewArgs(argv) {
|
|
|
1145
1187
|
function parsePushArgs(argv) {
|
|
1146
1188
|
return {
|
|
1147
1189
|
command: 'push',
|
|
1148
|
-
options:
|
|
1190
|
+
options: parseRootAndSyncTargetArgs(argv, 'push'),
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
function parseRootAndSyncTargetArgs(argv, commandName) {
|
|
1195
|
+
const parsed = {};
|
|
1196
|
+
|
|
1197
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
1198
|
+
const token = argv[index];
|
|
1199
|
+
if (!token.startsWith('--')) {
|
|
1200
|
+
throw new Error(`Unexpected argument "${token}".`);
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
const value = argv[index + 1];
|
|
1204
|
+
if (value === undefined) {
|
|
1205
|
+
throw new Error(`Flag "${token}" requires a value.`);
|
|
1206
|
+
}
|
|
1207
|
+
index += 1;
|
|
1208
|
+
|
|
1209
|
+
switch (token) {
|
|
1210
|
+
case '--root':
|
|
1211
|
+
parsed.rootDir = value;
|
|
1212
|
+
break;
|
|
1213
|
+
case '--sync-target':
|
|
1214
|
+
parsed.syncTarget = value;
|
|
1215
|
+
break;
|
|
1216
|
+
default:
|
|
1217
|
+
throw new Error(`Unknown flag "${token}" for ${commandName}.`);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
return parsed;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
function parseSyncTargetArgs(argv) {
|
|
1225
|
+
const parsed = {};
|
|
1226
|
+
|
|
1227
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
1228
|
+
const token = argv[index];
|
|
1229
|
+
|
|
1230
|
+
if (!token.startsWith('--')) {
|
|
1231
|
+
if (parsed.targetName !== undefined) {
|
|
1232
|
+
throw new Error(`Unexpected argument "${token}".`);
|
|
1233
|
+
}
|
|
1234
|
+
parsed.targetName = token;
|
|
1235
|
+
continue;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
const value = argv[index + 1];
|
|
1239
|
+
if (value === undefined) {
|
|
1240
|
+
throw new Error(`Flag "${token}" requires a value.`);
|
|
1241
|
+
}
|
|
1242
|
+
index += 1;
|
|
1243
|
+
|
|
1244
|
+
switch (token) {
|
|
1245
|
+
case '--root':
|
|
1246
|
+
parsed.rootDir = value;
|
|
1247
|
+
break;
|
|
1248
|
+
default:
|
|
1249
|
+
throw new Error(`Unknown flag "${token}" for sync-target.`);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
return {
|
|
1254
|
+
command: 'sync-target',
|
|
1255
|
+
options: parsed,
|
|
1149
1256
|
};
|
|
1150
1257
|
}
|
|
1151
1258
|
|
|
@@ -1173,6 +1280,9 @@ function parsePullPreviewArgs(argv) {
|
|
|
1173
1280
|
case '--root':
|
|
1174
1281
|
parsed.rootDir = value;
|
|
1175
1282
|
break;
|
|
1283
|
+
case '--sync-target':
|
|
1284
|
+
parsed.syncTarget = value;
|
|
1285
|
+
break;
|
|
1176
1286
|
default:
|
|
1177
1287
|
throw new Error(`Unknown flag "${token}".`);
|
|
1178
1288
|
}
|
|
@@ -1206,6 +1316,9 @@ function parsePullExportArgs(argv) {
|
|
|
1206
1316
|
case '--root':
|
|
1207
1317
|
parsed.rootDir = value;
|
|
1208
1318
|
break;
|
|
1319
|
+
case '--sync-target':
|
|
1320
|
+
parsed.syncTarget = value;
|
|
1321
|
+
break;
|
|
1209
1322
|
case '--preview-token':
|
|
1210
1323
|
parsed.previewToken = value;
|
|
1211
1324
|
break;
|
|
@@ -1353,6 +1466,11 @@ function writeWarnings(io, warnings = []) {
|
|
|
1353
1466
|
function splitAssignment(value, flagName) {
|
|
1354
1467
|
const separatorIndex = value.indexOf('=');
|
|
1355
1468
|
if (separatorIndex <= 0 || separatorIndex === value.length - 1) {
|
|
1469
|
+
if (flagName === '--repo' && separatorIndex > 0 && separatorIndex === value.length - 1) {
|
|
1470
|
+
throw new Error(
|
|
1471
|
+
'--repo remote resolved to empty. For a non-Git folder, use --repo-local <id=path> instead of --repo <id=remote>.',
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1356
1474
|
throw new Error(`${flagName} expects id=value.`);
|
|
1357
1475
|
}
|
|
1358
1476
|
|
|
@@ -1364,6 +1482,7 @@ function usageText() {
|
|
|
1364
1482
|
'Usage:',
|
|
1365
1483
|
' vibecompass init --name <project-name> --mode <local-only|local-primary|hosted-only> --repo <id=remote> [options]',
|
|
1366
1484
|
' vibecompass connect-hosted [options]',
|
|
1485
|
+
' vibecompass sync-target [<name>] [options]',
|
|
1367
1486
|
' vibecompass status [options]',
|
|
1368
1487
|
' vibecompass refresh-workflow [--dry-run|--apply] [options]',
|
|
1369
1488
|
' vibecompass start-session --id <lane-id> --working-on <text> [options]',
|
|
@@ -1386,7 +1505,8 @@ function usageText() {
|
|
|
1386
1505
|
' --placement <workspace-root|dedicated-memory-repo|primary-repo>',
|
|
1387
1506
|
' Optional explicit placement pattern',
|
|
1388
1507
|
' --guided Ask placement and setup questions interactively',
|
|
1389
|
-
' --repo <id=remote> Repeatable repo descriptor',
|
|
1508
|
+
' --repo <id=remote> Repeatable Git-backed repo descriptor',
|
|
1509
|
+
' --repo-local <id=path> Repeatable local folder source for non-Git projects',
|
|
1390
1510
|
' --repo-branch <id=branch> Optional per-repo default branch',
|
|
1391
1511
|
' --sync-api-url <url> Optional hosted sync api_url',
|
|
1392
1512
|
' --sync-project-id <id> Optional hosted sync project_id',
|
|
@@ -1405,10 +1525,17 @@ function usageText() {
|
|
|
1405
1525
|
'',
|
|
1406
1526
|
'Connect-hosted options:',
|
|
1407
1527
|
' --root <path> Project-memory root. Defaults to .compass',
|
|
1528
|
+
' --target <name> Add or update a named sync target (e.g. dev, prod)',
|
|
1529
|
+
' First named target becomes the default; flat sync fields mirror the default target',
|
|
1408
1530
|
' --sync-api-url <url> Hosted sync api_url',
|
|
1409
1531
|
' --sync-project-id <id> Hosted sync project_id',
|
|
1410
1532
|
' --sync-credential-env-var <name> Hosted sync env var reference',
|
|
1411
|
-
'
|
|
1533
|
+
' Without --target, replaces any existing flat project.yaml sync binding',
|
|
1534
|
+
'',
|
|
1535
|
+
'Sync-target options:',
|
|
1536
|
+
' vibecompass sync-target List named sync targets and the current default',
|
|
1537
|
+
' vibecompass sync-target <name> Switch the default sync target (re-mirrors flat sync fields)',
|
|
1538
|
+
' --root <path> Project-memory root. Defaults to .compass',
|
|
1412
1539
|
'',
|
|
1413
1540
|
'Status options:',
|
|
1414
1541
|
' --root <path> Project-memory root. Defaults to .compass',
|
|
@@ -1463,6 +1590,7 @@ function usageText() {
|
|
|
1463
1590
|
'',
|
|
1464
1591
|
'Sync transport options:',
|
|
1465
1592
|
' push --root <path> Push canonical local project memory to hosted',
|
|
1593
|
+
' --sync-target <name> Use a named sync target for this command (push, pull-preview, pull-export, docs-review)',
|
|
1466
1594
|
' pull-preview --root <path> Preview hosted proposals and remote state',
|
|
1467
1595
|
' pull-preview --no-pending-proposals Exclude pending proposals from preview',
|
|
1468
1596
|
' pull-export --proposal <id> Export a selected proposal; repeatable',
|
package/src/docs-review.js
CHANGED
|
@@ -5,8 +5,9 @@ import { parseFrontmatter } from './frontmatter.js';
|
|
|
5
5
|
import { sha256Text } from './hash.js';
|
|
6
6
|
import { writeStateManifest } from './manifest.js';
|
|
7
7
|
import { parseSimpleYaml } from './simple-yaml.js';
|
|
8
|
+
import { readSyncCursor, resolveSyncBinding } from './sync-binding.js';
|
|
8
9
|
|
|
9
|
-
const DOCS_REVIEW_PROMPT_VERSION = 'VibeCompass Docs Review Prompt
|
|
10
|
+
const DOCS_REVIEW_PROMPT_VERSION = 'VibeCompass Docs Review Prompt v3';
|
|
10
11
|
const ARCHITECTURE_DOC_SOFT_SIZE_LIMIT_BYTES = 12000;
|
|
11
12
|
const COVERAGE_PLAN_OUTPUT_VERSION = 1;
|
|
12
13
|
const COVERAGE_PLAN_STATUSES = new Set(['accepted', 'deferred', 'missing']);
|
|
@@ -83,6 +84,7 @@ export async function preflightDocsReview(options = {}, environment = {}) {
|
|
|
83
84
|
localProvider,
|
|
84
85
|
runLocal: shouldRunLocal(options),
|
|
85
86
|
anthropicEnvVar: options.anthropicEnvVar ?? 'ANTHROPIC_API_KEY',
|
|
87
|
+
syncTarget: options.syncTarget ?? null,
|
|
86
88
|
});
|
|
87
89
|
const reviewPrompt = renderReviewPrompt({
|
|
88
90
|
project,
|
|
@@ -617,11 +619,10 @@ function resolveDocsReviewRuntime(project, options) {
|
|
|
617
619
|
localProvider &&
|
|
618
620
|
typeof options.env?.[localProvider.credentialEnvVar] === 'string' &&
|
|
619
621
|
options.env[localProvider.credentialEnvVar].trim() !== '';
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
typeof project.sync.project_id === 'string';
|
|
622
|
+
// Throws on an explicitly requested unknown/incomplete --sync-target so a
|
|
623
|
+
// typo can never fall back to a different environment (D-236).
|
|
624
|
+
const hostedBinding = resolveSyncBinding(project, options.syncTarget ?? null);
|
|
625
|
+
const hasHostedBinding = Boolean(hostedBinding);
|
|
625
626
|
|
|
626
627
|
if (mode === 'local-only') {
|
|
627
628
|
if (options.runLocal && !localProvider) {
|
|
@@ -663,11 +664,10 @@ function resolveDocsReviewRuntime(project, options) {
|
|
|
663
664
|
if (hasHostedBinding) {
|
|
664
665
|
return {
|
|
665
666
|
provider: 'hosted',
|
|
666
|
-
api_url:
|
|
667
|
-
project_id:
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
: {}),
|
|
667
|
+
api_url: hostedBinding.apiUrl,
|
|
668
|
+
project_id: hostedBinding.projectId,
|
|
669
|
+
credential_env_var: hostedBinding.credentialEnvVar,
|
|
670
|
+
...(hostedBinding.target ? { sync_target: hostedBinding.target } : {}),
|
|
671
671
|
};
|
|
672
672
|
}
|
|
673
673
|
|
|
@@ -699,11 +699,10 @@ function resolveDocsReviewRuntime(project, options) {
|
|
|
699
699
|
|
|
700
700
|
return {
|
|
701
701
|
provider: 'hosted',
|
|
702
|
-
api_url:
|
|
703
|
-
project_id:
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
: {}),
|
|
702
|
+
api_url: hostedBinding.apiUrl,
|
|
703
|
+
project_id: hostedBinding.projectId,
|
|
704
|
+
credential_env_var: hostedBinding.credentialEnvVar,
|
|
705
|
+
...(hostedBinding.target ? { sync_target: hostedBinding.target } : {}),
|
|
707
706
|
};
|
|
708
707
|
}
|
|
709
708
|
|
|
@@ -786,13 +785,13 @@ function extractAnthropicText(body) {
|
|
|
786
785
|
|
|
787
786
|
function parseArchitectureDocBlocks(source) {
|
|
788
787
|
const blocks = [];
|
|
789
|
-
const pattern =
|
|
788
|
+
const pattern = /^(`{3,})vibecompass-architecture-doc\s+path=([^\s`]+)\s*\n([\s\S]*?)^\1[ \t]*$/gm;
|
|
790
789
|
let match;
|
|
791
790
|
|
|
792
791
|
while ((match = pattern.exec(source)) !== null) {
|
|
793
792
|
blocks.push({
|
|
794
|
-
path: normalizeBlockPath(match[
|
|
795
|
-
content: match[
|
|
793
|
+
path: normalizeBlockPath(match[2]),
|
|
794
|
+
content: match[3].replace(/\n$/, ''),
|
|
796
795
|
});
|
|
797
796
|
}
|
|
798
797
|
|
|
@@ -830,8 +829,8 @@ function parseDecisionRecommendationBlocks(source) {
|
|
|
830
829
|
}
|
|
831
830
|
|
|
832
831
|
function countMalformedArchitectureDocFences(source) {
|
|
833
|
-
const fencePattern =
|
|
834
|
-
const acceptedOpeningPattern =
|
|
832
|
+
const fencePattern = /^`{3,}vibecompass-architecture-doc\b.*$/gm;
|
|
833
|
+
const acceptedOpeningPattern = /^`{3,}vibecompass-architecture-doc\s+path=[^\s`]+\s*$/;
|
|
835
834
|
let count = 0;
|
|
836
835
|
let match;
|
|
837
836
|
|
|
@@ -1107,8 +1106,15 @@ async function submitHostedDocsReview(options) {
|
|
|
1107
1106
|
`api/sync/projects/${encodeURIComponent(options.runtime.project_id)}/docs-review`,
|
|
1108
1107
|
ensureTrailingSlash(options.runtime.api_url),
|
|
1109
1108
|
);
|
|
1109
|
+
// D-237: the baseline must come from the submitted target's own cursor so a
|
|
1110
|
+
// dev revision can never be sent as another environment's baseline.
|
|
1111
|
+
const submitCursor = readSyncCursor(options.manifest?.manifest?.sync, {
|
|
1112
|
+
target: options.runtime.sync_target ?? null,
|
|
1113
|
+
apiUrl: options.runtime.api_url,
|
|
1114
|
+
projectId: options.runtime.project_id,
|
|
1115
|
+
});
|
|
1110
1116
|
const baselineRemoteRevisionId = normalizeUuid(
|
|
1111
|
-
|
|
1117
|
+
submitCursor.last_successful_remote_revision,
|
|
1112
1118
|
);
|
|
1113
1119
|
const response = await options.fetch(endpoint.href, {
|
|
1114
1120
|
method: 'POST',
|
|
@@ -1699,18 +1705,20 @@ function renderReviewPrompt(options) {
|
|
|
1699
1705
|
'Stage 1 — Evidence inventory:',
|
|
1700
1706
|
'- Build a compact repo inventory from file paths, manifests/config files, route/job/test directories, and existing architecture frontmatter before reading large file bodies.',
|
|
1701
1707
|
'- Mine finalized session notes for decision candidates and product constraints, but cap proposed decisions to the five highest-value architecture choices.',
|
|
1702
|
-
'- Identify candidate domains, features, components,
|
|
1708
|
+
'- Identify candidate domains, features, components, ownership boundaries, user journey entry points, feature-to-feature relationships, and system-layer connections with concrete `repo:path` evidence.',
|
|
1703
1709
|
'- Do not fetch or summarize broad source bodies until the coverage plan says a file is needed.',
|
|
1704
1710
|
'',
|
|
1705
1711
|
'Stage 2 — Coverage plan:',
|
|
1706
1712
|
'- Propose the smallest useful architecture-doc set that will let future agents understand the project and retrieve targeted context.',
|
|
1707
|
-
'-
|
|
1713
|
+
'- The minimum useful set must cover: user journey, project/system map (frontend/backend/API/data/integration structure and connections), and summaries of the core journey-facing features.',
|
|
1714
|
+
'- Prioritize entry points, user-flow/gating relationships, data ownership, external integrations, jobs/async boundaries, auth/security boundaries, and test surfaces.',
|
|
1708
1715
|
'- Mark each proposed doc as `comprehensive`, `partial`, or `initial`, with the reason and blindspots.',
|
|
1709
1716
|
'- Emit the accepted plan as one `vibecompass-coverage-plan version=1` JSON fence before architecture-doc blocks.',
|
|
1710
1717
|
'- Ask for user acceptance before emitting fenced architecture-doc blocks.',
|
|
1711
1718
|
'',
|
|
1712
1719
|
'Stage 3 — Bounded doc generation:',
|
|
1713
1720
|
'- Generate only accepted docs, one complete fenced block per architecture path.',
|
|
1721
|
+
'- Ensure `architecture/overview/project-shape.md` includes a concise system map plus one `vibecompass-project-map version=1` JSON fence that lists journey-facing features and supported relationships.',
|
|
1714
1722
|
'- Keep docs concise and retrieval-oriented: explain contracts, flows, invariants, and ownership; do not rewrite source code or produce file-by-file walkthroughs.',
|
|
1715
1723
|
`- Soft size budget: keep each generated architecture doc under ${ARCHITECTURE_DOC_SOFT_SIZE_LIMIT_BYTES} bytes unless the extra detail is necessary and called out in Blindspots or Retrieval guidance.`,
|
|
1716
1724
|
'- Include only the most important involved files; prefer representative entry points and owned contracts over exhaustive file lists.',
|
|
@@ -1740,18 +1748,46 @@ function renderReviewPrompt(options) {
|
|
|
1740
1748
|
'',
|
|
1741
1749
|
'## Review Rules',
|
|
1742
1750
|
'1. Do not delete `architecture/overview/project-shape.md`.',
|
|
1743
|
-
'2. Add
|
|
1744
|
-
'3.
|
|
1745
|
-
'4.
|
|
1746
|
-
'5.
|
|
1747
|
-
'6.
|
|
1748
|
-
'7.
|
|
1749
|
-
'8. Do not
|
|
1750
|
-
'9.
|
|
1751
|
+
'2. Add or update `architecture/overview/project-shape.md` so it explains the project/system map and includes the accepted `vibecompass-project-map version=1` block.',
|
|
1752
|
+
'3. Add evidence-backed component docs alongside the overview for core journey-facing features and important supporting systems.',
|
|
1753
|
+
'4. Update `architecture/overview/project-shape.md` coverage and blindspot sections to summarize what has now been mapped.',
|
|
1754
|
+
'5. Keep all implementation claims source-backed with `repo:path` evidence.',
|
|
1755
|
+
'6. Use only the exact confidence and coverage enum values listed above.',
|
|
1756
|
+
'7. Put uncertainty in `Blindspots`; do not fill gaps with guesses.',
|
|
1757
|
+
'8. Do not append real `D-NNN` decisions without explicit user acceptance. Propose candidate decisions separately.',
|
|
1758
|
+
'9. Do not edit `decisions/INDEX.md` directly unless a canonical decision file was explicitly accepted and appended.',
|
|
1759
|
+
'10. Optimize for future retrieval: project-level docs stay compact; component docs carry focused context for a specific area.',
|
|
1760
|
+
'',
|
|
1761
|
+
'## Project Map Block',
|
|
1762
|
+
'Include exactly one project-map block inside `architecture/overview/project-shape.md` when the user accepts journey/system mapping output:',
|
|
1763
|
+
'',
|
|
1764
|
+
'```vibecompass-project-map version=1',
|
|
1765
|
+
'{',
|
|
1766
|
+
' "features": [',
|
|
1767
|
+
' {',
|
|
1768
|
+
' "domain": "Domain name matching architecture frontmatter",',
|
|
1769
|
+
' "feature": "Feature name matching architecture frontmatter",',
|
|
1770
|
+
' "is_entry_point": true,',
|
|
1771
|
+
' "summary": "Short user-facing feature summary"',
|
|
1772
|
+
' }',
|
|
1773
|
+
' ],',
|
|
1774
|
+
' "relationships": [',
|
|
1775
|
+
' {',
|
|
1776
|
+
' "from": { "domain": "Domain", "feature": "Feature" },',
|
|
1777
|
+
' "to": { "domain": "Domain", "feature": "Feature" },',
|
|
1778
|
+
' "kind": "gates | navigates_to | depends_on | writes_to",',
|
|
1779
|
+
' "label": "Short evidence-backed relationship label"',
|
|
1780
|
+
' }',
|
|
1781
|
+
' ]',
|
|
1782
|
+
'}',
|
|
1783
|
+
'```',
|
|
1784
|
+
'',
|
|
1785
|
+
'Only include relationships supported by evidence. Prefer `navigates_to` and `gates` for the main user journey; use `depends_on` and `writes_to` for supporting system relationships. If useful journey links cannot be inferred, state that as a coverage gap rather than inventing links.',
|
|
1751
1786
|
'',
|
|
1752
1787
|
'## Output Contract',
|
|
1753
1788
|
'First provide a concise findings summary with:',
|
|
1754
1789
|
'- mapped areas',
|
|
1790
|
+
'- user journey and project/system map coverage',
|
|
1755
1791
|
'- gaps/blindspots',
|
|
1756
1792
|
'- evidence inventory summary',
|
|
1757
1793
|
'- coverage plan and proposed architecture docs',
|
|
@@ -1785,6 +1821,8 @@ function renderReviewPrompt(options) {
|
|
|
1785
1821
|
'<complete markdown file content>',
|
|
1786
1822
|
'```',
|
|
1787
1823
|
'',
|
|
1824
|
+
'If the complete markdown file content contains its own fenced block, such as `vibecompass-project-map version=1`, use a longer outer fence for the architecture-doc block (for example four backticks) so the inner fence stays inside the document body.',
|
|
1825
|
+
'',
|
|
1788
1826
|
'If the review surfaced accepted decision candidates, output each candidate as a separate block without assigning a D-number:',
|
|
1789
1827
|
'',
|
|
1790
1828
|
'```vibecompass-decision-recommendation target=decisions/cross-cutting.md',
|