@dfosco/storyboard 0.6.0-beta.3 → 0.6.0-beta.5
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/package.json +3 -2
- package/scaffold/skills/migrate/SKILL.md +68 -52
- package/src/core/canvas/agent-session.js +15 -6
- package/src/core/canvas/agent-session.test.js +16 -1
- package/src/core/stores/configSchema.js +1 -0
- package/src/internals/vite/data-plugin.js +126 -3
- package/terminal.config.json +48 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dfosco/storyboard",
|
|
3
|
-
"version": "0.6.0-beta.
|
|
3
|
+
"version": "0.6.0-beta.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Storyboard prototyping framework — core engine, React integration, and canvas",
|
|
@@ -21,7 +21,8 @@
|
|
|
21
21
|
"toolbar.config.json",
|
|
22
22
|
"widgets.config.json",
|
|
23
23
|
"paste.config.json",
|
|
24
|
-
"commandpalette.config.json"
|
|
24
|
+
"commandpalette.config.json",
|
|
25
|
+
"terminal.config.json"
|
|
25
26
|
],
|
|
26
27
|
"scripts": {
|
|
27
28
|
"build:css": "tailwindcss -i src/core/styles/tailwind.css -o dist/tailwind.css --minify",
|
|
@@ -37,65 +37,81 @@ The storyboard homepage URL changed from `/viewfinder` to `/workspace`. The old
|
|
|
37
37
|
|
|
38
38
|
#### 2. Canvas config — terminal + agents + hot pool
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
**As of `0.6.0-beta.4`, terminal + agent config has its own dedicated file: `terminal.config.json` at the project root.** The library ships full defaults in `node_modules/@dfosco/storyboard/terminal.config.json` and a copy is auto-scaffolded to `.storyboard/scaffold/terminal.config.json` on every dev-server boot. Most clients won't need any project-level config — the defaults already cover Copilot/Claude/Codex with auto-resume.
|
|
41
41
|
|
|
42
|
-
**
|
|
42
|
+
**Only create a root `terminal.config.json`** if you want to override specific keys. Leaf-level merge means you set only what you change; everything else inherits the library defaults (so future agents and tweaks reach you automatically). Example minimal override:
|
|
43
43
|
|
|
44
44
|
```jsonc
|
|
45
45
|
{
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"startupCommand":
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
46
|
+
"terminal": {
|
|
47
|
+
"fontSize": 18,
|
|
48
|
+
"fontFamily": "'Ghostty', 'SF Mono', monospace"
|
|
49
|
+
},
|
|
50
|
+
"agents": {
|
|
51
|
+
"copilot": {
|
|
52
|
+
"startupCommand": "copilot --remote --agent terminal-agent"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Legacy back-compat.** Existing clients with `canvas.terminal` and `canvas.agents` blocks under `storyboard.config.json` continue to work — the loader merges them with the new file (with `terminal.config.json` winning on overlap, and a warning logged). New clients should prefer `terminal.config.json` and keep `storyboard.config.json` lean.
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
"sessionIdEnv": "CODEX_SESSION_ID",
|
|
89
|
-
"sessionStateGlob": "~/.codex/sessions/**/rollout-*-{id}.jsonl",
|
|
90
|
-
"configFiles": [".codex/config.toml"],
|
|
91
|
-
"resizable": true
|
|
92
|
-
}
|
|
60
|
+
**Full reference for what `terminal.config.json` accepts** (don't copy this into a new project unless you actually need to override every key — the library ships these as defaults):
|
|
61
|
+
|
|
62
|
+
```jsonc
|
|
63
|
+
{
|
|
64
|
+
// Terminal widget settings (the plain terminal, not agents)
|
|
65
|
+
"terminal": {
|
|
66
|
+
"fontSize": 18,
|
|
67
|
+
"fontFamily": "'SF Mono', 'Menlo', 'Monaco', 'Courier New', monospace",
|
|
68
|
+
"prompt": "❯ ",
|
|
69
|
+
"startupCommand": null,
|
|
70
|
+
"defaultStartupSequence": null,
|
|
71
|
+
"resizable": true,
|
|
72
|
+
"defaultWidth": 1000,
|
|
73
|
+
"defaultHeight": 600
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
// Agent widgets — each key becomes an entry in the "Add Agent" menu
|
|
77
|
+
// Remove any agents the client doesn't have installed
|
|
78
|
+
"agents": {
|
|
79
|
+
"copilot": {
|
|
80
|
+
"label": "Copilot CLI",
|
|
81
|
+
"default": true,
|
|
82
|
+
"icon": "primer/copilot",
|
|
83
|
+
"startupCommand": "copilot --agent terminal-agent",
|
|
84
|
+
"resumeCommand": "copilot --resume={id} --agent terminal-agent",
|
|
85
|
+
"sessionIdEnv": "COPILOT_AGENT_SESSION_ID",
|
|
86
|
+
"postStartup": "/allow-all on",
|
|
87
|
+
"readinessSignal": "Environment loaded:",
|
|
88
|
+
"resizable": true
|
|
93
89
|
},
|
|
90
|
+
"claude": {
|
|
91
|
+
"label": "Claude Code",
|
|
92
|
+
"icon": "claude",
|
|
93
|
+
"startupCommand": "claude --agent terminal-agent --dangerously-skip-permissions",
|
|
94
|
+
"resumeCommand": "claude --resume {id} --agent terminal-agent --dangerously-skip-permissions",
|
|
95
|
+
"sessionIdEnv": "CLAUDE_SESSION_ID",
|
|
96
|
+
"sessionStateGlob": "~/.claude/projects/*/{id}.jsonl",
|
|
97
|
+
"resizable": true,
|
|
98
|
+
"readinessSignal": "bypass permissions"
|
|
99
|
+
},
|
|
100
|
+
"codex": {
|
|
101
|
+
"label": "Codex CLI",
|
|
102
|
+
"icon": "codex",
|
|
103
|
+
"startupCommand": "codex --full-auto",
|
|
104
|
+
"resumeCommand": "codex resume {id}",
|
|
105
|
+
"sessionIdEnv": "CODEX_SESSION_ID",
|
|
106
|
+
"sessionStateGlob": "~/.codex/sessions/**/rollout-*-{id}.jsonl",
|
|
107
|
+
"configFiles": [".codex/config.toml"],
|
|
108
|
+
"resizable": true
|
|
109
|
+
}
|
|
110
|
+
},
|
|
94
111
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
112
|
+
// Set to true to show agent entries in the canvas "+" add menu
|
|
113
|
+
// Set to false to only show them in the command palette
|
|
114
|
+
"showAgentsInAddMenu": false
|
|
99
115
|
}
|
|
100
116
|
```
|
|
101
117
|
|
|
@@ -11,11 +11,11 @@
|
|
|
11
11
|
*
|
|
12
12
|
* A watcher on the per-widget capture file persists the captured id onto
|
|
13
13
|
* the widget's terminal config as `lastAgentSessionId`. On the next cold
|
|
14
|
-
* restart, the launch is rewritten to `copilot --resume=<id> --agent
|
|
15
|
-
* with a pre-flight check that the
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
14
|
+
* restart, the launch is rewritten to `copilot --resume=<id> --agent ...`
|
|
15
|
+
* (with a pre-flight check that the on-disk session still exists), and is
|
|
16
|
+
* shell-chained with a `|| <fresh-startup>` fallback so that if the agent
|
|
17
|
+
* CLI rejects the id at runtime the widget still ends up with a working
|
|
18
|
+
* fresh session instead of a dead terminal.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync, watch as fsWatch, readdirSync, statSync } from 'node:fs'
|
|
@@ -220,7 +220,16 @@ export function buildResumeStartupCommand({ startupCommand, sessionId, agentCfg
|
|
|
220
220
|
const template = agentCfg?.resumeCommand
|
|
221
221
|
if (!template || !template.includes('{id}')) return startupCommand
|
|
222
222
|
|
|
223
|
-
|
|
223
|
+
const resumeCmd = template.replace('{id}', sessionId)
|
|
224
|
+
|
|
225
|
+
// Graceful fallback: if the resume command exits non-zero (e.g. the agent
|
|
226
|
+
// CLI rejected the id, the on-disk session is corrupt, or the binary
|
|
227
|
+
// doesn't actually support resume the way we expect), fall through to a
|
|
228
|
+
// fresh session instead of leaving the widget with a dead terminal.
|
|
229
|
+
// A clean exit (user `/exit`s) returns 0 and skips the fallback.
|
|
230
|
+
if (agentCfg?.resumeFallback === false) return resumeCmd
|
|
231
|
+
const notice = `printf '\\n\\033[33m[storyboard] resume failed; starting fresh session...\\033[0m\\n'`
|
|
232
|
+
return `${resumeCmd} || { ${notice}; ${startupCommand}; }`
|
|
224
233
|
}
|
|
225
234
|
|
|
226
235
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
@@ -82,7 +82,7 @@ describe('agent-session', () => {
|
|
|
82
82
|
expect(out).toBe('copilot --agent terminal-agent')
|
|
83
83
|
})
|
|
84
84
|
|
|
85
|
-
it('substitutes {id} into resumeCommand
|
|
85
|
+
it('substitutes {id} into resumeCommand and chains a fresh-session fallback', () => {
|
|
86
86
|
const out = buildResumeStartupCommand({
|
|
87
87
|
startupCommand: 'copilot --agent terminal-agent',
|
|
88
88
|
sessionId: '11111111-2222-4333-8444-555555555555',
|
|
@@ -91,6 +91,21 @@ describe('agent-session', () => {
|
|
|
91
91
|
resumeCommand: 'copilot --resume={id} --agent terminal-agent',
|
|
92
92
|
},
|
|
93
93
|
})
|
|
94
|
+
expect(out).toContain('copilot --resume=11111111-2222-4333-8444-555555555555 --agent terminal-agent')
|
|
95
|
+
expect(out).toContain('|| {')
|
|
96
|
+
expect(out).toContain('copilot --agent terminal-agent')
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('skips the fallback when resumeFallback: false', () => {
|
|
100
|
+
const out = buildResumeStartupCommand({
|
|
101
|
+
startupCommand: 'copilot --agent terminal-agent',
|
|
102
|
+
sessionId: '11111111-2222-4333-8444-555555555555',
|
|
103
|
+
agentCfg: {
|
|
104
|
+
sessionStateDir: null,
|
|
105
|
+
resumeCommand: 'copilot --resume={id} --agent terminal-agent',
|
|
106
|
+
resumeFallback: false,
|
|
107
|
+
},
|
|
108
|
+
})
|
|
94
109
|
expect(out).toBe('copilot --resume=11111111-2222-4333-8444-555555555555 --agent terminal-agent')
|
|
95
110
|
})
|
|
96
111
|
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
* @property {string} [sessionIdEnv] — env var exposed in the agent's SessionStart hook payload that holds its session id (e.g. "COPILOT_AGENT_SESSION_ID"). When set, the server captures the id per-widget so cold restarts can auto-resume.
|
|
66
66
|
* @property {string} [sessionStateDir] — directory where the agent stores per-session state, used to pre-flight `--resume` (e.g. "~/.copilot/session-state"). Pass `null` to skip the fs check (UUID-only validation).
|
|
67
67
|
* @property {string} [sessionStateGlob] — alternative to sessionStateDir for agents that store sessions under a per-project subdir, with `{id}` placeholder (e.g. "~/.claude/projects/*/{id}.jsonl").
|
|
68
|
+
* @property {boolean} [resumeFallback] — when true (default), the resume command is shell-chained with `|| <startupCommand>` so a runtime resume failure falls through to a fresh session instead of leaving the widget with a dead terminal. Set false to opt out.
|
|
68
69
|
* @property {boolean} [resizable] — override terminal resizability for this agent
|
|
69
70
|
* @property {number} [defaultWidth] — override default width
|
|
70
71
|
* @property {number} [defaultHeight] — override default height
|
|
@@ -547,6 +547,90 @@ function readCoreConfigFile(root, filename) {
|
|
|
547
547
|
return null
|
|
548
548
|
}
|
|
549
549
|
|
|
550
|
+
/**
|
|
551
|
+
* Resolve the absolute path of a library config file (returns the first
|
|
552
|
+
* candidate that exists, or null). Used by syncScaffoldDir to copy raw
|
|
553
|
+
* file contents (including comments) rather than re-serializing parsed JSON.
|
|
554
|
+
*/
|
|
555
|
+
function resolveCoreConfigFilePath(root, filename) {
|
|
556
|
+
const candidates = [
|
|
557
|
+
path.resolve(root, `packages/storyboard/${filename}`),
|
|
558
|
+
path.resolve(root, `node_modules/@dfosco/storyboard/${filename}`),
|
|
559
|
+
]
|
|
560
|
+
for (const p of candidates) {
|
|
561
|
+
try { fs.accessSync(p); return p } catch { /* try next */ }
|
|
562
|
+
}
|
|
563
|
+
return null
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const SCAFFOLD_README = `# .storyboard/scaffold/
|
|
567
|
+
|
|
568
|
+
This directory is **always rewritten on dev-server boot** to reflect the
|
|
569
|
+
library's current default config files. The Storyboard server **never reads
|
|
570
|
+
config from this directory** — these files are reference copies for you to
|
|
571
|
+
customize.
|
|
572
|
+
|
|
573
|
+
## How to customize
|
|
574
|
+
|
|
575
|
+
1. Pick the config file you want to override (e.g. \`terminal.config.json\`).
|
|
576
|
+
2. **Copy it to your project root** (next to \`storyboard.config.json\`).
|
|
577
|
+
3. Edit only the keys you care about — leaf-level merge means everything
|
|
578
|
+
else continues to inherit the library defaults, so future updates
|
|
579
|
+
(new agents, new readiness signals, etc.) reach you automatically.
|
|
580
|
+
|
|
581
|
+
## Why a separate directory?
|
|
582
|
+
|
|
583
|
+
- Customers who don't want to customize don't see config clutter at the root.
|
|
584
|
+
- Customers who do want to customize have all the defaults available as a
|
|
585
|
+
living reference, version-bumped with every storyboard release.
|
|
586
|
+
- Files at the root override the defaults; missing files mean "use library
|
|
587
|
+
defaults". No empty placeholder files cluttering the project.
|
|
588
|
+
|
|
589
|
+
## What's in here
|
|
590
|
+
|
|
591
|
+
| File | What it covers |
|
|
592
|
+
|------|----------------|
|
|
593
|
+
| \`terminal.config.json\` | Terminal widgets + canvas agent CLIs (copilot/claude/codex) |
|
|
594
|
+
| \`toolbar.config.json\` | Toolbar tool registry + visibility |
|
|
595
|
+
| \`commandpalette.config.json\` | Command palette entries |
|
|
596
|
+
| \`paste.config.json\` | URL → widget paste rules |
|
|
597
|
+
| \`widgets.config.json\` | Widget defaults (size, behavior) |
|
|
598
|
+
|
|
599
|
+
Don't edit files in this directory — your changes will be overwritten on
|
|
600
|
+
the next dev-server boot. Always copy to the project root first.
|
|
601
|
+
`
|
|
602
|
+
|
|
603
|
+
const SCAFFOLD_FILES = [
|
|
604
|
+
'terminal.config.json',
|
|
605
|
+
'toolbar.config.json',
|
|
606
|
+
'commandpalette.config.json',
|
|
607
|
+
'paste.config.json',
|
|
608
|
+
'widgets.config.json',
|
|
609
|
+
]
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Sync `.storyboard/scaffold/` with the library's current default config
|
|
613
|
+
* files. Always overwrites — users must copy to the project root to
|
|
614
|
+
* customize. Idempotent and best-effort.
|
|
615
|
+
*/
|
|
616
|
+
function syncScaffoldDir(root) {
|
|
617
|
+
const scaffoldDir = path.resolve(root, '.storyboard', 'scaffold')
|
|
618
|
+
try { fs.mkdirSync(scaffoldDir, { recursive: true }) } catch { /* empty */ }
|
|
619
|
+
|
|
620
|
+
const readmePath = path.resolve(scaffoldDir, 'README.md')
|
|
621
|
+
try { fs.writeFileSync(readmePath, SCAFFOLD_README) } catch { /* empty */ }
|
|
622
|
+
|
|
623
|
+
for (const filename of SCAFFOLD_FILES) {
|
|
624
|
+
const src = resolveCoreConfigFilePath(root, filename)
|
|
625
|
+
if (!src) continue
|
|
626
|
+
const dest = path.resolve(scaffoldDir, filename)
|
|
627
|
+
try {
|
|
628
|
+
const raw = fs.readFileSync(src, 'utf-8')
|
|
629
|
+
fs.writeFileSync(dest, raw)
|
|
630
|
+
} catch { /* skip on error */ }
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
550
634
|
/**
|
|
551
635
|
* Deep-merge helper (same as loader.js deepMerge but available at build time).
|
|
552
636
|
* Arrays are replaced, not concatenated. Objects are recursively merged.
|
|
@@ -597,6 +681,7 @@ function buildUnifiedConfig(root) {
|
|
|
597
681
|
const coreCommandPalette = readCoreConfigFile(root, 'commandpalette.config.json') || {}
|
|
598
682
|
const corePaste = readCoreConfigFile(root, 'paste.config.json') || {}
|
|
599
683
|
const coreWidgets = readCoreConfigFile(root, 'widgets.config.json') || {}
|
|
684
|
+
const coreTerminal = readCoreConfigFile(root, 'terminal.config.json') || {}
|
|
600
685
|
|
|
601
686
|
// 2. Read storyboard.config.json (middle priority)
|
|
602
687
|
// Use the schema-defaulted config for most things, but also read
|
|
@@ -619,6 +704,17 @@ function buildUnifiedConfig(root) {
|
|
|
619
704
|
const afterSbWidgets = rawSbConfig.widgets
|
|
620
705
|
? deepMergeBuild(coreWidgets, sbConfig.widgets || {})
|
|
621
706
|
: coreWidgets
|
|
707
|
+
// For terminal/agents, slot canvas.terminal + canvas.agents from storyboard.config.json
|
|
708
|
+
// into the same shape as terminal.config.json so the merge is uniform.
|
|
709
|
+
const sbTerminalLike = (rawSbConfig.canvas && (rawSbConfig.canvas.terminal || rawSbConfig.canvas.agents))
|
|
710
|
+
? {
|
|
711
|
+
...(rawSbConfig.canvas.terminal ? { terminal: sbConfig.canvas.terminal } : {}),
|
|
712
|
+
...(rawSbConfig.canvas.agents ? { agents: sbConfig.canvas.agents } : {}),
|
|
713
|
+
}
|
|
714
|
+
: null
|
|
715
|
+
const afterSbTerminal = sbTerminalLike
|
|
716
|
+
? deepMergeBuild(coreTerminal, sbTerminalLike)
|
|
717
|
+
: coreTerminal
|
|
622
718
|
|
|
623
719
|
// 4. Read user domain config files (highest priority)
|
|
624
720
|
const userFiles = [
|
|
@@ -626,6 +722,7 @@ function buildUnifiedConfig(root) {
|
|
|
626
722
|
{ domain: 'paste', filename: 'paste.config.json' },
|
|
627
723
|
{ domain: 'toolbar', filename: 'toolbar.config.json' },
|
|
628
724
|
{ domain: 'commandPalette', filename: 'commandpalette.config.json' },
|
|
725
|
+
{ domain: 'terminal', filename: 'terminal.config.json' },
|
|
629
726
|
]
|
|
630
727
|
|
|
631
728
|
const userConfigs = {}
|
|
@@ -648,6 +745,9 @@ function buildUnifiedConfig(root) {
|
|
|
648
745
|
const finalWidgets = userConfigs.widgets
|
|
649
746
|
? deepMergeBuild(afterSbWidgets, userConfigs.widgets.data)
|
|
650
747
|
: afterSbWidgets
|
|
748
|
+
const finalTerminal = userConfigs.terminal
|
|
749
|
+
? deepMergeBuild(afterSbTerminal, userConfigs.terminal.data)
|
|
750
|
+
: afterSbTerminal
|
|
651
751
|
|
|
652
752
|
// 6. Detect overlaps between storyboard.config.json and user domain configs
|
|
653
753
|
const domainOverlapChecks = [
|
|
@@ -664,18 +764,32 @@ function buildUnifiedConfig(root) {
|
|
|
664
764
|
}
|
|
665
765
|
}
|
|
666
766
|
}
|
|
767
|
+
// Terminal overlap check: storyboard.config.json.canvas.{terminal,agents} vs terminal.config.json
|
|
768
|
+
if (sbTerminalLike && userConfigs.terminal) {
|
|
769
|
+
const overlaps = findOverlappingKeys(sbTerminalLike, userConfigs.terminal.data)
|
|
770
|
+
for (const key of overlaps) {
|
|
771
|
+
warnings.push(`Config overlap: "${key}" is defined in both storyboard.config.json.canvas and terminal.config.json — terminal.config.json wins.`)
|
|
772
|
+
}
|
|
773
|
+
}
|
|
667
774
|
|
|
668
775
|
// 7. Build the unified config object.
|
|
669
776
|
// Start from the schema-defaulted sbConfig so every top-level key from
|
|
670
777
|
// storyboard.config.json (and every schema default) flows to initConfig().
|
|
671
778
|
// Then override the domain-specific slices that have their own dedicated
|
|
672
|
-
// config files merged above (toolbar/commandPalette/paste/widgets).
|
|
779
|
+
// config files merged above (toolbar/commandPalette/paste/widgets/terminal).
|
|
780
|
+
const sbCanvas = sbConfig?.canvas || {}
|
|
673
781
|
const unified = {
|
|
674
|
-
...sbConfig,
|
|
782
|
+
...(sbConfig || {}),
|
|
675
783
|
toolbar: finalToolbar,
|
|
676
784
|
commandPalette: finalCommandPalette,
|
|
677
785
|
paste: finalPaste,
|
|
678
786
|
widgets: finalWidgets,
|
|
787
|
+
canvas: {
|
|
788
|
+
...sbCanvas,
|
|
789
|
+
terminal: deepMergeBuild(sbCanvas.terminal || {}, finalTerminal.terminal || {}),
|
|
790
|
+
agents: deepMergeBuild(sbCanvas.agents || {}, finalTerminal.agents || {}),
|
|
791
|
+
...(finalTerminal.showAgentsInAddMenu !== undefined ? { showAgentsInAddMenu: finalTerminal.showAgentsInAddMenu } : {}),
|
|
792
|
+
},
|
|
679
793
|
}
|
|
680
794
|
|
|
681
795
|
return { unified, warnings }
|
|
@@ -1063,6 +1177,14 @@ export default function storyboardDataPlugin() {
|
|
|
1063
1177
|
// dev so users can hit their routes, excluded from production builds
|
|
1064
1178
|
// so private experiments don't ship.
|
|
1065
1179
|
includeTilde = config.command === 'serve'
|
|
1180
|
+
|
|
1181
|
+
// On dev boot, sync .storyboard/scaffold/ with the library's current
|
|
1182
|
+
// default config files so users always have an up-to-date copy-source
|
|
1183
|
+
// for customizations. Files in .storyboard/scaffold/ are NEVER read by
|
|
1184
|
+
// the server — only files at the project root are. Always overwrites.
|
|
1185
|
+
if (config.command === 'serve') {
|
|
1186
|
+
try { syncScaffoldDir(root) } catch { /* best-effort */ }
|
|
1187
|
+
}
|
|
1066
1188
|
},
|
|
1067
1189
|
|
|
1068
1190
|
resolveId(id) {
|
|
@@ -1209,7 +1331,7 @@ export default function storyboardDataPlugin() {
|
|
|
1209
1331
|
}
|
|
1210
1332
|
|
|
1211
1333
|
// Invalidate when any config file inside a prototype changes
|
|
1212
|
-
const protoConfigPattern = /\/(toolbar|commandpalette|widgets|paste)\.config\.json$/
|
|
1334
|
+
const protoConfigPattern = /\/(toolbar|commandpalette|widgets|paste|terminal)\.config\.json$/
|
|
1213
1335
|
if (protoConfigPattern.test(normalized) && normalized.includes('/prototypes/')) {
|
|
1214
1336
|
buildResult = null
|
|
1215
1337
|
const mod = server.moduleGraph.getModuleById(RESOLVED_ID)
|
|
@@ -1368,6 +1490,7 @@ export default function storyboardDataPlugin() {
|
|
|
1368
1490
|
'commandpalette.config.json',
|
|
1369
1491
|
'paste.config.json',
|
|
1370
1492
|
'widgets.config.json',
|
|
1493
|
+
'terminal.config.json',
|
|
1371
1494
|
].map(f => path.resolve(root, f))
|
|
1372
1495
|
const watchedConfigPaths = new Set([configPath, ...domainConfigFiles])
|
|
1373
1496
|
for (const p of domainConfigFiles) watcher.add(p)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./terminal.schema.json",
|
|
3
|
+
"$comment": "Defaults for terminal widgets and canvas agents. Users can override at the project root by copying this file from .storyboard/scaffold/terminal.config.json. Leaf-level merge: only the specific keys you set are overridden, everything else inherits the library defaults — so future agent additions or readinessSignal tweaks reach you automatically.",
|
|
4
|
+
"terminal": {
|
|
5
|
+
"resizable": true,
|
|
6
|
+
"defaultWidth": 1000,
|
|
7
|
+
"defaultHeight": 800,
|
|
8
|
+
"fontSize": 14,
|
|
9
|
+
"fontFamily": "'SF Mono', 'Menlo', 'Monaco', 'Courier New', monospace",
|
|
10
|
+
"prompt": "❯ ",
|
|
11
|
+
"startupCommand": null,
|
|
12
|
+
"defaultStartupSequence": null
|
|
13
|
+
},
|
|
14
|
+
"agents": {
|
|
15
|
+
"copilot": {
|
|
16
|
+
"label": "Copilot CLI",
|
|
17
|
+
"default": true,
|
|
18
|
+
"icon": "primer/copilot",
|
|
19
|
+
"startupCommand": "copilot --agent terminal-agent",
|
|
20
|
+
"resumeCommand": "copilot --resume={id} --agent terminal-agent",
|
|
21
|
+
"sessionIdEnv": "COPILOT_AGENT_SESSION_ID",
|
|
22
|
+
"postStartup": "/allow-all on",
|
|
23
|
+
"readinessSignal": "Environment loaded:",
|
|
24
|
+
"resizable": true
|
|
25
|
+
},
|
|
26
|
+
"claude": {
|
|
27
|
+
"label": "Claude Code",
|
|
28
|
+
"icon": "claude",
|
|
29
|
+
"startupCommand": "claude --agent terminal-agent --dangerously-skip-permissions",
|
|
30
|
+
"resumeCommand": "claude --resume {id} --agent terminal-agent --dangerously-skip-permissions",
|
|
31
|
+
"sessionIdEnv": "CLAUDE_SESSION_ID",
|
|
32
|
+
"sessionStateGlob": "~/.claude/projects/*/{id}.jsonl",
|
|
33
|
+
"readinessSignal": "bypass permissions",
|
|
34
|
+
"resizable": true
|
|
35
|
+
},
|
|
36
|
+
"codex": {
|
|
37
|
+
"label": "Codex CLI",
|
|
38
|
+
"icon": "codex",
|
|
39
|
+
"startupCommand": "codex --ask-for-approval never",
|
|
40
|
+
"resumeCommand": "codex resume {id}",
|
|
41
|
+
"sessionIdEnv": "CODEX_SESSION_ID",
|
|
42
|
+
"sessionStateGlob": "~/.codex/sessions/**/rollout-*-{id}.jsonl",
|
|
43
|
+
"configFiles": [".codex/config.toml"],
|
|
44
|
+
"resizable": true
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"showAgentsInAddMenu": false
|
|
48
|
+
}
|