{
  "schema_version": 1,
  "type": "skill",
  "slug": "adom-repo-management",
  "title": "Repo Management",
  "brief": "Create, list, and delete repos and workspaces on the Adom platform via the Carbon API.",
  "version": "1.0.0",
  "tags": [],
  "license": "MIT",
  "source_path": "SKILL.md",
  "readme": "---\nname: adom-repo-management\ndescription: Use when the user wants to create, list, or delete Adom repos and workspaces, provision containers, or manage projects on hydrogen.adom.inc. Triggers on \"create repo\", \"new adom repo\", \"create workspace\", \"provision container\", \"delete repo\", \"list repos\", \"list workspaces\".\n---\n\n# Adom Repo & Workspace Management\n\nCreate, list, and delete repos and workspaces on the Adom platform via the Carbon API.\n\n## Authentication\n\nAll Carbon and Hydrogen API calls require a `session_token` cookie. Every Adom container has an API key that works as a session token — **always check for it first before asking the user for credentials**.\n\n### Step 1: Use the container API key (preferred)\n\nEvery Adom container has a pre-provisioned API key at `/var/run/adom/api-key`. This key works as a `session_token` cookie for both Carbon and Hydrogen APIs.\n\n```bash\nAPI_KEY=$(cat /var/run/adom/api-key)\n\n# Use as session_token cookie for any API call:\ncurl -s -X POST 'https://carbon.adom.inc/user/repos' \\\n  -b \"session_token=$API_KEY\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"name\":\"my-repo\",\"description\":\"...\",\"private\":false}'\n```\n\n**Important**: Never echo or display the API key. Store it in a shell variable only.\n\n### Step 2: Check for cached session (fallback)\n\nIf `/var/run/adom/api-key` doesn't exist (rare — only outside Adom containers):\n\n```bash\ncat /home/adom/.config/adom-session.json 2>/dev/null\n```\n\nIf the file exists and `created_at` is within 30 days, use the stored `session_token`.\n\n### Step 3: Login (last resort)\n\nOnly if neither the API key file nor a cached session exists, ask the user for credentials:\n\n```bash\ncurl -s -X POST 'https://carbon.adom.inc/auth/login' \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"email\":\"USERNAME\",\"password\":\"PASSWORD\"}' \\\n  -c /tmp/adom-login-cookies \\\n  -D /tmp/adom-login-headers\n```\n\nExtract the `session_token` from the `Set-Cookie` header and save it:\n\n```json\n{\n  \"session_token\": \"TOKEN_VALUE\",\n  \"carbon_api\": \"https://carbon.adom.inc\",\n  \"hydrogen_api\": \"https://hydrogen.adom.inc\",\n  \"username\": \"USERNAME\",\n  \"created_at\": \"YYYY-MM-DD\",\n  \"expires_in_days\": 30\n}\n```\n\nWrite to `/home/adom/.config/adom-session.json`.\n\n## Create a Repo\n\n```bash\nSESSION=$(cat /home/adom/.config/adom-session.json | python3 -c \"import sys,json;print(json.load(sys.stdin)['session_token'])\")\n\n# User-owned repo (RECOMMENDED — container shows up on repo homepage)\ncurl -s -X POST 'https://carbon.adom.inc/user/repos' \\\n  -b \"session_token=$SESSION\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"name\":\"REPO_NAME\",\"description\":\"DESCRIPTION\",\"private\":false}'\n\n# Org-owned repo (container won't show on repo homepage — see note below)\ncurl -s -X POST 'https://carbon.adom.inc/orgs/adom/repos' \\\n  -b \"session_token=$SESSION\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"name\":\"REPO_NAME\",\"description\":\"DESCRIPTION\",\"private\":false}'\n```\n\n**Always prefer user-owned repos** (`POST /user/repos`). The Hydrogen repo page looks for a container matching `{owner}-{repo}`. Containers are always created under a **user** (e.g., `john`), not an org. So org-owned repos (e.g., `adom/kicad-cli`) can never show their container on the repo homepage because the container is `john-kicad-cli-*` but the page looks for `adom-kicad-cli`. User-owned repos (e.g., `john/kicad-cli`) don't have this mismatch.\n\n**Response (200):**\n```json\n{\n  \"id\": \"REPO_ID\",\n  \"name\": \"REPO_NAME\",\n  \"full_name\": \"adom/REPO_NAME\",\n  \"git_remote\": \"ssh://[email protected]:/REPO_ID.git\",\n  \"created_at\": \"...\"\n}\n```\n\n**Errors:**\n- 409: `REPO_ALREADY_EXISTS`\n- 401: Session expired — re-login\n- 422: Missing required fields\n\n## Create a Workspace (Hydrogen layout — NOT the Docker container)\n\n**Important**: A \"workspace\" in the Carbon API is the Hydrogen panel layout (what tabs/panels are shown). It does **NOT** provision a Docker container. Container provisioning is separate (see next section).\n\n```bash\ncurl -s -X POST 'https://carbon.adom.inc/workspaces' \\\n  -b \"session_token=$SESSION\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\n    \"repo_id\": \"REPO_ID\",\n    \"name\": \"WORKSPACE_NAME\",\n    \"owner\": {\n      \"owner_type\": \"repository\",\n      \"id\": \"REPO_ID\"\n    },\n    \"workspace_data\": {\n      \"focusedPanelId\": \"main\",\n      \"meta\": { \"name\": \"WORKSPACE_NAME\" },\n      \"root\": {\n        \"type\": \"leaf\",\n        \"id\": \"main\",\n        \"panelType\": \"adom/a1b2c3d4-eeee-4000-a000-00000000000e\",\n        \"activeTabIndex\": 0,\n        \"tabs\": [{\n          \"id\": \"tab-vscode\",\n          \"panelType\": \"adom/a1b2c3d4-eeee-4000-a000-00000000000e\"\n        }]\n      }\n    }\n  }'\n```\n\n**Field reference:**\n- `repo_id`: The repo's `id` from creation\n- `name`: Alphanumeric + hyphens only (no underscores, no spaces)\n- `owner.owner_type`: Always `\"repository\"`\n- `owner.id`: Same as `repo_id`\n- `workspace_data.root`: Panel layout tree. The example above creates a single VS Code panel\n- `panelType`: `adom/a1b2c3d4-eeee-4000-a000-00000000000e` = VS Code editor\n\n**Response (200):**\n```json\n{\n  \"id\": \"WORKSPACE_ID\",\n  \"name\": \"WORKSPACE_NAME\",\n  \"owner\": { \"type\": \"repository\", \"id\": \"REPO_ID\" },\n  \"workspace_data\": { ... },\n  \"created_at\": \"...\"\n}\n```\n\n## Create a Dev Container (provisions Docker container)\n\nAfter creating a repo, you need to provision its Docker container. This requires **two steps** via the Hydrogen API (not Carbon), and is separate from creating a workspace.\n\n### Step 1: Initialize the Curium project\n\nThis creates the git project on the Coder side. **Must be done before container creation** or you get \"Failed to perform recursive copy\".\n\n```bash\ncurl -s -X POST 'https://hydrogen.adom.inc/api/curium/create-project' \\\n  -b \"session_token=$SESSION\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"username\":\"USERNAME\",\"projectName\":\"REPO_NAME\"}'\n```\n\n- `username`: The user who will own the container (must be a valid Coder user, e.g., `john`)\n- `projectName`: The repo name (e.g., `kicad-cli`)\n\n**Response (200):**\n```json\n{\"success\": true, \"message\": \"Project created successfully\", \"details\": {\"username\": \"john\", \"projectName\": \"kicad-cli\"}}\n```\n\n### Step 2: Create the container\n\n```bash\ncurl -s -X POST 'https://hydrogen.adom.inc/api/containers/create' \\\n  -b \"session_token=$SESSION\" \\\n  -H 'Content-Type: application/json' \\\n  -d '{\"owner\":\"USERNAME\",\"repository\":\"REPO_NAME\",\"actor\":\"USERNAME\"}'\n```\n\n- `owner`: **Must match the `username` from Step 1** — the Coder user who will own the container\n- `repository`: The repo name (must match `projectName` from Step 1)\n- `actor`: Same as `owner` — the user performing the action\n\n**Important**: `owner` and `actor` should both be the **user's** username (e.g., `john`), NOT the org name (e.g., `adom`), even for org-owned repos. The Coder platform associates containers with users, not orgs. Using the org name as owner causes \"Failed to fetch user: 404\".\n\n**Response (201):**\n```json\n{\n  \"id\": \"john-kicad-cli-ba3ddf337bb1d9a6\",\n  \"owner\": \"john\",\n  \"repository\": \"kicad-cli\",\n  \"unique_hash\": \"ba3ddf337bb1d9a6\",\n  \"services\": {\n    \"coder_url\": \"https://coder.john-kicad-cli-ba3ddf337bb1d9a6.containers.adom.inc/\",\n    \"s3_url\": \"https://s3.john-kicad-cli-ba3ddf337bb1d9a6.containers.adom.inc/\",\n    \"ssh_credentials\": { \"hostname\": \"ssh.containers.adom.localhost\", \"port\": 2222, \"username\": \"john-kicad-cli-ba3ddf337bb1d9a6\" }\n  }\n}\n```\n\n### Check container status\n\n```bash\n# Get a specific container (ID format: {owner}-{repo})\ncurl -s 'https://hydrogen.adom.inc/api/containers/get/{owner}-{repo}' \\\n  -b \"session_token=$SESSION\"\n\n# List all containers\ncurl -s 'https://hydrogen.adom.inc/api/curium-proxy/containers' \\\n  -b \"session_token=$SESSION\"\n```\n\n**Container response:**\n```json\n{\n  \"id\": \"john-my-repo-d4d7f7f287915e49\",\n  \"owner\": \"john\",\n  \"repository\": \"my-repo\",\n  \"unique_hash\": \"d4d7f7f287915e49\",\n  \"status\": \"running\",\n  \"services\": {\n    \"coder_url\": \"https://coder.john-my-repo-d4d7f7f287915e49.containers.adom.inc/\",\n    \"s3_url\": \"https://s3.john-my-repo-d4d7f7f287915e49.containers.adom.inc/\",\n    \"ssh_credentials\": {\n      \"hostname\": \"ssh.containers.adom.localhost\",\n      \"port\": 2222,\n      \"username\": \"john-my-repo-d4d7f7f287915e49\"\n    }\n  }\n}\n```\n\n**Container URL pattern:**\n`https://coder.{owner}-{repo}-{hash}.containers.adom.inc/`\n\nThe hash is assigned by the platform. Check the container status via API or the Hydrogen UI at `https://hydrogen.adom.inc/{owner}/{repo}` to find the full URL.\n\n## List Repos\n\n```bash\n# All org repos\ncurl -s -b \"session_token=$SESSION\" 'https://carbon.adom.inc/orgs/adom/repos' | python3 -m json.tool\n\n# User repos (replace USERNAME with the actual username from adom-session.json)\ncurl -s -b \"session_token=$SESSION\" 'https://carbon.adom.inc/users/USERNAME/repos' | python3 -m json.tool\n\n# All public repos\ncurl -s 'https://carbon.adom.inc/repos' | python3 -m json.tool\n```\n\n## List Workspaces\n\n```bash\n# For a specific repo\ncurl -s -b \"session_token=$SESSION\" 'https://carbon.adom.inc/repos/{owner}/{name}/workspaces'\n\n# Workspace detail\ncurl -s -b \"session_token=$SESSION\" 'https://carbon.adom.inc/workspaces/{WORKSPACE_ID}'\n```\n\n## Delete Repo\n\n```bash\ncurl -s -X DELETE \"https://carbon.adom.inc/repos/{owner}/{name}\" \\\n  -b \"session_token=$SESSION\"\n```\n\nReturns 202 Accepted. This is destructive and irreversible — always confirm with the user first.\n\n## Delete Workspace\n\n```bash\ncurl -s -X DELETE \"https://carbon.adom.inc/workspaces/{WORKSPACE_ID}\" \\\n  -b \"session_token=$SESSION\"\n```\n\nReturns 204 No Content.\n\n## Push Code to a Repo\n\n### SSH key setup (one-time)\n\nCheck for existing key:\n```bash\nls /home/adom/.ssh/id_ed25519 2>/dev/null\n```\n\nIf missing, generate one:\n```bash\nssh-keygen -t ed25519 -N \"\" -f /home/adom/.ssh/id_ed25519\nssh-keyscan git.adom.inc >> /home/adom/.ssh/known_hosts 2>/dev/null\n```\n\nThe public key (`/home/adom/.ssh/id_ed25519.pub`) must be registered with the Adom platform. There is no API endpoint for this yet — tell the user to add it via the Hydrogen UI at Settings > SSH Keys, or ask them to paste the public key for manual setup.\n\n### Push\n\n```bash\ncd /path/to/service\ngit init\ngit remote add origin ssh://[email protected]:/REPO_ID.git\ngit add .\ngit commit -m \"Initial commit\"\ngit push -u origin main\n```\n\n## Full Workflow Example\n\nTo create a new project with a Docker container:\n\n1. **Create repo**: `POST /user/repos` with name and description (user-owned so container shows on repo page)\n2. **Init Curium project**: `POST /api/curium/create-project` with `{username: \"YOUR_USERNAME\", projectName: \"REPO_NAME\"}` — initializes git on Coder\n3. **Create container**: `POST /api/containers/create` with `{owner: \"YOUR_USERNAME\", repository: \"REPO_NAME\", actor: \"YOUR_USERNAME\"}` — provisions Docker container. Response includes `coder_url`\n4. **(Optional) Create workspace**: `POST /workspaces` with repo_id and layout — defines Hydrogen panel layout\n5. **Open in Hydrogen**: Navigate to `https://hydrogen.adom.inc/{username}/{repo-name}` or directly to the `coder_url` from step 3\n6. **Bootstrap**: In the new container's terminal (or via a Claude Code prompt), install dependencies and start services\n7. **(Optional) Set up SSH & push code**: If you need to push code from another container, generate SSH key, register via Hydrogen UI, then git push\n\n## Troubleshooting\n\n| Symptom | Cause | Fix |\n|---------|-------|-----|\n| 401 Unauthorized on any API call | Session token expired (30-day TTL) | Delete `/home/adom/.config/adom-session.json` and re-login |\n| 409 on repo creation | Repo name already exists in the org | Choose a different name or delete the existing repo first |\n| `WORKSPACE_NAME_INVALID` on workspace creation | Name contains underscores, spaces, or special characters | Use only alphanumeric characters and hyphens |\n| `git push` rejected with \"Permission denied (publickey)\" | SSH key not registered with the platform | Add the public key via Hydrogen UI at Settings > SSH Keys |\n| \"No Container\" shown on repo page | Container not provisioned yet | Run the 2-step container creation: first `POST /api/curium/create-project`, then `POST /api/containers/create` |\n| `Failed to fetch user: 404` on container create | `owner` is an org name instead of a username | Use the **user's** username as `owner` (e.g., `john`), not the org name (e.g., `adom`). Coder users are per-user, not per-org |\n| `Failed to perform recursive copy` on container create | Curium project not initialized | Run `POST /api/curium/create-project` with `{username, projectName}` before creating the container |\n| `missing field` errors on workspace creation | `workspace_data` schema is strict | Ensure all required fields are present: `focusedPanelId`, `meta.name`, `root.type`, `root.id`, `root.panelType`, `root.activeTabIndex`, `root.tabs` |\n\n## API Reference\n\n### Carbon API (`carbon.adom.inc`)\n\n| Endpoint | Method | Auth | Purpose |\n|----------|--------|------|---------|\n| `/auth/login` | POST | None | Login → session cookie |\n| `/user/repos` | POST | Cookie | Create user-owned repo (recommended) |\n| `/orgs/{org}/repos` | POST | Cookie | Create org-owned repo (container won't show on repo page) |\n| `/orgs/{org}/repos` | GET | Public | Org repos |\n| `/repos/{owner}/{name}` | GET | Public | Repo details |\n| `/repos/{owner}/{name}` | DELETE | Cookie | Delete repo |\n| `/repos/{owner}/{name}/workspaces` | GET | Public | List workspaces |\n| `/repos` | GET | Public | List all repos |\n| `/users/{name}/repos` | GET | Public | User repos |\n| `/workspaces` | POST | Cookie | Create workspace (Hydrogen layout) |\n| `/workspaces/{id}` | GET | Cookie | Workspace detail |\n| `/workspaces/{id}` | DELETE | Cookie | Delete workspace |\n| `/user` | GET | Cookie | Current user profile |\n\n### Hydrogen API (`hydrogen.adom.inc`)\n\n| Endpoint | Method | Auth | Purpose |\n|----------|--------|------|---------|\n| `/api/curium/create-project` | POST | Cookie | Initialize Curium project (body: `{username, projectName}`) — **must call before container create** |\n| `/api/containers/create` | POST | Cookie | Create dev container (body: `{owner, repository, actor}`) — owner/actor = username, not org |\n| `/api/containers/get/{owner}-{repo}` | GET | Cookie | Get container status/URL |\n| `/api/curium-proxy/containers` | GET | Cookie | List all containers |\n",
  "author": {
    "name": "Kyle Bergstedt",
    "email": "[email protected]"
  },
  "visibility": {
    "public": true
  },
  "hero": null,
  "sample_prompts": [],
  "discovery_triggers": [],
  "discovery_pitch": null,
  "metadata": {},
  "created_at": "2026-05-28T05:30:28.198Z",
  "updated_at": "2026-05-28T05:30:28.198Z",
  "sub_skills": [],
  "parent_app": null,
  "org": "adom"
}