@gallopsystems/agent-skills 1.1.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -0
- package/commands/contribute-skill.md +65 -0
- package/package.json +2 -1
- package/plugins/copier-template/.claude-plugin/plugin.json +8 -0
- package/plugins/copier-template/skills/copier-template/SKILL.md +74 -0
- package/plugins/copier-template/skills/copier-template/applying-updates.md +87 -0
- package/plugins/copier-template/skills/copier-template/template-authoring.md +89 -0
- package/plugins/doctl/.claude-plugin/plugin.json +2 -2
- package/plugins/doctl/skills/doctl/SKILL.md +80 -48
- package/plugins/doctl/skills/doctl/other-services.md +56 -0
- package/plugins/doctl/skills/doctl/spec-management.md +74 -0
- package/plugins/git-github/.claude-plugin/plugin.json +8 -0
- package/plugins/git-github/skills/git-github/SKILL.md +91 -0
- package/plugins/git-github/skills/git-github/actions-debugging.md +102 -0
- package/plugins/git-github/skills/git-github/external-review.md +39 -0
- package/plugins/git-github/skills/git-github/getting-unstuck.md +106 -0
- package/plugins/git-github/skills/git-github/gh-api-recipes.md +73 -0
- package/plugins/git-github/skills/git-github/releases.md +53 -0
- package/plugins/kysely-postgres/skills/kysely-postgres/SKILL.md +6 -0
- package/plugins/linear/skills/linear/SKILL.md +6 -0
- package/plugins/nitro-testing/skills/nitro-testing/SKILL.md +6 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/SKILL.md +7 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/composables-utils.md +5 -0
- package/plugins/nuxt-nitro-api/skills/nuxt-nitro-api/formatters.md +139 -0
package/README.md
CHANGED
|
@@ -15,6 +15,9 @@ Then install the skills you want:
|
|
|
15
15
|
/plugin install nuxt-nitro-api@gallop-systems-agent-skills
|
|
16
16
|
/plugin install nitro-testing@gallop-systems-agent-skills
|
|
17
17
|
/plugin install linear@gallop-systems-agent-skills
|
|
18
|
+
/plugin install doctl@gallop-systems-agent-skills
|
|
19
|
+
/plugin install git-github@gallop-systems-agent-skills
|
|
20
|
+
/plugin install copier-template@gallop-systems-agent-skills
|
|
18
21
|
```
|
|
19
22
|
|
|
20
23
|
## Updating
|
|
@@ -111,6 +114,60 @@ Covers:
|
|
|
111
114
|
- Async/automation testing utilities
|
|
112
115
|
- CI/CD setup with GitHub Actions and PostgreSQL
|
|
113
116
|
|
|
117
|
+
### linear
|
|
118
|
+
|
|
119
|
+
Create, triage, and manage Linear issues following team conventions, with a GraphQL CLI for operations the Linear MCP server doesn't expose.
|
|
120
|
+
|
|
121
|
+
Covers:
|
|
122
|
+
- Issue creation and triage conventions
|
|
123
|
+
- Tech-stack labels
|
|
124
|
+
- A `linear.mjs` CLI for GraphQL operations
|
|
125
|
+
|
|
126
|
+
### doctl
|
|
127
|
+
|
|
128
|
+
Manage DigitalOcean resources with the doctl CLI.
|
|
129
|
+
|
|
130
|
+
Covers:
|
|
131
|
+
- Auth contexts (per-command `--context` over stateful switching)
|
|
132
|
+
- Resolving the current git repo to its DO context + app
|
|
133
|
+
- App Platform: deployments, bounded polling, logs and their retention quirks
|
|
134
|
+
- App specs: env var/secret round-trips, validation, creating apps
|
|
135
|
+
- `--format` / `-o json` gotchas
|
|
136
|
+
- Managed databases, Spaces keys, droplets, DNS
|
|
137
|
+
|
|
138
|
+
### git-github
|
|
139
|
+
|
|
140
|
+
Git and GitHub (gh CLI) workflows for agents.
|
|
141
|
+
|
|
142
|
+
Covers:
|
|
143
|
+
- Ground rules and the branch → commit → PR loop
|
|
144
|
+
- Reading PR and CI state (`gh pr view --json`, `gh pr checks`)
|
|
145
|
+
- Debugging failed GitHub Actions runs (the full playbook)
|
|
146
|
+
- Repair ladders: rejected pushes, rebase-after-squash-merge, shallow clones, worktrees
|
|
147
|
+
- `gh api` recipes: PR comments, no-checkout file reads, repo settings, PAT gotchas
|
|
148
|
+
- Releases: tags, npm Trusted Publishing, release-please
|
|
149
|
+
- External review loop with the codex CLI
|
|
150
|
+
|
|
151
|
+
### copier-template
|
|
152
|
+
|
|
153
|
+
Maintain a Copier project template and propagate updates to generated repos.
|
|
154
|
+
|
|
155
|
+
Covers:
|
|
156
|
+
- Template anatomy: copier.yml, conditional files, tasks, jinja escaping in CI workflows
|
|
157
|
+
- Testing template changes (`--vcs-ref HEAD`, generate-and-validate)
|
|
158
|
+
- Releasing versions (tag + GitHub Release) and the update-checker workflow pattern
|
|
159
|
+
- Applying `copier update` in descendants: conflict triage, `.rej` files, validation
|
|
160
|
+
|
|
161
|
+
## Contributing a Lesson Back
|
|
162
|
+
|
|
163
|
+
Every skill ends with a **Contributing Back** section: when Claude works through
|
|
164
|
+
something the skill didn't cover, it offers to contribute the lesson upstream. The
|
|
165
|
+
`/contribute-skill` command (shipped in this package and symlinked into
|
|
166
|
+
`.claude/commands/` on install) automates the flow: distill the generic lesson,
|
|
167
|
+
privacy-sweep it, clone or fork this repo, and open a PR against the right skill
|
|
168
|
+
file. PRs from forks are welcome — content must be generic (placeholders only, no
|
|
169
|
+
project-specific names, IDs, or domains).
|
|
170
|
+
|
|
114
171
|
## Adding New Skills
|
|
115
172
|
|
|
116
173
|
1. Create a new plugin directory: `plugins/my-skill/`
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Contribute a lesson learned this session back to the gallop-systems/agent-skills repo as a PR
|
|
3
|
+
argument-hint: [which skill and/or what lesson]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Contribute a Lesson Back to the Skills Repo
|
|
7
|
+
|
|
8
|
+
You are turning something learned in this session into a PR against
|
|
9
|
+
https://github.com/gallop-systems/agent-skills, the public repo behind the
|
|
10
|
+
installed skills.
|
|
11
|
+
|
|
12
|
+
## 1. Identify the lesson
|
|
13
|
+
|
|
14
|
+
From this session (or from `$ARGUMENTS` if given), pin down:
|
|
15
|
+
|
|
16
|
+
- **The lesson**: usually an error→fix sequence, a behavior that contradicted the
|
|
17
|
+
skill, or a workflow knot the skill didn't cover. It must be something you
|
|
18
|
+
*verified in this session* — not a guess.
|
|
19
|
+
- **The target**: which skill (`doctl`, `git-github`, `copier-template`,
|
|
20
|
+
`kysely-postgres`, `nuxt-nitro-api`, `nitro-testing`, `linear`), and within it,
|
|
21
|
+
whether it belongs in `SKILL.md` or one of its reference `.md` files. Read the
|
|
22
|
+
target file first and match its structure and tone.
|
|
23
|
+
|
|
24
|
+
If the lesson is ambiguous or you can't verify it, stop and clarify with the user.
|
|
25
|
+
|
|
26
|
+
## 2. Genericize — the repo is public
|
|
27
|
+
|
|
28
|
+
Rewrite the lesson with placeholders only: `<app-id>`, `<owner>/<repo>`, `<branch>`,
|
|
29
|
+
`<domain>`. **No project names, client names, UUIDs, IPs, domains, tokens, or file
|
|
30
|
+
paths from the user's codebase.** Keep it tight: the generic rule, a minimal example
|
|
31
|
+
command, and the failure it prevents — in that order.
|
|
32
|
+
|
|
33
|
+
## 3. Clone, edit, verify
|
|
34
|
+
|
|
35
|
+
Work in a temp directory, never in the user's project:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
dir=$(mktemp -d)
|
|
39
|
+
if [ "$(gh api repos/gallop-systems/agent-skills --jq .permissions.push)" = "true" ]; then
|
|
40
|
+
gh repo clone gallop-systems/agent-skills "$dir"
|
|
41
|
+
else
|
|
42
|
+
gh repo fork gallop-systems/agent-skills --clone "$dir" # outside contributors
|
|
43
|
+
fi
|
|
44
|
+
cd "$dir" && git checkout -b feat/<skill>-<short-slug>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Edit the target file under `plugins/<skill>/skills/<skill>/`. Then privacy-sweep
|
|
48
|
+
your diff before committing — grep the changed files for anything resembling the
|
|
49
|
+
user's project (project name, org, hostnames, IDs). If anything hits, fix it.
|
|
50
|
+
|
|
51
|
+
## 4. Commit and open the PR
|
|
52
|
+
|
|
53
|
+
The repo enforces Conventional Commit PR titles (release-please derives versions
|
|
54
|
+
from them). Use `feat(<skill>): <summary>` for new coverage, `fix(<skill>): <summary>`
|
|
55
|
+
for corrections to existing content.
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
git add -A && git commit -m "feat(<skill>): document <lesson summary>"
|
|
59
|
+
git push -u origin <branch>
|
|
60
|
+
gh pr create --repo gallop-systems/agent-skills \
|
|
61
|
+
--title "feat(<skill>): <summary>" \
|
|
62
|
+
--body "<what the lesson is, how it was hit and verified (genericized), why it belongs in this skill>"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Show the user the PR URL, then clean up: `cd - && rm -rf "$dir"`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gallopsystems/agent-skills",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Gallop Systems Claude Code skills, symlinked into .claude/skills on install.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"plugins",
|
|
12
|
+
"commands",
|
|
12
13
|
"scripts/link-skills.mjs",
|
|
13
14
|
"README.md"
|
|
14
15
|
],
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "copier-template",
|
|
3
|
+
"description": "Maintain a Copier project template and propagate updates to generated repos: template anatomy, testing changes, releasing versions, and applying copier update in descendants with conflict resolution.",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "yeedle"
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: copier-template
|
|
3
|
+
description: Maintain a Copier project template and propagate updates to generated ("descendant") repos. Covers template anatomy (copier.yml, jinja, tasks), testing template changes, tagging/releasing versions, the automated update-notification PR pattern, and applying copier update in descendants with conflict resolution.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Copier Template Maintenance & Propagation
|
|
7
|
+
|
|
8
|
+
Patterns for the full lifecycle of a [Copier](https://copier.readthedocs.io/) project template: authoring changes, testing them, releasing versions, and rolling updates out to every repo generated from the template.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
- Editing the template repo (questions, scaffold files, tasks, CI)
|
|
13
|
+
- "Upstream this pattern to the template" — porting something proven in a descendant
|
|
14
|
+
- Tagging/releasing a new template version
|
|
15
|
+
- Applying a template update in a descendant repo (often via an automated "template update available" PR)
|
|
16
|
+
- Debugging `copier copy`/`copier update` failures
|
|
17
|
+
|
|
18
|
+
## Mental Model
|
|
19
|
+
|
|
20
|
+
- The **template repo** holds `copier.yml` (questions + settings + tasks) and a `template/` subdirectory of scaffold files (some `.jinja`-suffixed for substitution). **Git tags (`v*`) are the version protocol**; GitHub Releases are the changelog protocol.
|
|
21
|
+
- Each **descendant** carries `.copier-answers.yml` recording its answers and `_commit: vX.Y.Z` — the template version it's on. Never hand-edit this file; `copier update` maintains it.
|
|
22
|
+
- `copier update` re-renders from old-tag → newest tag and three-way merges against local changes. **It always jumps to the latest tag** (unless `--vcs-ref` pins one) — a notification PR advertising v1.5.0 may actually land v1.8.0 if the template moved on.
|
|
23
|
+
- Run copier via `uvx copier ...` (no global install needed). Templates with `_tasks` require `--trust` — without it copier refuses to render at all. Non-interactive contexts also need `--defaults` (and `--data key=value` for required questions without defaults).
|
|
24
|
+
|
|
25
|
+
## Direction of Change
|
|
26
|
+
|
|
27
|
+
**Prove patterns in a real descendant first, then upstream.** Build and merge the feature in one generated project; once proven, port it into `template/` with jinja-aware adaptations. The upstream PR body should cite the originating repo/PR and include validation evidence (a project generated from the branch passing typecheck/lint/tests). Exception: infra-only changes (CI jobs, hooks config) can go straight to the template. For risky changes, stage the rollout — hand-run script in one repo first, graduate to the template once the win is proven.
|
|
28
|
+
|
|
29
|
+
When working in a descendant and a fix belongs in the template too: fix the symptom locally *and* make the corresponding change in the template repo (verify you have the right repo with `git remote -v` — don't trust the directory name).
|
|
30
|
+
|
|
31
|
+
## Releasing a Template Version
|
|
32
|
+
|
|
33
|
+
After merging to the template's main:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
git checkout main && git pull --ff-only
|
|
37
|
+
git tag --sort=-v:refname | head -5 # see existing versions
|
|
38
|
+
git cat-file -t v<latest> # match the tag type convention (lightweight vs annotated)
|
|
39
|
+
git tag v<X.Y.Z> && git push origin v<X.Y.Z>
|
|
40
|
+
gh release create v<X.Y.Z> --title "v<X.Y.Z> — <summary>" --notes "$(cat <<'EOF'
|
|
41
|
+
## Changes
|
|
42
|
+
- ...
|
|
43
|
+
|
|
44
|
+
## Upgrading
|
|
45
|
+
Run `uvx copier update --trust --defaults` in your project.
|
|
46
|
+
|
|
47
|
+
Full diff: <template-repo-url>/compare/v<prev>...v<X.Y.Z>
|
|
48
|
+
EOF
|
|
49
|
+
)"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- Semver: patch = fixes/dep bumps; minor = new features, questions, or components; major = breaking structure changes.
|
|
53
|
+
- **Always create the GitHub Release, not just the tag.** Descendant notification PRs link to `/releases/tag/<version>` — a tag without a release produces dead links downstream.
|
|
54
|
+
|
|
55
|
+
## Automated Update Notification
|
|
56
|
+
|
|
57
|
+
The template ships its descendants a checker workflow (daily cron + `workflow_dispatch`) that compares `.copier-answers.yml`'s `_commit` against the template's highest remote tag:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
git ls-remote --tags --refs --sort=-v:refname <template-url> 'v*' | head -1
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
If newer, it pushes a **static branch name** (e.g. `chore/template-update`) with an `--allow-empty` commit and opens a PR whose body contains the version delta, release-notes/compare links, and step-by-step instructions an agent can execute. Hard-won details to keep if reimplementing: an explicit `permissions: contents: write, pull-requests: write` block (default token can't open PRs), a static branch name (dated branches caused duplicate PRs), and comparing **tag versions, not commit SHAs**.
|
|
64
|
+
|
|
65
|
+
## Further Reading
|
|
66
|
+
|
|
67
|
+
- **Template anatomy & testing changes**: [template-authoring.md](template-authoring.md)
|
|
68
|
+
- **Applying an update in a descendant** (the conflict-resolution procedure): [applying-updates.md](applying-updates.md)
|
|
69
|
+
|
|
70
|
+
## Contributing Back
|
|
71
|
+
|
|
72
|
+
This skill grows by capturing what it missed. If you just worked through something in this domain that this skill did not cover — an error you had to figure out, a behavior that contradicts what is documented above, a workflow knot — ask the user: **"Want me to contribute this back to the copier-template skill?"**
|
|
73
|
+
|
|
74
|
+
If yes, run `/contribute-skill`. If that command is not available, do the equivalent inline: distill the generic lesson (placeholders only — no project names, IDs, domains, or secrets), then branch or fork [gallop-systems/agent-skills](https://github.com/gallop-systems/agent-skills) and open a PR editing this skill.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Applying a Template Update in a Descendant
|
|
2
|
+
|
|
3
|
+
Usually triggered by the automated "template update available" PR — its body contains the runbook; this file is the full procedure with the judgment calls spelled out.
|
|
4
|
+
|
|
5
|
+
## The sequence
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
gh pr view <n> --json title,body # read the bot PR's instructions
|
|
9
|
+
git checkout chore/template-update && git pull # the bot's static branch
|
|
10
|
+
|
|
11
|
+
# clean tree required — copier refuses otherwise
|
|
12
|
+
git stash --include-untracked -m "wip before template update" # if dirty
|
|
13
|
+
|
|
14
|
+
uvx copier update --trust --defaults
|
|
15
|
+
|
|
16
|
+
# triage
|
|
17
|
+
git status --short # UU = unmerged (inline conflict markers)
|
|
18
|
+
find . -name '*.rej' # hunks copier couldn't apply
|
|
19
|
+
grep _commit .copier-answers.yml # confirm the new version
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Note the version it reports: `copier update` goes to the **latest** tag, which may be newer than the one the bot PR advertised. A multi-version jump means several releases' worth of changes land at once — budget for more conflicts.
|
|
23
|
+
|
|
24
|
+
## Resolving conflicts
|
|
25
|
+
|
|
26
|
+
Copier writes diff3-style inline markers:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
<<<<<<< before updating
|
|
30
|
+
<your project's current content>
|
|
31
|
+
||||||| last update
|
|
32
|
+
<what the old template version had>
|
|
33
|
+
=======
|
|
34
|
+
<what the new template version has>
|
|
35
|
+
>>>>>>> after updating
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Survey all conflict blocks first: `awk '/^<<<<<<< /,/^>>>>>>> /' <file>`.
|
|
39
|
+
|
|
40
|
+
**Decision procedure per file** — check `git log --oneline -- <file>`:
|
|
41
|
+
|
|
42
|
+
| File history | Resolution |
|
|
43
|
+
|---|---|
|
|
44
|
+
| Hand-customized in this project | Keep ours (project side) |
|
|
45
|
+
| Untouched scaffold since generation | Take theirs (template side) |
|
|
46
|
+
| Shared file with both kinds of changes (login page, CI workflow, app config) | Merge both — keep project customizations *and* add the template's new feature |
|
|
47
|
+
|
|
48
|
+
Mechanical resolutions with perl (whole-block operations, safe for multi-line):
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# keep ours: delete the entire conflict block (template side discarded)
|
|
52
|
+
perl -0pi -e 's/^<<<<<<< before updating\n(.*?)^\|\|\|\|\|\|\| last update\n.*?^>>>>>>> after updating\n/$1/gms' <file>
|
|
53
|
+
|
|
54
|
+
# keep both sides (ours then theirs)
|
|
55
|
+
perl -0pi -e 's/^<<<<<<< before updating\n(.*?)^\|\|\|\|\|\|\| last update\n.*?^=======\n(.*?)^>>>>>>> after updating\n/$1$2/gms' <file>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**`.rej` files**: copier couldn't apply a hunk (the local file diverged too far). Read the `.rej`, re-apply its *intent* manually — and check whether copier dropped project-specific content nearby (e.g. local vars in `.env.example`) — then `rm` the `.rej`.
|
|
59
|
+
|
|
60
|
+
**Review the non-conflicted changes too.** Copier silently overwrites scaffold-owned files, and a new template assumption can be wrong for this project (e.g. a type coercion that assumes numeric IDs in a project using string IDs). `git diff` every copier-touched app-code file; revert what doesn't fit, and consider whether the template itself needs a fix.
|
|
61
|
+
|
|
62
|
+
Final sweep before staging:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
grep -rn '<<<<<<<\|>>>>>>>' . --exclude-dir=node_modules
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Validate, commit, hand back
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git add -A # includes .copier-answers.yml — it must be committed with the update
|
|
72
|
+
yarn install && yarn typecheck && yarn lint && yarn fmt:check && yarn test:run
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Commit as `chore: update to template vX.Y.Z` with a body listing the notable upstream changes **and each conflict resolution with its rationale** — that's the audit trail for the squash-merge. Then retitle the bot PR (`gh pr edit <n> --title "chore: update to template vX.Y.Z"`), push, watch CI, and ask the user before merging. The bot's empty placeholder commit is fine — it disappears in the squash.
|
|
76
|
+
|
|
77
|
+
**If something fails after the update**, prove whether it's pre-existing before blaming the update: `git worktree add /tmp/<proj>-main origin/main`, reuse node_modules (symlink), rerun the failing check there. A byte-identical failure on main means fix-forward in this PR, not a regression.
|
|
78
|
+
|
|
79
|
+
## Troubleshooting
|
|
80
|
+
|
|
81
|
+
| Symptom | Fix |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `Destination repository is dirty; cannot continue` | `git stash --include-untracked` — plain `git stash` misses untracked files (including editor swap files), which still count as dirty |
|
|
84
|
+
| Copier refuses to render at all | Template has `_tasks` — add `--trust` |
|
|
85
|
+
| Hangs or fails in non-interactive shells | Add `--defaults` (and `--data key=value` for questions without defaults) |
|
|
86
|
+
| Update landed a version you didn't expect | `copier update` always targets the latest tag; pin with `--vcs-ref v<X.Y.Z>` if you need a specific one |
|
|
87
|
+
| Template change is wrong for this project | Revert locally, note it in the commit body, open a template issue/PR if other descendants are affected |
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Template Anatomy & Testing Changes
|
|
2
|
+
|
|
3
|
+
## copier.yml settings
|
|
4
|
+
|
|
5
|
+
```yaml
|
|
6
|
+
_subdirectory: template # only this dir is rendered; repo root holds copier.yml, test.sh, README
|
|
7
|
+
_templates_suffix: .jinja # only .jinja files get substitution; everything else copies verbatim
|
|
8
|
+
_skip_if_exists:
|
|
9
|
+
- ".env" # never clobber these on update
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Questions
|
|
13
|
+
|
|
14
|
+
```yaml
|
|
15
|
+
project_name:
|
|
16
|
+
type: str
|
|
17
|
+
validator: "{% if not project_name %}Required{% endif %}"
|
|
18
|
+
database_name:
|
|
19
|
+
type: str
|
|
20
|
+
default: "{{ project_name }}" # defaults can reference earlier answers
|
|
21
|
+
include_ci:
|
|
22
|
+
type: bool
|
|
23
|
+
default: true
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Feature toggles as `include_*` booleans defaulting `true` keep `copier copy --defaults` producing a fully-featured project.
|
|
27
|
+
|
|
28
|
+
## Conditional files via filename templating
|
|
29
|
+
|
|
30
|
+
A file named `{% if include_ci %}ci.yml{% endif %}.jinja` renders to `ci.yml` when the flag is true and to an empty filename (skipped) when false. This works for plain files too — the filename is always templated, only contents need the `.jinja` suffix.
|
|
31
|
+
|
|
32
|
+
## The self-rendering answers file
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
template/{{_copier_conf.answers_file}}.jinja
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
containing:
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
# Changes here will be overwritten by Copier; NEVER EDIT MANUALLY
|
|
42
|
+
{{ _copier_answers|to_nice_yaml }}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This is what writes `.copier-answers.yml` into every descendant.
|
|
46
|
+
|
|
47
|
+
## Tasks
|
|
48
|
+
|
|
49
|
+
Gate scaffold-time tasks so they run on first copy only, never on update:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
_tasks:
|
|
53
|
+
- command: createdb {{ database_name }}
|
|
54
|
+
when: "{{ _copier_operation == 'copy' }}"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- **Task order is load-bearing**: anything importing generated artifacts must run after the generator (e.g. seed after codegen); `git init` before installing git hooks.
|
|
58
|
+
- Tasks needing env vars: `bash -c 'set -a && source .env && set +a && <cmd>'`.
|
|
59
|
+
- Tasks gated on a question flag: combine conditions in `when`.
|
|
60
|
+
- Prefer post-gen install tasks (e.g. `npx <tool> add ...`) over vendoring third-party files into the template — vendored copies go stale.
|
|
61
|
+
|
|
62
|
+
## Escaping `${{ }}` in templated GitHub workflows
|
|
63
|
+
|
|
64
|
+
Jinja eats GitHub Actions expressions in `.jinja` workflow files. Two working escapes:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
{% raw %}${{ secrets.GITHUB_TOKEN }}{% endraw %}
|
|
68
|
+
${{ '{{' }} secrets.GITHUB_TOKEN {{ '}}' }}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`{% raw %}` blocks are cleaner when a whole region is Actions syntax; the inline form suits one-offs inside otherwise-templated lines.
|
|
72
|
+
|
|
73
|
+
## Testing template changes
|
|
74
|
+
|
|
75
|
+
A `test.sh` that generates a real project and runs its full gate:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
tmp=$(mktemp -d)
|
|
79
|
+
trap 'dropdb --if-exists <test-dbs>; rm -rf "$tmp"' EXIT
|
|
80
|
+
uvx copier copy --trust --defaults --vcs-ref HEAD \
|
|
81
|
+
--data project_name=smoke-test --data project_description=test \
|
|
82
|
+
. "$tmp"
|
|
83
|
+
cd "$tmp" && yarn install && yarn lint && yarn fmt:check && yarn test:run
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- **`--vcs-ref HEAD` tests committed HEAD instead of the last tag** — without it, copier renders the latest *tag* and your changes are silently absent.
|
|
87
|
+
- For *uncommitted* working-tree validation: `cp -r` the template to a temp dir, `rm -rf .git` in the copy, and `copier copy` from there.
|
|
88
|
+
- When the generated project reveals a bug, **fix it in `template/` source and re-render** — never only in the generated copy. Re-sync (`cp` the fixed file into the generated project) to re-verify without a full regeneration.
|
|
89
|
+
- Generating a project is also how template-level type/lint bugs get caught — run the descendant's typecheck against the freshly generated output as part of any nontrivial template PR, and say so in the PR body.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "doctl",
|
|
3
|
-
"description": "Manage DigitalOcean
|
|
4
|
-
"version": "1.
|
|
3
|
+
"description": "Manage DigitalOcean resources with the doctl CLI: auth contexts, App Platform deployments and logs, app specs and secrets, repo-to-app resolution, databases, Spaces, and droplets.",
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "yeedle"
|
|
7
7
|
}
|
|
@@ -1,93 +1,125 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: doctl
|
|
3
|
-
description: Manage DigitalOcean
|
|
3
|
+
description: Manage DigitalOcean resources with the doctl CLI. Covers auth contexts, App Platform (deployments, logs, env vars/secrets via app specs), mapping a git repo to its app, managed databases, Spaces keys, and droplets.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# DigitalOcean doctl CLI Patterns
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Patterns for managing DigitalOcean resources via the `doctl` CLI, centered on App Platform.
|
|
9
9
|
|
|
10
10
|
## When to Use This Skill
|
|
11
11
|
|
|
12
12
|
Use this skill when:
|
|
13
13
|
- Deploying or monitoring apps on DigitalOcean App Platform
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
14
|
+
- Checking deployment status, build failures, or runtime logs
|
|
15
|
+
- Adding/changing env vars or secrets on a deployed app
|
|
16
|
+
- Working with managed databases, Spaces, or droplets
|
|
17
|
+
- Figuring out which DO account/app corresponds to the current repo
|
|
17
18
|
|
|
18
19
|
## Auth Contexts
|
|
19
20
|
|
|
20
21
|
doctl supports named auth contexts for managing multiple accounts/teams.
|
|
21
22
|
|
|
22
23
|
```bash
|
|
23
|
-
#
|
|
24
|
-
doctl
|
|
24
|
+
doctl auth list # list contexts; (current) marks the active one
|
|
25
|
+
doctl account get --context <name> # cheap probe: is this context valid, which account is it?
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
**Prefer the `--context` flag over switching.** Every doctl command accepts `--context <name>` (before or after the subcommand). This targets one account for one command without mutating global state — important when a session touches multiple accounts:
|
|
25
29
|
|
|
26
|
-
|
|
27
|
-
doctl
|
|
30
|
+
```bash
|
|
31
|
+
doctl apps list --context <ctx>
|
|
32
|
+
for ctx in $(doctl auth list); do doctl apps list --context "$ctx"; done
|
|
28
33
|
```
|
|
29
34
|
|
|
30
|
-
|
|
35
|
+
Only use `doctl auth switch --context <name>` when the user explicitly wants the default changed.
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
**`doctl auth init --context <name>` is interactive** (it prompts for a pasted token) — an agent cannot complete it. Ask the user to run it themselves.
|
|
33
38
|
|
|
34
|
-
|
|
39
|
+
## Resolving the Current Repo to a Context + App
|
|
40
|
+
|
|
41
|
+
App specs embed their source repo, so "check prod logs" is answerable from inside any repo:
|
|
35
42
|
|
|
36
43
|
```bash
|
|
37
|
-
|
|
38
|
-
doctl
|
|
44
|
+
repo=$(git remote get-url origin | sed -E 's#.*github.com[:/]##; s#\.git$##')
|
|
45
|
+
for ctx in $(doctl auth list); do
|
|
46
|
+
doctl apps list --context "$ctx" -o json 2>/dev/null | jq -r --arg ctx "$ctx" --arg repo "$repo" \
|
|
47
|
+
'.[] | select([.spec.services[]?, .spec.static_sites[]?, .spec.workers[]?, .spec.jobs[]?]
|
|
48
|
+
| any(.github.repo == $repo)) | "\($ctx)\t\(.spec.name)\t\(.id)"'
|
|
49
|
+
done
|
|
39
50
|
```
|
|
40
51
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
The app ID is a UUID — you'll need it for all subsequent commands.
|
|
52
|
+
This costs one API call per context — resolve once per session and reuse the `(context, app-id)` pair. If a repo backs multiple apps (e.g. staging + prod, or one repo deployed to several accounts), list the matches and ask the user which one they mean. Apps deployed without a git source won't be found this way.
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
## App Platform Basics
|
|
46
55
|
|
|
47
56
|
```bash
|
|
48
|
-
#
|
|
49
|
-
doctl apps list-deployments <app-id>
|
|
57
|
+
doctl apps list --context <ctx> # ID, Spec.Name, DefaultIngress, deployment IDs
|
|
58
|
+
doctl apps list-deployments <app-id> # recent deployments: ID, Cause, Progress, Phase
|
|
59
|
+
doctl apps get <app-id> --format DefaultIngress,ActiveDeployment.Phase,InProgressDeployment.ID
|
|
60
|
+
doctl apps list-domains <app-id> # custom domains (there is no Domains column)
|
|
50
61
|
```
|
|
51
62
|
|
|
52
|
-
|
|
63
|
+
Most commands require the app UUID, not the name — get it from `doctl apps list`.
|
|
53
64
|
|
|
54
|
-
|
|
55
|
-
- `PENDING_BUILD` — queued
|
|
56
|
-
- `BUILDING` — build in progress
|
|
57
|
-
- `DEPLOYING` — deploying built artifacts
|
|
58
|
-
- `ACTIVE` — successfully deployed and serving traffic
|
|
59
|
-
- `SUPERSEDED` — replaced by a newer deployment
|
|
60
|
-
- `ERROR` — deployment failed
|
|
65
|
+
The `Cause` column tells you *why* a deployment happened: `commit <sha> pushed to <repo>` for git pushes vs `app spec updated` for config changes.
|
|
61
66
|
|
|
62
|
-
|
|
67
|
+
Deployment phases: `PENDING_BUILD` → `BUILDING` → `DEPLOYING` → `ACTIVE`. Terminal failure states: `ERROR`, `CANCELED`. `SUPERSEDED` means replaced by a newer deployment.
|
|
63
68
|
|
|
64
|
-
###
|
|
69
|
+
### Deploying
|
|
65
70
|
|
|
66
|
-
|
|
67
|
-
# Get build logs for a specific deployment
|
|
68
|
-
doctl apps logs <app-id> --deployment <deployment-id> --type build
|
|
69
|
-
|
|
70
|
-
# Get runtime logs
|
|
71
|
-
doctl apps logs <app-id> --type run
|
|
71
|
+
Apps connected to GitHub auto-deploy on push to the configured branch. To redeploy without a code change (config refresh, transient build failure):
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
doctl apps
|
|
73
|
+
```bash
|
|
74
|
+
doctl apps create-deployment <app-id> --format ID,Phase
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
### Waiting for a deployment
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
Poll bounded, with a fixed or escalating interval — never an unbounded `--follow`/watch in an agent context:
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
|
-
|
|
83
|
-
doctl apps get <app-id>
|
|
82
|
+
for i in $(seq 1 90); do
|
|
83
|
+
PHASE=$(doctl apps get-deployment <app-id> <deployment-id> --format Phase --no-header | tr -d ' ')
|
|
84
|
+
case "$PHASE" in
|
|
85
|
+
ACTIVE) echo deployed; break;;
|
|
86
|
+
ERROR|CANCELED|SUPERSEDED) echo "failed: $PHASE"; exit 1;;
|
|
87
|
+
esac
|
|
88
|
+
sleep 20
|
|
89
|
+
done
|
|
90
|
+
```
|
|
84
91
|
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
## Logs
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
doctl apps logs <app-id> --type run --tail 500 # bounded runtime logs
|
|
96
|
+
doctl apps logs <app-id> <component> --type build # component is a POSITIONAL arg, not a flag
|
|
97
|
+
doctl apps logs <app-id> --type run --follow # live tail (interactive use only)
|
|
87
98
|
```
|
|
88
99
|
|
|
89
|
-
|
|
100
|
+
- Log types: `build`, `deploy`, `run` (default). There is no `--component` flag — pass the component name as the second positional argument.
|
|
101
|
+
- Always use `--tail N` and grep (`| grep -iE 'error|timeout|oom'`) rather than dumping everything — run logs can contain live secrets.
|
|
102
|
+
- **Logs rotate on each deployment and retention is short.** Yesterday's crash logs are usually gone after today's deploy (unless log forwarding is configured). Capture logs immediately after triggering the thing you're observing.
|
|
103
|
+
- Run logs are only retrievable from the **active** deployment — `--deployment <old-id> --type run` fails with a 400 (`phase final_cleanup`). Build logs of older deployments are fine.
|
|
104
|
+
- A brand-new app has no logs until its first deployment starts (`no deployment found for app`).
|
|
105
|
+
- To tell a crash-restart from a deploy-restart, compare timestamps against `list-deployments` Created times.
|
|
106
|
+
|
|
107
|
+
## Output Formats: `--format`, `-o json`
|
|
108
|
+
|
|
109
|
+
- **Column names differ per subcommand** and doctl version. `apps list --format ActiveDeployment.Phase` fails (`unknown column`) — but the same column works on `apps get`. Nested names use dots (`Spec.Name`, not `SpecName`). On `unknown column`, fall back to `-o json | jq` rather than guessing.
|
|
110
|
+
- **`-o json` returns an array even for a single resource** — use `.[0].spec...`, not `.spec...`.
|
|
111
|
+
- **`apps spec get` always emits YAML** — it silently ignores `-o json`. Don't pipe it to a JSON parser.
|
|
112
|
+
- **A bad `--format` on a mutating command does NOT roll back the mutation.** `doctl apps update ... --format BadColumn` applies the update, then errors. Do not re-run the command — verify state instead.
|
|
113
|
+
- Empty fields render as the literal string `<nil>`; `--no-header` keeps column padding — `tr -d ' '` before string-comparing.
|
|
114
|
+
- `--http-retry-max` (global flag) auto-retries 429/5xx responses.
|
|
115
|
+
|
|
116
|
+
## Further Reading
|
|
117
|
+
|
|
118
|
+
- **App specs — env vars, secrets, creating apps**: see [spec-management.md](spec-management.md)
|
|
119
|
+
- **Databases, Spaces, droplets, DNS**: see [other-services.md](other-services.md)
|
|
120
|
+
|
|
121
|
+
## Contributing Back
|
|
122
|
+
|
|
123
|
+
This skill grows by capturing what it missed. If you just worked through something in this domain that this skill did not cover — an error you had to figure out, a behavior that contradicts what is documented above, a workflow knot — ask the user: **"Want me to contribute this back to the doctl skill?"**
|
|
90
124
|
|
|
91
|
-
|
|
92
|
-
- **Deployment auto-trigger**: Apps connected to GitHub auto-deploy on push to the configured branch. No manual deploy needed unless auto-deploy is off.
|
|
93
|
-
- **App ID vs Name**: Most commands require the app UUID, not the human-readable name. Get it from `doctl apps list`.
|
|
125
|
+
If yes, run `/contribute-skill`. If that command is not available, do the equivalent inline: distill the generic lesson (placeholders only — no project names, IDs, domains, or secrets), then branch or fork [gallop-systems/agent-skills](https://github.com/gallop-systems/agent-skills) and open a PR editing this skill.
|