@vibecompass/vibecompass 0.3.0 → 0.3.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/README.md +61 -22
- package/package.json +1 -1
- package/src/cli.js +399 -3
- package/src/docs-review.js +408 -1
- package/src/index.js +6 -0
- package/src/init.js +64 -24
- package/src/manifest.js +18 -1
- package/src/project-memory.js +21 -2
- package/src/read-model.js +1 -1
- package/src/setup.js +149 -34
- package/src/sync.js +395 -0
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ Shipped today:
|
|
|
20
20
|
- `vibecompass end-session` as a discoverable alias for `close-session`
|
|
21
21
|
- `vibecompass sync-agents` for generated `CLAUDE.md`, `AGENTS.md`, `.cursorrules`, and `.github/copilot-instructions.md` views
|
|
22
22
|
- `vibecompass docs-review --guided` — print a comprehensive review prompt for your chosen LLM to run
|
|
23
|
-
- `vibecompass docs-review --submit-hosted` — submit the review request to the hosted app
|
|
23
|
+
- `vibecompass docs-review --submit-hosted` — submit the review request to the hosted app and poll for proposal/artifact output
|
|
24
24
|
- `vibecompass docs-review --run-local --provider anthropic` — local provider adapter that saves review output locally
|
|
25
25
|
- `vibecompass docs-review --apply-output` — apply accepted architecture-doc blocks into canonical `architecture/` docs
|
|
26
26
|
- `vibecompass docs-review --complete` — mark the review accepted after the docs land
|
|
@@ -153,15 +153,17 @@ VibeCompass dogfoods: read project memory first, inspect source evidence,
|
|
|
153
153
|
write component docs with review metadata, preserve the starter overview, and
|
|
154
154
|
emit accepted docs as `vibecompass-architecture-doc` blocks.
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
Plain `--guided` never calls an AI itself. You stay in control of which
|
|
157
|
+
provider runs the review and what gets saved. Use `--run-local --provider
|
|
158
|
+
anthropic` when you want the package to execute the same review through your
|
|
159
|
+
local Anthropic key.
|
|
158
160
|
|
|
159
161
|
### Other ways to run a review
|
|
160
162
|
|
|
161
163
|
- `--submit-hosted` sends the review request to the hosted app and records a
|
|
162
|
-
run ID.
|
|
163
|
-
|
|
164
|
-
|
|
164
|
+
run ID. Hosted execution creates proposals and docs-review artifacts; for
|
|
165
|
+
local-primary projects, canonical local files change only after
|
|
166
|
+
pull-preview/export/apply and a confirming push.
|
|
165
167
|
- `--run-local --provider anthropic` calls Anthropic directly with your
|
|
166
168
|
`ANTHROPIC_API_KEY` and saves the raw output to
|
|
167
169
|
`state/docs-review-output.md`.
|
|
@@ -243,7 +245,23 @@ npx -y @vibecompass/vibecompass sync-agents --root .compass
|
|
|
243
245
|
Content outside those markers is preserved. Existing files without markers are
|
|
244
246
|
reported as warnings and left untouched.
|
|
245
247
|
|
|
246
|
-
|
|
248
|
+
Connect hosted VibeCompass later when you want browser views, team sync, or hosted docs-review:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
npx -y @vibecompass/vibecompass connect-hosted \
|
|
252
|
+
--sync-api-url https://vibecompass.dev \
|
|
253
|
+
--sync-project-id vc_proj_example \
|
|
254
|
+
--sync-credential-env-var VIBECOMPASS_SYNC_TOKEN
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Then set the secret locally and push the first baseline:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
export VIBECOMPASS_SYNC_TOKEN='your-sync-token'
|
|
261
|
+
npx -y @vibecompass/vibecompass push
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
Create a `local-primary` root non-interactively when you also want the hosted sync binding written into `project.yaml` during init:
|
|
247
265
|
|
|
248
266
|
```bash
|
|
249
267
|
npx -y @vibecompass/vibecompass init \
|
|
@@ -269,14 +287,8 @@ That creates:
|
|
|
269
287
|
manifest.json
|
|
270
288
|
```
|
|
271
289
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
```bash
|
|
275
|
-
export VIBECOMPASS_SYNC_TOKEN='your-sync-token'
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
`vibecompass` does not yet perform hosted sync itself. The sync fields in
|
|
279
|
-
`project.yaml` are the non-secret binding for the upcoming push/pull flows.
|
|
290
|
+
The sync fields in `project.yaml` are non-secret. The token value stays in
|
|
291
|
+
your environment and is never written to canonical project memory.
|
|
280
292
|
|
|
281
293
|
## CLI
|
|
282
294
|
|
|
@@ -296,9 +308,9 @@ Options:
|
|
|
296
308
|
- `--guided`: ask placement and setup questions interactively
|
|
297
309
|
- `--repo <id=remote>`: repeatable repo descriptor
|
|
298
310
|
- `--repo-branch <id=branch>`: optional per-repo default branch
|
|
299
|
-
- `--sync-api-url <url>`: hosted sync API URL for `local-primary`
|
|
300
|
-
- `--sync-project-id <id>`: hosted sync project ID for `local-primary`
|
|
301
|
-
- `--sync-credential-env-var <name>`: local env-var reference for `local-primary`
|
|
311
|
+
- `--sync-api-url <url>`: hosted sync API URL for `local-primary` or `hosted-only`
|
|
312
|
+
- `--sync-project-id <id>`: hosted sync project ID for `local-primary` or `hosted-only`
|
|
313
|
+
- `--sync-credential-env-var <name>`: local env-var reference for `local-primary` or `hosted-only`
|
|
302
314
|
- `--with-workflow`: generate `context.md` plus workflow guide files
|
|
303
315
|
- `--with-claude`: create a starter `CLAUDE.md` if it does not already exist
|
|
304
316
|
- `--with-agents`: create a starter `AGENTS.md` if it does not already exist
|
|
@@ -315,6 +327,31 @@ Guided init can also record workflow defaults in
|
|
|
315
327
|
`project.yaml.metadata.workflow`, including whether close-session should
|
|
316
328
|
include a Git publish step and which remote name to use for that step.
|
|
317
329
|
|
|
330
|
+
Guided init is inference-first for the common single-repo case: it reads
|
|
331
|
+
`package.json`, detects the current Git `origin`, asks you to confirm the
|
|
332
|
+
detected repo, and maps a friendly setup goal to the stored project mode.
|
|
333
|
+
Hosted credentials are deferred by default; run `vibecompass connect-hosted`
|
|
334
|
+
when you are ready to connect the local root to `vibecompass.dev`.
|
|
335
|
+
|
|
336
|
+
### `vibecompass connect-hosted`
|
|
337
|
+
|
|
338
|
+
```text
|
|
339
|
+
vibecompass connect-hosted [options]
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Options:
|
|
343
|
+
|
|
344
|
+
- `--root <path>`: project-memory root; defaults to `.compass`
|
|
345
|
+
- `--sync-api-url <url>`: hosted sync API URL
|
|
346
|
+
- `--sync-project-id <id>`: hosted sync project ID
|
|
347
|
+
- `--sync-credential-env-var <name>`: local env-var reference
|
|
348
|
+
|
|
349
|
+
Without all three sync flags, `connect-hosted` prompts for the missing fields.
|
|
350
|
+
It updates only the non-secret `project.yaml.sync` binding; you still set the
|
|
351
|
+
actual token in your environment before running hosted commands.
|
|
352
|
+
Running it again replaces the existing hosted binding in `project.yaml`, which
|
|
353
|
+
is the intended rotation path after you create a new sync credential.
|
|
354
|
+
|
|
318
355
|
### `vibecompass start-session`
|
|
319
356
|
|
|
320
357
|
```text
|
|
@@ -417,7 +454,7 @@ Options:
|
|
|
417
454
|
|
|
418
455
|
- `--root <path>`: project-memory root; defaults to `.compass`
|
|
419
456
|
- `--guided`: print a comprehensive review prompt for your chosen LLM to run (recommended)
|
|
420
|
-
- `--submit-hosted`: submit the review request to the hosted app
|
|
457
|
+
- `--submit-hosted`: submit the review request to the hosted app
|
|
421
458
|
- `--run-local`: run the generated review request with a local provider
|
|
422
459
|
- `--provider <name>`: local provider for `--run-local`; currently `anthropic`
|
|
423
460
|
- `--run-local-anthropic`: compatibility alias for `--run-local --provider anthropic`
|
|
@@ -443,9 +480,11 @@ Project-mode requirements:
|
|
|
443
480
|
hosted binding, so hosted precedence does not apply when that flag is set.
|
|
444
481
|
|
|
445
482
|
`--submit-hosted` uses `sync.credential_env_var` and records the hosted run
|
|
446
|
-
ID in `state/docs-review.json`.
|
|
447
|
-
|
|
448
|
-
|
|
483
|
+
ID in `state/docs-review.json`. Hosted execution creates review proposals and
|
|
484
|
+
artifacts; for `local-primary` roots, local files stay untouched until you
|
|
485
|
+
pull-preview, export, apply locally, and push to confirm the accepted output.
|
|
486
|
+
Missing or stale hosted baselines fail with package-facing next-step guidance
|
|
487
|
+
instead of running model work against the wrong source of truth.
|
|
449
488
|
|
|
450
489
|
`--run-local-anthropic` remains as a compatibility alias. New scripts should
|
|
451
490
|
use `--run-local --provider anthropic`.
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
import { realpathSync } from 'node:fs';
|
|
4
4
|
import { pathToFileURL } from 'node:url';
|
|
5
|
-
import { initializeProjectMemory } from './init.js';
|
|
5
|
+
import { connectHostedProjectMemory, initializeProjectMemory } from './init.js';
|
|
6
6
|
import { preflightDocsReview } from './docs-review.js';
|
|
7
|
-
import { resolveInitCliOptions } from './setup.js';
|
|
7
|
+
import { resolveConnectHostedCliOptions, resolveInitCliOptions } from './setup.js';
|
|
8
8
|
import { closeProjectSession, listProjectSessions, startProjectSession, switchProjectSession } from './session.js';
|
|
9
9
|
import { syncAgentInstructionFiles } from './generators/agent-files/index.js';
|
|
10
|
+
import {
|
|
11
|
+
applyPullExport,
|
|
12
|
+
pullExportProjectMemory,
|
|
13
|
+
pullPreviewProjectMemory,
|
|
14
|
+
pushProjectMemory,
|
|
15
|
+
} from './sync.js';
|
|
10
16
|
|
|
11
17
|
export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
12
18
|
const parsed = parseCliArgs(argv);
|
|
@@ -41,7 +47,20 @@ export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
|
41
47
|
);
|
|
42
48
|
|
|
43
49
|
if (result.syncEnvVar) {
|
|
44
|
-
|
|
50
|
+
const rootFlag = formatOptionalRootFlag(initPlan.initOptions.rootDir);
|
|
51
|
+
io.stdout.write(`Hosted binding: configured for ${initPlan.initOptions.mode}\n`);
|
|
52
|
+
io.stdout.write(`Next step: set ${result.syncEnvVar} locally before your first hosted command.\n`);
|
|
53
|
+
if (initPlan.initOptions.mode === 'local-primary') {
|
|
54
|
+
io.stdout.write(`Then run: vibecompass push${rootFlag}\n`);
|
|
55
|
+
io.stdout.write(`Hosted docs-review: vibecompass docs-review --submit-hosted${rootFlag}\n`);
|
|
56
|
+
} else if (initPlan.initOptions.mode === 'hosted-only') {
|
|
57
|
+
io.stdout.write(`Hosted docs-review: vibecompass docs-review --submit-hosted${rootFlag}\n`);
|
|
58
|
+
}
|
|
59
|
+
} else if (initPlan.initOptions.mode === 'hosted-only') {
|
|
60
|
+
io.stdout.write('Hosted-only projects need a hosted sync binding before hosted commands.\n');
|
|
61
|
+
io.stdout.write('After creating a sync credential, rerun init with --force and --sync-api-url/--sync-project-id/--sync-credential-env-var, or add project.yaml.sync manually.\n');
|
|
62
|
+
} else if (initPlan.initOptions.mode === 'local-primary') {
|
|
63
|
+
io.stdout.write('Optional hosted setup later: vibecompass connect-hosted\n');
|
|
45
64
|
}
|
|
46
65
|
|
|
47
66
|
if (result.contextFilePath) {
|
|
@@ -92,6 +111,28 @@ export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
|
92
111
|
return 0;
|
|
93
112
|
}
|
|
94
113
|
|
|
114
|
+
if (parsed.command === 'connect-hosted') {
|
|
115
|
+
const connectPlan = await resolveConnectHostedCliOptions(parsed.options, {
|
|
116
|
+
cwd: runtime.cwd,
|
|
117
|
+
io,
|
|
118
|
+
runtime,
|
|
119
|
+
});
|
|
120
|
+
const result = await connectHostedProjectMemory({
|
|
121
|
+
...connectPlan,
|
|
122
|
+
...(runtime.cwd ? { cwd: runtime.cwd } : {}),
|
|
123
|
+
});
|
|
124
|
+
io.stdout.write(`Connected hosted VibeCompass for ${result.mode}\n`);
|
|
125
|
+
io.stdout.write(`Updated ${result.projectFilePath}\n`);
|
|
126
|
+
io.stdout.write(`Next step: set ${result.syncEnvVar} locally before your first hosted command.\n`);
|
|
127
|
+
if (result.mode === 'local-primary') {
|
|
128
|
+
io.stdout.write('Then run: vibecompass push\n');
|
|
129
|
+
io.stdout.write('Hosted docs-review: vibecompass docs-review --submit-hosted\n');
|
|
130
|
+
} else {
|
|
131
|
+
io.stdout.write('Hosted docs-review: vibecompass docs-review --submit-hosted\n');
|
|
132
|
+
}
|
|
133
|
+
return 0;
|
|
134
|
+
}
|
|
135
|
+
|
|
95
136
|
if (parsed.command === 'close-session' || parsed.command === 'end-session') {
|
|
96
137
|
const result = await closeProjectSession({
|
|
97
138
|
...parsed.options,
|
|
@@ -168,6 +209,9 @@ export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
|
168
209
|
}
|
|
169
210
|
if (result.hosted) {
|
|
170
211
|
io.stdout.write(`Hosted run: ${result.hosted.run_id}\n`);
|
|
212
|
+
if (result.hosted.phase) {
|
|
213
|
+
io.stdout.write(`Hosted phase: ${result.hosted.phase}\n`);
|
|
214
|
+
}
|
|
171
215
|
}
|
|
172
216
|
if (result.localReview) {
|
|
173
217
|
io.stdout.write(`Local review output: ${result.localReview.output_path}\n`);
|
|
@@ -178,6 +222,11 @@ export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
|
178
222
|
io.stdout.write(`- ${entry.path} (${entry.status})\n`);
|
|
179
223
|
}
|
|
180
224
|
}
|
|
225
|
+
if (result.appliedDecisionArtifact) {
|
|
226
|
+
io.stdout.write(`Applied decision artifact: ${result.appliedDecisionArtifact.artifact_id}\n`);
|
|
227
|
+
io.stdout.write(`Decision: D-${String(result.appliedDecisionArtifact.decision_id).padStart(3, '0')}\n`);
|
|
228
|
+
io.stdout.write(`Target: ${result.appliedDecisionArtifact.target_path}\n`);
|
|
229
|
+
}
|
|
181
230
|
writeWarnings(io, result.warnings);
|
|
182
231
|
io.stdout.write(`Recorded ${result.statePath}\n`);
|
|
183
232
|
io.stdout.write(`${result.message}\n`);
|
|
@@ -188,6 +237,80 @@ export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
|
188
237
|
return 0;
|
|
189
238
|
}
|
|
190
239
|
|
|
240
|
+
if (parsed.command === 'push') {
|
|
241
|
+
const result = await pushProjectMemory(parsed.options, {
|
|
242
|
+
cwd: runtime.cwd,
|
|
243
|
+
env: runtime.env,
|
|
244
|
+
runtime,
|
|
245
|
+
});
|
|
246
|
+
io.stdout.write(`Push: ${result.status}\n`);
|
|
247
|
+
io.stdout.write(`Remote revision: ${result.remoteRevisionId}\n`);
|
|
248
|
+
io.stdout.write(`Run: ${result.runId}\n`);
|
|
249
|
+
if (result.appliedProposalIds.length > 0) {
|
|
250
|
+
io.stdout.write(`Applied proposals: ${result.appliedProposalIds.join(', ')}\n`);
|
|
251
|
+
}
|
|
252
|
+
if (result.staleProposalIds.length > 0) {
|
|
253
|
+
io.stdout.write(`Stale proposals: ${result.staleProposalIds.join(', ')}\n`);
|
|
254
|
+
}
|
|
255
|
+
io.stdout.write(`Recorded ${result.manifestPath}\n`);
|
|
256
|
+
io.stdout.write(`${result.message}\n`);
|
|
257
|
+
return 0;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (parsed.command === 'pull-preview') {
|
|
261
|
+
const result = await pullPreviewProjectMemory(parsed.options, {
|
|
262
|
+
cwd: runtime.cwd,
|
|
263
|
+
env: runtime.env,
|
|
264
|
+
runtime,
|
|
265
|
+
});
|
|
266
|
+
io.stdout.write(`Pull-preview: ${result.status}\n`);
|
|
267
|
+
io.stdout.write(`Preview token: ${result.previewToken}\n`);
|
|
268
|
+
io.stdout.write(`Pending proposals: ${result.proposals.length}\n`);
|
|
269
|
+
for (const proposal of result.proposals) {
|
|
270
|
+
io.stdout.write(`- ${proposal.proposal_id}: ${proposal.summary}\n`);
|
|
271
|
+
}
|
|
272
|
+
if (result.conflicts.length > 0) {
|
|
273
|
+
io.stdout.write(`Conflicts: ${result.conflicts.length}\n`);
|
|
274
|
+
for (const conflict of result.conflicts) {
|
|
275
|
+
io.stdout.write(`- ${conflict.code}: ${conflict.message}\n`);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
writeWarnings(io, result.warnings.map((warning) => `${warning.code}: ${warning.message}`));
|
|
279
|
+
io.stdout.write(`Recorded ${result.manifestPath}\n`);
|
|
280
|
+
io.stdout.write(`${result.message}\n`);
|
|
281
|
+
return 0;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (parsed.command === 'pull-export') {
|
|
285
|
+
const result = await pullExportProjectMemory(parsed.options, {
|
|
286
|
+
cwd: runtime.cwd,
|
|
287
|
+
env: runtime.env,
|
|
288
|
+
runtime,
|
|
289
|
+
});
|
|
290
|
+
io.stdout.write(`Pull-export: ${result.status}\n`);
|
|
291
|
+
io.stdout.write(`Run: ${result.runId}\n`);
|
|
292
|
+
io.stdout.write(`Proposal IDs: ${result.proposalIds.join(', ')}\n`);
|
|
293
|
+
io.stdout.write(`Operations: ${result.operationCount}\n`);
|
|
294
|
+
io.stdout.write(`Wrote ${result.outputPath}\n`);
|
|
295
|
+
io.stdout.write(`${result.message}\n`);
|
|
296
|
+
return 0;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (parsed.command === 'apply-export') {
|
|
300
|
+
const result = await applyPullExport(parsed.options, {
|
|
301
|
+
cwd: runtime.cwd,
|
|
302
|
+
env: runtime.env,
|
|
303
|
+
runtime,
|
|
304
|
+
});
|
|
305
|
+
io.stdout.write(`Apply-export: ${result.status}\n`);
|
|
306
|
+
for (const entry of result.applied) {
|
|
307
|
+
io.stdout.write(`- ${entry.path} (${entry.status})\n`);
|
|
308
|
+
}
|
|
309
|
+
io.stdout.write(`Recorded ${result.outputPath}\n`);
|
|
310
|
+
io.stdout.write(`${result.message}\n`);
|
|
311
|
+
return 0;
|
|
312
|
+
}
|
|
313
|
+
|
|
191
314
|
throw new Error(`Unknown command "${parsed.command}".`);
|
|
192
315
|
}
|
|
193
316
|
|
|
@@ -203,6 +326,10 @@ export function parseCliArgs(argv) {
|
|
|
203
326
|
return parseStartSessionArgs(rest);
|
|
204
327
|
}
|
|
205
328
|
|
|
329
|
+
if (command === 'connect-hosted') {
|
|
330
|
+
return parseConnectHostedArgs(rest);
|
|
331
|
+
}
|
|
332
|
+
|
|
206
333
|
if (command === 'close-session' || command === 'end-session') {
|
|
207
334
|
return parseCloseSessionArgs(rest);
|
|
208
335
|
}
|
|
@@ -223,6 +350,22 @@ export function parseCliArgs(argv) {
|
|
|
223
350
|
return parseDocsReviewArgs(rest);
|
|
224
351
|
}
|
|
225
352
|
|
|
353
|
+
if (command === 'push') {
|
|
354
|
+
return parsePushArgs(rest);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (command === 'pull-preview') {
|
|
358
|
+
return parsePullPreviewArgs(rest);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (command === 'pull-export') {
|
|
362
|
+
return parsePullExportArgs(rest);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (command === 'apply-export') {
|
|
366
|
+
return parseApplyExportArgs(rest);
|
|
367
|
+
}
|
|
368
|
+
|
|
226
369
|
return { command };
|
|
227
370
|
}
|
|
228
371
|
|
|
@@ -448,6 +591,63 @@ function parseStartSessionArgs(argv) {
|
|
|
448
591
|
};
|
|
449
592
|
}
|
|
450
593
|
|
|
594
|
+
function parseConnectHostedArgs(argv) {
|
|
595
|
+
const parsed = {};
|
|
596
|
+
|
|
597
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
598
|
+
const token = argv[index];
|
|
599
|
+
|
|
600
|
+
if (!token.startsWith('--')) {
|
|
601
|
+
throw new Error(`Unexpected argument "${token}".`);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const value = argv[index + 1];
|
|
605
|
+
if (value === undefined) {
|
|
606
|
+
throw new Error(`Flag "${token}" requires a value.`);
|
|
607
|
+
}
|
|
608
|
+
index += 1;
|
|
609
|
+
|
|
610
|
+
switch (token) {
|
|
611
|
+
case '--root':
|
|
612
|
+
parsed.rootDir = value;
|
|
613
|
+
break;
|
|
614
|
+
case '--sync-api-url':
|
|
615
|
+
parsed.syncApiUrl = value;
|
|
616
|
+
break;
|
|
617
|
+
case '--sync-project-id':
|
|
618
|
+
parsed.syncProjectId = value;
|
|
619
|
+
break;
|
|
620
|
+
case '--sync-credential-env-var':
|
|
621
|
+
parsed.syncCredentialEnvVar = value;
|
|
622
|
+
break;
|
|
623
|
+
default:
|
|
624
|
+
throw new Error(`Unknown flag "${token}".`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
const syncValues = [
|
|
629
|
+
parsed.syncApiUrl,
|
|
630
|
+
parsed.syncProjectId,
|
|
631
|
+
parsed.syncCredentialEnvVar,
|
|
632
|
+
].filter(Boolean);
|
|
633
|
+
|
|
634
|
+
return {
|
|
635
|
+
command: 'connect-hosted',
|
|
636
|
+
options: {
|
|
637
|
+
rootDir: parsed.rootDir,
|
|
638
|
+
...(syncValues.length > 0
|
|
639
|
+
? {
|
|
640
|
+
sync: {
|
|
641
|
+
apiUrl: parsed.syncApiUrl,
|
|
642
|
+
projectId: parsed.syncProjectId,
|
|
643
|
+
credentialEnvVar: parsed.syncCredentialEnvVar,
|
|
644
|
+
},
|
|
645
|
+
}
|
|
646
|
+
: {}),
|
|
647
|
+
},
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
451
651
|
function parseCloseSessionArgs(argv) {
|
|
452
652
|
const parsed = {
|
|
453
653
|
completed: [],
|
|
@@ -651,6 +851,11 @@ function parseDocsReviewArgs(argv) {
|
|
|
651
851
|
continue;
|
|
652
852
|
}
|
|
653
853
|
|
|
854
|
+
if (token === '--poll-hosted') {
|
|
855
|
+
parsed.pollHosted = true;
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
|
|
654
859
|
if (token === '--complete') {
|
|
655
860
|
parsed.complete = true;
|
|
656
861
|
continue;
|
|
@@ -671,6 +876,16 @@ function parseDocsReviewArgs(argv) {
|
|
|
671
876
|
continue;
|
|
672
877
|
}
|
|
673
878
|
|
|
879
|
+
if (token === '--apply-decision-artifact') {
|
|
880
|
+
parsed.applyDecisionArtifact = true;
|
|
881
|
+
continue;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
if (token === '--refresh-index') {
|
|
885
|
+
parsed.refreshIndex = true;
|
|
886
|
+
continue;
|
|
887
|
+
}
|
|
888
|
+
|
|
674
889
|
if (!token.startsWith('--')) {
|
|
675
890
|
throw new Error(`Unexpected argument "${token}".`);
|
|
676
891
|
}
|
|
@@ -700,6 +915,9 @@ function parseDocsReviewArgs(argv) {
|
|
|
700
915
|
case '--output':
|
|
701
916
|
parsed.outputPath = value;
|
|
702
917
|
break;
|
|
918
|
+
case '--artifact':
|
|
919
|
+
parsed.artifactId = value;
|
|
920
|
+
break;
|
|
703
921
|
case '--max-tokens':
|
|
704
922
|
parsed.maxTokens = value;
|
|
705
923
|
break;
|
|
@@ -714,6 +932,151 @@ function parseDocsReviewArgs(argv) {
|
|
|
714
932
|
};
|
|
715
933
|
}
|
|
716
934
|
|
|
935
|
+
function parsePushArgs(argv) {
|
|
936
|
+
return {
|
|
937
|
+
command: 'push',
|
|
938
|
+
options: parseRootOnlyArgs(argv, 'push'),
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
function parsePullPreviewArgs(argv) {
|
|
943
|
+
const parsed = {};
|
|
944
|
+
|
|
945
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
946
|
+
const token = argv[index];
|
|
947
|
+
if (token === '--no-pending-proposals') {
|
|
948
|
+
parsed.includePendingProposals = false;
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (!token.startsWith('--')) {
|
|
953
|
+
throw new Error(`Unexpected argument "${token}".`);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
const value = argv[index + 1];
|
|
957
|
+
if (value === undefined) {
|
|
958
|
+
throw new Error(`Flag "${token}" requires a value.`);
|
|
959
|
+
}
|
|
960
|
+
index += 1;
|
|
961
|
+
|
|
962
|
+
switch (token) {
|
|
963
|
+
case '--root':
|
|
964
|
+
parsed.rootDir = value;
|
|
965
|
+
break;
|
|
966
|
+
default:
|
|
967
|
+
throw new Error(`Unknown flag "${token}".`);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
return {
|
|
972
|
+
command: 'pull-preview',
|
|
973
|
+
options: parsed,
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
function parsePullExportArgs(argv) {
|
|
978
|
+
const parsed = {
|
|
979
|
+
proposalIds: [],
|
|
980
|
+
};
|
|
981
|
+
|
|
982
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
983
|
+
const token = argv[index];
|
|
984
|
+
|
|
985
|
+
if (!token.startsWith('--')) {
|
|
986
|
+
throw new Error(`Unexpected argument "${token}".`);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
const value = argv[index + 1];
|
|
990
|
+
if (value === undefined) {
|
|
991
|
+
throw new Error(`Flag "${token}" requires a value.`);
|
|
992
|
+
}
|
|
993
|
+
index += 1;
|
|
994
|
+
|
|
995
|
+
switch (token) {
|
|
996
|
+
case '--root':
|
|
997
|
+
parsed.rootDir = value;
|
|
998
|
+
break;
|
|
999
|
+
case '--preview-token':
|
|
1000
|
+
parsed.previewToken = value;
|
|
1001
|
+
break;
|
|
1002
|
+
case '--proposal':
|
|
1003
|
+
parsed.proposalIds.push(value);
|
|
1004
|
+
break;
|
|
1005
|
+
case '--output':
|
|
1006
|
+
parsed.outputPath = value;
|
|
1007
|
+
break;
|
|
1008
|
+
default:
|
|
1009
|
+
throw new Error(`Unknown flag "${token}".`);
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
return {
|
|
1014
|
+
command: 'pull-export',
|
|
1015
|
+
options: parsed,
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
function parseApplyExportArgs(argv) {
|
|
1020
|
+
const parsed = {};
|
|
1021
|
+
|
|
1022
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
1023
|
+
const token = argv[index];
|
|
1024
|
+
|
|
1025
|
+
if (!token.startsWith('--')) {
|
|
1026
|
+
throw new Error(`Unexpected argument "${token}".`);
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
const value = argv[index + 1];
|
|
1030
|
+
if (value === undefined) {
|
|
1031
|
+
throw new Error(`Flag "${token}" requires a value.`);
|
|
1032
|
+
}
|
|
1033
|
+
index += 1;
|
|
1034
|
+
|
|
1035
|
+
switch (token) {
|
|
1036
|
+
case '--root':
|
|
1037
|
+
parsed.rootDir = value;
|
|
1038
|
+
break;
|
|
1039
|
+
case '--output':
|
|
1040
|
+
parsed.outputPath = value;
|
|
1041
|
+
break;
|
|
1042
|
+
default:
|
|
1043
|
+
throw new Error(`Unknown flag "${token}".`);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
return {
|
|
1048
|
+
command: 'apply-export',
|
|
1049
|
+
options: parsed,
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
function parseRootOnlyArgs(argv, commandName) {
|
|
1054
|
+
const parsed = {};
|
|
1055
|
+
|
|
1056
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
1057
|
+
const token = argv[index];
|
|
1058
|
+
if (!token.startsWith('--')) {
|
|
1059
|
+
throw new Error(`Unexpected argument "${token}".`);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
const value = argv[index + 1];
|
|
1063
|
+
if (value === undefined) {
|
|
1064
|
+
throw new Error(`Flag "${token}" requires a value.`);
|
|
1065
|
+
}
|
|
1066
|
+
index += 1;
|
|
1067
|
+
|
|
1068
|
+
switch (token) {
|
|
1069
|
+
case '--root':
|
|
1070
|
+
parsed.rootDir = value;
|
|
1071
|
+
break;
|
|
1072
|
+
default:
|
|
1073
|
+
throw new Error(`Unknown flag "${token}" for ${commandName}.`);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
return parsed;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
717
1080
|
function writeAgentFileSyncResult(io, result) {
|
|
718
1081
|
if (!result) {
|
|
719
1082
|
return;
|
|
@@ -746,12 +1109,17 @@ function usageText() {
|
|
|
746
1109
|
return [
|
|
747
1110
|
'Usage:',
|
|
748
1111
|
' vibecompass init --name <project-name> --mode <local-only|local-primary|hosted-only> --repo <id=remote> [options]',
|
|
1112
|
+
' vibecompass connect-hosted [options]',
|
|
749
1113
|
' vibecompass start-session --id <lane-id> --working-on <text> [options]',
|
|
750
1114
|
' vibecompass close-session --title <text> --completed <text> --next-step <text> [options]',
|
|
751
1115
|
' vibecompass end-session --title <text> --completed <text> --next-step <text> [options] # alias',
|
|
752
1116
|
' vibecompass list-sessions [options]',
|
|
753
1117
|
' vibecompass switch-session <id> [options]',
|
|
754
1118
|
' vibecompass sync-agents [options]',
|
|
1119
|
+
' vibecompass push [options]',
|
|
1120
|
+
' vibecompass pull-preview [options]',
|
|
1121
|
+
' vibecompass pull-export [options]',
|
|
1122
|
+
' vibecompass apply-export [options]',
|
|
755
1123
|
' vibecompass docs-review --guided [options]',
|
|
756
1124
|
'',
|
|
757
1125
|
'Init options:',
|
|
@@ -777,6 +1145,13 @@ function usageText() {
|
|
|
777
1145
|
' --close-session-git-remote <name> Default Git remote name for that close-session publish step',
|
|
778
1146
|
' --force Overwrite an existing project.yaml',
|
|
779
1147
|
'',
|
|
1148
|
+
'Connect-hosted options:',
|
|
1149
|
+
' --root <path> Project-memory root. Defaults to .compass',
|
|
1150
|
+
' --sync-api-url <url> Hosted sync api_url',
|
|
1151
|
+
' --sync-project-id <id> Hosted sync project_id',
|
|
1152
|
+
' --sync-credential-env-var <name> Hosted sync env var reference',
|
|
1153
|
+
' Replaces any existing project.yaml sync binding',
|
|
1154
|
+
'',
|
|
780
1155
|
'Start-session options:',
|
|
781
1156
|
' --root <path> Project-memory root. Defaults to .compass',
|
|
782
1157
|
' --tooling-root <path> Tooling root that contains CLAUDE.md. Defaults to cwd',
|
|
@@ -814,16 +1189,29 @@ function usageText() {
|
|
|
814
1189
|
' --format <name> Optional format: claude_md, agents_md, cursor_rules, copilot_instructions',
|
|
815
1190
|
' --dry-run Show planned writes without changing files',
|
|
816
1191
|
'',
|
|
1192
|
+
'Sync transport options:',
|
|
1193
|
+
' push --root <path> Push canonical local project memory to hosted',
|
|
1194
|
+
' pull-preview --root <path> Preview hosted proposals and remote state',
|
|
1195
|
+
' pull-preview --no-pending-proposals Exclude pending proposals from preview',
|
|
1196
|
+
' pull-export --proposal <id> Export a selected proposal; repeatable',
|
|
1197
|
+
' pull-export --preview-token <token> Export from a specific preview token',
|
|
1198
|
+
' pull-export --output <path> Output path under root; defaults to state/pull-export.json',
|
|
1199
|
+
' apply-export --output <path> Apply exported bundle; defaults to state/pull-export.json',
|
|
1200
|
+
'',
|
|
817
1201
|
'Docs-review options:',
|
|
818
1202
|
' --root <path> Project-memory root. Defaults to .compass',
|
|
819
1203
|
' --guided Accepted for the explicit comprehensive-review workflow',
|
|
820
1204
|
' --submit-hosted Submit the generated review request to hosted sync',
|
|
1205
|
+
' --poll-hosted Poll hosted docs-review run status and update state/docs-review.json',
|
|
821
1206
|
' --complete Mark accepted docs-review changes as completed locally',
|
|
822
1207
|
' --run-local Run the generated review request with a local provider',
|
|
823
1208
|
' --provider <name> Local provider for --run-local. Currently: anthropic',
|
|
824
1209
|
' --run-local-anthropic Compatibility alias for --run-local --provider anthropic',
|
|
825
1210
|
' --apply-output Apply accepted architecture doc blocks from review output',
|
|
826
1211
|
' --output <path> Review output path for --apply-output; defaults to state/docs-review-output.md',
|
|
1212
|
+
' --apply-decision-artifact Append an accepted hosted decision artifact locally',
|
|
1213
|
+
' --artifact <id> Artifact ID for --apply-decision-artifact',
|
|
1214
|
+
' --refresh-index Also regenerate decisions/INDEX.md after applying a decision artifact',
|
|
827
1215
|
' --llm <name> Preferred LLM/provider to run the external architecture review',
|
|
828
1216
|
' --model <name> Model name to record for the review',
|
|
829
1217
|
' --anthropic-env-var <name> Env var to use for local Anthropic docs-review runtime',
|
|
@@ -839,6 +1227,14 @@ function createDefaultIo() {
|
|
|
839
1227
|
};
|
|
840
1228
|
}
|
|
841
1229
|
|
|
1230
|
+
function formatOptionalRootFlag(rootDir) {
|
|
1231
|
+
if (!rootDir || rootDir === '.compass') {
|
|
1232
|
+
return '';
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
return ` --root ${rootDir}`;
|
|
1236
|
+
}
|
|
1237
|
+
|
|
842
1238
|
async function main() {
|
|
843
1239
|
try {
|
|
844
1240
|
const exitCode = await runCli(process.argv.slice(2));
|