gitspace 0.2.0-rc.12 → 0.2.0-rc.13

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/AGENTS.md CHANGED
@@ -14,7 +14,7 @@ This document provides comprehensive information for AI assistants working on th
14
14
  - **X3DH handshake** for forward-secret session encryption
15
15
  - Web terminal interface (React + xterm.js)
16
16
  - Linear issue integration for workspace creation
17
- - Convention-based custom scripts in `.gitspace/` (pre, setup, select, remove phases)
17
+ - Convention-based custom scripts in `.gitspace/scripts/` (pre, setup, select, remove phases)
18
18
 
19
19
  ## Architecture
20
20
 
@@ -27,7 +27,7 @@ This document provides comprehensive information for AI assistants working on th
27
27
  2. **Workspaces**: Individual git worktrees for features/branches
28
28
  - Located at `~/gitspace/<project-name>/workspaces/<workspace-name>/`
29
29
  - Each workspace has its own branch
30
- - Scripts are sourced from `.gitspace/<phase>/` in the workspace (version-controlled)
30
+ - Scripts are sourced from `.gitspace/scripts/<phase>/` in the workspace (version-controlled)
31
31
 
32
32
  3. **Sessions**: PTY terminal sessions managed by tmux-lite
33
33
  - Can be attached from multiple clients
package/README.md CHANGED
@@ -117,14 +117,15 @@ A bundle is a directory (typically `.gitspace/`) containing:
117
117
  ```
118
118
  .gitspace/
119
119
  ├── bundle.json # Bundle manifest with onboarding steps
120
- ├── pre/ # Scripts to run before setup
121
- │ └── 01-copy-env.sh
122
- ├── setup/ # Scripts to run on first workspace creation
123
- │ └── 01-install-deps.sh
124
- ├── select/ # Scripts to run every time workspace is opened
125
- │ └── 01-status.sh
126
- └── remove/ # Scripts to run before workspace deletion
127
- └── 01-cleanup.sh
120
+ └── scripts/
121
+ ├── pre/ # Scripts to run before setup
122
+ │ └── 01-copy-env.sh
123
+ ├── setup/ # Scripts to run on first workspace creation
124
+ │ └── 01-install-deps.sh
125
+ ├── select/ # Scripts to run every time workspace is opened
126
+ └── 01-status.sh
127
+ └── remove/ # Scripts to run before workspace deletion
128
+ └── 01-cleanup.sh
128
129
  ```
129
130
 
130
131
  ### Bundle Manifest (`bundle.json`)
@@ -188,7 +189,7 @@ Bundle values are passed to scripts as environment variables:
188
189
 
189
190
  ```bash
190
191
  #!/bin/bash
191
- # .gitspace/select/01-status.sh
192
+ # .gitspace/scripts/select/01-status.sh
192
193
 
193
194
  WORKSPACE_NAME=$1
194
195
  REPOSITORY=$2
@@ -361,10 +362,11 @@ inside each workspace so they can vary by branch:
361
362
 
362
363
  ```
363
364
  ~/gitspace/<project-name>/workspaces/<workspace-name>/.gitspace/
364
- ├── pre/ # Run before setup (terminal)
365
- ├── setup/ # Run once on workspace creation
366
- ├── select/ # Run every time workspace is opened
367
- └── remove/ # Run before workspace deletion
365
+ └── scripts/
366
+ ├── pre/ # Run before setup (terminal)
367
+ ├── setup/ # Run once on workspace creation
368
+ ├── select/ # Run every time workspace is opened
369
+ └── remove/ # Run before workspace deletion
368
370
  ```
369
371
 
370
372
  #### Script Execution Rules
@@ -406,19 +408,15 @@ export SPACES_CURRENT_PROJECT="my-app"
406
408
  │ ├── workspaces/ # Git worktrees
407
409
  │ │ └── <workspace-name>/
408
410
  │ │ ├── gitspace.lock # Setup completion marker
409
- │ │ └── .prompt/ # Linear issue details (if applicable)
410
- │ │ └── issue.md
411
- │ └── workspaces/
412
- └── <workspace-name>/
413
- │ └── .gitspace/ # Custom scripts (per worktree)
414
- │ ├── pre/
415
- │ ├── setup/
416
- │ ├── select/
417
- │ └── remove/
418
- │ ├── pre/
419
- │ ├── setup/
420
- │ ├── select/
421
- │ └── remove/
411
+ │ │ ├── .prompt/ # Linear issue details (if applicable)
412
+ │ │└── issue.md
413
+ └── .gitspace/
414
+ │ ├── bundle.json
415
+ └── scripts/ # Custom scripts (per worktree)
416
+ ├── pre/
417
+ ├── setup/
418
+ ├── select/
419
+ └── remove/
422
420
  ```
