@shipfox/client-onboarding 22.0.2 → 23.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ export {default} from '../../.storybook/main.ts';
@@ -0,0 +1,7 @@
1
+ @import "@shipfox/react-ui/index.css";
2
+
3
+ /* The `@import` supplies design-system tokens and base styles. Tailwind v4 only emits
4
+ utilities from scanned source, so include package stories plus the react-ui components
5
+ the setup checklist renders. */
6
+ @source "../src";
7
+ @source "../../../shared/react/ui/src";
@@ -0,0 +1,56 @@
1
+ import './preview.css';
2
+ import {ThemeProvider} from '@shipfox/react-ui/theme';
3
+ import type {Decorator, Preview} from '@storybook/react';
4
+
5
+ if (typeof document !== 'undefined' && document.fonts) {
6
+ void Promise.all([
7
+ document.fonts.load("16px 'IBM Plex Sans'"),
8
+ document.fonts.load("italic 16px 'IBM Plex Sans'"),
9
+ document.fonts.load("16px 'Commit Mono'"),
10
+ document.fonts.load("bold 16px 'Commit Mono'"),
11
+ ]);
12
+ }
13
+
14
+ const withTheme: Decorator = (Story, context) => {
15
+ const theme = context.globals.theme;
16
+ return (
17
+ <ThemeProvider key={theme} defaultTheme={theme} storageKey={`shipfox-theme-${theme}`}>
18
+ <Story />
19
+ </ThemeProvider>
20
+ );
21
+ };
22
+
23
+ const preview: Preview = {
24
+ decorators: [withTheme],
25
+ parameters: {
26
+ argos: {
27
+ // Dual-theme coverage lives in @shipfox/react-ui (the theming source of truth);
28
+ // feature packages capture only the primary dark theme to limit Argos spend.
29
+ modes: {
30
+ dark: {theme: 'dark'},
31
+ },
32
+ fitToContent: false,
33
+ },
34
+ options: {
35
+ storySort: {method: 'alphabetical'},
36
+ },
37
+ },
38
+ globalTypes: {
39
+ theme: {
40
+ name: 'Theme',
41
+ description: 'Global theme for components',
42
+ defaultValue: 'dark',
43
+ toolbar: {
44
+ icon: 'sun',
45
+ items: [
46
+ {value: 'light', icon: 'sun', title: 'Light'},
47
+ {value: 'dark', icon: 'moon', title: 'Dark'},
48
+ {value: 'system', icon: 'info', title: 'System'},
49
+ ],
50
+ showName: true,
51
+ },
52
+ },
53
+ },
54
+ };
55
+
56
+ export default preview;
@@ -1,2 +1,2 @@
1
1
  $ shipfox-swc
2
- Successfully compiled: 2 files with swc (199.78ms)
2
+ Successfully compiled: 4 files with swc (436.14ms)
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # @shipfox/client-onboarding
2
2
 
3
+ ## 23.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - f64c66f: Adds `deriveIntegrationReadiness` and `deriveSetupChecklist`, which derive
8
+ per-provider integration-connected and attention state and the workspace
9
+ Get-started setup checklist.
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies [693e656]
14
+ - Updated dependencies [f7b3db8]
15
+ - @shipfox/client-shell@23.0.0
16
+ - @shipfox/client-projects@23.0.0
17
+ - @shipfox/client-agent@23.0.0
18
+ - @shipfox/client-integrations@23.0.0
19
+
20
+ ## 22.0.3
21
+
22
+ ### Patch Changes
23
+
24
+ - 0d3c2e3: Updates @shipfox/client-agent, @shipfox/client-onboarding, and @shipfox/client-workflows to show
25
+ managed inference providers without exposing workspace credential setup, keep workflow examples
26
+ limited to supported models, and explain managed-provider failures in workflow runs.
27
+ - Updated dependencies [0d3c2e3]
28
+ - Updated dependencies [b734450]
29
+ - Updated dependencies [ddcc546]
30
+ - @shipfox/client-agent@22.0.3
31
+ - @shipfox/client-integrations@22.0.3
32
+ - @shipfox/client-projects@22.0.3
33
+ - @shipfox/client-shell@22.0.3
34
+
3
35
  ## 22.0.2
4
36
 
