sidecar-cli 0.1.0-beta.6 → 0.1.0-beta.7
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 +6 -0
- package/dist/cli.js +38 -30
- package/dist/lib/paths.js +6 -2
- package/package.json +1 -1
- package/dist/lib/validation.js +0 -31
package/README.md
CHANGED
|
@@ -161,6 +161,11 @@ npm run install:hooks
|
|
|
161
161
|
```
|
|
162
162
|
|
|
163
163
|
This installs a non-blocking pre-commit reminder that runs `npm run sidecar:reminder`.
|
|
164
|
+
If a pre-commit hook already exists, Sidecar will not overwrite it unless you run:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
npm run install:hooks -- --force
|
|
168
|
+
```
|
|
164
169
|
|
|
165
170
|
Agents can discover the CLI surface programmatically with:
|
|
166
171
|
|
|
@@ -223,6 +228,7 @@ Required configuration:
|
|
|
223
228
|
- `HOMEBREW_TAP_GITHUB_TOKEN` (secret, optional)
|
|
224
229
|
|
|
225
230
|
See [RELEASE.md](./RELEASE.md) for full release steps and examples.
|
|
231
|
+
See [CONTRIBUTING.md](./CONTRIBUTING.md) for code structure and contribution guidelines.
|
|
226
232
|
|
|
227
233
|
Quick preflight:
|
|
228
234
|
|
package/dist/cli.js
CHANGED
|
@@ -58,6 +58,25 @@ function respondSuccess(command, asJson, data, lines = []) {
|
|
|
58
58
|
console.log(line);
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
|
+
function summaryWasRefreshedRecently(db, projectId) {
|
|
62
|
+
return Boolean(db
|
|
63
|
+
.prepare(`SELECT id FROM events WHERE project_id = ? AND type = 'summary_generated' AND created_at >= datetime('now', '-3 day') LIMIT 1`)
|
|
64
|
+
.get(projectId));
|
|
65
|
+
}
|
|
66
|
+
function renderSessionHygiene(command, asJson, warnings) {
|
|
67
|
+
if (asJson) {
|
|
68
|
+
printJsonEnvelope(jsonSuccess(command, { warnings, healthy: warnings.length === 0 }));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (warnings.length === 0) {
|
|
72
|
+
console.log('Session hygiene looks good.');
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
console.log('Session hygiene warnings:');
|
|
76
|
+
for (const warning of warnings) {
|
|
77
|
+
console.log(`- ${warning}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
61
80
|
function renderContextText(data) {
|
|
62
81
|
const lines = [];
|
|
63
82
|
lines.push(`Project: ${data.projectName}`);
|
|
@@ -177,6 +196,15 @@ program
|
|
|
177
196
|
const rootPath = process.cwd();
|
|
178
197
|
const sidecar = getSidecarPaths(rootPath);
|
|
179
198
|
const projectName = opts.name?.trim() || path.basename(rootPath);
|
|
199
|
+
if (fs.existsSync(sidecar.sidecarPath)) {
|
|
200
|
+
const stat = fs.lstatSync(sidecar.sidecarPath);
|
|
201
|
+
if (stat.isSymbolicLink()) {
|
|
202
|
+
fail('Refusing to initialize: .sidecar is a symbolic link. Remove it and run init again.');
|
|
203
|
+
}
|
|
204
|
+
if (!stat.isDirectory()) {
|
|
205
|
+
fail('Refusing to initialize: .sidecar exists but is not a directory.');
|
|
206
|
+
}
|
|
207
|
+
}
|
|
180
208
|
if (fs.existsSync(sidecar.sidecarPath) && !opts.force) {
|
|
181
209
|
fail('Sidecar is already initialized in this project. Re-run with --force to recreate .sidecar files.');
|
|
182
210
|
}
|
|
@@ -187,6 +215,9 @@ program
|
|
|
187
215
|
sidecar.agentsPath,
|
|
188
216
|
sidecar.summaryPath,
|
|
189
217
|
];
|
|
218
|
+
const shouldWriteRootAgents = Boolean(opts.force) || !fs.existsSync(sidecar.rootAgentsPath);
|
|
219
|
+
if (shouldWriteRootAgents)
|
|
220
|
+
files.push(sidecar.rootAgentsPath);
|
|
190
221
|
fs.mkdirSync(sidecar.sidecarPath, { recursive: true });
|
|
191
222
|
if (opts.force) {
|
|
192
223
|
for (const file of [
|
|
@@ -220,6 +251,9 @@ program
|
|
|
220
251
|
output: { humanTime: true },
|
|
221
252
|
}));
|
|
222
253
|
fs.writeFileSync(sidecar.agentsPath, renderAgentsMarkdown(projectName));
|
|
254
|
+
if (shouldWriteRootAgents) {
|
|
255
|
+
fs.writeFileSync(sidecar.rootAgentsPath, renderAgentsMarkdown(projectName));
|
|
256
|
+
}
|
|
223
257
|
const db2 = new Database(sidecar.dbPath);
|
|
224
258
|
const refreshed = refreshSummaryFile(db2, rootPath, 1, 10);
|
|
225
259
|
db2.close();
|
|
@@ -632,22 +666,9 @@ session
|
|
|
632
666
|
const command = 'session verify';
|
|
633
667
|
try {
|
|
634
668
|
const { db, projectId } = requireInitialized();
|
|
635
|
-
const
|
|
636
|
-
.prepare(`SELECT id FROM events WHERE project_id = ? AND type = 'summary_generated' AND created_at >= datetime('now', '-3 day') LIMIT 1`)
|
|
637
|
-
.get(projectId));
|
|
638
|
-
const warnings = verifySessionHygiene(db, projectId, summaryRecent);
|
|
669
|
+
const warnings = verifySessionHygiene(db, projectId, summaryWasRefreshedRecently(db, projectId));
|
|
639
670
|
db.close();
|
|
640
|
-
|
|
641
|
-
printJsonEnvelope(jsonSuccess(command, { warnings, healthy: warnings.length === 0 }));
|
|
642
|
-
else {
|
|
643
|
-
if (warnings.length === 0) {
|
|
644
|
-
console.log('Session hygiene looks good.');
|
|
645
|
-
return;
|
|
646
|
-
}
|
|
647
|
-
console.log('Session hygiene warnings:');
|
|
648
|
-
for (const w of warnings)
|
|
649
|
-
console.log(`- ${w}`);
|
|
650
|
-
}
|
|
671
|
+
renderSessionHygiene(command, Boolean(opts.json), warnings);
|
|
651
672
|
}
|
|
652
673
|
catch (err) {
|
|
653
674
|
handleCommandError(command, Boolean(opts.json), err);
|
|
@@ -662,22 +683,9 @@ program
|
|
|
662
683
|
const command = 'doctor';
|
|
663
684
|
try {
|
|
664
685
|
const { db, projectId } = requireInitialized();
|
|
665
|
-
const
|
|
666
|
-
.prepare(`SELECT id FROM events WHERE project_id = ? AND type = 'summary_generated' AND created_at >= datetime('now', '-3 day') LIMIT 1`)
|
|
667
|
-
.get(projectId));
|
|
668
|
-
const warnings = verifySessionHygiene(db, projectId, summaryRecent);
|
|
686
|
+
const warnings = verifySessionHygiene(db, projectId, summaryWasRefreshedRecently(db, projectId));
|
|
669
687
|
db.close();
|
|
670
|
-
|
|
671
|
-
printJsonEnvelope(jsonSuccess(command, { warnings, healthy: warnings.length === 0 }));
|
|
672
|
-
else {
|
|
673
|
-
if (warnings.length === 0) {
|
|
674
|
-
console.log('Session hygiene looks good.');
|
|
675
|
-
return;
|
|
676
|
-
}
|
|
677
|
-
console.log('Session hygiene warnings:');
|
|
678
|
-
for (const w of warnings)
|
|
679
|
-
console.log(`- ${w}`);
|
|
680
|
-
}
|
|
688
|
+
renderSessionHygiene(command, Boolean(opts.json), warnings);
|
|
681
689
|
}
|
|
682
690
|
catch (err) {
|
|
683
691
|
handleCommandError(command, Boolean(opts.json), err);
|
package/dist/lib/paths.js
CHANGED
|
@@ -6,6 +6,7 @@ export function getSidecarPaths(rootPath) {
|
|
|
6
6
|
return {
|
|
7
7
|
rootPath,
|
|
8
8
|
sidecarPath,
|
|
9
|
+
rootAgentsPath: path.join(rootPath, 'AGENTS.md'),
|
|
9
10
|
dbPath: path.join(sidecarPath, 'sidecar.db'),
|
|
10
11
|
configPath: path.join(sidecarPath, 'config.json'),
|
|
11
12
|
preferencesPath: path.join(sidecarPath, 'preferences.json'),
|
|
@@ -16,8 +17,11 @@ export function getSidecarPaths(rootPath) {
|
|
|
16
17
|
export function findSidecarRoot(startDir = process.cwd()) {
|
|
17
18
|
let current = path.resolve(startDir);
|
|
18
19
|
while (true) {
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
const candidate = path.join(current, SIDECAR_DIR);
|
|
21
|
+
if (fs.existsSync(candidate)) {
|
|
22
|
+
const stat = fs.lstatSync(candidate);
|
|
23
|
+
if (stat.isDirectory() && !stat.isSymbolicLink())
|
|
24
|
+
return current;
|
|
21
25
|
}
|
|
22
26
|
const parent = path.dirname(current);
|
|
23
27
|
if (parent === current)
|
package/package.json
CHANGED
package/dist/lib/validation.js
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
export const eventTypeSchema = z.enum([
|
|
3
|
-
'note',
|
|
4
|
-
'worklog',
|
|
5
|
-
'decision',
|
|
6
|
-
'task_update',
|
|
7
|
-
'summary',
|
|
8
|
-
'context',
|
|
9
|
-
]);
|
|
10
|
-
export const taskPrioritySchema = z.enum(['low', 'medium', 'high']);
|
|
11
|
-
export const addEventSchema = z.object({
|
|
12
|
-
type: eventTypeSchema,
|
|
13
|
-
title: z.string().trim().min(1).max(140),
|
|
14
|
-
body: z.string().trim().min(1),
|
|
15
|
-
tags: z.array(z.string().trim().min(1)).default([]),
|
|
16
|
-
});
|
|
17
|
-
export const addTaskSchema = z.object({
|
|
18
|
-
title: z.string().trim().min(1).max(240),
|
|
19
|
-
priority: taskPrioritySchema.default('medium'),
|
|
20
|
-
});
|
|
21
|
-
export const completeTaskSchema = z.object({
|
|
22
|
-
id: z.number().int().positive(),
|
|
23
|
-
});
|
|
24
|
-
export function parseTags(input) {
|
|
25
|
-
if (!input)
|
|
26
|
-
return [];
|
|
27
|
-
return input
|
|
28
|
-
.split(',')
|
|
29
|
-
.map((t) => t.trim())
|
|
30
|
-
.filter(Boolean);
|
|
31
|
-
}
|