opencode-skills-collection 4.1.7 → 4.1.9
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.
Potentially problematic release.
This version of opencode-skills-collection might be problematic. Click here for more details.
- package/bundled-skills/.antigravity-install-manifest.json +7 -1
- package/bundled-skills/court/SKILL.md +141 -0
- package/bundled-skills/court/references/jurors.json +14 -0
- package/bundled-skills/court/scripts/court.py +348 -0
- package/bundled-skills/darkmoon-pentest/SKILL.md +123 -0
- package/bundled-skills/lognorm/SKILL.md +86 -0
- package/bundled-skills/lognorm/references/playbooks.md +76 -0
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +6 -6
- package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package.json +1 -1
- package/bundled-skills/parallel-search-mcp/SKILL.md +12 -0
- package/bundled-skills/scarf-single-cell/SKILL.md +326 -0
- package/bundled-skills/scarf-single-cell/references/LICENSE.md +29 -0
- package/bundled-skills/scarf-single-cell/references/clustering-and-embedding.md +270 -0
- package/bundled-skills/scarf-single-cell/references/data-access.md +269 -0
- package/bundled-skills/scarf-single-cell/references/features-and-graphs.md +279 -0
- package/bundled-skills/scarf-single-cell/references/gene-blacklists.md +182 -0
- package/bundled-skills/scarf-single-cell/references/integration-and-comparisons.md +295 -0
- package/bundled-skills/scarf-single-cell/references/markers-and-annotation.md +280 -0
- package/bundled-skills/scarf-single-cell/references/performance-and-export.md +291 -0
- package/bundled-skills/scarf-single-cell/references/pipeline-runs-and-artifacts.md +273 -0
- package/bundled-skills/scarf-single-cell/references/plotting.md +260 -0
- package/bundled-skills/scarf-single-cell/references/quality-control.md +290 -0
- package/bundled-skills/session-handoff/SKILL.md +112 -0
- package/bundled-skills/shark/SKILL.md +134 -0
- package/bundled-skills/shark/references/investors.json +12 -0
- package/bundled-skills/shark/scripts/shark.py +302 -0
- package/bundled-skills/telegram/assets/boilerplate/nodejs/package-lock.json +6 -6
- package/bundled-skills/telegram/assets/boilerplate/nodejs/package.json +1 -1
- package/bundled-skills/whatsapp-cloud-api/assets/boilerplate/nodejs/package-lock.json +6 -6
- package/bundled-skills/whatsapp-cloud-api/assets/boilerplate/nodejs/package.json +1 -1
- package/package.json +1 -1
- package/skills_index.json +341 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: shark
|
|
3
|
+
description: "Pitch a business idea to a Shark Tank-style panel of Claude sub-agent investors who grill it, hear a founder agent answer, and each go IN or OUT with a practice offer."
|
|
4
|
+
category: business
|
|
5
|
+
risk: safe
|
|
6
|
+
source: community
|
|
7
|
+
source_repo: alexyc9381/shark-skill
|
|
8
|
+
source_type: community
|
|
9
|
+
date_added: "2026-10-03"
|
|
10
|
+
author: "Alex Chen (@nocodealex)"
|
|
11
|
+
tags: [business, startup, pitch, investors, subagents, feedback]
|
|
12
|
+
tools: [claude]
|
|
13
|
+
license: "MIT"
|
|
14
|
+
license_source: "https://github.com/alexyc9381/shark-skill/blob/main/LICENSE"
|
|
15
|
+
argument-hint: "[--investors N] [--seed S] <the business idea>"
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# shark
|
|
19
|
+
|
|
20
|
+
For when Claude says every business idea is great. Instead of one friendly answer, the idea goes in
|
|
21
|
+
front of a Shark Tank-style panel of Claude sharks who want to say no. You are the host. You never invest, never
|
|
22
|
+
answer for the founder yourself, and never soften a decision.
|
|
23
|
+
|
|
24
|
+
What the user typed after `/shark`: `$ARGUMENTS`
|
|
25
|
+
|
|
26
|
+
If that is blank, or still reads like a placeholder, take the idea from the conversation.
|
|
27
|
+
|
|
28
|
+
## When to Use This Skill
|
|
29
|
+
|
|
30
|
+
- Use when the user wants honest feedback on a business, product or side-hustle idea instead of agreement.
|
|
31
|
+
- Use when the user asks "would investors fund this?" or wants to practice a pitch before a real meeting.
|
|
32
|
+
- Use when the user says "pitch", "shark tank" or "/shark".
|
|
33
|
+
|
|
34
|
+
## The tool
|
|
35
|
+
|
|
36
|
+
Every piece of bookkeeping goes through `scripts/shark.py` in this skill's folder:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
python3 "${CLAUDE_SKILL_DIR}/scripts/shark.py" <command>
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Below, `SHARK` means exactly that command. If the path looks unexpanded, use the "Base directory for
|
|
43
|
+
this skill" that Claude Code printed at the top of this skill. The state lives in
|
|
44
|
+
`.shark/<run>/state.json` in the current directory, and every command after `init` finds it through
|
|
45
|
+
`.shark/LATEST`.
|
|
46
|
+
|
|
47
|
+
## Step 1: size it
|
|
48
|
+
|
|
49
|
+
| flag | meaning |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| `--investors N` | 2 to 10 sharks. Default 5. |
|
|
52
|
+
| `--seed S` | Picks a random panel from the 10 sharks, reproducibly. |
|
|
53
|
+
|
|
54
|
+
Run `SHARK plan` with the same flags and tell the user in one line how big the session is, for example
|
|
55
|
+
"5 investors, 11 sub-agent calls", then start. If this skill fired on its own, ask once first.
|
|
56
|
+
|
|
57
|
+
Sub-agents write into `.shark/` in the current directory. Suggest accept-edits mode (Shift+Tab) for
|
|
58
|
+
the session. Do not change the user's settings yourself.
|
|
59
|
+
|
|
60
|
+
## Step 2: write the pitch file
|
|
61
|
+
|
|
62
|
+
**Sub-agents cannot see this conversation.** The investors and the founder know only what is in the
|
|
63
|
+
pitch file, so write `.shark/pitch.md` to stand on its own:
|
|
64
|
+
|
|
65
|
+
- First line: the idea in one sentence, in the user's words ("A subscription box of local hot sauces").
|
|
66
|
+
- Then everything the user told you: price, costs, customers so far, who it is for, how they will
|
|
67
|
+
find customers, what they need the money for, timeline.
|
|
68
|
+
- Do not add numbers, traction or plans the user never gave, and do not write your own opinion of the
|
|
69
|
+
idea into it. Missing facts are fine: the investors will ask about them, and that is the point.
|
|
70
|
+
|
|
71
|
+
## Step 3: open the session
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
SHARK init --pitch-file .shark/pitch.md [--investors N] [--seed S]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Step 4: the panel
|
|
78
|
+
|
|
79
|
+
Always drive it with `SHARK next`. The phases run in this order: `grill` (every investor writes
|
|
80
|
+
their three hardest questions), `answers` (one founder sub-agent answers them all, only from the
|
|
81
|
+
pitch file), `decide` (every investor goes IN or OUT).
|
|
82
|
+
|
|
83
|
+
Every phase works the same way:
|
|
84
|
+
|
|
85
|
+
1. `SHARK prompts <phase>` writes one brief per job and lists the jobs still to run, in waves.
|
|
86
|
+
2. Launch **one wave at a time**: a single message with one Agent tool call per job in that wave (the
|
|
87
|
+
Agent tool is called Task in older Claude Code versions). Each call is:
|
|
88
|
+
- `subagent_type`: `general-purpose`
|
|
89
|
+
- `description`: `shark <job id>`
|
|
90
|
+
- `prompt`: `Read <brief path> and follow it exactly. It is your whole brief.`
|
|
91
|
+
|
|
92
|
+
Wait until every agent in the wave has replied before you launch the next wave.
|
|
93
|
+
3. After the last wave, run `SHARK next`. If an output is missing, re-run those jobs once. If one
|
|
94
|
+
fails twice, write the single line `NO DECISION` into its file and move on. Never write a
|
|
95
|
+
question, an answer or a decision yourself.
|
|
96
|
+
|
|
97
|
+
## Step 5: the board
|
|
98
|
+
|
|
99
|
+
When `SHARK next` says the panel has decided:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
SHARK render
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
It counts the deals, writes `BOARD.md` into the run folder and prints it. Show the user the headline,
|
|
106
|
+
the panel table, "What would change their minds" and the homework list exactly as written. Then add
|
|
107
|
+
at most two lines of your own: the one piece of homework you would do first, and the path to the full
|
|
108
|
+
session.
|
|
109
|
+
|
|
110
|
+
Rules for you, the host:
|
|
111
|
+
|
|
112
|
+
- Never change, soften or re-count a decision. "No deal" is useful information.
|
|
113
|
+
- Never add praise the panel did not give.
|
|
114
|
+
- The offers are practice numbers from language models, not real money and not financial advice. Say
|
|
115
|
+
so once if the user treats an offer as a valuation.
|
|
116
|
+
- If the user wants another round, write their new answers into the pitch file and run a new session.
|
|
117
|
+
|
|
118
|
+
## Examples
|
|
119
|
+
|
|
120
|
+
```text
|
|
121
|
+
/shark A subscription box of local hot sauces. $35 a month, 12 customers so far, I want to reach 500.
|
|
122
|
+
/shark --investors 10 [paste your pitch deck text]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Limitations
|
|
126
|
+
|
|
127
|
+
- The investors are language models and the offers are practice numbers, not real money and not financial advice.
|
|
128
|
+
- The founder agent answers only from what the user wrote, so a thin pitch produces a long homework list rather than a fair verdict.
|
|
129
|
+
- Five investors is 11 sub-agent calls; each one counts toward the user's Claude plan usage.
|
|
130
|
+
- Runs in Claude Code (it spawns sub-agents and calls its script through `${CLAUDE_SKILL_DIR}`).
|
|
131
|
+
|
|
132
|
+
## Source
|
|
133
|
+
|
|
134
|
+
By Alex Chen ([@nocodealex](https://instagram.com/nocodealex)). Original repo with tests and a real example run: https://github.com/alexyc9381/shark-skill
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[
|
|
2
|
+
{"name": "The Tech Billionaire Shark", "signature": "Is this a business or a hobby?", "lens": "You made your money in tech and you are blunt. Your first question is always who is actually going to pay for this. Vague answers annoy you, and you say so."},
|
|
3
|
+
{"name": "The Royalty Shark", "signature": "I want a piece of every sale.", "lens": "You only make one kind of offer: money now in exchange for a set amount on every sale, until you have been paid back several times over. You care about getting your money back, not about being liked."},
|
|
4
|
+
{"name": "The Customer Shark", "signature": "Would I actually buy this?", "lens": "You are the person this is for. Would you pay for it, how much, how often, and what would you stop using to make room for it?"},
|
|
5
|
+
{"name": "The Numbers Shark", "signature": "Show me the numbers.", "lens": "You only trust math. Price, cost per sale, margin, how long until it pays back. If the pitch has no numbers, that is your first question."},
|
|
6
|
+
{"name": "The Skeptic Shark", "signature": "I've seen this movie before.", "lens": "You have seen this exact idea fail before. Your job is to find the one thing that kills it."},
|
|
7
|
+
{"name": "The Market Shark", "signature": "Who else is already doing this?", "lens": "You care about how many people want this, who already sells it, and why now. You have watched crowded markets eat founders alive."},
|
|
8
|
+
{"name": "The Operator Shark", "signature": "Who does the work on Monday?", "lens": "You have built and run companies. Who does the work, what breaks at ten times the size, and what is the slowest, most expensive part of delivering this?"},
|
|
9
|
+
{"name": "The Brand Shark", "signature": "Why would anyone tell a friend?", "lens": "You back things people talk about. Why would anyone tell a friend about this, and why would they pick it over a cheaper copy?"},
|
|
10
|
+
{"name": "The Legal Shark", "signature": "That could get you shut down.", "lens": "You look for regulation, licenses, liability and platform rules that could shut this down overnight."},
|
|
11
|
+
{"name": "The Long-Game Shark", "signature": "Where is this in ten years?", "lens": "You think in ten years. Does this get stronger as it grows, or easier to copy?"}
|
|
12
|
+
]
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Bookkeeping for the shark skill: pitch your idea to a Shark Tank-style panel of Claude sharks who want to say no.
|
|
3
|
+
|
|
4
|
+
The skill's orchestrator (Claude) drives the session with these commands. Every sub-agent reads a
|
|
5
|
+
brief this script writes and writes one output file; this script checks the files, counts the deals
|
|
6
|
+
and renders the board. All state lives in .shark/<run>/ in the current directory.
|
|
7
|
+
|
|
8
|
+
python3 shark.py plan [--investors N]
|
|
9
|
+
python3 shark.py init --pitch-file PATH [--investors N] [--seed S]
|
|
10
|
+
python3 shark.py next
|
|
11
|
+
python3 shark.py prompts <phase>
|
|
12
|
+
python3 shark.py check <phase>
|
|
13
|
+
python3 shark.py tally
|
|
14
|
+
python3 shark.py render
|
|
15
|
+
|
|
16
|
+
Phases, in order: grill, answers, decide.
|
|
17
|
+
"""
|
|
18
|
+
import argparse
|
|
19
|
+
import json
|
|
20
|
+
import os
|
|
21
|
+
import random
|
|
22
|
+
import re
|
|
23
|
+
import sys
|
|
24
|
+
import time
|
|
25
|
+
|
|
26
|
+
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
27
|
+
ROOT = ".shark"
|
|
28
|
+
LATEST = os.path.join(ROOT, "LATEST")
|
|
29
|
+
PHASES = ["grill", "answers", "decide"]
|
|
30
|
+
DEFAULT_INVESTORS = 5
|
|
31
|
+
WAVE = 5
|
|
32
|
+
CREDIT = ("_Made with [/shark](https://github.com/alexyc9381/shark-skill), a free Claude Code skill by Alex Chen ([@nocodealex](https://instagram.com/nocodealex))._")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def load_investors():
|
|
36
|
+
with open(os.path.join(HERE, "..", "references", "investors.json")) as f:
|
|
37
|
+
return json.load(f)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def run_dir():
|
|
41
|
+
if not os.path.exists(LATEST):
|
|
42
|
+
sys.exit("shark: no session yet. Run: shark.py init --pitch-file .shark/pitch.md")
|
|
43
|
+
with open(LATEST) as f:
|
|
44
|
+
return f.read().strip()
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def load_state(rd=None):
|
|
48
|
+
rd = rd or run_dir()
|
|
49
|
+
with open(os.path.join(rd, "state.json")) as f:
|
|
50
|
+
return json.load(f)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def save_state(state):
|
|
54
|
+
with open(os.path.join(state["dir"], "state.json"), "w") as f:
|
|
55
|
+
json.dump(state, f, indent=2)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def panel_size(args):
|
|
59
|
+
n = getattr(args, "investors", None) or DEFAULT_INVESTORS
|
|
60
|
+
if n < 2 or n > len(load_investors()):
|
|
61
|
+
sys.exit("shark: the panel must be 2 to %d investors" % len(load_investors()))
|
|
62
|
+
return n
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def jobs(state, phase):
|
|
66
|
+
d = state["dir"]
|
|
67
|
+
if phase in ("grill", "decide"):
|
|
68
|
+
ids = ["investor-%d" % (i + 1) for i in range(len(state["panel"]))]
|
|
69
|
+
elif phase == "answers":
|
|
70
|
+
ids = ["founder"]
|
|
71
|
+
else:
|
|
72
|
+
sys.exit("shark: unknown phase %r (phases: %s)" % (phase, ", ".join(PHASES)))
|
|
73
|
+
return [{"id": jid,
|
|
74
|
+
"brief": os.path.abspath(os.path.join(d, "briefs", "%s-%s.md" % (phase, jid))),
|
|
75
|
+
"output": os.path.abspath(os.path.join(d, phase, "%s.md" % jid))} for jid in ids]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def done(job):
|
|
79
|
+
p = job["output"]
|
|
80
|
+
return os.path.exists(p) and os.path.getsize(p) > 0
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def missing(state, phase):
|
|
84
|
+
return [j for j in jobs(state, phase) if not done(j)]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _abs(state, *parts):
|
|
88
|
+
return os.path.abspath(os.path.join(state["dir"], *parts))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def brief_text(state, phase, job):
|
|
92
|
+
pitch = _abs(state, "pitch.md")
|
|
93
|
+
head = ("You are one part of a practice investor panel run by the shark skill. You cannot see the "
|
|
94
|
+
"user's conversation: everything you know is in the files named below. Never invent facts, "
|
|
95
|
+
"numbers or traction the pitch does not give. Write your answer to exactly this file and "
|
|
96
|
+
"nothing else:\n\n %s\n\nThe pitch: %s\n\n" % (job["output"], pitch))
|
|
97
|
+
if phase in ("grill", "decide"):
|
|
98
|
+
idx = int(job["id"].split("-")[1]) - 1
|
|
99
|
+
inv = state["panel"][idx]
|
|
100
|
+
who = ("You are %s, shark %d of %d on a Shark Tank-style panel. %s Your signature line is \"%s\": use it "
|
|
101
|
+
"once, where it fits naturally.\n\nYou have heard a thousand pitches and you want to say no. You only "
|
|
102
|
+
"say yes when the founder's answers remove your biggest worry. Being nice is not your job; being right "
|
|
103
|
+
"with your money is. Talk like a sharp, blunt investor on TV, never like a polite assistant.\n\n"
|
|
104
|
+
% (inv["name"], idx + 1, len(state["panel"]), inv["lens"], inv.get("signature", "")))
|
|
105
|
+
if phase == "grill":
|
|
106
|
+
return head + who + (
|
|
107
|
+
"Read the pitch. Then write:\n\n"
|
|
108
|
+
"BIGGEST WORRY: one sentence\n"
|
|
109
|
+
"QUESTIONS:\n1. ...\n2. ...\n3. ...\n\n"
|
|
110
|
+
"Three questions, the hardest ones you would really ask, each about something specific in "
|
|
111
|
+
"the pitch or something it leaves out. 120 words at most.\n")
|
|
112
|
+
return head + who + (
|
|
113
|
+
"Read the pitch, your own questions in %s, and the founder's answers in %s.\n\n"
|
|
114
|
+
"Decide. Write exactly these lines and nothing else:\n\n"
|
|
115
|
+
"DECISION: IN or OUT\n"
|
|
116
|
+
"OFFER: if IN, an amount for a share of the company, for example \"$50,000 for 10%%\"; if OUT, "
|
|
117
|
+
"write none\n"
|
|
118
|
+
"REASON: one sentence, in your own voice. If you are OUT, end it with the words: For that reason, "
|
|
119
|
+
"I'm out.\n"
|
|
120
|
+
"WHAT WOULD CHANGE MY MIND: one specific thing the founder could show you\n"
|
|
121
|
+
% (job["output"].replace("/decide/", "/grill/"), _abs(state, "answers", "founder.md")))
|
|
122
|
+
if phase == "answers":
|
|
123
|
+
qs = "\n".join(" " + j["output"] for j in jobs(state, "grill"))
|
|
124
|
+
return head + (
|
|
125
|
+
"You are the FOUNDER, speaking for the user. Read the pitch, then every investor's questions:\n\n"
|
|
126
|
+
"%s\n\n"
|
|
127
|
+
"Answer every question, grouped by investor, in the order asked. Answer only from what the "
|
|
128
|
+
"pitch says. When the pitch does not contain the answer, write \"NOT ANSWERED: the founder needs "
|
|
129
|
+
"to find out ...\" and say exactly what to find out. Never make up numbers, customers or "
|
|
130
|
+
"plans. 450 words at most.\n" % qs)
|
|
131
|
+
raise ValueError(phase)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
DECISION_RE = re.compile(r"^\s*\**DECISION\**\s*:\s*\**\s*(IN|OUT)\b", re.I | re.M)
|
|
135
|
+
FIELD_RE = r"^\s*\**%s\**\s*:\s*(.+)$"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def parse_decision(text):
|
|
139
|
+
m = DECISION_RE.search(text or "")
|
|
140
|
+
if not m:
|
|
141
|
+
return None
|
|
142
|
+
out = {"decision": m.group(1).upper()}
|
|
143
|
+
for key, name in (("offer", "OFFER"), ("reason", "REASON"), ("change", "WHAT WOULD CHANGE MY MIND")):
|
|
144
|
+
f = re.search(FIELD_RE % name, text, re.I | re.M)
|
|
145
|
+
out[key] = f.group(1).strip().strip("*").strip() if f else ""
|
|
146
|
+
if out["decision"] == "OUT":
|
|
147
|
+
out["offer"] = "none"
|
|
148
|
+
return out
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def count_not_answered(state):
|
|
152
|
+
a = jobs(state, "answers")[0]
|
|
153
|
+
if not done(a):
|
|
154
|
+
return []
|
|
155
|
+
lines = open(a["output"]).read().splitlines()
|
|
156
|
+
return [l.split("NOT ANSWERED:", 1)[1].strip() for l in lines if "NOT ANSWERED:" in l]
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def tally(state):
|
|
160
|
+
rows = []
|
|
161
|
+
for j, inv in zip(jobs(state, "decide"), state["panel"]):
|
|
162
|
+
d = parse_decision(open(j["output"]).read()) if done(j) else None
|
|
163
|
+
rows.append({"investor": inv["name"], "decision": d["decision"] if d else "NO DECISION",
|
|
164
|
+
"offer": d["offer"] if d else "", "reason": d["reason"] if d else "",
|
|
165
|
+
"change": d["change"] if d else ""})
|
|
166
|
+
ins = sum(1 for r in rows if r["decision"] == "IN")
|
|
167
|
+
result = {"in": ins, "out": sum(1 for r in rows if r["decision"] == "OUT"), "panel": len(rows),
|
|
168
|
+
"investors": rows, "homework": count_not_answered(state)}
|
|
169
|
+
with open(os.path.join(state["dir"], "tally.json"), "w") as f:
|
|
170
|
+
json.dump(result, f, indent=2)
|
|
171
|
+
return result
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def headline(t):
|
|
175
|
+
if t["in"] == 0:
|
|
176
|
+
return "No deal: all %d sharks are out" % t["panel"]
|
|
177
|
+
return "%d of %d sharks are in" % (t["in"], t["panel"])
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def render(state):
|
|
181
|
+
t = tally(state)
|
|
182
|
+
pitch = open(os.path.join(state["dir"], "pitch.md")).read().strip()
|
|
183
|
+
first = pitch.splitlines()[0].lstrip("# ").strip() if pitch else "(empty pitch)"
|
|
184
|
+
lines = ["# %s" % headline(t), "", "**The pitch:** %s" % first, "",
|
|
185
|
+
"## The sharks", "", "| Shark | Decision | Offer | Why |", "|---|---|---|---|"]
|
|
186
|
+
for r in t["investors"]:
|
|
187
|
+
lines.append("| %s | %s | %s | %s |" % (r["investor"], r["decision"], r["offer"].replace("|", "/"),
|
|
188
|
+
r["reason"].replace("|", "/")))
|
|
189
|
+
lines += ["", "## What would change their minds", ""]
|
|
190
|
+
for r in t["investors"]:
|
|
191
|
+
if r["change"]:
|
|
192
|
+
lines.append("- **%s:** %s" % (r["investor"], r["change"]))
|
|
193
|
+
if t["homework"]:
|
|
194
|
+
lines += ["", "## Your homework: questions you could not answer", ""]
|
|
195
|
+
lines += ["- %s" % (h[:1].upper() + h[1:]) for h in t["homework"]]
|
|
196
|
+
lines += ["", "_The full session is in %s: every investor's questions, the answers and their decisions. "
|
|
197
|
+
"The investors are language models and the offers are practice, not real money._"
|
|
198
|
+
% os.path.abspath(state["dir"]), "", CREDIT]
|
|
199
|
+
text = "\n".join(lines) + "\n"
|
|
200
|
+
with open(os.path.join(state["dir"], "BOARD.md"), "w") as f:
|
|
201
|
+
f.write(text)
|
|
202
|
+
return text
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def cmd_plan(args):
|
|
206
|
+
n = panel_size(args)
|
|
207
|
+
print("panel of %d: %d sets of questions, 1 founder answering, %d decisions = %d sub-agent calls"
|
|
208
|
+
% (n, n, n, 2 * n + 1))
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def cmd_init(args):
|
|
212
|
+
n = panel_size(args)
|
|
213
|
+
if not os.path.exists(args.pitch_file):
|
|
214
|
+
sys.exit("shark: pitch file not found: %s" % args.pitch_file)
|
|
215
|
+
pitch = open(args.pitch_file).read().strip()
|
|
216
|
+
if not pitch:
|
|
217
|
+
sys.exit("shark: the pitch file is empty")
|
|
218
|
+
seed = args.seed if args.seed is not None else random.randrange(1, 10 ** 6)
|
|
219
|
+
investors = load_investors()
|
|
220
|
+
panel = investors[:n] if args.seed is None and n == DEFAULT_INVESTORS else \
|
|
221
|
+
random.Random(seed).sample(investors, n)
|
|
222
|
+
rd = os.path.join(ROOT, time.strftime("%Y%m%d-%H%M%S"))
|
|
223
|
+
for sub in ("briefs", "grill", "answers", "decide"):
|
|
224
|
+
os.makedirs(os.path.join(rd, sub), exist_ok=True)
|
|
225
|
+
with open(os.path.join(rd, "pitch.md"), "w") as f:
|
|
226
|
+
f.write(pitch + "\n")
|
|
227
|
+
state = {"dir": rd, "seed": seed, "panel": panel, "created": time.strftime("%Y-%m-%dT%H:%M:%S")}
|
|
228
|
+
save_state(state)
|
|
229
|
+
with open(LATEST, "w") as f:
|
|
230
|
+
f.write(rd)
|
|
231
|
+
print("shark: session opened in %s (%d investors, seed %d)" % (rd, n, seed))
|
|
232
|
+
print("next: python3 scripts/shark.py prompts grill")
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def cmd_prompts(args):
|
|
236
|
+
state = load_state()
|
|
237
|
+
todo = missing(state, args.phase)
|
|
238
|
+
for j in jobs(state, args.phase):
|
|
239
|
+
with open(j["brief"], "w") as f:
|
|
240
|
+
f.write(brief_text(state, args.phase, j))
|
|
241
|
+
if not todo:
|
|
242
|
+
print("shark: %s is complete. Run: shark.py next" % args.phase)
|
|
243
|
+
return
|
|
244
|
+
for w in range(0, len(todo), WAVE):
|
|
245
|
+
print("wave %d:" % (w // WAVE + 1))
|
|
246
|
+
for j in todo[w:w + WAVE]:
|
|
247
|
+
print(" %s brief=%s" % (j["id"], j["brief"]))
|
|
248
|
+
print("Launch one Agent call per job, one wave at a time. Prompt: "
|
|
249
|
+
"\"Read <brief> and follow it exactly. It is your whole brief.\"")
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
def cmd_check(args):
|
|
253
|
+
state = load_state()
|
|
254
|
+
todo = missing(state, args.phase)
|
|
255
|
+
if not todo:
|
|
256
|
+
print("shark: %s complete" % args.phase)
|
|
257
|
+
for j in todo:
|
|
258
|
+
print("missing: %s -> %s" % (j["id"], j["output"]))
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def cmd_next(args):
|
|
262
|
+
state = load_state()
|
|
263
|
+
for phase in PHASES:
|
|
264
|
+
if missing(state, phase):
|
|
265
|
+
print("next phase: %s. Run: python3 shark.py prompts %s" % (phase, phase))
|
|
266
|
+
return
|
|
267
|
+
print("shark: the panel has decided. Run: python3 shark.py render")
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def cmd_tally(args):
|
|
271
|
+
print(headline(tally(load_state())))
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def cmd_render(args):
|
|
275
|
+
sys.stdout.write(render(load_state()))
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def main(argv=None):
|
|
279
|
+
p = argparse.ArgumentParser(prog="shark.py")
|
|
280
|
+
sub = p.add_subparsers(dest="cmd")
|
|
281
|
+
pl = sub.add_parser("plan")
|
|
282
|
+
pl.add_argument("--investors", type=int)
|
|
283
|
+
pi = sub.add_parser("init")
|
|
284
|
+
pi.add_argument("--pitch-file", required=True)
|
|
285
|
+
pi.add_argument("--investors", type=int)
|
|
286
|
+
pi.add_argument("--seed", type=int)
|
|
287
|
+
for name in ("prompts", "check"):
|
|
288
|
+
sp = sub.add_parser(name)
|
|
289
|
+
sp.add_argument("phase", choices=PHASES)
|
|
290
|
+
for name in ("next", "tally", "render"):
|
|
291
|
+
sub.add_parser(name)
|
|
292
|
+
args = p.parse_args(argv)
|
|
293
|
+
if not args.cmd:
|
|
294
|
+
p.print_help()
|
|
295
|
+
return 1
|
|
296
|
+
{"plan": cmd_plan, "init": cmd_init, "prompts": cmd_prompts, "check": cmd_check,
|
|
297
|
+
"next": cmd_next, "tally": cmd_tally, "render": cmd_render}[args.cmd](args)
|
|
298
|
+
return 0
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
if __name__ == "__main__":
|
|
302
|
+
sys.exit(main())
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"version": "1.0.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"dotenv": "^16.4.0",
|
|
12
|
-
"express": "^4.
|
|
12
|
+
"express": "^4.22.3",
|
|
13
13
|
"telegraf": "^4.16.3"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
@@ -658,9 +658,9 @@
|
|
|
658
658
|
}
|
|
659
659
|
},
|
|
660
660
|
"node_modules/express": {
|
|
661
|
-
"version": "4.22.
|
|
662
|
-
"resolved": "https://registry.npmjs.org/express/-/express-4.22.
|
|
663
|
-
"integrity": "sha512-
|
|
661
|
+
"version": "4.22.3",
|
|
662
|
+
"resolved": "https://registry.npmjs.org/express/-/express-4.22.3.tgz",
|
|
663
|
+
"integrity": "sha512-Bdcs4+3qlpVlx2NRn6fgX2Ue2/gGRaPeawebgclM0ERSCqDpA+owF1fdPwjJUTAJWMTuAaxjDf+hzb0/4eKvvw==",
|
|
664
664
|
"license": "MIT",
|
|
665
665
|
"dependencies": {
|
|
666
666
|
"accepts": "~1.3.8",
|
|
@@ -682,9 +682,9 @@
|
|
|
682
682
|
"methods": "~1.1.2",
|
|
683
683
|
"on-finished": "~2.4.1",
|
|
684
684
|
"parseurl": "~1.3.3",
|
|
685
|
-
"path-to-regexp": "~0.1.
|
|
685
|
+
"path-to-regexp": "~0.1.13",
|
|
686
686
|
"proxy-addr": "~2.0.7",
|
|
687
|
-
"qs": "~6.
|
|
687
|
+
"qs": "~6.16.0",
|
|
688
688
|
"range-parser": "~1.2.1",
|
|
689
689
|
"safe-buffer": "5.2.1",
|
|
690
690
|
"send": "~0.19.0",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"axios": "^1.7.0",
|
|
12
12
|
"dotenv": "^16.4.0",
|
|
13
|
-
"express": "^4.
|
|
13
|
+
"express": "^4.22.3"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/express": "^5.0.0",
|
|
@@ -678,9 +678,9 @@
|
|
|
678
678
|
}
|
|
679
679
|
},
|
|
680
680
|
"node_modules/express": {
|
|
681
|
-
"version": "4.22.
|
|
682
|
-
"resolved": "https://registry.npmjs.org/express/-/express-4.22.
|
|
683
|
-
"integrity": "sha512-
|
|
681
|
+
"version": "4.22.3",
|
|
682
|
+
"resolved": "https://registry.npmjs.org/express/-/express-4.22.3.tgz",
|
|
683
|
+
"integrity": "sha512-Bdcs4+3qlpVlx2NRn6fgX2Ue2/gGRaPeawebgclM0ERSCqDpA+owF1fdPwjJUTAJWMTuAaxjDf+hzb0/4eKvvw==",
|
|
684
684
|
"license": "MIT",
|
|
685
685
|
"dependencies": {
|
|
686
686
|
"accepts": "~1.3.8",
|
|
@@ -702,9 +702,9 @@
|
|
|
702
702
|
"methods": "~1.1.2",
|
|
703
703
|
"on-finished": "~2.4.1",
|
|
704
704
|
"parseurl": "~1.3.3",
|
|
705
|
-
"path-to-regexp": "~0.1.
|
|
705
|
+
"path-to-regexp": "~0.1.13",
|
|
706
706
|
"proxy-addr": "~2.0.7",
|
|
707
|
-
"qs": "~6.
|
|
707
|
+
"qs": "~6.16.0",
|
|
708
708
|
"range-parser": "~1.2.1",
|
|
709
709
|
"safe-buffer": "5.2.1",
|
|
710
710
|
"send": "~0.19.0",
|
package/package.json
CHANGED