423
421
 
424
422
  ## Remote Access
package/bun.lock CHANGED
@@ -24,6 +24,7 @@
24
24
  "open": "^11.0.0",
25
25
  "ora": "^8.2.0",
26
26
  "react": "^19.2.4",
27
+ "react-dom": "^19.2.4",
27
28
  "simple-git": "^3.30.0",
28
29
  "ws": "^8.19.0",
29
30
  },
@@ -38,10 +39,10 @@
38
39
  "typescript": "^5.9.3",
39
40
  },
40
41
  "optionalDependencies": {
41
- "@gitspace/darwin-arm64": "0.2.0-rc.11",
42
- "@gitspace/darwin-x64": "0.2.0-rc.11",
43
- "@gitspace/linux-arm64": "0.2.0-rc.11",
44
- "@gitspace/linux-x64": "0.2.0-rc.11",
42
+ "@gitspace/darwin-arm64": "0.2.0-rc.12",
43
+ "@gitspace/darwin-x64": "0.2.0-rc.12",
44
+ "@gitspace/linux-arm64": "0.2.0-rc.12",
45
+ "@gitspace/linux-x64": "0.2.0-rc.12",
45
46
  },
46
47
  },
47
48
  },
@@ -62,7 +63,7 @@
62
63
 
63
64
  "@eslint/js": ["@eslint/js@8.57.1", "", {}, "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q=="],
64
65
 
65
- "@gitspace/darwin-arm64": ["@gitspace/darwin-arm64@0.2.0-rc.11", "", { "os": "darwin", "cpu": "arm64", "bin": { "gssh-darwin-arm64": "bin/gssh" } }, "sha512-MCApLGdB9iScZ/IW1TiUlt+HwdYACfTJxw3m8tShb15IjhrNxn06GvKlTIIq5fR13XkPlPZNuX6/IxO00TVa5A=="],
66
+ "@gitspace/darwin-arm64": ["@gitspace/darwin-arm64@0.2.0-rc.12", "", { "os": "darwin", "cpu": "arm64", "bin": { "gssh-darwin-arm64": "bin/gssh" } }, "sha512-FUCz7f6EThB1/epRZ+X3SV7wDq/m4mFRDy2AVXGurVK6OWAA+D8tddUYeGmxvYT9+PXEZDATu5ukBh2RKpr5OQ=="],
66
67
 
67
68
  "@graphql-typed-document-node/core": ["@graphql-typed-document-node/core@3.2.0", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="],
68
69
 
@@ -578,7 +579,7 @@
578
579
 
579
580
  "react-devtools-core": ["react-devtools-core@7.0.1", "", { "dependencies": { "shell-quote": "^1.6.1", "ws": "^7" } }, "sha512-C3yNvRHaizlpiASzy7b9vbnBGLrhvdhl1CbdU6EnZgxPNbai60szdLtl+VL76UNOt5bOoVTOz5rNWZxgGt+Gsw=="],
580
581
 
581
- "react-dom": ["react-dom@19.2.3", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.3" } }, "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg=="],
582
+ "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="],
582
583
 
583
584
  "react-is": ["react-is@17.0.2", "", {}, "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="],
584
585
 
@@ -281,14 +281,15 @@ D) CUSTOM SCRIPTS & BUNDLES
281
281
 
282
282
  ### Custom Scripts
283
283
 
284
- GitSpace uses convention-based scripts stored per workspace in `.gitspace/`:
284
+ GitSpace uses convention-based scripts stored per workspace in `.gitspace/scripts/`:
285
285
 
