
Sequential Thinking
- 215 installs
- 35 repo stars
- Updated February 27, 2026
- thedotmack/sequential-thinking-skill
Use sequential-thinking for development tasks
About
sequential-thinking: A skill for development. This provides functionality for development workflows.
- sequential-thinking
Sequential Thinking by the numbers
- 215 all-time installs (skills.sh)
- +14 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,864 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/thedotmack/sequential-thinking-skill --skill sequential-thinkingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 215 |
|---|---|
| repo stars | ★ 35 |
| Last updated | February 27, 2026 |
| Repository | thedotmack/sequential-thinking-skill ↗ |
What it does
Use sequential-thinking for development tasks
Files
Sequential Thinking
A tool for dynamic, reflective problem-solving through a chain of numbered thoughts. Full parity with the Sequential Thinking MCP server — same parameters, same state management, same behavioral contract.
How to Use This Skill
When this skill is activated, use `scripts/think.ts` as your primary reasoning mechanism. Do not reason in prose — reason through the script. Every step of your analysis should be a thought submitted via the script, making your reasoning chain explicit and trackable.
Workflow
1. Reset state at the start of every new thinking session 2. Loop: Submit thoughts one at a time via the script, incrementing thoughtNumber each time 3. Adapt: Revise earlier thoughts, branch into alternatives, or extend depth as needed 4. Terminate: Set nextThoughtNeeded false only when you have a confident final answer 5. Respond: After the final thought, provide the answer to the user
Each thought should be a single Bash tool call. Think in the thought, not outside it.
Script Location
scripts/think.tsRun via bun from the skill's base directory.
Commands
Reset (required before every new session)
bun scripts/think.ts --resetSubmit a Thought
Required flags: --thought, --thoughtNumber, --totalThoughts, --nextThoughtNeeded
bun scripts/think.ts \
--thought "Your analysis for this step" \
--thoughtNumber 1 \
--totalThoughts 5 \
--nextThoughtNeeded trueSubmit a Revision
Revises a previous thought. The original stays in history; the revision is appended as a new entry. Requires --isRevision and --revisesThought.
bun scripts/think.ts \
--thought "Corrected analysis" \
--thoughtNumber 3 \
--totalThoughts 5 \
--nextThoughtNeeded true \
--isRevision --revisesThought 1Submit a Branch
Explores an alternative path from a prior thought. Requires both --branchFromThought and --branchId.
bun scripts/think.ts \
--thought "Alternative approach" \
--thoughtNumber 4 \
--totalThoughts 7 \
--nextThoughtNeeded true \
--branchFromThought 2 --branchId alt-approachExtend Depth
Signal that more thoughts are needed beyond the original estimate.
bun scripts/think.ts \
--thought "Scope is larger than expected" \
--thoughtNumber 6 \
--totalThoughts 8 \
--nextThoughtNeeded true \
--needsMoreThoughtsInspect Full State
bun scripts/think.ts --statusReturns JSON with fullHistory and branchDetails.
Output Format
Each thought invocation prints the thought to stderr and a compact status line to stdout:
💭 Thought 3/7
The analysis shows that...
[3/7] history=3 next=trueRevision and branch thoughts use 🔄 Revision and 🌿 Branch headers respectively.
Parameters (Full MCP Parity)
| Parameter | Type | Required | Description |
|---|---|---|---|
--thought | string | yes | The content of this thinking step |
--thoughtNumber | int >= 1 | yes | Current thought number in the sequence |
--totalThoughts | int >= 1 | yes | Estimated total thoughts needed (adjustable) |
--nextThoughtNeeded | bool | yes | true to continue, false to terminate |
--isRevision | flag | no | Marks this thought as revising a previous one |
--revisesThought | int >= 1 | no | Which thought number is being revised (required with --isRevision) |
--branchFromThought | int >= 1 | no | Create a branch starting from this thought number |
--branchId | string | no | Label for the branch (required with --branchFromThought) |
--needsMoreThoughts | flag | no | Signal that totalThoughts should be expanded |
Behavioral Rules
These match the MCP server's tool description exactly:
1. Start with an initial estimate of totalThoughts, but adjust freely as understanding deepens 2. Auto-adjust: If thoughtNumber exceeds totalThoughts, the script raises totalThoughts to match 3. Revise previous thoughts when you realize an earlier step was wrong or incomplete — set --isRevision and --revisesThought N. The original stays in history; the revision is appended 4. Branch to explore alternative reasoning paths — set both --branchFromThought N and --branchId label. This does not abandon the main line 5. Extend beyond the initial estimate at any time with --needsMoreThoughts and an increased --totalThoughts 6. Express uncertainty — not every thought needs confidence. Questioning and exploring is encouraged 7. Filter noise — ignore information irrelevant to the current step 8. Generate hypotheses when you have enough evidence, then verify them against prior thoughts in the chain 9. Iterate hypothesis-verification cycles until satisfied with the answer 10. Terminate only when you have a satisfactory answer — set --nextThoughtNeeded false 11. Non-linear paths are first-class — branching, backtracking, and revision are features, not failures
State Management
- State is persisted to
scripts/.think_state.jsonbetween invocations thoughtHistory[]is append-only — thoughts are never deletedbranches{}maps branch IDs to their thought arrays--resetclears all state for a fresh session--statusdumps the full state as JSON for inspection
Example Session
See references/example-session.md for a complete worked example demonstrating normal thoughts, revisions, branches, and dynamic depth adjustment.
Example Session: "What is the optimal strategy for a 3-player auction?"
Thought 1 — Initial framing
💭 Thought 1/5
First, I need to clarify what type of auction we're dealing with.
Common types: English (ascending), Dutch (descending), sealed-bid
first-price, sealed-bid second-price (Vickrey). The optimal strategy
differs significantly by type. I'll assume a sealed-bid first-price
auction unless told otherwise.
[1/5] history=1 next=trueThought 2 — Core analysis
💭 Thought 2/5
In a sealed-bid first-price auction with 3 players and independent
private values uniformly distributed on [0,1], the symmetric Bayesian
Nash equilibrium bidding strategy is b(v) = v * (n-1)/n = v * 2/3.
Each player shades their bid below their true valuation.
[2/5] history=2 next=trueThought 3 — Revision of thought 1
🔄 Revision 3/5 (revising thought 1)
Wait — I assumed sealed-bid first-price, but the question says
"auction" generically. I should cover the dominant strategy for
the most common types rather than assuming one. This changes my
approach: I need to address multiple formats.
[3/5] history=3 next=trueThought 4 — Branching to explore Vickrey auction
totalThoughts adjusted upward from 5 to 7 because the revision revealed more ground to cover.
🌿 Branch 4/7 (from thought 2, ID: vickrey)
In a Vickrey (second-price sealed-bid) auction, the dominant strategy
is to bid your true valuation regardless of the number of players.
With 3 players, each bids v. The winner pays the second-highest bid.
This is strategy-proof: no player benefits from deviating.
[4/7] history=4 branches=vickrey next=trueThought 5 — Branching to explore English auction
🌿 Branch 5/7 (from thought 2, ID: english)
In an English (ascending) auction with 3 players, the dominant
strategy is to stay in the bidding until the price reaches your
valuation, then drop out. The winner is the last remaining bidder,
paying just above the second-highest valuation. Strategically
equivalent to the Vickrey auction.
[5/7] history=5 branches=vickrey,english next=trueThought 6 — Hypothesis generation
💭 Thought 6/7
Hypothesis: The optimal strategy depends entirely on auction format.
For 3 players: First-price sealed → bid 2/3 of value.
Vickrey/English → bid true value. Dutch → equivalent to first-price.
Let me verify this against the revenue equivalence theorem before
concluding.
[6/7] history=6 branches=vickrey,english next=trueThought 7 — Verification and final answer
💭 Thought 7/7
Verification: Revenue equivalence theorem confirms all four standard
auction formats yield the same expected revenue under symmetric
independent private values. The strategies:
• First-price sealed / Dutch: bid (n-1)/n × value = 2/3v
• Second-price sealed / English: bid true value = v
Both are equilibrium strategies. The 3-player case doesn't introduce
special considerations beyond the general n-player formulas.
Final answer confirmed.
[7/7] history=7 branches=vickrey,english next=falseKey Features Demonstrated
- Dynamic depth: Started at 5, expanded to 7 when scope grew
- Revision: Thought 3 reconsidered the assumption made in thought 1
- Branching: Thoughts 4 and 5 explored alternative auction types from thought 2
- Hypothesis + verification: Thought 6 proposed, thought 7 verified
- Clean termination:
nextThoughtNeeded: falseonly after confident answer
#!/usr/bin/env bun
/**
* Sequential thinking state machine.
*
* Maintains thoughtHistory and branches as persistent state across invocations.
* Returns structured status after each thought, exactly mirroring the MCP server.
*
* Usage:
* # Submit a thought
* tsx think.ts --thought "analysis here" --thoughtNumber 1 --totalThoughts 5 --nextThoughtNeeded true
*
* # Submit a revision
* tsx think.ts --thought "revised" --thoughtNumber 3 --totalThoughts 5 --nextThoughtNeeded true --isRevision --revisesThought 1
*
* # Submit a branch
* tsx think.ts --thought "alt path" --thoughtNumber 4 --totalThoughts 7 --nextThoughtNeeded true --branchFromThought 2 --branchId alt-approach
*
* # View current state
* tsx think.ts --status
*
* # Reset state for a new session
* tsx think.ts --reset
*/
import { readFileSync, writeFileSync, existsSync, unlinkSync } from "fs";
import { join, dirname } from "path";
import { fileURLToPath } from "url";
import { parseArgs } from "util";
const __dirname = dirname(fileURLToPath(import.meta.url));
const STATE_FILE = join(__dirname, ".think_state.json");
interface ThoughtData {
thought: string;
thoughtNumber: number;
totalThoughts: number;
nextThoughtNeeded: boolean;
isRevision?: boolean;
revisesThought?: number;
branchFromThought?: number;
branchId?: string;
needsMoreThoughts?: boolean;
}
interface State {
thoughtHistory: ThoughtData[];
branches: Record<string, ThoughtData[]>;
}
function loadState(): State {
if (existsSync(STATE_FILE)) {
return JSON.parse(readFileSync(STATE_FILE, "utf-8"));
}
return { thoughtHistory: [], branches: {} };
}
function saveState(state: State): void {
writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
}
function formatThought(t: ThoughtData): string {
let header: string;
if (t.isRevision && t.revisesThought != null) {
header = `🔄 Revision ${t.thoughtNumber}/${t.totalThoughts} (revising thought ${t.revisesThought})`;
} else if (t.branchFromThought != null && t.branchId != null) {
header = `🌿 Branch ${t.thoughtNumber}/${t.totalThoughts} (from thought ${t.branchFromThought}, ID: ${t.branchId})`;
} else {
header = `💭 Thought ${t.thoughtNumber}/${t.totalThoughts}`;
}
return `${header}\n${t.thought}`;
}
function makeStatusResponse(state: State) {
const branchIds = Object.keys(state.branches);
const historyLength = state.thoughtHistory.length;
if (historyLength === 0) {
return {
thoughtNumber: 0,
totalThoughts: 0,
nextThoughtNeeded: true,
branches: branchIds,
thoughtHistoryLength: historyLength,
};
}
const latest = state.thoughtHistory[historyLength - 1];
return {
thoughtNumber: latest.thoughtNumber,
totalThoughts: latest.totalThoughts,
nextThoughtNeeded: latest.nextThoughtNeeded,
branches: branchIds,
thoughtHistoryLength: historyLength,
};
}
function fail(message: string): never {
console.error(`Error: ${message}`);
process.exit(1);
}
// --- Parse CLI args ---
const { values } = parseArgs({
options: {
thought: { type: "string" },
thoughtNumber: { type: "string" },
totalThoughts: { type: "string" },
nextThoughtNeeded: { type: "string" },
isRevision: { type: "boolean", default: false },
revisesThought: { type: "string" },
branchFromThought: { type: "string" },
branchId: { type: "string" },
needsMoreThoughts: { type: "boolean", default: false },
status: { type: "boolean", default: false },
reset: { type: "boolean", default: false },
},
strict: true,
});
// --- Commands ---
if (values.reset) {
if (existsSync(STATE_FILE)) unlinkSync(STATE_FILE);
console.log(JSON.stringify({ status: "reset", message: "Thinking session cleared" }, null, 2));
process.exit(0);
}
const state = loadState();
if (values.status) {
const response = {
...makeStatusResponse(state),
fullHistory: state.thoughtHistory,
branchDetails: state.branches,
};
console.log(JSON.stringify(response, null, 2));
process.exit(0);
}
// --- Validate required fields ---
if (!values.thought) fail("--thought is required");
if (!values.thoughtNumber) fail("--thoughtNumber is required");
if (!values.totalThoughts) fail("--totalThoughts is required");
if (!values.nextThoughtNeeded) fail("--nextThoughtNeeded is required");
const thoughtNumber = parseInt(values.thoughtNumber, 10);
let totalThoughts = parseInt(values.totalThoughts, 10);
const nextThoughtNeeded = values.nextThoughtNeeded.toLowerCase() === "true";
if (isNaN(thoughtNumber) || thoughtNumber < 1) fail("--thoughtNumber must be an integer >= 1");
if (isNaN(totalThoughts) || totalThoughts < 1) fail("--totalThoughts must be an integer >= 1");
// Auto-adjust
if (thoughtNumber > totalThoughts) {
totalThoughts = thoughtNumber;
}
const thoughtData: ThoughtData = {
thought: values.thought,
thoughtNumber,
totalThoughts,
nextThoughtNeeded,
};
if (values.isRevision) {
if (!values.revisesThought) fail("--revisesThought is required when --isRevision is set");
const revisesThought = parseInt(values.revisesThought, 10);
if (isNaN(revisesThought) || revisesThought < 1) fail("--revisesThought must be an integer >= 1");
thoughtData.isRevision = true;
thoughtData.revisesThought = revisesThought;
}
if (values.branchFromThought != null) {
if (!values.branchId) fail("--branchId is required when --branchFromThought is set");
const branchFrom = parseInt(values.branchFromThought, 10);
if (isNaN(branchFrom) || branchFrom < 1) fail("--branchFromThought must be an integer >= 1");
thoughtData.branchFromThought = branchFrom;
thoughtData.branchId = values.branchId;
}
if (values.needsMoreThoughts) {
thoughtData.needsMoreThoughts = true;
}
// --- Append to history (never delete, only append) ---
state.thoughtHistory.push(thoughtData);
// --- Track branches ---
if (thoughtData.branchFromThought != null && thoughtData.branchId != null) {
if (!state.branches[thoughtData.branchId]) {
state.branches[thoughtData.branchId] = [];
}
state.branches[thoughtData.branchId].push(thoughtData);
}
saveState(state);
// Formatted thought → stderr (visual)
console.error(formatThought(thoughtData));
// Structured JSON → stdout (machine-readable)
const status = makeStatusResponse(state);
const branchList = status.branches.length > 0 ? ` branches=${status.branches.join(",")}` : "";
console.log(`[${status.thoughtNumber}/${status.totalThoughts}] history=${status.thoughtHistoryLength}${branchList} next=${status.nextThoughtNeeded}`);