Files
openclaw/extensions/migrate-claude/plan.ts
Peter Steinberger 4319ddbe8c feat(control-ui): import Codex and Claude Code memory (#106406)
* feat(control-ui): import coding assistant memory

* test(migrate): clean memory import temp dirs

* chore: move memory import note to PR body

* build: refresh memory import generated artifacts

* fix(control-ui): complete memory import checks

* build(control-ui): localize memory recovery labels

* fix(control-ui): harden memory import recovery

* refactor(migrate): keep memory import surfaces bounded

* fix(control-ui): use canonical agent row type

* fix(codex): drop dead migration type exports

* fix(migrate): restrict imported memory permissions

* fix(control-ui): preserve memory import recovery state

* fix(control-ui): retain memory import results

* build(control-ui): preserve translation memory history

* fix(control-ui): bind memory import state to agent

* fix(control-ui): unlock memory import after refresh

* test(gateway): preserve memory method suffix

* fix(migrate): bind reviewed memory imports

* fix(migrate): make memory imports retry-safe

* perf(ui): keep memory import lazy

* build: refresh generated artifacts after rebase

* chore: keep migration runtime within LOC guard

* build: refresh Swift protocol model
2026-07-13 15:30:06 -07:00

116 lines
3.7 KiB
TypeScript

// Migrate Claude plugin module implements plan behavior.
import { createMigrationItem, summarizeMigrationItems } from "openclaw/plugin-sdk/migration";
import type {
MigrationItem,
MigrationPlan,
MigrationProviderContext,
} from "openclaw/plugin-sdk/plugin-entry";
import { buildConfigItems } from "./config.js";
import { buildMemoryItems } from "./memory.js";
import { buildSkillItems } from "./skills.js";
import { discoverClaudeSource, hasClaudeSource } from "./source.js";
import { resolveTargets } from "./targets.js";
function addArchiveItem(
items: MigrationItem[],
params: { id: string; source?: string; relativePath: string; message?: string },
): void {
if (!params.source) {
return;
}
items.push(
createMigrationItem({
id: params.id,
kind: "archive",
action: "archive",
source: params.source,
message:
params.message ??
"Archived in the migration report for manual review; not imported into live config.",
details: { archiveRelativePath: params.relativePath },
}),
);
}
export async function buildClaudePlan(ctx: MigrationProviderContext): Promise<MigrationPlan> {
const source = await discoverClaudeSource(ctx.source);
if (!hasClaudeSource(source)) {
throw new Error(
`Claude state was not found at ${source.root}. Pass --from <path> if it lives elsewhere.`,
);
}
const targets = resolveTargets(ctx);
const items: MigrationItem[] = [];
const memoryOnly =
ctx.itemKinds !== undefined &&
ctx.itemKinds.length > 0 &&
ctx.itemKinds.every((kind) => kind === "memory");
items.push(
...(await buildMemoryItems({
source,
targets,
overwrite: ctx.overwrite,
includeInstructions: !memoryOnly,
})),
);
if (!memoryOnly) {
items.push(...(await buildConfigItems({ ctx, source })));
items.push(...(await buildSkillItems({ source, targets, overwrite: ctx.overwrite })));
for (const archivePath of source.archivePaths) {
addArchiveItem(items, {
id: archivePath.id,
source: archivePath.path,
relativePath: archivePath.relativePath,
});
}
addArchiveItem(items, {
id: "archive:CLAUDE.local.md",
source: source.projectLocalMemoryPath,
relativePath: "CLAUDE.local.md",
message:
"Claude local project memory is personal machine-local state. It is archived for manual review.",
});
addArchiveItem(items, {
id: "archive:.claude/rules",
source: source.projectRulesDir,
relativePath: ".claude/rules",
});
addArchiveItem(items, {
id: "archive:user-agents",
source: source.userAgentsDir,
relativePath: "agents/user",
});
addArchiveItem(items, {
id: "archive:project-agents",
source: source.projectAgentsDir,
relativePath: "agents/project",
});
}
const warnings = [
...(items.some((item) => item.status === "conflict")
? [
"Conflicts were found. Re-run with --overwrite to replace conflicting targets after item-level backups.",
]
: []),
...(items.some((item) => item.kind === "archive")
? [
"Some Claude files are archive-only. They will be copied into the migration report for manual review, not loaded into OpenClaw.",
]
: []),
...(items.some((item) => item.kind === "manual")
? ["Some Claude settings require manual review before they can be activated safely."]
: []),
];
return {
providerId: "claude",
source: source.root,
target: targets.workspaceDir,
summary: summarizeMigrationItems(items),
items,
warnings,
nextSteps: memoryOnly ? [] : ["Run openclaw doctor after applying the migration."],
metadata: { agentDir: targets.agentDir },
};
}