@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
package/dist/commands/config.js
CHANGED
|
@@ -7,7 +7,14 @@ import { output, formatConfigList } from "../lib/output.js";
|
|
|
7
7
|
export function registerConfigCommands(program) {
|
|
8
8
|
const config = program
|
|
9
9
|
.command("config")
|
|
10
|
-
.description("Manage simulation configs")
|
|
10
|
+
.description("Manage simulation configs")
|
|
11
|
+
.addHelpText("after", `
|
|
12
|
+
A simulation config tunes how testers behave during a run (model, timing, retries, etc.).
|
|
13
|
+
Pass \`--config <id>\` to \`ish study run\` to override the default for one dispatch.
|
|
14
|
+
|
|
15
|
+
Configs are global — not scoped to a workspace. The --workspace flag is rejected. Use aliases (c-...) or UUIDs to identify configs.
|
|
16
|
+
|
|
17
|
+
Run \`ish docs overview\` for the full mental model.`);
|
|
11
18
|
config
|
|
12
19
|
.command("list")
|
|
13
20
|
.description("List all simulation configs")
|
|
@@ -22,9 +29,25 @@ export function registerConfigCommands(program) {
|
|
|
22
29
|
.command("create")
|
|
23
30
|
.description("Create a simulation config")
|
|
24
31
|
.requiredOption("--file <path>", "JSON file with config data")
|
|
32
|
+
.addHelpText("after", "\nMinimal body: {\"name\": \"...\", \"model_settings\": {...}}. See `ish config schema` for the full JSON Schema.")
|
|
25
33
|
.action(async (opts, cmd) => {
|
|
26
34
|
await withClient(cmd, async (client, globals) => {
|
|
27
35
|
const body = await readJsonFileOrStdin(opts.file);
|
|
36
|
+
const proposedName = typeof body.name === "string" ? body.name : "";
|
|
37
|
+
// Names are informational; aliases (c-...) are canonical. Warn if a
|
|
38
|
+
// config with the same name already exists so the user isn't surprised
|
|
39
|
+
// when a list shows duplicates.
|
|
40
|
+
if (proposedName) {
|
|
41
|
+
try {
|
|
42
|
+
const existing = await client.get("/dev/simulation-configs");
|
|
43
|
+
if (existing.some((c) => c.name === proposedName)) {
|
|
44
|
+
process.stderr.write(`Note: a config named "${proposedName}" already exists. Aliases (c-...) are the canonical handle.\n`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
// Best-effort: skip the warning if the pre-check fails.
|
|
49
|
+
}
|
|
50
|
+
}
|
|
28
51
|
const data = await client.post("/dev/simulation-configs", body);
|
|
29
52
|
const result = data;
|
|
30
53
|
if (result.id)
|
|
@@ -59,6 +82,7 @@ export function registerConfigCommands(program) {
|
|
|
59
82
|
.description("Update a simulation config")
|
|
60
83
|
.argument("<id>", "Config ID")
|
|
61
84
|
.requiredOption("--file <path>", "JSON file with update data")
|
|
85
|
+
.addHelpText("after", "\nMinimal body: {\"name\": \"...\", \"model_settings\": {...}}. See `ish config schema` for the full JSON Schema.")
|
|
62
86
|
.action(async (id, opts, cmd) => {
|
|
63
87
|
await withClient(cmd, async (client, globals) => {
|
|
64
88
|
const body = await readJsonFileOrStdin(opts.file);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ish docs — Offline, in-binary documentation aimed at AI coding agents.
|
|
3
|
+
*
|
|
4
|
+
* Modeled on LiveKit's `lk docs` and Speakeasy's `speakeasy agent`. The
|
|
5
|
+
* goal is that an agent that only ever sees what `ish --help` and
|
|
6
|
+
* subcommands print can still reach a complete domain primer without
|
|
7
|
+
* needing the README or web docs.
|
|
8
|
+
*
|
|
9
|
+
* Surface:
|
|
10
|
+
* ish docs # alias for `ish docs overview`
|
|
11
|
+
* ish docs overview # mental model + traversal hints
|
|
12
|
+
* ish docs list # every page with one-line description
|
|
13
|
+
* ish docs get-page <slug> # full markdown for one page
|
|
14
|
+
* ish docs search <query> # keyword search across all pages
|
|
15
|
+
*/
|
|
16
|
+
import type { Command } from "commander";
|
|
17
|
+
export declare function registerDocsCommands(program: Command): void;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ish docs — Offline, in-binary documentation aimed at AI coding agents.
|
|
3
|
+
*
|
|
4
|
+
* Modeled on LiveKit's `lk docs` and Speakeasy's `speakeasy agent`. The
|
|
5
|
+
* goal is that an agent that only ever sees what `ish --help` and
|
|
6
|
+
* subcommands print can still reach a complete domain primer without
|
|
7
|
+
* needing the README or web docs.
|
|
8
|
+
*
|
|
9
|
+
* Surface:
|
|
10
|
+
* ish docs # alias for `ish docs overview`
|
|
11
|
+
* ish docs overview # mental model + traversal hints
|
|
12
|
+
* ish docs list # every page with one-line description
|
|
13
|
+
* ish docs get-page <slug> # full markdown for one page
|
|
14
|
+
* ish docs search <query> # keyword search across all pages
|
|
15
|
+
*/
|
|
16
|
+
import { getGlobals } from "../lib/command-helpers.js";
|
|
17
|
+
import { listPages, getPage, searchPages, } from "../lib/docs.js";
|
|
18
|
+
import { output, outputList } from "../lib/output.js";
|
|
19
|
+
function printOverview(json) {
|
|
20
|
+
const page = getPage("overview");
|
|
21
|
+
if (!page)
|
|
22
|
+
throw new Error("Overview page is missing.");
|
|
23
|
+
if (json) {
|
|
24
|
+
output(page, json);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
process.stdout.write(page.body);
|
|
28
|
+
if (!page.body.endsWith("\n"))
|
|
29
|
+
process.stdout.write("\n");
|
|
30
|
+
}
|
|
31
|
+
function printList(json) {
|
|
32
|
+
const pages = listPages();
|
|
33
|
+
if (json) {
|
|
34
|
+
outputList(pages.map((p) => ({
|
|
35
|
+
slug: p.slug,
|
|
36
|
+
title: p.title,
|
|
37
|
+
description: p.description,
|
|
38
|
+
})), json);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const widest = pages.reduce((n, p) => Math.max(n, p.slug.length), 0);
|
|
42
|
+
for (const p of pages) {
|
|
43
|
+
const pad = " ".repeat(widest - p.slug.length + 2);
|
|
44
|
+
process.stdout.write(` ${p.slug}${pad}${p.description}\n`);
|
|
45
|
+
}
|
|
46
|
+
process.stdout.write("\nRead a page: ish docs get-page <slug>\n" +
|
|
47
|
+
"Search: ish docs search <query>\n");
|
|
48
|
+
}
|
|
49
|
+
function printPage(slug, json) {
|
|
50
|
+
const page = getPage(slug);
|
|
51
|
+
if (!page) {
|
|
52
|
+
const available = listPages().map((p) => p.slug);
|
|
53
|
+
const err = new Error(`Unknown page "${slug}". Available pages:\n ${available.join("\n ")}`);
|
|
54
|
+
if (json) {
|
|
55
|
+
output({ error: "page_not_found", slug, available_slugs: available }, json);
|
|
56
|
+
process.exit(4);
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
if (json) {
|
|
61
|
+
output(page, json);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
process.stdout.write(page.body);
|
|
65
|
+
if (!page.body.endsWith("\n"))
|
|
66
|
+
process.stdout.write("\n");
|
|
67
|
+
}
|
|
68
|
+
function printSearch(query, json) {
|
|
69
|
+
const hits = searchPages(query, 20);
|
|
70
|
+
if (json) {
|
|
71
|
+
outputList(hits, json);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (hits.length === 0) {
|
|
75
|
+
process.stdout.write(`No matches for "${query}".\n`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
for (const hit of hits) {
|
|
79
|
+
process.stdout.write(`${hit.slug} — ${hit.title}\n`);
|
|
80
|
+
process.stdout.write(` ${hit.snippet}\n\n`);
|
|
81
|
+
}
|
|
82
|
+
process.stdout.write(`Read a result: ish docs get-page <slug>\n`);
|
|
83
|
+
}
|
|
84
|
+
export function registerDocsCommands(program) {
|
|
85
|
+
const docs = program
|
|
86
|
+
.command("docs")
|
|
87
|
+
.description("Offline docs for agents — mental model, concept pages, search")
|
|
88
|
+
.addHelpText("after", `\nSubcommands:
|
|
89
|
+
overview Mental model + how to navigate (default)
|
|
90
|
+
list All pages with one-line descriptions
|
|
91
|
+
get-page <slug> Full markdown for one page
|
|
92
|
+
search <query> Keyword search across all pages
|
|
93
|
+
|
|
94
|
+
Examples:
|
|
95
|
+
$ ish docs # overview
|
|
96
|
+
$ ish docs list
|
|
97
|
+
$ ish docs get-page concepts/study
|
|
98
|
+
$ ish docs search "assignment"
|
|
99
|
+
$ ish docs get-page reference/json-mode --json | jq -r .body
|
|
100
|
+
`)
|
|
101
|
+
.action((_opts, cmd) => {
|
|
102
|
+
// Bare `ish docs` → overview.
|
|
103
|
+
const globals = getGlobals(cmd);
|
|
104
|
+
printOverview(globals.json);
|
|
105
|
+
});
|
|
106
|
+
docs
|
|
107
|
+
.command("overview")
|
|
108
|
+
.description("Print the agent-facing mental model and traversal hints")
|
|
109
|
+
.addHelpText("after", "\nExamples:\n $ ish docs overview\n $ ish docs overview --json")
|
|
110
|
+
.action((_opts, cmd) => {
|
|
111
|
+
const globals = getGlobals(cmd);
|
|
112
|
+
printOverview(globals.json);
|
|
113
|
+
});
|
|
114
|
+
docs
|
|
115
|
+
.command("list")
|
|
116
|
+
.description("List every doc page with its one-line description")
|
|
117
|
+
.addHelpText("after", "\nExamples:\n $ ish docs list\n $ ish docs list --json | jq '.[].slug'")
|
|
118
|
+
.action((_opts, cmd) => {
|
|
119
|
+
const globals = getGlobals(cmd);
|
|
120
|
+
printList(globals.json);
|
|
121
|
+
});
|
|
122
|
+
docs
|
|
123
|
+
.command("get-page")
|
|
124
|
+
.description("Print the full markdown for one page")
|
|
125
|
+
.argument("<slug>", "Page slug, e.g. concepts/study (see `ish docs list`)")
|
|
126
|
+
.addHelpText("after", `\nExamples:
|
|
127
|
+
$ ish docs get-page overview
|
|
128
|
+
$ ish docs get-page concepts/study
|
|
129
|
+
$ ish docs get-page reference/aliases
|
|
130
|
+
$ ish docs get-page concepts/run-verbs --json`)
|
|
131
|
+
.action((slug, _opts, cmd) => {
|
|
132
|
+
const globals = getGlobals(cmd);
|
|
133
|
+
printPage(slug, globals.json);
|
|
134
|
+
});
|
|
135
|
+
docs
|
|
136
|
+
.command("search")
|
|
137
|
+
.description("Keyword search across every doc page")
|
|
138
|
+
.argument("<query>", "Search query (case-insensitive substring)")
|
|
139
|
+
.addHelpText("after", `\nExamples:
|
|
140
|
+
$ ish docs search assignment
|
|
141
|
+
$ ish docs search "site access"
|
|
142
|
+
$ ish docs search audience --json`)
|
|
143
|
+
.action((query, _opts, cmd) => {
|
|
144
|
+
const globals = getGlobals(cmd);
|
|
145
|
+
printSearch(query, globals.json);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ish init — Install the ish Agent Skill into a project.
|
|
3
|
+
*
|
|
4
|
+
* Spec the skill conforms to: https://agentskills.io/specification
|
|
5
|
+
*
|
|
6
|
+
* Default target is `.claude/skills/ish/` because Claude Code is the
|
|
7
|
+
* largest install base and originator of the SKILL.md format. Other
|
|
8
|
+
* agents (Codex, Cursor, Cline, Roo Code) read `.agents/skills/<name>/`
|
|
9
|
+
* — pass `--target agents` or `--target both`.
|
|
10
|
+
*
|
|
11
|
+
* The skill is materialised from in-binary string constants
|
|
12
|
+
* (src/lib/skill-content.ts) so the same content ships in both `tsc`
|
|
13
|
+
* builds and `bun build --compile` single-binary builds.
|
|
14
|
+
*/
|
|
15
|
+
import type { Command } from "commander";
|
|
16
|
+
export declare function registerInitCommands(program: Command): void;
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ish init — Install the ish Agent Skill into a project.
|
|
3
|
+
*
|
|
4
|
+
* Spec the skill conforms to: https://agentskills.io/specification
|
|
5
|
+
*
|
|
6
|
+
* Default target is `.claude/skills/ish/` because Claude Code is the
|
|
7
|
+
* largest install base and originator of the SKILL.md format. Other
|
|
8
|
+
* agents (Codex, Cursor, Cline, Roo Code) read `.agents/skills/<name>/`
|
|
9
|
+
* — pass `--target agents` or `--target both`.
|
|
10
|
+
*
|
|
11
|
+
* The skill is materialised from in-binary string constants
|
|
12
|
+
* (src/lib/skill-content.ts) so the same content ships in both `tsc`
|
|
13
|
+
* builds and `bun build --compile` single-binary builds.
|
|
14
|
+
*/
|
|
15
|
+
import * as fs from "node:fs";
|
|
16
|
+
import * as path from "node:path";
|
|
17
|
+
import { getGlobals } from "../lib/command-helpers.js";
|
|
18
|
+
import { output } from "../lib/output.js";
|
|
19
|
+
import { buildSkillFiles, getSkillMd, SKILL_TARGETS, } from "../lib/skill-content.js";
|
|
20
|
+
function resolveTargets(opts) {
|
|
21
|
+
if (opts.dir) {
|
|
22
|
+
return [{ key: "claude", path: opts.dir, consumers: ["custom"] }];
|
|
23
|
+
}
|
|
24
|
+
if (opts.target === "both")
|
|
25
|
+
return SKILL_TARGETS;
|
|
26
|
+
return SKILL_TARGETS.filter((t) => t.key === opts.target);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Per-file idempotence: matching contents are skipped, non-matching
|
|
30
|
+
* existing contents trigger a refusal unless --force is set. Files in
|
|
31
|
+
* the directory that aren't part of the bundle are never touched.
|
|
32
|
+
*/
|
|
33
|
+
function writeBundle(root, files, force) {
|
|
34
|
+
const conflicts = [];
|
|
35
|
+
const plan = [];
|
|
36
|
+
for (const file of files) {
|
|
37
|
+
const abs = path.join(root, file.relativePath);
|
|
38
|
+
let prior;
|
|
39
|
+
try {
|
|
40
|
+
prior = fs.readFileSync(abs, "utf-8");
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// not present
|
|
44
|
+
}
|
|
45
|
+
if (prior === file.contents) {
|
|
46
|
+
plan.push({ file, action: "skip" });
|
|
47
|
+
}
|
|
48
|
+
else if (prior !== undefined && !force) {
|
|
49
|
+
conflicts.push(file.relativePath);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
plan.push({ file, action: "write" });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (conflicts.length > 0) {
|
|
56
|
+
throw new Error(`Refusing to overwrite existing files in ${root}:\n` +
|
|
57
|
+
conflicts.map((f) => ` - ${f}`).join("\n") +
|
|
58
|
+
`\n\nRe-run with --force to overwrite, or pass --dir <path> to write elsewhere.`);
|
|
59
|
+
}
|
|
60
|
+
fs.mkdirSync(root, { recursive: true });
|
|
61
|
+
const written = [];
|
|
62
|
+
const skipped = [];
|
|
63
|
+
for (const item of plan) {
|
|
64
|
+
const abs = path.join(root, item.file.relativePath);
|
|
65
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
66
|
+
if (item.action === "skip") {
|
|
67
|
+
skipped.push(item.file.relativePath);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
fs.writeFileSync(abs, item.file.contents, "utf-8");
|
|
71
|
+
written.push(item.file.relativePath);
|
|
72
|
+
}
|
|
73
|
+
return { root, written, skipped };
|
|
74
|
+
}
|
|
75
|
+
function buildHints(targets, opts) {
|
|
76
|
+
if (opts.dir) {
|
|
77
|
+
return [
|
|
78
|
+
"Custom directory: tell your agent where to find this skill (most agents " +
|
|
79
|
+
"look at .claude/skills/<name>/ or .agents/skills/<name>/).",
|
|
80
|
+
];
|
|
81
|
+
}
|
|
82
|
+
const installed = new Set(targets.map((t) => t.key));
|
|
83
|
+
const hints = [];
|
|
84
|
+
for (const target of SKILL_TARGETS) {
|
|
85
|
+
if (installed.has(target.key))
|
|
86
|
+
continue;
|
|
87
|
+
const consumers = target.consumers.join(", ");
|
|
88
|
+
hints.push(`${consumers}: install to ${target.path} via ` +
|
|
89
|
+
`\`ish init --target ${target.key}${opts.force ? " --force" : ""}\` ` +
|
|
90
|
+
`(or copy: \`cp -r ${targets[0]?.path ?? ".claude/skills/ish"} ${target.path}\`).`);
|
|
91
|
+
}
|
|
92
|
+
return hints;
|
|
93
|
+
}
|
|
94
|
+
export function registerInitCommands(program) {
|
|
95
|
+
program
|
|
96
|
+
.command("init")
|
|
97
|
+
.description("Install the ish Agent Skill into a project (Claude Code, Codex, Cursor, Cline, Roo Code)")
|
|
98
|
+
.option("--target <which>", "Where to install: claude (.claude/skills/ish), agents (.agents/skills/ish), or both", "claude")
|
|
99
|
+
.option("--dir <path>", "Write to a custom directory instead of a recognised target path")
|
|
100
|
+
.option("--stdout", "Print the SKILL.md to stdout instead of writing files (skips references/)")
|
|
101
|
+
.option("--force", "Overwrite existing files in the target directory")
|
|
102
|
+
.addHelpText("after", `\nExamples:
|
|
103
|
+
$ ish init # → .claude/skills/ish/ (Claude Code)
|
|
104
|
+
$ ish init --target agents # → .agents/skills/ish/ (Codex / Cursor / Cline / Roo)
|
|
105
|
+
$ ish init --target both # both of the above
|
|
106
|
+
$ ish init --dir vendor/ish-skill # custom path
|
|
107
|
+
$ ish init --stdout > /tmp/SKILL.md # just the SKILL.md
|
|
108
|
+
$ ish init --force # overwrite existing skill files
|
|
109
|
+
|
|
110
|
+
Where do skills live?
|
|
111
|
+
Claude Code .claude/skills/<name>/ (also read by Cursor for back-compat)
|
|
112
|
+
Codex / Cursor / .agents/skills/<name>/ (cross-vendor convention)
|
|
113
|
+
Cline / Roo Code
|
|
114
|
+
|
|
115
|
+
Spec: https://agentskills.io/specification`)
|
|
116
|
+
.action(async (rawOpts, cmd) => {
|
|
117
|
+
const globals = getGlobals(cmd);
|
|
118
|
+
try {
|
|
119
|
+
const target = String(rawOpts.target ?? "claude");
|
|
120
|
+
if (!["claude", "agents", "both"].includes(target)) {
|
|
121
|
+
throw new Error(`Invalid --target "${target}". Valid: claude | agents | both.`);
|
|
122
|
+
}
|
|
123
|
+
const opts = {
|
|
124
|
+
target,
|
|
125
|
+
dir: typeof rawOpts.dir === "string" ? rawOpts.dir : undefined,
|
|
126
|
+
stdout: rawOpts.stdout === true,
|
|
127
|
+
force: rawOpts.force === true,
|
|
128
|
+
};
|
|
129
|
+
if (opts.stdout) {
|
|
130
|
+
process.stdout.write(getSkillMd());
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const targets = resolveTargets(opts);
|
|
134
|
+
const files = buildSkillFiles();
|
|
135
|
+
const results = [];
|
|
136
|
+
for (const t of targets) {
|
|
137
|
+
results.push(writeBundle(t.path, files, !!opts.force));
|
|
138
|
+
}
|
|
139
|
+
const hints = buildHints(targets, opts);
|
|
140
|
+
if (globals.json) {
|
|
141
|
+
output({
|
|
142
|
+
ok: true,
|
|
143
|
+
installed: results.map((r) => ({
|
|
144
|
+
root: r.root,
|
|
145
|
+
written: r.written,
|
|
146
|
+
skipped: r.skipped,
|
|
147
|
+
})),
|
|
148
|
+
other_paths: hints,
|
|
149
|
+
}, true);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
for (const r of results) {
|
|
153
|
+
if (r.written.length === 0 && r.skipped.length > 0) {
|
|
154
|
+
process.stdout.write(`Skill already up to date at ${r.root} (${r.skipped.length} files unchanged).\n`);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
process.stdout.write(`Installed skill at ${r.root}\n`);
|
|
158
|
+
for (const f of r.written)
|
|
159
|
+
process.stdout.write(` + ${f}\n`);
|
|
160
|
+
for (const f of r.skipped)
|
|
161
|
+
process.stdout.write(` = ${f} (unchanged)\n`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (hints.length > 0) {
|
|
165
|
+
process.stdout.write("\nOther agent paths you may want to populate:\n");
|
|
166
|
+
for (const h of hints)
|
|
167
|
+
process.stdout.write(` - ${h}\n`);
|
|
168
|
+
}
|
|
169
|
+
process.stdout.write("\nTip: run `ish docs overview` next — it's the entry point your agent should hit first.\n");
|
|
170
|
+
}
|
|
171
|
+
catch (err) {
|
|
172
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
173
|
+
if (globals.json) {
|
|
174
|
+
output({ ok: false, error: message }, true);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
process.stderr.write(`${message}\n`);
|
|
178
|
+
}
|
|
179
|
+
process.exit(2);
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ish iteration — Manage iterations
|
|
2
|
+
* ish iteration — Manage iterations of a study.
|
|
3
|
+
*
|
|
4
|
+
* An iteration carries the run-time details (URL for interactive,
|
|
5
|
+
* content/file for media). Create one before `ish study run` dispatches
|
|
6
|
+
* simulations against it.
|
|
3
7
|
*/
|
|
4
8
|
import type { Command } from "commander";
|
|
5
9
|
export declare function registerIterationCommands(program: Command): void;
|