diffmode 0.1.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/LICENSE +202 -0
- package/NOTICE +9 -0
- package/README.md +215 -0
- package/dist/bin.js +6603 -0
- package/package.json +51 -0
- package/skills/diffmode/.cursor/rules/diffmode.mdc +32 -0
- package/skills/diffmode/AGENTS.md +35 -0
- package/skills/diffmode/SKILL.md +136 -0
- package/skills/diffmode/llms.txt +20 -0
- package/skills/diffmode/references/commands.md +295 -0
- package/skills/diffmode/references/error-codes.md +118 -0
- package/skills/diffmode/references/founder-input-schema.md +113 -0
- package/skills/diffmode/references/idea-input-schema.md +82 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Exit codes reference
|
|
2
|
+
|
|
3
|
+
`diffmode`'s exit codes are part of the public contract — agents and shell
|
|
4
|
+
scripts branch on them. The table below is verbatim from spec §8.
|
|
5
|
+
|
|
6
|
+
| Code | Name | Trigger | Recovery |
|
|
7
|
+
| --- | --- | --- | --- |
|
|
8
|
+
| `0` | OK | Command succeeded. | None. |
|
|
9
|
+
| `1` | GENERIC | Unclassified failure. | Surface stderr `error.message` to the user. |
|
|
10
|
+
| `2` | USAGE | Wrong flags, missing args, schema violations, "module not resumable", "no prior free-tier", … | Read stderr; fix the invocation. |
|
|
11
|
+
| `3` | NETWORK | DNS, TLS, connection-reset, response-body parse failure. | Check connectivity; retry with backoff (≤ 3 attempts). |
|
|
12
|
+
| `4` | AUTH | No token, expired/revoked PAT, or 401 from server. | Ask the user to run `diffmode login`, then retry. |
|
|
13
|
+
| `5` | CONFLICT (409) | A job is already running for this product. | Parse stderr `error.job_id`; switch to `diffmode jobs watch <id>`. |
|
|
14
|
+
| `6` | NOT_FOUND | 404 from server (unknown job id, missing product). | Verify the id/product slug. |
|
|
15
|
+
| `7` | RATE_LIMITED (429) | Free-tier limit (3 submits / 24 h) or `Retry-After` set. | Honor `error.retry_after` (seconds); sleep and retry. |
|
|
16
|
+
| `8` | INSUFFICIENT_CREDITS (402) | Pre-flight or server rejected the submit. | Print `error.billing_url`; instruct the user to top up at `https://diffmode.app/app/billing`. **Do not retry.** |
|
|
17
|
+
| `9` | SERVER | 5xx. | If `error.retryable === true`, retry with backoff. Otherwise stop and surface. |
|
|
18
|
+
| `10` | INTERRUPTED_RESUMABLE | Server marked the job interrupted (deploy/rotation). | Run `diffmode jobs resume <id>` once, then `diffmode jobs watch <id>` again. |
|
|
19
|
+
| `130` | SIGINT | User hit Ctrl-C. | Print resume hint (the server job is still running); do NOT auto-cancel. |
|
|
20
|
+
|
|
21
|
+
## Per-code recovery scripts
|
|
22
|
+
|
|
23
|
+
### Exit 4 (AUTH)
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
diffmode whoami --json
|
|
27
|
+
# exit 4 → prompt user
|
|
28
|
+
echo "Please authenticate, then re-run."
|
|
29
|
+
echo " npx diffmode@latest login (paste PAT from https://diffmode.app/app/tokens)"
|
|
30
|
+
exit 4
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Exit 5 (CONFLICT — in-flight job)
|
|
34
|
+
|
|
35
|
+
The error envelope on stderr carries the running `job_id`:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{"error": {"code": "conflict", "message": "...", "retryable": false, "job_id": "<id>", "product_id": "<p>"}}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Pivot to watching that job:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
diffmode run "$PRODUCT" --input founder.json --idempotency-key "$UUID" --json
|
|
45
|
+
# if exit 5:
|
|
46
|
+
JOB_ID=$(jq -r '.error.job_id' /tmp/stderr.json)
|
|
47
|
+
diffmode jobs watch "$JOB_ID" --json --wait 4h
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Exit 7 (RATE_LIMITED)
|
|
51
|
+
|
|
52
|
+
Read `error.retry_after` (seconds). If ≤ 300, sleep and retry once. If
|
|
53
|
+
larger, surface to the user — don't loop silently.
|
|
54
|
+
|
|
55
|
+
### Exit 8 (INSUFFICIENT_CREDITS)
|
|
56
|
+
|
|
57
|
+
Browser-only top-up. The CLI **never** calls `POST /billing/checkout`. The
|
|
58
|
+
error payload includes the billing URL the CLI resolves at runtime:
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{"error": {"code": "insufficient_credits", "message": "...", "retryable": false, "billing_url": "https://diffmode.app/app/billing"}}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Tell the user, do not retry:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
You're out of credits. Top up at https://diffmode.app/app/billing,
|
|
68
|
+
then re-run `diffmode run <product>`.
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Exit 10 (INTERRUPTED_RESUMABLE)
|
|
72
|
+
|
|
73
|
+
Workflow + free-tier are resumable; idea-eval, smoke-test, and unlock are
|
|
74
|
+
not. `diffmode jobs resume` handles the module branching for you:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
diffmode jobs resume "$JOB_ID" --json
|
|
78
|
+
diffmode jobs watch "$JOB_ID" --json --wait 4h
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
If `jobs resume` itself exits 2 with "module not resumable", resubmit:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
diffmode "$MODULE" "$PRODUCT" --input founder.json --idempotency-key "$NEW_UUID"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Exit 130 (SIGINT)
|
|
88
|
+
|
|
89
|
+
The server-side job keeps running. Confirm with the user; if they want to
|
|
90
|
+
keep going:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
diffmode jobs watch "$JOB_ID" --json --wait 4h
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Do NOT issue `DELETE /jobs/{id}` on Ctrl-C — the CLI deliberately skips
|
|
97
|
+
that to keep jobs durable across watch interruptions.
|
|
98
|
+
|
|
99
|
+
## Coverage per command
|
|
100
|
+
|
|
101
|
+
The exact exit-code set per command is in `commands.md` (and emitted by
|
|
102
|
+
`diffmode commands --json` under each entry's `exits` field). This file
|
|
103
|
+
documents the codes themselves; that file documents which commands can emit
|
|
104
|
+
which codes.
|
|
105
|
+
|
|
106
|
+
## Drift policy
|
|
107
|
+
|
|
108
|
+
This table mirrors `src/lib/exit-codes.ts` (constants) and spec §8 (the
|
|
109
|
+
authoritative source). If you change a code's number or name:
|
|
110
|
+
|
|
111
|
+
1. Update `src/lib/exit-codes.ts`.
|
|
112
|
+
2. Update the constant test in `test/exit-codes.test.ts`.
|
|
113
|
+
3. Update this file + `commands.md`.
|
|
114
|
+
4. Bump `schema_version` if the JSON envelope's `error.code` strings
|
|
115
|
+
change semantically.
|
|
116
|
+
|
|
117
|
+
The CI matrix in `test/exit-codes.test.ts` enforces constants vs. spec
|
|
118
|
+
parity; this doc is for humans + agents.
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Founder input schema (`FounderDiagnostics`)
|
|
2
|
+
|
|
3
|
+
`diffmode run`, `diffmode workflow`, and `diffmode smoke-test` all accept a
|
|
4
|
+
founder-input JSON object via `--input <file|->`. The schema mirrors the
|
|
5
|
+
backend Pydantic class `FounderDiagnostics` in the upstream Diffmode API
|
|
6
|
+
(`src/api/models.py`).
|
|
7
|
+
|
|
8
|
+
> **Drift policy:** regenerate this file whenever
|
|
9
|
+
> `models.py:FounderDiagnostics` changes. Treat the Pydantic class as the
|
|
10
|
+
> source of truth; this document is a developer-facing mirror. The CLI
|
|
11
|
+
> parser intentionally tolerates unknown keys (`extra="allow"`) so adding
|
|
12
|
+
> a field to the backend never breaks an existing client.
|
|
13
|
+
|
|
14
|
+
## Required
|
|
15
|
+
|
|
16
|
+
- **`product_description`** *(string, non-empty)* — what the product does.
|
|
17
|
+
This is the only hard-required field. Missing it → CLI exits 2 with a
|
|
18
|
+
pointer to this file.
|
|
19
|
+
|
|
20
|
+
## Recommended (warn if missing)
|
|
21
|
+
|
|
22
|
+
These fields meaningfully sharpen the generated plan; the CLI prints a
|
|
23
|
+
non-fatal stderr warning when they're absent.
|
|
24
|
+
|
|
25
|
+
- `target_audience` *(string)* — current or target customers.
|
|
26
|
+
- `trigger_events` *(string)* — moments customers need the solution.
|
|
27
|
+
- `pricing` *(string)* — business model and pricing.
|
|
28
|
+
- `acquisition_sources` *(string)* — where customers come from today.
|
|
29
|
+
- `current_growth` *(object|null)* — last-30-days traffic + conversion.
|
|
30
|
+
|
|
31
|
+
## Optional — diagnostics (v2.2)
|
|
32
|
+
|
|
33
|
+
- `alternatives_used` *(string)*
|
|
34
|
+
- `marketing_experiments` *(string)*
|
|
35
|
+
- `tactics_ruled_out` *(string)*
|
|
36
|
+
- `goals` *(string)*
|
|
37
|
+
- `budget` *(string)* — monthly budget shorthand.
|
|
38
|
+
- `product_complexity` *(object|null)* — `{ type, details }`. `from-url`
|
|
39
|
+
pre-fill populates this when the analysis returns a complexity hint.
|
|
40
|
+
- `resource_constraints` *(object|null)*
|
|
41
|
+
- `problem_urgency` *(object|null)*
|
|
42
|
+
- `challenges` *(object|null)*
|
|
43
|
+
|
|
44
|
+
## Optional — business / team context
|
|
45
|
+
|
|
46
|
+
- `business_model` *(string)*
|
|
47
|
+
- `current_mrr` *(string)*
|
|
48
|
+
- `funding_stage` *(string)*
|
|
49
|
+
- `team_size` *(string)*
|
|
50
|
+
- `your_role` *(string)*
|
|
51
|
+
- `marketing_team_size` *(string)*
|
|
52
|
+
- `resource_profile` *(string)* — `"solo"` / `"small_team"` / `"dedicated"`.
|
|
53
|
+
|
|
54
|
+
## Optional — persona & customer mix
|
|
55
|
+
|
|
56
|
+
- `persona_type` *(string)* — founder vs marketing-hire framing.
|
|
57
|
+
- `observed_customers` *(string)*
|
|
58
|
+
- `customer_mix` *(string)*
|
|
59
|
+
- `top_competitors` *(string)*
|
|
60
|
+
- `what_makes_you_different` *(string)*
|
|
61
|
+
- `channels_tried_raw` *(string | string[])*
|
|
62
|
+
|
|
63
|
+
## Optional — growth & retention signals
|
|
64
|
+
|
|
65
|
+
- `retention_signal` *(string)*
|
|
66
|
+
- `emotional_signals` *(string)*
|
|
67
|
+
- `blind_spots` *(string)*
|
|
68
|
+
- `comprehension_friction` *(string)*
|
|
69
|
+
- `comfortable_with_outreach` *(`""` | `"yes"` | `"no"`)*
|
|
70
|
+
- `trigger_events_source` *(string)*
|
|
71
|
+
|
|
72
|
+
## Extra keys
|
|
73
|
+
|
|
74
|
+
The backend accepts arbitrary additional keys (`extra="allow"`). The
|
|
75
|
+
parent diagnostics formatter renders them under an "Additional Context"
|
|
76
|
+
section. **Reserved keys** (`user_id`, `created_at`, `updated_at`, `id`,
|
|
77
|
+
`job_id`, `product_id`) are server-managed and rejected by the CLI before
|
|
78
|
+
submit.
|
|
79
|
+
|
|
80
|
+
## Example
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"product_description": "Drop-in fraud-detection API for fintech apps.",
|
|
85
|
+
"pricing": "Usage-based — $0.002/request after a 10k-request free tier.",
|
|
86
|
+
"target_audience": "Series-A fintechs adding their first underwriting team.",
|
|
87
|
+
"trigger_events": "First fraud incident or compliance review.",
|
|
88
|
+
"acquisition_sources": "Founder outbound + a small founder-led community.",
|
|
89
|
+
"current_growth": { "traffic_30d": 12000, "conversion_rate": 0.018 },
|
|
90
|
+
"goals": "$50k MRR in 6 months."
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Pre-fill from a URL
|
|
95
|
+
|
|
96
|
+
`diffmode diagnostics from-url https://your-site.example` calls
|
|
97
|
+
`POST /public/v1/analyze-website` and emits a draft `FounderDiagnostics`
|
|
98
|
+
JSON. Field mapping:
|
|
99
|
+
|
|
100
|
+
| Response field | FounderDiagnostics field |
|
|
101
|
+
|-------------------------------------------|-----------------------------------|
|
|
102
|
+
| `company_description` | `product_description` |
|
|
103
|
+
| `analysis_pricing` | `pricing` |
|
|
104
|
+
| `target_customer` | `target_audience` |
|
|
105
|
+
| `analysis_trigger_events` | `trigger_events` |
|
|
106
|
+
| `how_customers_find_you_today` | `acquisition_sources` |
|
|
107
|
+
| `top_competitors` | `top_competitors` |
|
|
108
|
+
| `what_makes_you_different` | `what_makes_you_different` |
|
|
109
|
+
| `analysis_product_complexity_type` + `_details` | `product_complexity` *(object)* |
|
|
110
|
+
|
|
111
|
+
`auto_filled_fields` / `draft_fields` from the response are logged to
|
|
112
|
+
stderr (not part of the diagnostics object) so you can tell which fields
|
|
113
|
+
are confident vs. best-guess. Always review draft fields before submit.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# IdeaInput schema
|
|
2
|
+
|
|
3
|
+
The `diffmode idea-eval` command accepts a structured JSON list of `IdeaInput`
|
|
4
|
+
objects, not free-text strings. This file mirrors the canonical Pydantic
|
|
5
|
+
shape declared in the upstream Diffmode API (`IdeaInput` in `src/api/models.py`,
|
|
6
|
+
~lines 43–83) — regenerate this doc when that class changes.
|
|
7
|
+
|
|
8
|
+
## Top-level shape
|
|
9
|
+
|
|
10
|
+
The ideas file passed via `--ideas-file <path>` MUST be a JSON array of
|
|
11
|
+
`IdeaInput` objects:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
[
|
|
15
|
+
{ "name": "…", "description": "…", … },
|
|
16
|
+
{ "name": "…", "description": "…", … }
|
|
17
|
+
]
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
A single-object file (`{"ideas": [...]}`) is **not** accepted — the array
|
|
21
|
+
must be the top-level value.
|
|
22
|
+
|
|
23
|
+
## Required fields
|
|
24
|
+
|
|
25
|
+
| Field | Type | Notes |
|
|
26
|
+
| --- | --- | --- |
|
|
27
|
+
| `name` | string (non-empty) | Idea name |
|
|
28
|
+
| `description` | string (non-empty) | One-sentence description |
|
|
29
|
+
|
|
30
|
+
## Optional fields
|
|
31
|
+
|
|
32
|
+
All optional fields default to `""` server-side and are forwarded into the
|
|
33
|
+
"Additional Context" section of the formatted prompt.
|
|
34
|
+
|
|
35
|
+
| Field | Type | Description |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| `target_customer` | string | Target segment, B2B/B2C, ideal first 10 customers |
|
|
38
|
+
| `problem` | string | Pain point being solved, urgency, consequence of inaction |
|
|
39
|
+
| `solution` | string | Solution approach, how it solves, differentiation |
|
|
40
|
+
| `revenue_model` | string | Subscription, one-time, marketplace, etc. |
|
|
41
|
+
| `price_point` | string | Price point |
|
|
42
|
+
| `revenue_goal_min` | string | Minimum viable MRR + timeline |
|
|
43
|
+
| `revenue_goal_ambitious` | string | Ambitious revenue goal |
|
|
44
|
+
| `validation` | string | Existing validation: conversations, signups, prototypes |
|
|
45
|
+
|
|
46
|
+
## Extras
|
|
47
|
+
|
|
48
|
+
`IdeaInput` uses Pydantic `extra="allow"` — any additional fields you include
|
|
49
|
+
will be preserved and rendered in the formatted output under "Additional
|
|
50
|
+
Context". Use this to carry domain-specific metadata you want the model to
|
|
51
|
+
see.
|
|
52
|
+
|
|
53
|
+
## Example
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
[
|
|
57
|
+
{
|
|
58
|
+
"name": "founder-mode-onboarding",
|
|
59
|
+
"description": "Concierge onboarding videos recorded by the founder",
|
|
60
|
+
"target_customer": "B2B SaaS solopreneurs $0–$10k MRR",
|
|
61
|
+
"problem": "Activation drops 60% on day 2 of a free trial",
|
|
62
|
+
"solution": "Loom-style 90s personalized welcome per signup",
|
|
63
|
+
"revenue_model": "subscription, $79/mo add-on",
|
|
64
|
+
"price_point": "$79/mo",
|
|
65
|
+
"revenue_goal_min": "$2k MRR in 90 days",
|
|
66
|
+
"validation": "12 founders agreed to pilot"
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Companion flags
|
|
72
|
+
|
|
73
|
+
- `--intuition "<text>"` — founder's qualitative gut feel about the list
|
|
74
|
+
- `--target-idea "<slug>"` — evaluate a single idea (slug = name in
|
|
75
|
+
kebab-case, e.g. `founder-mode-onboarding`)
|
|
76
|
+
|
|
77
|
+
## See also
|
|
78
|
+
|
|
79
|
+
- [`founder-input-schema.md`](./founder-input-schema.md) — diagnostics that
|
|
80
|
+
`run` / `workflow` / `smoke-test` consume
|
|
81
|
+
- [`commands.md`](./commands.md) — endpoint + credit cost per command
|
|
82
|
+
- [`error-codes.md`](./error-codes.md) — exit-code recovery guide
|