Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ RUN npm install --global --prefix /usr/local \
# MCP configuration must work before the workspace volume has dependencies.
# jq extracts exactly the devDependencies the MCP script needs; the length guard
# fails the build when the manifest drops a required package.
COPY package.json /tmp/dxt-tooling-package.json
COPY tooling~/package.json /tmp/dxt-tooling-package.json
RUN mkdir -p /opt/dxt-mcp \
&& jq '{private: true, dependencies: (.devDependencies | with_entries(select(.key as $k | ["@modelcontextprotocol/sdk", "smol-toml", "jsonc-parser"] | index($k))))}' /tmp/dxt-tooling-package.json > /opt/dxt-mcp/package.json \
&& test "$(jq '.dependencies | length' /opt/dxt-mcp/package.json)" -eq 3 \
Expand Down
10 changes: 5 additions & 5 deletions .devcontainer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A fast, reliable VS Code devcontainer for this Unity UPM package. The Unity Editor
stays on the **host** (licensing + GUI); the container ships every CLI tool, drives the
host editor through the [unity-mcp bridge](../scripts/mcp/README.md), and needs **zero
host editor through the [unity-mcp bridge](../tooling~/scripts/mcp/README.md), and needs **zero
sudo** anywhere.

```
Expand Down Expand Up @@ -41,7 +41,7 @@ sudo** anywhere.
per-project port, so multiple editors never collide):

```bash
npm install # host checkout of this repo (one time)
npm --prefix tooling~ install # host checkout of this repo (one time)
npm run unity:mcp # bridge: host editor -> authenticated HTTP
```

Expand Down Expand Up @@ -143,7 +143,7 @@ Z.AI overrides: `CODEX_ZAI_MODEL`, `CODEX_ZAI_REASONING_EFFORT`, `CLAUDE_ZAI_*_M
`ZAI_API_TIMEOUT_MS` (default 3000000, matching the GLM coding-plan guidance),
`CLAUDE_ZAI_CONFIG_DIR`, `AI_BACKENDS_CONTAINER_MODE`,
`CLAUDE_ZAI_SUBPROCESS_ENV_SCRUB`. The launchers are regression-tested
(`scripts/tests/test-ai-backends.sh`).
(`tooling~/scripts/tests/test-ai-backends.sh`).

### OpenRouter (API key, any model)

Expand Down Expand Up @@ -181,7 +181,7 @@ from workspace watching; the workspace itself remains the package repository.
## Verify

```bash
npm test # node --test scripts/mcp/__tests__
bash scripts/tests/test-ai-backends.sh # Z.AI launcher regression suite
npm test # node --test tooling~/scripts/mcp/__tests__
bash tooling~/scripts/tests/test-ai-backends.sh # Z.AI launcher regression suite
npm run unity:mcp:probe # host editor readiness (bridge running)
```
4 changes: 2 additions & 2 deletions .devcontainer/cache-contract.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ readonly CACHE_MOUNT_TARGETS=(
"/home/vscode/.local/share/powershell"
"/home/vscode/.cache/pip"
"/home/vscode/.npm"
"${CACHE_WORKSPACE_ROOT}/node_modules"
"${CACHE_WORKSPACE_ROOT}/tooling~/node_modules"
)

