Skip to content

[Feature]: User-level t3.json for actions that apply to every project #6523

Description

@LunarRed

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I am describing a concrete problem or use case, not just a vague idea.

Area

apps/server

Problem or use case

Actions are stored per project checkout. project.created always writes scripts: [] (apps/server/src/orchestration/decider.ts:254), and nothing seeds them, so every new project and every additional checkout starts empty. Actions I want everywhere, like "Open Branch in Fork" or "Open in Xcode", have to be recreated or re-imported by hand each time.

Checking them into a repo's t3.json only partly helps. The import is still one click per checkout, and until it happens runOnWorktreeCreate: true silently does nothing, because the setup runner reads the project record rather than the file (ProjectSetupScriptRunner.ts:126). Nothing tells you the worktree was never initialized.

Proposed solution

Support a user-level t3.json in the server's state directory, at ~/.t3/userdata/t3.json, alongside keybindings.json and settings.json. It would carry a scripts array using the existing T3ProjectFileScript schema.

When a project is created, seed its actions from that file instead of the current hardcoded []. Seeded entries become ordinary project actions, so editing, keybindings, the worktree setup runner, and the mobile action list all work with no further changes. Seeding needs to happen where every create path passes, since normalizeDispatchCommand runs only in the transports (ws.ts, http.ts) and would miss auto-bootstrap (serverRuntimeStartup.ts:207) and the CLI (cli/project.ts:479).

Why this matters

Global actions become configurable once instead of once per checkout. These are per-user tool preferences rather than repo configuration, so a checked-in t3.json is the wrong home for them regardless of the import friction.

It also makes the existing global keybinding story work: ids are slugified from the action name (normalizeScriptId, apps/web/src/projectScripts.ts:35), so a global action named "Dev" is script.dev.run in every project, and a single rule in keybindings.json fires everywhere instead of dying silently in projects that lack the action.

Unlike #5063, this needs no repository trust gate. The file is user-authored and lives outside any repo, the same trust level as keybindings.json today.

Smallest useful scope

Read scripts from ~/.t3/userdata/t3.json at project creation and use it in place of the hardcoded empty array. New projects only, no settings UI, no editing from the app, no changes to the projection read path.

Alternatives considered

Merging the user-level actions into projects at read time would keep one live source of truth, but project shells are constructed in about five places in ProjectionSnapshotQuery.ts, and merged entries would need a source marker so persistScripts (which replaces the whole array) doesn't silently copy them into a project on the first edit. Worth doing later; too much for a first pass.

Merging in the clients is not smaller, since web and mobile both render project actions and the server-side setup runner still would not see them.

A global scripts array in settings.json works but splits action config across two formats when t3.json already defines this shape.

#5063 solves the adjacent repo-supplied case and needs a trust gate this does not.

Risks or tradeoffs

Seeding copies rather than links, so later edits to the user-level file will not reach existing projects. This matches the repo-level import, which is also a copy. A follow-up could add an "import from your global t3.json" entry to the existing import menu for projects that already exist, reusing importFileScript.

~/.t3 is on the machine running the server, so global actions are per-server, matching keybindings.json. Remote environments would each need their own file.

If the file defines an action with runOnWorktreeCreate: true, it becomes that project's setup action, since only one is allowed (setupProjectScript takes the first flagged one). Seeding is the only writer at creation time, so there is no conflict on day one, but the rule should be stated.

An invalid user-level file should be ignored with a logged warning, matching keybindings.json, rather than blocking project creation.

Examples or references

Related: #5063 (repo-supplied setup actions without per-user import), #5687 (editing or deleting an action leaves stale keybindings). #5090 proposes a dedicated "Open in" setting with a global default; a user-level t3.json would cover that case generically alongside any other per-user tooling action.

Contribution

  • I would be open to helping implement this.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions