Bid Command Reference¶
Command-line tool for bid proposal management.
Installation¶
After setup, run commands using:
Run bid setup to configure shell aliases.
| Global option | Description |
|---|---|
-h, --help |
Show help |
-v, --version |
Show version |
Commands¶
init¶
Create project folder structure in @rfqs.
Creates the following structure under @rfqs/<year>/<folder_name>/:
<folder_name>/
├── 00-ITB/<date>/
├── 01-Commercial/00-Arc/
├── 02-Technical/00-Arc/
├── 03-Supplier/00-Arc/
├── 04-Datasheet/00-Arc/
├── 05-Drawing/00-Arc/
├── 06-PO/00-Arc/
├── 07-VO/
└── 08-Toolkit/00-Arc/
Prompts to optionally create a Commercial Proposal Excel file from template.
clean¶
Normalize file names in a folder.
| Option | Description |
|---|---|
-d, --dry-run |
Preview changes without applying |
-y, --yes |
Skip confirmation prompts |
-r, --remove-git |
Remove .git folder and .gitignore |
What it cleans:
- Email prefixes (RE:, FW:, FWD:, EXTERNAL, URGENT)
- Uppercase extensions → lowercase
- Multiple dashes/underscores → single
- Brackets and extra whitespace
combine-pdf / cpdf¶
Merge PDFs in current directory.
| Option | Description |
|---|---|
-o, --outline |
Add bookmarks from filenames |
-t, --toc |
Add table of contents page |
-m, --manifest |
Use manifest file for custom order |
-c, --create-manifest |
Create manifest.md from all PDFs (recursive) |
-y, --yes |
Skip confirmation prompts |
Default output: 00-Combined.pdf
Creating a Manifest¶
Use -c to auto-generate a manifest.md draft from all PDFs in the current
directory (recursive). Edit it, then run with -m to combine.
bid cpdf -c # generates manifest.md
# edit manifest.md as needed
bid cpdf -m # combine using the manifest
00-Combined.pdf is excluded automatically. Files are listed as relative paths,
sorted alphabetically.
Manifest File Format¶
Create or edit a .md or .txt file in the directory:
# Title→ Output filename## Section→ Bookmark heading- file.pdf→ Files to include (searches locally, then@docs)
vo¶
Create a Variation Order folder structure under an existing project.
Searches for the project in @rfqs, then creates a numbered VO subfolder
inside 07-VO/ with the following structure:
07-VO/<nn>-VO <name>/
├── 00-ITB/<date>/
├── 01-Commercial/00-Arc/
├── 02-Technical/00-Arc/
├── 03-Supplier/00-Arc/
├── 04-Datasheet/00-Arc/
└── 05-PO/00-Arc/
VO folders are numbered automatically (01-VO, 02-VO, …). Prompts to optionally create a Commercial Proposal Excel file from template.
ho¶
Handover project or VO folders from @rfqs to @handover.
Performs a one-way mirror sync. Searches for the project in @rfqs, lets you
select the main project or a specific VO, then syncs to @handover/<project>/.
Folder mapping:
| Source | Destination |
|---|---|
00-ITB |
00-ITB |
06-PO or 05-PO |
01-PO |
02-Technical |
02-Technical |
03-Supplier |
03-Supplier |
04-Datasheet |
04-Datasheet |
05-Drawing |
06-Drawing |
| — | 05-Cost (created empty) |
The PO folder is read from whichever name actually exists in the source
folder — 06-PO if present, otherwise 05-PO — rather than assuming VO
folders always use 05-PO. This matters because a VO folder is sometimes
set up with the full project structure (bid vo normally omits
05-Drawing, so a lightweight VO's PO folder shifts up to 05-PO, but a
VO folder created with the full structure keeps 06-PO like a main
project).
05-Cost must be populated manually after handover.
co¶
Create a costing folder in @costing for a project.
Searches for the project in @rfqs, lets you select the main project or a
specific VO, then creates the destination folder in @costing/<project>/.
Files must be copied manually.
audit¶
Track file contributions in a folder.
| Option | Description |
|---|---|
-f, --fetch URL |
Fetch from SharePoint URL (recommended) |
-l, --library |
SharePoint library title (default: Documents) |
--folder |
Folder path within library (e.g., @docs) |
--month |
Filter by month: YYYY-MM or range YYYY-MM:YYYY-MM |
--all-time |
Show all data without time filtering |
--person NAME |
Filter to specific person (or all for everyone) |
-i, --import-file |
Import from SharePoint exported CSV/Excel |
-o, --output |
Export report to CSV file |
--debug |
Show browser window (for troubleshooting) |
Default: Audits local @docs folder (no author info). When fetching from
SharePoint, defaults to current month and shows interactive contributor selection.
Modes¶
- Local filesystem (default) — basic stats without author info
- SharePoint fetch (
-f) — fetch via browser automation (recommended) - SharePoint import (
-i) — import from exported CSV/Excel
Examples¶
# Local folder audit (no author info)
bid audit
bid audit /path/to/folder
# Fetch from SharePoint (recommended - includes author info)
bid audit -f https://company.sharepoint.com/sites/Site --folder "@docs"
# All contributors, current month
bid audit -f URL --folder "@docs" --person all
# Specific month
bid audit -f URL --folder "@docs" --month 2025-12
# Month range (January to June 2025)
bid audit -f URL --folder "@docs" --month 2025-01:2025-06
# All time (no month filter)
bid audit -f URL --folder "@docs" --all-time
# Specific person, all time
bid audit -f URL --folder "@docs" --person "John Doe" --all-time
# Import from exported file
bid audit -i sharepoint.csv
bid audit -i export.xlsx -o report.csv
Setup for SharePoint Fetch¶
Requires Playwright (one-time setup):
First run: Browser window opens for Microsoft login. Session is saved for future runs.
Subsequent runs: Runs headless (no browser window) using saved session.
beautify¶
Apply formatting to Excel files.
| Option | Description |
|---|---|
-w, --width |
Apply smart width only (skip font formatting) |
-f, --font-only |
Apply font/size only (skip smart width) |
Default shows the font template menu first (select 0 to keep the current font/size), resets sheet view to Normal, then applies smart width. Sheet view reset always runs, regardless of flags.
haz¶
Explain a hazardous area equipment marking (ATEX/IECEx/Inmetro/NEC-CEC).
| Option | Description |
|---|---|
-r, --reference |
Also open a plain-English reference article in the browser |
Paste the marking as-is, in any order (e.g. Ex ib IIC T4 Gb) — it's
tokenized and re-ordered into canonical form regardless of input order.
Prompts for the marking if omitted, unless -r is used with no marking
argument, in which case it just opens the reference article and exits.
Certification scheme is inferred from certificate numbers, ATEX category
prefixes, or NEC/CEC markers found in the input. The bare Ex ... string
is identical across ATEX/IECEx/Inmetro, so if none of those markers are
present, all three are reported as possible schemes.
Every run also prints a reference link (Electrical equipment in hazardous areas) as a footer.
bid haz "Ex ib IIC T4 Gb"
bid haz "[EEx ia] IIC" # bracketed = associated apparatus rating
bid haz -r "Ex ib IIC T4 Gb" # also opens the reference article
bid haz -r # just opens the reference article
word2pdf¶
Batch convert Word documents to PDF in the current directory.
| Option | Description |
|---|---|
-y, --yes |
Skip confirmation prompts |
Converts all .docx files. Requires Microsoft Word to be installed.
rate¶
Calculate OT, Sun/PH, and Standby rates from a man-day rate.
bid rate --onshore RATE
bid rate --offshore RATE
bid rate --onshore RATE --offshore RATE
bid rate --specialist TIER
| Option | Description |
|---|---|
--onshore RATE |
Onshore day rate (Mon-Sat, 10 hrs/day) |
--offshore RATE |
Offshore day rate (Mon-Sun, 12 hrs/day) |
--specialist TIER |
Specialist tier rate card — see tiers below |
--name NAME |
Specialist system designation for --md row (e.g. "PAGA Specialist", "POB Specialist"); defaults to "<Tier> Specialist" when omitted |
--tiers |
Show specialist tier definitions in SGD/USD/EUR/GBP |
--special |
MODEC/HOS rate structure, ×4/3 OT (default: ×3/2) |
--md |
Write selling rates to man-day-rates.md in current directory |
--currency SGD\|USD |
Currency for --md output. If omitted: SGD on the first write to a section, or whatever currency is already stored for that section on later calls (e.g. a --specialist ... --md call that adds a row doesn't need to repeat --currency USD). For --specialist, the Day Rate/OT/Standby/Sun-PH-hr figures are genuinely converted (SGD is the tier's native currency; USD uses the same ceiling-to-100 conversion shown in the terminal's SGD/USD columns) — not just relabeled. JEN Engineer figures are whatever you typed via --onshore/--offshore, with no separate SGD source to convert from, so make sure that input already matches the --currency you declare. |
--valid YEARS\|DATE |
Validity period: years from publish date (e.g. 2) or fixed date (YYYY-MM-DD). Years snap to the last day of the publish month. If omitted, inherits whatever validity (and caveat) is already stored in the header from an earlier --md call, rather than clearing it. |
--caveat |
Add clause that rates are subject to review if commencement is delayed past the validity date (requires --valid) |
--date YYYY-MM-DD |
Publish date written to the document header. If omitted: today on the first write, or the already-stored date on later calls (so building a document across several days doesn't drift the displayed date). |
--hotel-note / --no-hotel-note |
Include/exclude the excess-day hotel & allowance footnote in the Onshore section (default: include). Only takes effect when the section is first created. |
Specialist tiers¶
| Tier | Supplier examples | Onshore sell | Offshore sell | Offshore multiplier |
|---|---|---|---|---|
standard |
TMS-DK, Rutter | USD 2,500 | USD 3,300 | ×1.3 (suppliers charge flat) |
premium |
S3 ID | USD 3,200 | USD 4,400 | ×1.35 (matches S3 ID ratio) |
super |
Kongsberg Maritime | USD 3,300 | USD 5,000 | ×1.5 (KM charges ×1.86) |
Output shows SGD and USD side by side. --specialist TIER includes day rate, OT, standby, and mob/demob. Combine with --onshore/--offshore to override the config selling price while keeping the tier's mob rates. Use --tiers to see supplier cost thresholds across SGD/USD/EUR/GBP.
Onshore Sun/PH rates (Seatrium structure):
| Line | Value | Rationale |
|---|---|---|
| Sun/PH Day Rate | Day rate × 1.5 | Full day at OT rate (10 hrs × OT/hr) |
| Sun/PH hr | = OT/hr | Sun/PH work is already OT — no additional multiplier |
Sun/PH applies to onshore only. Offshore rates are flat (Mon–Sun, no Sun/PH distinction).
Markdown export (--md)¶
Writes a customer-facing rate card to man-day-rates.md in the current directory — selling prices only. Run bid rate and bid mob in sequence to build up the document, then convert to PDF with docr.
The document has independent named sections: a header (title, validity, tax clause), an Onshore section, and an Offshore section. Re-running any command updates only its own section/row; all other content is preserved.
Each Onshore/Offshore section holds two separate sub-tables, not one combined table:
| Sub-table | Columns | Keyed by | Written by |
|---|---|---|---|
### Rates |
Designation, Day Rate, OT/hr, Standby, Sun/PH hr (onshore only) | Designation only | bid rate --md |
### Mobilisation / Demobilisation |
Location, Designation, Mob, Demob, Mob/Demob | Location + Designation | bid mob --md |
They're split because Day Rate/OT/Standby/Sun-PH-hr never actually vary by location — only Mob/Demob does. A single combined table forces either the same rate repeated on every location row, or manually comma-joining locations by hand (SG, CN, ID) to avoid that — which is what real proposals ended up doing before this split. It also visibly wraps/cramps on A4 in the docr PDF pipeline once there are more than a couple of locations. bid rate --md never touches the Mob/Demob sub-table; bid mob --md never touches the Rates sub-table.
Document-level notes (auto-generated):
- All prices quoted are exclusive of applicable taxes. — always present
- Date: [publish date] — always present (defaults to today; override with
--date) - Rates are valid until [date]. — added when
--validis set; years snap to last day of publish month - Rates are subject to review should project commencement be delayed beyond [date]. — added when
--caveatis also set - Onshore footer: Mob/demob rates are based on a minimum 10-working-day deployment per engineer. Hotel and daily allowance of [CCY] [amount] per day applies for each day exceeding 10 working days. — the amount is genuinely converted (SGD 250 native; USD is
250 / usd_rate, ceiled to the nearest 100 — e.g. USD 200 at the default 1.35 rate), not just relabeled. Omit with--no-hotel-note.
Typical workflow:
# 1. Create Onshore + Offshore Rates tables (JEN Engineer row, validity, caveat).
# Also seeds an "SG | JEN Engineer | — | — | —" row in the Mob/Demob table,
# since JEN engineers are always ex-Singapore (home base = no mob cost).
bid rate --onshore 1100 --offshore 1650 --md --currency USD --valid 2028-12-31 --caveat
# 2. Add destination Mob/Demob rows
bid mob NL --md # adds an NL row to both Onshore and Offshore Mob tables
bid mob DE --md # adds a DE row to both
# 3. Add specialist system rows — no need to repeat --currency or --valid,
# both are inherited from the header/section (see below)
bid rate --specialist premium --name "PAGA Specialist" --md
bid rate --specialist super --name "POB Specialist" --md
--currency, --valid, and --date are only needed on the call that first sets them (or when you actively want to change them) — every later bid rate --md / bid mob --md call that omits them inherits whatever's already stored in the file rather than resetting it. Omitting --valid on a later bid rate --specialist --md call, for example, does not erase the validity line the first call set. Use bid mob --md-currency to override the currency for one specific mob run without touching what's stored.
Rate rows are keyed by Designation alone (JEN Engineer, PAGA Specialist, POB Specialist, etc. each get one row, once, regardless of location). Mob/Demob rows are keyed by Location + Designation — see Specialist --md below for how specialists group by haul tier while still displaying actual country codes.
Examples¶
# Standard Seatrium rates (×3/2 OT)
bid rate --onshore 1100
bid rate --offshore 1650
bid rate --onshore 1100 --offshore 1650
# MODEC/HOS special rates (×4/3 OT)
bid rate --onshore 875 --special
bid rate --offshore 1700 --special
# Show supplier day rate thresholds (SGD/USD/EUR/GBP)
bid rate --tiers
# Specialist tier rate cards (SGD + USD side by side)
bid rate --specialist standard
bid rate --specialist premium
bid rate --specialist super
# Custom selling price with tier mob rates
bid rate --onshore 3500 --specialist premium
# Specialist system row in man-day-rates.md (custom designation)
bid rate --specialist premium --name "PAGA Specialist" --md
bid rate --specialist super --name "POB Specialist" --md
mob¶
Estimate mob/demob costs for JEN engineer deployment (SGD, from Singapore) or third-party specialist deployment (USD, tier-based selling price).
bid mob COUNTRY [options] # JEN engineer
bid mob --batam [options] # JEN engineer via Batam ferry
bid mob --specialist TIER [COUNTRY] # specialist tier pricing
bid mob --day-rate RATE --currency CCY [COUNTRY] # specialist auto-classify
COUNTRY accepts a comma-separated list (no spaces) to run several countries in one call —
each runs the full breakdown/output/--md write, same as if the command were run once per
code:
JEN engineer options¶
| Option | Description |
|---|---|
--offshore |
Offshore deployment — anchorage or sea trial (standard 10-day rate) |
--days DAYS |
Override mob period in working days (default: 10; ignored with --offshore) |
--batam |
Batam deployment via ferry (no country code needed) |
--buffer AMOUNT LABEL |
Add a manual cost item; repeatable |
--md |
Write mob/demob selling price to man-day-rates.md in current directory |
--md-currency SGD\|USD |
Override currency for --md output (default: reads from man-day-rates.md) |
Supported countries: AU, BR, CN, DE, GY, ID, KR, MX, NA, NL, SN, SG, TW, US
ID (Indonesia) is a same-day flight deployment from SG (visa-free, no overnight) —
distinct from --batam, which uses the ferry crossing instead of a flight.
JEN engineer cost components:
| Component | Notes |
|---|---|
| Airfare (RT) | Flexible return estimate, +20% buffer |
| Date Change Fee | SGD 250; assumed return date changes (not applicable for Batam or zero-fare) |
| Work Visa | Country-specific; warning shown when visa coordination required |
| Transport | SG taxi to/from Changi + destination land transport, arrival + departure |
| Travel Time | SGD 500/day engineer time cost (capped at 8 hrs/day) |
| Allowance | SGD 100/day abroad allowance |
| Hotel | Per-night rate × mob period |
Output label:
| Condition | Header |
|---|---|
| Default (10 working days) | Onshore / Offshore — applies to both |
--days specified |
Onshore only |
--offshore |
Offshore (anchorage / sea trial) |
Offshore: Always at standard 10-day rate. --days is ignored.
Singapore: Onshore absorbed in day rates; offshore quoted normally.
Batam: 2× one-way ferry tickets; no date change fee; no travel time.
Routing-heavy destinations (BR, GY, NA, SN) add 2 travel days automatically. In-country transit destinations (KR: Incheon → Geoje) add extra transit days.
Lumpsum ceiled to nearest SGD 500. BUC = lumpsum +5% GM; selling price = BUC +30% GM, split 50/50 Mob/Demob. USD figures ceiled to nearest USD 100.
Specialist options¶
| Option | Description |
|---|---|
--specialist TIER |
Tier: standard, premium, or super |
--name NAME |
Specialist system designation for --md row (e.g. "PAGA Specialist", "POB Specialist"); defaults to "<Tier> Specialist". Keeps specialist rows separate from JEN Engineer rows at the same location. |
--day-rate RATE |
Supplier day rate — auto-classifies tier |
--currency CCY |
Currency of --day-rate: USD (default), SGD, EUR, GBP, NOK, DKK |
Specialist mob is priced in SGD with USD shown alongside. Tier is classified by the supplier's day rate converted to USD — use the onshore rate; for offshore-only suppliers (e.g. Kongsberg, whose onshore hourly rate understates deployment cost) use their offshore/accommodated rate instead. Destinations are split into short-haul (SG, CN, KR, TW) and long-haul (all others).
| Tier | Supplier onshore day rate | Short-haul mob/demob | Long-haul mob/demob |
|---|---|---|---|
standard |
≤ SGD 3,000 (≤ USD 2,200) | SGD 7,000 | SGD 10,500 |
premium |
SGD 3,001–4,600 (USD 2,201–3,400) | SGD 7,000 | SGD 10,500 |
super |
> SGD 4,600 (> USD 3,400) | SGD 9,000 | SGD 13,500 |
Mob/demob values are per leg (Mob and Demob each); combined total is 2×. Calibrated from TMS-DK (EUR 1,650/day), Rutter (USD 1,850/day), S3 ID (GBP 2,050–2,772/day), and Kongsberg Maritime (SGD 4,639–5,101/day, 2025 rates).
Specialist --md — grouped by haul tier, shown by country¶
bid mob --specialist TIER COUNTRY --md writes into the Mobilisation / Demobilisation
sub-table only (never touches Rates). Specialists have no fixed home base the way JEN Engineer
does (always ex-Singapore), and their mob/demob cost is tier-based only (Short-haul or
Long-haul), so rows are grouped internally by haul tier — but the Location column shows
the actual accumulated country codes, in the order they were run, not a "Long-haul"/
"Short-haul" label the client has no context for. Running it for NL then DE (both
long-haul) merges onto one row showing NL, DE, rather than duplicating an identical row or
displaying a bare tier name. COUNTRY is still required (it's how the haul tier gets
determined), it just isn't literally what ends up in the Location cell once merged with others
in the same tier.
Use the same --name on both the rate and mob calls so the Rates row and Mob/Demob rows
share a designation:
# 1. Create the specialist's Rates row (Day Rate/OT/Standby — no location)
bid rate --specialist premium --name "PAGA Specialist" --md
# 2. Add Mob/Demob for destinations in one call, in your preferred order —
# NL and DE (both long-haul) merge onto one row: "NL, DE"; SG (short-haul)
# stays a separate row: "SG"
bid mob --specialist premium --name "PAGA Specialist" NL,DE,SG --md
If the section for that mode (onshore/offshore) doesn't exist at all yet — i.e. bid rate --md
has never been run — mob --md prints a hint to run bid rate --specialist TIER --name NAME
--md first, since there's no ### Mobilisation / Demobilisation sub-table to write into.
Examples¶
# JEN engineer — standard deployment
bid mob BR
bid mob US
# Offshore label — anchorage / sea trial
bid mob KR --offshore
bid mob SG --offshore
# Batam via ferry
bid mob --batam
# Additional cost items
bid mob BR --buffer 1000 "agent fee" --buffer 500 "medical clearance"
# Override mob period (onshore only)
bid mob CN --days 14
# Write to man-day-rates.md — adds row to Onshore + Offshore tables (inherits currency from file)
bid mob NL --md
bid mob DE --md
# Override currency for this mob run
bid mob NL --md --md-currency USD
# Specialist — direct tier
bid mob --specialist standard
bid mob --specialist super NA
bid mob --specialist premium BR
# Specialist — auto-classify from supplier quote
bid mob --day-rate 1650 --currency EUR NA # → Standard
bid mob --day-rate 2772 --currency GBP BR # → Premium
bid mob --day-rate 5101 --currency SGD NA # → Super
# Specialist — write mob/demob to man-day-rates.md under a custom designation
bid rate --specialist premium --name "PAGA Specialist" --md
bid mob --specialist premium --name "PAGA Specialist" NL --md
Adding a new country¶
Add a [countries.XX] block to util/mob_config.toml:
[countries.XX]
name = "Country Name"
airfare_roundtrip = 1500 # SGD round-trip flexible return estimate
visa = 100 # SGD visa cost (0 if not required)
transport_one_way = 80 # SGD destination land transport, one way
hotel_per_night = 150 # SGD per night
travel_days_one_way = 1 # calendar travel days one way
visa_note = "Work visa required" # optional; displays warning in output
routing = true # optional; adds +2 days for multi-stop routes
extra_days = 1 # optional; extra days for in-country transit
onshore_mob_absorbed = true # optional; suppress onshore output (e.g. SG)
Updating specialist tiers¶
Edit [specialist.tiers.*] blocks in util/mob_config.toml. Each tier requires:
[specialist.tiers.standard]
day_rate_usd_max = 2200 # upper bound for tier classification (USD)
onshore_sell = 2500 # USD selling day rate — use with bid rate --specialist
offshore_sell = 3300 # USD onshore × 1.3, ceil to 100
mob_short = 7000 # mob/demob selling price per leg (SGD)
mob_long = 10500
[specialist.fx]
# Units of each currency per 1 SGD (SGD = 1 as base)
SGD = 1.000
USD = 0.741 # 1 SGD ≈ 0.741 USD
EUR = 0.679
GBP = 0.583
NOK = 7.78
DKK = 5.10
Offshore multipliers are differentiated by tier based on actual supplier rate cards: - Standard (Trelleborg, Rutter): suppliers charge flat rate — ×1.3 is our own margin premium - Premium (S3 ID): supplier charges ×1.35 offshore vs onshore — we match this ratio - Super (Kongsberg): supplier charges ×1.86 — we use ×1.5 which clears their offshore cost by ~24%
mob-config¶
Export the mob/demob config to Excel for review or client sharing.
| Argument | Description |
|---|---|
OUTPUT |
Output path (default: mob_config.xlsx) |
Generates a workbook with three sheets:
| Sheet | Contents |
|---|---|
| Countries | Per-country airfare, visa, transport, hotel, and routing settings |
| Batam | Batam ferry-specific config |
| Defaults | Global defaults (mob days, GM rates, FX rate, etc.) |
bond¶
Compute the performance bond amount and its issuance cost, so the cost of obtaining the bond can be included in the project's costing.
| Option | Description |
|---|---|
-p, --percent |
Performance bond percentage of project price (default: 10.0) |
-d, --duration |
Project execution duration in months (contract start to completion) |
-w, --warranty |
Warranty period in months |
-r, --rate |
Bond issuance premium rate, % per annum (default: 1.0) |
Prompts for price, duration, and warranty period if omitted.
Calculation:
- Bond amount = project price × bond percentage (10% is the typical rate requested in tenders).
- Bond duration = project duration + warranty period + 30 days (the bond is held from contract start until 30 days after the Warranty Period expires; the 30 days is approximated as 1 month).
- Issuance cost (the premium charged by the bank/insurer to issue the bond) = bond amount × premium rate p.a. × (bond duration / 12).
warranty¶
Compute extended warranty cost beyond the included base warranty, so an extension quote can be priced consistently instead of guessed.
| Option | Description |
|---|---|
-f, --from |
Extension months already quoted/committed (baseline for the incremental figure; default: 0) |
-t, --to |
Target extension months to highlight and/or compute incrementally from --from |
-m, --max-months |
Table range: max extension months to show (default: 66, reaching 7yr from delivery) |
-6, --six-month |
Show the 6-month-interval table instead of the default 1-year-interval table |
--md [FILE] |
Also write output as Markdown (bare --md defaults to warranty.md) |
-o, --output FILE |
Also write output as CSV or Excel — format inferred from the .csv/.xlsx extension |
-b, --buc-gm PCT |
GM% applied to cost to form Base Unit Cost (default: 5.0) |
-g, --gm PCT |
GM% applied to BUC to form the customer Selling Price (default: 20.0) |
Prompts for price if omitted — enter material cost only (equipment,
excluding labour and services).
Scope:
- The included base warranty (18 months from delivery or 12 months from commissioning, whichever occurs earlier) is free and not calculated here — this prices extensions beyond that point only.
- Manufacturer coverage is assumed already expired by the time an extension is being quoted, so every extension month is fully self-insured risk from month 1 (no separate manufacturer-gap tracking).
- Covers parts replacement + no-manufacturer-backstop risk + admin support only. Site visits during the extension are billed separately as T&M — not included in this price.
Tiered annual rate (of material cost), escalating by extension year — this is our cost (risk reserve), not the customer sell price:
| Extension year | Months | Rate |
|---|---|---|
| Y2 | 1–12 | 1.5% p.a. |
| Y3 | 13–24 | 2.0% p.a. |
| Y4 | 25–36 | 2.5% p.a. |
| Y5+ | 37+ | 3.0% p.a. (flat — does not climb further) |
Year labels count the included base warranty as "Y1", so "Y2" is the first paid extension year.
Material Cost → Selling Price (two-stage GM markup):
Matches the convention already used by bid mob
(price = cost / (1 - GM%)). Labeled "Material Cost" throughout since
it excludes labour/services:
- Material Cost → Base Unit Cost:
BUC = Material Cost / (1 - buc-gm%)(default 5%) - BUC → Selling Price:
Sell = BUC / (1 - gm%)(default 20%), rounded to the nearest whole dollar. GM $ and Δ Sell are derived from that rounded Sell, so they stay consistent with the quoted figure rather than an unrounded intermediate value. Material Cost and BUC keep 2-decimal precision since those are internal figures, not customer-facing.
Output:
- Default: a cumulative 1-Year Interval table (up to
--max-months) showing extension duration, the equivalent Yr from Delivery (assumes the worst-case 18-month base warranty, so non-specialists can read the schedule without knowing the tier convention), tier/rate, and Material Cost, Δ Cost, BUC, GM $, Sell, and Δ Sell as separate columns — so the internal risk reserve, its year-over-year delta, and the quotable figure are all visible without manual subtraction. The first row sits at the 6-month checkpoint (0.5yr extension), since that is what reaches the 2yr-from-delivery mark once the 18-month included warranty is added — subsequent rows then step by full years (3yr, 4yr, 5yr from delivery, ...). - Pass
-6/--six-monthto see the same columns broken out in 6-month steps instead (0.5yr, 1yr, 1.5yr, ...) — useful when a customer is negotiating a non-annual extension length. - With
-f/--fromset: an explicit incremental line reporting both the cost and sell increments between--fromand--to(or--max-monthsif--toomitted) — for "we're already quoted to 3 years, what does one more year to reach 4 cost, and what do we sell it for." --md/-owrite the same single table (whichever interval is active) to a file alongside the terminal output — Markdown for pasting into a proposal doc, CSV for raw data, or a formatted Excel workbook (Jason Blue005BBFheader) for sharing. The Excel version splits margin into two columns — GM % (the effective margin, computed as profit/sell from the actual rounded-dollar values) and Profit $ — instead of the single dollar-only figure shown in the terminal/Markdown/CSV output.
bid warranty 3119488 # full cumulative table, 1-year intervals
bid warranty 3119488 -6 # same, but 6-month intervals
bid warranty 3119488 -t 42 # highlight the 3.5-year point
bid warranty 3119488 -f 36 -t 48 # cost/sell to extend from 3yr to 4yr
bid warranty 3119488 -b 5 -g 25 # override BUC/selling GM%
bid warranty 3119488 --md # also write warranty.md
bid warranty 3119488 -o warranty.xlsx # also write an Excel workbook
bid warranty 3119488 -o warranty.csv # also write CSV
schedule¶
Generate a Gantt chart Excel workbook from a markdown schedule file.
| Option | Description |
|---|---|
--start YYYY-MM-DD |
Override the start date (default: start: in file, or today) |
--open |
Open the generated Excel file after creation |
Output is saved as <input_file_stem>.xlsx in the same directory.
Markdown format¶
# Project Name
start: 2026-08-01
## Phase One
- milestone: Kickoff
- Item A: 4–6 wks EXW NO
- Item B: 2 wks
- milestone: Design Freeze
## Phase Two [after: Phase One]
- Item C: 8–10 wks EXW CN
- Item D: 4 wks EXW DE [air] [sync]
- Item E: 2 wks [after: Item C]
Top-level keys
| Line | Description |
|---|---|
# Title |
Project name — appears in Excel title row |
start: YYYY-MM-DD |
Calendar date of Week 1 |
Phase header (## Name)
A phase groups related items and occupies one row in the Gantt. By default phases run sequentially; annotations change this.
Item (- Name: N wks [origin])
Lead time can be a single value (4 wks) or a range (4–6 wks). The higher value is used for scheduling. Origin is free text; the last word is treated as a country code for sea freight calculation.
Milestone (- milestone: Label)
Places a ◆ marker in the Gantt. Position is determined by where it appears in the item list:
- Listed before any items → placed at the phase start week
- Listed after items (most common) → placed at the phase end week
## Engineering & Design
- milestone: Project Kickoff ← start week
- System design review: 2 wks
- Detailed engineering: 4–6 wks
- milestone: Design Freeze ← end week
Milestone names cannot be used as [after:] targets. Use the phase name instead — it resolves to the same week as the end milestone.
Annotations¶
Annotations are bracket tags appended to a phase header or item line. They are case-insensitive ([Air], [SYNC], [After: X] all work).
| Annotation | Applies to | Description |
|---|---|---|
[after: Name] |
phase, item | Start after the named phase or item finishes. For items, "finishes" means delivery (lead + freight). Comma-separate for multiple: [after: A, B] |
[start: WK15] |
phase, item | Force start at a specific week. WK prefix is optional and case-insensitive — [start: 15] is the same. On a phase: acts as a floor — [after:] wins if the dependency ends later. On an item: absolute override — the item starts at the specified week regardless of phase start. If [after:] is also present on the item, the later of the two wins. |
[air] |
item | Use 2-week airfreight instead of auto-detected sea freight. Transit band is shown in orange in the Gantt to flag higher shipping cost. |
[sync] |
item | Back-schedule this item so it arrives at the same week as the longest non-sync item in the phase (just-in-time ordering). Equivalent to [sync:1]. |
[sync:N] |
item | Back-schedule to arrive with the Nth longest non-sync item (e.g. [sync:2] = second longest, [sync:3] = third longest). Clamped to available items if N exceeds the count. |
Sea freight auto-detection¶
Freight weeks are added on top of the lead time and shown as a separate green band in the Gantt.
| Origin country code | Freight |
|---|---|
SG |
0 weeks |
CN |
2 weeks (short sea) |
| Any other | 4–6 weeks (long-haul sea) |
[air] annotation |
2 weeks (overrides auto-detect) |
For long-haul sea freight a range of 4–6 weeks is displayed; the maximum (6 weeks) is used for conservative scheduling. The transit band is shown in green for sea freight and orange for air freight.
The country code is the last word of the origin string (e.g. EXW NO, FOB CN, EXW DE).
Scheduling rules¶
- Phases with no annotation run sequentially (each starts when the previous ends).
[after: Name]resolves against both phase names and item names. Phase names take priority on collision. Unknown names are silently ignored.- Item-level
[after:]references are resolved in document order — earlier items in the file must appear before items that depend on them. - Item-level
[start: WKN]is an absolute override: the item starts at the given week regardless of when its phase starts. This allows early ordering ahead of the phase (e.g. long-lead procurement starting before Engineering & Design finishes). If[after:]is also set on the item, the later of the two wins. [sync]/[sync:N]items are scheduled in a second pass after all non-sync items in the phase are resolved.[sync]targets the longest;[sync:2]targets the second longest, and so on.- Phase
end_weekis the maximum delivery week across all items (lead + freight).
Examples¶
# Generate schedule, open immediately
bid schedule project.md --open
# Override start date
bid schedule project.md --start 2026-09-01 --open
Parallel phases:
Both phases start when Engineering & Design ends, running in parallel.
Item-level dependency:
## Yard Integration
- Equipment installation: 2–3 wks
- System integration: 2 wks [after: Equipment installation]
- Onshore commissioning: 2–3 wks [after: System integration]
Just-in-time procurement ([sync]):
## Procurement
- Navigation System: 12–14 wks EXW NO
- Oil Spill Detection: 10–12 wks EXW CA [air] [sync]
- Alarm Management: 6–8 wks EXW DE [sync]
Navigation System (longest) drives the delivery date. Both are back-scheduled to arrive the same week.
To stagger arrivals by rank instead:
## Procurement
- Navigation System: 12–14 wks EXW NO
- Oil Spill Detection: 10–12 wks EXW CA [air] [sync:1]
- Alarm Management: 6–8 wks EXW DE [sync:2]
Oil Spill Detection arrives with the longest (Navigation System); Alarm Management arrives with the second longest.
Fixed week + dependency floor:
Starts at WK15 unless Engineering & Design finishes later, in which case that takes over.
setup¶
Configure environment and shell aliases.
Installs dependencies, configures xlwings, copies Excel templates, and adds
the bid alias to your shell configuration.