Handbooks
संदर्भ

CLI संदर्भ

हर subcommand, हर flag, उसका environment variable और उसका डिफ़ॉल्ट — साथ ही यह भी कि हर कमांड क्या लिखती है और किस exit code के साथ खत्म होती है।

handbook [global options] <command> [command options]

हर कमांड अपना परिणाम stdout पर JSON के रूप में और अपने logs stderr पर लिखती है, इसलिए piping ठीक वैसे ही काम करती है जैसी आप उम्मीद करेंगे:

handbook analyze --source ~/code/api --work work/api | jq .functions

`--help` जनरेट होती है, लिखी नहीं जाती

नीचे का हर flag एक ही settings registry से निकलता है, इसलिए handbook <cmd> --help हमेशा flag, उसका environment variable, उसका per-command scoped variable और उसका डिफ़ॉल्ट दिखाती है। अगर यह पृष्ठ और --help कभी असहमत हों, तो --help सही है — और एक drift test build को गिरा देता है।

वैश्विक विकल्प

Flagप्रभाव
-V, --versionसंस्करण छापें
-v, --verboseDebug logging
-q, --quietकेवल त्रुटियाँ — -v पर भारी पड़ता है
--env <name>एक environment चुनें: .env.local और .env से पहले .env.<name>.local और .env.<name> लोड करता है, और handbook.config.<name>.yaml को प्राथमिकता देता है। HANDBOOK_ENV जैसा ही
--env-file <path>ठीक यही फ़ाइल लोड करें, .env cascade को छोड़कर। गायब फ़ाइल एक ज़ोरदार त्रुटि है, कोई fallback नहीं। HANDBOOK_ENV_FILE को प्राथमिकता दें — नीचे की चेतावनी देखें
--config <path>निकटतम handbook.config.yaml खोजने के बजाय यह config फ़ाइल इस्तेमाल करें

वैश्विक विकल्प subcommand से पहले आते हैं:

handbook --env prod -v generate --source ~/code/api --work work/api

`--env-file` एक Node flag से टकराता है

Node >= 20.6 का अपना --env-file है, और वह उसके लिए पूरी command line को पहले से स्कैन करता है — उस हिस्से समेत जो script path के बाद आता है, जहाँ वह फ़ाइल को असल में लागू नहीं करता। मौजूद रास्ता Handbooks तक बिना छेड़े पहुँच जाता है, पर जो रास्ता मौजूद नहीं है वह प्रक्रिया को पहले ही मार देता है:

$ handbook --env-file /gone.env config
node: /gone.env: not found        # node, exit 9, before Handbooks ever runs

तो जिस एक मामले को ज़ोर से बताने का वादा यह flag करता है, वही एकमात्र मामला है जिसे वह बता नहीं सकता। HANDBOOK_ENV_FILE बिल्कुल वही काम करता है और उसे बीच में रोका नहीं जा सकता:

$ HANDBOOK_ENV_FILE=/gone.env handbook config
handbook: error: ENOENT: no such file or directory, open '/gone.env'

जब फ़ाइल सचमुच वहाँ हो तब flag अब भी काम करता है, और दोनों सेट होने पर वह environment variable पर भारी पड़ता है।


analyze

केवल Phase 1: static call graph बनाना। कोई LLM नहीं, कोई key नहीं, मुफ़्त।

handbook analyze --source <dir> --work <dir> [--lang <lang>]
Flagडिफ़ॉल्टEnv
--source <dir>आवश्यकHANDBOOK_SOURCE / HANDBOOK_ANALYZE_SOURCE
--work <dir>आवश्यकHANDBOOK_WORK / HANDBOOK_ANALYZE_WORK
--lang <lang>autoHANDBOOK_LANG / HANDBOOK_ANALYZE_LANG

--lang auto या इनमें से कोई एक स्वीकार करता है: cpp csharp dart go java kotlin objc ocaml php python ruby rust scala shell solidity swift typescript zig. auto एक ही पास में हर भाषा को पहचानकर मिला देता है और लगभग हमेशा वही होता है जो आप चाहते हैं।

