@gallopsystems/agent-skills 1.16.0 → 1.18.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gallopsystems/agent-skills",
3
- "version": "1.16.0",
3
+ "version": "1.18.0",
4
4
  "description": "Gallop Systems agent skills, symlinked into .claude/skills (Claude Code) and .agents/skills (Codex) on install.",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -62,6 +62,15 @@ git ls-remote --tags --refs --sort=-v:refname <template-url> 'v*' | head -1
62
62
 
63
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
64
 
65
+ ## Dependency Updates: Who Owns What
66
+
67
+ Two Renovate instances run, with a deliberate boundary so they never fight:
68
+
69
+ - **The template's Renovate** (in the template repo) keeps the pins in `template/package.json.jinja` fresh via a custom regex manager, and **auto-merges `@gallopsystems/agent-skills`** — which release-please then cuts as a template release. Those bumps reach descendants through `copier update`.
70
+ - **Each descendant's Renovate** (shipped as `renovate.json`, gated on the `include_renovate` question) owns that repo's **own** app dependencies — the only place an upgrade can be tested against the real app's code and CI.
71
+
72
+ The one overlap is resolved by ownership: the descendant's `renovate.json` **disables `@gallopsystems/agent-skills`**, leaving it solely template-owned. Every other pin is the descendant's. Because the template keeps bumping all pins, a descendant's `package.json` arrives with version conflicts on `copier update` — resolve them by keeping the descendant's versions (see [applying-updates.md](applying-updates.md) → *`package.json` dependency pins*). Newly-scaffolded repos start on the template's pins and are freshened by their own Renovate within a day.
73
+
65
74
  ## Branch Protection in Descendants
66
75
 
67
76
  A template **cannot** enable branch protection for the repos it generates — GitHub reads required status checks from repo config, never from committed workflow files. So every descendant starts with nothing gating merges until someone sets it once (after the first CI run, so the check is known). This template's CI exposes a **`ci-success`** summary job to be exactly that gate — require it on `main`:
@@ -101,6 +101,13 @@ perl -0pi -e 's/^<<<<<<< before updating\n(.*?)^\|\|\|\|\|\|\| last update\n.*?^
101
101
 
102
102
  After editing markers out, **`git add` each resolved file** — it stays `UU` until staged, and an unstaged `UU` later blocks `git stash pop` and the push.
103
103
 
104
+ **`package.json` dependency pins — keep *this repo's* versions, not the template's.** The template's Renovate bumps every pin in `package.json.jinja`, so each release moves those lines and they arrive here as conflicts. But a descendant runs its **own** Renovate, which keeps its deps ahead of — and CI-tested against — the template's pins, so the template side is almost always *behind*. Resolve each dependency-version conflict by **keeping ours**, with two exceptions:
105
+
106
+ - **`@gallopsystems/agent-skills`** is template-owned (the descendant's `renovate.json` is configured to ignore it, so the template is its only updater). Always **take theirs** for that line.
107
+ - If the template's pin is genuinely *higher* than ours (this repo lagged — Renovate paused, or a dep Renovate doesn't manage), taking theirs is fine **only when it's an obviously-safe move** — a patch within the same minor. For a minor/major where ours is behind, keep ours and let this repo's Renovate make the jump afterward rather than adopting the template's pin blind.
108
+
109
+ A *new* dependency the template adds is not a conflict (the descendant doesn't have it yet) — copier just adds it; keep it. This rule is only about shared pins. The split is deliberate: the template owns `agent-skills`, each descendant owns its own app dependencies.
110
+
104
111
  **Scaffold files arrive written against the TEMPLATE's schema — adopt the feature, adapt it to yours; never a naive side-pick.** Files like preview-login, factories, seeds, and `auth.d.ts` ship assuming the template's columns (`first_name`/`last_name`, `deactivated_at`, numeric `id`). A descendant that diverged (a single `name` column, camelCase, string IDs) won't compile against them. Take the template's *feature* but rewrite it to the real schema: revert `Number(id)` coercions, fix the anchor-user/seed columns, drop selects on columns that don't exist, repair the matching test. After adopting any such file, grep it against the real `db.d.ts`:
105
112
 
106
113
  ```bash
@@ -20,6 +20,35 @@
20
20
  )
21
21
  ```
22
22
 
23
+ **…but only when the expression references the schema.** The whole payoff of
24
+ dropping raw `sql` is letting Kysely validate column/table names against your
25
+ generated types — so the value is proportional to how many schema identifiers the
26
+ expression names. A `sql` template made only of **bound parameters and constants**
27
+ has nothing to check; converting it is lateral churn, and occasionally worse:
28
+
29
+ ```typescript
30
+ // WORTH CONVERTING - names a column, so eb.ref/eb() validate it exists
31
+ .where(sql`lower(name)`, "=", value)
32
+ .where((eb) => eb(eb.fn<string>("lower", [eb.ref("name")]), "=", value))
33
+
34
+ // WORTH CONVERTING - correlated EXISTS validates table + both column refs
35
+ sql<boolean>`EXISTS (SELECT 1 FROM child WHERE child.parent_id = parent.id)`
36
+ eb.exists(eb.selectFrom("child").select(eb.lit(1).as("one"))
37
+ .whereRef("child.parent_id", "=", "parent.id"))
38
+
39
+ // LEAVE RAW - a bound param + a constant cast: no column, nothing to verify.
40
+ // The "typed" form is also a schema MISMATCH: eb.cast(...) is Expression<Date>,
41
+ // but a DATE column is codegen'd as `string` (with --date-parser string).
42
+ sql`${value}::date` // keep it
43
+
44
+ // LEAVE RAW - bare string/number/bool literals have no schema surface
45
+ sql.lit("Unassigned") // keep it (or eb.lit for number/bool, but no real gain)
46
+ ```
47
+
48
+ Rule of thumb: **convert when the raw SQL names a column or table; leave it when
49
+ it is only parameters and literals.** The latter compiles to identical SQL and the
50
+ "type-safe" rewrite verifies nothing.
51
+
23
52
  ### 2. Don't Forget .execute()
24
53
 
25
54
  Queries are lazy - they won't run without calling an execute method:
@@ -236,7 +236,16 @@ The setup file stubs Nuxt/Nitro auto-imports:
236
236
 
237
237
  3. **Nested transactions work** - Code that calls `db.transaction()` works because we patch the prototype
238
238
 
239
- 4. **Test file location** - Co-locate with handlers: `handler.ts` → `handler.test.ts`
239
+ 4. **Test file location** - Co-locate with handlers: `handler.ts` → `handler.test.ts`.
240
+ **Exception — module-scanned directories:** never co-locate a test inside a
241
+ directory a Nuxt/Nitro module auto-imports *wholesale* (it globs every file in
242
+ the dir and bundles it into the server build — e.g. a tool/plugin registry
243
+ like an MCP toolkit's `server/mcp/tools/`, where dropping a `*.test.ts` next to
244
+ the tool means the test is pulled into the build). `yarn build` then fails when
245
+ that test imports build-absent test utilities (e.g. `~/server/test-utils` →
246
+ `ENOENT`). Vitest **and** `typecheck` stay green — only `yarn build` (or the
247
+ build CI job) catches it. Keep such tests outside the scanned dir (e.g. under
248
+ `server/utils/`) and import the unit under test by alias.
240
249
 
241
250
  5. **Separate test database** - Always use a dedicated test DB (`myapp-test`, not `myapp`)
242
251