@@ -14,11 +14,34 @@ WhatsApp library over baileys; import is always `import { Client } from 'zaileys
1414ESM (` "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
2346Fixed 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.
38641 . ** 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
81107Rules: storage construction always lives ** inside ` client.ts ` ** (not a new folder). Pure, side-effect-free
82108helpers 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({
258287export 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
314349import type { ChatCompletionMessageParam } from ' openai/resources/chat/completions'
315350import { AI_MODEL , openai } from ' ../services/openai.js'
316351import { history } from ' ./memory.js'
352+ import { SYSTEM_PROMPT } from ' ./prompt.js'
317353import { runTool , tools } from ' ./tools.js'
318354
319- const SYSTEM_PROMPT = process .env [' BOT_SYSTEM_PROMPT' ] ?? ' You are a concise, friendly WhatsApp assistant.'
320355const MAX_TOOL_ROUNDS = 5
321356
322357export 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