5
37
  ### Patch Changes
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # Shipfox Client Onboarding
2
+
3
+ Client onboarding for Shipfox workspaces: the pre-project setup gate and the
4
+ pure derivations behind the post-activation Get-started checklist.
5
+
6
+ ## What it does
7
+
8
+ - **`loadWorkspaceSetupRoute`**: the existing onboarding gate. It recomputes
9
+ "where should this user be" from live queries on every navigation under
10
+ `/w/$workspaceSlug`: suspended check, then project existence, then usable
11
+ source connections, then model-provider handling, then project creation.
12
+ - **`deriveIntegrationReadiness`**: the readiness model shared with the
13
+ first-workflow spec. It turns the provider catalog and the workspace's
14
+ connections into per-provider `connected` and `attention` state, an
15
+ `attentionProviders` list ordered by the most recent connection update, and
16
+ the two workspace-level facts `hasSourceControl` and `hasToolIntegration`.
17
+ - **`deriveSetupChecklist`**: the setup-checklist derivation. It turns
18
+ integration readiness plus runner, model-provider, and membership facts into
19
+ the ordered `items` list, the tracked `openCount` and `trackedCount`, and
20
+ `complete`. Rows follow the spec order; the runner and model-provider rows
21
+ exist only when the installation does not already provide the capability;
22
+ the first-workflow and teammates rows are pointers that never count.
23
+
24
+ Both derivations are pure functions. They test without React and decide what
25
+ the checklist shows, so the component work in this package renders their
26
+ output without re-deriving anything.
27
+
28
+ ## Installation and setup
29
+
30
+ ```sh
31
+ pnpm add @shipfox/client-onboarding
32
+ ```
33
+
34
+ The package is part of the `libs/client` workspace. Its runtime dependencies
35
+ are `client-agent`, `client-integrations`, `client-projects`, and `client-shell`.
36
+
37
+ ## Usage
38
+
39
+ ```ts
40
+ import {
41
+ deriveIntegrationReadiness,
42
+ deriveSetupChecklist,
43
+ } from '@shipfox/client-onboarding';
44
+
45
+ const readiness = deriveIntegrationReadiness({
46
+ providers: [
47
+ {provider: 'github', displayName: 'GitHub', capabilities: ['source_control']},
48
+ {provider: 'linear', displayName: 'Linear', capabilities: ['agent_tools']},
49
+ ],
50
+ connections: [],
51
+ });
52
+
53
+ const checklist = deriveSetupChecklist({
54
+ readiness,
55
+ installationRunners: 'none',
56
+ workspaceRunnerCapacity: false,
57
+ modelProvider: {installationProvided: false, configured: false},
58
+ membership: {memberCount: 1, pendingInvitationCount: 0},
59
+ });
60
+
61
+ checklist.items; // 7 rows: source control, project, tools, runner,
62
+ // model provider, first workflow, teammates
63
+ checklist.trackedCount; // 5
64
+ checklist.openCount; // 3
65
+ checklist.complete; // false
66
+ ```
67
+
68
+ The caller maps its own query results to the derivation inputs:
69
+
70
+ - `providers` and `connections` use the plain values from
71
+ `@shipfox/client-integrations`.
72
+ - `installationRunners` is `'managed'` or `'none'`.
73
+ - `workspaceRunnerCapacity` reports whether the workspace has runner capacity.
74
+ - `modelProvider` reports installation-provided inference and workspace
75
+ configuration.
76
+ - `membership` reports the member and pending-invitation counts.
77
+
78
+ ## Behavior notes
79
+
80
+ - A provider is `connected` when at least one connection is `active`; it needs
81
+ `attention` when connections exist but none is active. The two states are
82
+ mutually exclusive.
83
+ - `hasToolIntegration` is the tools-row criterion: an active connection whose
84
+ provider lacks the `source_control` capability. GitHub never satisfies it.
85
+ - The tools row names one attention provider ("Linear needs attention") or
86
+ counts several ("2 integrations need attention").
87
+ - `complete` is true when every tracked row is done. Tracked rows are source
88
+ control, project, and tools, plus runner and model-provider when those rows
89
+ exist. The first-workflow and teammates pointers never count.
90
+ - The runner row exists only when `installationRunners` is `'none'`; the
91
+ model-provider row exists only when `modelProvider.installationProvided` is
92
+ false.
93
+ - The teammates row renders done at `memberCount >= 2` or
94
+ `pendingInvitationCount >= 1`, but stays a pointer.
95
+
96
+ ## Development
97
+
98
+ ```sh
99
+ turbo check --filter=@shipfox/client-onboarding
100
+ turbo type --filter=@shipfox/client-onboarding
101
+ turbo test --filter=@shipfox/client-onboarding
102
+ turbo build --filter=@shipfox/client-onboarding
103
+ ```
104
+
105
+ The package runs Storybook per the per-package recipe (`pnpm storybook` on
106
+ port 6015). The Storybook test project activates when checklist stories land.
107
+
108
+ ## License
109
+
110
+ MIT
@@ -0,0 +1,35 @@
1
+ import { type IntegrationCapability, type IntegrationConnection, type IntegrationProvider } from '@shipfox/client-integrations';
2
+ /**
3
+ * Per-provider connection state for the workspace setup readiness model.
4
+ * `connected` and `attention` are mutually exclusive: a provider with at
5
+ * least one active connection is connected, a provider whose connections
6
+ * exist but are all inactive needs attention, and a provider without
7
+ * connections is neither.
8
+ */
9
+ export interface IntegrationProviderReadiness {
10
+ provider: string;
11
+ displayName: string;
12
+ capabilities: ReadonlyArray<IntegrationCapability>;
13
+ connected: boolean;
14
+ attention: boolean;
15
+ }
16
+ export interface WorkspaceIntegrationReadiness {
17
+ providers: ReadonlyArray<IntegrationProviderReadiness>;
18
+ /** Providers in attention, most recently updated connection first. */
19
+ attentionProviders: readonly string[];
20
+ /** At least one active connection to a source-control provider. */
21
+ hasSourceControl: boolean;
22
+ /** At least one active connection to a provider without `source_control`. */
23
+ hasToolIntegration: boolean;
24
+ }
25
+ export interface IntegrationReadinessInput {
26
+ providers: readonly IntegrationProvider[];
27
+ connections: readonly IntegrationConnection[];
28
+ }
29
+ /**
30
+ * Derives the workspace integration readiness from the provider catalog and
31
+ * the workspace's connections. The result is the shared input for the setup
32
+ * checklist and for the first-workflow spec's contextual "Connect X" prompt.
33
+ */
34
+ export declare function deriveIntegrationReadiness({ providers, connections, }: IntegrationReadinessInput): WorkspaceIntegrationReadiness;
35
+ //# sourceMappingURL=integration-readiness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-readiness.d.ts","sourceRoot":"","sources":["../../src/core/integration-readiness.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EAEzB,MAAM,8BAA8B,CAAC;AAEtC;;;;;;GAMG;AACH,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,aAAa,CAAC,qBAAqB,CAAC,CAAC;IACnD,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,6BAA6B;IAC5C,SAAS,EAAE,aAAa,CAAC,4BAA4B,CAAC,CAAC;IACvD,sEAAsE;IACtE,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,mEAAmE;IACnE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,6EAA6E;IAC7E,kBAAkB,EAAE,OAAO,CAAC;CAC7B;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAC1C,WAAW,EAAE,SAAS,qBAAqB,EAAE,CAAC;CAC/C;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,EACzC,SAAS,EACT,WAAW,GACZ,EAAE,yBAAyB,GAAG,6BAA6B,CAkC3D"}
@@ -0,0 +1,44 @@
1
+ import { isUsableConnection } from '@shipfox/client-integrations';
2
+ /**
3
+ * Derives the workspace integration readiness from the provider catalog and
4
+ * the workspace's connections. The result is the shared input for the setup
5
+ * checklist and for the first-workflow spec's contextual "Connect X" prompt.
6
+ */ export function deriveIntegrationReadiness({ providers, connections }) {
7
+ const readiness = providers.map((provider)=>{
8
+ const providerConnections = connections.filter((connection)=>connection.provider === provider.provider);
9
+ const connected = providerConnections.some(isUsableConnection);
10
+ return {
11
+ provider: provider.provider,
12
+ displayName: provider.displayName,
13
+ capabilities: provider.capabilities,
14
+ connected,
15
+ attention: providerConnections.length > 0 && !connected
16
+ };
17
+ });
18
+ const latestConnectionUpdate = latestConnectionUpdateByProvider(connections);
19
+ const attentionProviders = readiness.filter((provider)=>provider.attention).sort((a, b)=>{
20
+ const aUpdate = latestConnectionUpdate.get(a.provider) ?? 0;
21
+ const bUpdate = latestConnectionUpdate.get(b.provider) ?? 0;
22
+ return bUpdate - aUpdate;
23
+ }).map((provider)=>provider.provider);
24
+ const hasSourceControl = readiness.some((provider)=>provider.connected && provider.capabilities.includes('source_control'));
25
+ const hasToolIntegration = readiness.some((provider)=>provider.connected && !provider.capabilities.includes('source_control'));
26
+ return {
27
+ providers: readiness,
28
+ attentionProviders,
29
+ hasSourceControl,
30
+ hasToolIntegration
31
+ };
32
+ }
33
+ function latestConnectionUpdateByProvider(connections) {
34
+ const updates = new Map();
35
+ for (const connection of connections){
36
+ const timestamp = Date.parse(connection.updatedAt);
37
+ if (Number.isNaN(timestamp)) continue;
38
+ const current = updates.get(connection.provider) ?? 0;
39
+ if (timestamp > current) updates.set(connection.provider, timestamp);
40
+ }
41
+ return updates;
42
+ }
43
+
44
+ //# sourceMappingURL=integration-readiness.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/integration-readiness.ts"],"sourcesContent":["import {\n type IntegrationCapability,\n type IntegrationConnection,\n type IntegrationProvider,\n isUsableConnection,\n} from '@shipfox/client-integrations';\n\n/**\n * Per-provider connection state for the workspace setup readiness model.\n * `connected` and `attention` are mutually exclusive: a provider with at\n * least one active connection is connected, a provider whose connections\n * exist but are all inactive needs attention, and a provider without\n * connections is neither.\n */\nexport interface IntegrationProviderReadiness {\n provider: string;\n displayName: string;\n capabilities: ReadonlyArray<IntegrationCapability>;\n connected: boolean;\n attention: boolean;\n}\n\nexport interface WorkspaceIntegrationReadiness {\n providers: ReadonlyArray<IntegrationProviderReadiness>;\n /** Providers in attention, most recently updated connection first. */\n attentionProviders: readonly string[];\n /** At least one active connection to a source-control provider. */\n hasSourceControl: boolean;\n /** At least one active connection to a provider without `source_control`. */\n hasToolIntegration: boolean;\n}\n\nexport interface IntegrationReadinessInput {\n providers: readonly IntegrationProvider[];\n connections: readonly IntegrationConnection[];\n}\n\n/**\n * Derives the workspace integration readiness from the provider catalog and\n * the workspace's connections. The result is the shared input for the setup\n * checklist and for the first-workflow spec's contextual \"Connect X\" prompt.\n */\nexport function deriveIntegrationReadiness({\n providers,\n connections,\n}: IntegrationReadinessInput): WorkspaceIntegrationReadiness {\n const readiness = providers.map((provider) => {\n const providerConnections = connections.filter(\n (connection) => connection.provider === provider.provider,\n );\n const connected = providerConnections.some(isUsableConnection);\n return {\n provider: provider.provider,\n displayName: provider.displayName,\n capabilities: provider.capabilities,\n connected,\n attention: providerConnections.length > 0 && !connected,\n };\n });\n\n const latestConnectionUpdate = latestConnectionUpdateByProvider(connections);\n\n const attentionProviders = readiness\n .filter((provider) => provider.attention)\n .sort((a, b) => {\n const aUpdate = latestConnectionUpdate.get(a.provider) ?? 0;\n const bUpdate = latestConnectionUpdate.get(b.provider) ?? 0;\n return bUpdate - aUpdate;\n })\n .map((provider) => provider.provider);\n\n const hasSourceControl = readiness.some(\n (provider) => provider.connected && provider.capabilities.includes('source_control'),\n );\n const hasToolIntegration = readiness.some(\n (provider) => provider.connected && !provider.capabilities.includes('source_control'),\n );\n\n return {providers: readiness, attentionProviders, hasSourceControl, hasToolIntegration};\n}\n\nfunction latestConnectionUpdateByProvider(\n connections: readonly IntegrationConnection[],\n): Map<string, number> {\n const updates = new Map<string, number>();\n for (const connection of connections) {\n const timestamp = Date.parse(connection.updatedAt);\n if (Number.isNaN(timestamp)) continue;\n const current = updates.get(connection.provider) ?? 0;\n if (timestamp > current) updates.set(connection.provider, timestamp);\n }\n return updates;\n}\n"],"names":["isUsableConnection","deriveIntegrationReadiness","providers","connections","readiness","map","provider","providerConnections","filter","connection","connected","some","displayName","capabilities","attention","length","latestConnectionUpdate","latestConnectionUpdateByProvider","attentionProviders","sort","a","b","aUpdate","get","bUpdate","hasSourceControl","includes","hasToolIntegration","updates","Map","timestamp","Date","parse","updatedAt","Number","isNaN","current","set"],"mappings":"AAAA,SAIEA,kBAAkB,QACb,+BAA+B;AAgCtC;;;;CAIC,GACD,OAAO,SAASC,2BAA2B,EACzCC,SAAS,EACTC,WAAW,EACe;IAC1B,MAAMC,YAAYF,UAAUG,GAAG,CAAC,CAACC;QAC/B,MAAMC,sBAAsBJ,YAAYK,MAAM,CAC5C,CAACC,aAAeA,WAAWH,QAAQ,KAAKA,SAASA,QAAQ;QAE3D,MAAMI,YAAYH,oBAAoBI,IAAI,CAACX;QAC3C,OAAO;YACLM,UAAUA,SAASA,QAAQ;YAC3BM,aAAaN,SAASM,WAAW;YACjCC,cAAcP,SAASO,YAAY;YACnCH;YACAI,WAAWP,oBAAoBQ,MAAM,GAAG,KAAK,CAACL;QAChD;IACF;IAEA,MAAMM,yBAAyBC,iCAAiCd;IAEhE,MAAMe,qBAAqBd,UACxBI,MAAM,CAAC,CAACF,WAAaA,SAASQ,SAAS,EACvCK,IAAI,CAAC,CAACC,GAAGC;QACR,MAAMC,UAAUN,uBAAuBO,GAAG,CAACH,EAAEd,QAAQ,KAAK;QAC1D,MAAMkB,UAAUR,uBAAuBO,GAAG,CAACF,EAAEf,QAAQ,KAAK;QAC1D,OAAOkB,UAAUF;IACnB,GACCjB,GAAG,CAAC,CAACC,WAAaA,SAASA,QAAQ;IAEtC,MAAMmB,mBAAmBrB,UAAUO,IAAI,CACrC,CAACL,WAAaA,SAASI,SAAS,IAAIJ,SAASO,YAAY,CAACa,QAAQ,CAAC;IAErE,MAAMC,qBAAqBvB,UAAUO,IAAI,CACvC,CAACL,WAAaA,SAASI,SAAS,IAAI,CAACJ,SAASO,YAAY,CAACa,QAAQ,CAAC;IAGtE,OAAO;QAACxB,WAAWE;QAAWc;QAAoBO;QAAkBE;IAAkB;AACxF;AAEA,SAASV,iCACPd,WAA6C;IAE7C,MAAMyB,UAAU,IAAIC;IACpB,KAAK,MAAMpB,cAAcN,YAAa;QACpC,MAAM2B,YAAYC,KAAKC,KAAK,CAACvB,WAAWwB,SAAS;QACjD,IAAIC,OAAOC,KAAK,CAACL,YAAY;QAC7B,MAAMM,UAAUR,QAAQL,GAAG,CAACd,WAAWH,QAAQ,KAAK;QACpD,IAAIwB,YAAYM,SAASR,QAAQS,GAAG,CAAC5B,WAAWH,QAAQ,EAAEwB;IAC5D;IACA,OAAOF;AACT"}
@@ -0,0 +1,49 @@
1
+ import type { WorkspaceIntegrationReadiness } from './integration-readiness.js';
2
+ export type SetupChecklistItemStatus = 'done' | 'open' | 'info';
3
+ export type SetupChecklistItemId = 'source-control' | 'project' | 'tools' | 'runner' | 'model-provider' | 'first-workflow' | 'teammates';
4
+ export interface SetupChecklistAction {
5
+ label: string;
6
+ href: string;
7
+ }
8
+ export interface SetupChecklistItem {
9
+ id: SetupChecklistItemId;
10
+ title: string;
11
+ status: SetupChecklistItemStatus;
12
+ /** Tracked items count toward completion; pointers never do. */
13
+ tracked: boolean;
14
+ /** One line of purpose, rendered for open and pointer rows. */
15
+ purpose?: string;
16
+ /** Primary action, a link to the settings page where the work happens. */
17
+ action?: SetupChecklistAction;
18
+ }
19
+ export interface SetupChecklist {
20
+ /** Rows in spec order: done progress, activation asks, then pointers. */
21
+ items: readonly SetupChecklistItem[];
22
+ /** Open tracked rows. */
23
+ openCount: number;
24
+ /** Rows that count toward completion. */
25
+ trackedCount: number;
26
+ /** True when every tracked row is done. */
27
+ complete: boolean;
28
+ }
29
+ export interface SetupChecklistInput {
30
+ readiness: WorkspaceIntegrationReadiness;
31
+ installationRunners: 'managed' | 'none';
32
+ workspaceRunnerCapacity: boolean;
33
+ modelProvider: {
34
+ installationProvided: boolean;
35
+ configured: boolean;
36
+ };
37
+ membership: {
38
+ memberCount: number;
39
+ pendingInvitationCount: number;
40
+ };
41
+ }
42
+ /**
43
+ * Derives the workspace setup checklist from integration readiness and the
44
+ * runner, model-provider, and membership facts. Rows follow the spec order;
45
+ * the runner and model-provider rows exist only when the installation does
46
+ * not already provide the capability.
47
+ */
48
+ export declare function deriveSetupChecklist({ readiness, installationRunners, workspaceRunnerCapacity, modelProvider, membership, }: SetupChecklistInput): SetupChecklist;
49
+ //# sourceMappingURL=setup-checklist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-checklist.d.ts","sourceRoot":"","sources":["../../src/core/setup-checklist.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,6BAA6B,EAAC,MAAM,4BAA4B,CAAC;AAE9E,MAAM,MAAM,wBAAwB,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AAEhE,MAAM,MAAM,oBAAoB,GAC5B,gBAAgB,GAChB,SAAS,GACT,OAAO,GACP,QAAQ,GACR,gBAAgB,GAChB,gBAAgB,GAChB,WAAW,CAAC;AAEhB,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,oBAAoB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,wBAAwB,CAAC;IACjC,gEAAgE;IAChE,OAAO,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,oBAAoB,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,yEAAyE;IACzE,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,6BAA6B,CAAC;IACzC,mBAAmB,EAAE,SAAS,GAAG,MAAM,CAAC;IACxC,uBAAuB,EAAE,OAAO,CAAC;IACjC,aAAa,EAAE;QAAC,oBAAoB,EAAE,OAAO,CAAC;QAAC,UAAU,EAAE,OAAO,CAAA;KAAC,CAAC;IACpE,UAAU,EAAE;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,sBAAsB,EAAE,MAAM,CAAA;KAAC,CAAC;CACnE;AAWD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,mBAAmB,EACnB,uBAAuB,EACvB,aAAa,EACb,UAAU,GACX,EAAE,mBAAmB,GAAG,cAAc,CAiEtC"}
@@ -0,0 +1,110 @@
1
+ const TOOLS_TITLE = 'Connect your tools';
2
+ const TOOLS_PURPOSE = 'Connect issue tracking, messaging, observability, or any Shipfox integration so workflows can react to events and agents can use them';
3
+ const RUNNER_PURPOSE = 'Jobs wait in `pending` until a runner is online';
4
+ const MODEL_PROVIDER_PURPOSE = 'Agent steps need a model provider to run';
5
+ const FIRST_WORKFLOW_PURPOSE = 'Add a workflow file under `.shipfox/workflows/` and Shipfox picks it up on the next push';
6
+ const TEAMMATES_PURPOSE = 'Everyone in the workspace can edit workflows and see runs';
7
+ /**
8
+ * Derives the workspace setup checklist from integration readiness and the
9
+ * runner, model-provider, and membership facts. Rows follow the spec order;
10
+ * the runner and model-provider rows exist only when the installation does
11
+ * not already provide the capability.
12
+ */ export function deriveSetupChecklist({ readiness, installationRunners, workspaceRunnerCapacity, modelProvider, membership }) {
13
+ const items = [
14
+ {
15
+ id: 'source-control',
16
+ title: 'Connect source control',
17
+ status: 'done',
18
+ tracked: true
19
+ },
20
+ {
21
+ id: 'project',
22
+ title: 'Create a project',
23
+ status: 'done',
24
+ tracked: true
25
+ },
26
+ {
27
+ id: 'tools',
28
+ title: toolsTitle(readiness),
29
+ status: readiness.hasToolIntegration ? 'done' : 'open',
30
+ tracked: true,
31
+ purpose: TOOLS_PURPOSE,
32
+ action: {
33
+ label: 'Connect',
34
+ href: '/settings/integrations'
35
+ }
36
+ }
37
+ ];
38
+ if (installationRunners === 'none') {
39
+ items.push({
40
+ id: 'runner',
41
+ title: 'Set up runner capacity',
42
+ status: workspaceRunnerCapacity ? 'done' : 'open',
43
+ tracked: true,
44
+ purpose: RUNNER_PURPOSE,
45
+ action: {
46
+ label: 'Set up',
47
+ href: '/settings/runners'
48
+ }
49
+ });
50
+ }
51
+ if (!modelProvider.installationProvided) {
52
+ items.push({
53
+ id: 'model-provider',
54
+ title: 'Configure a model provider',
55
+ status: modelProvider.configured ? 'done' : 'open',
56
+ tracked: true,
57
+ purpose: MODEL_PROVIDER_PURPOSE,
58
+ action: {
59
+ label: 'Configure',
60
+ href: '/settings/agents'
61
+ }
62
+ });
63
+ }
64
+ items.push({
65
+ id: 'first-workflow',
66
+ title: 'Push your first workflow',
67
+ status: 'info',
68
+ tracked: false,
69
+ purpose: FIRST_WORKFLOW_PURPOSE,
70
+ action: {
71
+ label: 'Read the quickstart',
72
+ href: '/docs/getting-started'
73
+ }
74
+ }, {
75
+ id: 'teammates',
76
+ title: 'Invite your teammates',
77
+ status: membership.memberCount >= 2 || membership.pendingInvitationCount >= 1 ? 'done' : 'info',
78
+ tracked: false,
79
+ purpose: TEAMMATES_PURPOSE,
80
+ action: {
81
+ label: 'Invite',
82
+ href: '/settings/members'
83
+ }
84
+ });
85
+ const trackedItems = items.filter((item)=>item.tracked);
86
+ const openCount = trackedItems.filter((item)=>item.status === 'open').length;
87
+ return {
88
+ items,
89
+ openCount,
90
+ trackedCount: trackedItems.length,
91
+ complete: openCount === 0
92
+ };
93
+ }
94
+ function toolsTitle(readiness) {
95
+ if (!readiness.hasToolIntegration) {
96
+ const attentionToolProviders = readiness.attentionProviders.map((providerKey)=>readiness.providers.find(({ provider })=>provider === providerKey)).filter((provider)=>provider !== undefined && !provider.capabilities.includes('source_control'));
97
+ if (attentionToolProviders.length === 1) {
98
+ const provider = attentionToolProviders[0];
99
+ if (provider !== undefined) {
100
+ return `${provider.displayName || provider.provider} needs attention`;
101
+ }
102
+ }
103
+ if (attentionToolProviders.length > 1) {
104
+ return `${attentionToolProviders.length} integrations need attention`;
105
+ }
106
+ }
107
+ return TOOLS_TITLE;
108
+ }
109
+
110
+ //# sourceMappingURL=setup-checklist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/core/setup-checklist.ts"],"sourcesContent":["import type {WorkspaceIntegrationReadiness} from './integration-readiness.js';\n\nexport type SetupChecklistItemStatus = 'done' | 'open' | 'info';\n\nexport type SetupChecklistItemId =\n | 'source-control'\n | 'project'\n | 'tools'\n | 'runner'\n | 'model-provider'\n | 'first-workflow'\n | 'teammates';\n\nexport interface SetupChecklistAction {\n label: string;\n href: string;\n}\n\nexport interface SetupChecklistItem {\n id: SetupChecklistItemId;\n title: string;\n status: SetupChecklistItemStatus;\n /** Tracked items count toward completion; pointers never do. */\n tracked: boolean;\n /** One line of purpose, rendered for open and pointer rows. */\n purpose?: string;\n /** Primary action, a link to the settings page where the work happens. */\n action?: SetupChecklistAction;\n}\n\nexport interface SetupChecklist {\n /** Rows in spec order: done progress, activation asks, then pointers. */\n items: readonly SetupChecklistItem[];\n /** Open tracked rows. */\n openCount: number;\n /** Rows that count toward completion. */\n trackedCount: number;\n /** True when every tracked row is done. */\n complete: boolean;\n}\n\nexport interface SetupChecklistInput {\n readiness: WorkspaceIntegrationReadiness;\n installationRunners: 'managed' | 'none';\n workspaceRunnerCapacity: boolean;\n modelProvider: {installationProvided: boolean; configured: boolean};\n membership: {memberCount: number; pendingInvitationCount: number};\n}\n\nconst TOOLS_TITLE = 'Connect your tools';\nconst TOOLS_PURPOSE =\n 'Connect issue tracking, messaging, observability, or any Shipfox integration so workflows can react to events and agents can use them';\nconst RUNNER_PURPOSE = 'Jobs wait in `pending` until a runner is online';\nconst MODEL_PROVIDER_PURPOSE = 'Agent steps need a model provider to run';\nconst FIRST_WORKFLOW_PURPOSE =\n 'Add a workflow file under `.shipfox/workflows/` and Shipfox picks it up on the next push';\nconst TEAMMATES_PURPOSE = 'Everyone in the workspace can edit workflows and see runs';\n\n/**\n * Derives the workspace setup checklist from integration readiness and the\n * runner, model-provider, and membership facts. Rows follow the spec order;\n * the runner and model-provider rows exist only when the installation does\n * not already provide the capability.\n */\nexport function deriveSetupChecklist({\n readiness,\n installationRunners,\n workspaceRunnerCapacity,\n modelProvider,\n membership,\n}: SetupChecklistInput): SetupChecklist {\n const items: SetupChecklistItem[] = [\n {id: 'source-control', title: 'Connect source control', status: 'done', tracked: true},\n {id: 'project', title: 'Create a project', status: 'done', tracked: true},\n {\n id: 'tools',\n title: toolsTitle(readiness),\n status: readiness.hasToolIntegration ? 'done' : 'open',\n tracked: true,\n purpose: TOOLS_PURPOSE,\n action: {label: 'Connect', href: '/settings/integrations'},\n },\n ];\n\n if (installationRunners === 'none') {\n items.push({\n id: 'runner',\n title: 'Set up runner capacity',\n status: workspaceRunnerCapacity ? 'done' : 'open',\n tracked: true,\n purpose: RUNNER_PURPOSE,\n action: {label: 'Set up', href: '/settings/runners'},\n });\n }\n\n if (!modelProvider.installationProvided) {\n items.push({\n id: 'model-provider',\n title: 'Configure a model provider',\n status: modelProvider.configured ? 'done' : 'open',\n tracked: true,\n purpose: MODEL_PROVIDER_PURPOSE,\n action: {label: 'Configure', href: '/settings/agents'},\n });\n }\n\n items.push(\n {\n id: 'first-workflow',\n title: 'Push your first workflow',\n status: 'info',\n tracked: false,\n purpose: FIRST_WORKFLOW_PURPOSE,\n action: {label: 'Read the quickstart', href: '/docs/getting-started'},\n },\n {\n id: 'teammates',\n title: 'Invite your teammates',\n status:\n membership.memberCount >= 2 || membership.pendingInvitationCount >= 1 ? 'done' : 'info',\n tracked: false,\n purpose: TEAMMATES_PURPOSE,\n action: {label: 'Invite', href: '/settings/members'},\n },\n );\n\n const trackedItems = items.filter((item) => item.tracked);\n const openCount = trackedItems.filter((item) => item.status === 'open').length;\n\n return {\n items,\n openCount,\n trackedCount: trackedItems.length,\n complete: openCount === 0,\n };\n}\n\nfunction toolsTitle(readiness: WorkspaceIntegrationReadiness): string {\n if (!readiness.hasToolIntegration) {\n const attentionToolProviders = readiness.attentionProviders\n .map((providerKey) => readiness.providers.find(({provider}) => provider === providerKey))\n .filter(\n (provider) => provider !== undefined && !provider.capabilities.includes('source_control'),\n );\n if (attentionToolProviders.length === 1) {\n const provider = attentionToolProviders[0];\n if (provider !== undefined) {\n return `${provider.displayName || provider.provider} needs attention`;\n }\n }\n if (attentionToolProviders.length > 1) {\n return `${attentionToolProviders.length} integrations need attention`;\n }\n }\n return TOOLS_TITLE;\n}\n"],"names":["TOOLS_TITLE","TOOLS_PURPOSE","RUNNER_PURPOSE","MODEL_PROVIDER_PURPOSE","FIRST_WORKFLOW_PURPOSE","TEAMMATES_PURPOSE","deriveSetupChecklist","readiness","installationRunners","workspaceRunnerCapacity","modelProvider","membership","items","id","title","status","tracked","toolsTitle","hasToolIntegration","purpose","action","label","href","push","installationProvided","configured","memberCount","pendingInvitationCount","trackedItems","filter","item","openCount","length","trackedCount","complete","attentionToolProviders","attentionProviders","map","providerKey","providers","find","provider","undefined","capabilities","includes","displayName"],"mappings":"AAiDA,MAAMA,cAAc;AACpB,MAAMC,gBACJ;AACF,MAAMC,iBAAiB;AACvB,MAAMC,yBAAyB;AAC/B,MAAMC,yBACJ;AACF,MAAMC,oBAAoB;AAE1B;;;;;CAKC,GACD,OAAO,SAASC,qBAAqB,EACnCC,SAAS,EACTC,mBAAmB,EACnBC,uBAAuB,EACvBC,aAAa,EACbC,UAAU,EACU;IACpB,MAAMC,QAA8B;QAClC;YAACC,IAAI;YAAkBC,OAAO;YAA0BC,QAAQ;YAAQC,SAAS;QAAI;QACrF;YAACH,IAAI;YAAWC,OAAO;YAAoBC,QAAQ;YAAQC,SAAS;QAAI;QACxE;YACEH,IAAI;YACJC,OAAOG,WAAWV;YAClBQ,QAAQR,UAAUW,kBAAkB,GAAG,SAAS;YAChDF,SAAS;YACTG,SAASlB;YACTmB,QAAQ;gBAACC,OAAO;gBAAWC,MAAM;YAAwB;QAC3D;KACD;IAED,IAAId,wBAAwB,QAAQ;QAClCI,MAAMW,IAAI,CAAC;YACTV,IAAI;YACJC,OAAO;YACPC,QAAQN,0BAA0B,SAAS;YAC3CO,SAAS;YACTG,SAASjB;YACTkB,QAAQ;gBAACC,OAAO;gBAAUC,MAAM;YAAmB;QACrD;IACF;IAEA,IAAI,CAACZ,cAAcc,oBAAoB,EAAE;QACvCZ,MAAMW,IAAI,CAAC;YACTV,IAAI;YACJC,OAAO;YACPC,QAAQL,cAAce,UAAU,GAAG,SAAS;YAC5CT,SAAS;YACTG,SAAShB;YACTiB,QAAQ;gBAACC,OAAO;gBAAaC,MAAM;YAAkB;QACvD;IACF;IAEAV,MAAMW,IAAI,CACR;QACEV,IAAI;QACJC,OAAO;QACPC,QAAQ;QACRC,SAAS;QACTG,SAASf;QACTgB,QAAQ;YAACC,OAAO;YAAuBC,MAAM;QAAuB;IACtE,GACA;QACET,IAAI;QACJC,OAAO;QACPC,QACEJ,WAAWe,WAAW,IAAI,KAAKf,WAAWgB,sBAAsB,IAAI,IAAI,SAAS;QACnFX,SAAS;QACTG,SAASd;QACTe,QAAQ;YAACC,OAAO;YAAUC,MAAM;QAAmB;IACrD;IAGF,MAAMM,eAAehB,MAAMiB,MAAM,CAAC,CAACC,OAASA,KAAKd,OAAO;IACxD,MAAMe,YAAYH,aAAaC,MAAM,CAAC,CAACC,OAASA,KAAKf,MAAM,KAAK,QAAQiB,MAAM;IAE9E,OAAO;QACLpB;QACAmB;QACAE,cAAcL,aAAaI,MAAM;QACjCE,UAAUH,cAAc;IAC1B;AACF;AAEA,SAASd,WAAWV,SAAwC;IAC1D,IAAI,CAACA,UAAUW,kBAAkB,EAAE;QACjC,MAAMiB,yBAAyB5B,UAAU6B,kBAAkB,CACxDC,GAAG,CAAC,CAACC,cAAgB/B,UAAUgC,SAAS,CAACC,IAAI,CAAC,CAAC,EAACC,QAAQ,EAAC,GAAKA,aAAaH,cAC3ET,MAAM,CACL,CAACY,WAAaA,aAAaC,aAAa,CAACD,SAASE,YAAY,CAACC,QAAQ,CAAC;QAE5E,IAAIT,uBAAuBH,MAAM,KAAK,GAAG;YACvC,MAAMS,WAAWN,sBAAsB,CAAC,EAAE;YAC1C,IAAIM,aAAaC,WAAW;gBAC1B,OAAO,GAAGD,SAASI,WAAW,IAAIJ,SAASA,QAAQ,CAAC,gBAAgB,CAAC;YACvE;QACF;QACA,IAAIN,uBAAuBH,MAAM,GAAG,GAAG;YACrC,OAAO,GAAGG,uBAAuBH,MAAM,CAAC,4BAA4B,CAAC;QACvE;IACF;IACA,OAAOhC;AACT"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
+ export { deriveIntegrationReadiness, type IntegrationProviderReadiness, type IntegrationReadinessInput, type WorkspaceIntegrationReadiness, } from '#core/integration-readiness.js';
2
+ export { deriveSetupChecklist, type SetupChecklist, type SetupChecklistAction, type SetupChecklistInput, type SetupChecklistItem, type SetupChecklistItemId, type SetupChecklistItemStatus, } from '#core/setup-checklist.js';
1
3
  export { loadWorkspaceSetupRoute, type WorkspaceSetupRouteOptions, } from './workspace-setup-route.js';