286
286
  ```
287
287
  ~/gitspace/<project>/workspaces/<workspace>/.gitspace/
288
- ├── pre/ # Run before setup (once, in terminal)
289
- ├── setup/ # Run on workspace creation (once)
290
- ├── select/ # Run every time workspace is opened
291
- └── remove/ # Run before workspace deletion
288
+ └── scripts/
289
+ ├── pre/ # Run before setup (once, in terminal)
290
+ ├── setup/ # Run on workspace creation (once)
291
+ ├── select/ # Run every time workspace is opened
292
+ └── remove/ # Run before workspace deletion
292
293
  ```
293
294
 
294
295
  Script execution rules:
@@ -298,7 +299,7 @@ Script execution rules:
298
299
  - Arguments: `$1` = workspace name, `$2` = repository name
299
300
  - Environment: Bundle values available as `SPACE_VALUE_*` and `SPACE_SECRET_*`
300
301
 
301
- Example script (`.gitspace/select/01-status.sh`):
302
+ Example script (`.gitspace/scripts/select/01-status.sh`):
302
303
  ```bash
303
304
  #!/bin/bash
304
305
  WORKSPACE_NAME=$1
@@ -316,10 +317,11 @@ Bundles allow repository owners to share onboarding configurations. Place in `.g
316
317
  ```
317
318
  .gitspace/
318
319
  ├── bundle.json # Bundle manifest with onboarding steps
319
- ├── pre/ # Scripts to run before setup
320
- ├── setup/ # Scripts to run on first workspace creation
321
- ├── select/ # Scripts to run every time workspace is opened
322
- └── remove/ # Scripts to run before workspace deletion
320
+ └── scripts/
321
+ ├── pre/ # Scripts to run before setup
322
+ ├── setup/ # Scripts to run on first workspace creation
323
+ ├── select/ # Scripts to run every time workspace is opened
324
+ └── remove/ # Scripts to run before workspace deletion
323
325
  ```
324
326
 
325
327
  Bundle manifest example (`bundle.json`):
@@ -465,15 +467,15 @@ Location: `~/gitspace/.identity/`
465
467
  │ ├── workspaces/ # Git worktrees
466
468
  │ │ └── <workspace-name>/
467
469
  │ │ ├── gitspace.lock # Setup completion marker
