tor-to-proposal 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/.claude-plugin/marketplace.json +13 -0
  2. package/.claude-plugin/plugin.json +9 -0
  3. package/LICENSE +21 -0
  4. package/README.md +95 -0
  5. package/SKILL.md +189 -0
  6. package/assets/templates.md +74 -0
  7. package/bin/audit.mjs +60 -0
  8. package/bin/bid-pack.mjs +348 -0
  9. package/bin/cover-letter.mjs +67 -0
  10. package/bin/cv-gap.mjs +90 -0
  11. package/bin/cv-tailor.mjs +261 -0
  12. package/bin/dossier.mjs +32 -0
  13. package/bin/extract.mjs +148 -0
  14. package/bin/financial-proposal.mjs +114 -0
  15. package/bin/lib.mjs +109 -0
  16. package/bin/market-rates.mjs +54 -0
  17. package/bin/package.mjs +234 -0
  18. package/bin/pdf-extract.mjs +135 -0
  19. package/bin/pricing-model.mjs +73 -0
  20. package/bin/profile.mjs +201 -0
  21. package/bin/render.mjs +199 -0
  22. package/bin/simulator.mjs +67 -0
  23. package/bin/template-filler.mjs +160 -0
  24. package/bin/tor-to-proposal.mjs +63 -0
  25. package/commands/tor-audit.md +10 -0
  26. package/commands/tor-bid.md +18 -0
  27. package/commands/tor-cv.md +10 -0
  28. package/commands/tor-pack.md +11 -0
  29. package/commands/tor-price.md +11 -0
  30. package/commands/tor-screen.md +11 -0
  31. package/docs/PROMPT-GUIDE.md +245 -0
  32. package/docs/TECHNICAL-GUIDE.md +211 -0
  33. package/docs/USER-GUIDE.md +228 -0
  34. package/library/SCHEMA.md +45 -0
  35. package/library/bids/.gitkeep +0 -0
  36. package/mcp/server.mjs +358 -0
  37. package/package.json +57 -0
  38. package/references/packs/eu-prag.md +29 -0
  39. package/references/packs/undp.md +30 -0
  40. package/references/packs/usaid.md +29 -0
  41. package/references/packs/worldbank.md +26 -0
  42. package/references/pricing-standards.md +52 -0
  43. package/references/render-specs.md +60 -0
  44. package/references/review-rules.md +38 -0
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env node
2
+ // tor-to-proposal: command router. All tools are also directly runnable.
3
+ import { spawnSync } from 'node:child_process';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+
7
+ const TOOLS = {
8
+ 'pdf-extract': 'pdf-extract.mjs',
9
+ extract: 'extract.mjs',
10
+ 'cv-gap': 'cv-gap.mjs',
11
+ 'cv-tailor': 'cv-tailor.mjs',
12
+ 'cover-letter': 'cover-letter.mjs',
13
+ 'market-rates': 'market-rates.mjs',
14
+ 'pricing-model': 'pricing-model.mjs',
15
+ 'financial-proposal': 'financial-proposal.mjs',
16
+ dossier: 'dossier.mjs',
17
+ simulator: 'simulator.mjs',
18
+ 'template-filler': 'template-filler.mjs',
19
+ audit: 'audit.mjs',
20
+ render: 'render.mjs',
21
+ package: 'package.mjs',
22
+ 'bid-pack': 'bid-pack.mjs',
23
+ profile: 'profile.mjs',
24
+ init: 'profile.mjs', // plug-and-play alias: tor-to-proposal init
25
+ };
26
+
27
+ const DESCRIPTIONS = {
28
+ 'pdf-extract': 'PDF/docx/xlsx -> [[PAGE n]]-tagged text + signals',
29
+ extract: 'ToR -> tor-extract.json + bid screen + compliance matrix',
30
+ 'cv-gap': 'CV <-> ToR gap analysis (MATCH / GAP-do-not-claim)',
31
+ 'cv-tailor': 'build + lint a ToR-tailored CV (every bullet anchored to the master CV)',
32
+ 'cover-letter': 'lint a drafted cover letter (agent drafts)',
33
+ 'market-rates': 'benchmark scaffold + rate position vs cited band',
34
+ 'pricing-model': 'multi-format rates, every line shows its formula',
35
+ 'financial-proposal': 'client-facing financial proposal from pricing.json (md + xlsx)',
36
+ dossier: 'client intelligence scaffold',
37
+ simulator: 'coverage vs evaluation grid -> at-risk points',
38
+ 'template-filler': "fill the client's own docx/xlsx forms",
39
+ audit: 'pre-submission hard-fail gate',
40
+ render: 'fallback markdown -> PDF emitter (no document tooling needed)',
41
+ package: 'verify final docs (re-extract + re-audit) -> pack/ + checklist + zip',
42
+ 'bid-pack': 'one-shot orchestrator: start | apply | pack + the consolidated questionnaire',
43
+ profile: 'consultant profile wizard (~/.tor-to-proposal/profile.json)',
44
+ init: 'alias for profile — one-time setup wizard',
45
+ };
46
+
47
+ const [cmd, ...rest] = process.argv.slice(2);
48
+ if (!cmd || cmd === 'help' || cmd === '--help') {
49
+ process.stdout.write(`tor-to-proposal — the bid tool that refuses to lie.\n\nUsage: tor-to-proposal <command> [options]\n\nQuick start (plug and play):\n tor-to-proposal init one-time profile + setup wizard\n tor-to-proposal bid-pack start --tor tor.pdf --cv cv.txt --dir mybid/\n tor-to-proposal bid-pack apply --answers answers.json --dir mybid/\n tor-to-proposal bid-pack pack --dir mybid/ --fallback-pdf\n\nCommands:\n${Object.keys(TOOLS).map((k) => ` ${k.padEnd(19)} ${DESCRIPTIONS[k]}`).join('\n')}\n\nPer-command help: tor-to-proposal <command> --help\nAgent workflow: see SKILL.md\n`);
50
+ process.exit(cmd ? 0 : 1);
51
+ }
52
+ if (!TOOLS[cmd]) {
53
+ if (cmd === 'mcp-server') {
54
+ // npx launch path: "npx tor-to-proposal mcp-server" (stdio MCP)
55
+ const res = spawnSync(process.execPath, [path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'mcp', 'server.mjs'), ...rest], { stdio: 'inherit' });
56
+ process.exit(res.status ?? 1);
57
+ }
58
+ process.stderr.write(`unknown command: ${cmd}\n\nRun without arguments to list commands.\n`);
59
+ process.exit(1);
60
+ }
61
+ const script = path.join(path.dirname(fileURLToPath(import.meta.url)), TOOLS[cmd]);
62
+ const res = spawnSync(process.execPath, [script, ...rest], { stdio: 'inherit' });
63
+ process.exit(res.status ?? 1);
@@ -0,0 +1,10 @@
1
+ ---
2
+ description: Pre-submission audit — hard-fail gate before you submit a bid
3
+ allowed-tools: Bash(node:*), Read, Write
4
+ ---
5
+ Audit a finished proposal before submission. Proposal: $ARGUMENTS (ask for the compliance matrix path if it exists).
6
+
7
+ 1. `node <plugin-root>/bin/audit.mjs --proposal <file> --matrix <matrix> --extras <cover-letter>`.
8
+ 2. HARD FAILS ([FILL] leftovers, incomplete matrix rows): list them, fix what can be fixed from the ToR, ask the user for the rest. Re-run until exit 0.
9
+ 3. Review list (untraced claim-shaped sentences): for each, either add a page tag/source or delete the claim — never soften it into vagueness.
10
+ 4. Close with the final pre-submission checklist from the skill's references/review-rules.md (submission channel, deadline timezone, file naming).
@@ -0,0 +1,18 @@
1
+ ---
2
+ description: Run the full tor-to-proposal bid pack on a ToR (and CV)
3
+ allowed-tools: Bash(node:*), Read, Write, Glob, Grep
4
+ ---
5
+ Run the complete tor-to-proposal bid pack (see the skill's SKILL.md) on: $ARGUMENTS
6
+
7
+ Follow the four bid-pack phases in order:
8
+
9
+ 1. INTAKE — `bid-pack start --tor <file> [--cv <file>] --dir <bid-dir>`; review bid-screen.md with the user; go/no-go is THEIR call.
10
+ 2. QUESTIONNAIRE — put EVERY question from out/questions.md to the user in ONE message. Write answers.json, then `bid-pack apply`. Repeat until 0 open. Never answer for the user.
11
+ 3. DRAFT — cover letter (lint to PASS), technical response per criterion (simulator to ~0 at-risk), tailored CV (cv-tailor lint PASS), financial proposal (already rendered by apply).
12
+ 4. PACK — render the four PDFs (references/render-specs.md), then `bid-pack pack --dir <bid-dir>`; deliver pack/ + submission-checklist.md.
13
+
14
+ Hard requirements:
15
+ - Pricing only from user-confirmed numbers (profile confirm or answers.json) — never assume.
16
+ - Every ToR claim carries (p.X); anything unverified stays [FILL].
17
+ - GAP skills from cv-gap must not appear anywhere, including the tailored CV.
18
+ - `bid-pack pack` must exit 0 before you call anything done; it re-audits the rendered bytes.
@@ -0,0 +1,10 @@
1
+ ---
2
+ description: CV vs ToR gap analysis with honest MATCH/GAP verdicts
3
+ allowed-tools: Bash(node:*), Read, Write
4
+ ---
5
+ Analyze CV fit against a ToR. ToR: $ARGUMENTS (ask for the CV path if not obvious).
6
+
7
+ 1. Extract the ToR (`pdf-extract` if needed), then `node <plugin-root>/bin/cv-gap.mjs --tor tor.txt --cv cv.txt`.
8
+ 2. Present the MATCH/GAP table with page references.
9
+ 3. Suggest CV bullet reordering using the ToR's verbatim terminology — facts unchanged, numbers only from the user.
10
+ 4. GAP rows: state plainly these must NOT be claimed in the proposal. If a gap is disqualifying, say so as a go/no-go risk.
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: Verify and package a finished bid into the submittable bid pack
3
+ allowed-tools: Bash(node:*), Read, Write, Glob, Grep
4
+ ---
5
+ Package the finished bid directory into the verified submission pack: $ARGUMENTS
6
+
7
+ 1. Confirm the four documents exist as markdown in the bid dir (cover-letter.md, cv-tailored.md, technical-proposal.md, financial-proposal.md) and the lints pass (cover-letter lint, cv-tailor lint, simulator ~0 at-risk).
8
+ 2. Render each to PDF with your document tooling per references/render-specs.md (cover-letter.pdf, cv-tailored.pdf, technical-proposal.pdf, financial-proposal.pdf). If you have no document tooling, let --fallback-pdf do it.
9
+ 3. Run `bid-pack pack --dir <bid-dir>` (add --fallback-pdf only if needed).
10
+ 4. If it exits non-zero: fix every hard fail in pack-report.md, re-render, re-run. It removes stale packs on failure — never submit an old pack.
11
+ 5. Deliver pack/ + submission-checklist.md + the zip; remind the user pricing.md/pricing.json stay private.
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: Honest pricing — cited benchmark band + transparent rate model
3
+ allowed-tools: Bash(node:*), Read, Write
4
+ ---
5
+ Price this assignment honestly: $ARGUMENTS
6
+
7
+ 1. `node <plugin-root>/bin/market-rates.mjs scaffold` — have the user (or an approved web search) fill every benchmark row with source + date.
8
+ 2. GATE: ask the user to confirm THEIR base rate, loading (0.2–0.35 typical) and contingency (0.05–0.15 typical) before running pricing-model. Never suggest a base rate.
9
+ 3. `node <plugin-root>/bin/pricing-model.mjs --base N --basis day --loading L --contingency C --effort D --out-dir out` — present the table with formulas.
10
+ 4. Position vs the cited band with market-rates position. Above band → premium justification needed; below band → check the floor.
11
+ 5. Remind: the client's financial form governs; template-filler restructures into it.
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: Quick go/no-go screen of a ToR — extract + bid screen + compliance matrix
3
+ allowed-tools: Bash(node:*), Read, Write
4
+ ---
5
+ Screen this ToR for a go/no-go decision: $ARGUMENTS
6
+
7
+ 1. `node <plugin-root>/bin/pdf-extract.mjs <file> --out tor.txt` (if the source is PDF/docx/xlsx; scanned → tell the user to OCR, stop).
8
+ 2. `node <plugin-root>/bin/extract.mjs --tor tor.txt --out-dir out` (add EV inputs only if the user supplies fee, bid-days, day-rate).
9
+ 3. Present: title, client, deadline, duration/effort, evaluation weights + sum check, the go/no-go checklist, red flags — each with its page.
10
+
11
+ No drafting in this step. End by asking: go or no-go, and which steps to run next (cv-gap, pricing, full bid).
@@ -0,0 +1,245 @@
1
+ # tor-to-proposal — Prompt Guide for AI Assistants
2
+
3
+ For people who work **through an AI assistant** — Claude (Desktop/Code),
4
+ ChatGPT, Codex, or any agent — instead of typing CLI commands. Includes setup
5
+ per assistant and **copy-paste prompts for every step**.
6
+
7
+ The working pattern is always the same:
8
+ **you provide the evidence and the decisions; the assistant runs the tools;
9
+ the tools enforce the honesty rules.** When you see `[FILL]`, that's a
10
+ question addressed to you.
11
+
12
+ ---
13
+
14
+ ## 1. Setup per assistant (one-time)
15
+
16
+ ### Claude Desktop (chat app)
17
+ Settings → Developer → Edit Config → add to `claude_desktop_config.json`:
18
+ ```json
19
+ {
20
+ "mcpServers": {
21
+ "tor-to-proposal": {
22
+ "command": "npx",
23
+ "args": ["-y", "tor-to-proposal", "mcp-server"]
24
+ }
25
+ }
26
+ }
27
+ ```
28
+ Or, if you cloned the repo: `"command": "node", "args": ["/absolute/path/to/tor-to-proposal/mcp/server.mjs"]`.
29
+ Restart the app. Either way, run `npx tor-to-proposal init` once for your profile.
30
+
31
+ ### Claude Code (terminal)
32
+ ```bash
33
+ git clone https://github.com/ahaomar/tor-to-proposal && cd tor-to-proposal && npm install
34
+ claude mcp add tor-to-proposal -- node "$(pwd)/mcp/server.mjs"
35
+ ```
36
+ Or install as a plugin (skills + `/tor-*` slash commands + MCP in one):
37
+ ```bash
38
+ # inside Claude Code:
39
+ /plugin marketplace add ahaomar/tor-to-proposal
40
+ /plugin install tor-to-proposal@tor-to-proposal
41
+ ```
42
+
43
+ ### Codex CLI
44
+ `~/.codex/config.toml`:
45
+ ```toml
46
+ [mcp_servers.tor-to-proposal]
47
+ command = "node"
48
+ args = ["/absolute/path/to/tor-to-proposal/mcp/server.mjs"]
49
+ ```
50
+
51
+ ### ChatGPT
52
+ If your build supports local MCP connectors (desktop developer mode): point a
53
+ connector at the same `server.mjs`. **If not:** skip setup — use the
54
+ "paste-the-text" prompts in §4; everything works from pasted text except
55
+ direct PDF reading (paste the ToR text instead).
56
+
57
+ ### Any other agent (generic)
58
+ Any agent that can (a) read/write local files and (b) run one shell command
59
+ can drive this repo: give it the repo path and say *"follow SKILL.md in that
60
+ folder"*. The SKILL.md is written to be followed by any capable agent.
61
+
62
+ **Test any setup with:** *"List your tor-to-proposal tools."* → you should see
63
+ twenty tools: `bid_pack_start`, `bid_pack_apply`, `bid_pack_pack`,
64
+ `profile_set`, `profile_get`, `cv_tailor_build`, `cv_tailor_lint`,
65
+ `financial_proposal`, `render_pdf`, `package_bid`, plus the classics:
66
+ `pdf_extract`, `extract_tor`, `cv_gap`, `cover_letter_lint`,
67
+ `market_rates_scaffold`, `market_rates_position`, `pricing_model`, `dossier`,
68
+ `simulate`, `template_filler`, `audit`.
69
+
70
+ ---
71
+
72
+ ## 2. The one-prompt start (full workflow)
73
+
74
+ Paste this, with your paths filled in:
75
+
76
+ ```text
77
+ I am bidding on a consultancy. Run the full tor-to-proposal bid pack
78
+ (SKILL.md) with me. My files:
79
+ - ToR: /Users/me/bids/undp-2026-042/tor.pdf
80
+ - CV: /Users/me/bids/undp-2026-042/cv.txt
81
+ - Bid folder: /Users/me/bids/undp-2026-042/bid
82
+
83
+ Rules for you:
84
+ - Start with bid_pack_start, then show me the bid screen — I decide go/no-go.
85
+ - Then put EVERY question from out/questions.md to me in ONE message. I answer
86
+ once; write my answers to answers.json and run bid_pack_apply. Repeat until
87
+ zero open. Never invent an answer, never ask questions one by one.
88
+ - Drafts get linted/simulated/audited with the tools; fix and loop until they pass.
89
+ - Finish with the four PDFs rendered per references/render-specs.md and
90
+ bid_pack_pack. Deliver pack/ + submission-checklist.md. Nothing is "done"
91
+ while pack exits non-zero.
92
+ ```
93
+
94
+ The assistant runs the mechanical pipeline, shows the bid screen, then asks
95
+ **one consolidated questionnaire**. Answer in plain English; it does the tool
96
+ work and writes `answers.json` for you.
97
+
98
+ ---
99
+
100
+ ## 3. Step-by-step prompt library
101
+
102
+ Run them one at a time if you prefer control (recommended for your first bid).
103
+
104
+ **The bid pack in three beats (recommended):**
105
+ ```text
106
+ 1. Start the bid pack on /path/tor.pdf + /path/cv.txt (bid_pack_start) and
107
+ walk me through the bid screen. Wait for my go/no-go.
108
+ 2. Give me the consolidated questionnaire (out/questions.md) — all questions,
109
+ numbered, one message. I'll answer inline; transcribe to answers.json and
110
+ run bid_pack_apply until zero open.
111
+ 3. Draft the cover letter + technical response, lint/simulate to PASS, then
112
+ render the four PDFs and run bid_pack_pack. Show me pack-report.md.
113
+ ```
114
+
115
+ **Screen a ToR (go/no-go):**
116
+ ```text
117
+ Screen this ToR for a go/no-go decision: /path/tor.pdf. Give me the one-page
118
+ bid screen: title, reference, deadline, duration/effort, evaluation criteria
119
+ with weights, red flags. Ask me for EV inputs (expected fee, bid days, my day
120
+ rate) if you want to show expected value. No drafting yet.
121
+ ```
122
+
123
+ **CV reality check:**
124
+ ```text
125
+ Compare my CV (/path/cv.txt) against the extracted ToR. Show the MATCH/GAP
126
+ table with page numbers. Be blunt about GAP rows — I want to know what I must
127
+ not claim. Then suggest how to reorder my CV bullets using the ToR's own
128
+ terminology, without changing any facts.
129
+ ```
130
+
131
+ **Pricing (the gated one):**
132
+ ```text
133
+ Help me price this. My floor numbers: minimum net income $[X] per year,
134
+ realistic billable days [Y], overhead/loading about [Z]%. First scaffold the
135
+ benchmark table; research UNDP/UNICEF fee grids and UNGM award notices for
136
+ [this sector] in [region] and give me each band WITH source URL + access date
137
+ — no uncited numbers. Then run the pricing model with my confirmed inputs and
138
+ show every formula. Finally position my rate against the cited band.
139
+ ```
140
+ *(If you don't allow web search, the assistant must leave benchmark rows as
141
+ [FILL] and give you the table to fill. That is correct behavior — don't push it.)*
142
+
143
+ **Cover letter:**
144
+ ```text
145
+ Draft a one-page cover letter per assets/templates.md: verbatim title + ref +
146
+ deadline block, 2-sentence understanding from Background/Objectives only, fit
147
+ section quoting 3–5 JD requirements (p.X) with my CV evidence — mark [FILL]
148
+ where I haven't given you evidence yet — compliance declarations, availability
149
+ per the ToR start date. Then lint it with cover_letter_lint and fix everything
150
+ it flags. No superlatives.
151
+ ```
152
+
153
+ **Technical response + self-score:**
154
+ ```text
155
+ Draft the technical response: one section per scored criterion, highest weight
156
+ first, in the client's vocabulary, every deliverable addressed. Extract the
157
+ sub-elements of each criterion verbatim into sub-elements.json, then run
158
+ simulate and give me the at-risk points. Loop with me: rewrite the weakest
159
+ criterion, re-simulate, until at-risk ≈ 0.
160
+ ```
161
+
162
+ **Their forms:**
163
+ ```text
164
+ Fill the client's financial form (/path/financial-form.xlsx) using
165
+ template_filler: map first and show me every field, fill from pricing.json +
166
+ tor-extract.json (build data.json so labels match exactly), then check. If any
167
+ field stays [FILL], list what you need from me. Never touch their formulas.
168
+ ```
169
+
170
+ **Final gate:**
171
+ ```text
172
+ Run the audit on my final files (proposal, cover letter, compliance matrix).
173
+ If it exits non-zero, list the hard fails, fix what you can from the ToR, and
174
+ ask me for the rest. Then run it again until it passes. Finish with the
175
+ pre-submission checklist (submission channel, deadline timezone, file naming).
176
+ ```
177
+
178
+ **Record the outcome (later):**
179
+ ```text
180
+ Record this bid in my library per library/SCHEMA.md: outcome [won/lost],
181
+ fee quoted [X], any evaluator feedback I paste below. Local only.
182
+ ```
183
+
184
+ ---
185
+
186
+ ## 4. Paste-the-text variant (no file access — e.g. plain ChatGPT web)
187
+
188
+ If your assistant cannot read local files, paste content directly:
189
+
190
+ ```text
191
+ You are enforcing the tor-to-proposal honesty rules on the text I paste:
192
+ 1) Every claim about the assignment must cite "p.X" of the ToR text below or
193
+ stay [FILL]. 2) Never claim skills absent from my CV. 3) No pricing without
194
+ asking me first for my floor numbers. 4) No superlatives.
195
+
196
+ === ToR text ===
197
+ [paste the full ToR text, or use the pdf-extract output]
198
+
199
+ === My CV ===
200
+ [paste CV]
201
+ ```
202
+
203
+ Then use the same step prompts from §3, replacing file paths with "the ToR
204
+ text above". You lose automatic lint/simulate runs — so end with:
205
+
206
+ ```text
207
+ Before I submit: list every sentence in my drafts that violates the rules
208
+ above, with the fix. Act as the audit tool yourself, and be harsh.
209
+ ```
210
+
211
+ ---
212
+
213
+ ## 5. What the assistant will ask you (have answers ready)
214
+
215
+ - **Go/no-go gates:** Can you staff it by their start date? Is the EV
216
+ positive at a realistic win probability? Any eligibility conflicts?
217
+ - **Pricing gate (mandatory):** your minimum income, billable days, loading %,
218
+ contingency % — *your* numbers, not its suggestions.
219
+ - **[FILL] questions:** evidence for each fit claim (years, budgets, client
220
+ names), references, registration status (UNGM etc.).
221
+ - **Form fields:** anything their template demands that no ToR section states.
222
+
223
+ If it ever fills a `[FILL]` without asking, or produces a number with no
224
+ formula or source, reply: *"No invented values — mark it [FILL] and ask me."*
225
+ Then start a fresh session if it keeps drifting; the tools, not the vibes,
226
+ are the source of truth.
227
+
228
+ ---
229
+
230
+ ## 6. A real exchange, so you know what "good" looks like
231
+
232
+ > **You:** Screen /Users/me/bids/tor.pdf — should I bid?
233
+ >
234
+ > **Assistant:** Extracted 4 pages, 3 evaluation criteria (weights 15/30/25,
235
+ > sum 70 matches stated total). Deadline 12 Oct 2026 (p.2), effort ~20
236
+ > person-days (p.2), UNGM registration required before award (p.4).
237
+ > Red flags: none in the weights; validity 90 days (p.2) is standard.
238
+ > EV check — I need your inputs: expected fee, days to prepare the bid, your
239
+ > day rate. I won't assume them.
240
+ >
241
+ > **You:** fee ~9,500, bid takes 3 days, my rate 400.
242
+ >
243
+ > **Assistant:** EV at P(win)=0.5 → +$3,550; at 0.25 → −$250. At your realistic
244
+ > win rate this is worth pursuing only if the CV fit is strong. Running
245
+ > cv-gap next? Note: I will not draft anything until you call go/no-go.
@@ -0,0 +1,211 @@
1
+ # tor-to-proposal — Technical Guide
2
+
3
+ For developers, IT support, and AI engineers deploying/ extending the tool.
4
+ Companion to `USER-GUIDE.md` (end users) and `PROMPT-GUIDE.md` (agent users).
5
+
6
+ ---
7
+
8
+ ## 1. Architecture
9
+
10
+ ```
11
+ ┌────────────────────────────────────────────┐
12
+ surfaces │ CLI (bin/tor-to-proposal.mjs router) │
13
+ ───────── │ Agent skill (SKILL.md, read by the model) │
14
+ │ MCP stdio server (mcp/server.mjs) │
15
+ │ Claude Code plugin (commands/ + .mcp.json)│
16
+ └───────────────┬────────────────────────────┘
17
+ │ spawn
18
+ ┌───────────────▼────────────────────────────┐
19
+ deterministic core │ bin/*.mjs — parse, compute, gate, lint │
20
+ │ (no LLM calls, no network; pure Node ≥18) │
21
+ └───────────────┬────────────────────────────┘
22
+ │ reads/writes
23
+ ┌───────────────▼────────────────────────────┐
24
+ data │ [[PAGE n]]-tagged text · tor-extract.json │
25
+ │ pricing.json · client docx/xlsx (read-only│
26
+ │ structure) · library/bids/ (win/loss) │
27
+ └────────────────────────────────────────────┘
28
+ ```
29
+
30
+ **The agent/tool contract** (also in SKILL.md): deterministic tools parse,
31
+ compute, gate and lint; the agent drafts and does fuzzy extraction; every
32
+ agent-produced value must carry page attribution to be accepted (`extract.mjs
33
+ --json`), and every draft must pass the lint/simulate/audit gates. An LLM
34
+ alone invents; regex alone can't read a ToR; the pair is the product.
35
+
36
+ ## 2. Install variants
37
+
38
+ | Variant | Command / config | Audience |
39
+ | --- | --- | --- |
40
+ | Agent skill | `npx skills add ahaomar/tor-to-proposal` (skills.sh) | Claude Code, ZCode, any skill-aware agent |
41
+ | Claude Code plugin | `/plugin marketplace add ahaomar/tor-to-proposal` → `/plugin install tor-to-proposal@tor-to-proposal` | Claude Code users (skill + `/tor-*` commands + MCP) |
42
+ | MCP server | `claude mcp add tor-to-proposal -- node /abs/path/mcp/server.mjs`; Claude Desktop JSON; Codex `~/.codex/config.toml` `[mcp_servers.tor-to-proposal]` | Claude Desktop, Codex, ChatGPT (local connectors) |
43
+ | Standalone CLI | `git clone … && npm install`; `node bin/tor-to-proposal.mjs <cmd>` | CI, scripts, terminal users |
44
+
45
+ Deps (all local, pure JS): `unpdf` (pdf.js), `mammoth` (docx), `exceljs`
46
+ (xlsx), `adm-zip` (docx zip). Node ≥ 18. No telemetry, no network calls.
47
+
48
+ ## 3. CLI reference
49
+
50
+ Router: `node bin/tor-to-proposal.mjs <command> [options]` (each tool also
51
+ runs directly). Exit codes everywhere: **0** ok · **1** validation failure /
52
+ hard-fail gate · **2** cannot-process (scanned PDF, inverted band, refused
53
+ formula overwrite) · **3** dependencies missing.
54
+
55
+ | Command | Key options | Outputs |
56
+ | --- | --- | --- |
57
+ | `bid-pack start` | `--tor file [--cv file] [--dir bid/] [--no-cv-tailor]` | runs pdf-extract → extract → cv-gap → cv-tailor; `out/questions.{json,md}` (consolidated questionnaire), `out/bid-state.json`, `out/bid-context.json` |
58
+ | `bid-pack apply` | `--answers answers.json [--dir bid/]` | records answers; runs pricing + financial proposal on user numbers; `out/cv-evidence.json`, `out/fill-answers.json`, `out/clarifications.md`; regenerates questions |
59
+ | `bid-pack pack` | `--dir bid/ [--fallback-pdf] [--no-zip]` | delegates to `package` |
60
+ | `profile` (`init`) | `init \| set k=v… \| get [k] \| path \| erase` | `~/.tor-to-proposal/profile.json` — user-supplied identity, floor math, rate defaults, master-CV path |
61
+ | `cv-tailor build` | `--cv master.txt --extract tor-extract.json [--profile p.json] [--gap r.md]` | `cv-tailored.md` (reordered master-CV material only) + `cv-tailor-report.md` (trace + terminology mirror) |
62
+ | `cv-tailor lint` | `--cv tailored.md --master master.txt [--gap] [--evidence cv-evidence.json]` | anchor table; exit 1 on unanchored bullets, altered numbers, GAP terms |
63
+ | `financial-proposal` | `--pricing pricing.json --extract tor-extract.json [--context bid-context.json] [--xlsx]` | `financial-proposal.md` (+ `.xlsx` with visible formulas: `D8=B8*C8`, `D10=SUM`) |
64
+ | `render` | `input.md --out out.pdf [--footer text]` | plain submittable PDF (fallback tier; pure JS, WinAnsi text layout) |
65
+ | `package` | `--dir bid/ [--fallback-pdf] [--no-zip]` | re-extracts rendered PDF/docx/xlsx → re-runs audit on those bytes; `pack/` + `pack-report.md` + `submission-checklist.md` + `deadline.ics` + `<ref>-bid-pack.zip`; exit 1 removes stale packs |
66
+ | `pdf-extract` | `file`, `--out`, `--signals`, `--allow-scanned` | `[[PAGE n]]`/`[[SHEET n]]` text; signals JSON (scannedLikely, hasEvaluationTable…) |
67
+ | `extract` | `--tor`, `--json agent.json`, `--fee/--bid-days/--day-rate`, `--out-dir` | `tor-extract.json`, `bid-screen.md` (EV table), `compliance-matrix.md` |
68
+ | `cv-gap` | `--tor`, `--cv`, `--out` | match table, MATCH / "GAP — do not claim" |
69
+ | `cover-letter lint` | `lint --draft`, `--cv`, `--gap`, `--max-words` | lint report; exit 1 lists line-numbered violations |
70
+ | `market-rates scaffold` | `--title`, `--currency` | research table — zero numbers, all `[FILL]` rows |
71
+ | `market-rates position` | `--your-rate --benchmark-low --benchmark-high` | BELOW/WITHIN/ABOVE + advisory; exit 2 if low ≥ high |
72
+ | `pricing-model` | `--base --basis hour|day|week|month --loading --contingency --effort --weeks --out-dir` | `pricing.json`, `pricing.md` (formula per line) |
73
+ | `dossier` | `--client --sector --out` | dossier scaffold with type preset |
74
+ | `simulator` | `--extract --response --sub-elements --out` | at-risk pts per criterion, missing sub-elements, priorities |
75
+ | `template-filler map/fill/check` | `--template file.docx|.xlsx --data map.json --out --report` | fill-report.md (every write + trace); check exits 1 while fields unmapped |
76
+ | `audit` | `--proposal --matrix --extras … --report` | hard-fail list + untraced-claim review; exit 1 = do not submit |
77
+
78
+ ## 4. Data contracts
79
+
80
+ **Page-tag protocol** — the substrate: `[[PAGE n]]` before each PDF/docx
81
+ page's text; `[[SHEET n: Name]]` for xlsx. `lib.mjs#pageOf(text, index)`
82
+ attributes any match to its page. All citation output is `(p.n)` or
83
+ `[[PAGE n]]`; the universal placeholder is `[FILL…]`.
84
+
85
+ **`tor-extract.json`** (extract.mjs):
86
+ ```jsonc
87
+ {
88
+ "referenceNumber": { "value": "UNDP-RFP-2026-042", "page": 1 },
89
+ "deadline": { "value": "12 October 2026", "page": 2 },
90
+ "evaluation": {
91
+ "criteria": [ { "label": "…", "weight": 30, "page": 3 } ],
92
+ "weightsSum": 70, "statedTotal": 70,
93
+ "weightsSumCheck": "ok | MISMATCH (…) — surface, never fix | not-stated"
94
+ },
95
+ "paymentTerms": [ { "value": "…", "page": 4 } ],
96
+ "conflicts": [] // regex vs agent-field conflicts, recorded not hidden
97
+ }
98
+ ```
99
+
100
+ **Agent field merge (`--json`)** — `{"field": {"value": …, "page": n}}`.
101
+ Agent values are accepted **only** with page attribution; arrays must be
102
+ fully attributed; conflicts land in `conflicts[]`.
103
+
104
+ **`pricing.json`** — `{inputs (userSupplied: true), constants {8h, 5d, 4.33},
105
+ rates {hour, day, week, month}, loadedDay, quoteFloorDay, lumpSum,
106
+ assignmentTotal}` — consumed by template-filler via a label-mapped `data.json`.
107
+
108
+ **`sub-elements.json`** (simulator) — `{ "criterion label": ["verbatim
109
+ sub-element", …] }` — agent-extracted from the ToR; labels must match
110
+ tor-extract criteria exactly.
111
+
112
+ **v2 bid-pack contracts** (all under `<bid-dir>/out/`):
113
+
114
+ - **`~/.tor-to-proposal/profile.json`** — `{identity{name,credentials,…},
115
+ rates{floor{annualIncome,billableDays,costLoading},
116
+ defaults{base,basis,currency,loading,contingency}}, cv{masterPath}}`.
117
+ Every value user-supplied (`profile set` validates numbers/ranges and the
118
+ master-CV path exists). This file is the "user input" trace source.
119
+ - **`questions.json`** — `{generatedAt, dir, openCount, items:[{id, area,
120
+ type, question, why, default?, answer, answerSource}]}`. Ids are stable:
121
+ `Q-PRICING-BASE/LOADING/CONTINGENCY/CURRENCY`, `Q-PRICING-CONFIRM`,
122
+ `Q-EFFORT-DAYS`, `Q-AVAILABILITY`, `Q-VALIDITY`, `Q-FILL-CONSULTANT/
123
+ REIMBURSABLES/TAXES` (generated slots), `Q-FILL-<md5-8>` (draft fills —
124
+ hash of file+excerpt so a fixed draft drops its question),
125
+ `Q-CVE-<n>` (CV evidence), `Q-CLAR-*` (client clarifications).
126
+ - **`answers.json`** (user-written via the agent) — flat `{id: value}`.
127
+ - **`bid-context.json`** — derived: identity/consultantLine, refNo, title,
128
+ deadline, currency, availabilityDate, validity, reimbursables, taxes.
129
+ Input to financial-proposal and the pack checklist.
130
+ - **`cv-evidence.json`** — `[{id, text, source:"user-input", deleted?}]` —
131
+ the ONLY way an unanchored line may enter the tailored CV.
132
+ - **`fill-answers.json`** — `[{id, answer, source}]` — trace of draft-fill answers.
133
+ - **`bid-state.json`** — `{steps:{torExtract,structured,cvGap,cvTailored,
134
+ questions,pricing:{status}}, createdAt, updatedAt}` — resumability.
135
+ - **Pack outputs** — `<bid-dir>/pack/` (canonical names Cover-Letter.*,
136
+ CV.*, Technical-Proposal.*, Financial-Proposal.* + client forms +
137
+ submission-checklist.md + deadline.ics), `<bid-dir>/pack-report.md`,
138
+ `<bid-dir>/<ref>-bid-pack.zip`. `pricing.md`/`pricing.json` are NEVER
139
+ copied into the pack (floor math stays private).
140
+
141
+ **`library`** — `library/bids/<ref>/`: `tor-extract.json` (frozen),
142
+ `outcome.json`, `feedback.md`, `rate-band.json`. See `library/SCHEMA.md`;
143
+ aggregation only at n ≥ 10 with n + date.
144
+
145
+ ## 5. MCP server details
146
+
147
+ `mcp/server.mjs` — dependency-free MCP **stdio** server (JSON-RPC 2.0,
148
+ newline-delimited). Methods: `initialize`, `notifications/*`, `ping`,
149
+ `tools/list` (21 tools), `tools/call`. Tool args are camelCase and mapped to
150
+ CLI flags; array args flatten to repeated flags (`audit.extras`,
151
+ `profile_set.pairs`).
152
+ `tools/call` results carry stdout + stderr and `isError: true` on any non-zero
153
+ exit (a hard-fail gate therefore surfaces as a tool error the agent must read).
154
+ Host cwd is not the repo: tool descriptions mandate absolute paths. Adding a
155
+ tool = one entry in the `TOOLS` array (name, description, JSON-schema,
156
+ `argv(a)` mapper, script). `npx tor-to-proposal mcp-server` launches the same
157
+ server for npx-installed users.
158
+
159
+ ## 6. Claude Code plugin
160
+
161
+ `.claude-plugin/plugin.json` + `marketplace.json` make the repo installable
162
+ via `/plugin marketplace add`; `commands/*.md` are slash commands
163
+ (`tor-bid` — full bid pack, `tor-pack` — verify + package, `tor-screen`,
164
+ `tor-cv`, `tor-price`, `tor-audit`) that reference
165
+ `<plugin-root>` paths; `.mcp.json` registers the MCP server with
166
+ `${CLAUDE_PLUGIN_ROOT}` so npm-installed binaries resolve inside the plugin.
167
+
168
+ ## 7. Extension points
169
+
170
+ - **New donor pack**: `references/packs/<donor>.md` — conventions + what to
171
+ verify; linked from SKILL.md's read-on-demand list. No code changes.
172
+ - **New requirement pattern** (cv-gap): extend the arrays in `bin/cv-gap.mjs`
173
+ (`DOMAINS`, `CLIENTS`, `CERTS`, `SOFT`, `LANGS`).
174
+ - **New tool**: follow `bin/pricing-model.mjs` as the pattern (lib helpers,
175
+ `--help`, strict arg validation, exit codes, `[FILL]`-discipline) + wire into
176
+ router `TOOLS` map, MCP `TOOLS` array, and the test suite.
177
+ - **Richer evaluation parsing**: prefer contributing ToR samples to the
178
+ fixtures; keep the weights-sum cross-check as the integrity anchor.
179
+
180
+ ## 8. Testing
181
+
182
+ `npm test` — zero test dependencies, spawns the real CLI (39 tests):
183
+ extraction + weights-sum, pricing arithmetic, band inversion rejection, CV-gap
184
+ honesty, lint pass/fail, simulator at-risk math, docx/xlsx fill + formula
185
+ preservation (verified by re-reading the filled files), audit gate, OCR path,
186
+ **profile set/get/erase + validation rejections (isolated $HOME), cv-tailor
187
+ build + lint (honest pass; invented number, GAP term, and evidence-rescued
188
+ lines), financial proposal md+xlsx formulas, the pure-JS PDF renderer
189
+ (round-tripped through unpdf, multipage), the full bid-pack lifecycle
190
+ (start → questions → apply → pricing → pack happy path), the pack failure
191
+ path ([FILL] survives rendering → exit 1 + stale pack removal), the profile
192
+ one-keystroke confirm path, and an MCP round-trip (handshake → tools/list
193
+ with all 10 v2 tools → profile_set/get → audit hard-fail as isError)**.
194
+ Fixtures in `test/fixtures/`; runtime binaries built into `test/.tmp/`;
195
+ profile-dependent tests run with `$HOME` pointed at the temp dir so a real
196
+ profile is never touched.
197
+
198
+ ## 9. Trust model
199
+
200
+ - All parsing/computation is local; no tool makes network calls. The only
201
+ outbound step is market-rate research, run explicitly by/for the user.
202
+ - Client templates are opened read-only in structure terms: only mapped cells
203
+ are written; formulas are never overwritten (refused with exit 2).
204
+ - `library/bids/` is gitignored; corpus analytics are opt-in, n ≥ 10, always
205
+ dated.
206
+ - The gates are the guarantee: a draft that skips lint/simulate/audit is, by
207
+ definition, not produced by this workflow.
208
+ - The pack gate applies to the **rendered bytes**: `package.mjs` re-extracts
209
+ the text of every final PDF/docx/xlsx and re-runs the audit on that text, so
210
+ what is verified is what the client reads. On failure the previous pack/ and
211
+ zip are deleted — a stale pack can never survive next to a failing bid.