Dagger
Search

skill-gen

gen-AI-free generation of project-level Claude Code skills by introspecting
a 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@a04d791b72a85735bb05ff1271e6c6cf58f4b389

Entrypoint

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
NameTypeDefault ValueDescription
hostString !-No description provided
portInteger !5432No description provided
userString !-No description provided
dbString !-No description provided
passwordSecret !-No description provided
serverCaFile -

serverCa pins the server’s CA (sslmode=verify-full). Required for TLS/mTLS; omit for plaintext.

clientCertFile -

clientCert is the PEM client leaf for mTLS; its CN must equal user. Provide with clientKey.

clientKeySecret -

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:MYSECRET
func (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)
}