cache_contract_validate_shape() {
Expand Down Expand Up @@ -106,7 +106,7 @@ cache_contract_repair_permissions() {
for target in "${CACHE_MOUNT_TARGETS[@]}" "${HOME}/.local"; do
cache_contract_repair_directory "$target" "$current_uid" "$current_gid" || return 1
done
for target in "${HOME}/.npmrc" "${CACHE_WORKSPACE_ROOT}/package-lock.json" \
for target in "${HOME}/.npmrc" "${CACHE_WORKSPACE_ROOT}/tooling~/package-lock.json" \
"${CACHE_WORKSPACE_ROOT}/package.json"; do
if [[ -f "$target" && ! -w "$target" ]]; then
# Host bind mounts can be writable without supporting ownership changes.
Expand Down
4 changes: 2 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// Project: DxCommandTerminal | Unity UPM package (min 2021.3, host editor 6000.4)
// The Unity Editor stays on the host; this container ships the .NET / Node /
// PowerShell toolchain plus every agent CLI and MCP server, and drives the host
// editor through the unity-mcp bridge (scripts/mcp/unity-mcp.mjs).
// editor through the unity-mcp bridge (tooling~/scripts/mcp/unity-mcp.mjs).
// =============================================================================
{
"name": "DxCommandTerminal Dev",
Expand Down Expand Up @@ -41,7 +41,7 @@
"source=dxt-powershell-modules,target=/home/vscode/.local/share/powershell,type=volume",
"source=dxt-python-cache,target=/home/vscode/.cache/pip,type=volume",
"source=dxt-npm-cache,target=/home/vscode/.npm,type=volume",
"source=dxt-node-modules,target=${containerWorkspaceFolder}/node_modules,type=volume",
"source=dxt-node-modules,target=${containerWorkspaceFolder}/tooling~/node_modules,type=volume",
"source=${localWorkspaceFolder}/../..,target=/unity-project,type=bind,consistency=cached"
],
// ---------------------------------------------------------------------------
Expand Down
5 changes: 3 additions & 2 deletions .devcontainer/post-create.sh
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ EOF
}

configure_agent_mcps() {
local configure=(node "${WORKSPACE_DIR}/scripts/mcp/unity-mcp.mjs" configure --offline)
local configure=(node "${WORKSPACE_DIR}/tooling~/scripts/mcp/unity-mcp.mjs" configure --offline)
if command -v flock >/dev/null 2>&1; then
flock -w 180 "${MCP_CONFIGURE_LOCK}" "${configure[@]}"
return
Expand Down Expand Up @@ -228,7 +228,8 @@ main() {
cd "${WORKSPACE_DIR}"
run_optional "Restoring .NET local tools (CSharpier)" dotnet tool restore
# npm install reuses the persistent modules tree; npm ci would remove it.
run_optional "Installing workspace npm dependencies" npm install --prefer-offline --no-audit --no-fund
# The npm project lives under tooling~/ so Unity never imports node_modules.
run_optional "Installing workspace npm dependencies" npm --prefix tooling~ install --prefer-offline --no-audit --no-fund
run_optional "Configuring MCP servers for every agent front end" configure_agent_mcps
run_optional "Installing Z.AI and OpenRouter agent launchers" \
bash "${SCRIPT_DIR}/ai-backends.sh" install
Expand Down
2 changes: 1 addition & 1 deletion .devcontainer/post-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export NPM_CONFIG_PREFIX="${HOME}/.local"

# Configuration is local and works with the image's baked dependencies before npm
# install. Finish it before clients start, without probing a sleeping host.
mcp_script="${SCRIPT_DIR}/../scripts/mcp/unity-mcp.mjs"
mcp_script="${SCRIPT_DIR}/../tooling~/scripts/mcp/unity-mcp.mjs"
mcp_lock="${TMPDIR:-/tmp}/dxt-mcp-configure.lock"
if command -v flock >/dev/null 2>&1; then
flock -w 30 "${mcp_lock}" node "${mcp_script}" configure --offline
Expand Down
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ Editor
Tests
.llm
.qodo
tooling~/node_modules
4 changes: 2 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,9 @@ indent_size = 4
tab_width = 4

# LLM context and tooling: UTF-8 without BOM + LF for byte-stable cross-OS
# generation (enforced by scripts/lint-llm-instructions.ps1). Overrides the
# generation (enforced by tooling~/scripts/lint-llm-instructions.ps1). Overrides the
# repo-wide BOM/CRLF defaults, which remain for C# assets.
[{.llm/**,scripts/**,.devcontainer/**,.github/copilot-instructions.md,AGENTS.md,CLAUDE.md,.cursorrules}]
[{.llm/**,tooling~/**,.devcontainer/**,.github/copilot-instructions.md,AGENTS.md,CLAUDE.md,.cursorrules}]
charset = utf-8
end_of_line = lf
insert_final_newline = true
Expand Down
2 changes: 1 addition & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Matches the .editorconfig overrides; keeps generated content and shell
# scripts intact on CRLF-configured checkouts (autocrlf=true).
.llm/** text eol=lf
scripts/** text eol=lf
tooling~/** text eol=lf
AGENTS.md text eol=lf
CLAUDE.md text eol=lf
.cursorrules text eol=lf
Expand Down
3 changes: 3 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ updates:
- wallstop
reviewers:
- wallstop
# Repo policy: all action refs are pinned to commit SHAs with a "# vX"
# version comment (issue #39). Dependabot updates SHA-pinned refs and
# preserves the comment; keep the comment in sync with each bump.
32 changes: 16 additions & 16 deletions .github/workflows/llm-instructions-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ on:
- "CLAUDE.md"
- ".cursorrules"
- ".github/copilot-instructions.md"
- "scripts/generate-skills-index.ps1"
- "scripts/lint-llm-instructions.ps1"
- "scripts/lint-skill-sizes.ps1"
- "scripts/tests/**"
- "tooling~/scripts/generate-skills-index.ps1"
- "tooling~/scripts/lint-llm-instructions.ps1"
- "tooling~/scripts/lint-skill-sizes.ps1"
- "tooling~/scripts/tests/**"
- ".github/workflows/llm-instructions-lint.yml"
pull_request:
paths:
Expand All @@ -23,10 +23,10 @@ on:
- "CLAUDE.md"
- ".cursorrules"
- ".github/copilot-instructions.md"
- "scripts/generate-skills-index.ps1"
- "scripts/lint-llm-instructions.ps1"
- "scripts/lint-skill-sizes.ps1"
- "scripts/tests/**"
- "tooling~/scripts/generate-skills-index.ps1"
- "tooling~/scripts/lint-llm-instructions.ps1"
- "tooling~/scripts/lint-skill-sizes.ps1"
- "tooling~/scripts/tests/**"
- ".github/workflows/llm-instructions-lint.yml"

permissions:
Expand All @@ -49,39 +49,39 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6

- name: Lint LLM instructions
shell: pwsh
run: ./scripts/lint-llm-instructions.ps1 -VerboseOutput
run: ./tooling~/scripts/lint-llm-instructions.ps1 -VerboseOutput

- name: Check skill file sizes
shell: pwsh
run: ./scripts/lint-skill-sizes.ps1 -VerboseOutput
run: ./tooling~/scripts/lint-skill-sizes.ps1 -VerboseOutput

- name: Test LLM instructions lint
shell: pwsh
run: ./scripts/tests/test-llm-instructions-lint.ps1 -VerboseOutput
run: ./tooling~/scripts/tests/test-llm-instructions-lint.ps1 -VerboseOutput

- name: Test skill size linter
shell: pwsh
run: ./scripts/tests/test-lint-skill-sizes.ps1 -VerboseOutput
run: ./tooling~/scripts/tests/test-lint-skill-sizes.ps1 -VerboseOutput

- name: Test skills index generator
shell: pwsh
run: ./scripts/tests/test-generate-skills-index.ps1 -VerboseOutput
run: ./tooling~/scripts/tests/test-generate-skills-index.ps1 -VerboseOutput

- name: Validate skills index is up-to-date
# Regenerate the file and fail if it differs from the committed bytes.
# --porcelain catches BOTH a drifted (modified) and a never-committed
# (untracked) index.md, unlike `git diff` which ignores untracked files.
shell: pwsh
run: |
./scripts/generate-skills-index.ps1
./tooling~/scripts/generate-skills-index.ps1
if ($LASTEXITCODE) { exit $LASTEXITCODE }
$status = git status --porcelain -- .llm/skills/index.md
if ($status) {
Write-Host "::error::.llm/skills/index.md is out of date or uncommitted. Run: pwsh -NoProfile -File scripts/generate-skills-index.ps1 and commit it."
Write-Host "::error::.llm/skills/index.md is out of date or uncommitted. Run: pwsh -NoProfile -File tooling~/scripts/generate-skills-index.ps1 and commit it."
git diff -- .llm/skills/index.md
exit 1
}
Expand Down
36 changes: 29 additions & 7 deletions .github/workflows/tooling-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ on:
- main
paths:
- "package.json"
- "scripts/**"
- "tooling~/**"
- ".github/workflows/tooling-tests.yml"
pull_request:
paths:
- "package.json"
- "scripts/**"
- "tooling~/**"
- ".github/workflows/tooling-tests.yml"

permissions:
Expand All @@ -30,7 +30,9 @@ jobs:
#
# Installs with `npm install`: no lockfile is committed for this Unity
# package (reviewer decision), so there is nothing for `npm ci` to pin
# and setup-node's npm cache cannot be keyed.
# and setup-node's npm cache cannot be keyed. The npm project lives under
# `tooling~/` (Unity-hidden via the tilde suffix, so editors never import
# node_modules); root package.json scripts delegate with `npm --prefix`.
name: node-tests (${{ matrix.os }})
strategy:
fail-fast: false
Expand All @@ -39,15 +41,35 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6

- name: Setup Node
uses: actions/setup-node@v6
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: 24

- name: Install dependencies
run: npm install --no-audit --no-fund
run: npm --prefix tooling~ install --no-audit --no-fund

- name: Test tooling
run: npm test
run: npm --prefix tooling~ test

package-content:
# Clean-install guard for the UPM artifact (issue #22, PLAN.md T02
# "package-content validators"): packs the package exactly like npm/UPM
# consumers receive it and asserts required artifacts ship, tooling and
# repo-internal files do not, and every shipped file carries its .meta.
# Unity-free: node stdlib + npm pack + system tar only.
name: package-content
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6

- name: Setup Node
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: 24

- name: Validate package contents
run: npm --prefix tooling~ run package:validate
4 changes: 2 additions & 2 deletions .github/workflows/unity-meta-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6

- name: Lint Unity meta hygiene
shell: pwsh
run: ./scripts/lint-unity-meta.ps1
run: ./tooling~/scripts/lint-unity-meta.ps1
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ node_modules/

# npm lockfiles are intentionally not committed for Unity packages (reviewer
# decision): the package ships no Node code, and a lockfile would churn with
# every dev environment. Install with `npm install` to work on the tooling.
# every dev environment. Install with `npm --prefix tooling~ install` to work on the tooling (the npm
# project lives under tooling~/ so Unity never imports node_modules).
package-lock.json

# Local credentials - never commit (preferred credential source; see .env.example)
Expand Down
22 changes: 12 additions & 10 deletions .llm/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,18 @@ Styles/ # USS/TSS stylesheets consumed by Terminal
Tests/Runtime/ # PlayMode tests (asmdef: ...Tests.Runtime)
Components/ # Test harness components (TestCommands, TerminalInputHandler)
Media/ # Screenshots and demo GIFs
scripts/ # Repo tooling (PowerShell + Node; not shipped in the UPM artifact)
mcp/ # unity-mcp.mjs: Unity MCP bridge/probe/configure/capture + tests
tests/ # Script regression suites (pwsh + bash)
tooling~/ # Unity-hidden tooling (tilde-suffixed; not shipped in the UPM artifact)
package.json # npm manifest (devDependencies; root package.json delegates via --prefix)
scripts/ # Repo tooling (PowerShell + Node)
mcp/ # unity-mcp.mjs: Unity MCP bridge/probe/configure/capture + tests
tests/ # Script regression suites (pwsh + bash)
.devcontainer/ # VS Code devcontainer (Dockerfile, lifecycle, Z.AI launchers)
```

### Dev tooling (scripts/, .devcontainer/)
### Dev tooling (tooling~/scripts/, .devcontainer/)

`npm run unity:mcp:probe|configure|bridge|capture` drives the host Unity editor
over the authenticated MCP bridge in `scripts/mcp/unity-mcp.mjs` (see the
over the authenticated MCP bridge in `tooling~/scripts/mcp/unity-mcp.mjs` (see the
[unity-mcp](./skills/unity-mcp/SKILL.md) and
[capture-unity-state](./skills/capture-unity-state/SKILL.md) skills). Credentials
live in gitignored `.env.local` (see `.env.example`); agent MCP configs are
Expand All @@ -76,8 +78,8 @@ generated, never hand-edited; `npm test` runs the Node tooling suite.
## Skills Reference

See the generated [Skills Index](./skills/index.md). Regenerate it after adding or editing any
skill: `pwsh -NoProfile -File scripts/generate-skills-index.ps1` (validated by
`scripts/lint-llm-instructions.ps1`).
skill: `pwsh -NoProfile -File tooling~/scripts/generate-skills-index.ps1` (validated by
`tooling~/scripts/lint-llm-instructions.ps1`).

### SKILL.md Contract

Expand Down Expand Up @@ -164,13 +166,13 @@ frontmatter validity, index freshness, and pointer-file delegation; see

1. **Line limits**: every authored file under `.llm/` MUST stay at or below 300 lines; 270+ gets
a critical warning. The generated `.llm/skills/index.md` is exempt (machine-written).
Enforced by `scripts/lint-skill-sizes.ps1` (pre-commit + CI + tests).
Enforced by `tooling~/scripts/lint-skill-sizes.ps1` (pre-commit + CI + tests).
2. **SKILL.md validity + index freshness + pointer delegation**: enforced by
`scripts/lint-llm-instructions.ps1` (pre-commit + CI + tests).
`tooling~/scripts/lint-llm-instructions.ps1` (pre-commit + CI + tests).
3. **Generated files are byte-stable**: UTF-8 without BOM, LF line endings, ordinal sorting, no
timestamps. Never hand-edit `.llm/skills/index.md`.
4. **Encoding overrides**: `.editorconfig` forces UTF-8 (no BOM) + LF for `.llm/**` and
`scripts/**` regardless of the repo defaults for C# assets.
`tooling~/**` regardless of the repo defaults for C# assets.
5. `.editorconfig` charset/line-ending defaults for C# assets remain BOM/CRLF per repo
convention; only the LLM-context paths above are overridden.

Expand Down
2 changes: 1 addition & 1 deletion .llm/skills/capture-unity-state/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ manifest to complete. Artifacts land inside this package's `.artifacts/unity-sta
passing from where you run capture.
2. Capture script compiled in the editor. Install it host-side with
`npm run unity:mcp:install-capture -- --project <host-project>` (it copies
`scripts/mcp/DxTerminalStateCapture.cs.txt` to `<project>/Assets/Editor/`,
`tooling~/scripts/mcp/DxTerminalStateCapture.cs.txt` to `<project>/Assets/Editor/`,
backing up any previous copy under `.artifacts/unity-state/backup/`). `capture`
performs this install itself when the project directory is reachable locally
(host runs, or the container's `/unity-project` bind mount).
Expand Down
2 changes: 1 addition & 1 deletion .llm/skills/create-unity-meta/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ TextScriptImporter:

## Verification

Run `pwsh -NoProfile -File scripts/lint-unity-meta.ps1` (also enforced by pre-commit and
Run `pwsh -NoProfile -File tooling~/scripts/lint-unity-meta.ps1` (also enforced by pre-commit and
CI). It checks, over tracked files only:

1. Every tracked `.meta` has its target tracked (no orphan metas for gitignored or
Expand Down
2 changes: 1 addition & 1 deletion .llm/skills/formatting-and-linting/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ on every commit and fails fast when tools are missing.
- Modifier order: `public private protected internal file new static abstract virtual sealed
readonly override extern unsafe volatile async required`.
- Files: UTF-8 BOM + CRLF for C# assets (repo default). Do not "fix" line endings on untouched
files - that pollutes diffs. Exceptions: `.llm/**` and `scripts/**` are UTF-8 no BOM + LF
files - that pollutes diffs. Exceptions: `.llm/**` and `tooling~/scripts/**` are UTF-8 no BOM + LF
(enforced by `.editorconfig` overrides and the LLM linters).

## Pre-commit hooks
Expand Down
Loading