@v0idd0/message-next-step 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vøiddo
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,264 @@
1
+ # message-next-step
2
+
3
+ [![License: MIT](https://img.shields.io/badge/license-MIT-0F172A.svg)](LICENSE)
4
+ [![Node ≥18](https://img.shields.io/badge/node-%E2%89%A518-0F172A)](package.json)
5
+
6
+ **[Web app](https://tells.voiddo.com/message-next-step/?ref=message-next-step-readme)** · **[Live compare page](https://tells.voiddo.com/message-next-step/compare-chatgpt-gemini.html?ref=message-next-step-readme)** · **[Packaged compare brief](compare-chatgpt-gemini.md)** · **[Deep Dive](https://tells.voiddo.com/deep-dive/?ref=message-next-step-readme)** · **[Signal toolkit](https://tells.voiddo.com/signal-toolkit/?ref=message-next-step-readme)** · **[GitHub](https://github.com/voidd0/message-next-step)** · **[npm](https://www.npmjs.com/package/@v0idd0/message-next-step)** · **[All tools](https://tools.voiddo.com/?ref=message-next-step-catalog-readme)** · **[Contact](mailto:support@voiddo.com)**
7
+
8
+ ---
9
+
10
+ `message-next-step` is a deterministic checker for one narrow question:
11
+
12
+ What should you do with this message right now?
13
+
14
+ It reads one incoming message and suggests one next step:
15
+
16
+ - `reply now`
17
+ - `wait`
18
+ - `ask one concrete question`
19
+ - `move to a call`
20
+ - `let it go`
21
+
22
+ It does not pretend to read the whole relationship. It gives a fast, explainable first pass when you are stuck between overreacting and overinterpreting in dating, recruiter, workplace, client, support, or family threads.
23
+
24
+ It also works as a narrower, faster alternative to ChatGPT or Gemini when the real question is not "help me think forever" but "what is the next move on this one message?"
25
+
26
+ ## Why this exists
27
+
28
+ The paid product is [`tells`](https://tells.voiddo.com/?ref=message-next-step-readme), which reads what people leave unsaid across messages, people, and profiles.
29
+
30
+ But many users do not start with "analyze the whole pattern." They start with a smaller operational question:
31
+
32
+ - Should I answer this now?
33
+ - Are they asking for something real, or just creating pressure?
34
+ - Is this vague enough that I need one clarifying question?
35
+ - Is texting the wrong bandwidth for this?
36
+
37
+ That same question shows up outside dating too:
38
+
39
+ - recruiter loops where timing and seriousness are unclear
40
+ - client or account messages that may need a cleaner boundary
41
+ - support escalations that are getting too heated for chat
42
+ - family or cofounder threads where the real question is whether text is still working
43
+
44
+ `message-next-step` is that free first step. It gives a deterministic action recommendation before the user commits to a deeper paid read.
45
+
46
+ ## Install
47
+
48
+ ```bash
49
+ npm install -g @v0idd0/message-next-step
50
+ ```
51
+
52
+ ## Web app
53
+
54
+ Use the browser version here:
55
+
56
+ ```text
57
+ https://tells.voiddo.com/message-next-step/?ref=message-next-step-readme
58
+ ```
59
+
60
+ If you want the browser-side AI-alternative framing first:
61
+
62
+ ```text
63
+ https://tells.voiddo.com/message-next-step/compare-chatgpt-gemini.html?ref=message-next-step-readme
64
+ ```
65
+
66
+ If you want the packaged compare brief for npm/GitHub readers:
67
+
68
+ ```text
69
+ compare-chatgpt-gemini.md
70
+ ```
71
+
72
+ If the blocker is follow-up timing rather than reply content, use the sibling checker here:
73
+
74
+ ```text
75
+ https://tells.voiddo.com/double-text-risk/?ref=message-next-step-readme
76
+ ```
77
+
78
+ If the incoming message is warm but still too vague to treat as a real yes, check that layer here first:
79
+
80
+ ```text
81
+ https://tells.voiddo.com/soft-yes-or-no/?ref=message-next-step-readme
82
+ ```
83
+
84
+ ## Usage
85
+
86
+ ```bash
87
+ message-next-step "Can you confirm by Friday whether you want me to send the draft?"
88
+ ```
89
+
90
+ ```bash
91
+ cat incoming.txt | message-next-step
92
+ ```
93
+
94
+ ```bash
95
+ message-next-step --json "Maybe later. We'll see when things calm down."
96
+ ```
97
+
98
+ ```bash
99
+ message-next-step --file incoming.txt
100
+ ```
101
+
102
+ ## Example output
103
+
104
+ ```text
105
+ posture: direct
106
+ directness: 7.9/10
107
+ ambiguity: 2.5/10
108
+ pressure: 3.2/10
109
+ escalation: 1/10
110
+ avoidance: 1/10
111
+ call shift: 1/10
112
+
113
+ next step: reply now
114
+ why: The text names a usable next step without much ambiguity.
115
+ - There is enough concrete signal to answer directly.
116
+
117
+ next:
118
+ Use tells when one message is not enough and you need the thread, the person, or the pattern over time.
119
+ - quick next paid step: https://tells.voiddo.com/deep-dive/?ref=message-next-step-cli
120
+ - recurring reads: https://tells.voiddo.com/?ref=message-next-step-cli
121
+ ```
122
+
123
+ ## What it scores
124
+
125
+ The tool uses transparent heuristics around six dimensions:
126
+
127
+ - `directness` — is there a real ask, timing, or next step?
128
+ - `ambiguity` — is timing or intent being left blurry?
129
+ - `pressure` — is force doing more work than content?
130
+ - `escalation` — is this message getting too hot for text?
131
+ - `avoidance` — is the sender stalling instead of deciding?
132
+ - `call shift` — are there enough cues that text is the wrong channel?
133
+
134
+ The recommendation is intentionally conservative. It is designed to stop bad reactive moves, not to invent certainty.
135
+
136
+ ## Good use cases
137
+
138
+ - tense client, recruiter, or support texts
139
+ - dating or relationship messages with mixed clarity
140
+ - workplace follow-ups where you are unsure whether to push
141
+ - family messages where a call may be better than more texting
142
+ - any incoming message where your real question is "what do I do next?"
143
+
144
+ ## Bad use cases
145
+
146
+ - therapy, legal, or crisis guidance
147
+ - deception detection
148
+ - full relationship analysis
149
+ - multi-message pattern reading
150
+
151
+ Those are outside this tool's scope. If the stakes are real and one message is not enough, use `tells`.
152
+
153
+ ## Why not just use ChatGPT or Gemini?
154
+
155
+ Because the first question is often operational, not interpretive.
156
+
157
+ For a narrow next-step call, a deterministic tool is faster and easier to trust:
158
+
159
+ - no API key
160
+ - no prompt fiddling
161
+ - no latency roulette
162
+ - no surprise token bill
163
+ - same input gives the same output
164
+
165
+ Then, if the user needs the deeper read, the next step is not "more tokens." It is `tells`, which is built around subtext, conspicuous absence, pressure, and likely next move.
166
+
167
+ If you want the side-by-side framing inside the live browser route:
168
+
169
+ ```text
170
+ https://tells.voiddo.com/message-next-step/compare-chatgpt-gemini.html?ref=message-next-step-readme
171
+ ```
172
+
173
+ If you want the package-side compare brief instead:
174
+
175
+ ```text
176
+ compare-chatgpt-gemini.md
177
+ ```
178
+
179
+ ## Paid next step
180
+
181
+ When the one-message answer is not enough, the paid ladder is simple:
182
+
183
+ - `Deep Dive` — `$19 once` for one loaded thread or one recurring person
184
+ - `Starter` — `$14.99/mo` for repeated message reading
185
+ - `Practitioner` — `$99.99/mo` for coaches, recruiters, mediators, trainers, or client-facing teams using this with clients
186
+
187
+ If the message-level answer is not enough, the fastest paid handoff is usually `Deep Dive`:
188
+
189
+ ```text
190
+ https://tells.voiddo.com/deep-dive/?ref=message-next-step-readme
191
+ ```
192
+
193
+ If you still want the wider browser-first free path first:
194
+
195
+ ```text
196
+ https://tells.voiddo.com/signal-toolkit/?ref=message-next-step-readme
197
+ ```
198
+
199
+ Start here:
200
+
201
+ ```text
202
+ https://tells.voiddo.com/message-next-step/?ref=message-next-step-readme
203
+ ```
204
+
205
+ ## Related free checkers
206
+
207
+ If the next-step question turns out to be the wrong layer, route into the narrower free tool first:
208
+
209
+ - `double-text-risk` for deciding whether another outbound follow-up is too soon or too pushy: `https://tells.voiddo.com/double-text-risk/?ref=message-next-step-related-readme`
210
+ - `soft-yes-or-no` for deciding whether the incoming message is polite interest, real intent, or a warm stall before you commit to a direct reply: `https://tells.voiddo.com/soft-yes-or-no/?ref=message-next-step-related-readme`
211
+ - `replytone` for checking whether your drafted response sounds clear, warm, or too forceful: `https://tells.voiddo.com/replytone/?ref=message-next-step-related-readme`
212
+ - `ambiguity-meter` for scoring whether the incoming message is vague, evasive, or mixed: `https://tells.voiddo.com/ambiguity-meter/?ref=message-next-step-related-readme`
213
+ - `call-not-text` for deciding whether the thread is already too compressed or heated for another message: `https://tells.voiddo.com/call-not-text/?ref=message-next-step-related-readme`
214
+ - `ghost-or-go` for deciding whether ongoing silence means wait, one final ping, or close the loop: `https://tells.voiddo.com/ghost-or-go/?ref=message-next-step-related-readme`
215
+ - `raincheck-or-run` for deciding whether repeated reschedules and no-new-time messages are still workable or already a stall pattern: `https://tells.voiddo.com/raincheck-or-run/?ref=message-next-step-related-readme`
216
+
217
+ ## Programmatic API
218
+
219
+ ```javascript
220
+ import { analyzeMessageNextStep, formatReport } from "@v0idd0/message-next-step";
221
+
222
+ const result = analyzeMessageNextStep("You always do this. Call me right now!!!");
223
+
224
+ console.log(result.decision.action);
225
+ console.log(formatReport(result));
226
+ ```
227
+
228
+ ## Development
229
+
230
+ ```bash
231
+ npm test
232
+ node bin/message-next-step.js "Maybe later. We'll see when things calm down."
233
+ ```
234
+
235
+ ## More from the studio
236
+
237
+ See [`from-the-studio.md`](from-the-studio.md) for the wider vøiddo catalogue.
238
+
239
+ ## Compare surface
240
+
241
+ - live compare page: `https://tells.voiddo.com/message-next-step/compare-chatgpt-gemini.html?ref=message-next-step-readme`
242
+ - packaged compare brief: [`compare-chatgpt-gemini.md`](compare-chatgpt-gemini.md)
243
+
244
+ If you are positioning this tool against general-purpose AI assistants, use the dedicated comparison asset:
245
+
246
+ [`compare-chatgpt-gemini.md`](compare-chatgpt-gemini.md)
247
+
248
+ ## Best next free exits
249
+
250
+ - `replytone` for checking whether your drafted response sounds warm, clear, or too forceful before you send it: `https://tells.voiddo.com/replytone/?ref=message-next-step-readme`
251
+ - `double-text-risk` for deciding whether a follow-up is timely, needy, or better left unsent: `https://tells.voiddo.com/double-text-risk/?ref=message-next-step-readme`
252
+ - `soft-yes-or-no` for deciding whether the incoming wording is a real yes, a warm maybe, or a polite stall before you over-commit to a reply: `https://tells.voiddo.com/soft-yes-or-no/?ref=message-next-step-readme`
253
+ - `ambiguity-meter` for checking whether the incoming message is vague, evasive, or mixed enough to justify a clarifier instead of a direct answer: `https://tells.voiddo.com/ambiguity-meter/?ref=message-next-step-readme`
254
+ - `call-not-text` for deciding whether the thread has outgrown text and needs a call, pause, or firmer channel switch: `https://tells.voiddo.com/call-not-text/?ref=message-next-step-readme`
255
+ - `ghost-or-go` for deciding whether ongoing silence after this message means wait, one final ping, or close the loop: `https://tells.voiddo.com/ghost-or-go/?ref=message-next-step-readme`
256
+ - `raincheck-or-run` for deciding whether repeated schedule slips, delays, or "maybe later" loops are still genuine or already a stall pattern: `https://tells.voiddo.com/raincheck-or-run/?ref=message-next-step-readme`
257
+
258
+ ## License
259
+
260
+ MIT.
261
+
262
+ ---
263
+
264
+ Built by [vøiddo](https://voiddo.com/) — a small studio shipping AI-flavoured products, free dev tools, Chrome extensions and weird browser games.
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import { analyzeMessageNextStep, formatReport } from "../src/index.js";
5
+
6
+ function printHelp() {
7
+ console.log(`message-next-step
8
+
9
+ Usage:
10
+ message-next-step "text to analyze"
11
+ cat message.txt | message-next-step
12
+ message-next-step --file message.txt
13
+ message-next-step --json "Can we talk later?"
14
+ `);
15
+ }
16
+
17
+ function parseArgs(argv) {
18
+ const args = [...argv];
19
+ let json = false;
20
+ let file = null;
21
+ const textParts = [];
22
+
23
+ while (args.length) {
24
+ const arg = args.shift();
25
+ if (arg === "--help" || arg === "-h") {
26
+ return { help: true };
27
+ }
28
+ if (arg === "--json") {
29
+ json = true;
30
+ continue;
31
+ }
32
+ if (arg === "--file") {
33
+ file = args.shift();
34
+ continue;
35
+ }
36
+ textParts.push(arg);
37
+ }
38
+
39
+ return { help: false, json, file, text: textParts.join(" ").trim() };
40
+ }
41
+
42
+ async function readStdin() {
43
+ if (process.stdin.isTTY) {
44
+ return "";
45
+ }
46
+ return new Promise((resolve) => {
47
+ let data = "";
48
+ process.stdin.setEncoding("utf8");
49
+ process.stdin.on("data", (chunk) => {
50
+ data += chunk;
51
+ });
52
+ process.stdin.on("end", () => resolve(data.trim()));
53
+ });
54
+ }
55
+
56
+ async function main() {
57
+ const options = parseArgs(process.argv.slice(2));
58
+ if (options.help) {
59
+ printHelp();
60
+ process.exit(0);
61
+ }
62
+
63
+ let input = options.text;
64
+ if (options.file) {
65
+ input = fs.readFileSync(options.file, "utf8").trim();
66
+ }
67
+ if (!input) {
68
+ input = await readStdin();
69
+ }
70
+ if (!input) {
71
+ printHelp();
72
+ process.exit(1);
73
+ }
74
+
75
+ const result = analyzeMessageNextStep(input);
76
+ if (options.json) {
77
+ console.log(JSON.stringify(result, null, 2));
78
+ return;
79
+ }
80
+ console.log(formatReport(result));
81
+ }
82
+
83
+ main().catch((error) => {
84
+ console.error(`message-next-step: ${error.message}`);
85
+ process.exit(1);
86
+ });
@@ -0,0 +1,82 @@
1
+ # message-next-step vs ChatGPT or Gemini
2
+
3
+ `message-next-step` is not trying to replace a general-purpose model.
4
+
5
+ It is trying to win a narrower, high-frequency moment:
6
+
7
+ > "I already have the incoming message. I just need to know whether to reply now, wait, ask one clarifier, move to a call, or let it go."
8
+
9
+ That is where a deterministic checker beats a blank prompt box.
10
+
11
+ ## When message-next-step is the better first step
12
+
13
+ - You want a stable `reply now`, `wait`, `ask one question`, `move to a call`, or `let it go` call.
14
+ - You do not want to write prompts, add fake context, or compare three slightly different model answers.
15
+ - You are checking a dating, recruiter, client, support, family, or workplace thread where the immediate next move matters more than broad interpretation.
16
+ - You want the same message to score the same way every time.
17
+
18
+ ## When ChatGPT or Gemini is still useful
19
+
20
+ - You want rewrites, brainstorming, or roleplay.
21
+ - You want a broad assistant rather than a narrow next-step call.
22
+ - You need long-form drafting more than decision support.
23
+
24
+ ## The practical difference
25
+
26
+ | question | message-next-step | ChatGPT / Gemini |
27
+ | --- | --- | --- |
28
+ | "What should I do with this message right now?" | deterministic action + why | depends on prompt and model variance |
29
+ | "Can I re-check the same message later?" | yes, same input = same output | not reliably |
30
+ | "Do I need an account or API key?" | no | usually yes |
31
+ | "Can it rewrite five responses for me?" | no, narrow checker by design | yes |
32
+ | "Can it read the deeper recurring pattern behind this person?" | no, move into tells for that | not from one prompt alone |
33
+
34
+ ## What each tool is actually good at
35
+
36
+ - Use `message-next-step` when the immediate question is the next action on one incoming message and you want a stable `reply now`, `wait`, `ask one question`, `move to a call`, or `let it go` call.
37
+ - Use ChatGPT or Gemini when you already know the decision and want rewrite options, brainstorming, roleplay, or longer drafting help.
38
+ - Use `ambiguity-meter` when the main question is whether the message is genuinely clear, vague, or evasive before you decide what to do with it.
39
+ - Use `soft-yes-or-no` when the message sounds warm but still might be a polite stall instead of a real commitment.
40
+ - Use `replytone` when you already wrote the answer and want to know whether it sounds warm, clear, or pushy.
41
+ - Use `double-text-risk` when the timing of your next outbound follow-up matters more than the meaning of the current message.
42
+ - Use `call-not-text` when the real question is no longer the reply itself, but whether text is the wrong channel for the pressure, heat, or complexity in the thread.
43
+ - Use `raincheck-or-run` when the message is mainly another reschedule, delay, or "maybe later" loop and the decision is whether to rebook or walk away.
44
+ - Use `tells` when one message is not enough and the issue is the recurring person, loaded thread, or wider pattern over time.
45
+
46
+ ## Where tells fits
47
+
48
+ `message-next-step` is the free first door.
49
+
50
+ If the real problem is not one message but one recurring person, one loaded thread, or one pattern of pressure, avoidance, or mixed signals, the better next step is `tells`.
51
+
52
+ - `Deep Dive` — `$19 once` for one loaded case
53
+ - `Starter` — `$14.99/mo` for recurring message reads
54
+ - `Practitioner` — `$99.99/mo` for coaches, recruiters, mediators, trainers, and client-facing teams
55
+
56
+ Use the browser checker:
57
+
58
+ ```text
59
+ https://tells.voiddo.com/message-next-step/?ref=message-next-step-compare-md
60
+ ```
61
+
62
+ If you want the browser-side compare page first:
63
+
64
+ ```text
65
+ https://tells.voiddo.com/message-next-step/compare-chatgpt-gemini.html?ref=message-next-step-compare-md
66
+ ```
67
+
68
+ If one message is not enough:
69
+
70
+ ```text
71
+ https://tells.voiddo.com/deep-dive/?ref=message-next-step-compare-md
72
+ ```
73
+
74
+ If you want the wider free cluster first:
75
+
76
+ ```text
77
+ https://tells.voiddo.com/signal-toolkit/?ref=message-next-step-compare-md
78
+ ```
79
+
80
+ ---
81
+
82
+ Built by [vøiddo](https://voiddo.com/) — a small studio shipping AI-flavoured products, free dev tools, Chrome extensions and weird browser games.
@@ -0,0 +1,69 @@
1
+ # From the same studio
2
+
3
+ This package is one piece of [vøiddo](https://voiddo.com/), a small studio that ships AI-flavoured products, free dev tools, Chrome extensions and weird browser games.
4
+
5
+ ## Flagship path
6
+
7
+ | product | what it does |
8
+ |---|---|
9
+ | [**tells**](https://tells.voiddo.com/?ref=message-next-step-studio) | opinionated AI that reads what people do not say in messages, threads, and profiles |
10
+ | [**Deep Dive**](https://tells.voiddo.com/deep-dive/?ref=message-next-step-studio) | the fastest paid handoff when one message-level action call is not enough and the user needs the fuller thread or person read |
11
+ | [**Signal toolkit**](https://tells.voiddo.com/signal-toolkit/?ref=message-next-step-studio) | browser-first hub for the tells micro-tools when the user still wants a free/cheap checkpoint first |
12
+ | [**scrb**](https://scrb.voiddo.com/?ref=message-next-step-studio) | AI product description generator for Amazon, Etsy, Shopify, and eBay |
13
+ | [**rankd**](https://rankd.voiddo.com/?ref=message-next-step-studio) | opinionated cultural verdicts on music, books, and films |
14
+
15
+ ## Related free tools
16
+
17
+ | tool | one-line |
18
+ |---|---|
19
+ | [message-next-step vs ChatGPT/Gemini](https://tells.voiddo.com/message-next-step/compare-chatgpt-gemini.html?ref=message-next-step-studio) | comparison page for people deciding whether they need a deterministic next-step checker or a broader general AI assistant |
20
+ | [`message-next-step` packaged compare brief](compare-chatgpt-gemini.md) | npm/GitHub-ready acquisition brief for install-surface readers deciding whether they need a deterministic next-step checker or a broader general model |
21
+ | [replytone](https://tells.voiddo.com/replytone/?ref=message-next-step-studio) | score an outgoing draft for warmth, clarity, pressure, and urgency |
22
+ | [soft-yes-or-no](https://tells.voiddo.com/soft-yes-or-no/?ref=message-next-step-studio) | decide whether a warm-but-vague message is real interest, a soft no, or a polite stall |
23
+ | [ambiguity-meter](https://tells.voiddo.com/ambiguity-meter/?ref=message-next-step-studio) | check one incoming message for mixed signals and missing next steps |
24
+ | [double-text-risk](https://tells.voiddo.com/double-text-risk/?ref=message-next-step-studio) | score whether a follow-up is timely, needy, or better left unsent |
25
+ | [call-not-text](https://tells.voiddo.com/call-not-text/?ref=message-next-step-studio) | decide whether the thread is already too messy, heated, or compressed for more texting |
26
+ | [ghost-or-go](https://tells.voiddo.com/ghost-or-go/?ref=message-next-step-studio) | decide whether silence means wait, send one final ping, or close the loop |
27
+ | [raincheck-or-run](https://tells.voiddo.com/raincheck-or-run/?ref=message-next-step-studio) | decide whether repeated reschedules are still workable or already a stall pattern |
28
+ | [profile-red-flags](https://tells.voiddo.com/profile-red-flags/?ref=message-next-step-studio) | triage one profile for vagueness, pressure, cynicism, and effort |
29
+ | [All free tools](https://tools.voiddo.com/?ref=message-next-step-studio) | the wider vøiddo CLI and browser-tools catalogue |
30
+
31
+ ## When the free pass is not enough
32
+
33
+ `message-next-step` is meant to stop one bad reactive move before the user needs a fuller read of the thread or person.
34
+
35
+ That applies across dating, recruiter loops, workplace friction, client or support escalations, and family threads where the next move matters more than a broad interpretation.
36
+
37
+ The paid next steps are explicit:
38
+
39
+ - `Deep Dive` — `$19 once` for one loaded thread or one recurring person
40
+ - `Starter` — `$14.99/mo` for repeat message reads
41
+ - `Practitioner` — `$99.99/mo` for coach, recruiter, mediator, trainer, or client-facing team workflows
42
+
43
+ If the message-level answer is not enough, the best next stop is usually `Deep Dive`:
44
+
45
+ ```text
46
+ https://tells.voiddo.com/deep-dive/?ref=message-next-step-studio
47
+ ```
48
+
49
+ If the user still wants a wider browser-first free path before buying, send them here instead:
50
+
51
+ ```text
52
+ https://tells.voiddo.com/signal-toolkit/?ref=message-next-step-studio
53
+ ```
54
+
55
+ ## Best next free exits
56
+
57
+ - [replytone](https://tells.voiddo.com/replytone/?ref=message-next-step-studio) if the user already drafted the response and wants a warmth / clarity / pressure check before sending
58
+ - [double-text-risk](https://tells.voiddo.com/double-text-risk/?ref=message-next-step-studio) if the real blocker is follow-up timing instead of reply content
59
+ - [soft-yes-or-no](https://tells.voiddo.com/soft-yes-or-no/?ref=message-next-step-studio) if the incoming message sounds warm but still not concrete enough to treat as a real yes
60
+ - [ambiguity-meter](https://tells.voiddo.com/ambiguity-meter/?ref=message-next-step-studio) if the incoming wording is so vague that the user first needs an ambiguity read
61
+ - [call-not-text](https://tells.voiddo.com/call-not-text/?ref=message-next-step-studio) if the thread has outgrown text and needs a call / pause / channel switch decision
62
+ - [ghost-or-go](https://tells.voiddo.com/ghost-or-go/?ref=message-next-step-studio) if the next question becomes silence management after this message lands
63
+ - [raincheck-or-run](https://tells.voiddo.com/raincheck-or-run/?ref=message-next-step-studio) if the message is mostly another delay, cancellation, or no-new-time reschedule loop
64
+
65
+ Need support or a custom workflow? Contact `support@voiddo.com`.
66
+
67
+ ---
68
+
69
+ Built by [vøiddo](https://voiddo.com/) — a small studio shipping AI-flavoured products, free dev tools, Chrome extensions and weird browser games.
package/package.json ADDED
@@ -0,0 +1,117 @@
1
+ {
2
+ "name": "@v0idd0/message-next-step",
3
+ "version": "0.1.0",
4
+ "description": "message-next-step — free deterministic incoming-message checker and faster ChatGPT alternative for dating, workplace, recruiter, client, and family threads when you need to decide whether to reply now, wait, ask one question, move to a call, or let it go.",
5
+ "type": "module",
6
+ "main": "src/index.js",
7
+ "bin": {
8
+ "message-next-step": "./bin/message-next-step.js"
9
+ },
10
+ "scripts": {
11
+ "test": "node test.js",
12
+ "stage:site": "bash ./scripts/stage-site.sh"
13
+ },
14
+ "keywords": [
15
+ "message-next-step",
16
+ "next-step-checker",
17
+ "texting",
18
+ "message-analysis",
19
+ "incoming-message-analyzer",
20
+ "mixed-signals",
21
+ "incoming-message",
22
+ "incoming-text",
23
+ "reply-or-wait",
24
+ "wait-or-reply",
25
+ "should-i-reply",
26
+ "should-i-answer-this-message",
27
+ "should-i-respond-now",
28
+ "reply-checker",
29
+ "reply-decision",
30
+ "reply-now-or-wait",
31
+ "what-should-i-do-with-this-message",
32
+ "how-to-answer-this-text",
33
+ "follow-up-decision",
34
+ "follow-up-triage",
35
+ "how-to-respond",
36
+ "message-decision",
37
+ "message-triage",
38
+ "message-response-decision",
39
+ "chatgpt-alternative",
40
+ "gemini-alternative",
41
+ "ai-writing-alternative",
42
+ "email-reply-decision",
43
+ "incoming-email-next-step",
44
+ "recruiter-message",
45
+ "recruiter-follow-up",
46
+ "client-message",
47
+ "client-follow-up",
48
+ "support-escalation",
49
+ "support-reply",
50
+ "family-conflict",
51
+ "family-thread-next-step",
52
+ "move-to-call",
53
+ "move-this-to-a-call",
54
+ "clarifying-question",
55
+ "need-one-clarifying-question",
56
+ "support-thread-next-step",
57
+ "customer-thread-next-step",
58
+ "recruiter-next-step",
59
+ "client-next-step",
60
+ "email-next-step",
61
+ "reply-later-or-now",
62
+ "respond-or-wait",
63
+ "when-to-reply-to-a-text",
64
+ "when-to-reply-to-an-email",
65
+ "date-text-next-step",
66
+ "vague-text-next-step",
67
+ "mixed-signal-reply",
68
+ "reschedule-message-decision",
69
+ "message-escalation-checker",
70
+ "what-should-i-reply",
71
+ "what-do-i-do-with-this-text",
72
+ "what-do-i-do-with-this-email",
73
+ "should-i-call-instead-of-text",
74
+ "texting-anxiety",
75
+ "communication",
76
+ "relationships",
77
+ "dating",
78
+ "workplace",
79
+ "recruiting",
80
+ "customer-support",
81
+ "account-management",
82
+ "coaching",
83
+ "heuristics",
84
+ "cli",
85
+ "voiddo",
86
+ "tells"
87
+ ],
88
+ "author": "vøiddo <support@voiddo.com> (https://voiddo.com)",
89
+ "license": "MIT",
90
+ "homepage": "https://tells.voiddo.com/message-next-step/?ref=message-next-step-npm",
91
+ "repository": {
92
+ "type": "git",
93
+ "url": "git+https://github.com/voidd0/message-next-step.git"
94
+ },
95
+ "bugs": {
96
+ "url": "https://github.com/voidd0/message-next-step/issues",
97
+ "email": "support@voiddo.com"
98
+ },
99
+ "publishConfig": {
100
+ "access": "public"
101
+ },
102
+ "engines": {
103
+ "node": ">=18"
104
+ },
105
+ "files": [
106
+ "bin",
107
+ "src",
108
+ "README.md",
109
+ "compare-chatgpt-gemini.md",
110
+ "LICENSE",
111
+ "from-the-studio.md"
112
+ ],
113
+ "funding": {
114
+ "type": "individual",
115
+ "url": "https://voiddo.com/contact/"
116
+ }
117
+ }
package/src/index.js ADDED
@@ -0,0 +1,247 @@
1
+ const URGENCY_PHRASES = [
2
+ "asap",
3
+ "urgent",
4
+ "today",
5
+ "by tonight",
6
+ "right now",
7
+ "need an answer",
8
+ "let me know today",
9
+ "before friday",
10
+ "can you confirm"
11
+ ];
12
+
13
+ const DELAY_PHRASES = [
14
+ "maybe later",
15
+ "we'll see",
16
+ "not right now",
17
+ "another time",
18
+ "sometime",
19
+ "for now",
20
+ "eventually",
21
+ "when things calm down"
22
+ ];
23
+
24
+ const SPECIFICITY_PHRASES = [
25
+ "tomorrow",
26
+ "friday",
27
+ "monday",
28
+ "confirm by",
29
+ "at 3",
30
+ "call at",
31
+ "meet at",
32
+ "send me",
33
+ "i can do",
34
+ "let's talk",
35
+ "reply by",
36
+ "next week"
37
+ ];
38
+
39
+ const CALL_PHRASES = [
40
+ "call me",
41
+ "can we talk",
42
+ "let's talk",
43
+ "jump on a call",
44
+ "phone call",
45
+ "talk later",
46
+ "need to talk"
47
+ ];
48
+
49
+ const BLAME_PHRASES = [
50
+ "you're overthinking",
51
+ "you always do this",
52
+ "don't start",
53
+ "calm down",
54
+ "that's not what i meant",
55
+ "why are you making this about you",
56
+ "you're making this harder"
57
+ ];
58
+
59
+ const SOFTENERS = [
60
+ "thanks",
61
+ "appreciate",
62
+ "glad",
63
+ "happy to",
64
+ "makes sense",
65
+ "understand"
66
+ ];
67
+
68
+ function clamp(value, min = 0, max = 10) {
69
+ return Math.max(min, Math.min(max, value));
70
+ }
71
+
72
+ function round1(value) {
73
+ return Math.round(value * 10) / 10;
74
+ }
75
+
76
+ function countMatches(text, phrases) {
77
+ return phrases.reduce((count, phrase) => {
78
+ const escaped = phrase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
79
+ const regex = new RegExp(`\\b${escaped}\\b`, "gi");
80
+ const hits = text.match(regex);
81
+ return count + (hits ? hits.length : 0);
82
+ }, 0);
83
+ }
84
+
85
+ function buildDecision(metrics, counters) {
86
+ const reasons = [];
87
+ let action = "ask one concrete question";
88
+ let summary =
89
+ "The message is not clear enough on timing or intent to justify filling the blanks with optimism.";
90
+
91
+ if (
92
+ metrics.escalation >= 6 ||
93
+ (metrics.callShift >= 5.5 && metrics.pressure >= 5) ||
94
+ (metrics.callShift >= 4.5 && counters.exclamations >= 2)
95
+ ) {
96
+ action = "move to a call";
97
+ summary =
98
+ "The text is carrying enough heat or compression that another round of texting will probably distort it further.";
99
+ reasons.push("Escalation is high enough that text is the wrong bandwidth.");
100
+ } else if (metrics.directness >= 6.5 && metrics.pressure <= 5 && metrics.ambiguity <= 4.5) {
101
+ action = "reply now";
102
+ summary = "The text names a usable next step without much ambiguity.";
103
+ reasons.push("There is enough concrete signal to answer directly.");
104
+ } else if (metrics.avoidance >= 5 && metrics.pressure <= 4.5) {
105
+ action = "wait";
106
+ summary = "The message leans toward delay rather than a clean request. Pushing now is unlikely to improve clarity.";
107
+ reasons.push("Delay language is stronger than commitment language.");
108
+ } else if (metrics.blame >= 6.5 && metrics.directness <= 4.5) {
109
+ action = "let it go";
110
+ summary = "The text shifts the frame onto your reaction without offering much constructive direction back.";
111
+ reasons.push("Blame or minimising language is doing more work than the actual issue.");
112
+ } else {
113
+ reasons.push("One concrete follow-up will surface whether the sender has a real next step or not.");
114
+ }
115
+
116
+ if (metrics.ambiguity >= 5) {
117
+ reasons.push("Timing or intent stays unresolved in the current wording.");
118
+ }
119
+ if (metrics.pressure >= 6) {
120
+ reasons.push("The message carries more force than detail.");
121
+ }
122
+ if (counters.questions >= 3) {
123
+ reasons.push("Stacked questions increase friction without adding clarity.");
124
+ }
125
+
126
+ return {
127
+ action,
128
+ summary,
129
+ reasons: [...new Set(reasons)].slice(0, 3)
130
+ };
131
+ }
132
+
133
+ function buildFollowUpPrompts(metrics) {
134
+ const prompts = [];
135
+
136
+ if (metrics.ambiguity >= 5) {
137
+ prompts.push("What exactly are you asking me to decide or confirm?");
138
+ }
139
+ if (metrics.directness < 5.5) {
140
+ prompts.push("What timing are you actually proposing?");
141
+ }
142
+ if (metrics.pressure >= 6) {
143
+ prompts.push("I can answer this better on a call than in fragments here.");
144
+ }
145
+ if (metrics.avoidance >= 5) {
146
+ prompts.push("If now is not the right time, when should I revisit this?");
147
+ }
148
+
149
+ return [...new Set(prompts)].slice(0, 3);
150
+ }
151
+
152
+ export function analyzeMessageNextStep(input) {
153
+ const text = String(input || "").trim();
154
+ if (!text) {
155
+ throw new Error("message-next-step needs message text");
156
+ }
157
+
158
+ const lower = text.toLowerCase();
159
+ const exclamations = (text.match(/!/g) || []).length;
160
+ const questions = (text.match(/\?/g) || []).length;
161
+ const uppercaseWords = (text.match(/\b[A-Z]{3,}\b/g) || []).length;
162
+ const words = text.split(/\s+/).filter(Boolean);
163
+ const sentences = Math.max(
164
+ 1,
165
+ text.split(/[.!?]+/).map((part) => part.trim()).filter(Boolean).length
166
+ );
167
+
168
+ const urgencyHits = countMatches(lower, URGENCY_PHRASES);
169
+ const delayHits = countMatches(lower, DELAY_PHRASES);
170
+ const specificityHits = countMatches(lower, SPECIFICITY_PHRASES);
171
+ const callHits = countMatches(lower, CALL_PHRASES);
172
+ const blameHits = countMatches(lower, BLAME_PHRASES);
173
+ const softenerHits = countMatches(lower, SOFTENERS);
174
+
175
+ const directness = clamp(4 + specificityHits * 1.35 + urgencyHits * 0.8 - delayHits * 0.6);
176
+ const ambiguity = clamp(3 + delayHits * 1.25 - specificityHits * 0.5);
177
+ const pressure = clamp(2 + urgencyHits * 1.25 + blameHits * 0.8 + uppercaseWords * 0.9 + exclamations * 0.45 - softenerHits * 0.35);
178
+ const escalation = clamp(1 + blameHits * 1.8 + callHits * 0.9 + exclamations * 0.8 + uppercaseWords * 0.9);
179
+ const avoidance = clamp(1 + delayHits * 1.35 - specificityHits * 0.25);
180
+ const callShift = clamp(1 + callHits * 2.4 + Math.max(0, escalation - 5) * 0.45);
181
+
182
+ const metrics = {
183
+ directness: round1(directness),
184
+ ambiguity: round1(ambiguity),
185
+ pressure: round1(pressure),
186
+ escalation: round1(escalation),
187
+ avoidance: round1(avoidance),
188
+ callShift: round1(callShift)
189
+ };
190
+
191
+ let posture = "mixed";
192
+ if (metrics.directness >= 6.5 && metrics.ambiguity <= 4.5) {
193
+ posture = "direct";
194
+ } else if (metrics.avoidance >= 5) {
195
+ posture = "avoidant";
196
+ } else if (metrics.escalation >= 6 || metrics.callShift >= 5.5) {
197
+ posture = "heated";
198
+ }
199
+
200
+ return {
201
+ posture,
202
+ metrics,
203
+ decision: buildDecision(metrics, { questions, exclamations }),
204
+ followUps: buildFollowUpPrompts(metrics),
205
+ counters: {
206
+ words: words.length,
207
+ sentences,
208
+ questions
209
+ }
210
+ };
211
+ }
212
+
213
+ export function formatReport(result) {
214
+ const lines = [
215
+ `posture: ${result.posture}`,
216
+ `directness: ${result.metrics.directness}/10`,
217
+ `ambiguity: ${result.metrics.ambiguity}/10`,
218
+ `pressure: ${result.metrics.pressure}/10`,
219
+ `escalation: ${result.metrics.escalation}/10`,
220
+ `avoidance: ${result.metrics.avoidance}/10`,
221
+ `call shift: ${result.metrics.callShift}/10`,
222
+ "",
223
+ `next step: ${result.decision.action}`,
224
+ `why: ${result.decision.summary}`
225
+ ];
226
+
227
+ for (const reason of result.decision.reasons) {
228
+ lines.push(`- ${reason}`);
229
+ }
230
+
231
+ if (result.followUps.length) {
232
+ lines.push("", "follow-up prompts:");
233
+ result.followUps.forEach((prompt, index) => {
234
+ lines.push(`${index + 1}. ${prompt}`);
235
+ });
236
+ }
237
+
238
+ lines.push(
239
+ "",
240
+ "next:",
241
+ "Use tells when one message is not enough and you need the thread, the person, or the pattern over time.",
242
+ "- quick next paid step: https://tells.voiddo.com/deep-dive/?ref=message-next-step-cli",
243
+ "- recurring reads: https://tells.voiddo.com/?ref=message-next-step-cli"
244
+ );
245
+
246
+ return lines.join("\n");
247
+ }