Project config and scripts
Project scripts turn repository-specific setup and development commands into worktree-aware controls. Store shared commands in the repository or keep personal commands in app project settings.
Repository config
Section titled “Repository config”Create this file at the project root:
.super.engineering/config.jsonRepositories set up with older builds may have a legacy config file in a different folder. The app still reads it when .super.engineering/config.json is absent; when both exist in a checkout, the new file wins. A malformed new config reports an error instead of silently falling back to the legacy file.
A basic configuration looks like this:
{ "setup": ["bun install --frozen-lockfile"], "run": ["bun run dev"], "teardown": ["docker compose down"]}The app selects the new or legacy config within each checkout, checking the current worktree before the main checkout. A worktree’s legacy config therefore takes precedence over the main checkout’s new config. If the worktree has no config, the app reads the main checkout’s config. This lets a task test a script change from its own branch while other worktrees continue to use the main copy.
For each supported field, repository config overrides app project settings when the field is present. An omitted field falls back to the corresponding app setting.
Migrating repository config
Section titled “Migrating repository config”Existing setup, run, and teardown definitions still load. An explicit repository-config edit through the updated app or CLI writes the complete config to .super.engineering/config.json, preserves unknown fields, and leaves the legacy file intact.
Arbitrary repository script files are not moved, renamed, aliased, or rewritten automatically. If a command refers to a script in an older config folder, it keeps working while that file exists. If you move that script, update its command to match. There is no automatic replacement for every file in the old folder.
Repository config is separate from home app-data migration.
Script phases
Section titled “Script phases”| Phase | When it runs | Allowed owner |
|---|---|---|
setup | While preparing a new worktree, automatically or on demand | Repository config or app project settings |
run | When you start a named run target | Repository config or app project settings |
pre_cleanup | Before deletion; a nonzero exit can stop cleanup | App project settings only |
teardown | During worktree deletion | Repository config or app project settings |
post_cleanup | After the worktree is removed | App project settings only |
pre_cleanup and post_cleanup are intentionally user-owned because they run automatically around deletion. If those keys appear in repository config, the app does not use them as automatic cleanup hooks.
Commands in one phase run in order. For example:
{ "setup": [ "bun install --frozen-lockfile", "bun run generate" ]}Setup runs in the new worktree. If it modifies tracked files, those edits become part of the worktree diff.
App project settings add setup controls that do not belong in repository policy:
- Run setup automatically or require a manual start.
- Run in the foreground or background.
- Initialize Git submodules.
- Symlink the main checkout’s
.envinto the worktree. - Clear setup output automatically after completion.
Do not commit secrets to repository config. Use environment management appropriate to the project.
Copy ignored files into new worktrees
Section titled “Copy ignored files into new worktrees”A new worktree is a fresh checkout, so gitignored local files such as .env or config/local.json are missing. List them in a .worktreeinclude file at the root of the main checkout, using .gitignore syntax:
.env*!.env.exampleconfig/local.jsonWhen the app creates a worktree for a new branch, an existing branch, a pull request, or a shared context repository, it copies each file from the main checkout that matches a pattern and is also ignored by Git. The copy happens before setup runs, so setup commands can use the copied files.
- Files must be ignored through
.gitignore,.git/info/exclude, or your global Git excludes file. Patterns that match tracked or unignored files have no effect. - Patterns without a leading
/match at any depth, so.envalso copiesapps/web/.env. Use/.envto copy only the root file. - Files already present in the new worktree are never overwritten. Symbolic links are skipped.
- Copies are made once, at creation. Later edits in the main checkout do not reach existing worktrees.
- A file that fails to copy is skipped; worktree creation continues.
- The app reads
.worktreeincludefrom the main checkout’s working directory, so it works uncommitted. Commit it to share the list with collaborators.
To copy a whole ignored directory, list the directory, such as secrets/. To copy only part of it, name paths inside it, such as secrets/**/*.json or secrets/local.json.
Ignored directories that the list does not name, such as node_modules/, are skipped without being scanned. A bare file pattern such as .env therefore does not copy node_modules/pkg/.env.
The Symlink .env project setting adds a setup command that links the worktree’s .env to the main checkout’s file. Because setup runs after the copy, that link replaces a copied .env. Use the symlink when every worktree should share one live file, and .worktreeinclude when each worktree needs its own copy.
One run target
Section titled “One run target”The legacy array form remains valid:
{ "run": ["bun run dev"]}It creates one run target named Run. Multiple commands in the array execute sequentially as one target.
Multiple named run targets
Section titled “Multiple named run targets”Use object entries when a project has several services, watchers, or previews:
{ "run": [ { "name": "web", "commands": ["bun run dev"], "default": true }, { "name": "worker", "commands": ["bun run worker:dev"] }, { "name": "tests", "commands": ["bun run test:watch"] } ]}Each entry accepts:
name: the label shown in the run controls.commands: one or more commands, joined in order with&&.default: optional; marks the initial target.stop_commands: optional; commands that run when you stop the target, such asdocker compose stop.
When a target has stop_commands, stopping it runs each one in order, even if an earlier one fails, and then ends the target’s process. A failed stop command is reported, and the process still ends. Stop the target again to end it immediately. Without stop_commands, the app ends the process directly.
Use distinct, descriptive names. The right panel exposes the configured targets, shows each process’s state and output, and can keep several named services available within the same worktree.
When no run target exists, the panel offers Configure run script, which opens project settings.
Press ⌘R outside a browser tab to start the active run target, falling back to the configured default and then the first target. In a browser tab, the same shortcut reloads the page.
Shared custom commands
Section titled “Shared custom commands”Repository config can also carry custom commands for everyone who works in the checkout:
{ "commands": [ { "id": "review-diff", "name": "Review diff", "command": "Review the current diff for correctness bugs." }, { "id": "run-tests", "name": "Run tests", "command": "bun test", "provider_preset_key": "none" } ]}Each command requires:
id: a unique, stable identifier without surrounding spaces.name: the label shown in pickers.- ⌘: the prompt, or the shell text when the provider is
none.
Optional fields match the custom command options in Settings:
dispatch_mode:inject_current_chat(default) ornew_tab.provider_preset_key: a provider key such asclaudeorcodex;noneruns the text in a plain terminal. Runsc chat providersto list keys.model_idandreasoning_effort: model and thinking-level overrides for an explicit provider.icon: an icon name or emoji.auto_submit:trueby default;falseleaves the text ready to review.
Shared commands appear in a Shared group after Global and Workspace commands and before Project commands. The app rejects a config with a missing, blank, or duplicate id, or a blank name or ⌘. Edit shared commands in project settings under Shared commands, or with sc commands ... --scope shared.
Script variables
Section titled “Script variables”Insert these environment variables from app project settings or type them into a command:
| Variable | Value |
|---|---|
$SUPER_ENGINEERING_ROOT_PATH | Absolute path to the main repository root |
$SUPER_ENGINEERING_WORKSPACE_NAME | Current worktree directory name |
$SUPER_ENGINEERING_WORKSPACE_PATH | Absolute path to the current worktree |
$SUPER_ENGINEERING_WORKTREE_PATH | Same as $SUPER_ENGINEERING_WORKSPACE_PATH |
Every phase receives these variables, including teardown and both cleanup hooks. For example, this setup command links the main checkout’s .env into each worktree:
ln -sf "$SUPER_ENGINEERING_ROOT_PATH/.env" "$SUPER_ENGINEERING_WORKSPACE_PATH/.env"Quote path variables in shell commands because repository and worktree paths may contain spaces.
Teardown and cleanup
Section titled “Teardown and cleanup”Use teardown for a repository-defined service stop or local runtime cleanup:
{ "teardown": [ "docker compose down", "rm -rf tmp/app-preview" ]}Use personal app settings for guards or machine-specific cleanup:
pre_cleanupcan verify that important work was pushed or stop deletion by returning a nonzero exit.post_cleanupcan remove machine-local state after the worktree path is gone.
The app first stops run processes it owns, then follows the configured cleanup sequence. Read Worktree cleanup before adding destructive commands.
Config or app settings?
Section titled “Config or app settings?”Use repository config when a command is safe, reviewable project policy that every contributor should share. Use app project settings when a command is personal, contains machine-specific paths, depends on secrets, or runs automatically as a cleanup guard.
After changing config, create or reopen a worktree and confirm the resolved targets before relying on them in cleanup or automation.