gsd-surface
SkillDev toolsLets your agent switch which skills are active by applying profiles or disabling groups without reinstalling.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the gsd-surface skill
About this capability
Toggle which skills are surfaced — apply a profile, list, or disable a cluster without reinstall
What this skill tells your AI
The instructions your AI receives, as published by open-gsd/gsd-core in skills/gsd-surface/SKILL.md and read by ahel’s review.
Sub-commands: list · status · profile · disable · enable · reset
Sub-command routing
Parse the first token of $ARGUMENTS:
| Token | Action |
|---|---|
list | Show enabled + disabled clusters and skills |
status | Alias for list plus token cost summary |
profile <name> | Write baseProfile and re-stage |
profile <n1>,<n2> | Composed profiles (comma-separated, no spaces) |
disable <cluster> | Add cluster to disabledClusters, re-stage |
enable <cluster> | Remove cluster from disabledClusters, re-stage |
reset | Delete .gsd-surface.json, return to install-time profile |
| (none) | Treat as list |
list / status
Load the capability registry and call listSurface(runtimeConfigDir, manifest, CLUSTERS, registry) from
the engine module at ${runtimeConfigDir}/gsd-core/bin/lib/surface.cjs. The registry is loaded via:
const registry = require(runtimeConfigDir + '/gsd-core/bin/lib/capability-registry.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)
Mutation protocol
Derive the next surfaceState in memory and pass it to applySurface as
opts.surfaceState. Do not call writeSurface first: applySurface stages all
artifact kinds before mutation and publishes the candidate state only after
materialization succeeds. Pass null to reset to the install-time profile.
profile <name>
- Read current surface:
readSurface(runtimeConfigDir)→ if null, seed fromreadActiveProfile(runtimeConfigDir). - Set
surfaceState.baseProfile = name. - Keep the new state in memory; do not write it directly.
- Resolve and re-apply:
const registry = require(runtimeConfigDir + '/gsd-core/bin/lib/capability-registry.cjs'); const layout = resolveRuntimeArtifactLayout(runtime, runtimeConfigDir, scope); applySurface(runtimeConfigDir, layout, manifest, CLUSTERS, registry, { surfaceState }); - Confirm: "Surface updated to profile
<name>. N skills enabled."
disable <cluster>
Valid cluster names: core_loop, audit_review, milestone, research_ideate,
workspace_state, docs, ui, ai_eval, ns_meta, utility.
- Validate cluster name against
Object.keys(CLUSTERS). - Read or initialize surface state.
- Add cluster to
surfaceState.disabledClusters(deduplicate). - Resolve layout and apply the in-memory candidate:
const registry = require(runtimeConfigDir + '/gsd-core/bin/lib/capability-registry.cjs'); const layout = resolveRuntimeArtifactLayout(runtime, runtimeConfigDir, scope); applySurface(runtimeConfigDir, layout, manifest, CLUSTERS, registry, { surfaceState }); - Confirm: "Disabled cluster
<cluster>. N skills removed from surface."
enable <cluster>
- Read surface state; if null, nothing to enable — print "No surface delta active."
- Remove cluster from
surfaceState.disabledClusters. - Resolve layout and apply the in-memory candidate:
const registry = require(runtimeConfigDir + '/gsd-core/bin/lib/capability-registry.cjs'); const layout = resolveRuntimeArtifactLayout(runtime, runtimeConfigDir, scope); applySurface(runtimeConfigDir, layout, manifest, CLUSTERS, registry, { surfaceState }); - Confirm: "Enabled cluster
<cluster>. N skills added back to surface."
reset
- Check if
.gsd-surface.jsonexists. - Do not delete it directly.
- Re-apply with
{ surfaceState: null }; the state file is removed only after the install-time profile materializes successfully. - Confirm: "Surface reset to install-time profile
<name>."
runtimeConfigDir resolution
The runtimeConfigDir for applySurface is the base Claude config directory
(~/.claude), NOT the skills sub-directory (~/.claude/skills).
This matches installRuntimeArtifacts and uninstallRuntimeArtifacts, which also
receive ~/.claude as configDir. The skill dirs themselves live at
~/.claude/skills/gsd-*/ because the claude global layout has destSubpath = 'skills' — they are derived from configDir, not the root for it.
# Claude Code — 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. ~/.claude/.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: "Runnpm i -g @opengsd/gsd-coreto reinstall GSD."
<execution_context>
Surface state file: ~/.claude/.gsd-surface.json
Install profile marker: ~/.claude/.gsd-profile
Skill dirs: ~/.claude/skills/gsd-*/
Engine module: ~/.claude/gsd-core/bin/lib/surface.cjs
Cluster definitions: ~/.claude/gsd-core/bin/lib/clusters.cjs
</execution_context>
Signals
- GitHub stars
- 9k
- Forks
- 680
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
gsd-surface- Source
- github.com/open-gsd/gsd-core