@ishlabs/cli 0.8.4 → 0.9.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/README.md +2 -2
- package/dist/auth.d.ts +38 -4
- package/dist/auth.js +205 -39
- package/dist/commands/ask.js +28 -2
- package/dist/commands/iteration.js +105 -6
- package/dist/commands/profile.js +25 -12
- package/dist/commands/source.js +24 -2
- package/dist/commands/study.js +14 -6
- package/dist/config.d.ts +4 -0
- package/dist/connect.js +100 -14
- package/dist/index.js +6 -3
- package/dist/lib/auth.js +7 -1
- package/dist/lib/command-helpers.d.ts +37 -0
- package/dist/lib/command-helpers.js +199 -7
- package/dist/lib/docs.js +316 -39
- package/dist/lib/output.d.ts +6 -0
- package/dist/lib/output.js +133 -2
- package/dist/lib/skill-content.js +120 -6
- package/package.json +3 -3
package/dist/lib/output.js
CHANGED
|
@@ -11,9 +11,105 @@ import { deterministicAlias, getAliasMap, ALIAS_PREFIX } from "./alias-store.js"
|
|
|
11
11
|
// --- Lean JSON: strip noise for agent-friendly output ---
|
|
12
12
|
let _verbose = false;
|
|
13
13
|
let _fields;
|
|
14
|
+
let _getField;
|
|
14
15
|
/** Set by withClient() based on global flags. */
|
|
15
16
|
export function setVerbose(v) { _verbose = v; }
|
|
16
17
|
export function setFields(fields) { _fields = fields; }
|
|
18
|
+
/**
|
|
19
|
+
* Pattern Ω capture mode: when set, jsonOutput() returns the bare value at
|
|
20
|
+
* the dotted path instead of the full JSON. Cleared between command runs by
|
|
21
|
+
* each invocation of `applyGlobals()`.
|
|
22
|
+
*/
|
|
23
|
+
export function setGetField(field) { _getField = field; }
|
|
24
|
+
/**
|
|
25
|
+
* Walk a dotted path through a JSON value. Returns the resolved value or
|
|
26
|
+
* `MISSING` if any step is undefined. Numeric segments index into arrays;
|
|
27
|
+
* non-numeric segments key into objects. When a segment is non-numeric and
|
|
28
|
+
* the current value is an array, the segment is mapped over the array
|
|
29
|
+
* (e.g. `items.alias` on `{items: [...]}` after `items` is unwrapped to the
|
|
30
|
+
* array yields the per-element `alias` values).
|
|
31
|
+
*/
|
|
32
|
+
const MISSING = Symbol("missing");
|
|
33
|
+
function walkPath(data, segments) {
|
|
34
|
+
let cur = data;
|
|
35
|
+
for (const seg of segments) {
|
|
36
|
+
if (cur === null || cur === undefined)
|
|
37
|
+
return MISSING;
|
|
38
|
+
if (Array.isArray(cur)) {
|
|
39
|
+
// `seg` could be a numeric index, or a key to apply to each element.
|
|
40
|
+
const asIndex = /^\d+$/.test(seg) ? parseInt(seg, 10) : null;
|
|
41
|
+
if (asIndex !== null) {
|
|
42
|
+
if (asIndex < 0 || asIndex >= cur.length)
|
|
43
|
+
return MISSING;
|
|
44
|
+
cur = cur[asIndex];
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
// Map across array: pick the key on each element. Skip elements that
|
|
48
|
+
// lack the key so `--get items.alias` on a list with one bad row
|
|
49
|
+
// still returns the rest.
|
|
50
|
+
const mapped = [];
|
|
51
|
+
for (const el of cur) {
|
|
52
|
+
if (el !== null && typeof el === "object" && seg in el) {
|
|
53
|
+
mapped.push(el[seg]);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (mapped.length === 0)
|
|
57
|
+
return MISSING;
|
|
58
|
+
cur = mapped;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (typeof cur !== "object")
|
|
62
|
+
return MISSING;
|
|
63
|
+
const obj = cur;
|
|
64
|
+
if (!(seg in obj))
|
|
65
|
+
return MISSING;
|
|
66
|
+
cur = obj[seg];
|
|
67
|
+
}
|
|
68
|
+
return cur;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Resolve `_getField` against `data`. Auto-descends into a top-level
|
|
72
|
+
* `items: [...]` wrapper when the requested path doesn't start with `items`
|
|
73
|
+
* and the path resolves on items but not at top level — i.e.
|
|
74
|
+
* `--get alias` on a list response acts like `--get items.alias`.
|
|
75
|
+
*/
|
|
76
|
+
function extractGetField(data, path) {
|
|
77
|
+
const segments = path.split(".").map((s) => s.trim()).filter(Boolean);
|
|
78
|
+
if (segments.length === 0)
|
|
79
|
+
return MISSING;
|
|
80
|
+
const direct = walkPath(data, segments);
|
|
81
|
+
if (direct !== MISSING)
|
|
82
|
+
return direct;
|
|
83
|
+
// Auto-descend through {items: [...]} wrapper for paginated list responses.
|
|
84
|
+
if (segments[0] !== "items"
|
|
85
|
+
&& data !== null
|
|
86
|
+
&& typeof data === "object"
|
|
87
|
+
&& !Array.isArray(data)
|
|
88
|
+
&& Array.isArray(data.items)) {
|
|
89
|
+
const viaItems = walkPath(data, ["items", ...segments]);
|
|
90
|
+
if (viaItems !== MISSING)
|
|
91
|
+
return viaItems;
|
|
92
|
+
}
|
|
93
|
+
return MISSING;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Render an extracted value as a bare string for stdout. Rules:
|
|
97
|
+
* - string/number/boolean: printed as-is (no JSON quotes).
|
|
98
|
+
* - null: empty string.
|
|
99
|
+
* - arrays: one element per line, each element rendered by the same rules
|
|
100
|
+
* (objects within the array are compact JSON).
|
|
101
|
+
* - objects: compact JSON on a single line.
|
|
102
|
+
*/
|
|
103
|
+
function renderBare(value) {
|
|
104
|
+
if (value === null || value === undefined)
|
|
105
|
+
return "";
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
return value.map((v) => renderBare(v)).join("\n");
|
|
108
|
+
}
|
|
109
|
+
if (typeof value === "object")
|
|
110
|
+
return JSON.stringify(value);
|
|
111
|
+
return String(value);
|
|
112
|
+
}
|
|
17
113
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
18
114
|
const TIMESTAMP_KEYS = new Set(["created_at", "updated_at"]);
|
|
19
115
|
const PAGINATION_KEYS = new Set(["items", "total", "returned", "limit", "offset", "has_more"]);
|
|
@@ -151,6 +247,19 @@ function jsonOutput(data, options = {}) {
|
|
|
151
247
|
if (_fields && _fields.length > 0) {
|
|
152
248
|
out = pickFields(out, _fields);
|
|
153
249
|
}
|
|
250
|
+
// Pattern Ω capture mode: --get <field> returns bare values instead of
|
|
251
|
+
// structured JSON. We extract from the post-lean / post-fields data so the
|
|
252
|
+
// path the agent reasons about matches what they'd see on a normal --json
|
|
253
|
+
// call (e.g. UUIDs already replaced by aliases).
|
|
254
|
+
if (_getField) {
|
|
255
|
+
const extracted = extractGetField(out, _getField);
|
|
256
|
+
if (extracted === MISSING) {
|
|
257
|
+
const err = new Error(`--get: field "${_getField}" not found in response.`);
|
|
258
|
+
err.name = "ValidationError";
|
|
259
|
+
throw err;
|
|
260
|
+
}
|
|
261
|
+
return renderBare(extracted);
|
|
262
|
+
}
|
|
154
263
|
return JSON.stringify(out, null, 2);
|
|
155
264
|
}
|
|
156
265
|
/**
|
|
@@ -166,9 +275,29 @@ function injectAliases(items, prefix, idField = "id") {
|
|
|
166
275
|
}
|
|
167
276
|
}
|
|
168
277
|
// --- JSON mode ---
|
|
278
|
+
/**
|
|
279
|
+
* Catch jsonOutput's --get extraction failure (a ValidationError thrown when
|
|
280
|
+
* the requested field is missing) and route it through outputError + exit 2,
|
|
281
|
+
* so commands that don't go through withClient/runInline (e.g. `ish docs *`)
|
|
282
|
+
* still surface a clean usage error instead of an uncaught stack trace.
|
|
283
|
+
*/
|
|
284
|
+
function safeJsonOutput(data, options = {}) {
|
|
285
|
+
try {
|
|
286
|
+
return jsonOutput(data, options);
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
if (err instanceof Error && err.name === "ValidationError") {
|
|
290
|
+
outputError(err, true);
|
|
291
|
+
process.exit(2);
|
|
292
|
+
}
|
|
293
|
+
throw err;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
169
296
|
export function output(data, json, options = {}) {
|
|
170
297
|
if (json) {
|
|
171
|
-
|
|
298
|
+
const text = safeJsonOutput(data, options);
|
|
299
|
+
if (text !== undefined)
|
|
300
|
+
console.log(text);
|
|
172
301
|
return;
|
|
173
302
|
}
|
|
174
303
|
if (data === null || data === undefined)
|
|
@@ -185,7 +314,9 @@ export function output(data, json, options = {}) {
|
|
|
185
314
|
}
|
|
186
315
|
export function outputList(rows, json) {
|
|
187
316
|
if (json) {
|
|
188
|
-
|
|
317
|
+
const text = safeJsonOutput(rows);
|
|
318
|
+
if (text !== undefined)
|
|
319
|
+
console.log(text);
|
|
189
320
|
return;
|
|
190
321
|
}
|
|
191
322
|
if (rows.length === 0) {
|
|
@@ -77,7 +77,7 @@ Workspace (= product)
|
|
|
77
77
|
├── Tester Profiles (tp-…) reusable audience personas
|
|
78
78
|
│ └── Sources (tps-…) transcripts/audio/images that seed generation
|
|
79
79
|
├── Study (s-…) persistent research artifact
|
|
80
|
-
│ ├── modality interactive | text | video | audio | image | document
|
|
80
|
+
│ ├── modality interactive | text | video | audio | image | document | chat
|
|
81
81
|
│ ├── assignments tasks the tester does
|
|
82
82
|
│ ├── questionnaire questions the tester answers
|
|
83
83
|
│ └── Iterations (i-…) one configured run; carries the URL or media
|
|
@@ -140,10 +140,58 @@ See \`references/workflows.md\` in this skill for end-to-end transcripts:
|
|
|
140
140
|
- Targeting a gated URL (basic auth, session cookie, login form)
|
|
141
141
|
- Re-running a study with a fresh audience
|
|
142
142
|
|
|
143
|
+
## Display vs. capture: the right output mode
|
|
144
|
+
|
|
145
|
+
Three output modes — pick the one matching your intent, **don't reach
|
|
146
|
+
for \`jq\` / \`python\` reflexively**:
|
|
147
|
+
|
|
148
|
+
| Intent | Use |
|
|
149
|
+
|-------------------------------------------------|----------------------|
|
|
150
|
+
| Show the user a list/table | bare command (TTY) or \`--human\` |
|
|
151
|
+
| Capture one value to feed into the next command | \`--get <field>\` |
|
|
152
|
+
| Parse multiple fields / nested shape | \`--json\` |
|
|
153
|
+
|
|
154
|
+
\`--get\` extracts a single field from the JSON response and prints its
|
|
155
|
+
bare value. It supports dotted paths and auto-descends into list
|
|
156
|
+
\`items\` so \`--get alias\` on a paginated list yields one alias per
|
|
157
|
+
line. \`--human\` forces human output even when stdout is piped — use
|
|
158
|
+
it when you want to \`tee\` a table to a file but still show it. The
|
|
159
|
+
two flags are mutually exclusive (capture and display are different
|
|
160
|
+
intents).
|
|
161
|
+
|
|
162
|
+
### Worked example — capture in a script, display to the user
|
|
163
|
+
|
|
164
|
+
\`\`\`bash
|
|
165
|
+
# DON'T: shim around the CLI with jq just to grab one value.
|
|
166
|
+
# ASK=$(ish ask create … --json | jq -r .alias)
|
|
167
|
+
|
|
168
|
+
# DO: capture mode — bare value, exit 0.
|
|
169
|
+
ASK=$(ish ask create --new --name demo \\
|
|
170
|
+
--prompt "Which?" --variant text:A --variant text:B \\
|
|
171
|
+
--sample 30 --get alias)
|
|
172
|
+
|
|
173
|
+
# DON'T: pipe --json through jq when you want to show the user a table.
|
|
174
|
+
# ish ask results "$ASK" --json | jq … | tee /tmp/x.txt
|
|
175
|
+
|
|
176
|
+
# DO: --human keeps the table layout even through tee.
|
|
177
|
+
ish ask results "$ASK" --human | tee /tmp/transcript.txt
|
|
178
|
+
\`\`\`
|
|
179
|
+
|
|
180
|
+
Missing field on \`--get\` → exit 2 with a usage error. \`--get\` also
|
|
181
|
+
implies \`--quiet\` so the bare value is the only thing on stdout.
|
|
182
|
+
|
|
143
183
|
## Output handling
|
|
144
184
|
|
|
145
185
|
- Every command supports \`--json\`. JSON mode is **auto-enabled when
|
|
146
186
|
stdout is piped**, so an agent rarely needs \`--json\` explicitly.
|
|
187
|
+
- **\`--get <field>\` is the right way to capture a single value.**
|
|
188
|
+
Dotted paths supported (\`tester_profile.name\`); on a paginated
|
|
189
|
+
\`{items: [...]}\` response, a leading non-\`items\` segment
|
|
190
|
+
auto-descends into items. Replaces the
|
|
191
|
+
\`--json | jq -r .field\` shim. Implies \`--json\` and \`--quiet\`.
|
|
192
|
+
- **\`--human\` forces human output even when stdout is piped.** Use it
|
|
193
|
+
to \`tee\` a table without losing the layout. Mutually exclusive
|
|
194
|
+
with \`--get\`.
|
|
147
195
|
- \`--fields a,b,c\` strips JSON output to the listed fields (saves
|
|
148
196
|
tokens). \`--verbose\` adds full UUIDs and timestamps.
|
|
149
197
|
- **Stdout is data only.** All progress, status, and "Open in browser"
|
|
@@ -175,10 +223,38 @@ See \`references/workflows.md\` in this skill for end-to-end transcripts:
|
|
|
175
223
|
Top-level field with per-round picks/winner snapshots and
|
|
176
224
|
\`picks_delta\` (R1 → last). Don't diff two \`ask results\` calls by
|
|
177
225
|
hand.
|
|
178
|
-
- **\`--workspace\`
|
|
179
|
-
|
|
180
|
-
\`
|
|
181
|
-
|
|
226
|
+
- **\`--workspace\` works at the program root AND every subcommand.**
|
|
227
|
+
\`ish --workspace w-6ec study list\` and \`ish study list --workspace
|
|
228
|
+
w-6ec\` are equivalent; if both are passed, the subcommand-level
|
|
229
|
+
flag wins. Without either, the CLI falls back to \`ISH_WORKSPACE\`
|
|
230
|
+
env then the active workspace in \`~/.ish/config.json\`.
|
|
231
|
+
- **\`profile generate\` emits stderr progress.** \`generating N
|
|
232
|
+
profiles…\` then \`generated N profiles\` around the ~10–20s LLM
|
|
233
|
+
call. Suppress with \`--quiet\`. Generated bios reference the
|
|
234
|
+
brief's domain context naturally (occupation, daily work,
|
|
235
|
+
frustrations) — they no longer parrot vocabulary from the brief
|
|
236
|
+
verbatim. DOBs spread across the year instead of all-on-\`06-15\`.
|
|
237
|
+
- **Empty-pool errors include a country-suggestion line.** When
|
|
238
|
+
\`study run\` / \`ask run --new\` rejects because \`--country XX\`
|
|
239
|
+
matched zero profiles, the error includes the top-3 populated
|
|
240
|
+
countries that satisfy your *other* filters. Pivot directly without
|
|
241
|
+
a second \`profile list\` round-trip.
|
|
242
|
+
- **\`<entity> list\` emits a stderr pagination hint** when
|
|
243
|
+
\`has_more=true\` and \`--quiet\` is unset. Goes to stderr in **every
|
|
244
|
+
mode** (including \`--json\` and piped stdout) — it never pollutes
|
|
245
|
+
machine-readable stdout but is visible to any agent reading stderr.
|
|
246
|
+
Format: "showing N–M of TOTAL; pass --offset M --limit N for more."
|
|
247
|
+
- **\`study delete\` requires explicit confirmation.** Interactive:
|
|
248
|
+
prompts on stderr. Non-interactive (\`--json\`, piped, non-TTY
|
|
249
|
+
stdin): pass \`-y\` / \`--yes\` to confirm. Without it, the CLI
|
|
250
|
+
exits with usage code 2.
|
|
251
|
+
- **\`ask add-questions\` supports \`--wait\` / \`--timeout\`.** Match
|
|
252
|
+
the parity of \`ask create\` and \`ask run\`. Without \`--wait\` the
|
|
253
|
+
command returns after dispatch (round still running).
|
|
254
|
+
- **\`pick_confidence\` (0..1) is on every \`--wants-pick\` response.**
|
|
255
|
+
The model's self-reported confidence in its variant choice. Use it
|
|
256
|
+
to break ties when nominal pick counts are close. See
|
|
257
|
+
\`ish docs get-page concepts/ask\`.
|
|
182
258
|
- Exit codes carry meaning: 0 success, 2 usage/validation,
|
|
183
259
|
3 auth, 4 not-found, 5 transient. See
|
|
184
260
|
\`ish docs get-page reference/json-mode\`.
|
|
@@ -404,6 +480,35 @@ URL=$(jq -r 'select(.status=="connected") | .tunnel_url' /tmp/ish-tunnel.log | h
|
|
|
404
480
|
ish iteration create --url "$URL"
|
|
405
481
|
\`\`\`
|
|
406
482
|
|
|
483
|
+
## 7. Display-vs-capture: a script that does both
|
|
484
|
+
|
|
485
|
+
Goal: drive an A/B in a script, capture aliases without \`jq\`, and
|
|
486
|
+
still show the human a readable result table at the end.
|
|
487
|
+
|
|
488
|
+
\`\`\`bash
|
|
489
|
+
# Capture mode — bare values, suitable for shell variables.
|
|
490
|
+
ASK=$(ish ask create --new --name "tagline AB" \\
|
|
491
|
+
--prompt "Which sounds better?" \\
|
|
492
|
+
--variant text:"Short and punchy." \\
|
|
493
|
+
--variant text:"A longer, descriptive line." \\
|
|
494
|
+
--sample 30 --wants-pick --get alias)
|
|
495
|
+
|
|
496
|
+
# Wait silently — exit code is what matters here.
|
|
497
|
+
ish ask wait "$ASK" --timeout 600 --quiet
|
|
498
|
+
|
|
499
|
+
# Capture the winner letter for downstream branching:
|
|
500
|
+
WINNER=$(ish ask results "$ASK" --get rounds.aggregates.winner.letter)
|
|
501
|
+
echo "Winning variant: $WINNER"
|
|
502
|
+
|
|
503
|
+
# Display mode — show the user the full results table even though
|
|
504
|
+
# we're inside a script (stdout is piped to tee).
|
|
505
|
+
ish ask results "$ASK" --human | tee "/tmp/ask-\${ASK}.txt"
|
|
506
|
+
\`\`\`
|
|
507
|
+
|
|
508
|
+
The mental rule: **\`--get\` is for capture, bare commands / \`--human\`
|
|
509
|
+
are for display, \`--json\` is for chaining (multiple fields at once).**
|
|
510
|
+
If you find yourself reaching for \`jq -r .x\`, you wanted \`--get x\`.
|
|
511
|
+
|
|
407
512
|
## Tips for chaining commands as an agent
|
|
408
513
|
|
|
409
514
|
- Capture aliases from JSON: \`ITER=$(ish iteration create --url … --json | jq -r .alias)\`
|
|
@@ -428,12 +533,16 @@ ish iteration create --url "$URL"
|
|
|
428
533
|
|
|
429
534
|
| You want to… | Don't | Do |
|
|
430
535
|
|-------------------------------------------|----------------------------------------|--------------------------------------------------------------------|
|
|
536
|
+
| Capture a single value (alias, id, …) | \`--json \\| jq -r .alias\` | \`--get alias\` |
|
|
537
|
+
| Capture a nested value | \`--json \\| jq -r .tester_profile.name\` | \`--get tester_profile.name\` |
|
|
538
|
+
| Capture every alias from a list | \`--json \\| jq -r '.items[].alias'\` | \`--get alias\` (auto-descends into \`items\`, one per line) |
|
|
539
|
+
| Force human output through tee/redirect | none, output silently became JSON | \`--human\` |
|
|
431
540
|
| Look up 2-3 specific profiles | \`profile list --json \\| jq '.items[] \\| select(...)'\` | \`ish profile get tp-1b9 tp-fc1 tp-2fc\` |
|
|
432
541
|
| Show only some fields | \`--json \\| jq '{alias, name, country}'\` | \`--fields alias,name,country\` |
|
|
433
542
|
| Count testers on an ask | \`--json \\| jq '.testers \\| length'\` | \`ish ask get a-… --fields alias,testers_count\` |
|
|
434
543
|
| Count responses on a round | \`--json \\| jq '.rounds[0].responses \\| length'\` | \`ish ask get a-… --fields alias,rounds,responses_complete,responses_total\` |
|
|
435
544
|
| Pick the A/B winner | \`--json \\| jq '.rounds[0].responses…'\` | \`ish ask results a-… --json\` then read \`.rounds[].aggregates.winner\` |
|
|
436
|
-
| List of testers from \`study run\` | \`--json \\| jq '.testers[].id'\` | \`--
|
|
545
|
+
| List of testers from \`study run\` | \`--json \\| jq '.testers[].id'\` | \`--get tester_aliases\` (or \`tester_ids\` for UUIDs) |
|
|
437
546
|
|
|
438
547
|
The bias here is intentional: \`ish\` ships shapes designed for agent
|
|
439
548
|
consumption. If you find yourself reaching for \`jq\` or \`python\` to
|
|
@@ -496,6 +605,8 @@ is expected. Full UUIDs always work too. See
|
|
|
496
605
|
|
|
497
606
|
| Flag | Effect |
|
|
498
607
|
|------------------|----------------------------------------------------------|
|
|
608
|
+
| \`--get <field>\` | **Capture mode.** Print the bare value at the dotted path; auto-descends into list \`items\`. Implies \`--json\` + \`--quiet\`. Replaces \`--json \\| jq -r .field\`. |
|
|
609
|
+
| \`--human\` | **Force display mode** even when stdout is piped (overrides JSON-when-piped). Mutually exclusive with \`--get\`. |
|
|
499
610
|
| \`--json\` | JSON output (auto-on when stdout is piped) |
|
|
500
611
|
| \`--fields a,b\` | Keep only listed fields in JSON |
|
|
501
612
|
| \`--verbose\` | Include UUIDs + timestamps in JSON |
|
|
@@ -503,6 +614,9 @@ is expected. Full UUIDs always work too. See
|
|
|
503
614
|
| \`-t, --token\` | Auth token (else ISH_TOKEN env, else \`ish login\` saved) |
|
|
504
615
|
| \`--api-url\` | Override backend (default https://api.ishlabs.io) |
|
|
505
616
|
|
|
617
|
+
See \`ish docs get-page reference/json-mode\` for the full display-vs-
|
|
618
|
+
capture-vs-chain decision rule.
|
|
619
|
+
|
|
506
620
|
## Exit codes
|
|
507
621
|
|
|
508
622
|
\`0\` ok · \`1\` general · \`2\` usage/validation · \`3\` auth ·
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ishlabs/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "The command-line interface for
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "The command-line interface for ish",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"ish": "./dist/index.js"
|
|
@@ -41,4 +41,4 @@
|
|
|
41
41
|
"@types/node": "^22.0.0",
|
|
42
42
|
"typescript": "^5.7.0"
|
|
43
43
|
}
|
|
44
|
-
}
|
|
44
|
+
}
|