sidecar-cli 0.1.3 → 0.1.4-beta.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 CHANGED
@@ -85,12 +85,41 @@ npm run dev -- --help
85
85
 
86
86
  ## Quick start
87
87
 
88
- Initialize in a project directory:
88
+ 1. Initialize in a project directory:
89
89
 
90
90
  ```bash
91
91
  sidecar init
92
92
  ```
93
93
 
94
+ 2. (Optional) Define shared instruction templates once:
95
+
96
+ ```bash
97
+ mkdir -p ~/.sidecar-cli/instructions
98
+ ```
99
+
100
+ Create template files such as:
101
+
102
+ - `~/.sidecar-cli/instructions/web-app.md`
103
+ - `~/.sidecar-cli/instructions/desktop-app.md`
104
+
105
+ 3. Initialize with a shared template (writes project `instructions.md`):
106
+
107
+ ```bash
108
+ sidecar init --instructions-template web-app
109
+ ```
110
+
111
+ Or load directly from a specific file:
112
+
113
+ ```bash
114
+ sidecar init --instructions-file /absolute/path/to/instructions.md
115
+ ```
116
+
117
+ Notes:
118
+
119
+ - `--instructions-template <name>` resolves to `~/.sidecar-cli/instructions/<name>.md`.
120
+ - Use either `--instructions-template` or `--instructions-file` (not both).
121
+ - If `instructions.md` already exists, Sidecar will not overwrite it unless `--force` is used.
122
+
94
123
  This creates:
95
124
 
96
125
  - `.sidecar/sidecar.db`
@@ -100,6 +129,7 @@ This creates:
100
129
  - `.sidecar/summary.md`
101
130
  - `AGENTS.md` (repo root)
102
131
  - `CLAUDE.md` (repo root)
132
+ - `instructions.md` (repo root, only when `--instructions-template` or `--instructions-file` is provided)
103
133
 
104
134
  Use `--force` to overwrite Sidecar-managed files.
105
135
 
@@ -107,7 +137,7 @@ Use `--force` to overwrite Sidecar-managed files.
107
137
 
108
138
  Global:
109
139
 
110
- - `sidecar init [--force] [--name <project-name>] [--json]`
140
+ - `sidecar init [--force] [--name <project-name>] [--instructions-template <name>] [--instructions-file <path>] [--json]`
111
141
  - `sidecar status [--json]`
112
142
  - `sidecar preferences show [--json]`
113
143
  - `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';
@@ -279,14 +280,21 @@ program
279
280
  .description('Initialize Sidecar in the current directory')
280
281
  .option('--force', 'Overwrite Sidecar files if they already exist')
281
282
  .option('--name <project-name>', 'Project name (defaults to current directory name)')
283
+ .option('--instructions-template <name>', `Load instructions template by name from ${GLOBAL_INSTRUCTIONS_DIR} (example: "web-app")`)
284
+ .option('--instructions-file <path>', 'Load instructions from a specific markdown file path')
282
285
  .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')
286
+ .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
287
  .action((opts) => {
285
288
  const command = 'init';
286
289
  try {
287
290
  const rootPath = process.cwd();
288
291
  const sidecar = getSidecarPaths(rootPath);
289
292
  const projectName = opts.name?.trim() || path.basename(rootPath);
293
+ const resolvedInstructions = resolveInstructionsSource({
294
+ templateName: opts.instructionsTemplate,
295
+ sourcePath: opts.instructionsFile,
296
+ cwd: rootPath,
297
+ });
290
298
  if (fs.existsSync(sidecar.sidecarPath)) {
291
299
  const stat = fs.lstatSync(sidecar.sidecarPath);
292
300
  if (stat.isSymbolicLink()) {
@@ -315,6 +323,13 @@ program
315
323
  files.push(sidecar.rootAgentsPath);
316
324
  if (shouldWriteRootClaude)
317
325
  files.push(sidecar.rootClaudePath);
326
+ if (resolvedInstructions) {
327
+ const canWriteInstructions = Boolean(opts.force) || !fs.existsSync(sidecar.rootInstructionsPath);
328
+ if (!canWriteInstructions) {
329
+ fail(`Refusing to overwrite ${sidecar.rootInstructionsPath}. Re-run with --force or choose another destination.`);
330
+ }
331
+ files.push(sidecar.rootInstructionsPath);
332
+ }
318
333
  fs.mkdirSync(sidecar.sidecarPath, { recursive: true });
319
334
  if (opts.force) {
320
335
  for (const file of [
@@ -365,6 +380,9 @@ program
365
380
  if (shouldWriteRootClaude) {
366
381
  fs.writeFileSync(sidecar.rootClaudePath, renderClaudeMarkdown(projectName));
367
382
  }
383
+ if (resolvedInstructions) {
384
+ fs.writeFileSync(sidecar.rootInstructionsPath, resolvedInstructions.content);
385
+ }
368
386
  const db2 = new DatabaseSync(sidecar.dbPath);
369
387
  const refreshed = refreshSummaryFile(db2, rootPath, 1, 10);
370
388
  db2.close();
@@ -374,6 +392,7 @@ program
374
392
  projectName,
375
393
  filesCreated: files,
376
394
  summaryGeneratedAt: refreshed.generatedAt,
395
+ instructionsSource: resolvedInstructions?.sourceLabel ?? null,
377
396
  timestamp: nowIso(),
378
397
  };
379
398
  const shouldShowBanner = !opts.json && !bannerDisabled();
@@ -386,6 +405,7 @@ program
386
405
  'Sidecar provides local project memory for decisions, work logs, tasks, and summaries.',
387
406
  'Created:',
388
407
  ...data.filesCreated.map((f) => `- ${f}`),
408
+ ...(resolvedInstructions ? ['', `Loaded instructions.md from ${resolvedInstructions.sourceLabel}`] : []),
389
409
  '',
390
410
  'Next step:',
391
411
  'sidecar context',
@@ -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.1",
4
4
  "description": "Local-first project memory and recording tool",
5
5
  "scripts": {
6
6
  "build": "npm run clean && tsc -p tsconfig.json",