@vibecompass/vibecompass 0.1.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/LICENSE +21 -0
- package/README.md +329 -0
- package/package.json +42 -0
- package/src/cli.js +572 -0
- package/src/frontmatter.js +28 -0
- package/src/generators/agent-files/agents-md.js +13 -0
- package/src/generators/agent-files/claude-md.js +13 -0
- package/src/generators/agent-files/copilot-instructions.js +13 -0
- package/src/generators/agent-files/cursor-rules.js +13 -0
- package/src/generators/agent-files/index.js +147 -0
- package/src/generators/agent-files/markers.js +49 -0
- package/src/generators/agent-files/template.js +75 -0
- package/src/hash.js +29 -0
- package/src/index.js +15 -0
- package/src/init.js +227 -0
- package/src/manifest.js +75 -0
- package/src/project-memory.js +678 -0
- package/src/project-yaml.js +107 -0
- package/src/read-model.js +557 -0
- package/src/scaffold.js +404 -0
- package/src/session.js +553 -0
- package/src/setup.js +719 -0
- package/src/simple-yaml.js +227 -0
- package/src/workflow.js +120 -0
package/src/cli.js
ADDED
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { realpathSync } from 'node:fs';
|
|
4
|
+
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { initializeProjectMemory } from './init.js';
|
|
6
|
+
import { resolveInitCliOptions } from './setup.js';
|
|
7
|
+
import { closeProjectSession, startProjectSession } from './session.js';
|
|
8
|
+
import { syncAgentInstructionFiles } from './generators/agent-files/index.js';
|
|
9
|
+
|
|
10
|
+
export async function runCli(argv, io = createDefaultIo(), runtime = {}) {
|
|
11
|
+
const parsed = parseCliArgs(argv);
|
|
12
|
+
|
|
13
|
+
if (parsed.command === 'help') {
|
|
14
|
+
io.stdout.write(`${usageText()}\n`);
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (parsed.command === 'init') {
|
|
19
|
+
const initPlan = await resolveInitCliOptions(parsed.options, {
|
|
20
|
+
cwd: runtime.cwd,
|
|
21
|
+
io,
|
|
22
|
+
runtime,
|
|
23
|
+
});
|
|
24
|
+
const result = await initializeProjectMemory({
|
|
25
|
+
...initPlan.initOptions,
|
|
26
|
+
...(runtime.cwd ? { cwd: runtime.cwd } : {}),
|
|
27
|
+
});
|
|
28
|
+
io.stdout.write(`Initialized VibeCompass project memory at ${result.rootDir}\n`);
|
|
29
|
+
if (initPlan.placementPattern) {
|
|
30
|
+
io.stdout.write(`Placement: ${initPlan.placementPattern}\n`);
|
|
31
|
+
}
|
|
32
|
+
io.stdout.write(`Wrote ${result.projectFilePath}\n`);
|
|
33
|
+
if (result.gitignoreUpdated) {
|
|
34
|
+
io.stdout.write(`Updated ${result.gitignorePath} to ignore state/\n`);
|
|
35
|
+
} else {
|
|
36
|
+
io.stdout.write(`${result.gitignorePath} already ignored state/\n`);
|
|
37
|
+
}
|
|
38
|
+
io.stdout.write(
|
|
39
|
+
`Generated ${result.manifestPath} (${result.manifest.canonical.document_count} canonical docs, ${result.manifest.canonical.warning_count} warnings)\n`,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (result.syncEnvVar) {
|
|
43
|
+
io.stdout.write(`Next step: set ${result.syncEnvVar} locally before your first sync.\n`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (result.contextFilePath) {
|
|
47
|
+
io.stdout.write(`Generated ${result.contextFilePath}\n`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const filePath of result.scaffoldCreatedFiles) {
|
|
51
|
+
if (filePath === result.contextFilePath) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
io.stdout.write(`Created ${filePath}\n`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (const filePath of result.scaffoldSkippedFiles) {
|
|
58
|
+
io.stdout.write(`Left existing ${filePath} untouched\n`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (initPlan.sessionPlan) {
|
|
62
|
+
const sessionResult = await startProjectSession({
|
|
63
|
+
rootDir: initPlan.initOptions.rootDir,
|
|
64
|
+
toolingRootDir: initPlan.initOptions.toolingRootDir,
|
|
65
|
+
workingOn: initPlan.sessionPlan.workingOn,
|
|
66
|
+
...(runtime.cwd ? { cwd: runtime.cwd } : {}),
|
|
67
|
+
});
|
|
68
|
+
io.stdout.write(`Started session ${sessionResult.sessionDate}-${sessionResult.sessionNumber}\n`);
|
|
69
|
+
io.stdout.write(`Updated ${sessionResult.claudePath}\n`);
|
|
70
|
+
io.stdout.write(`Created ${sessionResult.wipFilePath}\n`);
|
|
71
|
+
io.stdout.write(`Created ${sessionResult.handoffFilePath}\n`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (parsed.command === 'start-session') {
|
|
78
|
+
const result = await startProjectSession({
|
|
79
|
+
...parsed.options,
|
|
80
|
+
...(runtime.cwd ? { cwd: runtime.cwd } : {}),
|
|
81
|
+
});
|
|
82
|
+
io.stdout.write(`Started session ${result.sessionDate}-${result.sessionNumber}\n`);
|
|
83
|
+
io.stdout.write(`Updated ${result.claudePath}\n`);
|
|
84
|
+
io.stdout.write(`Created ${result.wipFilePath}\n`);
|
|
85
|
+
io.stdout.write(`Created ${result.handoffFilePath}\n`);
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (parsed.command === 'close-session') {
|
|
90
|
+
const result = await closeProjectSession({
|
|
91
|
+
...parsed.options,
|
|
92
|
+
...(runtime.cwd ? { cwd: runtime.cwd } : {}),
|
|
93
|
+
});
|
|
94
|
+
io.stdout.write(`Closed session ${result.sessionDate}-${result.sessionNumber}\n`);
|
|
95
|
+
io.stdout.write(`Wrote ${result.sessionFilePath}\n`);
|
|
96
|
+
io.stdout.write(`Updated ${result.claudePath}\n`);
|
|
97
|
+
if (result.workflowGuidance.length > 0) {
|
|
98
|
+
io.stdout.write('Workflow guidance:\n');
|
|
99
|
+
for (const item of result.workflowGuidance) {
|
|
100
|
+
io.stdout.write(`- ${item}\n`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
writeAgentFileSyncResult(io, result.agentFileSync);
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (parsed.command === 'sync-agents') {
|
|
108
|
+
const result = await syncAgentInstructionFiles({
|
|
109
|
+
...parsed.options,
|
|
110
|
+
...(runtime.cwd ? { cwd: runtime.cwd } : {}),
|
|
111
|
+
});
|
|
112
|
+
writeAgentFileSyncResult(io, result);
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
throw new Error(`Unknown command "${parsed.command}".`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function parseCliArgs(argv) {
|
|
120
|
+
const [command, ...rest] = argv;
|
|
121
|
+
|
|
122
|
+
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
|
123
|
+
return { command: 'help' };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (command !== 'init') {
|
|
127
|
+
if (command === 'start-session') {
|
|
128
|
+
return parseStartSessionArgs(rest);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (command === 'close-session') {
|
|
132
|
+
return parseCloseSessionArgs(rest);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (command === 'sync-agents') {
|
|
136
|
+
return parseSyncAgentsArgs(rest);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return { command };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const parsed = {
|
|
143
|
+
repos: [],
|
|
144
|
+
repoBranches: new Map(),
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
for (let index = 0; index < rest.length; index += 1) {
|
|
148
|
+
const token = rest[index];
|
|
149
|
+
|
|
150
|
+
if (token === '--force') {
|
|
151
|
+
parsed.force = true;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (token === '--guided') {
|
|
156
|
+
parsed.guided = true;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (token === '--with-workflow') {
|
|
161
|
+
parsed.withWorkflow = true;
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (token === '--with-claude') {
|
|
166
|
+
parsed.withClaude = true;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (token === '--with-agents') {
|
|
171
|
+
parsed.withAgents = true;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (token === '--start-session') {
|
|
176
|
+
parsed.startSession = true;
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (token === '--close-session-git-publish') {
|
|
181
|
+
parsed.closeSessionGitPublish = true;
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (!token.startsWith('--')) {
|
|
186
|
+
throw new Error(`Unexpected argument "${token}".`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const value = rest[index + 1];
|
|
190
|
+
if (value === undefined) {
|
|
191
|
+
throw new Error(`Flag "${token}" requires a value.`);
|
|
192
|
+
}
|
|
193
|
+
index += 1;
|
|
194
|
+
|
|
195
|
+
switch (token) {
|
|
196
|
+
case '--root':
|
|
197
|
+
parsed.rootDir = value;
|
|
198
|
+
break;
|
|
199
|
+
case '--tooling-root':
|
|
200
|
+
parsed.toolingRootDir = value;
|
|
201
|
+
break;
|
|
202
|
+
case '--name':
|
|
203
|
+
parsed.name = value;
|
|
204
|
+
break;
|
|
205
|
+
case '--slug':
|
|
206
|
+
parsed.slug = value;
|
|
207
|
+
break;
|
|
208
|
+
case '--description':
|
|
209
|
+
parsed.description = value;
|
|
210
|
+
break;
|
|
211
|
+
case '--placement':
|
|
212
|
+
parsed.placementPattern = value;
|
|
213
|
+
break;
|
|
214
|
+
case '--mode':
|
|
215
|
+
parsed.mode = value;
|
|
216
|
+
break;
|
|
217
|
+
case '--repo': {
|
|
218
|
+
const [id, remote] = splitAssignment(value, '--repo');
|
|
219
|
+
parsed.repos.push({ id, remote });
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
case '--repo-branch': {
|
|
223
|
+
const [id, defaultBranch] = splitAssignment(value, '--repo-branch');
|
|
224
|
+
parsed.repoBranches.set(id, defaultBranch);
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
case '--sync-api-url':
|
|
228
|
+
parsed.syncApiUrl = value;
|
|
229
|
+
break;
|
|
230
|
+
case '--sync-project-id':
|
|
231
|
+
parsed.syncProjectId = value;
|
|
232
|
+
break;
|
|
233
|
+
case '--sync-credential-env-var':
|
|
234
|
+
parsed.syncCredentialEnvVar = value;
|
|
235
|
+
break;
|
|
236
|
+
case '--session-working-on':
|
|
237
|
+
parsed.sessionWorkingOn = value;
|
|
238
|
+
break;
|
|
239
|
+
case '--close-session-git-remote':
|
|
240
|
+
parsed.closeSessionGitRemote = value;
|
|
241
|
+
break;
|
|
242
|
+
default:
|
|
243
|
+
throw new Error(`Unknown flag "${token}".`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
for (const repo of parsed.repos) {
|
|
248
|
+
const defaultBranch = parsed.repoBranches.get(repo.id);
|
|
249
|
+
if (defaultBranch) {
|
|
250
|
+
repo.defaultBranch = defaultBranch;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const syncValues = [
|
|
255
|
+
parsed.syncApiUrl,
|
|
256
|
+
parsed.syncProjectId,
|
|
257
|
+
parsed.syncCredentialEnvVar,
|
|
258
|
+
].filter(Boolean);
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
command: 'init',
|
|
262
|
+
options: {
|
|
263
|
+
guided: parsed.guided,
|
|
264
|
+
rootDir: parsed.rootDir,
|
|
265
|
+
toolingRootDir: parsed.toolingRootDir,
|
|
266
|
+
name: parsed.name,
|
|
267
|
+
slug: parsed.slug,
|
|
268
|
+
description: parsed.description,
|
|
269
|
+
placementPattern: parsed.placementPattern,
|
|
270
|
+
mode: parsed.mode,
|
|
271
|
+
repos: parsed.repos,
|
|
272
|
+
force: parsed.force,
|
|
273
|
+
startSession: parsed.startSession,
|
|
274
|
+
sessionWorkingOn: parsed.sessionWorkingOn,
|
|
275
|
+
closeSessionGitPublish: parsed.closeSessionGitPublish,
|
|
276
|
+
closeSessionGitRemote: parsed.closeSessionGitRemote,
|
|
277
|
+
...(parsed.withWorkflow || parsed.withClaude || parsed.withAgents
|
|
278
|
+
? {
|
|
279
|
+
bootstrap: {
|
|
280
|
+
workflow: parsed.withWorkflow,
|
|
281
|
+
claude: parsed.withClaude,
|
|
282
|
+
agents: parsed.withAgents,
|
|
283
|
+
},
|
|
284
|
+
}
|
|
285
|
+
: {}),
|
|
286
|
+
...(syncValues.length > 0
|
|
287
|
+
? {
|
|
288
|
+
sync: {
|
|
289
|
+
apiUrl: parsed.syncApiUrl,
|
|
290
|
+
projectId: parsed.syncProjectId,
|
|
291
|
+
credentialEnvVar: parsed.syncCredentialEnvVar,
|
|
292
|
+
},
|
|
293
|
+
}
|
|
294
|
+
: {}),
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function parseStartSessionArgs(argv) {
|
|
300
|
+
const parsed = {};
|
|
301
|
+
|
|
302
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
303
|
+
const token = argv[index];
|
|
304
|
+
|
|
305
|
+
if (!token.startsWith('--')) {
|
|
306
|
+
throw new Error(`Unexpected argument "${token}".`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const value = argv[index + 1];
|
|
310
|
+
if (value === undefined) {
|
|
311
|
+
throw new Error(`Flag "${token}" requires a value.`);
|
|
312
|
+
}
|
|
313
|
+
index += 1;
|
|
314
|
+
|
|
315
|
+
switch (token) {
|
|
316
|
+
case '--root':
|
|
317
|
+
parsed.rootDir = value;
|
|
318
|
+
break;
|
|
319
|
+
case '--tooling-root':
|
|
320
|
+
parsed.toolingRootDir = value;
|
|
321
|
+
break;
|
|
322
|
+
case '--working-on':
|
|
323
|
+
parsed.workingOn = value;
|
|
324
|
+
break;
|
|
325
|
+
case '--date':
|
|
326
|
+
parsed.date = value;
|
|
327
|
+
break;
|
|
328
|
+
case '--last-thing-completed':
|
|
329
|
+
parsed.lastThingCompleted = value;
|
|
330
|
+
break;
|
|
331
|
+
case '--blockers':
|
|
332
|
+
parsed.blockers = value;
|
|
333
|
+
break;
|
|
334
|
+
case '--next-session-should':
|
|
335
|
+
parsed.nextSessionShould = value;
|
|
336
|
+
break;
|
|
337
|
+
default:
|
|
338
|
+
throw new Error(`Unknown flag "${token}".`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
command: 'start-session',
|
|
344
|
+
options: parsed,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function parseCloseSessionArgs(argv) {
|
|
349
|
+
const parsed = {
|
|
350
|
+
completed: [],
|
|
351
|
+
decisions: [],
|
|
352
|
+
models: [],
|
|
353
|
+
blockers: [],
|
|
354
|
+
nextSteps: [],
|
|
355
|
+
};
|
|
356
|
+
// Repeatable CLI flags stay singular for readability, but map to plural arrays in the JS API.
|
|
357
|
+
|
|
358
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
359
|
+
const token = argv[index];
|
|
360
|
+
|
|
361
|
+
if (!token.startsWith('--')) {
|
|
362
|
+
throw new Error(`Unexpected argument "${token}".`);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const value = argv[index + 1];
|
|
366
|
+
if (value === undefined) {
|
|
367
|
+
throw new Error(`Flag "${token}" requires a value.`);
|
|
368
|
+
}
|
|
369
|
+
index += 1;
|
|
370
|
+
|
|
371
|
+
switch (token) {
|
|
372
|
+
case '--root':
|
|
373
|
+
parsed.rootDir = value;
|
|
374
|
+
break;
|
|
375
|
+
case '--tooling-root':
|
|
376
|
+
parsed.toolingRootDir = value;
|
|
377
|
+
break;
|
|
378
|
+
case '--title':
|
|
379
|
+
parsed.title = value;
|
|
380
|
+
break;
|
|
381
|
+
case '--worked-on':
|
|
382
|
+
parsed.workedOn = value;
|
|
383
|
+
break;
|
|
384
|
+
case '--completed':
|
|
385
|
+
parsed.completed.push(value);
|
|
386
|
+
break;
|
|
387
|
+
case '--decision':
|
|
388
|
+
parsed.decisions.push(value);
|
|
389
|
+
break;
|
|
390
|
+
case '--model':
|
|
391
|
+
parsed.models.push(value);
|
|
392
|
+
break;
|
|
393
|
+
case '--blocker':
|
|
394
|
+
parsed.blockers.push(value);
|
|
395
|
+
break;
|
|
396
|
+
case '--next-step':
|
|
397
|
+
parsed.nextSteps.push(value);
|
|
398
|
+
break;
|
|
399
|
+
case '--last-thing-completed':
|
|
400
|
+
parsed.lastThingCompleted = value;
|
|
401
|
+
break;
|
|
402
|
+
case '--next-session-should':
|
|
403
|
+
parsed.nextSessionShould = value;
|
|
404
|
+
break;
|
|
405
|
+
default:
|
|
406
|
+
throw new Error(`Unknown flag "${token}".`);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return {
|
|
411
|
+
command: 'close-session',
|
|
412
|
+
options: parsed,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function parseSyncAgentsArgs(argv) {
|
|
417
|
+
const parsed = {};
|
|
418
|
+
|
|
419
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
420
|
+
const token = argv[index];
|
|
421
|
+
|
|
422
|
+
if (token === '--dry-run') {
|
|
423
|
+
parsed.dryRun = true;
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (!token.startsWith('--')) {
|
|
428
|
+
throw new Error(`Unexpected argument "${token}".`);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
const value = argv[index + 1];
|
|
432
|
+
if (value === undefined) {
|
|
433
|
+
throw new Error(`Flag "${token}" requires a value.`);
|
|
434
|
+
}
|
|
435
|
+
index += 1;
|
|
436
|
+
|
|
437
|
+
switch (token) {
|
|
438
|
+
case '--root':
|
|
439
|
+
parsed.rootDir = value;
|
|
440
|
+
break;
|
|
441
|
+
case '--tooling-root':
|
|
442
|
+
parsed.toolingRootDir = value;
|
|
443
|
+
break;
|
|
444
|
+
case '--format':
|
|
445
|
+
parsed.format = value;
|
|
446
|
+
break;
|
|
447
|
+
default:
|
|
448
|
+
throw new Error(`Unknown flag "${token}".`);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return {
|
|
453
|
+
command: 'sync-agents',
|
|
454
|
+
options: parsed,
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function writeAgentFileSyncResult(io, result) {
|
|
459
|
+
if (!result) {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
io.stdout.write('Agent instruction files:\n');
|
|
464
|
+
for (const item of result.results) {
|
|
465
|
+
const suffix = item.warning ? ` — ${item.warning}` : '';
|
|
466
|
+
const stream = item.warning ? io.stderr : io.stdout;
|
|
467
|
+
stream.write(`- ${item.relativePath}: ${item.status}${suffix}\n`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function splitAssignment(value, flagName) {
|
|
472
|
+
const separatorIndex = value.indexOf('=');
|
|
473
|
+
if (separatorIndex <= 0 || separatorIndex === value.length - 1) {
|
|
474
|
+
throw new Error(`${flagName} expects id=value.`);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
return [value.slice(0, separatorIndex), value.slice(separatorIndex + 1)];
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function usageText() {
|
|
481
|
+
return [
|
|
482
|
+
'Usage:',
|
|
483
|
+
' vibecompass init --name <project-name> --mode <local-only|local-primary|hosted-only> --repo <id=remote> [options]',
|
|
484
|
+
' vibecompass start-session --working-on <text> [options]',
|
|
485
|
+
' vibecompass close-session --title <text> --completed <text> --next-step <text> [options]',
|
|
486
|
+
' vibecompass sync-agents [options]',
|
|
487
|
+
'',
|
|
488
|
+
'Init options:',
|
|
489
|
+
' --root <path> Project-memory root. Defaults to .compass',
|
|
490
|
+
' --tooling-root <path> Owner directory for workflow files and placement defaults',
|
|
491
|
+
' --slug <slug> Optional project slug',
|
|
492
|
+
' --description <text> Optional short project description',
|
|
493
|
+
' --placement <workspace-root|dedicated-memory-repo|primary-repo>',
|
|
494
|
+
' Optional explicit placement pattern',
|
|
495
|
+
' --guided Ask placement and setup questions interactively',
|
|
496
|
+
' --repo <id=remote> Repeatable repo descriptor',
|
|
497
|
+
' --repo-branch <id=branch> Optional per-repo default branch',
|
|
498
|
+
' --sync-api-url <url> Optional hosted sync api_url',
|
|
499
|
+
' --sync-project-id <id> Optional hosted sync project_id',
|
|
500
|
+
' --sync-credential-env-var <name> Optional hosted sync env var reference',
|
|
501
|
+
' --with-workflow Scaffold context.md and workflow guide files',
|
|
502
|
+
' --with-claude Create a starter CLAUDE.md if missing',
|
|
503
|
+
' --with-agents Create a starter AGENTS.md if missing',
|
|
504
|
+
' --start-session Open the first builder session after init',
|
|
505
|
+
' --session-working-on <text> Required with --start-session outside guided mode',
|
|
506
|
+
' --close-session-git-publish Include a Git publish step in the stored close-session workflow',
|
|
507
|
+
' --close-session-git-remote <name> Default Git remote name for that close-session publish step',
|
|
508
|
+
' --force Overwrite an existing project.yaml',
|
|
509
|
+
'',
|
|
510
|
+
'Start-session options:',
|
|
511
|
+
' --root <path> Project-memory root. Defaults to .compass',
|
|
512
|
+
' --tooling-root <path> Tooling root that contains CLAUDE.md. Defaults to cwd',
|
|
513
|
+
' --working-on <text> Required active-session summary',
|
|
514
|
+
' --date <YYYY-MM-DD> Optional explicit session date',
|
|
515
|
+
' --last-thing-completed <text> Optional override for the CLAUDE.md current-session block',
|
|
516
|
+
' --blockers <text> Optional current blockers summary',
|
|
517
|
+
' --next-session-should <text> Optional current-session handoff summary',
|
|
518
|
+
'',
|
|
519
|
+
'Close-session options:',
|
|
520
|
+
' --root <path> Project-memory root. Defaults to .compass',
|
|
521
|
+
' --tooling-root <path> Tooling root that contains CLAUDE.md. Defaults to cwd',
|
|
522
|
+
' --title <text> Required display title for the finalized session note',
|
|
523
|
+
' --worked-on <text> Optional override for "What we worked on"',
|
|
524
|
+
' --completed <text> Repeatable completed item',
|
|
525
|
+
' --decision <text> Repeatable decision reference or summary',
|
|
526
|
+
' --model <text> Optional repeatable model contribution entry',
|
|
527
|
+
' --blocker <text> Repeatable blocker or open question',
|
|
528
|
+
' --next-step <text> Repeatable next-session step',
|
|
529
|
+
' --last-thing-completed <text> Optional override for the CLAUDE.md completed summary',
|
|
530
|
+
' --next-session-should <text> Optional override for the CLAUDE.md next-session summary',
|
|
531
|
+
'',
|
|
532
|
+
'Sync-agents options:',
|
|
533
|
+
' --root <path> Project-memory root. Defaults to .compass',
|
|
534
|
+
' --tooling-root <path> Directory where agent files are written. Defaults to cwd',
|
|
535
|
+
' --format <name> Optional format: claude_md, agents_md, cursor_rules, copilot_instructions',
|
|
536
|
+
' --dry-run Show planned writes without changing files',
|
|
537
|
+
].join('\n');
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function createDefaultIo() {
|
|
541
|
+
return {
|
|
542
|
+
stdin: process.stdin,
|
|
543
|
+
stdout: process.stdout,
|
|
544
|
+
stderr: process.stderr,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
async function main() {
|
|
549
|
+
try {
|
|
550
|
+
const exitCode = await runCli(process.argv.slice(2));
|
|
551
|
+
process.exitCode = exitCode;
|
|
552
|
+
} catch (error) {
|
|
553
|
+
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
554
|
+
process.exitCode = 1;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (isDirectExecution(process.argv[1], import.meta.url)) {
|
|
559
|
+
main();
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
export function isDirectExecution(entryPath, moduleUrl = import.meta.url) {
|
|
563
|
+
if (!entryPath) {
|
|
564
|
+
return false;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
try {
|
|
568
|
+
return moduleUrl === pathToFileURL(realpathSync(entryPath)).href;
|
|
569
|
+
} catch {
|
|
570
|
+
return false;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { parseSimpleYaml } from './simple-yaml.js';
|
|
2
|
+
|
|
3
|
+
export function parseFrontmatter(source, options = {}) {
|
|
4
|
+
const sourceName = options.sourceName ?? 'Markdown';
|
|
5
|
+
const lines = source.split(/\r?\n/);
|
|
6
|
+
|
|
7
|
+
if (lines[0]?.trim() !== '---') {
|
|
8
|
+
return {
|
|
9
|
+
hasFrontmatter: false,
|
|
10
|
+
data: null,
|
|
11
|
+
body: source,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const closingIndex = lines.findIndex((line, index) => index > 0 && line.trim() === '---');
|
|
16
|
+
if (closingIndex === -1) {
|
|
17
|
+
throw new Error(`${sourceName} starts with frontmatter but is missing the closing delimiter.`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const frontmatterSource = lines.slice(1, closingIndex).join('\n');
|
|
21
|
+
const body = lines.slice(closingIndex + 1).join('\n').replace(/^\n/, '');
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
hasFrontmatter: true,
|
|
25
|
+
data: parseSimpleYaml(frontmatterSource, { sourceName: `${sourceName} frontmatter` }),
|
|
26
|
+
body,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { renderSharedInstructionBody } from './template.js';
|
|
2
|
+
|
|
3
|
+
export const agentsMdFormat = {
|
|
4
|
+
name: 'agents_md',
|
|
5
|
+
path: 'AGENTS.md',
|
|
6
|
+
render(context) {
|
|
7
|
+
return renderSharedInstructionBody(context, {
|
|
8
|
+
heading: `${context.projectName} Agent Instructions`,
|
|
9
|
+
intro:
|
|
10
|
+
'Agents should use VibeCompass project memory as the source of truth before planning, editing, or reviewing code.',
|
|
11
|
+
});
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { renderSharedInstructionBody } from './template.js';
|
|
2
|
+
|
|
3
|
+
export const claudeMdFormat = {
|
|
4
|
+
name: 'claude_md',
|
|
5
|
+
path: 'CLAUDE.md',
|
|
6
|
+
render(context) {
|
|
7
|
+
return renderSharedInstructionBody(context, {
|
|
8
|
+
heading: `${context.projectName} Claude Instructions`,
|
|
9
|
+
intro:
|
|
10
|
+
'Claude should use VibeCompass project memory as the source of truth before planning, editing, or reviewing code.',
|
|
11
|
+
});
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { renderSharedInstructionBody } from './template.js';
|
|
2
|
+
|
|
3
|
+
export const copilotInstructionsFormat = {
|
|
4
|
+
name: 'copilot_instructions',
|
|
5
|
+
path: '.github/copilot-instructions.md',
|
|
6
|
+
render(context) {
|
|
7
|
+
return renderSharedInstructionBody(context, {
|
|
8
|
+
heading: `${context.projectName} Copilot Instructions`,
|
|
9
|
+
intro:
|
|
10
|
+
'GitHub Copilot should use VibeCompass project memory as the source of truth before suggesting code changes.',
|
|
11
|
+
});
|
|
12
|
+
},
|
|
13
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { renderSharedInstructionBody } from './template.js';
|
|
2
|
+
|
|
3
|
+
export const cursorRulesFormat = {
|
|
4
|
+
name: 'cursor_rules',
|
|
5
|
+
path: '.cursorrules',
|
|
6
|
+
render(context) {
|
|
7
|
+
return renderSharedInstructionBody(context, {
|
|
8
|
+
heading: `${context.projectName} Cursor Rules`,
|
|
9
|
+
intro:
|
|
10
|
+
'Cursor should use VibeCompass project memory as the source of truth before suggesting or applying code changes.',
|
|
11
|
+
});
|
|
12
|
+
},
|
|
13
|
+
};
|