sidecar-cli 0.1.0-beta.6 → 0.1.0-beta.8

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 CHANGED
@@ -11,15 +11,7 @@ Sidecar is a local-first, CLI-first project memory and recording tool for human
11
11
  - Keep project memory structured and local.
12
12
  - Make session handoffs easier for humans and agents.
13
13
  - Record decisions, work logs, tasks, notes, sessions, and artifacts in one stable CLI.
14
- - Generate deterministic context and summary outputs without any cloud or LLM dependency.
15
-
16
- ## v1 scope
17
-
18
- - No cloud sync
19
- - No remote server
20
- - No GUI
21
- - No MCP server
22
- - No passive prompt capture
14
+ - Generate deterministic context and summary outputs from local project data.
23
15
 
24
16
  ## Install
25
17
 
@@ -76,7 +68,7 @@ npm run dev -- --help
76
68
  Initialize in a project directory:
77
69
 
78
70
  ```bash
79
- npm run dev -- init
71
+ sidecar init
80
72
  ```
81
73
 
82
74
  This creates:
@@ -86,6 +78,8 @@ This creates:
86
78
  - `.sidecar/preferences.json`
87
79
  - `.sidecar/AGENTS.md`
88
80
  - `.sidecar/summary.md`
81
+ - `AGENTS.md` (repo root)
82
+ - `CLAUDE.md` (repo root)
89
83
 
90
84
  Use `--force` to overwrite Sidecar-managed files.
91
85
 
