Skip to content

Commit 7587a61

Browse files
committed
feat(skill): scaffold conforms to existing codebase; prompts are code not env
Add EXISTING-CODEBASE PRECEDENCE — scan target first; brownfield conforms to existing folders/naming, greenfield uses the canonical layout, partial fills gaps in the project's style, ambiguous dirs prompt a question. Add a golden rule that env is for secrets/connection/toggles only; system prompts and static content live in code constants (src/prompts/), never env.
1 parent 02e0052 commit 7587a61

2 files changed

Lines changed: 92 additions & 16 deletions

File tree

plugins/zaileys-official/skills/scaffold/SKILL.md

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,34 @@ WhatsApp library over baileys; import is always `import { Client } from 'zaileys
1414
ESM (`"type": "module"`). Output a **layered `src/` project** (never a single flat `bot.ts`),
1515
`package.json`, `tsconfig.json`, `.env.example`, `.gitignore`, and run steps.
1616

17-
## DETERMINISM CONTRACT (read first — this is the point of this skill)
18-
19-
The generated tree MUST be a pure function of the spec: **same (use case + features + auth + storage)
20-
→ byte-for-byte same file layout, names, and module boundaries, every session, every developer.**
21-
Do not improvise structure per session. Decide the file set ONLY from the tables below.
17+
## EXISTING-CODEBASE PRECEDENCE (check this FIRST, before any structure decision)
18+
19+
**Existing project conventions always win over this skill's canonical layout.** Before generating
20+
anything, inspect the target directory:
21+
22+
1. **Greenfield** (empty dir, or no `src/` / `package.json`): apply the canonical PROJECT STRUCTURE
23+
CONTRACT below verbatim.
24+
2. **Brownfield** (a project already exists — `package.json`, `src/`, established folders, or even
25+
empty-but-intentional dirs the developer pre-created like `src/prompts/`, `src/tools/`): **conform
26+
to what is there.** Map each role onto the existing folders and **match their conventions** — folder
27+
names, file naming (kebab vs camel), barrel (`index.ts`) vs flat, import style, default vs named
28+
exports. Do NOT rename, move, or re-layer their files; do NOT impose this skill's folder names.
29+
3. **Partial / incomplete**: keep everything that exists as-is; only ADD the missing pieces, placed and
30+
named in the existing project's style. Use the canonical mapping below only to decide *what role a
31+
new file plays*, then express that role using their conventions.
32+
33+
How to detect intended structure: an empty folder a developer created (`src/prompts/`, `src/tools/`,
34+
`src/pkg/`) is a deliberate signal — put that role's files there, do not invent a parallel location.
35+
When the existing convention for a given role is genuinely ambiguous (e.g. an empty folder whose purpose
36+
you cannot infer from its name), ASK rather than guess.
37+
38+
## DETERMINISM CONTRACT
39+
40+
Within each regime above, output is reproducible — a pure function of **(spec + the project's existing
41+
conventions)**. Greenfield: **same (use case + features + auth + storage) → byte-for-byte same tree,
42+
every session, every developer.** Brownfield: same spec + same existing layout → same placement, every
43+
session. Never improvise structure per session; decide from the existing project first, then the tables
44+
below.
2245

2346
Fixed conventions (never vary):
2447

@@ -35,6 +58,9 @@ Fixed conventions (never vary):
3558

3659
## WORKFLOW
3760

61+
0. **Scan the target directory first.** Determine greenfield vs brownfield per EXISTING-CODEBASE
62+
PRECEDENCE above. List existing folders/files (including intentionally pre-created empty dirs). This
63+
decides whether you follow the canonical layout or conform to what is already there.
3864
1. **Ask ONLY the gaps** (one short round; assume defaults if "just make it work"):
3965
- **Use case** — what the bot is for. Drives which feature modules exist. Common: `echo`,
4066
`ai-agent`, `slash-commands`, `interactive-buttons`, `broadcast`. Use cases vary widely and are
@@ -76,7 +102,7 @@ Root files (always): `package.json`, `tsconfig.json`, `.env.example`, `.gitignor
76102
| `slash-commands` | `src/commands/index.ts``registerCommands(client)`; one `src/commands/<name>.ts` per command | `index.ts` calls `registerCommands`; set `commandPrefix` in `client.ts` |
77103
| `interactive-buttons` | `src/handlers/buttons.ts``registerButtonHandlers(client)`; menu lives in a `src/commands/menu.ts` | `handlers/index.ts` |
78104
| `broadcast` | `src/features/broadcast.ts``runBroadcast(client, …)` | invoked from a command in `src/commands/broadcast.ts` |
79-
| `ai-agent` | `src/services/<provider>.ts` (client factory); `src/features/ai-agent.ts` (`askAgent`); `src/features/memory.ts` (working memory); `src/features/tools.ts` (defs + runner, if tool calling) | `message.ts` calls `askAgent` |
105+
| `ai-agent` | `src/services/<provider>.ts` (client factory); `src/features/prompt.ts` (`SYSTEM_PROMPT` constant); `src/features/ai-agent.ts` (`askAgent`); `src/features/memory.ts` (working memory); `src/features/tools.ts` (defs + runner, if tool calling) | `message.ts` calls `askAgent` |
80106