2
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,KAAK,0BAA0B,GAChC,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,0BAA0B,EAC1B,KAAK,4BAA4B,EACjC,KAAK,yBAAyB,EAC9B,KAAK,6BAA6B,GACnC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,oBAAoB,EACpB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,GAC9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,uBAAuB,EACvB,KAAK,0BAA0B,GAChC,MAAM,4BAA4B,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ export { deriveIntegrationReadiness } from '#core/integration-readiness.js';
2
+ export { deriveSetupChecklist } from '#core/setup-checklist.js';
1
3
  export { loadWorkspaceSetupRoute } from './workspace-setup-route.js';
2
4
 
3
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {\n loadWorkspaceSetupRoute,\n type WorkspaceSetupRouteOptions,\n} from './workspace-setup-route.js';\n"],"names":["loadWorkspaceSetupRoute"],"mappings":"AAAA,SACEA,uBAAuB,QAElB,6BAA6B"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export {\n deriveIntegrationReadiness,\n type IntegrationProviderReadiness,\n type IntegrationReadinessInput,\n type WorkspaceIntegrationReadiness,\n} from '#core/integration-readiness.js';\nexport {\n deriveSetupChecklist,\n type SetupChecklist,\n type SetupChecklistAction,\n type SetupChecklistInput,\n type SetupChecklistItem,\n type SetupChecklistItemId,\n type SetupChecklistItemStatus,\n} from '#core/setup-checklist.js';\nexport {\n loadWorkspaceSetupRoute,\n type WorkspaceSetupRouteOptions,\n} from './workspace-setup-route.js';\n"],"names":["deriveIntegrationReadiness","deriveSetupChecklist","loadWorkspaceSetupRoute"],"mappings":"AAAA,SACEA,0BAA0B,QAIrB,iCAAiC;AACxC,SACEC,oBAAoB,QAOf,2BAA2B;AAClC,SACEC,uBAAuB,QAElB,6BAA6B"}