CLI Onboarding Agent Targets Contract
Date: 2026-06-26
Product Decision#
almanac setup should start like OpenAlmanac setup: a beautiful terminal UI with
a vertical checklist for where Almanac should be installed.
The default path is:
- Install agent instructions for selected global agent targets.
- Install CLI auto-update by default.
- Ask whether the user wants to handle automations themselves, default no.
- If no, skip all local lifecycle machinery and point to the hosted dashboard.
Final Onboarding Flow#
ALMANAC
a living wiki for codebases, for your agent
Where do you want to install Almanac?
[x] Claude Code
[x] Codex
[x] Cursor
[x] Windsurf
[x] OpenCode
[space] toggle [up/down] move [a] all [enter] confirm [q] quitThen:
Keep the Almanac CLI updated automatically? [Y/n]Then:
Do you want to handle automations yourself? [y/N]Default Branch#
If the user answers no to self-managed automations:
Agent instructions installed
CLI auto-update installed
Hosted updates -> codealmanac.com/dashboard
Setup completeThis branch must not:
- check Claude/Codex lifecycle readiness
- install sync automation
- install Garden automation
- ask auto-commit
Self-Managed Automation Branch#
If the user answers yes to self-managed automations:
- Choose lifecycle agent.
- Recommend Codex.
- Choose model.
- Recommend
gpt-5.5for Codex. - Install local automations.
- Ask auto-commit, default no.
The local automation install is equivalent to:
almanac automation installThat installs both scheduled jobs:
sync -> almanac sync --quiet 45m every 5h
garden -> almanac garden every 4hThere should not be separate setup prompts for sync and Garden.
Instruction Targets#
TypeScript:
export type InstructionTargetId =
| "claude"
| "codex"
| "cursor"
| "windsurf"
| "opencode";
export const DEFAULT_INSTRUCTION_TARGETS: readonly InstructionTargetId[] = [
"claude",
"codex",
"cursor",
"windsurf",
"opencode",
];Python reading:
instruction_targets = [
"claude",
"codex",
"cursor",
"windsurf",
"opencode",
]
default_instruction_targets = [
"claude",
"codex",
"cursor",
"windsurf",
"opencode",
]Target install contract:
TypeScript:
const INSTRUCTION_TARGETS = {
claude: {
label: "Claude Code",
kind: "import",
guidePath: "~/.claude/almanac.md",
importPath: "~/.claude/CLAUDE.md",
},
codex: {
label: "Codex",
kind: "inline",
path: "~/.codex/AGENTS.md",
},
cursor: {
label: "Cursor",
kind: "rule",
path: "~/.cursor/rules/almanac/RULE.md",
},
windsurf: {
label: "Windsurf",
kind: "inline",
path: "~/.codeium/windsurf/memories/global_rules.md",
},
opencode: {
label: "OpenCode",
kind: "inline",
path: "~/.config/opencode/AGENTS.md",
},
} as const;Python reading:
instruction_targets = {
"claude": {
"label": "Claude Code",
"install": "write ~/.claude/almanac.md and import it from ~/.claude/CLAUDE.md",
},
"codex": {
"label": "Codex",
"install": "write managed inline block to ~/.codex/AGENTS.md",
},
"cursor": {
"label": "Cursor",
"install": "write ~/.cursor/rules/almanac/RULE.md",
},
"windsurf": {
"label": "Windsurf",
"install": "write managed inline block to ~/.codeium/windsurf/memories/global_rules.md",
},
"opencode": {
"label": "OpenCode",
"install": "write managed inline block to ~/.config/opencode/AGENTS.md",
},
}Cursor Verification#
Cursor global rules were verified on the user's machine by creating:
~/.cursor/rules/almanac/RULE.mdwith:
---
alwaysApply: true
description: "Almanac global rule test"
---
When asked "almanac cursor global rule test", reply exactly:
loaded from global cursor ruleCursor Agent loaded the rule and replied:
loaded from global cursor ruleSo Cursor is a valid global setup target.
Setup Plan Contract#
TypeScript:
export interface SetupPlan {
instructionTargets: InstructionTargetId[];
cliAutoUpdate: boolean;
selfManagedAutomation: boolean;
autoCommit: boolean;
}Python reading:
setup_plan = {
"instruction_targets": selected_instruction_targets,
"cli_auto_update": user_answer_or_default_yes,
"self_managed_automation": user_answer_or_default_no,
"auto_commit": user_answer_or_default_no if self_managed_automation else explicit_auto_commit_only,
}Rules:
TypeScript:
async function buildSetupPlan(args: SetupPlanOptions): Promise<SetupPlan> {
const instructionTargets = await resolveInstructionTargets(args);
const cliAutoUpdate = await resolveCliAutoUpdate(args);
const selfManagedAutomation = await resolveSelfManagedAutomation(args);
const autoCommit = selfManagedAutomation
? await resolveAutoCommit(args)
: resolveExplicitAutoCommit(args.options);
return {
instructionTargets,
cliAutoUpdate,
selfManagedAutomation,
autoCommit,
};
}Python reading:
def build_setup_plan(args):
instruction_targets = resolve_instruction_targets(args)
cli_auto_update = resolve_cli_auto_update(args)
self_managed_automation = resolve_self_managed_automation(args)
if self_managed_automation:
auto_commit = resolve_auto_commit(args)
else:
auto_commit = resolve_explicit_auto_commit(args.options)
return {
"instruction_targets": instruction_targets,
"cli_auto_update": cli_auto_update,
"self_managed_automation": self_managed_automation,
"auto_commit": auto_commit,
}Setup Orchestration#
Setup chooses the lifecycle agent only inside the self-managed automation branch.
TypeScript:
const plan = await buildSetupPlan({ out, interactive, options });
await runGuidesSetupStep({
out,
options,
targets: plan.instructionTargets,
});
const globalInstall = await runGlobalInstallStep({ out, interactive, options });
if (plan.cliAutoUpdate) {
await runAutoUpdateSetupStep(...);
}
if (plan.selfManagedAutomation) {
const agentChoice = await chooseDefaultAgent({
out,
interactive,
requested: options.agent,
requestedModel: options.model,
spawnCli: options.spawnCli,
});
await runAutomationSetupStep(...);
}
if (plan.selfManagedAutomation || plan.autoCommit || options.autoCommit === false) {
await runAutoCommitSetupStep(...);
}Python reading:
plan = build_setup_plan(out, interactive, options)
install_agent_instructions(plan["instruction_targets"])
global_install = install_globally_if_needed()
if plan["cli_auto_update"]:
install_cli_auto_update()
if plan["self_managed_automation"]:
agent = choose_lifecycle_agent() # Codex recommended
model = choose_model(agent) # gpt-5.5 recommended for Codex
install_local_automations() # sync + garden together
configure_auto_commit(plan["auto_commit"])
else:
skip_lifecycle_agent_check()
skip_sync_cron()
skip_garden_cron()
skip_auto_commit_question()
if options.auto_commit is not None and not plan["self_managed_automation"]:
configure_auto_commit(plan["auto_commit"])File Changes#
src/cli/commands/setup/setup-plan.ts#
Add:
instructionTargetsselfManagedAutomationresolveInstructionTargetsresolveSelfManagedAutomation
Change:
autoCommitis only asked whenselfManagedAutomationis true.- explicit
--auto-commit/--no-auto-commitstill apply for compatibility. - legacy explicit sync/Garden flags still force
selfManagedAutomation = true.
TypeScript:
function hasExplicitLocalAutomationOptions(options: SetupOptions): boolean {
return options.automationEvery !== undefined ||
options.automationQuiet !== undefined ||
options.gardenEvery !== undefined ||
options.gardenOff === true;
}Python reading:
def has_explicit_local_automation_options(options):
return (
options.automation_every is not None
or options.automation_quiet is not None
or options.garden_every is not None
or options.garden_off is True
)src/cli/commands/setup/instruction-target-choice.ts#
New file.
Responsibilities:
- render OpenAlmanac-style vertical checklist
- default-select every supported global target
- return selected
InstructionTargetId[] - support non-interactive defaults
src/agent/install-targets.ts#
Expand current Claude/Codex-only install to target-selected install.
Move Claude-specific file writing out of this file. install-targets.ts must
not contain product-specific install mechanics after this change; it should be
the registry/dispatcher for target agents.
TypeScript:
export async function installAgentInstructions(args: {
targets: readonly InstructionTargetId[];
claudeDir: string;
codexDir: string;
cursorDir: string;
windsurfDir: string;
opencodeDir: string;
guidesDir: string;
}): Promise<AgentInstructionsChange> {
for (const target of args.targets) {
await installInstructionTarget(target, args);
}
}Python reading:
def install_agent_instructions(args):
for target in args["targets"]:
install_instruction_target(target, args)src/agent/instructions/cursor.ts#
New file.
Write:
~/.cursor/rules/almanac/RULE.mdThe file should be fully owned by Almanac, because it is target-specific.
Cursor rule body:
---
alwaysApply: true
description: "Almanac instructions for Cursor"
---
<mini guide contents>src/agent/instructions/claude.ts#
New file.
Move current Claude logic from install-targets.ts into this file.
Responsibilities:
- copy
guides/mini.mdto~/.claude/almanac.md - copy
guides/reference.mdto~/.claude/almanac-reference.md - add
@~/.claude/almanac.mdto~/.claude/CLAUDE.md - remove those files/imports during uninstall
- check whether Claude instructions are present for doctor
install-targets.ts should call this module; it should not implement these
file operations directly.
src/agent/instructions/windsurf.ts#
New file.
Write/update managed block in:
~/.codeium/windsurf/memories/global_rules.mdUse markers:
<!-- almanac:start -->
...
<!-- almanac:end -->src/agent/instructions/opencode.ts#
New file.
Write/update managed block in:
~/.config/opencode/AGENTS.mdUse markers:
<!-- almanac:start -->
...
<!-- almanac:end -->src/cli/commands/setup/index.ts#
Change order:
- print banner
- build setup plan, including target picker
- install selected agent instructions
- global install step if needed
- CLI auto-update step
- if self-managed automation:
- choose lifecycle agent/model
- install sync + Garden automation
- ask/configure auto-commit
- print next steps
src/agent/readiness/view.ts#
Ensure lifecycle recommendation prefers Codex when possible.
The current code already prefers Codex if ready:
if (ready.includes("codex")) return "codex";Keep that behavior. Tests should cover it.
src/agent/provider-id.ts#
Ensure Codex default model remains:
defaultModel: "gpt-5.5"Next Steps Copy#
Default hosted branch:
Next steps
1. Create or update your wiki from the dashboard:
https://codealmanac.com/dashboard
2. Query locally once .almanac/ exists:
almanac search "auth"
almanac search --mentions <file>Existing wiki branch:
Next steps
1. Start querying it locally:
almanac search --mentions <file>
almanac search "auth"
2. Read one of the result pages:
almanac show <result-slug>Self-managed branch:
Next steps
1. Local automations are running:
almanac sync --quiet 45m
almanac garden
2. Query locally:
almanac search "auth"
almanac search --mentions <file>Tests#
Update or add:
test/setup-plan.test.tstest/setup.test.tstest/agents-command.test.tsif install-target checks live theretest/provider-view.test.tsfor Codex recommendation
Required assertions:
- default setup installs Claude, Codex, Cursor, Windsurf, and OpenCode instructions
- default setup does not check lifecycle readiness
- default setup does not install sync plist
- default setup does not install Garden plist
- default setup does not ask auto-commit
- selecting Cursor writes
~/.cursor/rules/almanac/RULE.md - selecting Windsurf writes managed block to
~/.codeium/windsurf/memories/global_rules.md - selecting OpenCode writes managed block to
~/.config/opencode/AGENTS.md - self-managed yes installs both sync and Garden jobs
- self-managed yes asks/configures auto-commit with default no
- explicit legacy sync/Garden flags still enter self-managed automation path
Out of Scope#
- No new CLI commands.
- No project-local Cursor/Windsurf install.
- No hosted dashboard implementation.
- No separate sync/Garden prompts.
- No readiness checks on the default hosted branch.