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 +34 -2
- package/dist/cli.js +34 -8
- package/dist/lib/instructions.js +40 -0
- package/dist/lib/paths.js +1 -0
- package/dist/services/capabilities-service.js +1 -1
- package/package.json +1 -1
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(
|
|
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
|
-
'
|
|
387
|
-
'
|
|
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',
|