468
- │ │ └── .prompt/ # Linear issue details (if applicable)
469
- │ │ └── issue.md
470
- │ └── workspaces/
471
- └── <workspace-name>/
472
- │ └── .gitspace/ # Custom scripts (per worktree)
473
- │ ├── pre/
474
- │ ├── setup/
475
- │ ├── select/
476
- │ └── remove/
470
+ │ │ ├── .prompt/ # Linear issue details (if applicable)
471
+ │ │└── issue.md
472
+ └── .gitspace/
473
+ │ ├── bundle.json
474
+ └── scripts/ # Custom scripts (per worktree)
475
+ ├── pre/
476
+ ├── setup/
477
+ ├── select/
478
+ └── remove/
477
479
  ```
478
480
 
479
481
  ============================================================
@@ -621,8 +623,8 @@ gssh identity init --label "My Device"
621
623
  - Try: `git fetch origin` first
622
624
 
623
625
  **"Setup scripts failed"**
624
- - Check script is executable: `chmod +x .gitspace/setup/*.sh`
625
- - Run manually to see error: `./.gitspace/setup/01-script.sh`
626
+ - Check script is executable: `chmod +x .gitspace/scripts/setup/*.sh`
627
+ - Run manually to see error: `./.gitspace/scripts/setup/01-script.sh`
626
628
  - Check environment variables are set
627
629
 
628
630
  ### Bundle/Secrets Issues
@@ -893,14 +895,15 @@ gssh status # Show daemon statuses
893
895
 
894
896
  SECTION: Local Workflow - Custom Scripts
895
897
 
896
- GitSpace uses convention-based scripts stored per workspace in `.gitspace/`:
898
+ GitSpace uses convention-based scripts stored per workspace in `.gitspace/scripts/`:
897
899
 
898
900
  ```
899
901
  ~/gitspace/<project>/workspaces/<workspace>/.gitspace/
900
- ├── pre/ # Run before setup (once)
901
- ├── setup/ # Run on workspace creation (once)
902
- ├── select/ # Run every time workspace is opened
903
- └── remove/ # Run before workspace deletion
902
+ └── scripts/
903
+ ├── pre/ # Run before setup (once)
904
+ ├── setup/ # Run on workspace creation (once)
905
+ ├── select/ # Run every time workspace is opened
906
+ └── remove/ # Run before workspace deletion
904
907
  ```
905
908
 
906
909
  **Rules:**
@@ -912,7 +915,7 @@ GitSpace uses convention-based scripts stored per workspace in `.gitspace/`:
912
915
  **Example:**
913
916
  ```bash
914
917
  #!/bin/bash
915
- # .gitspace/select/01-status.sh
918
+ # .gitspace/scripts/select/01-status.sh
916
919
  echo "Switching to: $1"
917
920
  git fetch origin
918
921
  git status
@@ -927,9 +930,11 @@ Bundles allow teams to share onboarding configurations. Place in `.gitspace/`:
927
930
  ```
928
931
  .gitspace/
929
932
  ├── bundle.json # Manifest
930
- ├── pre/ # Pre-setup scripts
931
- ├── setup/ # Setup scripts
932
- └── select/ # Select scripts
933
+ └── scripts/
934
+ ├── pre/ # Pre-setup scripts
935
+ ├── setup/ # Setup scripts
936
+ ├── select/ # Select scripts
937
+ └── remove/ # Remove scripts
933
938
  ```
934
939
 
935
940
  **Manifest example:**
@@ -284,15 +284,16 @@ gssh status # Show daemon statuses`} multiLine language="bash" />
284
284
  <h1 className="text-4xl font-bold mb-6">Custom Scripts</h1>
285
285
 
286
286
  <p className="text-zinc-400 mb-4">
287
- GitSpace uses convention-based scripts stored per workspace in <code className="text-zinc-300">~/gitspace/&lt;project&gt;/workspaces/&lt;workspace&gt;/.gitspace/</code>:
287
+ GitSpace uses convention-based scripts stored per workspace in <code className="text-zinc-300">~/gitspace/&lt;project&gt;/workspaces/&lt;workspace&gt;/.gitspace/scripts/</code>:
288
288
  </p>
289
289
 
290
290
  <div className="bg-zinc-900 rounded-lg border border-zinc-800 p-4 font-mono text-sm mb-8">
291
291
  <pre className="text-zinc-300">{`~/gitspace/<project>/workspaces/<workspace>/.gitspace/
292
- ├── pre/ # Run before setup (once)
293
- ├── setup/ # Run on workspace creation (once)
294
- ├── select/ # Run every time workspace is opened
295
- └── remove/ # Run before workspace deletion`}</pre>
292
+ └── scripts/
293
+ ├── pre/ # Run before setup (once)
294
+ ├── setup/ # Run on workspace creation (once)
295
+ ├── select/ # Run every time workspace is opened
296
+ └── remove/ # Run before workspace deletion`}</pre>
296
297
  </div>
297
298
 
298
299
  <h3 className="text-xl font-semibold text-white mb-4">Rules</h3>
@@ -304,7 +305,7 @@ gssh status # Show daemon statuses`} multiLine language="bash" />
304
305
  </ul>
305
306
 
306
307
  <h3 className="text-xl font-semibold text-white mb-4">Example Script</h3>
307
- <p className="text-zinc-500 text-sm mb-2">.gitspace/select/01-status.sh</p>
308
+ <p className="text-zinc-500 text-sm mb-2">.gitspace/scripts/select/01-status.sh</p>
308
309
  <JsonBlock code={`#!/bin/bash
309
310
  echo "Switching to: $1"
310
311
  git fetch origin
@@ -324,9 +325,11 @@ git status`} />
324
325
  <div className="bg-zinc-900 rounded-lg border border-zinc-800 p-4 font-mono text-sm mb-8">
325
326
  <pre className="text-zinc-300">{`.gitspace/
326
327
  ├── bundle.json # Manifest
327
- ├── pre/ # Pre-setup scripts
328
- ├── setup/ # Setup scripts
329
- └── select/ # Select scripts`}</pre>
328
+ └── scripts/
329
+ ├── pre/ # Pre-setup scripts
330
+ ├── setup/ # Setup scripts
331
+ ├── select/ # Select scripts
332
+ └── remove/ # Remove scripts`}</pre>
330
333
  </div>
331
334
 
332
335
  <h3 className="text-xl font-semibold text-white mb-4">Manifest Example</h3>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitspace",
3
- "version": "0.2.0-rc.12",
3
+ "version": "0.2.0-rc.13",
4
4
  "description": "CLI for managing GitHub workspaces with git worktrees and secure remote terminal access",
5
5
  "bin": {
6
6
  "gssh": "./bin/gssh"
@@ -17,10 +17,10 @@
17
17
  "relay": "bun src/relay/index.ts"
18
18
  },
19
19
  "optionalDependencies": {
20
- "@gitspace/darwin-arm64": "0.2.0-rc.12",
21
- "@gitspace/darwin-x64": "0.2.0-rc.12",
22
- "@gitspace/linux-x64": "0.2.0-rc.12",
23
- "@gitspace/linux-arm64": "0.2.0-rc.12"
20
+ "@gitspace/darwin-arm64": "0.2.0-rc.13",
21
+ "@gitspace/darwin-x64": "0.2.0-rc.13",
22
+ "@gitspace/linux-x64": "0.2.0-rc.13",
23
+ "@gitspace/linux-arm64": "0.2.0-rc.13"
24
24
  },
25
25
  "keywords": [
26
26
  "cli",
@@ -59,6 +59,7 @@
59
59
  "open": "^11.0.0",
60
60
  "ora": "^8.2.0",
61
61
  "react": "^19.2.4",
62
+ "react-dom": "^19.2.4",
62
63
  "simple-git": "^3.30.0",
63
64
  "ws": "^8.19.0"
64
65
  },
@@ -184,7 +184,7 @@ export async function addProject(options: {
184
184
  baseBranch
185
185
  );
186
186
 
187
- // Store bundle info if bundle was loaded (scripts are read from workspace .gitspace/)
187
+ // Store bundle info if bundle was loaded (scripts are read from workspace .gitspace/scripts/)
188
188
  if (loadedBundle) {
189
189
  // Store bundle values and info in project config
190
190
  const configUpdates: Record<string, unknown> = {};
@@ -447,7 +447,7 @@ export async function addWorkspace(
447
447
 
448
448
  // Run pre scripts if this is the first time (before tmux/setup)
449
449
  if (isFirstTime && !options.noSetup) {
450
- const preScriptsDir = join(workspacePath, '.gitspace', 'pre');
450
+ const preScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'pre');
451
451
  await runScriptsInTerminal(preScriptsDir, workspacePath, workspaceName, projectConfig.repository);
452
452
  }
453
453
 
package/src/core/shell.ts CHANGED
@@ -59,7 +59,7 @@ export async function openWorkspaceShell(
59
59
 
60
60
  if (selectOnly) {
61
61
  // TUI mode: setup was done during creation, just run select scripts
62
- const selectScriptsDir = join(workspacePath, '.gitspace', 'select')
62
+ const selectScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'select')
63
63
  await runScriptsInTerminal(
64
64
  selectScriptsDir,
65
65
  workspacePath,
@@ -73,7 +73,7 @@ export async function openWorkspaceShell(
73
73
  // Determine which scripts to run based on setup status
74
74
  if (setupAlreadyRun) {
75
75
  // Setup has been run before, run select scripts
76
- const selectScriptsDir = join(workspacePath, '.gitspace', 'select')
76
+ const selectScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'select')
77
77
  await runScriptsInTerminal(
78
78
  selectScriptsDir,
79
79
  workspacePath,
@@ -84,7 +84,7 @@ export async function openWorkspaceShell(
84
84
  } else if (!noSetup) {
85
85
  // First time setup, run setup scripts
86
86
  printToTerminal('Running setup scripts (first time)...')
87
- const setupScriptsDir = join(workspacePath, '.gitspace', 'setup')
87
+ const setupScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'setup')
88
88
  await runScriptsInTerminal(
89
89
  setupScriptsDir,
90
90
  workspacePath,
@@ -122,7 +122,7 @@ export async function deleteWorkspaceCore(
122
122
  // Run remove scripts (cleanup before deletion)
123
123
  try {
124
124
  const projectConfig = readProjectConfig(projectName);
125
- const removeScriptsDir = join(workspacePath, '.gitspace', 'remove');
125
+ const removeScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'remove');
126
126
  options.onProgress?.('Running cleanup scripts...');
127
127
  await runScriptsInTerminal(
128
128
  removeScriptsDir,
@@ -22,6 +22,10 @@ async function runCli(command: string): Promise<{ stdout: string; stderr: string
22
22
  cmd: ["bun", "run", CLI_SCRIPT, command, "--test"],
23
23
  stdout: "pipe",
24
24
  stderr: "pipe",
25
+ env: {
26
+ ...process.env,
27
+ TMUX_LITE: "",
28
+ },
25
29
  });
26
30
 
27
31
  const stdout = await new Response(proc.stdout).text();
@@ -232,7 +232,7 @@ describe('itemToToast', () => {
232
232
  expect(toast.sessionId).toBe('session-1');
233
233
  expect(toast.sessionName).toBe('my-project:my-workspace:default');
234
234
  expect(toast.icon).toBe('✅');
235
- expect(toast.title).toBe('Completed: npm test');
235
+ expect(toast.title).toBe('my-project / my-workspace / default: Completed - npm test');
236
236
  expect(toast.preview).toBe('Tests passed');
237
237
  expect(toast.item).toBe(item);
238
238
  });
@@ -248,7 +248,7 @@ describe('itemToToast', () => {
248
248
  const toast = itemToToast(item);
249
249
 
250
250
  expect(toast.icon).toBe('❌');
251
- expect(toast.title).toBe('Exit code 1: npm build');
251
+ expect(toast.title).toBe('my-project / my-workspace / default: Exit code 1 - npm build');
252
252
  });
253
253
 
254
254
  it('should convert bell item', () => {
@@ -261,7 +261,7 @@ describe('itemToToast', () => {
261
261
  const toast = itemToToast(item);
262
262
 
263
263
  expect(toast.icon).toBe('🔔');
264
- expect(toast.title).toBe('Bell');
264
+ expect(toast.title).toBe('my-project / my-workspace / default: Bell');
265
265
  });
266
266
 
267
267
  it('should convert idle item', () => {
@@ -274,7 +274,7 @@ describe('itemToToast', () => {
274
274
  const toast = itemToToast(item);
275
275
 
276
276
  expect(toast.icon).toBe('⏸️');
277
- expect(toast.title).toBe('Activity Complete: vim');
277
+ expect(toast.title).toBe('my-project / my-workspace / default: Activity Complete - vim');
278
278
  });
279
279
 
280
280
  it('should convert title item', () => {
@@ -287,7 +287,7 @@ describe('itemToToast', () => {
287
287
  const toast = itemToToast(item);
288
288
 
289
289
  expect(toast.icon).toBe('📝');
290
- expect(toast.title).toBe('Title Change: bash');
290
+ expect(toast.title).toBe('my-project / my-workspace / default: Title Change - bash');
291
291
  });
292
292
 
293
293
  it('should convert osc item', () => {
@@ -300,7 +300,7 @@ describe('itemToToast', () => {
300
300
  const toast = itemToToast(item);
301
301
 
302
302
  expect(toast.icon).toBe('📟');
303
- expect(toast.title).toBe('OSC Notification: app');
303
+ expect(toast.title).toBe('my-project / my-workspace / default: OSC Notification - app');
304
304
  });
305
305
 
306
306
  it('should truncate long preview text', () => {
package/src/tui/app.tsx CHANGED
@@ -1193,7 +1193,7 @@ function App({ relayConfig, onQuit }: AppProps) {
1193
1193
  return;
1194
1194
  }
1195
1195
 
1196
- // No onboarding, just create project (scripts are in workspace .gitspace/)
1196
+ // No onboarding, just create project (scripts are in workspace .gitspace/scripts/)
1197
1197
  createProject(projectName, repo, baseBranch);
1198
1198
  updateProjectConfig(projectName, {
1199
1199
  appliedBundle: {
@@ -8,6 +8,11 @@
8
8
  import { describe, it, expect, beforeEach, afterEach, mock } from 'bun:test';
9
9
 
10
10
  describe('onboarding', () => {
11
+ async function loadOnboardingModule() {
12
+ const cacheBust = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
13
+ return import(`../onboarding.ts?cacheBust=${cacheBust}`);
14
+ }
15
+
11
16
  // Store references to mock functions
12
17
  let mockPromptInput: (message: string, options?: { default?: string }) => Promise<string | null>;
13
18
  let mockPromptPassword: () => Promise<string | null>;
@@ -58,14 +63,14 @@ describe('onboarding', () => {
58
63
 
59
64
  describe('KEEP_EXISTING_SECRET constant', () => {
60
65
  it('should export the constant', async () => {
61
- const { KEEP_EXISTING_SECRET } = await import('../onboarding');
66
+ const { KEEP_EXISTING_SECRET } = await loadOnboardingModule();
62
67
  expect(KEEP_EXISTING_SECRET).toBe('__KEEP_EXISTING_SECRET__');
63
68
  });
64
69
  });
65
70
 
66
71
  describe('runOnboarding', () => {
67
72
  it('should complete with empty steps', async () => {
68
- const { runOnboarding } = await import('../onboarding');
73
+ const { runOnboarding } = await loadOnboardingModule();
69
74
  const result = await runOnboarding([]);
70
75
 
71
76
  expect(result.completed).toBe(true);
@@ -75,7 +80,7 @@ describe('onboarding', () => {
75
80
  it('should collect input values', async () => {
76
81
  mockPromptInput = async () => 'my-input';
77
82
 
78
- const { runOnboarding } = await import('../onboarding');
83
+ const { runOnboarding } = await loadOnboardingModule();
79
84
  const result = await runOnboarding([
80
85
  {
81
86
  id: 'name-step',
@@ -93,7 +98,7 @@ describe('onboarding', () => {
93
98
  it('should collect secret values', async () => {
94
99
  mockPromptPassword = async () => 'my-secret';
95
100
 
96
- const { runOnboarding } = await import('../onboarding');
101
+ const { runOnboarding } = await loadOnboardingModule();
97
102
  const result = await runOnboarding([
98
103
  {
99
104
  id: 'api-key',
@@ -111,7 +116,7 @@ describe('onboarding', () => {
111
116
  it('should handle info steps', async () => {
112
117
  mockPromptConfirm = async () => true;
113
118
 
114
- const { runOnboarding } = await import('../onboarding');
119
+ const { runOnboarding } = await loadOnboardingModule();
115
120
  const result = await runOnboarding([
116
121
  {
117
122
  id: 'welcome',
@@ -127,7 +132,7 @@ describe('onboarding', () => {
127
132
  it('should handle confirm steps', async () => {
128
133
  mockPromptConfirm = async () => true;
129
134
 
130
- const { runOnboarding } = await import('../onboarding');
135
+ const { runOnboarding } = await loadOnboardingModule();
131
136
  const result = await runOnboarding([
132
137
  {
133
138
  id: 'confirm-install',
@@ -143,7 +148,7 @@ describe('onboarding', () => {
143
148
  it('should handle cancelled input', async () => {
144
149
  mockPromptInput = async () => null;
145
150
 
146
- const { runOnboarding } = await import('../onboarding');
151
+ const { runOnboarding } = await loadOnboardingModule();
147
152
  const result = await runOnboarding([
148
153
  {
149
154
  id: 'name-step',
@@ -161,7 +166,7 @@ describe('onboarding', () => {
161
166
  it('should handle cancelled info step', async () => {
162
167
  mockPromptConfirm = async () => false;
163
168
 
164
- const { runOnboarding } = await import('../onboarding');
169
+ const { runOnboarding } = await loadOnboardingModule();
165
170
  const result = await runOnboarding([
166
171
  {
167
172
  id: 'welcome',
@@ -184,7 +189,7 @@ describe('onboarding', () => {
184
189
  return options?.default ?? '';
185
190
  };
186
191
 
187
- const { runOnboarding } = await import('../onboarding');
192
+ const { runOnboarding } = await loadOnboardingModule();
188
193
  const result = await runOnboarding(
189
194
  [
190
195
  {
@@ -209,7 +214,7 @@ describe('onboarding', () => {
209
214
  it('should allow overriding previous value', async () => {
210
215
  mockPromptInput = async () => 'New Name';
211
216
 
212
- const { runOnboarding } = await import('../onboarding');
217
+ const { runOnboarding } = await loadOnboardingModule();
213
218
  const result = await runOnboarding(
214
219
  [
215
220
  {
@@ -234,7 +239,7 @@ describe('onboarding', () => {
234
239
  // User confirms to keep existing
235
240
  mockPromptConfirm = async () => true;
236
241
 
237
- const { runOnboarding, KEEP_EXISTING_SECRET } = await import('../onboarding');
242
+ const { runOnboarding, KEEP_EXISTING_SECRET } = await loadOnboardingModule();
238
243
  const result = await runOnboarding(
239
244
  [
240
245
  {
@@ -268,7 +273,7 @@ describe('onboarding', () => {
268
273
  };
269
274
  mockPromptPassword = async () => 'new-secret-value';
270
275
 
271
- const { runOnboarding } = await import('../onboarding');
276
+ const { runOnboarding } = await loadOnboardingModule();
272
277
  const result = await runOnboarding(
273
278
  [
274
279
  {
@@ -292,7 +297,7 @@ describe('onboarding', () => {
292
297
  it('should not show keep existing prompt for new secrets', async () => {
293
298
  mockPromptPassword = async () => 'brand-new-secret';
294
299
 
295
- const { runOnboarding } = await import('../onboarding');
300
+ const { runOnboarding } = await loadOnboardingModule();
296
301
  const result = await runOnboarding(
297
302
  [
298
303
  {
@@ -319,7 +324,7 @@ describe('onboarding', () => {
319
324
  };
320
325
  mockPromptConfirm = async () => true;
321
326
 
322
- const { runOnboarding, KEEP_EXISTING_SECRET } = await import('../onboarding');
327
+ const { runOnboarding, KEEP_EXISTING_SECRET } = await loadOnboardingModule();
323
328
  const result = await runOnboarding(
324
329
  [
325
330
  {
@@ -40,10 +40,10 @@ describe('runWorkspaceScripts', () => {
40
40
  beforeEach(() => {
41
41
  testDir = join(tmpdir(), `workspace-scripts-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
42
42
  workspacePath = join(testDir, 'workspace');
43
- // Scripts are now in workspace/.gitspace/<phase>/
44
- preScriptsDir = join(workspacePath, '.gitspace', 'pre');
45
- setupScriptsDir = join(workspacePath, '.gitspace', 'setup');
46
- selectScriptsDir = join(workspacePath, '.gitspace', 'select');
43
+ // Scripts are in workspace/.gitspace/scripts/<phase>/
44
+ preScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'pre');
45
+ setupScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'setup');
46
+ selectScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'select');
47
47
 
48
48
  mkdirSync(preScriptsDir, { recursive: true });
49
49
  mkdirSync(setupScriptsDir, { recursive: true });
@@ -76,7 +76,7 @@ export async function runWorkspaceScripts(
76
76
 
77
77
  if (setupAlreadyRun) {
78
78
  // Run select scripts for existing workspace
79
- const selectScriptsDir = join(workspacePath, '.gitspace', 'select');
79
+ const selectScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'select');
80
80
  try {
81
81
  onPhaseStart?.('select');
82
82
  await runScriptsInTerminal(selectScriptsDir, workspacePath, workspaceName, repository, scriptOptions);
@@ -91,12 +91,12 @@ export async function runWorkspaceScripts(
91
91
 
92
92
  try {
93
93
  onPhaseStart?.('pre');
94
- const preScriptsDir = join(workspacePath, '.gitspace', 'pre');
94
+ const preScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'pre');
95
95
  await runScriptsInTerminal(preScriptsDir, workspacePath, workspaceName, repository, scriptOptions);
96
96
  preScriptsSucceeded = true;
97
97
 
98
98
  onPhaseStart?.('setup');
99
- const setupScriptsDir = join(workspacePath, '.gitspace', 'setup');
99
+ const setupScriptsDir = join(workspacePath, '.gitspace', 'scripts', 'setup');
100
100
  await runScriptsInTerminal(setupScriptsDir, workspacePath, workspaceName, repository, scriptOptions);
101
101
 
102
102
  // Only mark complete if both phases succeeded