feat(onboard): recommendations store with once-semantics and self-naming bootstrap (#110173)

* feat(onboarding): persist app recommendations once

* feat(onboarding): add self-naming birth sequence

* chore(wizard): hoist bootstrap-defer imports to the top of the module

* docs(bootstrap): persist agreed identity into IDENTITY.md and SOUL.md as well

* docs: regenerate docs map after onboarding plan merge

* fix(wizard): reuse a pending stored offer instead of rescanning apps

* fix(onboard): deduplicate bootstrap recommendations
This commit is contained in:
Peter Steinberger
2026-07-17 23:42:01 +01:00
committed by GitHub
parent ed2284c31a
commit 9a93a52a8a
19 changed files with 987 additions and 77 deletions

View File

@@ -20,7 +20,7 @@ const coreCliCommandCatalog = defineCommandDescriptorCatalog([
{
name: "onboard",
description: "Guided setup for auth, models, Gateway, workspace, channels, and skills",
hasSubcommands: false,
hasSubcommands: true,
},
{
name: "configure",

View File

@@ -4,6 +4,8 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
import { registerOnboardCommand } from "./register.onboard.js";
const mocks = vi.hoisted(() => ({
acknowledgeOnboardRecommendationsCommand: vi.fn(),
onboardRecommendationsCommand: vi.fn(),
runSystemAgentWithInference: vi.fn(),
setupWizardCommandMock: vi.fn(),
runtime: {
@@ -49,6 +51,11 @@ vi.mock("../../commands/onboard.js", () => ({
setupWizardCommand: mocks.setupWizardCommandMock,
}));
vi.mock("../../commands/onboard-recommendations.js", () => ({
acknowledgeOnboardRecommendationsCommand: mocks.acknowledgeOnboardRecommendationsCommand,
onboardRecommendationsCommand: mocks.onboardRecommendationsCommand,
}));
vi.mock("../../commands/system-agent-with-inference.js", () => ({
runSystemAgentWithInference: mocks.runSystemAgentWithInference,
}));
@@ -79,6 +86,20 @@ describe("registerOnboardCommand", () => {
setupWizardCommandMock.mockResolvedValue(undefined);
});
it("routes the read-only recommendations subcommand", async () => {
await runCli(["onboard", "recommendations", "--json"]);
expect(mocks.onboardRecommendationsCommand).toHaveBeenCalledWith({ json: true }, runtime);
expect(setupWizardCommandMock).not.toHaveBeenCalled();
});
it("routes the recommendations acknowledgement subcommand", async () => {
await runCli(["onboard", "recommendations", "acknowledge"]);
expect(mocks.acknowledgeOnboardRecommendationsCommand).toHaveBeenCalledWith(runtime);
expect(setupWizardCommandMock).not.toHaveBeenCalled();
});
it("defaults installDaemon to undefined when no daemon flags are provided", async () => {
await runCli(["onboard"]);

View File

@@ -236,6 +236,36 @@ export function registerOnboardCommand(program: Command): void {
.option("--import-secrets", "Import supported secrets during onboarding migration", false)
.option("--json", "Output JSON summary", false);
const recommendations = command
.command("recommendations")
.description("Read the app recommendations stored during onboarding")
.option("--json", "Output stored recommendation matches as JSON", false)
.action(async (opts, recommendationsCommand: Command) => {
const { defaultRuntime } = await import("../../runtime.js");
await runCommandWithRuntime(defaultRuntime, async () => {
const { onboardRecommendationsCommand } =
await import("../../commands/onboard-recommendations.js");
onboardRecommendationsCommand(
{
json: Boolean(opts.json || recommendationsCommand.parent?.opts().json),
},
defaultRuntime,
);
});
});
recommendations
.command("acknowledge")
.description("Mark the stored onboarding recommendation offer as answered")
.action(async () => {
const { defaultRuntime } = await import("../../runtime.js");
await runCommandWithRuntime(defaultRuntime, async () => {
const { acknowledgeOnboardRecommendationsCommand } =
await import("../../commands/onboard-recommendations.js");
acknowledgeOnboardRecommendationsCommand(defaultRuntime);
});
});
command.action(async (opts, commandRuntime: Command) => {
const { defaultRuntime } = await import("../../runtime.js");
await runCommandWithRuntime(defaultRuntime, async () => {