81107
Rules: storage construction always lives **inside `client.ts`** (not a new folder). Pure, side-effect-free
82108
helpers go in `src/lib/`. If a use case needs a new external integration (HTTP API, DB, queue), it is a
@@ -94,6 +120,9 @@ that is what makes output reproducible.
94120
- **One content method per builder**, then optional modifiers, then `await`:
95121
`await client.send(jid).text('hi')`. Awaiting yields a `WAMessageKey` (`key.id`).
96122
- **Env only via `config/env.ts`** — never read `process.env` elsewhere; never hardcode tokens/URLs/numbers.
123+
- **Env is for secrets / connection / deploy toggles only** (API keys, DB URLs, model name, tunable limits).
124+
Static application content — system prompts, persona, fixed copy/messages — is a **code constant**
125+
(e.g. `src/features/prompt.ts`), NEVER an env var or an `env(key, '…long default…')` fallback.
97126
- **Correct JIDs** — user `628xxx@s.whatsapp.net`, group `xxx@g.us`. Use `lib/jid.ts` to strip to digits before comparing.
98127
- **Pairing requires `phoneNumber`** (E.164 digits, no `+`) or `connect()` rejects.
99128
- **Broadcast must be rate-limited** (`rateLimitPerSec`, default 5) and called inside a handler.
@@ -258,6 +287,12 @@ export const openai = new OpenAI({
258287
export const AI_MODEL = process.env['OPENAI_MODEL'] ?? 'gpt-4o-mini'
259288
```
260289

290+
`src/features/prompt.ts` (persona — a code constant, NOT env; edit here to change the bot's behavior):
291+
292+
```typescript
293+
export const SYSTEM_PROMPT = `You are a concise, friendly WhatsApp assistant. Use a tool when you need real-time data.`
294+
```
295+
261296
`src/features/memory.ts` (working memory — last N turns per chat; full archive lives in the message store):
262297

263298
```typescript
@@ -314,9 +349,9 @@ export const runTool = async (name: string, args: Record<string, unknown>): Prom
314349
import type { ChatCompletionMessageParam } from 'openai/resources/chat/completions'
315350
import { AI_MODEL, openai } from '../services/openai.js'
316351
import { history } from './memory.js'
352+
import { SYSTEM_PROMPT } from './prompt.js'
317353
import { runTool, tools } from './tools.js'
318354

319-
const SYSTEM_PROMPT = process.env['BOT_SYSTEM_PROMPT'] ?? 'You are a concise, friendly WhatsApp assistant.'
320355
const MAX_TOOL_ROUNDS = 5
321356

322357
export const askAgent = async (convoId: string, userText: string): Promise<string> => {
@@ -547,8 +582,8 @@ SESSION_ID=bot
547582
# OPENAI_API_KEY=sk-xxxx
548583
# OPENAI_MODEL=gpt-4o-mini
549584
# OPENAI_BASE_URL=http://localhost:11434/v1
550-
# BOT_SYSTEM_PROMPT=You are a concise, friendly WhatsApp assistant.
551585
# MAX_HISTORY=16
586+
# (persona/system prompt lives in code: src/features/prompt.ts — not env)
552587

553588
# Broadcast guard
554589
# OWNER=6281234567890
@@ -580,6 +615,9 @@ so later runs skip auth. To force a fresh login, delete the session.
580615

581616
## VERIFY BEFORE HANDOFF
582617

618+
- **Brownfield**: every generated file lands in the existing project's folders and matches its naming/
619+
import/export conventions; no pre-existing file was renamed, moved, or re-layered; pre-created empty
620+
dirs were used for their role. (The skeleton checks below apply to greenfield only.)
583621
- The full skeleton exists (`src/index.ts`, `src/client.ts`, `src/config/env.ts`,
584622
`src/handlers/index.ts`, `src/handlers/connection.ts`, `src/lib/jid.ts`) — even for trivial bots.
585623
- Only `src/index.ts` has top-level execution; every other module exports functions, no import-time side effects.

skills/scaffold/SKILL.md

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,34 @@ WhatsApp library over baileys; import is always `import { Client } from 'zaileys
1414
ESM (`"type": "module"`). Output a **layered `src/` project** (never a single flat `bot.ts`),
1515
`package.json`, `tsconfig.json`, `.env.example`, `.gitignore`, and run steps.
1616

17-
## DETERMINISM CONTRACT (read first — this is the point of this skill)
18-
19-
The generated tree MUST be a pure function of the spec: **same (use case + features + auth + storage)
20-
→ byte-for-byte same file layout, names, and module boundaries, every session, every developer.**
21-
Do not improvise structure per session. Decide the file set ONLY from the tables below.
17+
## EXISTING-CODEBASE PRECEDENCE (check this FIRST, before any structure decision)
18+
19+
**Existing project conventions always win over this skill's canonical layout.** Before generating
20+
anything, inspect the target directory:
21+
22+
1. **Greenfield** (empty dir, or no `src/` / `package.json`): apply the canonical PROJECT STRUCTURE
23+
CONTRACT below verbatim.
24+
2. **Brownfield** (a project already exists — `package.json`, `src/`, established folders, or even
25+
empty-but-intentional dirs the developer pre-created like `src/prompts/`, `src/tools/`): **conform
26+
to what is there.** Map each role onto the existing folders and **match their conventions** — folder
27+
names, file naming (kebab vs camel), barrel (`index.ts`) vs flat, import style, default vs named
28+
exports. Do NOT rename, move, or re-layer their files; do NOT impose this skill's folder names.
29+
3. **Partial / incomplete**: keep everything that exists as-is; only ADD the missing pieces, placed and
30+
named in the existing project's style. Use the canonical mapping below only to decide *what role a
31+
new file plays*, then express that role using their conventions.
32+
33+
How to detect intended structure: an empty folder a developer created (`src/prompts/`, `src/tools/`,
34+
`src/pkg/`) is a deliberate signal — put that role's files there, do not invent a parallel location.
35+
When the existing convention for a given role is genuinely ambiguous (e.g. an empty folder whose purpose
36+
you cannot infer from its name), ASK rather than guess.
37+
38+
## DETERMINISM CONTRACT
39+
40+
Within each regime above, output is reproducible — a pure function of **(spec + the project's existing
41+
conventions)**. Greenfield: **same (use case + features + auth + storage) → byte-for-byte same tree,
42+
every session, every developer.** Brownfield: same spec + same existing layout → same placement, every
43+
session. Never improvise structure per session; decide from the existing project first, then the tables
44+
below.
2245

2346
Fixed conventions (never vary):
2447

@@ -35,6 +58,9 @@ Fixed conventions (never vary):
3558

3659
## WORKFLOW
3760

61+
0. **Scan the target directory first.** Determine greenfield vs brownfield per EXISTING-CODEBASE
62+
PRECEDENCE above. List existing folders/files (including intentionally pre-created empty dirs). This
63+
decides whether you follow the canonical layout or conform to what is already there.
3864
1. **Ask ONLY the gaps** (one short round; assume defaults if "just make it work"):
3965
- **Use case** — what the bot is for. Drives which feature modules exist. Common: `echo`,
4066
`ai-agent`, `slash-commands`, `interactive-buttons`, `broadcast`. Use cases vary widely and are
@@ -76,7 +102,7 @@ Root files (always): `package.json`, `tsconfig.json`, `.env.example`, `.gitignor
76102
| `slash-commands` | `src/commands/index.ts``registerCommands(client)`; one `src/commands/<name>.ts` per command | `index.ts` calls `registerCommands`; set `commandPrefix` in `client.ts` |
77103
| `interactive-buttons` | `src/handlers/buttons.ts``registerButtonHandlers(client)`; menu lives in a `src/commands/menu.ts` | `handlers/index.ts` |
78104
| `broadcast` | `src/features/broadcast.ts``runBroadcast(client, …)` | invoked from a command in `src/commands/broadcast.ts` |
79-
| `ai-agent` | `src/services/<provider>.ts` (client factory); `src/features/ai-agent.ts` (`askAgent`); `src/features/memory.ts` (working memory); `src/features/tools.ts` (defs + runner, if tool calling) | `message.ts` calls `askAgent` |
105+
| `ai-agent` | `src/services/<provider>.ts` (client factory); `src/features/prompt.ts` (`SYSTEM_PROMPT` constant); `src/features/ai-agent.ts` (`askAgent`); `src/features/memory.ts` (working memory); `src/features/tools.ts` (defs + runner, if tool calling) | `message.ts` calls `askAgent` |
80106

81107
Rules: storage construction always lives **inside `client.ts`** (not a new folder). Pure, side-effect-free
82108
helpers go in `src/lib/`. If a use case needs a new external integration (HTTP API, DB, queue), it is a
@@ -94,6 +120,9 @@ that is what makes output reproducible.
94120
- **One content method per builder**, then optional modifiers, then `await`:
95121
`await client.send(jid).text('hi')`. Awaiting yields a `WAMessageKey` (`key.id`).
96122
- **Env only via `config/env.ts`** — never read `process.env` elsewhere; never hardcode tokens/URLs/numbers.
123+
- **Env is for secrets / connection / deploy toggles only** (API keys, DB URLs, model name, tunable limits).
124+
Static application content — system prompts, persona, fixed copy/messages — is a **code constant**
125+
(e.g. `src/features/prompt.ts`), NEVER an env var or an `env(key, '…long default…')` fallback.
97126
- **Correct JIDs** — user `628xxx@s.whatsapp.net`, group `xxx@g.us`. Use `lib/jid.ts` to strip to digits before comparing.
98127
- **Pairing requires `phoneNumber`** (E.164 digits, no `+`) or `connect()` rejects.
99128
- **Broadcast must be rate-limited** (`rateLimitPerSec`, default 5) and called inside a handler.
@@ -258,6 +287,12 @@ export const openai = new OpenAI({
258287
export const AI_MODEL = process.env['OPENAI_MODEL'] ?? 'gpt-4o-mini'
259288
```
260289

290+
`src/features/prompt.ts` (persona — a code constant, NOT env; edit here to change the bot's behavior):
291+
292+
```typescript
293+
export const SYSTEM_PROMPT = `You are a concise, friendly WhatsApp assistant. Use a tool when you need real-time data.`
294+
```
295+
261296
`src/features/memory.ts` (working memory — last N turns per chat; full archive lives in the message store):
262297

263298
```typescript
@@ -314,9 +349,9 @@ export const runTool = async (name: string, args: Record<string, unknown>): Prom
314349
import type { ChatCompletionMessageParam } from 'openai/resources/chat/completions'
315350
import { AI_MODEL, openai } from '../services/openai.js'
316351
import { history } from './memory.js'
352+
import { SYSTEM_PROMPT } from './prompt.js'
317353
import { runTool, tools } from './tools.js'
318354

319-
const SYSTEM_PROMPT = process.env['BOT_SYSTEM_PROMPT'] ?? 'You are a concise, friendly WhatsApp assistant.'
320355
const MAX_TOOL_ROUNDS = 5
321356

322357
export const askAgent = async (convoId: string, userText: string): Promise<string> => {
@@ -547,8 +582,8 @@ SESSION_ID=bot
547582
# OPENAI_API_KEY=sk-xxxx
548583
# OPENAI_MODEL=gpt-4o-mini
549584
# OPENAI_BASE_URL=http://localhost:11434/v1
550-
# BOT_SYSTEM_PROMPT=You are a concise, friendly WhatsApp assistant.
551585
# MAX_HISTORY=16
586+
# (persona/system prompt lives in code: src/features/prompt.ts — not env)
552587

553588
# Broadcast guard
554589
# OWNER=6281234567890
@@ -580,6 +615,9 @@ so later runs skip auth. To force a fresh login, delete the session.
580615

581616
## VERIFY BEFORE HANDOFF
582617

618+
- **Brownfield**: every generated file lands in the existing project's folders and matches its naming/
619+
import/export conventions; no pre-existing file was renamed, moved, or re-layered; pre-created empty
620+
dirs were used for their role. (The skeleton checks below apply to greenfield only.)
583621
- The full skeleton exists (`src/index.ts`, `src/client.ts`, `src/config/env.ts`,
584622
`src/handlers/index.ts`, `src/handlers/connection.ts`, `src/lib/jid.ts`) — even for trivial bots.
585623
- Only `src/index.ts` has top-level execution; every other module exports functions, no import-time side effects.

0 commit comments

Comments
 (0)