---
name: gsd-surface
description: "Toggle which skills are surfaced — apply a profile, list, or disable a cluster without reinstall"
---
## A. Skill Invocation
- This skill is invoked when the user mentions `gsd-surface` or describes a task matching this skill.
- Treat all user text after the skill mention as `{{GSD_ARGS}}`.
- If no arguments are present, treat `{{GSD_ARGS}}` as empty.
## B. User Prompting
When the workflow needs user input, prompt the user conversationally:
- Present options as a numbered list in your response text
- Ask the user to reply with their choice
- For multi-select, ask for comma-separated numbers
## C. Tool Usage
Use these Cursor tools when executing GSD workflows:
- `Shell` for running commands (terminal operations)
- `StrReplace` for editing existing files
- `Read`, `Write`, `Glob`, `Grep`, `Task`, `WebSearch`, `WebFetch`, `TodoWrite` as needed
## D. Subagent Spawning
When the workflow needs to spawn a subagent:
- Use `Task(subagent_type="generalPurpose", ...)`
- The `model` parameter maps to Cursor's model options (e.g., "fast")
Manage the runtime skill surface without reinstall. Reads/writes `/home/domini/src/My/Surypus/.cursor/.gsd-surface.json`
(sibling to `/home/domini/src/My/Surypus/.cursor/.gsd-profile`) and re-stages the active skills directory in place.
Skill dirs live at `/home/domini/src/My/Surypus/.cursor/skills/gsd-*/`.
Sub-commands: list · status · profile · disable · enable · reset
## Sub-command routing
Parse the first token of {{GSD_ARGS}}:
| Token | Action |
|---|---|
| `list` | Show enabled + disabled clusters and skills |
| `status` | Alias for `list` plus token cost summary |
| `profile ` | Write `baseProfile` and re-stage |
| `profile ,` | Composed profiles (comma-separated, no spaces) |
| `disable ` | Add cluster to `disabledClusters`, re-stage |
| `enable ` | Remove cluster from `disabledClusters`, re-stage |
| `reset` | Delete `.gsd-surface.json`, return to install-time profile |
| *(none)* | Treat as `list` |
---
## list / status
Call `listSurface(runtimeConfigDir, manifest, CLUSTERS)` from
`get-shit-done/bin/lib/surface.cjs`. Display:
```
Enabled (N skills, ~T tokens):
core_loop: new-project discuss-phase plan-phase execute-phase help update
audit_review: …
…
Disabled:
utility: health stats settings …
Token cost: ~T (budget cap ~500 tokens for 200k context @ 1%)
```
For `status` also append:
```
Base profile: standard (from .gsd-surface.json)
Install profile: standard (from .gsd-profile)
```
---
## profile \
1. Read current surface: `readSurface(runtimeConfigDir)` → if null, seed from `readActiveProfile(runtimeConfigDir)`.
2. Set `surfaceState.baseProfile = name`.
3. `writeSurface(runtimeConfigDir, surfaceState)`.
4. Resolve and re-apply:
```js
const layout = resolveRuntimeArtifactLayout(runtime, runtimeConfigDir, scope);
applySurface(runtimeConfigDir, layout, manifest, CLUSTERS);
```
5. Confirm: "Surface updated to profile ``. N skills enabled."
---
## disable \
Valid cluster names: `core_loop`, `audit_review`, `milestone`, `research_ideate`,
`workspace_state`, `docs`, `ui`, `ai_eval`, `ns_meta`, `utility`.
1. Validate cluster name against `Object.keys(CLUSTERS)`.
2. Read or initialize surface state.
3. Add cluster to `surfaceState.disabledClusters` (deduplicate).
4. `writeSurface` → resolve layout → `applySurface`:
```js
const layout = resolveRuntimeArtifactLayout(runtime, runtimeConfigDir, scope);
applySurface(runtimeConfigDir, layout, manifest, CLUSTERS);
```
5. Confirm: "Disabled cluster ``. N skills removed from surface."
---
## enable \
1. Read surface state; if null, nothing to enable — print "No surface delta active."
2. Remove cluster from `surfaceState.disabledClusters`.
3. `writeSurface` → resolve layout → `applySurface`:
```js
const layout = resolveRuntimeArtifactLayout(runtime, runtimeConfigDir, scope);
applySurface(runtimeConfigDir, layout, manifest, CLUSTERS);
```
4. Confirm: "Enabled cluster ``. N skills added back to surface."
---
## reset
1. Check if `.gsd-surface.json` exists.
2. Delete it.
3. Re-apply using only `readActiveProfile(runtimeConfigDir)` (install-time profile).
4. Confirm: "Surface reset to install-time profile ``."
---
## runtimeConfigDir resolution
The `runtimeConfigDir` for `applySurface` is the **base Claude config directory**
(`~/.claude`), NOT the skills sub-directory (`/home/domini/src/My/Surypus/.cursor/skills`).
This matches `installRuntimeArtifacts` and `uninstallRuntimeArtifacts`, which also
receive `~/.claude` as `configDir`. The skill dirs themselves live at
`/home/domini/src/My/Surypus/.cursor/skills/gsd-*/` because the `claude global` layout has `destSubpath =
'skills'` — they are derived from `configDir`, not the root for it.
```bash
# Cursor — global install
RUNTIME_CONFIG_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
SCOPE="global"
# Artifact destinations are derived from runtime layout
# via resolveRuntimeArtifactLayout(runtime, RUNTIME_CONFIG_DIR, SCOPE)
# then applySurface(RUNTIME_CONFIG_DIR, layout, manifest, CLUSTERS)
```
Surface state is stored at `${RUNTIME_CONFIG_DIR}/.gsd-surface.json`
(i.e. `/home/domini/src/My/Surypus/.cursor/.gsd-surface.json`).
All paths can be overridden by reading the `CLAUDE_CONFIG_DIR` env var if set.
---
## Error handling
- Unknown cluster name → list valid cluster names, exit without writing.
- Unknown profile name → list known profiles (`core`, `standard`, `full`), exit.
- Missing `surface.cjs` → prompt: "Run `npm i -g get-shit-done` to reinstall GSD."
Surface state file: `/home/domini/src/My/Surypus/.cursor/.gsd-surface.json`
Install profile marker: `/home/domini/src/My/Surypus/.cursor/.gsd-profile`
Skill dirs: `/home/domini/src/My/Surypus/.cursor/skills/gsd-*/`
Engine module: `/home/domini/src/My/Surypus/.cursor/get-shit-done/bin/lib/surface.cjs`
Cluster definitions: `/home/domini/src/My/Surypus/.cursor/get-shit-done/bin/lib/clusters.cjs`