skill-gen
gen-AI-free generation of project-level Claude Code skills by introspectinga source system. The first (and only, in this story) source is Postgres.
All determinism-critical logic — the introspection model, top-table
ranking, markdown rendering, and verification — lives in the pure-Go
./skill subpackage (zero dagger import, go test -race-able with no engine).
This file owns only the Dagger I/O: delegating introspection to the
postgres module's Client.QueryJSON and assembling the result Directory.
Installation
dagger install github.com/z5labs/devex/daggerverse/skill-gen@a04d791b72a85735bb05ff1271e6c6cf58f4b389Entrypoint
Return Type
SkillGen Example
dagger -m github.com/z5labs/devex/daggerverse/skill-gen@a04d791b72a85735bb05ff1271e6c6cf58f4b389 call \
func (m *MyModule) Example() *dagger.SkillGen {
return dag.
Skillgen()
}@function
def example() -> dagger.SkillGen:
return (
dag.skill_gen()
)@func()
example(): SkillGen {
return dag
.skillGen()
}Types
SkillGen 🔗
SkillGen is the module’s root object.
postgres() 🔗
Postgres introspects the PostgreSQL database at host:port and returns a
generated pg-<db> Claude Code skill as a *dagger.Directory. The returned
tree is the skill directory itself (SKILL.md at its root) with no enclosing
.claude/skills/ wrapper, so it can be dropped straight into Claude Code,
Copilot, or any other gen-AI environment. The function never touches the
host filesystem — the caller exports the tree wherever they want (e.g.
export --path pg-<db> for Copilot, or export --path .claude/skills/pg-<db>
for Claude Code).
Introspection is delegated to the postgres module’s pgx-backed
Client.QueryJSON; only core types cross this module’s boundary
(*dagger.Secret/*dagger.File in, *dagger.Directory out). db is validated
against ^[A-Za-z0-9_-]+$ before any network I/O, because it flows into the
skill’s name: pg-<db> frontmatter and into filenames. Any introspection
failure aborts with a non-zero error and no partial output.
The transport security mode is inferred from the supplied cert params, so it can never disagree with the material actually provided:
- none → plaintext (scram-sha-256 over an unencrypted TCP connection).
- serverCa only → one-way TLS (sslmode=verify-full against serverCa).
- serverCa + clientCert + clientKey → mTLS; the client presents its leaf
to satisfy the server’s clientcert=verify-full. The client cert’s CN
must equal
user, or the server rejects it with a misleading 28P01.
serverCa and clientCert are public PEM certs (*dagger.File); clientKey is the PEM PKCS#8 private key kept as a *dagger.Secret end-to-end.
Return Type
Directory !Arguments
| Name | Type | Default Value | Description |
|---|---|---|---|
| host | String ! | - | No description provided |
| port | Integer ! | 5432 | No description provided |
| user | String ! | - | No description provided |
| db | String ! | - | No description provided |
| password | Secret ! | - | No description provided |
| serverCa | File | - | serverCa pins the server’s CA (sslmode=verify-full). Required for TLS/mTLS; omit for plaintext. |
| clientCert | File | - | clientCert is the PEM client leaf for mTLS; its CN must equal |
| clientKey | Secret | - | clientKey is the PEM PKCS#8 client private key for mTLS. Provide with clientCert. |
Example
dagger -m github.com/z5labs/devex/daggerverse/skill-gen@a04d791b72a85735bb05ff1271e6c6cf58f4b389 call \
postgres --host string --port integer --user string --db string --password env:MYSECRETfunc (m *MyModule) Example(host string, port int, user string, db string, password *dagger.Secret) *dagger.Directory {
return dag.
Skillgen().
Postgres(host, port, user, db, password)
}@function
def example(host: str, port: int, user: str, db: str, password: dagger.Secret) -> dagger.Directory:
return (
dag.skill_gen()
.postgres(host, port, user, db, password)
)@func()
example(host: string, port: number, user: string, db: string, password: Secret): Directory {
return dag
.skillGen()
.postgres(host, port, user, db, password)
}