लिखता है phase1/graph.json, functions.csv, graph.dot, dropped-calls.json, scan-coverage.json

files वह गिनता है जो सचमुच पढ़ा और parse हुआ; filesUnparsed वह जो नहीं हुआ, और उनमें से हर एक का नाम एक reason के साथ scan-coverage.json में दर्ज होता है। देखें Artifact प्रारूप

stdout
{
  "language": "multi",
  "files": 412,
  "functions": 3187,
  "edgesKept": 9042,
  "edgesDropped": 611,
  "filesUnparsed": 3
}

generate

पूरी pipeline। phase 1 के बाद की हर चीज़ के लिए एक LLM endpoint चाहिए।

handbook generate --source <dir> --work <dir> [options]

Pipeline विकल्प

Flagडिफ़ॉल्टयह क्या करता है
--phase <spec>allall · 1 · 2 (=2a+2b+2c) · 2a · 2b · 2c · 3, या अल्पविराम सूची
--strategy <s>(work directory में दर्ज, वरना file)file या member
--skeleton <path>आपका अपना skeleton.yaml--strategy member के लिए आवश्यक
--detail <d>briefcard की गहराई brief या deep
--synth-mode <m>oneshotoneshot, या actor–critic मरम्मत लूप के लिए doctor
--narrate-lang <l>enen या zh
--max-doctor-rounds <n>6Doctor अभिसरण राउंड
--resumefalseजिन फ़ाइलों का card पहले से पूरा है उन्हें छोड़ दें
--refreshfalsephase 3 के caches को अनदेखा करें
--llm-cachefalseकच्चे LLM उत्तर <work>/phase3/cache के नीचे cache करें

थ्रूपुट विकल्प

Flagडिफ़ॉल्टयह क्या करता है
--read-workers <n>12समवर्ती card बैच
--read-batch-size <n>(deep के लिए 1, brief के लिए 8)प्रति card बैच फ़ाइलें
--max-chars-per-file <n>0हर फ़ाइल को n अक्षरों पर काटें; 0 = कोई सीमा नहीं
--assign-batch-size <n>25प्रति assignment बैच cards
--assign-workers <n>12समवर्ती assignment बैच
--organize-workers <n>8समवर्ती stage-organize कॉल
--narrate-workers <n>8समवर्ती narration कॉल

LLM विकल्प (generate, plan, resync, studio में साझा)

Flagडिफ़ॉल्टEnv उपनाम
--provider <name>openaiOPENAI_PROVIDER
--model <id>gpt-4o-miniOPENAI_MODEL
--base-url <url>https://api.openai.com/v1OPENAI_BASE_URL
--max-tokens <n>16000OPENAI_MAX_TOKENS
--timeout <sec>300OPENAI_TIMEOUT
--llm-retries <n>6
--llm-retry-backoff <sec>3
--llm-concurrency <n>16

API key कभी flag नहीं होतीOPENAI_API_KEY (या HANDBOOK_LLM_API_KEY) को environment में या किसी .env फ़ाइल में सेट करें। config फ़ाइल में यह अस्वीकार कर दी जाती है, क्योंकि config फ़ाइलें commit होती हैं।

अतिरिक्त request body भी कभी flag नहीं होती, और उसी वजह से config फ़ाइल में अस्वीकार कर दी जाती है। इसके लिए OPENAI_EXTRA_BODY (या HANDBOOK_LLM_EXTRA_BODY) को environment में सेट करें: यह हर request body में vendor फ़ील्ड मिला देती है — जैसे {"thinking":{"type":"disabled"}} — और चूँकि यह free-form है, इसके भीतर किसी tuning फ़ील्ड को auth फ़ील्ड से अलग पहचानने का कोई तरीका ही नहीं है। इसके ज़रिये model, message और token फ़ील्ड override नहीं किए जा सकते।