@@ -161,6 +155,11 @@ npm run install:hooks
161
155
  ```
162
156
 
163
157
  This installs a non-blocking pre-commit reminder that runs `npm run sidecar:reminder`.
158
+ If a pre-commit hook already exists, Sidecar will not overwrite it unless you run:
159
+
160
+ ```bash
161
+ npm run install:hooks -- --force
162
+ ```
164
163
 
165
164
  Agents can discover the CLI surface programmatically with:
166
165
 
@@ -195,48 +194,5 @@ This makes Sidecar easy to automate from scripts and AI agents.
195
194
 
196
195
  ## Release and distribution
197
196
 
198
- Sidecar uses tag-based GitHub Actions releases.
199
-
200
- Tag formats:
201
-
202
- - stable: `v1.2.3`
203
- - beta: `v1.2.3-beta.1`
204
- - rc: `v1.2.3-rc.1`
205
-
206
- Behavior:
207
-
208
- - stable tags publish npm `latest`
209
- - beta tags publish npm `beta`
210
- - rc tags publish npm `rc`
211
- - all release tags create GitHub Releases and upload tarball assets
212
- - Homebrew tap updates are stable-only (beta/rc intentionally skipped)
213
-
214
- Workflows:
215
-
216
- - CI: `.github/workflows/ci.yml`
217
- - Release: `.github/workflows/release.yml`
218
-
219
- Required configuration:
220
-
221
- - `NPM_TOKEN` (secret)
222
- - `HOMEBREW_TAP_REPO` (variable, optional)
223
- - `HOMEBREW_TAP_GITHUB_TOKEN` (secret, optional)
224
-
225
- See [RELEASE.md](./RELEASE.md) for full release steps and examples.
226
-
227
- Quick preflight:
228
-
229
- ```bash
230
- npm run release_check -- --tag v1.2.3
231
- ```
232
-
233
- One-command release:
234
-
235
- ```bash
236
- npm run release:stable -- --version 1.2.3
237
- npm run release:beta -- --version 1.2.3 --pre 1
238
- npm run release:rc -- --version 1.2.3 --pre 1
239
-
240
- # preview only (no commit/tag/push)
241
- npm run release:beta -- --version 1.2.3 --pre 1 --dry-run
242
- ```
197
+ See [RELEASE.md](./RELEASE.md) for publishing/release details.
198
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for code structure and contribution guidelines.
package/dist/cli.js CHANGED
@@ -12,7 +12,7 @@ import { jsonFailure, jsonSuccess, printJsonEnvelope } from './lib/output.js';
12
12
  import { bannerDisabled, renderBanner } from './lib/banner.js';
13
13
  import { getUpdateNotice } from './lib/update-check.js';
14
14
  import { requireInitialized } from './db/client.js';
15
- import { renderAgentsMarkdown } from './templates/agents.js';
15
+ import { renderAgentsMarkdown, renderClaudeMarkdown } from './templates/agents.js';
16
16
  import { refreshSummaryFile } from './services/summary-service.js';
17
17
  import { buildContext } from './services/context-service.js';
18
18
  import { getCapabilitiesManifest } from './services/capabilities-service.js';
@@ -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,12 @@ program
187
215
  sidecar.agentsPath,
188
216
  sidecar.summaryPath,
189
217
  ];
218
+ const shouldWriteRootAgents = Boolean(opts.force) || !fs.existsSync(sidecar.rootAgentsPath);
219
+ const shouldWriteRootClaude = Boolean(opts.force) || !fs.existsSync(sidecar.rootClaudePath);
220
+ if (shouldWriteRootAgents)
221
+ files.push(sidecar.rootAgentsPath);
222
+ if (shouldWriteRootClaude)
223
+ files.push(sidecar.rootClaudePath);
190
224
  fs.mkdirSync(sidecar.sidecarPath, { recursive: true });
191
225
  if (opts.force) {
192
226
  for (const file of [
@@ -220,6 +254,12 @@ program
220
254
  output: { humanTime: true },
221
255
  }));
222
256
  fs.writeFileSync(sidecar.agentsPath, renderAgentsMarkdown(projectName));
257
+ if (shouldWriteRootAgents) {
258
+ fs.writeFileSync(sidecar.rootAgentsPath, renderAgentsMarkdown(projectName));
259
+ }
260
+ if (shouldWriteRootClaude) {
261
+ fs.writeFileSync(sidecar.rootClaudePath, renderClaudeMarkdown(projectName));
262
+ }
223
263
  const db2 = new Database(sidecar.dbPath);
224
264
  const refreshed = refreshSummaryFile(db2, rootPath, 1, 10);
225
265
  db2.close();
@@ -632,22 +672,9 @@ session
632
672
  const command = 'session verify';
633
673
  try {
634
674
  const { db, projectId } = requireInitialized();
635
- const summaryRecent = Boolean(db
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);
675
+ const warnings = verifySessionHygiene(db, projectId, summaryWasRefreshedRecently(db, projectId));
639
676
  db.close();
640
- if (opts.json)
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
- }
677
+ renderSessionHygiene(command, Boolean(opts.json), warnings);
651
678
  }
652
679
  catch (err) {
653
680
  handleCommandError(command, Boolean(opts.json), err);
@@ -662,22 +689,9 @@ program
662
689
  const command = 'doctor';
663
690
  try {
664
691
  const { db, projectId } = requireInitialized();
665
- const summaryRecent = Boolean(db
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);
692
+ const warnings = verifySessionHygiene(db, projectId, summaryWasRefreshedRecently(db, projectId));
669
693
  db.close();
670
- if (opts.json)
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
- }
694
+ renderSessionHygiene(command, Boolean(opts.json), warnings);
681
695
  }
682
696
  catch (err) {
683
697
  handleCommandError(command, Boolean(opts.json), err);
package/dist/lib/paths.js CHANGED
@@ -6,6 +6,8 @@ export function getSidecarPaths(rootPath) {
6
6
  return {
7
7
  rootPath,
8
8
  sidecarPath,
9
+ rootAgentsPath: path.join(rootPath, 'AGENTS.md'),
10
+ rootClaudePath: path.join(rootPath, 'CLAUDE.md'),
9
11
  dbPath: path.join(sidecarPath, 'sidecar.db'),
10
12
  configPath: path.join(sidecarPath, 'config.json'),
11
13
  preferencesPath: path.join(sidecarPath, 'preferences.json'),
@@ -16,8 +18,11 @@ export function getSidecarPaths(rootPath) {
16
18
  export function findSidecarRoot(startDir = process.cwd()) {
17
19
  let current = path.resolve(startDir);
18
20
  while (true) {
19
- if (fs.existsSync(path.join(current, SIDECAR_DIR))) {
20
- return current;
21
+ const candidate = path.join(current, SIDECAR_DIR);
22
+ if (fs.existsSync(candidate)) {
23
+ const stat = fs.lstatSync(candidate);
24
+ if (stat.isDirectory() && !stat.isSymbolicLink())
25
+ return current;
21
26
  }
22
27
  const parent = path.dirname(current);
23
28
  if (parent === current)
@@ -1,6 +1,8 @@
1
- export function renderAgentsMarkdown(projectName) {
1
+ function renderSharedGuide(projectName, heading) {
2
2
  return `# Sidecar Agent Guide
3
3
 
4
+ ${heading}
5
+
4
6
  Sidecar is the local project memory tool for this repository.
5
7
 
6
8
  ## MUST before final response
@@ -71,3 +73,9 @@ Run this before final response to catch missed Sidecar logging:
71
73
  Project: ${projectName}.
72
74
  `;
73
75
  }
76
+ export function renderAgentsMarkdown(projectName) {
77
+ return renderSharedGuide(projectName, 'Use this guide for AI coding agents in this repository.');
78
+ }
79
+ export function renderClaudeMarkdown(projectName) {
80
+ return renderSharedGuide(projectName, 'Claude-specific note: treat this as required workflow, not advisory context.');
81
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidecar-cli",
3
- "version": "0.1.0-beta.6",
3
+ "version": "0.1.0-beta.8",
4
4
  "description": "Local-first project memory and recording tool",
5
5
  "scripts": {
6
6
  "build": "npm run clean && tsc -p tsconfig.json",
@@ -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
- }