sidecar-cli 0.1.3 → 0.1.4-beta.2

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
@@ -6,6 +6,8 @@ project memory for your work
6
6
 
7
7
  Sidecar is a local-first, CLI-first project memory and recording tool for human developers and AI coding agents.
8
8
 
9
+ Documentation website: [usesidecar.dev](https://usesidecar.dev/)
10
+
9
11
  ## Why Sidecar exists
10
12
 
11
13
  - Keep project memory structured and local.
@@ -85,12 +87,41 @@ npm run dev -- --help
85
87
 
86
88
  ## Quick start
87
89
 
88
- Initialize in a project directory:
90
+ 1. Initialize in a project directory:
89
91
 
90
92
  ```bash
91
93
  sidecar init
92
94
  ```
93
95
 
96
+ 2. (Optional) Define shared instruction templates once:
97
+
98
+ ```bash
99
+ mkdir -p ~/.sidecar-cli/instructions
100
+ ```
101
+
102
+ Create template files such as:
103
+
104
+ - `~/.sidecar-cli/instructions/web-app.md`
105
+ - `~/.sidecar-cli/instructions/desktop-app.md`
106
+
107
+ 3. Initialize with a shared template (writes project `instructions.md`):
108
+
109
+ ```bash
110
+ sidecar init --instructions-template web-app
111
+ ```
112
+
113
+ Or load directly from a specific file:
114
+
115
+ ```bash
116
+ sidecar init --instructions-file /absolute/path/to/instructions.md
117
+ ```
118
+
119
+ Notes:
120
+
121
+ - `--instructions-template <name>` resolves to `~/.sidecar-cli/instructions/<name>.md`.
122
+ - Use either `--instructions-template` or `--instructions-file` (not both).
123
+ - If `instructions.md` already exists, Sidecar will not overwrite it unless `--force` is used.
124
+
94
125
  This creates:
95
126
 
96
127
  - `.sidecar/sidecar.db`
@@ -100,6 +131,7 @@ This creates:
100
131
  - `.sidecar/summary.md`
101
132
  - `AGENTS.md` (repo root)
102
133
  - `CLAUDE.md` (repo root)
134
+ - `instructions.md` (repo root, only when `--instructions-template` or `--instructions-file` is provided)
103
135
 
104
136
  Use `--force` to overwrite Sidecar-managed files.
105
137
 
@@ -107,7 +139,7 @@ Use `--force` to overwrite Sidecar-managed files.
107
139
 
108
140
  Global:
109
141
 
110
- - `sidecar init [--force] [--name <project-name>] [--json]`
142
+ - `sidecar init [--force] [--name <project-name>] [--instructions-template <name>] [--instructions-file <path>] [--json]`
111
143
  - `sidecar status [--json]`
112
144
  - `sidecar preferences show [--json]`
113
145
  - `sidecar ui [--no-open] [--port <port>] [--install-only] [--project <path>] [--reinstall]`
package/dist/cli.js CHANGED
@@ -9,6 +9,7 @@ import { initializeSchema } from './db/schema.js';
9
9
  import { findSidecarRoot, getSidecarPaths } from './lib/paths.js';
10
10
  import { nowIso, humanTime, stringifyJson } from './lib/format.js';
11
11
  import { SidecarError } from './lib/errors.js';
12
+ import { GLOBAL_INSTRUCTIONS_DIR, resolveInstructionsSource } from './lib/instructions.js';
12
13
  import { jsonFailure, jsonSuccess, printJsonEnvelope } from './lib/output.js';
13
14
  import { bannerDisabled, renderBanner } from './lib/banner.js';
14
15
  import { getUpdateNotice } from './lib/update-check.js';
@@ -72,6 +73,17 @@ function respondSuccess(command, asJson, data, lines = []) {
72
73
  console.log(line);
73
74
  }
74
75
  }
