feat(FN-1163): align command references with fn naming
- Update CLI command strings, help text, and project-resolution messaging to reference fn - Sync core backup/store messaging and dashboard API/planning/subtask prompt examples with fn usage - Refresh dashboard UI copy in settings, project detection, and PR guidance for consistent command naming - Adjust CLI/dashboard tests and add a @gsxdsm/fusion changeset for the naming alignment patch
This commit is contained in:
@@ -161,7 +161,7 @@ describe("build-exe", () => {
|
||||
child!.stdout!.on("data", (d: Buffer) => {
|
||||
startupOutput += d.toString();
|
||||
if (
|
||||
startupOutput.includes("kb board") &&
|
||||
startupOutput.includes("fn board") &&
|
||||
startupOutput.includes(`→ http://localhost:${port}`)
|
||||
) {
|
||||
settle("ready");
|
||||
|
||||
@@ -57,7 +57,7 @@ function makeCtx(cwd: string) {
|
||||
|
||||
// ── Tests ──────────────────────────────────────────────────────────
|
||||
|
||||
describe("kb pi extension", () => {
|
||||
describe("fn pi extension", () => {
|
||||
let tmpDir: string;
|
||||
let api: ReturnType<typeof createMockAPI>;
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ const HELP = `
|
||||
fn — AI-orchestrated task board
|
||||
|
||||
Usage:
|
||||
fn init [opts] Initialize a new kb project in the current directory
|
||||
fn init [opts] Initialize a new fn project in the current directory
|
||||
fn dashboard Start the board web UI
|
||||
fn dashboard --paused Start with automation paused
|
||||
fn dashboard --dev Start web UI only (no AI engine)
|
||||
|
||||
@@ -104,7 +104,7 @@ describe("backup commands", () => {
|
||||
|
||||
it("runBackupList without project falls back to current cwd task store when resolution fails", async () => {
|
||||
const cwdSpy = vi.spyOn(process, "cwd").mockReturnValue("/local/project");
|
||||
mockResolveProject.mockRejectedValueOnce(new Error("No kb project found"));
|
||||
mockResolveProject.mockRejectedValueOnce(new Error("No fn project found"));
|
||||
await runBackupList();
|
||||
expect(mockResolveProject).toHaveBeenCalledWith(undefined);
|
||||
expect(TaskStore).toHaveBeenCalledWith("/local/project");
|
||||
@@ -113,7 +113,7 @@ describe("backup commands", () => {
|
||||
|
||||
it("falls back to current cwd task store when project resolution fails for project-targeted commands", async () => {
|
||||
const cwdSpy = vi.spyOn(process, "cwd").mockReturnValue("/fallback/project");
|
||||
mockResolveProject.mockRejectedValue(new Error("Project 'missing' not found. Run 'kb project list' to see registered projects."));
|
||||
mockResolveProject.mockRejectedValue(new Error("Project 'missing' not found. Run 'fn project list' to see registered projects."));
|
||||
|
||||
await runBackupList("missing");
|
||||
expect(TaskStore).toHaveBeenCalledWith("/fallback/project");
|
||||
|
||||
@@ -34,7 +34,7 @@ async function getBackupManager(projectName?: string): Promise<{
|
||||
|
||||
/**
|
||||
* Create a database backup immediately.
|
||||
* Usage: kb backup --create
|
||||
* Usage: fn backup --create
|
||||
*/
|
||||
export async function runBackupCreate(projectName?: string): Promise<void> {
|
||||
const { manager, kbDir, store } = await getBackupManager(projectName);
|
||||
@@ -55,7 +55,7 @@ export async function runBackupCreate(projectName?: string): Promise<void> {
|
||||
|
||||
/**
|
||||
* List all database backups.
|
||||
* Usage: kb backup --list
|
||||
* Usage: fn backup --list
|
||||
*/
|
||||
export async function runBackupList(projectName?: string): Promise<void> {
|
||||
const { manager } = await getBackupManager(projectName);
|
||||
@@ -88,7 +88,7 @@ export async function runBackupList(projectName?: string): Promise<void> {
|
||||
|
||||
/**
|
||||
* Restore database from a backup file.
|
||||
* Usage: kb backup --restore <filename>
|
||||
* Usage: fn backup --restore <filename>
|
||||
*/
|
||||
export async function runBackupRestore(filename: string, projectName?: string): Promise<void> {
|
||||
const { manager } = await getBackupManager(projectName);
|
||||
@@ -108,7 +108,7 @@ export async function runBackupRestore(filename: string, projectName?: string):
|
||||
|
||||
/**
|
||||
* Remove old backups exceeding retention limit.
|
||||
* Usage: kb backup --cleanup
|
||||
* Usage: fn backup --cleanup
|
||||
*/
|
||||
export async function runBackupCleanup(projectName?: string): Promise<void> {
|
||||
const { manager } = await getBackupManager(projectName);
|
||||
|
||||
@@ -840,7 +840,7 @@ export async function runDashboard(port: number, opts: { paused?: boolean; dev?:
|
||||
}
|
||||
|
||||
console.log();
|
||||
console.log(` kb board`);
|
||||
console.log(` fn board`);
|
||||
console.log(` ────────────────────────`);
|
||||
console.log(` → http://localhost:${actualPort}`);
|
||||
console.log();
|
||||
|
||||
@@ -126,7 +126,7 @@ describe("git commands", () => {
|
||||
});
|
||||
|
||||
it("propagates project resolution errors for git commands", async () => {
|
||||
vi.mocked(resolveProject).mockRejectedValue(new Error("Project 'missing' not found. Run 'kb project list' to see registered projects."));
|
||||
vi.mocked(resolveProject).mockRejectedValue(new Error("Project 'missing' not found. Run 'fn project list' to see registered projects."));
|
||||
|
||||
await expect(runGitFetch("origin", "missing")).rejects.toThrow("Project 'missing' not found");
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Init command for kb CLI.
|
||||
* Init command for fn CLI.
|
||||
*
|
||||
* Initializes a new kb project in the current directory by:
|
||||
* Initializes a new fn project in the current directory by:
|
||||
* 1. Creating the .fusion/ directory with fusion.db
|
||||
* 2. Registering the project in the central database
|
||||
*
|
||||
@@ -42,7 +42,7 @@ export async function runInit(options: InitOptions = {}): Promise<void> {
|
||||
|
||||
const existing = await central.getProjectByPath(cwd);
|
||||
if (existing) {
|
||||
console.log(`✓ kb project already initialized: "${existing.name}"`);
|
||||
console.log(`✓ fn project already initialized: "${existing.name}"`);
|
||||
console.log(` Path: ${cwd}`);
|
||||
console.log(`\n Project is registered in the central registry.`);
|
||||
console.log(` To re-initialize with a different name, run:`);
|
||||
@@ -64,7 +64,7 @@ export async function runInit(options: InitOptions = {}): Promise<void> {
|
||||
// Get or generate project name
|
||||
const projectName = options.name ?? detectProjectName(cwd);
|
||||
|
||||
console.log(`Initializing kb project: "${projectName}"`);
|
||||
console.log(`Initializing fn project: "${projectName}"`);
|
||||
console.log(` Path: ${cwd}`);
|
||||
|
||||
// Create .fusion/ directory
|
||||
|
||||
@@ -38,7 +38,7 @@ export async function runNodeList(options: NodeListOptions = {}): Promise<void>
|
||||
console.log(JSON.stringify([], null, 2));
|
||||
} else {
|
||||
console.log("\n No nodes registered.");
|
||||
console.log(" Register one with: kb node add <name>\n");
|
||||
console.log(" Register one with: fn node add <name>\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -78,7 +78,7 @@ export async function runNodeList(options: NodeListOptions = {}): Promise<void>
|
||||
*/
|
||||
export async function runNodeAdd(name: string, options: NodeAddOptions = {}): Promise<void> {
|
||||
if (!name) {
|
||||
console.error("Usage: kb node add <name> [--url <url>] [--api-key <key>] [--max-concurrent <n>]");
|
||||
console.error("Usage: fn node add <name> [--url <url>] [--api-key <key>] [--max-concurrent <n>]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ export async function runNodeAdd(name: string, options: NodeAddOptions = {}): Pr
|
||||
*/
|
||||
export async function runNodeRemove(name: string, options: NodeRemoveOptions = {}): Promise<void> {
|
||||
if (!name) {
|
||||
console.error("Usage: kb node remove <name> [--force]");
|
||||
console.error("Usage: fn node remove <name> [--force]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ export async function runNodeShow(name?: string): Promise<void> {
|
||||
*/
|
||||
export async function runNodeHealth(name: string): Promise<void> {
|
||||
if (!name) {
|
||||
console.error("Usage: kb node health <name>");
|
||||
console.error("Usage: fn node health <name>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Project command implementations for kb CLI.
|
||||
* Project command implementations for fn CLI.
|
||||
*
|
||||
* Provides commands for managing the project registry:
|
||||
* - list: List all registered projects
|
||||
@@ -157,7 +157,7 @@ export async function runProjectList(options: ProjectListOptions = {}): Promise<
|
||||
console.log(JSON.stringify([], null, 2));
|
||||
} else {
|
||||
console.log("\n No projects registered.");
|
||||
console.log(" Register one with: kb project add <name> <path>\n");
|
||||
console.log(" Register one with: fn project add <name> <path>\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -281,17 +281,17 @@ export async function runProjectAdd(
|
||||
// Check for .fusion directory
|
||||
const kbDbPath = resolve(absolutePath, ".fusion", "fusion.db");
|
||||
if (!existsSync(kbDbPath) && !options.force) {
|
||||
console.log(`\n No kb project found at ${formatDisplayPath(absolutePath)}`);
|
||||
const init = await rl.question(" Initialize kb here first? [Y/n] ");
|
||||
console.log(`\n No fn project found at ${formatDisplayPath(absolutePath)}`);
|
||||
const init = await rl.question(" Initialize fn here first? [Y/n] ");
|
||||
rl.close();
|
||||
|
||||
if (init.trim().toLowerCase() !== "n") {
|
||||
// Initialize the project
|
||||
const store = new TaskStore(absolutePath);
|
||||
await store.init();
|
||||
console.log(` ✓ Initialized kb at ${absolutePath}`);
|
||||
console.log(` ✓ Initialized fn at ${absolutePath}`);
|
||||
} else {
|
||||
console.log("\n Cancelled. Run `kb init` to initialize a project first.\n");
|
||||
console.log("\n Cancelled. Run `fn init` to initialize a project first.\n");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@@ -334,8 +334,8 @@ export async function runProjectAdd(
|
||||
// Check for .fusion directory
|
||||
const kbDbPath = resolve(absolutePath, ".fusion", "fusion.db");
|
||||
if (!existsSync(kbDbPath) && !options.force) {
|
||||
console.error(`\n ✗ No kb project found at ${formatDisplayPath(absolutePath)}`);
|
||||
console.error(" Run `kb init` first to initialize the project.\n");
|
||||
console.error(`\n ✗ No fn project found at ${formatDisplayPath(absolutePath)}`);
|
||||
console.error(" Run `fn init` first to initialize the project.\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -383,7 +383,7 @@ export async function runProjectAdd(
|
||||
*/
|
||||
export async function runProjectRemove(name: string, options: ProjectRemoveOptions = {}): Promise<void> {
|
||||
if (!name) {
|
||||
console.error("Usage: kb project remove <name> [--force]");
|
||||
console.error("Usage: fn project remove <name> [--force]");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -414,7 +414,7 @@ export async function runProjectRemove(name: string, options: ProjectRemoveOptio
|
||||
console.log(` Location: ${formatDisplayPath(project.path)}`);
|
||||
console.log();
|
||||
console.log(" Note: Project data is preserved. You can re-register with:");
|
||||
console.log(` kb project add ${project.name} ${project.path}`);
|
||||
console.log(` fn project add ${project.name} ${project.path}`);
|
||||
console.log();
|
||||
} finally {
|
||||
await central.close();
|
||||
@@ -461,7 +461,7 @@ export async function runProjectShow(name?: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
console.log();
|
||||
console.log(" Run 'kb project add' to register this project.");
|
||||
console.log(" Run 'fn project add' to register this project.");
|
||||
console.log();
|
||||
return;
|
||||
}
|
||||
@@ -530,7 +530,7 @@ export const runProjectInfo = runProjectShow;
|
||||
*/
|
||||
export async function runProjectSetDefault(name: string): Promise<void> {
|
||||
if (!name) {
|
||||
console.error("Usage: kb project set-default <name>");
|
||||
console.error("Usage: fn project set-default <name>");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -579,7 +579,7 @@ export async function runProjectDetect(): Promise<void> {
|
||||
console.log();
|
||||
} else {
|
||||
console.log();
|
||||
console.log(" No kb project detected from current directory.");
|
||||
console.log(" No fn project detected from current directory.");
|
||||
console.log();
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -5,7 +5,7 @@ import { resolveProject } from "../project-context.js";
|
||||
|
||||
/**
|
||||
* Run settings export command.
|
||||
* Usage: kb settings export [--output <path>] [--scope global|project|both]
|
||||
* Usage: fn settings export [--output <path>] [--scope global|project|both]
|
||||
*
|
||||
* @param options.output - Custom output file path (optional, auto-generates if not provided)
|
||||
* @param options.scope - Which settings to export: 'global', 'project', or 'both' (default: 'both')
|
||||
|
||||
@@ -5,7 +5,7 @@ import { resolveProject } from "../project-context.js";
|
||||
|
||||
/**
|
||||
* Run settings import command.
|
||||
* Usage: kb settings import <file> [--scope global|project|both] [--merge] [--yes]
|
||||
* Usage: fn settings import <file> [--scope global|project|both] [--merge] [--yes]
|
||||
*
|
||||
* @param filePath - Path to the JSON file to import
|
||||
* @param options.scope - Which settings to import: 'global', 'project', or 'both' (default: 'both')
|
||||
|
||||
@@ -78,7 +78,7 @@ describe("settings commands", () => {
|
||||
|
||||
expect(getSettings).toHaveBeenCalled();
|
||||
expect(resolveProject).not.toHaveBeenCalled();
|
||||
expect(logSpy).toHaveBeenCalledWith(" kb Global Settings");
|
||||
expect(logSpy).toHaveBeenCalledWith(" fn Global Settings");
|
||||
});
|
||||
|
||||
it("runSettingsShow with project uses project store", async () => {
|
||||
@@ -94,7 +94,7 @@ describe("settings commands", () => {
|
||||
await runSettingsShow("demo-project");
|
||||
|
||||
expect(resolveProject).toHaveBeenCalledWith("demo-project");
|
||||
expect(logSpy).toHaveBeenCalledWith(" kb Settings for project 'demo-project'");
|
||||
expect(logSpy).toHaveBeenCalledWith(" fn Settings for project 'demo-project'");
|
||||
});
|
||||
|
||||
it("runSettingsSet without project updates global-only settings", async () => {
|
||||
|
||||
@@ -171,8 +171,8 @@ function getSettingLabel(key: string): string {
|
||||
* Run settings show command.
|
||||
*
|
||||
* Behavior:
|
||||
* - `kb settings` shows global settings
|
||||
* - `kb settings --project <name>` shows project settings for that project
|
||||
* - `fn settings` shows global settings
|
||||
* - `fn settings --project <name>` shows project settings for that project
|
||||
*/
|
||||
export async function runSettingsShow(projectName?: string): Promise<void> {
|
||||
const project = projectName ? await resolveProject(projectName) : undefined;
|
||||
@@ -182,8 +182,8 @@ export async function runSettingsShow(projectName?: string): Promise<void> {
|
||||
|
||||
console.log();
|
||||
console.log(project
|
||||
? ` kb Settings for project '${project.projectName}'`
|
||||
: " kb Global Settings");
|
||||
? ` fn Settings for project '${project.projectName}'`
|
||||
: " fn Global Settings");
|
||||
console.log(" " + "─".repeat(50));
|
||||
|
||||
const settingGroups = [
|
||||
|
||||
@@ -282,7 +282,7 @@ describe("project-aware task command behavior", () => {
|
||||
const init = vi.fn();
|
||||
|
||||
vi.mocked(resolveProject).mockRejectedValueOnce(
|
||||
new Error("No kb project found in current directory. Use --project or run from a project directory.")
|
||||
new Error("No fn project found in current directory. Use --project or run from a project directory.")
|
||||
);
|
||||
|
||||
(TaskStore as unknown as ReturnType<typeof vi.fn>).mockImplementation((projectPath: string) => ({
|
||||
@@ -661,11 +661,11 @@ describe("project-aware task command behavior", () => {
|
||||
|
||||
it("surfaces project resolution failures from shared context when project flag is explicit", async () => {
|
||||
vi.mocked(resolveProject).mockRejectedValueOnce(
|
||||
new Error("Project 'demo-project' not found. Run 'kb project list' to see registered projects.")
|
||||
new Error("Project 'demo-project' not found. Run 'fn project list' to see registered projects.")
|
||||
);
|
||||
|
||||
await expect(runTaskList("demo-project")).rejects.toThrow(
|
||||
"Project 'demo-project' not found. Run 'kb project list' to see registered projects."
|
||||
"Project 'demo-project' not found. Run 'fn project list' to see registered projects."
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -158,7 +158,7 @@ export async function runTaskList(projectName?: string) {
|
||||
const tasks = await store.listTasks();
|
||||
|
||||
if (tasks.length === 0) {
|
||||
console.log("\n No tasks yet. Create one with: kb task create\n");
|
||||
console.log("\n No tasks yet. Create one with: fn task create\n");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -1492,7 +1492,7 @@ export default function kbExtension(pi: ExtensionAPI) {
|
||||
name: "kb_feature_link_task",
|
||||
label: "KB: Link Feature to Task",
|
||||
description:
|
||||
"Link a feature to a kb task for implementation. " +
|
||||
"Link a feature to a fn task for implementation. " +
|
||||
"Updates the feature status to 'triaged' and associates it with the task.",
|
||||
promptSnippet: "Link a feature to a task",
|
||||
promptGuidelines: [
|
||||
|
||||
@@ -184,10 +184,10 @@ export async function detectProjectFromCwd(
|
||||
|
||||
// Walk up the directory tree
|
||||
while (true) {
|
||||
// Check for kb database
|
||||
// Check for fn database
|
||||
const kbPath = resolve(currentDir, ".fusion", "fusion.db");
|
||||
if (existsSync(kbPath)) {
|
||||
// Found a kb project - check if it's registered
|
||||
// Found a fn project - check if it's registered
|
||||
const project = await central.getProjectByPath(currentDir);
|
||||
if (project) {
|
||||
return project;
|
||||
|
||||
@@ -245,7 +245,7 @@ export async function resolveProject(options: ResolveOptions = {}): Promise<Reso
|
||||
|
||||
// 4. Has .fusion/ but not registered
|
||||
if (interactive) {
|
||||
console.log(`\n Found kb project at ${kbDir} but it's not registered.`);
|
||||
console.log(`\n Found fn project at ${kbDir} but it's not registered.`);
|
||||
const shouldRegister = await promptConfirm("Register this project now?", true);
|
||||
|
||||
if (shouldRegister) {
|
||||
@@ -284,7 +284,7 @@ export async function resolveProject(options: ResolveOptions = {}): Promise<Reso
|
||||
}
|
||||
} else {
|
||||
throw new ProjectResolutionError(
|
||||
`Found kb project at ${kbDir} but it's not registered.\n\n` +
|
||||
`Found fn project at ${kbDir} but it's not registered.\n\n` +
|
||||
"Run `fn project add " + kbDir + "` to register it, or use --project <name>.",
|
||||
"NOT_REGISTERED",
|
||||
{ directory: kbDir }
|
||||
@@ -301,7 +301,7 @@ export async function resolveProject(options: ResolveOptions = {}): Promise<Reso
|
||||
"No projects registered.\n\n" +
|
||||
"To get started:\n" +
|
||||
" 1. Navigate to your project directory\n" +
|
||||
" 2. Run `fn init` to initialize kb\n" +
|
||||
" 2. Run `fn init` to initialize fn\n" +
|
||||
" 3. Run `fn project add .` to register it\n" +
|
||||
"\nOr: `fn project add <path>` to register from anywhere.",
|
||||
"NO_PROJECTS"
|
||||
@@ -450,7 +450,7 @@ export async function isProjectNameTaken(
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that a path contains an initialized kb project (.fusion/ directory exists).
|
||||
* Validate that a path contains an initialized fn project (.fusion/ directory exists).
|
||||
*/
|
||||
export function isKbProject(path: string): boolean {
|
||||
const kbPath = resolve(path, ".fusion");
|
||||
@@ -557,14 +557,14 @@ export async function registerProjectInteractive(
|
||||
if (!isKbProject(absPath)) {
|
||||
if (interactive) {
|
||||
console.log(`\n No .fusion/ directory found in ${absPath}`);
|
||||
const shouldInit = await promptConfirm("Initialize kb here first?", true);
|
||||
const shouldInit = await promptConfirm("Initialize fn here first?", true);
|
||||
|
||||
if (shouldInit) {
|
||||
// Initialize the project (create .fusion/)
|
||||
const { TaskStore } = await import("@fusion/core");
|
||||
const store = new TaskStore(absPath);
|
||||
await store.init();
|
||||
console.log(` ✓ Initialized kb at ${absPath}`);
|
||||
console.log(` ✓ Initialized fn at ${absPath}`);
|
||||
} else {
|
||||
throw new ProjectResolutionError(
|
||||
"Cannot register project without .fusion/ directory. Run `fn init` first.",
|
||||
|
||||
Reference in New Issue
Block a user