/** * Core init logic - project initialization and maintenance. * * Single source of truth for all init operations. CLI delegates here * (shared-core pattern). * * Handles: * 1. .cleo/ directory structure creation * 2. Core data files (config.json, tasks.db) * 3. Schema file installation (~/.cleo/schemas/) * 4. Sequence counter (SQLite schema_meta) * 5. Project info (.cleo/project-info.json) * 6. CAAMP injection into agent instruction files (AGENTS.md hub pattern) * 7. Agent definition installation (cleo-subagent) * 9. Core skill installation via CAAMP * 10. NEXUS project registration * 11. Project type detection (--detect) * 12. Injection refresh * 13. Git hook installation (commit-msg, pre-commit) * 14. GitHub issue/PR templates (.github/ directory) * * @task T4681 * @task T4682 * @task T4684 * @task T4685 * @task T4686 * @task T4687 * @task T4689 * @task T4706 * @task T4707 * @epic T4663 */ import type { GitHookInstallOptions } from '@cleocode/contracts/git-hooks.js'; /** Options for the init operation. */ export interface InitOptions extends Pick { /** Project name override. */ name?: string; /** Overwrite existing files. */ force?: boolean; /** Auto-detect project configuration. */ detect?: boolean; /** Run codebase analysis and store findings to brain.db. */ mapCodebase?: boolean; /** * @deprecated No-op since T1934 / ADR-068. All five worker templates are * now automatically registered at project tier on every plain `cleo init` * call via {@link installTemplatesAtProjectTier}. Passing this flag emits * a deprecation warning and is otherwise ignored. The flag is preserved for * one minor release to avoid breaking existing scripts. */ installSeedAgents?: boolean; /** * Mint a new project identity instead of re-linking one the global * registry already holds for this checkout (T12325). Has no effect when * `project-info.json` or the tracked `.cleo/project-id` already declares * an identity — neither is ever rewritten. */ newIdentity?: boolean; /** * Point the registry row at this checkout even though its previous location * still exists on this device (T12470). Without it such a checkout is only a * `candidate`. */ forceRebind?: boolean; /** * Initialize the current working directory itself, even when it sits inside * an ancestor CLEO project (T12562). A directory that is its own git root is * targeted without this flag. * * T12558: at a root a project was rerooted AWAY from, `here` alone is * refused (it would adopt the relocated project's id and create a second * store for it); `here` + {@link InitOptions.newIdentity} starts a genuinely * different project there, audited in `.cleo/audit/relocation-override.jsonl`. */ here?: boolean; } /** * How {@link resolveInitTarget} chose the directory `cleo init` acts on. * * - `here` — `--here` asked for the current directory. * - `pinned` — a worktree scope, absolute `CLEO_DIR` or `CLEO_ROOT` pinned it. * - `git-root` — the current directory is its own git repository root (`.git` * is a directory). A submodule / separate-git-dir gitlink is NOT: the * resolver cannot use a store there yet (T12562). * - `resolved` — the ancestor walk; the current directory or an ancestor project. * * @task T12562 */ export type InitTargetSource = 'here' | 'pinned' | 'git-root' | 'resolved'; /** * The nearest git checkout boundary at or above the working directory. * * - `repo` — `.git` is a directory. * - `submodule` — `.git` is a gitlink to a separate repository (a submodule * or `--separate-git-dir`). It is its own repository, but project-root * resolution still walks past it, so init must not give it a store (T12562). * - `worktree` — `.git` is a gitlink into `/worktrees/`: a * linked worktree that shares its main checkout's CLEO project (D009). * * @task T12562 */ export interface InitGitBoundary { /** Absolute directory that holds the `.git` entry. */ root: string; /** Kind of checkout. */ kind: 'repo' | 'submodule' | 'worktree'; /** For `worktree`: the main checkout the worktree belongs to. */ mainRoot?: string; } /** * The directory `cleo init` will act on, and whether it is the current directory. * * @task T12562 */ export interface InitTarget { /** Absolute path of the target's `.cleo/` directory. */ cleoDir: string; /** Absolute path of the target project root (parent of `cleoDir`). */ projectRoot: string; /** Absolute path of the working directory init was invoked from. */ cwd: string; /** Rule that selected the target. */ source: InitTargetSource; /** `true` when `projectRoot` is the working directory (compared by realpath). */ isCwd: boolean; /** `true` when an absolute `CLEO_DIR` or `CLEO_ROOT` explicitly pinned the target. */ envPinned: boolean; /** Nearest git checkout boundary at or above `cwd`, or `null` outside git. */ gitBoundary: InitGitBoundary | null; /** * For a `submodule` boundary: the initialized CLEO project that commands run * from it resolve to (the superproject), when there is one. */ enclosingProjectRoot?: string; } /** * Stable machine-readable codes for `cleo init` refusals (T12562). Carried in * `CleoError.details.codeName` so the CLI and the dispatch engine can surface * them instead of a generic `E_INTERNAL` / "use force=true". * * @task T12562 */ export declare const INIT_ERROR_CODES: { /** cwd is the initialized project root; `--force` re-initializes it. */ readonly alreadyInitialized: "E_ALREADY_INITIALIZED"; /** cwd is inside an ancestor project; init changed nothing. */ readonly ancestorProject: "E_INIT_ANCESTOR_PROJECT"; /** `--force` resolved to a root other than cwd. */ readonly forceNotCwd: "E_INIT_FORCE_NOT_CWD"; /** cwd is inside a linked git worktree; its project is the main checkout. */ readonly inWorktree: "E_INIT_IN_WORKTREE"; /** cwd is a submodule / separate-git-dir checkout under a CLEO project. */ readonly gitlinkUnsupported: "E_INIT_GITLINK_UNSUPPORTED"; /** The pre-`--force` snapshot could not be taken; nothing was changed. */ readonly snapshotFailed: "E_INIT_SNAPSHOT_FAILED"; }; /** Result of the init operation. */ export interface InitResult { initialized: boolean; directory: string; created: string[]; skipped: string[]; warnings: string[]; updateDocsOnly?: boolean; /** * Phase 5 — Greenfield/brownfield classification of the directory. * Populated by the discovery module during init. */ classification?: { kind: 'greenfield' | 'brownfield'; signalCount: number; topLevelFileCount: number; hasGit: boolean; }; /** * Phase 5 — Next-step guidance for the agent/operator, emitted as a * LAFS-compatible suggestion list. Each entry has an action description * and a copy-pasteable command. */ nextSteps?: Array<{ action: string; command: string; }>; /** * T1263 PSYCHE E6 — Recent session journal context for meta-agent consumption. * * Contains up to 5 most recent journal entries so the meta-agent (agent-architect) * can pick up prior session context at `cleo init` time without loading brain.db. * Absent when no journals exist or when `readRecentJournals` fails (best-effort). */ sessionContext?: { /** Most recent session journal entries, newest first. */ recentJournals: import('@cleocode/contracts').SessionJournalEntry[]; }; } /** * Resolve the absolute path to the bundled `seed-agents/` directory inside * the `@cleocode/agents` package. * * Mirrors the multi-candidate resolution pattern used by * {@link initAgentDefinition} so the same code path works across all layouts: * 1. **npm install** — `require.resolve('@cleocode/agents/package.json')` * finds the package under `node_modules/@cleocode/agents/`. * 2. **Workspace dev (bundled CLI)** — walks up from `getPackageRoot()` * (which resolves to `packages/cleo/dist/` or `packages/core/`) to find * `packages/agents/seed-agents/`. * 3. **Monorepo dev (source)** — falls back to `packages/agents/seed-agents/` * relative to `getPackageRoot()`. * * @returns Absolute path to an existing `seed-agents/` directory, or `null` * if no candidate exists. Returning `null` lets callers skip the * seed install gracefully without crashing. * * @task T283 * @epic T280 */ export declare function resolveSeedAgentsDir(): Promise; /** * Per-agent outcome of {@link installTemplatesAtProjectTier}. * * @task T1934 */ export interface TemplateInstallEntry { /** Business identifier of the agent (kebab-case, filename sans `.cant`). */ readonly agentId: string; /** Absolute source path of the `.cant` template file. */ readonly cantPath: string; /** Whether the DB row was inserted (`true`) or updated (`false`). */ readonly inserted: boolean; } /** * Result of {@link installTemplatesAtProjectTier}. * * @task T1934 */ export interface TemplateInstallResult { /** Agents that were successfully (re)registered. */ readonly installed: ReadonlyArray; /** Agents that failed to install — paired with the error message. */ readonly failed: ReadonlyArray<{ readonly cantPath: string; readonly error: string; }>; /** * Absolute path to the templates directory that was scanned. * `null` when the templates directory could not be resolved. */ readonly templatesDir: string | null; } /** * Walk `@cleocode/agents/templates/` and register every `project-*.cant` file * into the global `signaldock.db` registry with `tier = 'project'`. * * This is the load-bearing UX change introduced by T1934 (ADR-068): plain * `cleo init` (no flags) now produces a fully working agent dispatch system. * Each template is installed via the atomic {@link installAgentFromCant} * pipeline — the `.cant` file is copied to `.cleo/cant/agents/` AND the * `agents` row is written to `signaldock.db` in a single transaction. * * Idempotent: re-running init on an already-initialised project does not * duplicate rows — `force: true` rewrites stale rows instead of throwing. * * Tolerant of per-file failures: a malformed template blocks only its own * row and is reported in `failed`, leaving sibling installs intact. * * @param projectRoot - Absolute path to the project root. * @returns Per-file install outcomes and the directory that was scanned. * * @task T1934 */ export declare function installTemplatesAtProjectTier(projectRoot: string): Promise; /** * Install cleo-subagent agent definition to ~/.agents/agents/. * @task T4685 */ export declare function initAgentDefinition(created: string[], warnings: string[]): Promise; /** * No-op. Kept for API compatibility. * @task T4706 */ export declare function initMcpServer(_projectRoot: string, _created: string[], _warnings: string[]): Promise; /** * Install CLEO core skills to the canonical skills directory via CAAMP. * @task T4707 * @task T4689 */ export declare function initCoreSkills(created: string[], warnings: string[]): Promise; /** Options for {@link initNexusRegistration} (T12470). */ export interface InitNexusRegistrationOptions { /** * Maintenance caller (`cleo upgrade` / `self-update`): record an encounter * only — never mint an identity and never repoint an existing row. */ maintenance?: boolean; /** Explicit rebind even though the previous location still exists. */ forceRebind?: boolean; } /** * Register/reconcile project with NEXUS. * Uses nexusReconcile for idempotent handshake — auto-registers if new, * updates path if moved, confirms identity if unchanged. * @task T4684 * @task T5368 */ export declare function initNexusRegistration(projectRoot: string, created: string[], warnings: string[], opts?: InitNexusRegistrationOptions): Promise; /** * Install GitHub issue and PR templates to .github/ if a git repo exists * but .github/ISSUE_TEMPLATE/ is not yet present. * * Idempotent: skips files that already exist. Never overwrites existing * templates — the project owner's customisations take precedence. * * @param projectRoot Absolute path to the project root. * @param created Array to push "created: ..." log entries into. * @param skipped Array to push "skipped: ..." log entries into. */ export declare function installGitHubTemplates(projectRoot: string, created: string[], skipped: string[]): Promise; /** * Install redirect stubs over deprecated markdown handoff files. * * Replaces any `NEXT-SESSION-HANDOFF.md` or `HONEST-HANDOFF-*.md` files in * `.cleo/agent-outputs/` that still contain narrative state (i.e. are more * than the redirect stub itself). Files that are already stubs are left alone. * * This prevents fresh agents from reading stale markdown as canonical state * instead of running `cleo briefing`. * * Idempotent: calling multiple times is safe. * * @param projectRoot Absolute path to the project root. * @param created Array to push "replaced: ..." log entries into. * * @task T1610 */ export declare function installHandoffRedirectStubs(projectRoot: string, created: string[]): Promise; /** * Choose the directory `cleo init` acts on (T12562). * * Every other command walks up from a subdirectory to the enclosing project, * and keeps doing so. `init` is different because it CREATES a project, so the * walk must not silently redirect it: from a child git repository nested under * an initialized CLEO root the walk found the parent, and the guard then * advised `--force` against the parent's store. * * Resolution order: * 1. `--here` targets the working directory ({@link initProject} still refuses * it inside a linked worktree). * 2. An active worktree scope, an absolute `CLEO_DIR` or `CLEO_ROOT` / * `CLEO_PROJECT_ROOT` pins the target explicitly (unchanged behaviour). * 3. A working directory that is its own git root (`.git` is a directory) * targets itself: a nested repository is a separate project. A gitlink * checkout (linked worktree, submodule, separate-git-dir) does not, because * project-root resolution cannot use a store there. * 4. Otherwise the existing resolution applies. A plain (non-git) * subdirectory of a CLEO project therefore still resolves to that ancestor; * {@link initProject} reports it by absolute path and never wipes it. * * @param opts - `here` forces the working directory; `cwd` overrides `process.cwd()`. * @returns The chosen target. * * @example * ```ts * resolveInitTarget(); // { projectRoot: '/repo/child', source: 'git-root', isCwd: true, ... } * ``` * * @task T12562 */ export declare function resolveInitTarget(opts?: { here?: boolean; cwd?: string; }): InitTarget; /** * Return the stable init refusal code carried by `err`, if any (T12562). * * @param err - Error thrown by core `initProject`. * @returns One of {@link INIT_ERROR_CODES}, or `undefined`. * * @example * ```ts * initErrorCodeName(err); // 'E_INIT_ANCESTOR_PROJECT' * ``` * * @task T12562 */ export declare function initErrorCodeName(err: unknown): string | undefined; /** * Run update-docs only: refresh all injections without reinitializing. * Re-injects CLEO-INJECTION.md into all detected agent instruction files. * * @task T4686 */ export declare function updateDocs(): Promise; /** * Run full project initialization. * * Creates the .cleo/ directory structure, installs schemas, templates, * agent definitions, skills, and registers with NEXUS. * * @task T4681 * @task T4682 * @task T4684 * @task T4685 * @task T4686 * @task T4687 * @task T4689 * @task T4706 * @task T4707 */ export declare function initProject(opts?: InitOptions): Promise; /** * Check if auto-init is enabled via environment variable. * @task T4789 */ export declare function isAutoInitEnabled(): boolean; /** * Check if a project is initialized and auto-init if configured. * Returns { initialized: true } if ready, throws otherwise. * @task T4789 */ export declare function ensureInitialized(projectRoot?: string): Promise<{ initialized: boolean; }>; /** * Get the current CLEO/project version. * Checks VERSION file, then package.json. * @task T4789 */ export declare function getVersion(projectRoot?: string): Promise<{ version: string; }>; /** * Deploy the starter CANT bundle (team + agents) to a project's `.cleo/cant/`. * * Idempotent: skips deployment if `.cleo/cant/` already contains `.cant` files. * Does not overwrite existing files. Resolves the starter bundle via the * {@link resolveStarterBundle} SDK helper — per D035 (v2026.4.111) the bundle * lives in `@cleocode/agents/starter-bundle/` rather than * `@cleocode/cleo-os/starter-bundle/`. * * Called by both `initProject()` and `runUpgrade()` to ensure every project * gets a working team topology for the CANT bridge. * * @param cleoDir - Absolute path to the project's `.cleo/` directory. * @param created - Array to push created-file descriptions into. * @param warnings - Array to push warning messages into. */ export declare function deployStarterBundle(cleoDir: string, created: string[], warnings: string[]): Promise; //# sourceMappingURL=init.d.ts.map