@agentled/cli
Version:
CLI for Agentled — manage workflows, apps, and knowledge from the command line. Zero context-window cost for AI agents.
133 lines • 5.16 kB
JavaScript
/**
* Bootstrap `~/.agentled/` on `agentled setup` runs.
*
* Writes:
* ~/.agentled/README.md (top-level pointer)
* ~/.agentled/.content-version (refresh marker)
* ~/.agentled/docs/WORKFLOW-SHAPE.md (canonical shape)
* ~/.agentled/docs/GETTING-STARTED.md (5-min first workflow)
* ~/.agentled/docs/GOTCHAS.md (silent failure modes)
* ~/.agentled/docs/PATTERNS.md (scaffold index)
* ~/.agentled/examples/scaffolds/* (copied from packages/cli/scaffolds/)
*
* **`docs/` and `examples/scaffolds/` are CLI-managed.** When the
* bundled `HOME_CONTENT_VERSION` advances past the on-disk marker, the
* bootstrap unconditionally rewrites every bundled file — user edits
* to those files WILL be overwritten. Treat them as read-only and put
* your own notes in `~/.agentled/docs/local/` (never touched) or in
* the per-workspace `agentled_<slug>/` folder.
*
* `config.json` is never touched.
*
* `HOME_CONTENT_VERSION` is decoupled from the CLI semver — it bumps
* only when bundled docs change, so a CLI patch release with no doc
* change does NOT trigger a rewrite.
*
* Idempotent: a re-run with the same marker version is a no-op.
*/
import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, copyFileSync } from 'node:fs';
import { join } from 'node:path';
import { homedir } from 'node:os';
import { bundledScaffoldsDir } from './workspace-folder.js';
import { HOME_CONTENT_VERSION, HOME_README_MD, WORKFLOW_SHAPE_MD, GETTING_STARTED_MD, GOTCHAS_MD, PATTERNS_MD, } from './agentled-home-content.js';
const MARKER_FILE = '.content-version';
function readMarker(homeDir) {
const path = join(homeDir, MARKER_FILE);
if (!existsSync(path))
return null;
try {
return readFileSync(path, 'utf-8').trim() || null;
}
catch {
return null;
}
}
function writeMarker(homeDir) {
writeFileSync(join(homeDir, MARKER_FILE), HOME_CONTENT_VERSION + '\n');
}
/**
* Compare integer-like marker versions: -1, 0, 1.
*
* `HOME_CONTENT_VERSION` is intentionally an opaque integer string
* ("1", "2", …), but parsed defensively in case a future maintainer
* uses a semver-shaped value.
*/
function compareVersions(a, b) {
const parse = (v) => v.replace(/^v/, '').split('-')[0].split('.').map(n => parseInt(n, 10) || 0);
const [a1, a2 = 0, a3 = 0] = parse(a);
const [b1, b2 = 0, b3 = 0] = parse(b);
if (a1 !== b1)
return a1 < b1 ? -1 : 1;
if (a2 !== b2)
return a2 < b2 ? -1 : 1;
if (a3 !== b3)
return a3 < b3 ? -1 : 1;
return 0;
}
function shouldRefresh(homeDir) {
const installed = readMarker(homeDir);
if (!installed)
return true; // first run
return compareVersions(installed, HOME_CONTENT_VERSION) < 0;
}
/**
* Copy bundled scaffold assets into `examples/scaffolds/`. Includes
* any `.json` pipeline scaffold and any `.md` companion documentation.
* Other extensions are skipped — keep this whitelist tight so a stray
* file in `packages/cli/scaffolds/` never lands in user-visible space.
*/
function copyBundledScaffolds(targetDir) {
const sourceDir = bundledScaffoldsDir();
if (!existsSync(sourceDir))
return 0;
mkdirSync(targetDir, { recursive: true });
let count = 0;
for (const entry of readdirSync(sourceDir)) {
if (!entry.endsWith('.json') && !entry.endsWith('.md'))
continue;
const src = join(sourceDir, entry);
const dest = join(targetDir, entry);
copyFileSync(src, dest);
count++;
}
return count;
}
/**
* Idempotent. Creates `~/.agentled/` if missing and rewrites the
* bundled docs + scaffolds when the on-disk content version is older
* than `HOME_CONTENT_VERSION`. Bundled files are unconditionally
* overwritten on refresh; never touches `config.json` or any file
* under `docs/local/`.
*/
export function bootstrapAgentledHome() {
const homeDir = join(homedir(), '.agentled');
const docsDir = join(homeDir, 'docs');
const examplesDir = join(homeDir, 'examples');
const scaffoldsDir = join(examplesDir, 'scaffolds');
if (!existsSync(homeDir))
mkdirSync(homeDir, { recursive: true });
if (!shouldRefresh(homeDir)) {
return {
homeDir,
refreshed: false,
scaffoldsCopied: 0,
version: readMarker(homeDir) ?? HOME_CONTENT_VERSION,
};
}
mkdirSync(docsDir, { recursive: true });
mkdirSync(scaffoldsDir, { recursive: true });
writeFileSync(join(homeDir, 'README.md'), HOME_README_MD);
writeFileSync(join(docsDir, 'WORKFLOW-SHAPE.md'), WORKFLOW_SHAPE_MD);
writeFileSync(join(docsDir, 'GETTING-STARTED.md'), GETTING_STARTED_MD);
writeFileSync(join(docsDir, 'GOTCHAS.md'), GOTCHAS_MD);
writeFileSync(join(docsDir, 'PATTERNS.md'), PATTERNS_MD);
const scaffoldsCopied = copyBundledScaffolds(scaffoldsDir);
writeMarker(homeDir);
return {
homeDir,
refreshed: true,
scaffoldsCopied,
version: HOME_CONTENT_VERSION,
};
}
//# sourceMappingURL=home-bootstrap.js.map