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,13 @@
1
+ {
2
+ "name": "tor-to-proposal",
3
+ "owner": {
4
+ "name": "Omar Farooq"
5
+ },
6
+ "plugins": [
7
+ {
8
+ "name": "tor-to-proposal",
9
+ "source": "./",
10
+ "description": "ToR + CV in -> verified bid pack out: bid screen, consolidated questionnaire, tailored CV, cover letter lint, honest pricing, financial proposal, evaluation simulator, form filling, PDF packaging with a final honesty audit."
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "tor-to-proposal",
3
+ "description": "The bid tool that refuses to lie \u2014 turns a ToR + CV into a complete, verified bid pack: cover letter, tailored CV, technical response, financial proposal, submission checklist. Skills + slash commands + MCP tools.",
4
+ "version": "2.0.0",
5
+ "author": {
6
+ "name": "Omar Farooq"
7
+ },
8
+ "license": "MIT"
9
+ }
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Omar Farooq
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # tor-to-proposal
2
+
3
+ **The bid tool that refuses to lie.**
4
+
5
+ ToR + CV in → **verified bid pack out** — customized cover letter, tailored CV,
6
+ technical response, financial proposal, submission checklist — as PDFs, in
7
+ minutes, for UN / NGO / government consultants.
8
+
9
+ It does **not** write your evidence for you. Your track record wins bids; this
10
+ tool guarantees your bid is complete, compliant, honestly priced, verified
11
+ against the client's scoring grid, and audited **on the final rendered bytes**
12
+ before you submit.
13
+
14
+ ## Why it exists
15
+
16
+ Every factual claim in any output must trace to a page in the source ToR, a
17
+ user-supplied input, or a cited external source — otherwise it stays `[FILL]`.
18
+ The tailored CV is proven line-by-line against your master CV. Pricing runs
19
+ only on numbers you confirm. The app never fabricates rates, experience,
20
+ dates, names or client facts. In a domain where one invented fact means
21
+ disqualification (and vendor blacklisting), that is the feature, not a
22
+ limitation.
23
+
24
+ ## Install — one command, once
25
+
26
+ ```bash
27
+ npx tor-to-proposal init
28
+ ```
29
+
30
+ That sets up your consultant profile (name, credentials, rates, master CV) at
31
+ `~/.tor-to-proposal/profile.json`. Then use it with any AI assistant (Claude
32
+ Desktop / Claude Code / Codex / ChatGPT) via the MCP server at
33
+ `mcp/server.mjs`, or install the agent skill:
34
+
35
+ ```bash
36
+ npx skills add ahaomar/tor-to-proposal # agent users
37
+ ```
38
+
39
+ Local-first: no network calls carry your documents. The only optional outbound
40
+ step is market-rate research you run yourself.
41
+
42
+ ## The workflow — ToR + CV in, bid pack out
43
+
44
+ ```bash
45
+ tor-to-proposal bid-pack start --tor tor.pdf --cv cv.txt --dir mybid/ # screen + gaps + draft CV
46
+ tor-to-proposal bid-pack apply --answers answers.json --dir mybid/ # your answers -> pricing + financials
47
+ tor-to-proposal bid-pack pack --dir mybid/ # verify + checklist + zip
48
+ ```
49
+
50
+ With an assistant, you never run these — you say *"run a bid pack on this
51
+ folder"* and answer **one consolidated questionnaire**.
52
+
53
+ | Phase | What happens | You decide |
54
+ | --- | --- | --- |
55
+ | 1. Intake | ToR structured: deadline, effort, evaluation grid, compliance matrix, bid screen. CV gap analysis (MATCH / **GAP — do not claim**). Tailored CV drafted from your master CV. | Go / no-go |
56
+ | 2. The one questionnaire | Every open item — pricing, availability, validity, missing evidence — in a single numbered list. `apply` turns answers into pricing + the financial proposal. | All of it |
57
+ | 3. Drafts | Cover letter (lint-gated), technical response per scored criterion (coverage-simulated), tailored CV (anchor-verified), financial proposal (formulas visible). | Review |
58
+ | 4. Pack | Every rendered PDF/docx/xlsx is **re-extracted and re-audited**; pack/ + submission-checklist.md + deadline.ics + zip. Stale packs are removed on failure. | Submit |
59
+
60
+ The agent-facing master workflow lives in [SKILL.md](SKILL.md). Every stage is
61
+ also directly runnable: `tor-to-proposal <command> --help` (17 commands).
62
+
63
+ ## Guides
64
+
65
+ | Guide | For |
66
+ | --- | --- |
67
+ | [docs/USER-GUIDE.md](docs/USER-GUIDE.md) | Non-technical consultants — plain-language, step-by-step, what you'll see at every step |
68
+ | [docs/PROMPT-GUIDE.md](docs/PROMPT-GUIDE.md) | Agent users — setup for Claude/ChatGPT/Codex/any agent + a copy-paste prompt library for each phase |
69
+ | [docs/TECHNICAL-GUIDE.md](docs/TECHNICAL-GUIDE.md) | Developers / IT — architecture, CLI reference, data contracts, MCP/plugin internals, extension points |
70
+
71
+ ## Trust
72
+
73
+ - Local-first. Nothing about your bid leaves your machine.
74
+ - Honesty gates are mechanical, not aspirational: cover-letter lint,
75
+ CV anchor lint, pricing gate, simulator, template-filler (client's formulas
76
+ never touched), and a final audit that runs on the rendered bytes.
77
+ - `pricing.md` / `pricing.json` contain your floor math — the pack builder
78
+ never includes them in what you submit.
79
+
80
+ ## Development
81
+
82
+ ```bash
83
+ npm install
84
+ npm test # 39 tests: extract, pricing, rates, cv-gap, cover-letter lint,
85
+ # simulator, template-filler (docx+xlsx), audit, profile,
86
+ # cv-tailor build/lint, financial proposal (md+xlsx), render
87
+ # round-trip, bid-pack start/apply/pack, MCP handshake
88
+ ```
89
+
90
+ Node ≥ 18. Dependencies (all local, all pure JS): `unpdf`, `mammoth`,
91
+ `exceljs`, `adm-zip`.
92
+
93
+ ## License
94
+
95
+ MIT — see [LICENSE](LICENSE). Design spec: [tor-to-proposal-build-spec.md](tor-to-proposal-build-spec.md).
package/SKILL.md ADDED
@@ -0,0 +1,189 @@
1
+ ---
2
+ name: tor-to-proposal
3
+ description: >
4
+ Turn UN / NGO / government Terms of Reference (ToR), solicitations, RFPs and
5
+ job descriptions into a complete, verified bid pack — customized cover letter,
6
+ tailored CV, technical response, financial proposal, submission checklist —
7
+ as PDF/docx/xlsx in minutes. MUST BE USED when the user mentions a ToR,
8
+ tender, solicitation, RFP, RFQ, IC/SSA consultancy, UNGM bid,
9
+ UNDP/UNOPS/UNICEF/World Bank/USAID assignment, wants a go/no-go bid decision,
10
+ a compliance matrix, a cover letter for a consultancy, CV tailoring against a
11
+ ToR, consultancy pricing/day rates, evaluation-criteria scoring of a draft,
12
+ or a pre-submission audit of a proposal. Covers: bid screen, consolidated
13
+ questionnaire, CV gap analysis + tailoring, cover letter lint, market rate
14
+ benchmarking, pricing model, financial proposal, client dossier, evaluation
15
+ simulator, filling the client's own docx/xlsx forms, PDF packaging with a
16
+ final honesty audit.
17
+ ---
18
+
19
+ # tor-to-proposal — the bid tool that refuses to lie
20
+
21
+ You (the agent) draft. The tools verify. Nothing ships that a tool has not
22
+ gated. Every claim traces to the ToR (p.X), a user input, or a cited source —
23
+ otherwise it stays `[FILL]`.
24
+
25
+ ## The product: ToR + CV in → bid pack out
26
+
27
+ The default experience is the **bid pack**. One ToR, one CV, one consolidated
28
+ questionnaire, one verified, zipped submission folder:
29
+
30
+ ```
31
+ <bid-dir>/pack/ Cover-Letter.pdf · CV.pdf · Technical-Proposal.pdf ·
32
+ Financial-Proposal.pdf|xlsx · submission-checklist.md ·
33
+ deadline.ics · audit passed on the final bytes
34
+ ```
35
+
36
+ ## Bootstrap (once per machine)
37
+
38
+ ```bash
39
+ npx tor-to-proposal init # preferred: profile wizard (name, rates, master CV)
40
+ # — or, from a clone:
41
+ cd <this-skill-directory> && npm install
42
+ ```
43
+
44
+ All processing is local. No network calls are made with the user's documents.
45
+ The only optional outbound step is market-rate research the user approves.
46
+
47
+ ## Non-negotiable rules (enforced by the tools, restated for you)
48
+
49
+ 1. **CITATION** — every factual claim traces to `[[PAGE n]]` / `(p.X)` from the
50
+ ToR, a user input, or a cited source (URL + access date). Otherwise `[FILL]`.
51
+ 2. **NO INVENTION** — never fabricate rates, experience, dates, names, client
52
+ facts. Tools compute; the user decides. The tailored CV may only reorder and
53
+ rephrase the master CV — `cv-tailor lint` proves it line by line.
54
+ 3. **CLIENT TEMPLATE WINS** — outputs restructure into the client's own forms
55
+ via `template-filler`, never the reverse.
56
+ 4. **HONESTY (pricing)** — numbers only from the user's confirmed inputs
57
+ (their profile answer, or this bid's answers.json). Never gut-feel.
58
+ 5. **NO FALSE PRECISION** — simulator output is coverage arithmetic, never a
59
+ predicted evaluator score.
60
+
61
+ If a step would force you to violate a rule, stop and tell the user what input
62
+ is missing.
63
+
64
+ ## The bid pack conversation (default workflow)
65
+
66
+ Paths assume `--dir <bid-dir>` (default `bid/`); artifacts live in
67
+ `<bid-dir>/out/`. Read the ToR first; never quote it from memory.
68
+
69
+ ### Phase 1 — Intake (all mechanical steps, one command)
70
+
71
+ ```bash
72
+ node bin/bid-pack.mjs start --tor <tor.(pdf|docx|txt)> --cv <cv.(pdf|docx|txt)> --dir <bid-dir>
73
+ ```
74
+
75
+ Runs pdf-extract → extract (bid screen, compliance matrix) → cv-gap →
76
+ cv-tailor build, then writes **`out/questions.md`**. Exit 2 on a scanned PDF →
77
+ OCR first; never improvise page numbers.
78
+
79
+ **Present the bid screen to the user** (deadline, effort, scoring, red flags).
80
+ Go/no-go is THEIR call. If no-go: stop and say so.
81
+
82
+ ### Phase 2 — The ONE questionnaire (no scattered questions, ever)
83
+
84
+ `out/questions.md` consolidates every open decision: pricing, availability,
85
+ validity, draft `[FILL]`s, CV evidence. **Put all of them to the user in one
86
+ message**, grouped. Never drip-feed questions and never answer one for the
87
+ user. Then:
88
+
89
+ ```bash
90
+ # write the answers verbatim:
91
+ node bin/bid-pack.mjs apply --answers answers.json --dir <bid-dir>
92
+ ```
93
+
94
+ `apply` runs pricing on the user's confirmed numbers, renders the financial
95
+ proposal, records CV evidence + fill answers, and regenerates the open list.
96
+ Repeat until `0 still open`.
97
+
98
+ ### Phase 3 — Draft all four documents (you draft, tools gate)
99
+
100
+ Write drafts to `<bid-dir>/` as `cover-letter.md`, `technical-proposal.md`
101
+ (`cv-tailored.md` already exists from Phase 1; edit it only with master-CV
102
+ material or user evidence answers).
103
+
104
+ - **Cover letter** — structure in `assets/templates.md` (≤1 page: reference
105
+ block → understanding from Background/Objectives → fit mapped to JD
106
+ competencies → compliance declarations → availability). Then loop:
107
+ `node bin/cover-letter.mjs lint --draft <bid-dir>/cover-letter.md --cv <cv.txt> --gap <bid-dir>/out/cv-gap-report.md` until PASS.
108
+ - **Technical response** — one section per scored criterion, highest weight
109
+ first, in the client's vocabulary. Extract sub-elements verbatim into
110
+ `sub-elements.json`, then loop:
111
+ `node bin/simulator.mjs --extract <bid-dir>/out/tor-extract.json --response <bid-dir>/technical-proposal.md --sub-elements sub-elements.json` until at-risk ≈ 0.
112
+ Present it as coverage arithmetic — never as a predicted score.
113
+ - **Tailored CV** — verify:
114
+ `node bin/cv-tailor.mjs lint --cv <bid-dir>/out/cv-tailored.md --master <master-cv> --gap <bid-dir>/out/cv-gap-report.md --evidence <bid-dir>/out/cv-evidence.json`
115
+ GAP rows are never claimed; unanchored lines fail until the user supplies
116
+ evidence via the questionnaire.
117
+ - **Financial proposal** — already rendered by `apply` from pricing.json
118
+ (md + xlsx, formulas visible). If the client supplied their own form:
119
+ `template-filler.mjs map/fill/check` restructures INTO it.
120
+
121
+ ### Phase 4 — Render, package, deliver
122
+
123
+ Render each final document to PDF with your own document tooling (typography:
124
+ `references/render-specs.md`). If no document tooling is available, add
125
+ `--fallback-pdf` to the command below and the built-in renderer will produce
126
+ plain, valid PDFs. Then:
127
+
128
+ ```bash
129
+ node bin/bid-pack.mjs pack --dir <bid-dir>
130
+ ```
131
+
132
+ `pack` re-extracts the text from every rendered PDF/docx/xlsx and **re-runs the
133
+ full audit on those bytes** — the gate applies to what the client will read.
134
+ On pass it builds `pack/`, `submission-checklist.md`, `deadline.ics` and the
135
+ zip. On fail it removes any stale pack so nothing half-finished can be
136
+ submitted. Deliver the pack folder + checklist to the user.
137
+
138
+ ### After submission (opt-in, builds the moat)
139
+
140
+ Record the bid under `library/bids/<ref-no>/` per `library/SCHEMA.md`
141
+ (outcome + client feedback verbatim + rate band). This corpus is what makes
142
+ rate bands real data instead of guesses.
143
+
144
+ ## Reference files — read on demand
145
+
146
+ - `references/render-specs.md` — typography/structure for the four final PDFs.
147
+ - `references/review-rules.md` — before showing the user any draft (checklist +
148
+ banned superlatives).
149
+ - `references/pricing-standards.md` — before discussing rates (conventions,
150
+ loading/contingency norms).
151
+ - `references/packs/<donor>.md` — when the client is UNDP, World Bank, EU-PRAG
152
+ or USAID: where their rules live, what to verify in the ToR.
153
+ - `assets/templates.md` — letter, technical response, compliance rows, CV
154
+ bullet upgrade, clarification questions.
155
+
156
+ ## Advanced — step-by-step mode
157
+
158
+ Every stage is still directly runnable (and exposed via MCP):
159
+
160
+ ```bash
161
+ node bin/pdf-extract.mjs tor.pdf --out tor.txt # 0. page-tagged text
162
+ node bin/extract.mjs --tor tor.txt --out-dir out # 1. screen + matrix
163
+ node bin/dossier.mjs --client "UNOPS" --sector "M&E" # 2. client intel
164
+ node bin/cv-gap.mjs --tor tor.txt --cv cv.txt # 3. MATCH / GAP
165
+ node bin/market-rates.mjs scaffold --title "<t + ref>" # 4a. research scaffold
166
+ node bin/market-rates.mjs position --your-rate 450 ... # 4b. band position
167
+ node bin/pricing-model.mjs --base 400 --basis day ... # 5. ⛔ gated on user
168
+ node bin/cover-letter.mjs lint --draft letter.md --cv cv.txt # 6. gate
169
+ node bin/simulator.mjs --extract ... --response ... # 7. coverage gate
170
+ node bin/template-filler.mjs map|fill|check # 8. client's forms
171
+ node bin/audit.mjs --proposal final.md --matrix out/compliance-matrix.md # 9. gate
172
+ node bin/cv-tailor.mjs build | lint # CV generate/verify
173
+ node bin/financial-proposal.mjs --pricing ... --extract ... # financial doc
174
+ node bin/render.mjs input.md --out output.pdf # fallback PDFs
175
+ node bin/package.mjs --dir <bid-dir> [--fallback-pdf] # verify + zip
176
+ node bin/profile.mjs init | set | get | path | erase # consultant profile
177
+ ```
178
+
179
+ `node bin/tor-to-proposal.mjs <command> --help` documents every one.
180
+
181
+ ## What you must never do
182
+
183
+ - Quote a ToR requirement without its page.
184
+ - Fill a `[FILL]` with anything the user did not supply or approve.
185
+ - Mention a GAP skill anywhere in the proposal or tailored CV.
186
+ - Ask the user ten small questions when one consolidated questionnaire exists.
187
+ - Let an unaudited draft leave the session marked "ready to submit".
188
+ - Present simulator output as a predicted evaluation score.
189
+ - Build the pack while any verification fails — `pack` won't let you either.
@@ -0,0 +1,74 @@
1
+ # Structure templates
2
+
3
+ Formats the agent drafts into. `[FILL]` marks where user evidence is required;
4
+ nothing else may occupy a `[FILL]` slot.
5
+
6
+ ## Cover letter (≤1 page — lint-enforced)
7
+
8
+ ```
9
+ Dear [contact name/committee, p.X],
10
+
11
+ RE: [exact assignment title] — Reference: [exact ref no.] (p.X)
12
+ Deadline acknowledged: [submission deadline] (p.X)
13
+
14
+ [UNDERSTANDING — 2 sentences, facts only from Background/Objectives, each tagged (p.X).]
15
+
16
+ [FIT — one block per core competency from the JD:]
17
+ Requirement (p.X): “[verbatim requirement]”
18
+ [FILL: your matching evidence — quantified, traceable to CV §]
19
+
20
+ [COMPLIANCE — validity per ToR (p.X); acceptance of GCCs / no-deviation clause (p.X);
21
+ registration status if required (p.X).]
22
+
23
+ [AVAILABILITY per start date (p.X) + contact block.]
24
+
25
+ Sincerely,
26
+ [Name exactly as in CV + credentials]
27
+ ```
28
+
29
+ ## Technical response — one section per scored criterion
30
+
31
+ ```
32
+ ## [n]. [Criterion label, verbatim from the ToR evaluation grid] (p.X, [weight] pts)
33
+
34
+ [Direct answer to every sub-element of this criterion, in the client's vocabulary.
35
+ Each factual statement about the assignment carries (p.X); each statement about
36
+ your experience carries [FILL] or CV-traceable evidence.]
37
+
38
+ [Where the methodology is asked for: approach → steps → how each ToR
39
+ deliverable (p.X) is produced → quality control → sustainability.]
40
+ ```
41
+
42
+ Ordering rule: highest weight first (unless the client's template fixes its own
43
+ order — then their order wins and template-filler maps sections into it).
44
+
45
+ ## Compliance matrix row (generated by extract.mjs; complete each before audit)
46
+
47
+ ```
48
+ | Requirement (verbatim, p.X) | p.X | Our response (2–3 lines / [FILL]) | Owner | Complete |
49
+ ```
50
+
51
+ ## CV bullet upgrade pattern (cv-gap suggestions follow this shape)
52
+
53
+ ```
54
+ Before: “Responsible for M&E activities in the region.”
55
+ After: “[FILL: n] years leading M&E for [client names], covering [FILL: n]
56
+ evaluations in [FILL: countries]; produced [FILL: deliverable] used by
57
+ [FILL: client] for [FILL: decision].” (numbers user-supplied only)
58
+ ```
59
+
60
+ Terminology mirroring: if the ToR says "results framework", the CV bullet says
61
+ "results framework" — not "logframe", not "M&E plan".
62
+
63
+ ## Clarification questions (send before the cutoff date, p.X)
64
+
65
+ ```
66
+ Subject: Request for clarification — [ref no.] — [title]
67
+
68
+ Q1. [Ambiguity in the ToR, quoted (p.X)] — please confirm whether [interpretation A or B].
69
+ Q2. [Gap between deliverables (p.X) and effort estimate (p.X)] — please confirm the expected effort split.
70
+ Q3. [Financial form field with no stated basis (p.X)] — please confirm whether [X] is included.
71
+ ```
72
+
73
+ Every question must quote the ToR line it refers to. Never ask what the ToR
74
+ already answers — that signals you did not read it.
package/bin/audit.mjs ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+ // audit: the pre-submission hard-fail gate. [FILL] leftovers, incomplete compliance
3
+ // matrix, claim-shaped sentences with no trace. Exit 1 = do not submit yet.
4
+ import { basename as path0 } from 'node:path';
5
+ import { parseArgs, readText, writeOut, helpText } from './lib.mjs';
6
+
7
+ const HELP = helpText('audit', [
8
+ 'Usage: node bin/audit.mjs --proposal final.md --matrix compliance-matrix.md',
9
+ ' [--extras file.md ...] [--report audit-report.md]',
10
+ '',
11
+ 'Hard fails: [FILL] remaining; matrix rows not Complete.',
12
+ 'Review list: claim-shaped sentences (achievement verb + number) without',
13
+ '[[PAGE n]] / p.X / [FILL] / user-input / http trace.',
14
+ ]);
15
+
16
+ const args = parseArgs(process.argv.slice(2));
17
+ if (args.__help || !args.proposal) {
18
+ process.stdout.write(HELP);
19
+ process.exit(args.__help ? 0 : 1);
20
+ }
21
+ const files = [args.proposal, ...(args.extras ? [].concat(args.extras) : [])];
22
+ const hard = [];
23
+ const review = [];
24
+
25
+ for (const file of files) {
26
+ const text = readText(file, 'proposal file');
27
+ const fills = [...text.matchAll(/\[FILL[^\]]*\]/g)];
28
+ if (fills.length) hard.push(`${path0(file)}: ${fills.length} [FILL] placeholder(s) remain — resolve every one before submission`);
29
+
30
+ const sentences = text
31
+ .split('\n')
32
+ .filter((l) => !l.trim().startsWith('#') && !l.trim().startsWith('|'))
33
+ .join(' ')
34
+ .split(/(?<=[.!?])\s+/);
35
+ sentences.forEach((s) => {
36
+ const clean = s.replace(/\s+/g, ' ').trim();
37
+ if (clean.length < 20 || clean.startsWith('|') || clean.startsWith('#')) return;
38
+ const claimish = /\b(led|delivered|managed|implemented|achieved|reduced|increased|trained|designed|coordinated|produced|authored|advised)\b/i.test(clean) && /\d|%/.test(clean);
39
+ if (!claimish) return;
40
+ if (/\[\[PAGE \d+\]\]|\bp\.\s?\d+\b|https?:\/\/|\[FILL|user[- ]input|\(user/i.test(clean)) return;
41
+ review.push(`${path0(file)}: “${clean.slice(0, 140)}${clean.length > 140 ? '…' : ''}” — claim-shaped, no trace (add [[PAGE n]], (p.X), source URL, or mark as user-input fact)`);
42
+ });
43
+ }
44
+
45
+ if (args.matrix) {
46
+ const matrix = readText(args.matrix, '--matrix');
47
+ const rows = matrix.split('\n').filter((l) => l.trim().startsWith('|') && !/^\|\s*[-:\s|]+\|/.test(l) && !/requirement/i.test(l.split('|')[1] ?? ''));
48
+ let incomplete = 0;
49
+ for (const r of rows) {
50
+ const cells = r.split('|').map((c) => c.trim());
51
+ const status = cells[cells.length - 2]; // last cell before trailing empty from split
52
+ if (status && !/^complete$/i.test(status) && cells.length > 3) incomplete++;
53
+ }
54
+ if (incomplete) hard.push(`${path0(args.matrix)}: ${incomplete} row(s) not marked Complete`);
55
+ }
56
+
57
+ const report = `# Audit report\n\n${hard.length ? `## HARD FAILS — do not submit\n${hard.map((h) => '- ' + h).join('\n')}` : '## HARD FAILS — none'}\n\n## Review list (citation-rule lint, human judgement required)\n${review.length ? review.map((r) => '- ' + r).join('\n') : 'none — no untraceable claim-shaped sentences found'}\n\nGate: fix every hard fail. Work the review list: each flagged sentence needs a page tag, a source, or removal.\n`;
58
+ writeOut(args.report || 'audit-report.md', report);
59
+ process.stdout.write(report);
60
+ if (hard.length) process.exit(1);