--base-url flag है, और config फ़ाइल में उसका पूरा स्वागत है — हर checkout को एक ही साझा gateway पर भेजने वाली टीम के लिए ही तो वह फ़ाइल बनी है। जिस URL में credentials बैठे हों (https://user:pass@gw.internal/v1) वह वहाँ अस्वीकार होता है, और सिर्फ़ वहीं; credential environment में ही रखें।

--provider वेंडर नहीं, wire format चुनता है: openai (default) हर OpenAI-compatible endpoint से बात करता है — यानी लगभग सभी से; anthropic और gemini उन दो के लिए हैं जो OpenAI-compatible नहीं हैं।

stdout
{
  "phasesRun": ["1", "2a", "2b", "2c", "3"],
  "nCards": 412,
  "nStages": 9,
  "nUnassignedFiles": 0,
  "nRegisters": 6,
  "usage": { "promptTokens": 1840221, "completionTokens": 214880, "totalTokens": 2055101 }
}

render

Work directory → markdown, और चाहें तो और भी। कोई LLM नहीं।

handbook render --work <dir> [--title <t>] [--html] [--html-single] [--agent-site] [--llms-txt]
Flagडिफ़ॉल्टयह क्या करता है
--work <dir>आवश्यकरेंडर करने के लिए work directory
--title <title>System Handbookआउटपुट में handbook का शीर्षक
--out <dir><work>/handbookकहाँ लिखना है
--htmlfalseसाथ में बहु-पृष्ठ HTML साइट, <out>/html के नीचे
--html-singlefalseसाथ में एक स्वयं-निहित <out>/handbook.html
--agent-sitefalseसाथ में agent index + fact तालिकाएँ, <out>/agent के नीचे
--llms-txtfalseसाथ में llms.txt और llms-full.txt
--source-base-url <url>हर file card को <url>/<relative path> से जोड़ें

--source-base-url के बिना आउटपुट में कोई भी बाहरी URL नहीं होता, जो तब मायने रखता है जब आप किसी निजी codebase के लिए handbook भेज रहे हों।

--out केवल scoped है: इसका environment variable HANDBOOK_RENDER_OUT है, सपाट HANDBOOK_OUT नहीं, क्योंकि plan और skill पर --out का मतलब अलग होता है।


skill

रेंडर किया गया handbook → agent SKILL पैकेज। कोई LLM नहीं।

handbook skill --handbook <dir> --out <dir> --name <slug> [options]
Flagडिफ़ॉल्टयह क्या करता है
--handbook <dir>आवश्यकरेंडर किए गए handbook की directory
--out <dir>आवश्यकSKILL पैकेज कहाँ जाएगा
--name <slug>आवश्यकछोटे अक्षरों-हाइफ़न वाला slug; <slug>-handbook बनाता है
--project <name>(--name)गद्य में इस्तेमाल होने वाला मानवीय प्रोजेक्ट नाम
--work <dir>phase 2 के assignment से coverage.json जोड़ता है
--source <dir>--work के साथ, हर फ़ाइल का content hash जोड़ता है
--agent-dir <dir>agent index और उसकी fact तालिकाएँ references/agent/ के नीचे भेजता है
--lang <l>enSKILL.md के मुख्य भाग की भाषा। Frontmatter अंग्रेज़ी रहता है

जानने लायक दो इनकार

--out न तो handbook directory होनी चाहिए और न ही उसकी कोई पूर्वज: build की शुरुआत --out को मिटाने से होती है, जो ठीक उसी चीज़ को हटा देगा जिसे पैक किया जा रहा है। और --lang zh आपको अंग्रेज़ी frontmatter के साथ चीनी मुख्य भाग देता है — agent runtimes description के पाठ पर रूटिंग करते हैं, इसलिए उसका अनुवाद चुपचाप skill चयन तोड़ देगा।


validate

एक SKILL पैकेज जाँचें। कोई LLM नहीं। विफलता पर 2 लौटाता है।

handbook validate --skill <dir> [--source <dir>]
Flagडिफ़ॉल्टयह क्या करता है
--skill <dir>आवश्यकजाँचने के लिए skill directory
--source <dir>drift पकड़ने के लिए जीवित स्रोत का दोबारा hash लें

संरचना, frontmatter अनुबंध, index ↔ stage-पृष्ठ संगति, coverage.json स्कीमा और hash की ताज़गी जाँचता है। त्रुटियाँ और चेतावनियाँ stderr पर जाती हैं।


plan

Handbooks-निर्देशित परिवर्तन स्थानीयकरण। एक LLM endpoint चाहिए। केवल पढ़ने वाला।

handbook plan --source <dir> --request "<text>" [--handbook <dir>] [--out <file>]
Flagडिफ़ॉल्टयह क्या करता है
--source <dir>आवश्यकजिस codebase के लिए योजना बननी है (उसमें कभी लिखा नहीं जाता)
--request <text>आवश्यकप्राकृतिक भाषा में परिवर्तन का अनुरोध
--handbook <dir>रेंडर किया handbook या skills/<x>/references। ज़ोरदार सिफ़ारिश
--out <file>(stdout)योजना यहाँ लिखें
--max-turns <n>30Agent का turn बजट

साथ में साझा LLM विकल्प।

अगर planner ने हार मान ली — उसने tool परिणाम गढ़ लिए, turns खत्म कर दिए, या बिना किसी काम की चीज़ के खत्म किया — तो वह गैर-शून्य के साथ बाहर निकलता है, बजाय ऐसी माफ़ी लिखने के जिसे कोई स्क्रिप्ट apply में डाल दे।


apply

किसी योजना के EDIT ब्लॉक लागू करें। कोई LLM नहीं। कुछ भी न उतरे तो 2 लौटाता है।

handbook apply --source <dir> --plan <file> [--dry-run] [--backup-root <dir>]
Flagडिफ़ॉल्टयह क्या करता है
--source <dir>आवश्यकसंपादित किया जाने वाला वृक्ष
--plan <file>आवश्यकhandbook plan से आई योजना
--dry-runfalseकेवल सत्यापन — कभी नहीं लिखता
--backup-root <dir><source>/.handbook-patchesबैकअप कहाँ जाते हैं
stdout
{
  "ok": true,
  "dryRun": false,
  "outcomes": [
    { "index": 1, "file": "src/upload.py", "where": "Uploader.send (~88)", "status": "applied", "line": 88 }
  ],
  "changedFiles": ["src/upload.py"],
  "backupDir": "/repo/.handbook-patches/2026-08-08T14-05-11-204Z",
  "problems": []
}

स्थितियाँ: applied · created · no-match · ambiguous · file-missing · not-a-file · unsafe-path · undecodable · skipped


rollback

Patch बैकअप से स्रोत वृक्ष बहाल करें। कोई LLM नहीं।

handbook rollback --backup <dir> [--source <dir>] [--force]
Flagडिफ़ॉल्टयह क्या करता है
--backup <dir>आवश्यकmanifest.json वाली बैकअप directory
--source <dir>किसी दूसरे वृक्ष का बैकअप हो तो अस्वीकार करें
--forcefalsepatch के बाद बदली फ़ाइलें भी बहाल करें

--force के बिना, जिस फ़ाइल का मौजूदा hash patch-के-बाद वाले hash से मेल नहीं खाता उसे अस्वीकार कर दिया जाता है — उसे बहाल करना तब से किए गए हर काम को चुपचाप मिटा देता।


resync

कोड बदलने के बाद handbook को आगे बढ़ाएँ।

handbook resync --case <dir> --work <dir> [options]
Flagडिफ़ॉल्टयह क्या करता है
--case <dir>आवश्यकCase directory: edited/ + वैकल्पिक plan.md + वैकल्पिक change.diff
--work <dir>आवश्यकआगे बढ़ाने के लिए work directory
--title <title>System Handbookदोबारा रेंडर करते समय इस्तेमाल होने वाला शीर्षक
--no-llm(LLM चालू)केवल संरचनात्मक ताज़गी; गद्य बासी चिह्नित होता है
--no-render(रेंडर चालू)पहले से रेंडर किए आउटपुट ताज़ा करना छोड़ दें
--corrections <file>corrections.jsonl; उसकी फ़ाइलें refresh सेट को चौड़ा करती हैं
--detail <d>(मौजूदा handbook से मिलाएँ)दोबारा बने cards के लिए brief या deep
--narrate-lang <l>(मौजूदा handbook से मिलाएँ)en या zh

साथ में साझा LLM विकल्प।

--detail और --narrate-lang को अनसेट छोड़ना ही सही डिफ़ॉल्ट है: अनसेट का मतलब है «जो यह handbook पहले से है, वही रखो», इसलिए resync किसी deep handbook को चुपचाप brief पर नहीं गिराता।


studio

स्थानीय वेब UI। Ctrl-C तक चलता रहता है।

handbook studio [--port <n>] [--host <addr>] [--state-dir <dir>]
Flagडिफ़ॉल्टयह क्या करता है
--port <n>4860जिस पोर्ट पर सुनना है
--host <addr>127.0.0.1Bind पता। Containers को 0.0.0.0 चाहिए
--state-dir <dir>$HOME/.handbook-studioRegistry और प्रबंधित work directories

साथ में साझा LLM विकल्प — Studio उन्हें बाकी हर कमांड जैसी ही परतों से हल करता है, इसलिए --model और config फ़ाइल का llm: ब्लॉक दोनों उसके jobs तक पहुँचते हैं।

--host 0.0.0.0 सेट करने से Studio किसी काम के अर्थ में दूर से पहुँच योग्य नहीं हो जाता: CSRF guard Host हेडर जाँचता है, इसलिए LAN IP बताने वाला अनुरोध 403 के साथ अस्वीकार होता है। देखें Studio


config

हल की गई configuration और हर मान कहाँ से आया, यह छापें। कोई LLM नहीं।

handbook config [--command <name>] [--json] [--check]
Flagडिफ़ॉल्टयह क्या करता है
--command <name>generateकेवल वे settings दिखाएँ जो इस subcommand पर लागू होती हैं
--jsonfalseमशीन-पठनीय आउटपुट
--checkfalseकेवल सत्यापन; कुछ भी अमान्य या गायब हो तो exit code 2
handbook config --command generate      # a table, with provenance per row
handbook config --json | jq '.settings[] | select(.source.kind == "env")'
handbook config --check                 # put this one in CI

यह जान-बूझकर टूटी configuration दिखाती है

हर दूसरी कमांड के उलट, config अमान्य मान पर रुकती नहीं। गायब --source एक दिखने वाली पंक्ति — unset (required) के रूप में आता है, बजाय उसी एक औज़ार को गिरा देने के जिससे आप ठीक वही समस्या डीबग करते।


Exit codes

Codeअर्थ
0सफलता
1एक त्रुटि — अमान्य configuration, गायब artifact, विफल रन। संदेश stderr पर, उपसर्ग handbook: error:
2एक जाँच विफल: validate को समस्याएँ मिलीं, apply पूरी तरह नहीं उतरा, या config --check को कुछ अमान्य मिला

2 का मतलब है «औज़ार ने काम किया, और जवाब है नहीं»। स्क्रिप्टों को इसे 1 से अलग तरह से बरतना चाहिए।

pnpm शॉर्टकट

किसी clone से, इनमें से हर एक पहले build करता है और flags सीधे आगे भेज देता है:

pnpm analyze  --source ~/code/proj --work work/proj
pnpm generate --source ~/code/proj --work work/proj
pnpm render   --work work/proj --html --agent-site --llms-txt
pnpm skill    --handbook work/proj/handbook --out skills/proj --name proj
pnpm validate --skill skills/proj --source ~/code/proj
pnpm plan     --source ~/code/proj --request "…" --out plan.md
pnpm apply    --source ~/code/proj --plan plan.md --dry-run
pnpm rollback --backup ~/code/proj/.handbook-patches/<stamp>
pnpm resync   --case cases/mycase --work work/proj
pnpm studio
pnpm config:show --command generate
pnpm handbook --help

इस पृष्ठ पर