@ishlabs/cli 0.8.1 → 0.8.2
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/README.md +323 -21
- package/dist/auth.d.ts +17 -1
- package/dist/auth.js +62 -9
- package/dist/commands/ask.d.ts +5 -0
- package/dist/commands/ask.js +722 -0
- package/dist/commands/config.js +25 -1
- package/dist/commands/docs.d.ts +17 -0
- package/dist/commands/docs.js +147 -0
- package/dist/commands/init.d.ts +16 -0
- package/dist/commands/init.js +182 -0
- package/dist/commands/iteration.d.ts +5 -1
- package/dist/commands/iteration.js +243 -31
- package/dist/commands/profile.d.ts +5 -0
- package/dist/commands/profile.js +313 -0
- package/dist/commands/source.d.ts +10 -0
- package/dist/commands/source.js +78 -0
- package/dist/commands/study-run.d.ts +11 -0
- package/dist/commands/study-run.js +552 -0
- package/dist/commands/study-tester.d.ts +8 -0
- package/dist/commands/study-tester.js +149 -0
- package/dist/commands/study.js +145 -70
- package/dist/commands/workspace.js +193 -7
- package/dist/config.d.ts +3 -1
- package/dist/config.js +10 -10
- package/dist/connect.d.ts +4 -1
- package/dist/connect.js +127 -94
- package/dist/index.js +82 -34
- package/dist/lib/alias-store.d.ts +3 -0
- package/dist/lib/alias-store.js +9 -7
- package/dist/lib/api-client.d.ts +9 -6
- package/dist/lib/api-client.js +87 -26
- package/dist/lib/ask-questions.d.ts +9 -0
- package/dist/lib/ask-questions.js +35 -0
- package/dist/lib/ask-variants.d.ts +48 -0
- package/dist/lib/ask-variants.js +236 -0
- package/dist/lib/auth.d.ts +1 -1
- package/dist/lib/auth.js +24 -8
- package/dist/lib/colors.d.ts +30 -0
- package/dist/lib/colors.js +48 -0
- package/dist/lib/command-helpers.d.ts +74 -0
- package/dist/lib/command-helpers.js +232 -6
- package/dist/lib/docs.d.ts +32 -0
- package/dist/lib/docs.js +930 -0
- package/dist/lib/local-sim/browser.d.ts +0 -1
- package/dist/lib/local-sim/browser.js +0 -2
- package/dist/lib/local-sim/install.d.ts +4 -7
- package/dist/lib/local-sim/install.js +6 -21
- package/dist/lib/output.d.ts +25 -3
- package/dist/lib/output.js +465 -20
- package/dist/lib/paths.d.ts +14 -0
- package/dist/lib/paths.js +36 -0
- package/dist/lib/profile-sources.d.ts +55 -0
- package/dist/lib/profile-sources.js +157 -0
- package/dist/lib/site-access.d.ts +80 -0
- package/dist/lib/site-access.js +188 -0
- package/dist/lib/skill-content.d.ts +31 -0
- package/dist/lib/skill-content.js +462 -0
- package/dist/lib/study-inputs.d.ts +20 -0
- package/dist/lib/study-inputs.js +72 -0
- package/dist/lib/types.d.ts +207 -9
- package/dist/lib/types.js +7 -0
- package/dist/lib/upload.js +2 -2
- package/dist/upgrade.js +11 -1
- package/package.json +1 -1
- package/dist/commands/simulation.d.ts +0 -10
- package/dist/commands/simulation.js +0 -647
- package/dist/commands/tester-profile.d.ts +0 -5
- package/dist/commands/tester-profile.js +0 -109
- package/dist/commands/tester.d.ts +0 -5
- package/dist/commands/tester.js +0 -73
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Skill content for the `ish` CLI.
|
|
3
|
+
*
|
|
4
|
+
* Shipped inline as TypeScript constants so the same content is available
|
|
5
|
+
* in both `tsc`-compiled npm installs and `bun build --compile` single
|
|
6
|
+
* binaries (which don't bundle markdown files outside of TS imports).
|
|
7
|
+
*
|
|
8
|
+
* Spec: https://agentskills.io/specification (April 2026).
|
|
9
|
+
*
|
|
10
|
+
* Distribution model: `ish init` materialises this content as a real
|
|
11
|
+
* directory tree at the path the user picks. Native consumers:
|
|
12
|
+
* - Claude Code .claude/skills/ish/
|
|
13
|
+
* - Codex / Cursor / .agents/skills/ish/
|
|
14
|
+
* Cline / Roo Code
|
|
15
|
+
*/
|
|
16
|
+
import pkg from "../../package.json" with { type: "json" };
|
|
17
|
+
const VERSION = pkg.version;
|
|
18
|
+
/**
|
|
19
|
+
* SKILL.md description is the primary trigger mechanism — it's the only
|
|
20
|
+
* field always loaded into agent context (the body is loaded on match).
|
|
21
|
+
*
|
|
22
|
+
* Anthropic's authoring guidance: skills under-trigger by default; pack
|
|
23
|
+
* the description with verbs the user is likely to say plus the noun
|
|
24
|
+
* "ish". Hard cap is 1024 chars. Front-load the use case.
|
|
25
|
+
*/
|
|
26
|
+
const SKILL_DESCRIPTION = "Use this skill whenever the user mentions ish, a study, a tester profile, " +
|
|
27
|
+
"a simulation run, an \"ask\", an audience, or wants to dispatch tests against AI testers. " +
|
|
28
|
+
"Wraps the `ish` CLI for managing studies, asks, iterations, tester profiles, and simulation " +
|
|
29
|
+
"runs against the Ish platform. Always start by running `ish docs overview` to load the " +
|
|
30
|
+
"domain model, then `ish docs list` and `ish docs get-page <slug>` for specifics. Prefer " +
|
|
31
|
+
"this skill over guessing flags from `ish --help`.";
|
|
32
|
+
const SKILL_BODY = `# ish
|
|
33
|
+
|
|
34
|
+
A CLI for the Ish platform — run user-research studies and quick "ask"
|
|
35
|
+
reactions against AI tester audiences. The CLI is the agent surface;
|
|
36
|
+
this skill teaches you how to use it without re-reading its docs every
|
|
37
|
+
time.
|
|
38
|
+
|
|
39
|
+
## When to invoke this skill
|
|
40
|
+
|
|
41
|
+
The user mentioned any of: \`ish\`, a study, a tester profile,
|
|
42
|
+
a tester source, a simulation run, an iteration, an "ask", an audience,
|
|
43
|
+
or wants to dispatch tests against AI testers. Also invoke if the user
|
|
44
|
+
asks to "run a study", "generate testers", "compare variants", "test a
|
|
45
|
+
prototype with users", or similar.
|
|
46
|
+
|
|
47
|
+
## First step, every time: load the mental model
|
|
48
|
+
|
|
49
|
+
Before producing any \`ish\` command, run:
|
|
50
|
+
|
|
51
|
+
\`\`\`bash
|
|
52
|
+
ish docs overview
|
|
53
|
+
\`\`\`
|
|
54
|
+
|
|
55
|
+
This prints a one-page mental model (workspace → study | ask → testers
|
|
56
|
+
→ results) and lists every concept page available offline. The model is
|
|
57
|
+
non-obvious — *do not* skip this step the first time the user asks for
|
|
58
|
+
anything ish-related in a session.
|
|
59
|
+
|
|
60
|
+
If you need detail on a specific concept:
|
|
61
|
+
|
|
62
|
+
\`\`\`bash
|
|
63
|
+
ish docs list # every page available
|
|
64
|
+
ish docs get-page concepts/study # one page, full markdown
|
|
65
|
+
ish docs get-page concepts/run-verbs # study run vs ask run
|
|
66
|
+
ish docs search "<keyword>" # ranked hits with snippets
|
|
67
|
+
\`\`\`
|
|
68
|
+
|
|
69
|
+
The pages \`ish docs\` exposes are the source of truth — newer than this
|
|
70
|
+
skill file. **Trust \`ish docs\` over anything in this skill if they
|
|
71
|
+
conflict.**
|
|
72
|
+
|
|
73
|
+
## Quick orientation (one-screen)
|
|
74
|
+
|
|
75
|
+
\`\`\`
|
|
76
|
+
Workspace (= product)
|
|
77
|
+
├── Tester Profiles (tp-…) reusable audience personas
|
|
78
|
+
│ └── Sources (tps-…) transcripts/audio/images that seed generation
|
|
79
|
+
├── Study (s-…) persistent research artifact
|
|
80
|
+
│ ├── modality interactive | text | video | audio | image | document
|
|
81
|
+
│ ├── assignments tasks the tester does
|
|
82
|
+
│ ├── questionnaire questions the tester answers
|
|
83
|
+
│ └── Iterations (i-…) one configured run; carries the URL or media
|
|
84
|
+
│ └── Testers (t-…) instance of a profile in this iteration
|
|
85
|
+
└── Ask (a-…) lightweight reaction artifact
|
|
86
|
+
└── Rounds unit of execution; audience fixed at ask creation
|
|
87
|
+
\`\`\`
|
|
88
|
+
|
|
89
|
+
Two run verbs:
|
|
90
|
+
- \`ish study run\` — dispatches simulations on the latest iteration of a study.
|
|
91
|
+
- \`ish ask run\` — appends a round to an ask (or \`--new\` to create one).
|
|
92
|
+
|
|
93
|
+
Use **study** when the tester must *do* something on a real surface;
|
|
94
|
+
use **ask** for quick reactions to text/image variants.
|
|
95
|
+
|
|
96
|
+
## High-frequency commands
|
|
97
|
+
|
|
98
|
+
\`\`\`bash
|
|
99
|
+
# Auth & active selection (saved to ~/.ish/config.json)
|
|
100
|
+
ish login
|
|
101
|
+
ish workspace use w-6ec
|
|
102
|
+
ish study use s-b2c
|
|
103
|
+
ish ask use a-6ec
|
|
104
|
+
|
|
105
|
+
# Inspect
|
|
106
|
+
ish workspace list
|
|
107
|
+
ish study list
|
|
108
|
+
ish iteration list --study s-b2c
|
|
109
|
+
ish ask list
|
|
110
|
+
|
|
111
|
+
# Define / configure
|
|
112
|
+
ish study create --name "..." --modality interactive --assignment "..." --question "..."
|
|
113
|
+
ish iteration create --url https://example.com
|
|
114
|
+
ish profile generate --description "..." --count 5
|
|
115
|
+
|
|
116
|
+
# Run
|
|
117
|
+
ish study run --sample 5 --country SE --wait
|
|
118
|
+
ish ask run --new --name "..." --prompt "..." --variant text:"A" --variant text:"B" --sample 30 --wants-pick --wait
|
|
119
|
+
|
|
120
|
+
# Results
|
|
121
|
+
ish study results
|
|
122
|
+
ish ask results a-6ec --round 1
|
|
123
|
+
|
|
124
|
+
# Read offline docs
|
|
125
|
+
ish docs overview
|
|
126
|
+
ish docs get-page <slug>
|
|
127
|
+
ish docs search <query>
|
|
128
|
+
\`\`\`
|
|
129
|
+
|
|
130
|
+
## Common workflows (worked examples)
|
|
131
|
+
|
|
132
|
+
See \`references/workflows.md\` in this skill for end-to-end transcripts:
|
|
133
|
+
- First study from zero (auth → workspace → audience → study → iteration → run → results)
|
|
134
|
+
- Quick A/B ask with image variants
|
|
135
|
+
- Generating profiles from a transcript or audio source
|
|
136
|
+
- Targeting a gated URL (basic auth, session cookie, login form)
|
|
137
|
+
- Re-running a study with a fresh audience
|
|
138
|
+
|
|
139
|
+
## Output handling
|
|
140
|
+
|
|
141
|
+
- Every command supports \`--json\`. JSON mode is **auto-enabled when
|
|
142
|
+
stdout is piped**, so an agent rarely needs \`--json\` explicitly.
|
|
143
|
+
- \`--fields a,b,c\` strips JSON output to the listed fields (saves
|
|
144
|
+
tokens). \`--verbose\` adds full UUIDs and timestamps.
|
|
145
|
+
- Exit codes carry meaning: 0 success, 2 usage/validation,
|
|
146
|
+
3 auth, 4 not-found, 5 transient. See
|
|
147
|
+
\`ish docs get-page reference/json-mode\`.
|
|
148
|
+
- Aliases (\`s-…\`, \`a-…\`, \`tp-…\`, \`i-…\`, \`t-…\`, \`tps-…\`, \`w-…\`)
|
|
149
|
+
are accepted anywhere a UUID is. See
|
|
150
|
+
\`ish docs get-page reference/aliases\`.
|
|
151
|
+
|
|
152
|
+
## Common pitfalls (don't do these)
|
|
153
|
+
|
|
154
|
+
1. **Don't paste flags from memory.** The CLI evolves; flags change.
|
|
155
|
+
Run \`ish <command> --help\` to confirm before constructing a command.
|
|
156
|
+
2. **Don't run \`ish study run\` before an iteration exists** — create
|
|
157
|
+
one first via \`ish iteration create --url …\` (or \`--content-url …\`
|
|
158
|
+
for media studies). The error message tells you this, but the round
|
|
159
|
+
trip wastes time.
|
|
160
|
+
3. **Don't pass \`--profile\` together with demographic filters** — they
|
|
161
|
+
are mutually exclusive. Either explicit IDs or
|
|
162
|
+
\`--country\`/\`--gender\`/\`--min-age\`/\`--max-age\` + \`--sample\`.
|
|
163
|
+
4. **Don't change audience between rounds of an ask.** It's fixed at
|
|
164
|
+
ask creation. Use \`ish ask add-testers\` to *extend* it; you can't
|
|
165
|
+
replace it.
|
|
166
|
+
5. **Don't try to put credentials in the URL** for gated study URLs.
|
|
167
|
+
Configure them once on the workspace via
|
|
168
|
+
\`ish workspace site-access …\` (basic-auth, cookie, login).
|
|
169
|
+
See \`ish docs get-page concepts/site-access\`.
|
|
170
|
+
6. **Don't commit \`~/.ish/config.json\`** — it stores tokens and active
|
|
171
|
+
workspace/study/ask selections. It lives in \`$HOME\`, not the repo.
|
|
172
|
+
|
|
173
|
+
## Authentication
|
|
174
|
+
|
|
175
|
+
\`ish login\` opens a browser and saves tokens to \`~/.ish/config.json\`.
|
|
176
|
+
The CLI also accepts \`--token <token>\` or \`ISH_TOKEN\` env var. If a
|
|
177
|
+
command exits with code 3 ("auth"), tell the user to re-run \`ish login\`.
|
|
178
|
+
|
|
179
|
+
## When ish is the wrong tool
|
|
180
|
+
|
|
181
|
+
If the user wants to *write code* against the Ish API directly, point
|
|
182
|
+
them at the API docs at https://ishlabs.io — this CLI is for
|
|
183
|
+
orchestration, not as an API client library.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
**Skill version:** ${VERSION}
|
|
188
|
+
**Skill source of truth:** \`ish docs\` (offline, ships with the binary)
|
|
189
|
+
`;
|
|
190
|
+
const WORKFLOWS_MD = `# ish workflows — worked examples
|
|
191
|
+
|
|
192
|
+
Each workflow below is a complete transcript an agent can adapt. Run
|
|
193
|
+
\`ish docs overview\` first if you haven't already this session.
|
|
194
|
+
|
|
195
|
+
## 1. First study from zero
|
|
196
|
+
|
|
197
|
+
Goal: from a fresh install to a finished interactive study with 3
|
|
198
|
+
testers and one question.
|
|
199
|
+
|
|
200
|
+
\`\`\`bash
|
|
201
|
+
# 1. Authenticate (browser flow, saves tokens to ~/.ish/config.json)
|
|
202
|
+
ish login
|
|
203
|
+
|
|
204
|
+
# 2. Create + select a workspace
|
|
205
|
+
ish workspace create --name "Demo" --base-url https://example.com
|
|
206
|
+
ish workspace use w-…
|
|
207
|
+
|
|
208
|
+
# 3. Generate a small audience
|
|
209
|
+
ish profile generate \\
|
|
210
|
+
--description "Tech-savvy millennials in the US who use mobile banking" \\
|
|
211
|
+
--count 3
|
|
212
|
+
|
|
213
|
+
# 4. Define the study
|
|
214
|
+
ish study create --name "Onboarding UX" --modality interactive \\
|
|
215
|
+
--assignment "Sign up:Complete the signup flow" \\
|
|
216
|
+
--question "How easy was it?"
|
|
217
|
+
ish study use s-…
|
|
218
|
+
|
|
219
|
+
# 5. Configure an iteration with the URL under test
|
|
220
|
+
ish iteration create --url https://example.com
|
|
221
|
+
|
|
222
|
+
# 6. Run, blocking until done
|
|
223
|
+
ish study run --all --wait
|
|
224
|
+
|
|
225
|
+
# 7. Read results
|
|
226
|
+
ish study results --json | jq .
|
|
227
|
+
\`\`\`
|
|
228
|
+
|
|
229
|
+
## 2. Quick A/B ask with image variants
|
|
230
|
+
|
|
231
|
+
Goal: ship 30 simulated reactions to two hero images, with a "which do
|
|
232
|
+
you prefer" question.
|
|
233
|
+
|
|
234
|
+
\`\`\`bash
|
|
235
|
+
ish ask run --new --name "hero shots" \\
|
|
236
|
+
--prompt "Which feels more premium?" \\
|
|
237
|
+
--variant image:./hero-a.png::label=A \\
|
|
238
|
+
--variant image:./hero-b.png::label=B \\
|
|
239
|
+
--sample 30 --wants-pick --wait
|
|
240
|
+
|
|
241
|
+
ish ask results --json | jq .
|
|
242
|
+
\`\`\`
|
|
243
|
+
|
|
244
|
+
Add a follow-up round with no audience change:
|
|
245
|
+
|
|
246
|
+
\`\`\`bash
|
|
247
|
+
ish ask run --prompt "Which one would you click on?" \\
|
|
248
|
+
--variant image:./hero-a.png::label=A \\
|
|
249
|
+
--variant image:./hero-b.png::label=B \\
|
|
250
|
+
--wait
|
|
251
|
+
\`\`\`
|
|
252
|
+
|
|
253
|
+
## 3. Generate profiles from a real source
|
|
254
|
+
|
|
255
|
+
Goal: turn a customer interview transcript into a 4-profile audience.
|
|
256
|
+
|
|
257
|
+
\`\`\`bash
|
|
258
|
+
# Inline — auto-uploads the file:
|
|
259
|
+
ish profile generate --source ./interviews/sarah.txt --count 4
|
|
260
|
+
|
|
261
|
+
# Or upload once and reuse the source alias:
|
|
262
|
+
ish source upload ./call.mp3 --diarize
|
|
263
|
+
# → tps-3a4 (status: processed)
|
|
264
|
+
ish profile generate --source tps-3a4 --propose-count
|
|
265
|
+
# → { proposed_count: 4, rationale: "..." }
|
|
266
|
+
ish profile generate --source tps-3a4 --count 4
|
|
267
|
+
\`\`\`
|
|
268
|
+
|
|
269
|
+
## 4. Target a gated URL (Vercel preview / staging gate / login form)
|
|
270
|
+
|
|
271
|
+
Configure credentials once on the workspace; testers reuse them.
|
|
272
|
+
|
|
273
|
+
\`\`\`bash
|
|
274
|
+
# Show what's configured:
|
|
275
|
+
ish workspace site-access status
|
|
276
|
+
|
|
277
|
+
# HTTP basic auth:
|
|
278
|
+
ish workspace site-access basic-auth --username alice --password hunter2
|
|
279
|
+
|
|
280
|
+
# Session cookie (Vercel preview, Lovable, etc.):
|
|
281
|
+
ish workspace site-access cookie --name session --value abc123
|
|
282
|
+
|
|
283
|
+
# Login form (typed by the tester into the page):
|
|
284
|
+
ish workspace site-access login --username demo --password demo
|
|
285
|
+
\`\`\`
|
|
286
|
+
|
|
287
|
+
Keep secrets out of shell history by passing \`-\` for \`--password\` /
|
|
288
|
+
\`--value\` and piping from stdin:
|
|
289
|
+
|
|
290
|
+
\`\`\`bash
|
|
291
|
+
printf %s "$STAGING_PW" | ish workspace site-access basic-auth \\
|
|
292
|
+
--username alice --password -
|
|
293
|
+
\`\`\`
|
|
294
|
+
|
|
295
|
+
## 5. Re-run a study with a fresh audience
|
|
296
|
+
|
|
297
|
+
Goal: same study, same iteration, but compare audiences.
|
|
298
|
+
|
|
299
|
+
\`\`\`bash
|
|
300
|
+
# First run — Swedish 35-50:
|
|
301
|
+
ish study run --country SE --min-age 35 --max-age 50 --sample 5 --wait
|
|
302
|
+
|
|
303
|
+
# Second run — every female profile in the workspace, same iteration:
|
|
304
|
+
ish study run --gender female --all --wait
|
|
305
|
+
\`\`\`
|
|
306
|
+
|
|
307
|
+
If you don't pass any audience flags, \`ish study run\` reuses the
|
|
308
|
+
iteration's existing testers — useful for re-running after fixing the
|
|
309
|
+
target page.
|
|
310
|
+
|
|
311
|
+
## 6. Localhost target (dev environment)
|
|
312
|
+
|
|
313
|
+
Expose a port via a Cloudflare tunnel; \`ish connect\` prints the public
|
|
314
|
+
URL the study iteration can point at. \`connect\` is foreground and
|
|
315
|
+
long-running — keep it open in a separate terminal (or background it).
|
|
316
|
+
|
|
317
|
+
\`\`\`bash
|
|
318
|
+
# Terminal A — keep open:
|
|
319
|
+
ish connect 3000
|
|
320
|
+
# stdout: Tunnel URL: https://<random>.trycloudflare.com → http://localhost:3000
|
|
321
|
+
|
|
322
|
+
# Terminal B — use the URL:
|
|
323
|
+
ish iteration create --url https://<random>.trycloudflare.com
|
|
324
|
+
ish study run --sample 3 --wait
|
|
325
|
+
\`\`\`
|
|
326
|
+
|
|
327
|
+
For agents/scripts, run \`connect\` in the background with \`--json\` and
|
|
328
|
+
read the URL from stdout (one JSON line per state change):
|
|
329
|
+
|
|
330
|
+
\`\`\`bash
|
|
331
|
+
ish connect 3000 --json > /tmp/ish-tunnel.log &
|
|
332
|
+
sleep 3
|
|
333
|
+
URL=$(jq -r 'select(.status=="connected") | .tunnel_url' /tmp/ish-tunnel.log | head -1)
|
|
334
|
+
ish iteration create --url "$URL"
|
|
335
|
+
\`\`\`
|
|
336
|
+
|
|
337
|
+
## Tips for chaining commands as an agent
|
|
338
|
+
|
|
339
|
+
- Capture aliases from JSON: \`ITER=$(ish iteration create --url … --json | jq -r .alias)\`
|
|
340
|
+
- After \`ish study run --json\`, the testers you just dispatched are at
|
|
341
|
+
\`.tester_aliases[]\` (and \`.tester_ids[]\` for UUIDs). Pass these to
|
|
342
|
+
\`ish study poll/wait/cancel <tester_id>\`.
|
|
343
|
+
- Use \`--fields\` to keep JSON tight: \`ish study list --fields alias,name,status\`
|
|
344
|
+
- Always pass \`--wait\` (or \`ish study wait\`) before reading
|
|
345
|
+
\`ish study results\` — without it you may read partial data.
|
|
346
|
+
- For \`ask\` write-paths (update/archive/wait/add-questions/add-testers),
|
|
347
|
+
default JSON is compact (changed fields + alias). Pass \`--verbose\` for
|
|
348
|
+
the full Ask payload.
|
|
349
|
+
- For \`profile generate --json\`, \`simulation_config\` is trimmed by
|
|
350
|
+
default (~9× smaller). Pass \`--include-simulation-config\` to include it.
|
|
351
|
+
`;
|
|
352
|
+
const COMMANDS_MD = `# ish commands — quick index
|
|
353
|
+
|
|
354
|
+
This file is intentionally thin. The authoritative, always-current
|
|
355
|
+
reference is \`ish docs\` (built into the binary) and per-command
|
|
356
|
+
\`--help\`. Use this index only for orientation — when in doubt, run:
|
|
357
|
+
|
|
358
|
+
\`\`\`bash
|
|
359
|
+
ish docs list
|
|
360
|
+
ish <command> --help
|
|
361
|
+
\`\`\`
|
|
362
|
+
|
|
363
|
+
## Top-level groups
|
|
364
|
+
|
|
365
|
+
| Group | Purpose | Concept page |
|
|
366
|
+
|-------------|-------------------------------------------------|-----------------------------|
|
|
367
|
+
| \`workspace\` | Top-level container (= product) | concepts/workspace |
|
|
368
|
+
| \`study\` | Persistent research artifact | concepts/study |
|
|
369
|
+
| \`iteration\` | One configured run of a study (URL or media) | concepts/iteration |
|
|
370
|
+
| \`ask\` | Lightweight reaction artifact | concepts/ask |
|
|
371
|
+
| \`profile\` | Tester profiles + audience generation | concepts/profile |
|
|
372
|
+
| \`source\` | Upload sources for profile generation | concepts/source |
|
|
373
|
+
| \`config\` | Simulation configs (model, timing, retries) | (run \`ish config --help\`) |
|
|
374
|
+
| \`docs\` | Offline docs for agents | (run \`ish docs --help\`) |
|
|
375
|
+
| \`init\` | Drop this skill into a Claude Code / Codex / | (run \`ish init --help\`) |
|
|
376
|
+
| | Cursor / Cline / Roo project | |
|
|
377
|
+
| \`login\` | Browser-based auth | — |
|
|
378
|
+
| \`logout\` | Clear saved credentials | — |
|
|
379
|
+
| \`connect\` | Cloudflare tunnel exposing localhost | — |
|
|
380
|
+
| \`upgrade\` | Self-update | — |
|
|
381
|
+
|
|
382
|
+
## Discovering flags safely
|
|
383
|
+
|
|
384
|
+
Don't construct \`ish\` commands from memory for anything beyond the
|
|
385
|
+
high-frequency examples. Run:
|
|
386
|
+
|
|
387
|
+
\`\`\`bash
|
|
388
|
+
ish <group> --help # group-level summary + concept primer
|
|
389
|
+
ish <group> <verb> --help # exact flags for the verb
|
|
390
|
+
\`\`\`
|
|
391
|
+
|
|
392
|
+
Every group's \`--help\` ends with a "Concept pages" footer pointing at
|
|
393
|
+
the right \`ish docs get-page <slug>\` to read deep context.
|
|
394
|
+
|
|
395
|
+
## Aliases
|
|
396
|
+
|
|
397
|
+
Short prefixed IDs (e.g. \`s-b2c\`, \`tp-795\`, \`a-6ec\`, \`i-d4e\`,
|
|
398
|
+
\`t-a17\`, \`tps-3a4\`, \`w-6ec\`, \`c-c3c\`) are accepted anywhere a UUID
|
|
399
|
+
is expected. Full UUIDs always work too. See
|
|
400
|
+
\`ish docs get-page reference/aliases\`.
|
|
401
|
+
|
|
402
|
+
## Output flags (global)
|
|
403
|
+
|
|
404
|
+
| Flag | Effect |
|
|
405
|
+
|------------------|----------------------------------------------------------|
|
|
406
|
+
| \`--json\` | JSON output (auto-on when stdout is piped) |
|
|
407
|
+
| \`--fields a,b\` | Keep only listed fields in JSON |
|
|
408
|
+
| \`--verbose\` | Include UUIDs + timestamps in JSON |
|
|
409
|
+
| \`-q, --quiet\` | Suppress progress messages on stderr |
|
|
410
|
+
| \`-t, --token\` | Auth token (else ISH_TOKEN env, else \`ish login\` saved) |
|
|
411
|
+
| \`--api-url\` | Override backend (default https://api.ishlabs.io) |
|
|
412
|
+
|
|
413
|
+
## Exit codes
|
|
414
|
+
|
|
415
|
+
\`0\` ok · \`1\` general · \`2\` usage/validation · \`3\` auth ·
|
|
416
|
+
\`4\` not-found · \`5\` transient (retryable). See
|
|
417
|
+
\`ish docs get-page reference/json-mode\`.
|
|
418
|
+
`;
|
|
419
|
+
/**
|
|
420
|
+
* Build the SKILL.md frontmatter + body. Version is stamped from the
|
|
421
|
+
* package.json at build time so users can see drift between the skill
|
|
422
|
+
* they installed and the CLI they're running.
|
|
423
|
+
*/
|
|
424
|
+
function buildSkillMd() {
|
|
425
|
+
const frontmatter = [
|
|
426
|
+
"---",
|
|
427
|
+
"name: ish",
|
|
428
|
+
`description: ${JSON.stringify(SKILL_DESCRIPTION)}`,
|
|
429
|
+
"license: SEE LICENSE IN LICENSE",
|
|
430
|
+
"metadata:",
|
|
431
|
+
" author: ish",
|
|
432
|
+
` version: ${JSON.stringify(VERSION)}`,
|
|
433
|
+
"allowed-tools: Bash(ish:*)",
|
|
434
|
+
"---",
|
|
435
|
+
"",
|
|
436
|
+
].join("\n");
|
|
437
|
+
return frontmatter + SKILL_BODY;
|
|
438
|
+
}
|
|
439
|
+
/** Returns every file that makes up the bundled skill, in stable order. */
|
|
440
|
+
export function buildSkillFiles() {
|
|
441
|
+
return [
|
|
442
|
+
{ relativePath: "SKILL.md", contents: buildSkillMd() },
|
|
443
|
+
{ relativePath: "references/workflows.md", contents: WORKFLOWS_MD },
|
|
444
|
+
{ relativePath: "references/commands.md", contents: COMMANDS_MD },
|
|
445
|
+
];
|
|
446
|
+
}
|
|
447
|
+
/** Convenience: just the SKILL.md content (for `ish init --stdout`). */
|
|
448
|
+
export function getSkillMd() {
|
|
449
|
+
return buildSkillMd();
|
|
450
|
+
}
|
|
451
|
+
export const SKILL_TARGETS = [
|
|
452
|
+
{
|
|
453
|
+
key: "claude",
|
|
454
|
+
path: ".claude/skills/ish",
|
|
455
|
+
consumers: ["Claude Code", "Cursor (back-compat)"],
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
key: "agents",
|
|
459
|
+
path: ".agents/skills/ish",
|
|
460
|
+
consumers: ["Codex", "Cursor", "Cline", "Roo Code"],
|
|
461
|
+
},
|
|
462
|
+
];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsers and loaders for `ish study create/update` assignment & question
|
|
3
|
+
* flags. Mirrors the loose validation style of `src/lib/ask-questions.ts`.
|
|
4
|
+
*/
|
|
5
|
+
import type { Assignment, InterviewQuestion } from "./types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Parse `"Name:Instructions"`. Splits on the first `:`, so colons inside
|
|
8
|
+
* the instructions text are preserved.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseAssignment(value: string): Assignment;
|
|
11
|
+
/**
|
|
12
|
+
* Read a JSON file containing an array of `{name, instructions}` entries.
|
|
13
|
+
*/
|
|
14
|
+
export declare function loadAssignmentsFile(filePath: string): Assignment[];
|
|
15
|
+
/**
|
|
16
|
+
* Parse a plain question text into a default text-typed, after-timed
|
|
17
|
+
* `InterviewQuestion`. Anything more complex (slider, likert, choice,
|
|
18
|
+
* timing=before) goes through `--questionnaire <file.json>`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function parseQuestion(value: string): InterviewQuestion;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsers and loaders for `ish study create/update` assignment & question
|
|
3
|
+
* flags. Mirrors the loose validation style of `src/lib/ask-questions.ts`.
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync } from "node:fs";
|
|
6
|
+
import { resolve as resolvePath } from "node:path";
|
|
7
|
+
/**
|
|
8
|
+
* Parse `"Name:Instructions"`. Splits on the first `:`, so colons inside
|
|
9
|
+
* the instructions text are preserved.
|
|
10
|
+
*/
|
|
11
|
+
export function parseAssignment(value) {
|
|
12
|
+
const idx = value.indexOf(":");
|
|
13
|
+
if (idx < 0) {
|
|
14
|
+
throw new Error(`Invalid --assignment "${value}". Use "Name:Instructions" (e.g. --assignment "Sign up:Complete the signup flow").`);
|
|
15
|
+
}
|
|
16
|
+
const name = value.slice(0, idx).trim();
|
|
17
|
+
const instructions = value.slice(idx + 1).trim();
|
|
18
|
+
if (!name) {
|
|
19
|
+
throw new Error(`Invalid --assignment "${value}": name is empty.`);
|
|
20
|
+
}
|
|
21
|
+
if (!instructions) {
|
|
22
|
+
throw new Error(`Invalid --assignment "${value}": instructions are empty.`);
|
|
23
|
+
}
|
|
24
|
+
return { name, instructions };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Read a JSON file containing an array of `{name, instructions}` entries.
|
|
28
|
+
*/
|
|
29
|
+
export function loadAssignmentsFile(filePath) {
|
|
30
|
+
let raw;
|
|
31
|
+
try {
|
|
32
|
+
raw = readFileSync(resolvePath(filePath), "utf-8");
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new Error(`Cannot read assignments file: ${filePath}`);
|
|
36
|
+
}
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = JSON.parse(raw);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
throw new Error(`Invalid JSON in assignments file: ${filePath}`);
|
|
43
|
+
}
|
|
44
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
45
|
+
throw new Error(`Assignments file must be a non-empty JSON array: ${filePath}`);
|
|
46
|
+
}
|
|
47
|
+
for (let i = 0; i < parsed.length; i++) {
|
|
48
|
+
const a = parsed[i];
|
|
49
|
+
if (!a || typeof a !== "object") {
|
|
50
|
+
throw new Error(`assignments[${i}] must be an object with name + instructions.`);
|
|
51
|
+
}
|
|
52
|
+
if (typeof a.name !== "string" || !a.name.trim()) {
|
|
53
|
+
throw new Error(`assignments[${i}].name must be a non-empty string.`);
|
|
54
|
+
}
|
|
55
|
+
if (typeof a.instructions !== "string" || !a.instructions.trim()) {
|
|
56
|
+
throw new Error(`assignments[${i}].instructions must be a non-empty string.`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return parsed;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Parse a plain question text into a default text-typed, after-timed
|
|
63
|
+
* `InterviewQuestion`. Anything more complex (slider, likert, choice,
|
|
64
|
+
* timing=before) goes through `--questionnaire <file.json>`.
|
|
65
|
+
*/
|
|
66
|
+
export function parseQuestion(value) {
|
|
67
|
+
const text = value.trim();
|
|
68
|
+
if (!text) {
|
|
69
|
+
throw new Error(`Invalid --question "": question text is empty.`);
|
|
70
|
+
}
|
|
71
|
+
return { question: text, type: "text", timing: "after" };
|
|
72
|
+
}
|