@uniqbit/mate-core 0.18.4 → 1.0.0-canary.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.
- package/claude-plugin/hooks/companion-instructions.mjs +9 -0
- package/claude-plugin/hooks/hooks.json +12 -2
- package/opencode-plugin/src/add-dir.ts +76 -10
- package/opencode-plugin/src/companion.ts +1 -8
- package/opencode-plugin/src/server.ts +2 -15
- package/package.json +10 -2
- package/src/brand/art.generated.ts +69 -0
- package/src/brand/index.ts +12 -0
- package/src/brand/palette.ts +19 -0
- package/src/brand/svg.generated.ts +8 -0
- package/src/brand/terminal.ts +83 -0
- package/src/brand/types.ts +33 -0
- package/src/cli/commands/artifact/artifact.ts +8 -0
- package/src/cli/commands/artifact/delivery/archive.ts +117 -0
- package/src/cli/commands/artifact/delivery/branch-flags.ts +55 -0
- package/src/cli/commands/artifact/delivery/environment.ts +104 -0
- package/src/cli/commands/artifact/delivery/link.ts +154 -0
- package/src/cli/commands/artifact/delivery/output.ts +58 -0
- package/src/cli/commands/artifact/finish/command.ts +33 -3
- package/src/cli/commands/artifact/finish/engine.ts +47 -3
- package/src/cli/commands/artifact/finish/finisher.ts +10 -0
- package/src/cli/commands/artifact/finish/openspec.ts +200 -129
- package/src/cli/commands/artifact/finish/secret-scan.ts +43 -0
- package/src/cli/commands/artifact/pending/command.ts +31 -3
- package/src/cli/commands/artifact/pending/discovery.ts +21 -57
- package/src/cli/commands/cap/graphify.ts +52 -3
- package/src/cli/commands/cap/index-cmd.ts +8 -1
- package/src/cli/commands/cap/openspec.ts +13 -25
- package/src/cli/commands/companion/open.ts +56 -39
- package/src/cli/commands/companion/update.ts +75 -42
- package/src/cli/commands/doctor.ts +257 -78
- package/src/cli/commands/install.ts +0 -8
- package/src/cli/commands/plugin/install.ts +9 -4
- package/src/cli/commands/plugin/plugin.ts +3 -2
- package/src/cli/commands/report/adapter.ts +11 -2
- package/src/cli/commands/report/contract.ts +21 -0
- package/src/cli/commands/report/highlight.ts +34 -4
- package/src/cli/commands/report/markdown.ts +110 -0
- package/src/cli/commands/report/palette.ts +118 -0
- package/src/cli/commands/report/renderer.ts +456 -153
- package/src/cli/commands/report/types.ts +16 -0
- package/src/cli/commands/setup.ts +7 -15
- package/src/cli/commands/studio/client-asset.ts +16 -0
- package/src/cli/commands/studio/openspec-cli.ts +10 -58
- package/src/cli/commands/studio/routes.ts +2 -0
- package/src/cli/commands/studio/selection.ts +19 -1
- package/src/cli/commands/studio/server.ts +42 -1
- package/src/cli/commands/studio/terminal-launch.ts +7 -16
- package/src/cli/commands/studio/views/client.ts +6 -616
- package/src/cli/commands/studio/views/dashboard/changes.tsx +5 -10
- package/src/cli/commands/studio/views/document.tsx +137 -69
- package/src/cli/commands/studio/views/markdown.tsx +250 -0
- package/src/cli/commands/studio/views/palette.ts +134 -0
- package/src/cli/commands/studio/views/reports/index.tsx +2 -7
- package/src/cli/commands/studio/views/skills/index.tsx +43 -62
- package/src/cli/commands/studio/views/specs/index.tsx +7 -15
- package/src/cli/commands/studio/views/studio-client/actions.ts +51 -0
- package/src/cli/commands/studio/views/studio-client/companion.ts +13 -0
- package/src/cli/commands/studio/views/studio-client/index.ts +32 -0
- package/src/cli/commands/studio/views/studio-client/keys.ts +3 -0
- package/src/cli/commands/studio/views/studio-client/main.ts +3 -0
- package/src/cli/commands/studio/views/studio-client/page.ts +146 -0
- package/src/cli/commands/studio/views/studio-client/panels.ts +57 -0
- package/src/cli/commands/studio/views/studio-client/reports.ts +122 -0
- package/src/cli/commands/studio/views/studio-client/theme.ts +63 -0
- package/src/cli/commands/studio/views/studio-client/vault-editor.ts +166 -0
- package/src/cli/commands/studio/views/studio-client/vault-pane.ts +202 -0
- package/src/cli/commands/studio/views/studio-client/vault-tree.ts +258 -0
- package/src/cli/commands/studio/views/studio-client.generated.ts +2 -0
- package/src/cli/commands/studio/views/styles.ts +217 -206
- package/src/cli/commands/studio/views/vault/state.ts +3 -5
- package/src/cli/commands/studio/views/workflow/delivery-path.tsx +370 -0
- package/src/cli/commands/studio/views/workflow/graph.ts +220 -0
- package/src/cli/commands/studio/views/workflow/index.tsx +2 -3
- package/src/cli/commands/studio/views/workflow/steps.ts +228 -287
- package/src/cli/commands/wrap.ts +9 -42
- package/src/cli/main.ts +4 -1
- package/src/cli/parse-flags.ts +25 -7
- package/src/cli/setup-selector.tsx +38 -162
- package/src/cli/usage.ts +5 -2
- package/src/hooks/companion-instructions.ts +190 -0
- package/src/hooks/hook-io.ts +63 -0
- package/src/hooks/session-banner.ts +21 -29
- package/src/hooks/session-guidance.ts +10 -36
- package/src/hooks/session-prepare.ts +115 -28
- package/src/hooks/validate-artifact-path.ts +82 -24
- package/src/index.ts +4 -0
- package/src/lib/change-layout.ts +154 -0
- package/src/lib/components/wizard-header.tsx +3 -1
- package/src/lib/context-mode-package.ts +24 -11
- package/src/lib/delivery/archive-change.ts +154 -0
- package/src/lib/delivery/branches.ts +108 -0
- package/src/lib/delivery/environment.ts +29 -0
- package/src/lib/delivery/forge.ts +279 -0
- package/src/lib/delivery/overlap.ts +202 -0
- package/src/lib/delivery/record-delivery.ts +196 -0
- package/src/lib/delivery/record.ts +343 -0
- package/src/lib/delivery/refresh.ts +174 -0
- package/src/lib/delivery/repository-access.ts +97 -0
- package/src/lib/delivery/stages.ts +111 -0
- package/src/lib/delivery/token.ts +58 -0
- package/src/lib/frontmatter.ts +18 -0
- package/src/lib/opencode-plugin-package.ts +5 -5
- package/src/lib/openspec-cli.ts +116 -0
- package/src/lib/openspec-scopes.ts +86 -0
- package/src/lib/orchestrator/adapters/claude-args.ts +50 -0
- package/src/lib/orchestrator/adapters/claude.ts +60 -162
- package/src/lib/orchestrator/adapters/opencode.ts +9 -24
- package/src/lib/orchestrator/config-store.ts +2 -70
- package/src/lib/orchestrator/editor.ts +9 -9
- package/src/lib/orchestrator/openspec-store.ts +14 -54
- package/src/lib/orchestrator/projection-claude-commands.ts +36 -0
- package/src/lib/orchestrator/projection-claude-links.ts +159 -0
- package/src/lib/orchestrator/projection-claude-skills.ts +19 -190
- package/src/lib/orchestrator/projection-entries.ts +45 -15
- package/src/lib/orchestrator/projection-runtime-documents.ts +15 -6
- package/src/lib/orchestrator/projection-types.ts +3 -2
- package/src/lib/orchestrator/refresh-working-claude-links.ts +87 -0
- package/src/lib/orchestrator/session-prepare.ts +49 -87
- package/src/lib/orchestrator/setup-compatibilities.ts +18 -75
- package/src/lib/orchestrator/types.ts +2 -2
- package/src/lib/orchestrator/wrap-pass.ts +22 -88
- package/src/lib/package-paths.ts +8 -0
- package/src/opencode/companion-hooks.ts +64 -5
- package/src/opencode/companion-policy.ts +37 -18
- package/src/opencode/projected-guidance.ts +4 -1
- package/src/opencode/session-prepare.ts +2 -1
- package/src/opencode/tui.tsx +4 -2
- package/src/runtime/artifact-refusal.ts +63 -0
- package/src/runtime/claude-delivery-plan.ts +89 -7
- package/src/runtime/commit-attribution-guard.ts +181 -0
- package/src/runtime/companion-guidance.ts +15 -3
- package/src/runtime/companion-instructions.ts +340 -0
- package/src/runtime/error-message.ts +4 -0
- package/src/runtime/guidance.ts +0 -17
- package/src/runtime/index.ts +5 -0
- package/src/runtime/managed-block.ts +117 -0
- package/src/runtime/markdown-scan.ts +266 -0
- package/src/runtime/policy.ts +16 -15
- package/src/runtime/projected-flag.ts +7 -0
- package/src/runtime/projected-guidance.ts +1 -0
- package/src/runtime/runtime-document-manifest.ts +62 -28
- package/src/runtime/secret-guard.ts +148 -0
- package/src/runtime/secret-patterns.ts +164 -0
- package/src/runtime/secret-scan.ts +90 -0
- package/src/runtime/session-kind.ts +13 -0
- package/src/runtime/shell-heredoc.ts +81 -0
- package/src/runtime/wrap-state.ts +36 -6
- package/src/runtime/write-atomic.ts +64 -0
- package/src/templates/capabilities/openspec-cap/mate-minimal/schema.yaml +8 -6
- package/src/templates/capabilities/openspec-cap/mate-minimal/templates/spec.md +3 -5
- package/src/templates/capabilities/openspec-cap/mate-v1/schema.yaml +7 -9
- package/src/templates/capabilities/openspec-cap/mate-v1/templates/spec.md +3 -5
- package/src/templates/capabilities/openspec-cap/openspec-conventions.yaml +10 -12
- package/src/templates/mate-skills/THIRD-PARTY-NOTICES.md +40 -0
- package/src/templates/mate-skills/agents/mate-apply-change/SKILL.md +76 -0
- package/src/templates/mate-skills/agents/mate-apply-change/references/openspec-apply-change.md +198 -0
- package/src/templates/mate-skills/agents/mate-archive-change/SKILL.md +59 -0
- package/src/templates/mate-skills/{claude/mate-openspec-backfill → agents/mate-backfill-spec}/SKILL.md +4 -4
- package/src/templates/mate-skills/agents/mate-clarify-intent/SKILL.md +95 -0
- package/src/templates/mate-skills/agents/mate-explore-change/SKILL.md +50 -0
- package/src/templates/mate-skills/agents/mate-explore-change/references/openspec-explore.md +348 -0
- package/src/templates/mate-skills/agents/mate-grill-me/SKILL.md +3 -3
- package/src/templates/mate-skills/agents/mate-interview-me/SKILL.md +5 -5
- package/src/templates/mate-skills/agents/mate-propose-change/SKILL.md +103 -0
- package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-propose.md +167 -0
- package/src/templates/mate-skills/agents/mate-propose-change/references/openspec-update-change.md +101 -0
- package/src/templates/mate-skills/agents/{mate-artifact-publish → mate-publish-artifact}/SKILL.md +91 -23
- package/src/templates/mate-skills/agents/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
- package/src/templates/mate-skills/agents/mate-show-me/SKILL.md +74 -16
- package/src/templates/mate-skills/agents/mate-simplify-code/SKILL.md +74 -68
- package/src/templates/mate-skills/claude/mate-apply-change/SKILL.md +76 -0
- package/src/templates/mate-skills/claude/mate-apply-change/references/openspec-apply-change.md +198 -0
- package/src/templates/mate-skills/claude/mate-archive-change/SKILL.md +59 -0
- package/src/templates/mate-skills/{agents/mate-openspec-backfill → claude/mate-backfill-spec}/SKILL.md +4 -4
- package/src/templates/mate-skills/claude/mate-clarify-intent/SKILL.md +95 -0
- package/src/templates/mate-skills/claude/mate-explore-change/SKILL.md +50 -0
- package/src/templates/mate-skills/claude/mate-explore-change/references/openspec-explore.md +348 -0
- package/src/templates/mate-skills/claude/mate-grill-me/SKILL.md +3 -3
- package/src/templates/mate-skills/claude/mate-interview-me/SKILL.md +5 -5
- package/src/templates/mate-skills/claude/mate-propose-change/SKILL.md +103 -0
- package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-propose.md +167 -0
- package/src/templates/mate-skills/claude/mate-propose-change/references/openspec-update-change.md +101 -0
- package/src/templates/mate-skills/claude/{mate-artifact-publish → mate-publish-artifact}/SKILL.md +91 -23
- package/src/templates/mate-skills/claude/{mate-artifact-publish → mate-publish-artifact}/references/openspec.md +4 -3
- package/src/templates/mate-skills/claude/mate-show-me/SKILL.md +74 -16
- package/src/templates/mate-skills/claude/mate-simplify-code/SKILL.md +74 -68
- package/src/templates/mate-skills/openspec-references.ts +140 -0
- package/src/templates/root/TEMPLATE_AGENTS.md +2 -2
- package/src/tools/setup/__snapshots__/runtime-surface-golden.test.ts.snap +3727 -320
- package/src/tools/setup/capabilities/context-mode.ts +25 -5
- package/src/tools/setup/capabilities/graphify-gate.ts +31 -0
- package/src/tools/setup/capabilities/graphify-secrets.ts +255 -0
- package/src/tools/setup/capabilities/graphify.ts +12 -0
- package/src/tools/setup/capabilities/openspec.ts +273 -270
- package/src/tools/setup/capabilities/tokensave.ts +36 -3
- package/src/tools/setup/context-services.ts +9 -17
- package/src/tools/setup/dynamic-plugins/audiences.ts +7 -39
- package/src/tools/setup/dynamic-plugins/declarations.ts +1 -1
- package/src/tools/setup/dynamic-plugins/hydrate.ts +4 -7
- package/src/tools/setup/dynamic-plugins/install.ts +24 -28
- package/src/tools/setup/engine.ts +93 -148
- package/src/tools/setup/managed-output.ts +51 -0
- package/src/tools/setup/mate.ts +86 -11
- package/src/tools/setup/plugin.ts +37 -17
- package/src/tools/setup/plugins/gitignore.ts +1 -7
- package/src/tools/setup/plugins/guidance.ts +7 -4
- package/src/tools/setup/providers/agent-file-sections.ts +4 -2
- package/src/tools/setup/providers/claude-attribution.ts +65 -0
- package/src/tools/setup/providers/claude-plugin-folders.ts +625 -0
- package/src/tools/setup/providers/claude-plugin-hooks.ts +39 -19
- package/src/tools/setup/providers/claude.ts +285 -34
- package/src/tools/setup/providers/opencode.ts +180 -11
- package/src/tools/setup/registry.ts +2 -24
- package/src/tools/setup/surface-target.ts +4 -13
- package/src/tools/setup/utils.ts +14 -4
- package/src/tools/setup.ts +131 -18
- package/wrappers/bin/graphify +52 -4
- package/wrappers/bin/openspec +33 -9
- package/wrappers/lib/graphify-gate.mjs +8 -0
- package/opencode-plugin/src/audience-layer.ts +0 -91
- package/src/cli/commands/studio/views/workflow/transcript.tsx +0 -190
- package/src/runtime/audience-layer.ts +0 -84
- package/src/runtime/opencode-audience-layer.ts +0 -176
- package/src/tools/setup/audience-isolation.fixture.ts +0 -325
- package/src/tools/setup/audience-layer.ts +0 -207
- package/src/tools/setup/audience-registrations.ts +0 -52
- package/src/tools/setup/contribution-ledger.ts +0 -207
- package/src/tools/setup/isolation-diagnostics.ts +0 -139
- package/src/tools/setup/runtime-documents.ts +0 -291
- package/src/tools/setup/upgrade-sweep.ts +0 -153
|
@@ -6,15 +6,13 @@ export async function vaultState(
|
|
|
6
6
|
manager: VaultManager,
|
|
7
7
|
companionPath: string,
|
|
8
8
|
sel: StudioVaultSelection,
|
|
9
|
-
options: { tree: "prefetch" | "await" },
|
|
9
|
+
options: { tree: "prefetch" | "await" | "skip" },
|
|
10
10
|
): Promise<StudioVaultPage> {
|
|
11
11
|
let tree: VaultTreeResult | null = null;
|
|
12
12
|
let failure: string | null = null;
|
|
13
13
|
try {
|
|
14
|
-
tree =
|
|
15
|
-
|
|
16
|
-
? await manager.prefetch(companionPath, sel.refresh)
|
|
17
|
-
: await manager.tree(companionPath, sel.refresh);
|
|
14
|
+
if (options.tree === "prefetch") tree = await manager.prefetch(companionPath, sel.refresh);
|
|
15
|
+
else if (options.tree === "await") tree = await manager.tree(companionPath, sel.refresh);
|
|
18
16
|
} catch (error) {
|
|
19
17
|
failure = error instanceof Error ? error.message : String(error);
|
|
20
18
|
}
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/** @jsxImportSource hono/jsx */
|
|
2
|
+
|
|
3
|
+
import type { StudioCompanionPayload } from "../../payload";
|
|
4
|
+
import {
|
|
5
|
+
bandSpan,
|
|
6
|
+
edgeMidpoint,
|
|
7
|
+
edgePath,
|
|
8
|
+
graphAcross,
|
|
9
|
+
nodeCenter,
|
|
10
|
+
percentOf,
|
|
11
|
+
workflowGraph,
|
|
12
|
+
type WorkflowGraph,
|
|
13
|
+
} from "./graph";
|
|
14
|
+
import {
|
|
15
|
+
PHASE_LABELS,
|
|
16
|
+
workflowPlan,
|
|
17
|
+
type WorkflowPath,
|
|
18
|
+
type WorkflowStep,
|
|
19
|
+
type WorkflowStepBadge,
|
|
20
|
+
} from "./steps";
|
|
21
|
+
|
|
22
|
+
export function DeliveryPathView({ payload }: { payload: StudioCompanionPayload }) {
|
|
23
|
+
const plan = workflowPlan({
|
|
24
|
+
skills: payload.skills && payload.skills.length > 0 ? payload.skills : undefined,
|
|
25
|
+
topology: payload.topology,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<section className="workflow-delivery-path" aria-label="Delivery Path">
|
|
30
|
+
{plan.paths.length > 1 ? (
|
|
31
|
+
<div className="workflow-schema" data-workflow-schema-root>
|
|
32
|
+
<label className="view-choice">
|
|
33
|
+
<span>OpenSpec schema</span>
|
|
34
|
+
<select data-workflow-schema-select>
|
|
35
|
+
{plan.paths.map((path, index) => (
|
|
36
|
+
<option key={path.schema} value={path.schema} selected={index === 0}>
|
|
37
|
+
{path.label} – {path.tagline}
|
|
38
|
+
</option>
|
|
39
|
+
))}
|
|
40
|
+
</select>
|
|
41
|
+
</label>
|
|
42
|
+
<div className="workflow-schema-panels">
|
|
43
|
+
{plan.paths.map((path, index) => (
|
|
44
|
+
<section
|
|
45
|
+
key={path.schema}
|
|
46
|
+
data-workflow-schema-panel={path.schema}
|
|
47
|
+
data-active={index === 0 ? "true" : "false"}
|
|
48
|
+
>
|
|
49
|
+
<PathBody path={path} />
|
|
50
|
+
</section>
|
|
51
|
+
))}
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
) : (
|
|
55
|
+
<div className="workflow-schema">
|
|
56
|
+
<p className="view-choice">
|
|
57
|
+
<span>OpenSpec schema</span>
|
|
58
|
+
<code>
|
|
59
|
+
{plan.paths[0].label} – {plan.paths[0].tagline}
|
|
60
|
+
</code>
|
|
61
|
+
</p>
|
|
62
|
+
<PathBody path={plan.paths[0]} />
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
</section>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The graph is the path; the side panel shows one stage at a time. A node link targets its
|
|
71
|
+
* card, and CSS shows only the targeted card, so selection needs no script. With nothing
|
|
72
|
+
* selected the panel shows the path overview.
|
|
73
|
+
*/
|
|
74
|
+
function PathBody({ path }: { path: WorkflowPath }) {
|
|
75
|
+
return (
|
|
76
|
+
<div className="workflow-path">
|
|
77
|
+
<div className="workflow-path-graph">
|
|
78
|
+
<WorkflowGraphView path={path} />
|
|
79
|
+
<GraphLegend />
|
|
80
|
+
</div>
|
|
81
|
+
<aside className="workflow-detail" aria-label={`${path.label} stage details`}>
|
|
82
|
+
<div className="workflow-detail-overview" id={overviewId(path)}>
|
|
83
|
+
<p className="workflow-description">{path.description}</p>
|
|
84
|
+
{!path.reviewsKnown ? (
|
|
85
|
+
<p className="workflow-description">
|
|
86
|
+
The schema's artifacts could not be read, so its review stages are unknown.
|
|
87
|
+
</p>
|
|
88
|
+
) : null}
|
|
89
|
+
<p className="workflow-description">
|
|
90
|
+
A workset is one or more changes published together: after Archive change, start again
|
|
91
|
+
at the top for the next change, and publish once they are archived.
|
|
92
|
+
</p>
|
|
93
|
+
<p className="workflow-detail-hint">
|
|
94
|
+
Select a node in the graph to see what that stage does and the prompt that runs it.
|
|
95
|
+
</p>
|
|
96
|
+
</div>
|
|
97
|
+
{path.steps.map((step) => (
|
|
98
|
+
<StageCard key={step.id} path={path} step={step} />
|
|
99
|
+
))}
|
|
100
|
+
</aside>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const cardId = (path: WorkflowPath, step: WorkflowStep): string =>
|
|
106
|
+
`workflow-${path.schema}-${step.id}`;
|
|
107
|
+
|
|
108
|
+
const overviewId = (path: WorkflowPath): string => `workflow-${path.schema}-overview`;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Node links are the only announced part: their document order is the stage order. Bands,
|
|
112
|
+
* edges, and marks are decorative and hidden from assistive technology.
|
|
113
|
+
*/
|
|
114
|
+
function WorkflowGraphView({ path }: { path: WorkflowPath }) {
|
|
115
|
+
const graph = workflowGraph(path);
|
|
116
|
+
return (
|
|
117
|
+
<nav
|
|
118
|
+
className="workflow-graph"
|
|
119
|
+
aria-label="Delivery Path map"
|
|
120
|
+
style={{ "--graph-columns": String(graph.columns), "--graph-rows": String(graph.rows) }}
|
|
121
|
+
>
|
|
122
|
+
<div className="workflow-graph-bands" aria-hidden="true">
|
|
123
|
+
{graph.bands.map((band) => (
|
|
124
|
+
<div
|
|
125
|
+
key={band.phase}
|
|
126
|
+
className="workflow-graph-band"
|
|
127
|
+
data-band={band.phase}
|
|
128
|
+
style={bandSpan(graph, band)}
|
|
129
|
+
>
|
|
130
|
+
{band.label}
|
|
131
|
+
</div>
|
|
132
|
+
))}
|
|
133
|
+
{graph.cycle ? (
|
|
134
|
+
<div className="workflow-graph-cycle" style={bandSpan(graph, graph.cycle)}>
|
|
135
|
+
<span>repeats for each change</span>
|
|
136
|
+
</div>
|
|
137
|
+
) : null}
|
|
138
|
+
<div className="workflow-graph-workset">
|
|
139
|
+
<span>workset</span>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
<GraphLayer graph={graph} />
|
|
143
|
+
<ol className="workflow-graph-nodes">
|
|
144
|
+
{graph.nodes.map((node) =>
|
|
145
|
+
node.kind === "start" ? null : (
|
|
146
|
+
<li
|
|
147
|
+
key={node.id}
|
|
148
|
+
className="workflow-graph-node"
|
|
149
|
+
data-node-kind={node.kind}
|
|
150
|
+
style={{
|
|
151
|
+
"--graph-row": String(node.column + 1),
|
|
152
|
+
"--graph-lane": String(node.row + 1),
|
|
153
|
+
"--graph-span": String(node.rowSpan),
|
|
154
|
+
}}
|
|
155
|
+
>
|
|
156
|
+
{node.kind === "stage" ? (
|
|
157
|
+
<NodeLink path={path} step={node.step} box />
|
|
158
|
+
) : (
|
|
159
|
+
<div className="workflow-graph-box workflow-graph-group" data-map-group="human">
|
|
160
|
+
<span className="workflow-graph-group-label">Human review</span>
|
|
161
|
+
<ol>
|
|
162
|
+
{node.steps.map((step) => (
|
|
163
|
+
<li key={step.id}>
|
|
164
|
+
<NodeLink path={path} step={step} />
|
|
165
|
+
</li>
|
|
166
|
+
))}
|
|
167
|
+
</ol>
|
|
168
|
+
</div>
|
|
169
|
+
)}
|
|
170
|
+
</li>
|
|
171
|
+
),
|
|
172
|
+
)}
|
|
173
|
+
</ol>
|
|
174
|
+
</nav>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Explains the graph's visual marks; the panel states the same facts per stage. */
|
|
179
|
+
function GraphLegend() {
|
|
180
|
+
return (
|
|
181
|
+
<ul className="workflow-graph-legend" aria-hidden="true">
|
|
182
|
+
<li>
|
|
183
|
+
<i className="workflow-legend-optional" />
|
|
184
|
+
optional
|
|
185
|
+
</li>
|
|
186
|
+
<li>
|
|
187
|
+
<i className="workflow-graph-openspec" />
|
|
188
|
+
OpenSpec
|
|
189
|
+
</li>
|
|
190
|
+
<li>
|
|
191
|
+
<i className="workflow-legend-session" />
|
|
192
|
+
new session
|
|
193
|
+
</li>
|
|
194
|
+
<li>
|
|
195
|
+
<i className="workflow-legend-skip" />
|
|
196
|
+
can be skipped
|
|
197
|
+
</li>
|
|
198
|
+
</ul>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function GraphLayer({ graph }: { graph: WorkflowGraph }) {
|
|
203
|
+
return (
|
|
204
|
+
<svg className="workflow-graph-layer" aria-hidden="true" focusable="false">
|
|
205
|
+
<svg
|
|
206
|
+
viewBox={`0 0 ${graphAcross(graph)} ${graph.columns}`}
|
|
207
|
+
preserveAspectRatio="none"
|
|
208
|
+
overflow="visible"
|
|
209
|
+
>
|
|
210
|
+
{graph.edges.map((edge) => (
|
|
211
|
+
<path
|
|
212
|
+
key={`${edge.kind}-${edge.from}-${edge.to}`}
|
|
213
|
+
className="workflow-graph-edge"
|
|
214
|
+
d={edgePath(graph, edge)}
|
|
215
|
+
data-edge-kind={edge.kind}
|
|
216
|
+
data-edge-from={edge.from}
|
|
217
|
+
data-edge-to={edge.to}
|
|
218
|
+
data-new-session={edge.newSession ? "true" : undefined}
|
|
219
|
+
/>
|
|
220
|
+
))}
|
|
221
|
+
</svg>
|
|
222
|
+
{graph.nodes.map((node) => {
|
|
223
|
+
if (node.kind !== "start") return null;
|
|
224
|
+
const { x, y } = percentOf(graph, nodeCenter(node));
|
|
225
|
+
return <circle key={node.id} className="workflow-graph-start" cx={x} cy={y} r="3.5" />;
|
|
226
|
+
})}
|
|
227
|
+
{graph.edges.map((edge) => {
|
|
228
|
+
if (!edge.newSession) return null;
|
|
229
|
+
const { x, y } = percentOf(graph, edgeMidpoint(graph, edge));
|
|
230
|
+
return (
|
|
231
|
+
<circle
|
|
232
|
+
key={`${edge.from}-${edge.to}`}
|
|
233
|
+
className="workflow-graph-session"
|
|
234
|
+
data-session-mark={edge.to}
|
|
235
|
+
cx={x}
|
|
236
|
+
cy={y}
|
|
237
|
+
r="3.5"
|
|
238
|
+
/>
|
|
239
|
+
);
|
|
240
|
+
})}
|
|
241
|
+
</svg>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** `box` marks a standalone node; links inside the review group share its box. */
|
|
246
|
+
function NodeLink({ path, step, box }: { path: WorkflowPath; step: WorkflowStep; box?: boolean }) {
|
|
247
|
+
return (
|
|
248
|
+
<a
|
|
249
|
+
className={box ? "workflow-graph-link workflow-graph-box" : "workflow-graph-link"}
|
|
250
|
+
href={`#${cardId(path, step)}`}
|
|
251
|
+
data-map-stage={step.id}
|
|
252
|
+
data-stage-kind={step.kind}
|
|
253
|
+
data-optional={step.optional ? "true" : undefined}
|
|
254
|
+
aria-label={step.kind === "human" ? step.label : undefined}
|
|
255
|
+
>
|
|
256
|
+
{/* The legend explains the dot; the panel names the badge in words. */}
|
|
257
|
+
{step.badges.includes("openspec") ? (
|
|
258
|
+
<span className="workflow-graph-openspec" aria-hidden="true" />
|
|
259
|
+
) : null}
|
|
260
|
+
{/* Inside the review group the group names the action, so only the artifact shows. */}
|
|
261
|
+
{step.kind === "human" ? step.artifact : step.label}
|
|
262
|
+
</a>
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function StageCard({ path, step }: { path: WorkflowPath; step: WorkflowStep }) {
|
|
267
|
+
return (
|
|
268
|
+
<article
|
|
269
|
+
className="workflow-card"
|
|
270
|
+
id={cardId(path, step)}
|
|
271
|
+
data-stage={step.id}
|
|
272
|
+
data-stage-kind={step.kind}
|
|
273
|
+
data-optional={step.optional ? "true" : undefined}
|
|
274
|
+
>
|
|
275
|
+
<header className="workflow-card-header">
|
|
276
|
+
<div className="workflow-card-heading">
|
|
277
|
+
<h3>{step.label}</h3>
|
|
278
|
+
<WorkflowBadges badges={shownBadges(step)} />
|
|
279
|
+
</div>
|
|
280
|
+
<p className="workflow-card-meta">
|
|
281
|
+
<span>{PHASE_LABELS[step.phase]}</span>
|
|
282
|
+
{step.skill ? <code>{step.skill}</code> : null}
|
|
283
|
+
{step.sessionBreakBefore ? (
|
|
284
|
+
<span className="workflow-card-session">New session / clean chat</span>
|
|
285
|
+
) : null}
|
|
286
|
+
</p>
|
|
287
|
+
</header>
|
|
288
|
+
<StageRun step={step} />
|
|
289
|
+
{step.missingSkill ? (
|
|
290
|
+
<p className="workflow-missing-skill" role="note">
|
|
291
|
+
Skill not reported for this companion: <code>{step.missingSkill}</code>. Run{" "}
|
|
292
|
+
<code>mate companion update</code>; this stage is not complete.
|
|
293
|
+
</p>
|
|
294
|
+
) : null}
|
|
295
|
+
<dl className="workflow-card-explanation">
|
|
296
|
+
<div>
|
|
297
|
+
<dt>What</dt>
|
|
298
|
+
<dd>{step.what}</dd>
|
|
299
|
+
</div>
|
|
300
|
+
<div>
|
|
301
|
+
<dt>Why</dt>
|
|
302
|
+
<dd>{step.why}</dd>
|
|
303
|
+
</div>
|
|
304
|
+
</dl>
|
|
305
|
+
<footer className="workflow-card-footer">
|
|
306
|
+
<a href={`#${overviewId(path)}`}>Path overview</a>
|
|
307
|
+
</footer>
|
|
308
|
+
</article>
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/** How a stage runs: a person's review, a choice of prompts, or its one prompt. */
|
|
313
|
+
function StageRun({ step }: { step: WorkflowStep }) {
|
|
314
|
+
if (step.kind === "human") {
|
|
315
|
+
return (
|
|
316
|
+
<p className="workflow-card-human">Review {step.artifact} yourself — no skill, no prompt</p>
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
if (step.alternatives?.length) return <WorkflowOptions step={step} />;
|
|
320
|
+
if (!step.prompt) return null;
|
|
321
|
+
return (
|
|
322
|
+
<div className="workflow-command">
|
|
323
|
+
<span className="workflow-command-label">Run</span>
|
|
324
|
+
<code>{step.prompt}</code>
|
|
325
|
+
<WorkflowCopy text={step.copyPrompt ?? step.prompt} label={`${step.label} prompt`} />
|
|
326
|
+
</div>
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function WorkflowOptions({ step }: { step: WorkflowStep }) {
|
|
331
|
+
if (!step.alternatives?.length) return null;
|
|
332
|
+
return (
|
|
333
|
+
<div className="workflow-option-list">
|
|
334
|
+
{step.alternatives.map((alternative) => (
|
|
335
|
+
<div key={alternative.name} className="workflow-option">
|
|
336
|
+
<div className="workflow-command">
|
|
337
|
+
<span className="workflow-command-label">Option</span>
|
|
338
|
+
<code>{alternative.prompt}</code>
|
|
339
|
+
<WorkflowCopy text={alternative.copyPrompt} label={`${alternative.name} prompt`} />
|
|
340
|
+
</div>
|
|
341
|
+
<small>{alternative.description}</small>
|
|
342
|
+
</div>
|
|
343
|
+
))}
|
|
344
|
+
</div>
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function WorkflowCopy({ text, label }: { text: string; label: string }) {
|
|
349
|
+
return (
|
|
350
|
+
<button type="button" className="runway-step-copy" data-copy={text} data-copy-label={label}>
|
|
351
|
+
Copy
|
|
352
|
+
</button>
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** The skill name already says a stage is a skill stage, so that badge is not shown. */
|
|
357
|
+
const shownBadges = (step: WorkflowStep): WorkflowStepBadge[] =>
|
|
358
|
+
step.badges.filter((badge) => badge !== "skill");
|
|
359
|
+
|
|
360
|
+
function WorkflowBadges({ badges }: { badges: WorkflowStepBadge[] }) {
|
|
361
|
+
return (
|
|
362
|
+
<span className="workflow-badges">
|
|
363
|
+
{badges.map((badge) => (
|
|
364
|
+
<em key={badge} className={`runway-step-badge runway-step-badge-${badge}`}>
|
|
365
|
+
{badge === "human-review" ? "Human review" : badge}
|
|
366
|
+
</em>
|
|
367
|
+
))}
|
|
368
|
+
</span>
|
|
369
|
+
);
|
|
370
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { PHASE_LABELS, type WorkflowPath, type WorkflowPhaseId, type WorkflowStep } from "./steps";
|
|
2
|
+
|
|
3
|
+
/** Slot on the graph grid: a column per sequential position, a row per stacked peer. */
|
|
4
|
+
interface GraphSlot {
|
|
5
|
+
column: number;
|
|
6
|
+
row: number;
|
|
7
|
+
rowSpan: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
type ColumnNode =
|
|
11
|
+
| { id: "start"; kind: "start" }
|
|
12
|
+
| { id: string; kind: "stage"; step: WorkflowStep }
|
|
13
|
+
| { id: "reviews"; kind: "review-group"; steps: WorkflowStep[] };
|
|
14
|
+
|
|
15
|
+
export type WorkflowGraphNode = GraphSlot & ColumnNode;
|
|
16
|
+
|
|
17
|
+
export interface WorkflowGraphEdge {
|
|
18
|
+
from: string;
|
|
19
|
+
to: string;
|
|
20
|
+
/** A bypass skips an optional stage or run of optional peers. */
|
|
21
|
+
kind: "flow" | "bypass";
|
|
22
|
+
/** The stage the edge enters starts a new session. */
|
|
23
|
+
newSession: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** A phase as a column range, `to` exclusive. */
|
|
27
|
+
export interface WorkflowGraphBand {
|
|
28
|
+
phase: WorkflowPhaseId;
|
|
29
|
+
label: string;
|
|
30
|
+
from: number;
|
|
31
|
+
to: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface WorkflowGraph {
|
|
35
|
+
columns: number;
|
|
36
|
+
/** Node rows; the bypass lane below them is not counted. */
|
|
37
|
+
rows: number;
|
|
38
|
+
/** Stage order: columns left to right, peers top to bottom. */
|
|
39
|
+
nodes: WorkflowGraphNode[];
|
|
40
|
+
edges: WorkflowGraphEdge[];
|
|
41
|
+
bands: WorkflowGraphBand[];
|
|
42
|
+
/** Columns each change of a feature repeats, from the start to Archive change; `to` exclusive. */
|
|
43
|
+
cycle: { from: number; to: number } | null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface Column {
|
|
47
|
+
nodes: ColumnNode[];
|
|
48
|
+
optional: boolean;
|
|
49
|
+
phase: WorkflowPhaseId;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const stageNode = (step: WorkflowStep): ColumnNode => ({ id: step.id, kind: "stage", step });
|
|
53
|
+
|
|
54
|
+
function columnsOf(path: WorkflowPath): Column[] {
|
|
55
|
+
const columns: Column[] = path.map.map((entry) => {
|
|
56
|
+
if (entry.kind === "stage") {
|
|
57
|
+
return {
|
|
58
|
+
nodes: [stageNode(entry.step)],
|
|
59
|
+
optional: entry.step.optional,
|
|
60
|
+
phase: entry.step.phase,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (entry.group === "human") {
|
|
64
|
+
return {
|
|
65
|
+
nodes: [{ id: "reviews", kind: "review-group", steps: entry.steps }],
|
|
66
|
+
optional: false,
|
|
67
|
+
phase: entry.steps[0].phase,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
return { nodes: entry.steps.map(stageNode), optional: true, phase: entry.steps[0].phase };
|
|
71
|
+
});
|
|
72
|
+
// The start marker anchors the bypass of a leading optional run and opens the cycle.
|
|
73
|
+
columns.unshift({
|
|
74
|
+
nodes: [{ id: "start", kind: "start" }],
|
|
75
|
+
optional: false,
|
|
76
|
+
phase: columns[0]?.phase ?? "plan",
|
|
77
|
+
});
|
|
78
|
+
return columns;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function entersNewSession(node: ColumnNode): boolean {
|
|
82
|
+
if (node.kind === "stage") return Boolean(node.step.sessionBreakBefore);
|
|
83
|
+
if (node.kind === "review-group") return Boolean(node.steps[0].sessionBreakBefore);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function edgesOf(columns: readonly Column[]): WorkflowGraphEdge[] {
|
|
88
|
+
const edges: WorkflowGraphEdge[] = [];
|
|
89
|
+
const link = (
|
|
90
|
+
from: readonly ColumnNode[],
|
|
91
|
+
to: readonly ColumnNode[],
|
|
92
|
+
kind: "flow" | "bypass",
|
|
93
|
+
) => {
|
|
94
|
+
for (const a of from) {
|
|
95
|
+
for (const b of to)
|
|
96
|
+
edges.push({ from: a.id, to: b.id, kind, newSession: entersNewSession(b) });
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
columns.forEach((column, index) => {
|
|
100
|
+
const next = columns[index + 1];
|
|
101
|
+
if (next) link(column.nodes, next.nodes, "flow");
|
|
102
|
+
const before = columns[index - 1];
|
|
103
|
+
if (column.optional && before && next) link(before.nodes, next.nodes, "bypass");
|
|
104
|
+
});
|
|
105
|
+
return edges;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function bandsOf(columns: readonly Column[]): WorkflowGraphBand[] {
|
|
109
|
+
const bands: WorkflowGraphBand[] = [];
|
|
110
|
+
columns.forEach((column, index) => {
|
|
111
|
+
const band = bands.at(-1);
|
|
112
|
+
if (band?.phase === column.phase) {
|
|
113
|
+
band.to = index + 1;
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
bands.push({
|
|
117
|
+
phase: column.phase,
|
|
118
|
+
label: PHASE_LABELS[column.phase],
|
|
119
|
+
from: index,
|
|
120
|
+
to: index + 1,
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
return bands;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Changes archive before they publish, so everything up to Archive change repeats per change. */
|
|
127
|
+
function cycleOf(columns: readonly Column[]): WorkflowGraph["cycle"] {
|
|
128
|
+
const archive = columns.findIndex((column) => column.nodes.some((node) => node.id === "archive"));
|
|
129
|
+
return archive === -1 ? null : { from: 0, to: archive + 1 };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** The path's node graph. Single nodes span every row; peers take a row each. */
|
|
133
|
+
export function workflowGraph(path: WorkflowPath): WorkflowGraph {
|
|
134
|
+
const columns = columnsOf(path);
|
|
135
|
+
const rows = Math.max(1, ...columns.map((column) => column.nodes.length));
|
|
136
|
+
const nodes = columns.flatMap((column, index) =>
|
|
137
|
+
column.nodes.map((node, row): WorkflowGraphNode =>
|
|
138
|
+
column.nodes.length === 1
|
|
139
|
+
? { ...node, column: index, row: 0, rowSpan: rows }
|
|
140
|
+
: { ...node, column: index, row, rowSpan: 1 },
|
|
141
|
+
),
|
|
142
|
+
);
|
|
143
|
+
return {
|
|
144
|
+
columns: columns.length,
|
|
145
|
+
rows,
|
|
146
|
+
nodes,
|
|
147
|
+
edges: edgesOf(columns),
|
|
148
|
+
bands: bandsOf(columns),
|
|
149
|
+
cycle: cycleOf(columns),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Drawing units, top to bottom: one per column down the flow; two per node row across it,
|
|
155
|
+
* plus one for the bypass lane on the right. Node rows are laid out at `2fr` and the lane
|
|
156
|
+
* at `1fr` to match.
|
|
157
|
+
*/
|
|
158
|
+
export interface GraphPoint {
|
|
159
|
+
along: number;
|
|
160
|
+
across: number;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export const graphAcross = (graph: WorkflowGraph): number => graph.rows * 2 + 1;
|
|
164
|
+
|
|
165
|
+
export function nodeCenter(node: WorkflowGraphNode): GraphPoint {
|
|
166
|
+
return { along: node.column + 0.5, across: node.row * 2 + node.rowSpan };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const round = (value: number): number => Math.round(value * 1e4) / 1e4;
|
|
170
|
+
|
|
171
|
+
const percent = (part: number, whole: number): string => `${round((part / whole) * 100)}%`;
|
|
172
|
+
|
|
173
|
+
/** An SVG `x y` pair: across is horizontal, along runs down. */
|
|
174
|
+
const pair = ({ along, across }: GraphPoint): string => `${round(across)} ${round(along)}`;
|
|
175
|
+
|
|
176
|
+
function endpoints(graph: WorkflowGraph, edge: WorkflowGraphEdge): [GraphPoint, GraphPoint] {
|
|
177
|
+
const at = (id: string): GraphPoint => {
|
|
178
|
+
const node = graph.nodes.find((candidate) => candidate.id === id);
|
|
179
|
+
if (!node) throw new Error(`Unknown graph node: ${id}`);
|
|
180
|
+
return nodeCenter(node);
|
|
181
|
+
};
|
|
182
|
+
return [at(edge.from), at(edge.to)];
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Center to center: nodes are opaque and drawn over the edges, so a line meets each border. */
|
|
186
|
+
export function edgePath(graph: WorkflowGraph, edge: WorkflowGraphEdge): string {
|
|
187
|
+
const [a, b] = endpoints(graph, edge);
|
|
188
|
+
if (edge.kind === "bypass") {
|
|
189
|
+
const lane = graphAcross(graph);
|
|
190
|
+
return `M ${pair(a)} C ${pair({ along: a.along, across: lane })}, ${pair({ along: b.along, across: lane })}, ${pair(b)}`;
|
|
191
|
+
}
|
|
192
|
+
const middle = (a.along + b.along) / 2;
|
|
193
|
+
return `M ${pair(a)} C ${pair({ along: middle, across: a.across })}, ${pair({ along: middle, across: b.across })}, ${pair(b)}`;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Midpoint of an edge's curve, where a new-session mark sits. */
|
|
197
|
+
export function edgeMidpoint(graph: WorkflowGraph, edge: WorkflowGraphEdge): GraphPoint {
|
|
198
|
+
const [a, b] = endpoints(graph, edge);
|
|
199
|
+
const along = (a.along + b.along) / 2;
|
|
200
|
+
if (edge.kind === "bypass") {
|
|
201
|
+
return { along, across: (a.across + b.across) / 8 + (graphAcross(graph) * 3) / 4 };
|
|
202
|
+
}
|
|
203
|
+
return { along, across: (a.across + b.across) / 2 };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** A point as percentages of the drawing, for undistorted marks. */
|
|
207
|
+
export function percentOf(graph: WorkflowGraph, point: GraphPoint): { x: string; y: string } {
|
|
208
|
+
return { x: percent(point.across, graphAcross(graph)), y: percent(point.along, graph.columns) };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** A column range's vertical extent as percentages of the drawing: a band or the cycle. */
|
|
212
|
+
export function bandSpan(
|
|
213
|
+
graph: WorkflowGraph,
|
|
214
|
+
band: { from: number; to: number },
|
|
215
|
+
): { top: string; height: string } {
|
|
216
|
+
return {
|
|
217
|
+
top: percent(band.from, graph.columns),
|
|
218
|
+
height: percent(band.to - band.from, graph.columns),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type { StudioCompanionPayload } from "../../payload";
|
|
4
4
|
import { Warnings } from "../warnings";
|
|
5
|
-
import {
|
|
5
|
+
import { DeliveryPathView } from "./delivery-path";
|
|
6
6
|
|
|
7
7
|
interface WorkflowProps {
|
|
8
8
|
payload: StudioCompanionPayload;
|
|
@@ -12,8 +12,7 @@ export function Workflow({ payload }: WorkflowProps) {
|
|
|
12
12
|
return (
|
|
13
13
|
<>
|
|
14
14
|
<div className="workflow-view">
|
|
15
|
-
<
|
|
16
|
-
<WorkflowTranscript payload={payload} />
|
|
15
|
+
<DeliveryPathView payload={payload} />
|
|
17
16
|
</div>
|
|
18
17
|
<Warnings warnings={payload.warnings} />
|
|
19
18
|
</>
|