Files
fusion/plugins/fusion-plugin-linear-import/src/settings.ts
gsxdsm dfb6e5270d FN-7443: add bundled Linear import plugin
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>
2026-07-02 14:31:16 -07:00

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);
}