Add a plugin-owned Linear importer that creates Fusion tasks from Linear issues. - Add the fusion-plugin-linear-import package with settings, Linear GraphQL client, import routes, tools, and dashboard UI. - Bundle and register the Linear import plugin in the CLI and dashboard plugin view registry. - Document bundled plugin authoring details and cover duplicate detection, routes, tools, UI, and packaging with tests. Files changed: .changeset/fn-7443-linear-import-plugin.md | 7 + docs/PLUGIN_AUTHORING.md | 7 + docs/task-management.md | 2 + packages/cli/src/__tests__/bundle-output.test.ts | 22 ++ .../__tests__/bundled-plugin-install.test.ts | 29 +++ packages/cli/src/plugins/bundled-plugin-install.ts | 1 + .../cli/src/plugins/staged-bundled-plugin-ids.ts | 1 + packages/cli/tsup.config.ts | 8 + .../__tests__/registerBundledPluginViews.test.tsx | 9 +- .../app/plugins/registerBundledPluginViews.ts | 18 ++ .../app/types/plugin-dashboard-views.d.ts | 9 + .../src/__tests__/routes-plugin-registry.test.ts | 5 + .../runtime-plugin-alias-regression.test.ts | 12 + packages/dashboard/src/registry-manifest.json | 10 + packages/dashboard/vite.config.ts | 8 + packages/dashboard/vitest.config.ts | 8 + plugins/fusion-plugin-linear-import/README.md | 75 ++++++ plugins/fusion-plugin-linear-import/manifest.json | 48 ++++ plugins/fusion-plugin-linear-import/package.json | 38 +++ .../scripts/copy-css.mjs | 11 + .../src/LinearImportView.css | 167 +++++++++++++ .../src/LinearImportView.tsx | 263 +++++++++++++++++++++ .../src/__tests__/LinearImportView.test.tsx | 124 ++++++++++ .../src/__tests__/import-linear.test.ts | 78 ++++++ .../src/__tests__/linear-client.test.ts | 81 +++++++ .../src/__tests__/routes.test.ts | 90 +++++++ .../src/__tests__/tools.test.ts | 79 +++++++ .../src/dashboard-interop.d.ts | 13 + .../src/dashboard-view.tsx | 10 + .../src/import-linear.ts | 154 ++++++++++++ plugins/fusion-plugin-linear-import/src/index.ts | 45 ++++ .../src/linear-client.ts | 247 +++++++++++++++++++ plugins/fusion-plugin-linear-import/src/routes.ts | 149 ++++++++++++ .../fusion-plugin-linear-import/src/settings.ts | 68 ++++++ plugins/fusion-plugin-linear-import/src/tools.ts | 138 +++++++++++ plugins/fusion-plugin-linear-import/tsconfig.json | 14 ++ .../fusion-plugin-linear-import/vitest.config.ts | 40 ++++ pnpm-lock.yaml | 40 ++++ pnpm-workspace.yaml | 1 + 39 files changed, 2128 insertions(+), 1 deletion(-) Fusion-Task-Id: FN-7443 Fusion-Task-Lineage: a016a9d4-84a4-4a0b-b9b6-b9a2886da49a Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
69 lines
2.4 KiB
TypeScript
69 lines
2.4 KiB
TypeScript
import type { PluginSettingSchema } from "@fusion/plugin-sdk";
|
|
|
|
export const LINEAR_PLUGIN_ID = "fusion-plugin-linear-import";
|
|
|
|
export const linearSettingsSchema: Record<string, PluginSettingSchema> = {
|
|
apiKey: {
|
|
type: "password",
|
|
label: "Linear API key",
|
|
description: "Personal Linear API key used only by this plugin to browse and import issues.",
|
|
required: true,
|
|
group: "Authentication",
|
|
},
|
|
defaultTeamKey: {
|
|
type: "string",
|
|
label: "Default team key or ID",
|
|
description: "Optional Linear team key or UUID to prefill issue searches.",
|
|
group: "Defaults",
|
|
},
|
|
defaultStateFilter: {
|
|
type: "enum",
|
|
label: "Default issue state filter",
|
|
description: "Initial issue state filter for browse and import tools.",
|
|
enumValues: ["active", "backlog", "started", "unstarted", "completed", "canceled", "all"],
|
|
defaultValue: "active",
|
|
group: "Defaults",
|
|
},
|
|
defaultAssigneeId: {
|
|
type: "string",
|
|
label: "Default assignee ID",
|
|
description: "Optional Linear user UUID used as the default assignee filter.",
|
|
group: "Defaults",
|
|
},
|
|
};
|
|
|
|
export type LinearStateFilter = "active" | "backlog" | "started" | "unstarted" | "completed" | "canceled" | "all";
|
|
|
|
export interface LinearPluginSettings {
|
|
apiKey?: string;
|
|
defaultTeamKey?: string;
|
|
defaultStateFilter: LinearStateFilter;
|
|
defaultAssigneeId?: string;
|
|
}
|
|
|
|
function optionalTrimmed(value: unknown): string | undefined {
|
|
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
}
|
|
|
|
export function resolveLinearSettings(settings: Record<string, unknown>): LinearPluginSettings {
|
|
const rawState = optionalTrimmed(settings.defaultStateFilter);
|
|
const defaultStateFilter = rawState && linearSettingsSchema.defaultStateFilter.enumValues?.includes(rawState)
|
|
? rawState as LinearStateFilter
|
|
: "active";
|
|
|
|
/*
|
|
FNXC:LinearImport 2026-07-02-00:00:
|
|
FN-7443 requires Linear credentials to be plugin-owned settings, not host settings. Resolve only sanitized defaults here and keep the password value out of logs, task descriptions, route responses, and tool details.
|
|
*/
|
|
return {
|
|
apiKey: optionalTrimmed(settings.apiKey),
|
|
defaultTeamKey: optionalTrimmed(settings.defaultTeamKey),
|
|
defaultStateFilter,
|
|
defaultAssigneeId: optionalTrimmed(settings.defaultAssigneeId),
|
|
};
|
|
}
|
|
|
|
export function hasLinearApiKey(settings: Record<string, unknown>): boolean {
|
|
return Boolean(resolveLinearSettings(settings).apiKey);
|
|
}
|