@gluecharm-lab/easyspecs-cli
v0.3.11
Published
EasySpecs headless CLI (synthesis, analysis, diagnose, download/upload context, auth, ACE)
Readme
EasySpecs CLI
Headless EasySpecs command-line tool: same orchestration ideas as the EasySpecs VS Code extension, without an editor. easyspecs-cli help is organized around three factory-level jobs on application context; the rest of the commands and flags exist to tune where the work runs (--cwd, --worktree, --root), how it behaves (--ci, --json, OpenCode and config.json settings), optional cloud sync, and step-level escapes (partial pipelines, resumes, deterministic checks).
The three factory-level commands
easyspecs-cli analysis— create new context
Runs the full Generate Context factory end to end (synthesis until stable, coverage, remediation, reporting, link mapping, index assembly). Use this when you need a fresh or full rebuild of.gluecharm/contextfrom the repo.--force-new-context-analysisoverrides skip-when-already-analyzed behavior wheneasyspecs.factory.cloudContextAnalyzedwould otherwise short-circuit the run.--no-worktree(alias--noworktree) runs the factory on the repository checkout itself instead of a detached temp git worktree (SRS-71). On a successful factory exit, the CLI always copies.gluecharm/contextfrom the analysis checkout into<repoRoot>unless you pass--no-promoteor the checkout is already the repo root (no--promoteneeded); stderr[pipeline:analysis] promoted …or a skip line when in-place. This step does not useeasyspecs.analysis.promoteContextToWorkspace— that setting still applies torun synthesisandupdate context. Afteranalysis, runeasyspecs-cli upload contextorupload republishwhen you want context on EasySpecs (auth login+config set-project-id). Ifgit worktree addfails because a temp analysis checkout folder was removed but Git still lists it, the CLI runsgit worktree prunein your repository and retriesgit worktree addonce (same behaviour wherever the CLI creates that temp worktree).easyspecs-cli update context— update context from code churn
Incremental refresh from a git delta since the stored baseline (easyspecs.factory.updateContext.lastRunAt) or, when that is absent, inferred from workspace context mtimes: materialiseschanges-since-date.md, runs scoped remediation when commits and touched paths warrant it, then optional promotion.--no-worktree/--noworktreeuses<repoRoot>as the analysis checkout (SRS-71). Useupload contextafterward when you need cloud sync. Use this day to day afteranalysiswhen the codebase moved but specs did not restart from zero.easyspecs-cli context drift <referencePath>— analyze drift against specsreferencePathis a file or directory of truth documents (requirements, specs, PRD, architecture notes — whatever your team treats as ground truth relative to repo root). The CLI compares repository / analysis checkout context against that reference (OpenCode-assisted), writesdrift-<label>-<date>.mdunder.gluecharm/context/drift/, and can update the reference index markdown.--label <slug>names the report file;--index <path>overrides which root markdown anchors the comparison;--dry-runvalidates references only (no worktree, agent, or writes).--no-worktree/--noworktreeuses<repoRoot>as the checkout and cannot be combined with--root worktreeor--worktree <path>(SRS-71).
Supporting commands (not substitutes for those three goals): auth and config wire API access and easyspecs.easyspecsProjectId; download context / upload context / upload republish move artefacts between disk and EasySpecs; doctor checks readiness and redacted merged settings; diagnose and context link-graph are deterministic gates and link maintenance; run synthesis and run synthesis resume-* run single stages or catch-up without the full factory; ace * manages optional learnings under .gluecharm/context/learnings.
Vocabulary: A Factory is the full ordered analysis run. A Pipeline is one major stage inside it (synthesis, coverage, remediation, link mapping, upload, download). A Workstation is one atomic OpenCode or programmatic step inside a pipeline. Canonical config.json keys live under easyspecs.factory.*, easyspecs.workstations.*, and easyspecs.pipelines.upload.*; legacy easyspecs.orchestration.*, easyspecs.macro.debug, and several easyspecs.analysis.* tunables still work for one deprecation cycle and may log [deprecated-setting] on stderr when read. Human stderr may tag lines [factory], [pipeline:…], [workstation:…], [pool], [ace].
Published as @gluecharm-lab/easyspecs-cli (npm org gluecharm-lab; EasySpecs is the product). The executable on your PATH is easyspecs-cli.
Requirements
- Node.js ≥ 18
- OpenCode (
opencodeonPATH) for flows that run agents — install OpenCode using its own install instructions
Install
npm install -g @gluecharm-lab/easyspecs-cliCheck:
easyspecs-cli version
easyspecs-cli helpRun without a global install:
npx @gluecharm-lab/easyspecs-cli@latest helpUninstall: npm uninstall -g @gluecharm-lab/easyspecs-cli
Quick start
From a git repo (OpenCode on PATH for agent steps). The CLI creates minimal .gluecharm/ directories when needed for analysis, update context, diagnose, upload, and download context:
cd /your/repo
easyspecs-cli doctor
easyspecs-cli config init
easyspecs-cli analysisconfig init creates .easyspecs/config.json with defaults (add --overwrite to replace an existing file). After the first analysis, typical follow-ups are update context (incremental sync with git) and context drift when you hold truth in specs files/folders elsewhere in the repo. Use download context (auth login + config set-project-id) to pull stored context from EasySpecs into .gluecharm/context. Run upload context or upload republish after analysis / update context when you want edits on EasySpecs. Use --ci in automation for non-interactive behaviour and stricter defaults (e.g. Factory outer-iteration cap).
Set easyspecs.defaultGitRemoteUrl with config set-git-remote <url> when you want that recorded in config. Tunables belong in <repo>/.easyspecs/config.json — do not rely on EASYSPECS_* environment variables for product settings.
Optional checks:
easyspecs-cli doctor --inspect-config
easyspecs-cli help
easyspecs-cli diagnose coordination-duplicates --cwd /your/repo --ci --jsonDownload context (pull from EasySpecs)
Use this when you want application context already stored for the EasySpecs project written into <repo>/.gluecharm/context (after a clone, environment switch, or to match cloud contents).
Prerequisites: same as upload context: a prior auth login, and easyspecs.easyspecsProjectId set (e.g. easyspecs-cli config set-project-id <uuid>). The CLI creates minimal .gluecharm/ layout when needed, including .gluecharm/context.
Behaviour: the CLI GETs the application, reads linked identifiers for persisted context fragments, batches a content GET, and writes one UTF‑8 file per row under <repo>/.gluecharm/context, using each row’s name as a safe relative path (path traversal is rejected). Rows named easyspecs-upload-target.json are skipped. When files are written, ids are merged into index-application-context.json the same way as after upload, if that index file exists.
cd /your/repo
easyspecs-cli auth login --email [email protected] --password '…'
easyspecs-cli config set-project-id <your-easyspecs-project-uuid>
easyspecs-cli download context| Flag | Meaning |
| ---- | ------- |
| (none) | Skip any local file that already exists (no silent overwrite). |
| --force | Overwrite existing files that map to cloud rows. |
| --replace-from-cloud | Delete local files under .gluecharm/context first, then write from the cloud. Root easyspecs-upload-target.json is preserved when present. Implies a full replace of downloaded content; pair with care. |
--json: prints one summary line on stdout with ok, downloaded, skipped, failed (count), and localRemoved (files deleted when --replace-from-cloud was used). With --verbose, if failed > 0**, the line may include a **failures** array. A non-zero exit is used when **failed > 0.
Examples:
easyspecs-cli download context
easyspecs-cli download context --force
easyspecs-cli download context --replace-from-cloud
easyspecs-cli --json download contextConfiguration
Primary configuration file: <repo>/.easyspecs/config.json (auto-created when needed, except for help, version, and doctor which do not create the file on first run). auth login --email … --password … stores tokens for upload and download context commands. You may append --session-path <file> at the end of the auth login argument list to update easyspecs.cliSessionPath in config.json for that login flow.
After auth login, session data is stored at easyspecs.cliSessionPath in config.json when that path is set (otherwise ~/.easyspecs/cli-session.json). Tools such as the VS Code extension may spawn easyspecs-cli with global --session-path <file> so this process uses a temporary session file without editing config.json.
Default config.json (from easyspecs-cli config init)
Run easyspecs-cli config init in your repo (add --overwrite to replace an existing file). A fresh install writes .easyspecs/config.json with a shape similar to:
{
"schemaVersion": 2,
"easyspecs": {
"deploymentEnvironment": "production",
"apiBaseUrl": "https://api.easyspecs.ai",
"easyspecsProjectId": "",
"defaultGitRemoteUrl": "",
"cliSessionPath": "",
"openCode": {
"executable": "opencode",
"skipCredentialsCheck": false
},
"analysis": {
"promoteContextToWorkspace": true
},
"orchestration": {},
"openCodeRuntime": {
"executable": "opencode",
"skipCredentialsCheck": false,
"credentialsPath": ".opencode/auth.json",
"providers": {},
"run": {
"argvTemplate": [
"run",
"--agent",
"{agentId}",
"Execute the task described in the attached EasySpecs prompt file.",
"-f",
"{promptFile}"
],
"timeoutMs": 600000
},
"coordinationRepairs": {
"listJsonSchemaRepairAttempts": 1,
"markdownEvidenceRepairAttempts": 2,
"markdownOpenQuestionIterations": 5
},
"pool": {
"maxConcurrentAgents": 30
},
"projectConfigOverlay": {}
},
"diagnose": {
"zeroReference": {
"maxPercentNonReferenced": null
},
"coordinationDuplicates": {
"strict": true
}
},
"upload": {
"contextDirectory": ""
},
"macro": {
"debug": false
},
"cli": {
"bundledResourcesRoot": ""
},
"auth": {
"ciLogin": {}
}
}
}Your file may gain additional keys over time (for example cloud-analysis cache fields under easyspecs.analysis); validate against the schema your package version documents.
Config keys (reference)
Merge order for easyspecs-cli: .easyspecs/config.json plus global CLI flags (for example --api-base-url, --environment). The CLI does not read EASYSPECS_* or VITE_* for product settings.
| Path | Purpose |
| ---- | ------- |
| schemaVersion | Integer (e.g. 2) for future migrations. |
| easyspecs.deploymentEnvironment | production or staging (default production). Used with built-in System Manager URLs when apiBaseUrl is empty. |
| easyspecs.apiBaseUrl | Explicit System Manager origin; wins after --api-base-url. Default non-empty value uses the production API host. Use "" if you want the built-in URL from deploymentEnvironment / --environment only. |
| easyspecs.easyspecsProjectId | EasySpecs project id (Content application UUID) for upload context, upload republish, and download context. Set with config set-project-id or the EasySpecs app; legacy applicationId in older files may be migrated into this field. |
| easyspecs.defaultGitRemoteUrl | Optional primary git remote URL (HTTPS or SSH) for reference; analysis still uses the live repo. |
| easyspecs.cliSessionPath | Repo-relative or absolute path to cli-session.json. Empty "" → ~/.easyspecs/cli-session.json. Precedence: global --session-path → cliSessionPath → home default. |
| easyspecs.openCode | executable, skipCredentialsCheck (shortcut; see openCodeRuntime). |
| easyspecs.analysis.* | promoteContextToWorkspace, ACE toggles, and other analysis-only flags. Cloud “already analyzed” cache is canonical under easyspecs.factory.cloudContextAnalyzed / At (legacy easyspecs.analysis.cloudContext* still read once with deprecation stderr). |
| easyspecs.factory.* | Factory debug, backoff, outer-iteration caps, ping-pong cap, cloud cache. Replaces easyspecs.orchestration.* and easyspecs.macro.debug (aliases still read). |
| easyspecs.workstations.* | OpenCode argv, timeouts, repair attempts, pool width, coordination lock timeout (canonical; legacy easyspecs.analysis.openCodeTest* / markdown* / listJsonSchemaRepairAttempts / maxConcurrentOpenCodeAgents aliases). |
| easyspecs.pipelines.upload.useBatch | Upload batching (legacy easyspecs.analysis.uploadUseBatch). |
| easyspecs.orchestration.* | Deprecated — prefer easyspecs.factory.*. |
| easyspecs.openCodeRuntime | Providers (apiKey, defaultModel per provider), run, coordinationRepairs, pool, projectConfigOverlay. |
| easyspecs.diagnose.zeroReference.maxPercentNonReferenced | Number or null. null means diagnose reference-coverage does not fail on percentage alone. |
| easyspecs.diagnose.coordinationDuplicates.strict | Boolean (default true). |
| easyspecs.upload.contextDirectory | Repo-relative or absolute override for upload republish; empty "" → automatic resolution from the analysis snapshot. |
| easyspecs.upload.fetchContextAnalyzedInCloud | Boolean, default true. When true, after a successful upload context / upload republish, the CLI may GET application status and emit contextAnalyzedInCloud / contextAnalyzedInCloudAt on --json, and update easyspecs.analysis.cloudContext* cache. When false, skips that fetch. |
| easyspecs.upload.contextAnalyzedStatusTimeoutMs | Optional positive integer (ms) for that GET; default 15000 if omitted. |
| easyspecs.factory.debug | When true, Factory phase transitions log extra lines to stderr. Legacy easyspecs.macro.debug. |
| easyspecs.cli.bundledResourcesRoot | Optional absolute or repo-relative path to the bundled resources/ directory. Empty → resolve next to the installed CLI package. |
| easyspecs.auth.ciLogin.email / password | Used with --ci when --email / --password are omitted on auth login. Prefer CI-generated config.json; avoid committing secrets. |
Resolved API base URL: --api-base-url → non-empty easyspecs.apiBaseUrl → built-in URL from --environment or easyspecs.deploymentEnvironment or production.
OpenCode providers: put API keys under easyspecs.openCodeRuntime.providers in config.json. They are passed to the opencode child process and are redacted in doctor --inspect-config output.
Global flags
These appear before the subcommand (everything after the first non-flag token is the command and its arguments).
| Flag | Effect |
| ---- | ------ |
| --cwd <dir> | Repository root for git resolution and paths (default: current working directory). |
| --ci | Non-interactive mode; affects merged settings (e.g. Factory outer-iteration default). EASYSPECS_CI is not read — use this flag. |
| --json | On supported exits, one JSON summary line on stdout. On non-zero exits, the line includes exitCode (number) and exitMeaning (one-line classification) alongside any command-specific fields. Factory / pipeline failures include factoryFailures (per failed phase), failurePhase, and validationExitId (legacy alias of failureExitId). See error-code.md — OS exit 5 is not used (SRS-70). |
| --verbose | Extra stderr logging where implemented. |
| --api-base-url <url> | System Manager API origin for this process (overrides easyspecs.apiBaseUrl). |
| --session-path <file> | Session JSON path for this process only (overrides easyspecs.cliSessionPath); does not rewrite config.json unless you use auth login tail --session-path as documented under Auth. |
| --environment production | staging | Alias --env. Overrides easyspecs.deploymentEnvironment for built-in URL selection when no explicit URL is set. |
| --promote | After run synthesis: force copying .gluecharm/context into the repo when easyspecs.analysis.promoteContextToWorkspace would disable it. analysis does not need this flag — promotion is automatic unless --no-promote. |
| --no-promote | After run synthesis or analysis: skip copying .gluecharm/context into the repo for this run. |
| --help, -h | Built-in help. Does not create .easyspecs/config.json. |
| --version, -V | CLI package version. Does not create config. |
| --config <path> | Parsed but unused in current releases; read <repo>/.easyspecs/config.json instead. |
Run logs (persistent stderr transcript)
Every command that resolves a repository root (all commands except help and version) writes a plain-text run log under <repo>/.easyspecs/logs/:
- Filename:
run-log-YYYY-MM-DDTHHmmssZ.txt(UTC start time). If that name already exists, the CLI appends-<pid>before.txt. - Contents: A short header (CLI version, repo root, redacted argv, log path), then every human stderr diagnostic line emitted during the run (tags such as
[pool],[AgentCode],[factory]), with no ANSI color codes. Each line is flushed to disk as it is printed. - Discoverability: The first stderr line after the header is
[log] run log: <absolute path>(also recorded in the file). - Stdout is not mirrored (including
--jsonsummary lines). With--json, stderr diagnostics are suppressed unless you also pass--verbose; the run log then contains only the header, the[log]line, and the footer. - Secrets: Passwords on argv are redacted in the header; session tokens are never written.
- Git: Add
.easyspecs/logs/to.gitignore— run logs are local forensics, not source artefacts.
Diagnose / context flags
Used on the tail of diagnose <subcommand> or context link-graph:
| Flag | Values | Meaning |
| ---- | ------ | ------- |
| --root | workspace or worktree | Resolve paths against the workspace repo root or an analysis checkout (required for reference-coverage, coordination-duplicates, coverage-report, missing-artefacts, context link-graph). |
| --worktree | path | Analysis git checkout (use with --root worktree, or alone where diagnose zero-reference documents it). |
Commands and flags (complete)
Every command the CLI accepts, with command-specific tokens. Global flags above apply to all of these unless noted.
| Command | Command-specific flags / tokens | Behaviour summary |
| ------- | ------------------------------- | ------------------ |
| help or first token --help | — | Prints usage (same as --help before the command). |
| version | — | Prints the CLI package version string. |
| doctor | --readiness, --inspect-config (optional; default if none → readiness-style check) | Does not create config.json. --readiness: cliVersion= (same as version), repo root, API URL, OpenCode, agents dir; --json adds cliVersion on the envelope. --inspect-config: redacted merged settings + config JSON. Both flags together runs both. |
| config init | --overwrite (optional) | Writes .easyspecs/config.json with defaults; may import legacy .easyspecs/cli.json once if present. Existing file unchanged unless --overwrite. |
| config set-project-id <easyspecsProjectId> | — | Sets easyspecs.easyspecsProjectId (creates config with defaults if missing). |
| config set-git-remote <url> | — | Sets easyspecs.defaultGitRemoteUrl (same bootstrap-if-missing behaviour). |
| config dump | — | Deprecated. Prints the same redacted payload as doctor --inspect-config and stderr warns to use doctor --inspect-config. |
| auth login | --email <email> --password <password>, optional tail --session-path <path> | Login to resolved API; writes session file. With --ci, credentials may come from easyspecs.auth.ciLogin in config.json if argv omits email/password. Tail --session-path updates easyspecs.cliSessionPath in config.json. Global --session-path overrides effective session file without editing config.json. |
| auth logout | — | Deletes the effective session file. |
| auth status | — | Reports whether a session file exists / looks populated. |
| run synthesis | — | Context artefact pipeline pass; requireOpenCode applies. --promote / --no-promote affect promotion after success. |
| run synthesis resume-missing | --worktree <path> (optional) | Parallel missing-artefact remediation pool on an existing checkout (from --worktree or last snapshot). requireOpenCode. |
| run synthesis resume-synthesis | --worktree <path> (optional) | Same implementation as resume-missing (alias command path). |
| analysis | --force-new-context-analysis (optional; tail only). --no-worktree / --noworktree (SRS-71). --skip-remediation-pipeline — skip zero-reference remediation; synthesis, coverage, execution report, link mapping, and index assembly still run. --zero-reference-convergence=strict|best_effort (ignored when remediation is skipped). --upload / --skip-upload → usage exit; --synthesis-only → ignored + stderr deprecation (SRS-60); cannot combine with --skip-remediation-pipeline. | Full Generate Context factory — create new context trajectory. CLI does not run backend upload inside analysis; use upload context / upload republish afterward. Promotion after success is automatic unless --no-promote or checkout is already <repoRoot> (independent of easyspecs.analysis.promoteContextToWorkspace). If easyspecs.factory.cloudContextAnalyzed is true and --force-new-context-analysis is absent, exits 0 early with analysisSkipped. Stale temp-worktree registration: git worktree prune then one retry of git worktree add. |
| update context | --no-worktree / --noworktree (SRS-71). | Update context trajectory: git window after baseline → analysis checkout → changes-since-date.md → optional remediation → --promote / --no-promote → easyspecs.factory.updateContext.lastRunAt. Run upload context separately for cloud sync. Seed context should exist on HEAD in the checkout (the factory may copy from workspace when the detached worktree lacks context; skipped when --no-worktree). |
| diagnose reference-coverage | --root workspace | --root worktree, optional --worktree <path> | Reference coverage gate; optional percent limit via easyspecs.diagnose.zeroReference.maxPercentNonReferenced. |
| diagnose coordination-duplicates | same | Duplicate/orphan reporting; easyspecs.diagnose.coordinationDuplicates.strict. |
| diagnose coverage-report | same | Writes coverage execution report path. |
| diagnose missing-artefacts | same | Lists missing artefacts from workspace state (JSON on stdout in human mode). |
| diagnose zero-reference | --worktree <path> (optional; no --root branch) | Zero-reference remediation pool (OpenCode). After a successful pool run, applies context markdown navigation links under the analysis checkout context directory (fails the command if that step reports broken links). |
| context link-graph | --root workspace | --root worktree, optional --worktree <path> | Refreshes EasySpecs navigation marker blocks in .gluecharm/context/**/*.md from coordination JSON only (no agents). --json may include contextDir, error, brokenLinks. |
| context drift <referencePath> | --label <slug>, --index <path>, --dry-run, --no-worktree / --noworktree (SRS-71; not with --root worktree / --worktree). Optional --root / --worktree for other routing. | Analyze drift against specs trajectory: referencePath = file or directory of reference docs. Uses analysis checkout + OpenCode drift agent → .gluecharm/context/drift/drift-<slug>-<date>.md and reference index maintenance; global --promote / --no-promote apply when not --dry-run. Success stdout: drift report path. |
| download context | --force, --replace-from-cloud (optional; any order after download context) | Requires auth login session + easyspecs.easyspecsProjectId. Resolves linked context documents from GET /api/content/application/:id, batch-fetches content, writes UTF-8 files under <repo>/.gluecharm/context. Default skips existing paths; --force overwrites; --replace-from-cloud wipes local context files first (preserves root easyspecs-upload-target.json). --json: downloaded, skipped, failed, localRemoved. Non-zero exit if failed > 0. Full narrative under **Download context (pull from EasySpecs)** above. |
| upload context | — | Requires auth session + **easyspecs.easyspecsProjectId**. Context dir: **/.gluecharm/context**. Optional cloud status GET per **easyspecs.upload.fetchContextAnalyzedInCloud**. |
| upload republish | — | Same auth + project id; context dir from **easyspecs.upload.contextDirectory** or analysis worktree snapshot. Same optional cloud status behaviour as **upload context**. |
| ace clear | — | Deletes learnings under **.gluecharm/context/learnings**. |
| ace learn | **--worktree ** (optional) | Offline ACE learn from traces; **requireOpenCode**. Worktree = **--worktree** if it contains **.opencode/schemas/ace**, else workspace root. |
| ace auto-learn | **--worktree ** (optional) | ACE auto-learn pool; **requireOpenCode**. Worktree = **--worktree** if valid git dir, else workspace root. Concurrency capped by **merged.pipelineOpenCode.maxConcurrentAgents** (schema field name; mirrors **easyspecs.workstations.maxConcurrentAi** / legacy **maxConcurrentOpenCodeAgents`). |
OpenCode: commands that spawn agents require opencode (or configured executable) on PATH and credentials unless skipCredentialsCheck or provider keys in config.json apply (requireOpenCode).
EasySpecs API session: download context, upload context, and upload republish need a prior auth login (or --ci with easyspecs.auth.ciLogin in config.json when argv omits email/password on login), plus easyspecs.easyspecsProjectId for those commands.
Unknown command: prints help and exits with a usage error.
Product information
EasySpecs is a product for agentic documentation and requirements. The public site and app live at easyspecs.ai (sign-in, projects, browser workspace). This npm package is the headless CLI only; it does not bundle the web app.
License
Copyright © Spaii Edutainment SL (Barcelona, Spain).
This package is licensed under the Elastic License 2.0 (ELv2, SPDX Elastic-2.0). The complete license text ships in the LICENSE file inside the published @gluecharm-lab/easyspecs-cli package on npm.
