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.
- package/.claude-plugin/marketplace.json +13 -0
- package/.claude-plugin/plugin.json +9 -0
- package/LICENSE +21 -0
- package/README.md +95 -0
- package/SKILL.md +189 -0
- package/assets/templates.md +74 -0
- package/bin/audit.mjs +60 -0
- package/bin/bid-pack.mjs +348 -0
- package/bin/cover-letter.mjs +67 -0
- package/bin/cv-gap.mjs +90 -0
- package/bin/cv-tailor.mjs +261 -0
- package/bin/dossier.mjs +32 -0
- package/bin/extract.mjs +148 -0
- package/bin/financial-proposal.mjs +114 -0
- package/bin/lib.mjs +109 -0
- package/bin/market-rates.mjs +54 -0
- package/bin/package.mjs +234 -0
- package/bin/pdf-extract.mjs +135 -0
- package/bin/pricing-model.mjs +73 -0
- package/bin/profile.mjs +201 -0
- package/bin/render.mjs +199 -0
- package/bin/simulator.mjs +67 -0
- package/bin/template-filler.mjs +160 -0
- package/bin/tor-to-proposal.mjs +63 -0
- package/commands/tor-audit.md +10 -0
- package/commands/tor-bid.md +18 -0
- package/commands/tor-cv.md +10 -0
- package/commands/tor-pack.md +11 -0
- package/commands/tor-price.md +11 -0
- package/commands/tor-screen.md +11 -0
- package/docs/PROMPT-GUIDE.md +245 -0
- package/docs/TECHNICAL-GUIDE.md +211 -0
- package/docs/USER-GUIDE.md +228 -0
- package/library/SCHEMA.md +45 -0
- package/library/bids/.gitkeep +0 -0
- package/mcp/server.mjs +358 -0
- package/package.json +57 -0
- package/references/packs/eu-prag.md +29 -0
- package/references/packs/undp.md +30 -0
- package/references/packs/usaid.md +29 -0
- package/references/packs/worldbank.md +26 -0
- package/references/pricing-standards.md +52 -0
- package/references/render-specs.md +60 -0
- 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.
|