76
+ function renderInitBanner() {
77
+ return [
78
+ ' [■]─[▪]',
79
+ ' ███████╗██╗██████╗ ███████╗ ██████╗ █████╗ ██████╗',
80
+ ' ██╔════╝██║██╔══██╗██╔════╝██╔════╝██╔══██╗██╔══██╗',
81
+ ' ███████╗██║██║ ██║█████╗ ██║ ███████║██████╔╝',
82
+ ' ╚════██║██║██║ ██║██╔══╝ ██║ ██╔══██║██╔══██╗',
83
+ ' ███████║██║██████╔╝███████╗╚██████╗██║ ██║██║ ██║',
84
+ ' ╚══════╝╚═╝╚═════╝ ╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝',
85
+ ].join('\n');
86
+ }
75
87
  function summaryWasRefreshedRecently(db, projectId) {
76
88
  return Boolean(db
77
89
  .prepare(`SELECT id FROM events WHERE project_id = ? AND type = 'summary_generated' AND created_at >= datetime('now', '-3 day') LIMIT 1`)
@@ -279,14 +291,21 @@ program
279
291
  .description('Initialize Sidecar in the current directory')
280
292
  .option('--force', 'Overwrite Sidecar files if they already exist')
281
293
  .option('--name <project-name>', 'Project name (defaults to current directory name)')
294
+ .option('--instructions-template <name>', `Load instructions template by name from ${GLOBAL_INSTRUCTIONS_DIR} (example: "web-app")`)
295
+ .option('--instructions-file <path>', 'Load instructions from a specific markdown file path')
282
296
  .option('--json', 'Print machine-readable JSON output')
283
- .addHelpText('after', '\nExamples:\n $ sidecar init\n $ sidecar init --name "My Project"\n $ sidecar init --force --json')
297
+ .addHelpText('after', '\nExamples:\n $ sidecar init\n $ sidecar init --name "My Project"\n $ sidecar init --instructions-template web-app\n $ sidecar init --instructions-file ~/.sidecar-cli/instructions/desktop.md\n $ sidecar init --force --json')
284
298
  .action((opts) => {
285
299
  const command = 'init';
286
300
  try {
287
301
  const rootPath = process.cwd();
288
302
  const sidecar = getSidecarPaths(rootPath);
289
303
  const projectName = opts.name?.trim() || path.basename(rootPath);
304
+ const resolvedInstructions = resolveInstructionsSource({
305
+ templateName: opts.instructionsTemplate,
306
+ sourcePath: opts.instructionsFile,
307
+ cwd: rootPath,
308
+ });
290
309
  if (fs.existsSync(sidecar.sidecarPath)) {
291
310
  const stat = fs.lstatSync(sidecar.sidecarPath);
292
311
  if (stat.isSymbolicLink()) {
@@ -315,6 +334,13 @@ program
315
334
  files.push(sidecar.rootAgentsPath);
316
335
  if (shouldWriteRootClaude)
317
336
  files.push(sidecar.rootClaudePath);
337
+ if (resolvedInstructions) {
338
+ const canWriteInstructions = Boolean(opts.force) || !fs.existsSync(sidecar.rootInstructionsPath);
339
+ if (!canWriteInstructions) {
340
+ fail(`Refusing to overwrite ${sidecar.rootInstructionsPath}. Re-run with --force or choose another destination.`);
341
+ }
342
+ files.push(sidecar.rootInstructionsPath);
343
+ }
318
344
  fs.mkdirSync(sidecar.sidecarPath, { recursive: true });
319
345
  if (opts.force) {
320
346
  for (const file of [
@@ -365,6 +391,9 @@ program
365
391
  if (shouldWriteRootClaude) {
366
392
  fs.writeFileSync(sidecar.rootClaudePath, renderClaudeMarkdown(projectName));
367
393
  }
394
+ if (resolvedInstructions) {
395
+ fs.writeFileSync(sidecar.rootInstructionsPath, resolvedInstructions.content);
396
+ }
368
397
  const db2 = new DatabaseSync(sidecar.dbPath);
369
398
  const refreshed = refreshSummaryFile(db2, rootPath, 1, 10);
370
399
  db2.close();
@@ -374,21 +403,18 @@ program
374
403
  projectName,
375
404
  filesCreated: files,
376
405
  summaryGeneratedAt: refreshed.generatedAt,
406
+ instructionsSource: resolvedInstructions?.sourceLabel ?? null,
377
407
  timestamp: nowIso(),
378
408
  };
379
409
  const shouldShowBanner = !opts.json && !bannerDisabled();
380
410
  if (shouldShowBanner) {
381
- console.log(renderBanner());
411
+ console.log(renderInitBanner());
382
412
  console.log('');
383
413
  }
384
414
  respondSuccess(command, Boolean(opts.json), data, [
385
415
  `Initialized Sidecar for project: ${projectName}`,
386
- 'Sidecar provides local project memory for decisions, work logs, tasks, and summaries.',
387
- 'Created:',
388
- ...data.filesCreated.map((f) => `- ${f}`),
389
- '',
390
- 'Next step:',
391
- 'sidecar context',
416
+ 'Documentation: https://usesidecar.dev/',
417
+ ...(resolvedInstructions ? ['', `Loaded instructions.md from ${resolvedInstructions.sourceLabel}`] : []),
392
418
  ]);
393
419
  }
394
420
  catch (err) {
@@ -0,0 +1,40 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { SidecarError } from './errors.js';
5
+ export const GLOBAL_INSTRUCTIONS_DIR = path.join(os.homedir(), '.sidecar-cli', 'instructions');
6
+ function readInstructionsFile(sourcePath, sourceLabel) {
7
+ if (!fs.existsSync(sourcePath)) {
8
+ throw new SidecarError(`Instructions source not found: ${sourcePath}`);
9
+ }
10
+ const stat = fs.statSync(sourcePath);
11
+ if (!stat.isFile()) {
12
+ throw new SidecarError(`Instructions source is not a file: ${sourcePath}`);
13
+ }
14
+ return {
15
+ sourcePath,
16
+ sourceLabel,
17
+ content: fs.readFileSync(sourcePath, 'utf8'),
18
+ };
19
+ }
20
+ export function resolveInstructionsSource(opts) {
21
+ const templateName = opts.templateName?.trim();
22
+ const sourcePath = opts.sourcePath?.trim();
23
+ if (!templateName && !sourcePath)
24
+ return null;
25
+ if (templateName && sourcePath) {
26
+ throw new SidecarError('Use only one option: --instructions-template or --instructions-file.');
27
+ }
28
+ if (sourcePath) {
29
+ const resolvedPath = path.resolve(opts.cwd, sourcePath);
30
+ return readInstructionsFile(resolvedPath, `file:${resolvedPath}`);
31
+ }
32
+ const fileName = templateName?.endsWith('.md') ? templateName : `${templateName}.md`;
33
+ if (!fileName)
34
+ return null;
35
+ const globalPath = path.join(GLOBAL_INSTRUCTIONS_DIR, fileName);
36
+ if (!fs.existsSync(globalPath)) {
37
+ throw new SidecarError(`Instructions template "${templateName}" not found at ${globalPath}. Create it under ${GLOBAL_INSTRUCTIONS_DIR}.`);
38
+ }
39
+ return readInstructionsFile(globalPath, `template:${templateName}`);
40
+ }
package/dist/lib/paths.js CHANGED
@@ -11,6 +11,7 @@ export function getSidecarPaths(rootPath) {
11
11
  promptsPath: path.join(sidecarPath, 'prompts'),
12
12
  rootAgentsPath: path.join(rootPath, 'AGENTS.md'),
13
13
  rootClaudePath: path.join(rootPath, 'CLAUDE.md'),
14
+ rootInstructionsPath: path.join(rootPath, 'instructions.md'),
14
15
  dbPath: path.join(sidecarPath, 'sidecar.db'),
15
16
  configPath: path.join(sidecarPath, 'config.json'),
16
17
  preferencesPath: path.join(sidecarPath, 'preferences.json'),
@@ -22,7 +22,7 @@ export function getCapabilitiesManifest(version) {
22
22
  description: 'Initialize Sidecar in the current directory',
23
23
  json_output: true,
24
24
  arguments: [],
25
- options: ['--force', '--name <project-name>', '--json'],
25
+ options: ['--force', '--name <project-name>', '--instructions-template <name>', '--instructions-file <path>', '--json'],
26
26
  },
27
27
  {
28
28
  name: 'status',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sidecar-cli",
3
- "version": "0.1.3",
3
+ "version": "0.1.4-beta.2",
4
4
  "description": "Local-first project memory and recording tool",
5
5
  "scripts": {
6
6
  "build": "npm run clean && tsc -p tsconfig.json",