@ishlabs/cli 0.17.7 → 0.18.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 +54 -54
- package/dist/commands/ask.d.ts +4 -4
- package/dist/commands/ask.js +66 -66
- package/dist/commands/chat.js +10 -10
- package/dist/commands/config.js +1 -1
- package/dist/commands/docs.js +1 -1
- package/dist/commands/iteration.js +57 -57
- package/dist/commands/mcp.d.ts +23 -0
- package/dist/commands/mcp.js +676 -0
- package/dist/commands/person.d.ts +5 -0
- package/dist/commands/{profile.js → person.js} +197 -162
- package/dist/commands/source.d.ts +6 -2
- package/dist/commands/source.js +35 -30
- package/dist/commands/study-analyze.d.ts +1 -1
- package/dist/commands/study-analyze.js +3 -3
- package/dist/commands/study-participant.d.ts +8 -0
- package/dist/commands/{study-tester.js → study-participant.js} +50 -50
- package/dist/commands/study-run.d.ts +6 -6
- package/dist/commands/study-run.js +295 -271
- package/dist/commands/study.js +89 -66
- package/dist/commands/workspace.js +13 -13
- package/dist/connect.js +5 -5
- package/dist/index.js +6 -4
- package/dist/lib/accessibility-profile.d.ts +1 -1
- package/dist/lib/accessibility-profile.js +1 -1
- package/dist/lib/alias-hydrate.js +4 -4
- package/dist/lib/alias-store.d.ts +5 -5
- package/dist/lib/alias-store.js +8 -8
- package/dist/lib/api-client.d.ts +1 -1
- package/dist/lib/api-client.js +1 -1
- package/dist/lib/billing.d.ts +11 -11
- package/dist/lib/billing.js +16 -16
- package/dist/lib/chat-endpoint-templates.js +1 -1
- package/dist/lib/command-helpers.d.ts +18 -18
- package/dist/lib/command-helpers.js +49 -37
- package/dist/lib/docs.js +560 -386
- package/dist/lib/enums.d.ts +2 -2
- package/dist/lib/enums.js +2 -2
- package/dist/lib/local-sim/browser.d.ts +1 -1
- package/dist/lib/local-sim/browser.js +1 -1
- package/dist/lib/local-sim/debug-report.d.ts +2 -2
- package/dist/lib/local-sim/debug-report.js +3 -3
- package/dist/lib/local-sim/loop.d.ts +5 -5
- package/dist/lib/local-sim/loop.js +38 -38
- package/dist/lib/local-sim/types.d.ts +12 -12
- package/dist/lib/mcp-clients.d.ts +51 -0
- package/dist/lib/mcp-clients.js +175 -0
- package/dist/lib/modality.d.ts +10 -10
- package/dist/lib/modality.js +46 -46
- package/dist/lib/output.d.ts +13 -12
- package/dist/lib/output.js +244 -184
- package/dist/lib/profile-sources.d.ts +64 -16
- package/dist/lib/profile-sources.js +91 -30
- package/dist/lib/skill-content.js +215 -168
- package/dist/lib/study-events.d.ts +3 -3
- package/dist/lib/study-events.js +1 -1
- package/dist/lib/study-inputs.d.ts +11 -1
- package/dist/lib/study-inputs.js +68 -17
- package/dist/lib/types.d.ts +105 -34
- package/package.json +1 -1
- package/dist/commands/profile.d.ts +0 -5
- package/dist/commands/study-tester.d.ts +0 -8
|
@@ -24,7 +24,7 @@ function readTextOrAtFile(value) {
|
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* Parse a JSON-blob flag that also supports `@filepath` (read from disk).
|
|
27
|
-
* Used for
|
|
27
|
+
* Used for participant_pair `--role-criteria-a/-b` and any future blob inputs.
|
|
28
28
|
* Throws a descriptive Error on bad JSON or wrong top-level type.
|
|
29
29
|
*/
|
|
30
30
|
function parseJsonOrAtFile(raw, flagName) {
|
|
@@ -46,27 +46,27 @@ function parseJsonOrAtFile(raw, flagName) {
|
|
|
46
46
|
return parsed;
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
49
|
-
* Pattern C / M12: project each
|
|
49
|
+
* Pattern C / M12: project each participant row on an iteration response so its
|
|
50
50
|
* `alias` and `name` survive `leanJson` (which strips raw UUID values). The
|
|
51
|
-
* existing `id` and `
|
|
51
|
+
* existing `id` and `person` payload still pass through under
|
|
52
52
|
* `--verbose`, but the agent-default `--json` shape now exposes the same
|
|
53
53
|
* `{alias, name, status}` triple `study results` already does, so an agent
|
|
54
|
-
* can correlate
|
|
54
|
+
* can correlate participants across the two verbs.
|
|
55
55
|
*/
|
|
56
|
-
function
|
|
57
|
-
const
|
|
58
|
-
if (!Array.isArray(
|
|
56
|
+
function enrichIterationParticipants(iteration) {
|
|
57
|
+
const participants = iteration.participants;
|
|
58
|
+
if (!Array.isArray(participants))
|
|
59
59
|
return;
|
|
60
|
-
for (const t of
|
|
60
|
+
for (const t of participants) {
|
|
61
61
|
if (!t || typeof t !== "object")
|
|
62
62
|
continue;
|
|
63
|
-
const
|
|
64
|
-
const id =
|
|
63
|
+
const participant = t;
|
|
64
|
+
const id = participant.id ? String(participant.id) : "";
|
|
65
65
|
if (id)
|
|
66
|
-
|
|
67
|
-
const profile =
|
|
68
|
-
if (!
|
|
69
|
-
|
|
66
|
+
participant.alias = tagAlias(ALIAS_PREFIX.participant, id);
|
|
67
|
+
const profile = participant.person;
|
|
68
|
+
if (!participant.name) {
|
|
69
|
+
participant.name = profile?.name ?? participant.instance_name ?? null;
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -164,7 +164,7 @@ function buildIterationDetails(modality, opts) {
|
|
|
164
164
|
case "chat": {
|
|
165
165
|
const mode = opts.chatMode === undefined ? "external_chatbot" : normalizeChatMode(opts.chatMode);
|
|
166
166
|
if (mode === null) {
|
|
167
|
-
throw new ValidationError(`Invalid --chat-mode "${opts.chatMode}". Expected "external_chatbot" or "
|
|
167
|
+
throw new ValidationError(`Invalid --chat-mode "${opts.chatMode}". Expected "external_chatbot" or "participant_pair" (hyphenated variants accepted: "external-chatbot", "participant-pair").`, ["external_chatbot", "participant_pair"]);
|
|
168
168
|
}
|
|
169
169
|
let maxTurns;
|
|
170
170
|
if (opts.maxTurns !== undefined) {
|
|
@@ -179,7 +179,7 @@ function buildIterationDetails(modality, opts) {
|
|
|
179
179
|
...(maxTurns !== undefined && { max_turns: maxTurns }),
|
|
180
180
|
...(opts.earlyTermination !== undefined && { early_termination: opts.earlyTermination }),
|
|
181
181
|
};
|
|
182
|
-
if (mode === "
|
|
182
|
+
if (mode === "participant_pair") {
|
|
183
183
|
// Reject external-chatbot flags so users don't silently lose them.
|
|
184
184
|
const conflictingFlags = [];
|
|
185
185
|
if (opts.chatEndpointId)
|
|
@@ -187,10 +187,10 @@ function buildIterationDetails(modality, opts) {
|
|
|
187
187
|
if (opts.chatEndpointJson)
|
|
188
188
|
conflictingFlags.push("--chat-endpoint-json");
|
|
189
189
|
if (conflictingFlags.length > 0) {
|
|
190
|
-
throw new ValidationError(`--chat-mode
|
|
190
|
+
throw new ValidationError(`--chat-mode participant_pair is incompatible with ${conflictingFlags.join(", ")}. Use --group-a/-b or --role-criteria-a/-b plus --scenario-a/-b instead.`, conflictingFlags);
|
|
191
191
|
}
|
|
192
|
-
const audA = (opts.
|
|
193
|
-
const audB = (opts.
|
|
192
|
+
const audA = (opts.groupA ?? []).map(resolveId);
|
|
193
|
+
const audB = (opts.groupB ?? []).map(resolveId);
|
|
194
194
|
const critARaw = parseJsonOrAtFile(opts.roleCriteriaA, "--role-criteria-a");
|
|
195
195
|
const critBRaw = parseJsonOrAtFile(opts.roleCriteriaB, "--role-criteria-b");
|
|
196
196
|
let critA;
|
|
@@ -205,14 +205,14 @@ function buildIterationDetails(modality, opts) {
|
|
|
205
205
|
const sideAHasInput = audA.length > 0 || !!critA;
|
|
206
206
|
const sideBHasInput = audB.length > 0 || !!critB;
|
|
207
207
|
if (!sideAHasInput || !sideBHasInput) {
|
|
208
|
-
throw new ValidationError("
|
|
208
|
+
throw new ValidationError("participant_pair chat iterations require, for each side, either explicit people (--group-a / --group-b) or a role-criteria filter (--role-criteria-a / --role-criteria-b).", ["--group-a", "--group-b", "--role-criteria-a", "--role-criteria-b"]);
|
|
209
209
|
}
|
|
210
210
|
// 1×N broadcast: the canonical "rehearse one side against N
|
|
211
211
|
// variations" shape. When one side has exactly one explicit
|
|
212
212
|
// profile and the other has more, broadcast the singleton so
|
|
213
213
|
// every conversation has the same fixed side and a distinct
|
|
214
|
-
// varying side. Example: --
|
|
215
|
-
//
|
|
214
|
+
// varying side. Example: --group-a p-rep --group-b
|
|
215
|
+
// p-cto1,p-cto2,p-cto3 → N=3 conversations, same rep vs
|
|
216
216
|
// 3 different CTO personas. Same backend wire shape, just
|
|
217
217
|
// CLI-side ergonomics.
|
|
218
218
|
let audA_final = audA;
|
|
@@ -220,11 +220,11 @@ function buildIterationDetails(modality, opts) {
|
|
|
220
220
|
let broadcastMsg;
|
|
221
221
|
if (audA.length === 1 && audB.length > 1 && !critA && !critB) {
|
|
222
222
|
audA_final = Array(audB.length).fill(audA[0]);
|
|
223
|
-
broadcastMsg = `Broadcasting --
|
|
223
|
+
broadcastMsg = `Broadcasting --group-a (1 profile) to length ${audB.length} to match --group-b — same side-A profile across all ${audB.length} conversations.`;
|
|
224
224
|
}
|
|
225
225
|
else if (audB.length === 1 && audA.length > 1 && !critA && !critB) {
|
|
226
226
|
audB_final = Array(audA.length).fill(audB[0]);
|
|
227
|
-
broadcastMsg = `Broadcasting --
|
|
227
|
+
broadcastMsg = `Broadcasting --group-b (1 profile) to length ${audA.length} to match --group-a — same side-B profile across all ${audA.length} conversations.`;
|
|
228
228
|
}
|
|
229
229
|
if (broadcastMsg) {
|
|
230
230
|
// stderr so it doesn't pollute --json stdout.
|
|
@@ -235,13 +235,13 @@ function buildIterationDetails(modality, opts) {
|
|
|
235
235
|
// resolution happens server-side and may yield differing counts.
|
|
236
236
|
const bothExplicit = audA_final.length > 0 && audB_final.length > 0 && !critA && !critB;
|
|
237
237
|
if (bothExplicit && audA_final.length !== audB_final.length) {
|
|
238
|
-
throw new ValidationError(`--
|
|
238
|
+
throw new ValidationError(`--group-a (${audA_final.length}) and --group-b (${audB_final.length}) cannot be paired. ` +
|
|
239
239
|
`Pick the same number on each side (1:1 by index), or pass exactly one profile on one side to broadcast ` +
|
|
240
|
-
`(e.g. --
|
|
241
|
-
`or use --role-criteria-a/-b on either side to let the backend resolve the pool.`, ["--
|
|
240
|
+
`(e.g. --group-a p-rep --group-b p-cto1,p-cto2,p-cto3), ` +
|
|
241
|
+
`or use --role-criteria-a/-b on either side to let the backend resolve the pool.`, ["--group-a", "--group-b"]);
|
|
242
242
|
}
|
|
243
243
|
if (!opts.scenarioA || !opts.scenarioB) {
|
|
244
|
-
throw new ValidationError("
|
|
244
|
+
throw new ValidationError("participant_pair chat iterations require --scenario-a and --scenario-b (text or @filepath).", ["--scenario-a", "--scenario-b"]);
|
|
245
245
|
}
|
|
246
246
|
const scenarioA = readTextOrAtFile(opts.scenarioA);
|
|
247
247
|
const scenarioB = readTextOrAtFile(opts.scenarioB);
|
|
@@ -255,9 +255,9 @@ function buildIterationDetails(modality, opts) {
|
|
|
255
255
|
return {
|
|
256
256
|
...topLevel,
|
|
257
257
|
mode_details: {
|
|
258
|
-
mode: "
|
|
259
|
-
|
|
260
|
-
|
|
258
|
+
mode: "participant_pair",
|
|
259
|
+
group_a: audA_final,
|
|
260
|
+
group_b: audB_final,
|
|
261
261
|
scenario_a: scenarioA,
|
|
262
262
|
scenario_b: scenarioB,
|
|
263
263
|
initiator_side: initiatorRaw,
|
|
@@ -269,7 +269,7 @@ function buildIterationDetails(modality, opts) {
|
|
|
269
269
|
// external_chatbot (default)
|
|
270
270
|
const endpoint = parseJsonFlag(opts.chatEndpointJson, "--chat-endpoint-json");
|
|
271
271
|
if (!endpoint && !opts.chatEndpointId) {
|
|
272
|
-
throw new Error("Chat iterations require either --chat-endpoint-id (saved endpoint) or --chat-endpoint-json (inline config). For two-AI rehearsal use --chat-mode
|
|
272
|
+
throw new Error("Chat iterations require either --chat-endpoint-id (saved endpoint) or --chat-endpoint-json (inline config). For two-AI rehearsal use --chat-mode participant_pair with --group-a/-b and --scenario-a/-b.");
|
|
273
273
|
}
|
|
274
274
|
return {
|
|
275
275
|
...topLevel,
|
|
@@ -393,11 +393,11 @@ Concept pages: ish docs get-page concepts/iteration
|
|
|
393
393
|
.option("--segmentation-json <json>", "Segmentation JSON — time_based {intervals_seconds, labels?}, section_based {sections[{name,label,...}]}, or page_based {} — media modalities")
|
|
394
394
|
.option("--content-config-json <json>", "Content config JSON — {early_termination, selected_segment_indices?} — media modalities")
|
|
395
395
|
// Chat modality
|
|
396
|
-
.option("--chat-mode <mode>", "Chat mode: external_chatbot (default; probe a customer chatbot) or
|
|
396
|
+
.option("--chat-mode <mode>", "Chat mode: external_chatbot (default; probe a customer chatbot) or participant_pair (two AI people talk to each other)")
|
|
397
397
|
.option("--chat-endpoint-id <id>", "Saved chatbot endpoint id — chat modality, external_chatbot mode (legacy; prefer --endpoint)")
|
|
398
398
|
.option("--chat-endpoint-json <json>", "Inline chatbot endpoint config JSON — chat modality, external_chatbot mode (legacy; prefer --endpoint-config)")
|
|
399
|
-
.option("--max-turns <n>", "Max
|
|
400
|
-
.option("--early-termination", "End the chat session early when the
|
|
399
|
+
.option("--max-turns <n>", "Max participant turns (1-50) — chat modality (default 12)")
|
|
400
|
+
.option("--early-termination", "End the chat session early when the participant signals stop — chat modality")
|
|
401
401
|
// Agent-friendly chat shortcuts: --endpoint <id> resolves a saved
|
|
402
402
|
// chatbot endpoint via alias / UUID and fetches its config inline;
|
|
403
403
|
// --endpoint-config <file> takes a raw config file (or `-` for
|
|
@@ -406,16 +406,16 @@ Concept pages: ish docs get-page concepts/iteration
|
|
|
406
406
|
// external_chatbot mode.
|
|
407
407
|
.option("--endpoint <id>", "Saved chatbot endpoint id (alias or UUID) — chat modality, external_chatbot mode")
|
|
408
408
|
.option("--endpoint-config <file>", "Raw ChatbotEndpointConfig JSON file or `-` for stdin — chat modality, external_chatbot mode")
|
|
409
|
-
//
|
|
409
|
+
// participant_pair (two-AI rehearsal) flags. group_a and group_b must
|
|
410
410
|
// be the same length — pairs are 1:1 by index. Each side has its own
|
|
411
411
|
// scenario + goal; the partner does NOT see the other side's prompt.
|
|
412
|
-
.option("--
|
|
413
|
-
.option("--
|
|
414
|
-
.option("--scenario-a <text-or-@file>", "Side-A scenario + goal text, or @filepath — chat
|
|
415
|
-
.option("--scenario-b <text-or-@file>", "Side-B scenario + goal text, or @filepath — chat
|
|
416
|
-
.option("--initiator-side <a|b>", "Which side speaks first (default: a) — chat
|
|
417
|
-
.option("--role-criteria-a <json-or-@file>", 'RoleCriteria filter for side A (inline JSON or @filepath). Keys: occupation[], min_age, max_age, gender[], country[], education_level_in[], household_in[], locale_type_in[], income_level_in[], employment_status_in[], requires_captions, uses_screen_reader, prefers_reduced_motion, prefers_high_contrast, has_any_accessibility_need. The five *_in arrays accept snake_case spec values; the five accessibility filters are booleans. Persona-first: filters the eligible profile pool without altering personas. Use INSTEAD of --
|
|
418
|
-
.option("--role-criteria-b <json-or-@file>", "RoleCriteria filter for side B — same shape as --role-criteria-a. chat
|
|
412
|
+
.option("--group-a <ids>", "Person IDs/aliases for side A (comma-separated or repeatable). Pass a single person and N on --group-b to broadcast (1×N rehearsal: fix side A, vary side B) — chat participant_pair mode", collectIds, [])
|
|
413
|
+
.option("--group-b <ids>", "Person IDs/aliases for side B (comma-separated or repeatable). When both sides are explicit they must be equal length, BUT if either side is a singleton it's auto-broadcast to match the other (1×N rehearsal) — chat participant_pair mode", collectIds, [])
|
|
414
|
+
.option("--scenario-a <text-or-@file>", "Side-A scenario + goal text, or @filepath — chat participant_pair mode")
|
|
415
|
+
.option("--scenario-b <text-or-@file>", "Side-B scenario + goal text, or @filepath — chat participant_pair mode")
|
|
416
|
+
.option("--initiator-side <a|b>", "Which side speaks first (default: a) — chat participant_pair mode")
|
|
417
|
+
.option("--role-criteria-a <json-or-@file>", 'RoleCriteria filter for side A (inline JSON or @filepath). Keys: occupation[], min_age, max_age, gender[], country[], education_level_in[], household_in[], locale_type_in[], income_level_in[], employment_status_in[], requires_captions, uses_screen_reader, prefers_reduced_motion, prefers_high_contrast, has_any_accessibility_need. The five *_in arrays accept snake_case spec values; the five accessibility filters are booleans. Persona-first: filters the eligible profile pool without altering personas. Use INSTEAD of --group-a or alongside it (criteria then validates the explicit list). chat participant_pair mode.')
|
|
418
|
+
.option("--role-criteria-b <json-or-@file>", "RoleCriteria filter for side B — same shape as --role-criteria-a. chat participant_pair mode.")
|
|
419
419
|
// Escape hatch
|
|
420
420
|
.option("--details-json <json>", "Raw iteration details JSON (overrides individual flags)")
|
|
421
421
|
.addHelpText("after", `
|
|
@@ -517,16 +517,16 @@ Next: \`ish study run\` to dispatch simulations against this iteration.`)
|
|
|
517
517
|
if (!isChat && (opts.endpoint !== undefined || opts.endpointConfig !== undefined)) {
|
|
518
518
|
throw new Error(`This study uses "${modality}" modality — --endpoint / --endpoint-config are for chat studies.`);
|
|
519
519
|
}
|
|
520
|
-
const pairFlagsSet = (opts.
|
|
521
|
-
|| (opts.
|
|
520
|
+
const pairFlagsSet = (opts.groupA && opts.groupA.length > 0)
|
|
521
|
+
|| (opts.groupB && opts.groupB.length > 0)
|
|
522
522
|
|| opts.scenarioA !== undefined
|
|
523
523
|
|| opts.scenarioB !== undefined
|
|
524
524
|
|| opts.initiatorSide !== undefined
|
|
525
525
|
|| opts.roleCriteriaA !== undefined
|
|
526
526
|
|| opts.roleCriteriaB !== undefined
|
|
527
|
-
|| normalizeChatMode(opts.chatMode) === "
|
|
527
|
+
|| normalizeChatMode(opts.chatMode) === "participant_pair";
|
|
528
528
|
if (!isChat && pairFlagsSet) {
|
|
529
|
-
throw new Error(`This study uses "${modality}" modality — --chat-mode / --
|
|
529
|
+
throw new Error(`This study uses "${modality}" modality — --chat-mode / --group-a/-b / --scenario-a/-b / --role-criteria-a/-b are for chat studies.`);
|
|
530
530
|
}
|
|
531
531
|
// Validate per-modality required flags BEFORE any upload so we don't
|
|
532
532
|
// orphan blobs in storage when the wrong flag is passed (e.g.
|
|
@@ -556,9 +556,9 @@ Next: \`ish study run\` to dispatch simulations against this iteration.`)
|
|
|
556
556
|
case "chat": {
|
|
557
557
|
const mode = opts.chatMode === undefined ? "external_chatbot" : normalizeChatMode(opts.chatMode);
|
|
558
558
|
if (mode === null) {
|
|
559
|
-
throw new ValidationError(`Invalid --chat-mode "${opts.chatMode}". Expected "external_chatbot" or "
|
|
559
|
+
throw new ValidationError(`Invalid --chat-mode "${opts.chatMode}". Expected "external_chatbot" or "participant_pair" (hyphenated variants accepted).`, ["external_chatbot", "participant_pair"]);
|
|
560
560
|
}
|
|
561
|
-
if (mode === "
|
|
561
|
+
if (mode === "participant_pair") {
|
|
562
562
|
const conflicting = [];
|
|
563
563
|
if (opts.endpoint)
|
|
564
564
|
conflicting.push("--endpoint");
|
|
@@ -569,15 +569,15 @@ Next: \`ish study run\` to dispatch simulations against this iteration.`)
|
|
|
569
569
|
if (opts.chatEndpointJson)
|
|
570
570
|
conflicting.push("--chat-endpoint-json");
|
|
571
571
|
if (conflicting.length > 0) {
|
|
572
|
-
throw new ValidationError(`--chat-mode
|
|
572
|
+
throw new ValidationError(`--chat-mode participant_pair is incompatible with ${conflicting.join(", ")}. Use --group-a/-b or --role-criteria-a/-b plus --scenario-a/-b instead.`, conflicting);
|
|
573
573
|
}
|
|
574
|
-
const sideAHasInput = (opts.
|
|
575
|
-
const sideBHasInput = (opts.
|
|
574
|
+
const sideAHasInput = (opts.groupA && opts.groupA.length > 0) || !!opts.roleCriteriaA;
|
|
575
|
+
const sideBHasInput = (opts.groupB && opts.groupB.length > 0) || !!opts.roleCriteriaB;
|
|
576
576
|
if (!sideAHasInput || !sideBHasInput) {
|
|
577
|
-
throw new Error("
|
|
577
|
+
throw new Error("participant_pair chat iterations require, for each side, either explicit people (--group-a / --group-b) or a role-criteria filter (--role-criteria-a / --role-criteria-b).");
|
|
578
578
|
}
|
|
579
579
|
if (!opts.scenarioA || !opts.scenarioB) {
|
|
580
|
-
throw new Error("
|
|
580
|
+
throw new Error("participant_pair chat iterations require --scenario-a <text-or-@file> and --scenario-b <text-or-@file>.");
|
|
581
581
|
}
|
|
582
582
|
break;
|
|
583
583
|
}
|
|
@@ -586,7 +586,7 @@ Next: \`ish study run\` to dispatch simulations against this iteration.`)
|
|
|
586
586
|
&& !opts.chatEndpointJson
|
|
587
587
|
&& !opts.endpoint
|
|
588
588
|
&& !opts.endpointConfig) {
|
|
589
|
-
throw new Error("Chat iterations (external_chatbot mode) require one of: --endpoint <id>, --endpoint-config <file>, --chat-endpoint-id, or --chat-endpoint-json. For two-AI rehearsal use --chat-mode
|
|
589
|
+
throw new Error("Chat iterations (external_chatbot mode) require one of: --endpoint <id>, --endpoint-config <file>, --chat-endpoint-id, or --chat-endpoint-json. For two-AI rehearsal use --chat-mode participant_pair.");
|
|
590
590
|
}
|
|
591
591
|
if ((opts.endpoint || opts.endpointConfig)
|
|
592
592
|
&& (opts.chatEndpointId || opts.chatEndpointJson)) {
|
|
@@ -708,7 +708,7 @@ With multiple IDs, returns a {items:[...], total:N} envelope.`)
|
|
|
708
708
|
const result = data;
|
|
709
709
|
if (result.id)
|
|
710
710
|
result.alias = tagAlias(ALIAS_PREFIX.iteration, String(result.id));
|
|
711
|
-
|
|
711
|
+
enrichIterationParticipants(result);
|
|
712
712
|
output(result, globals.json);
|
|
713
713
|
return;
|
|
714
714
|
}
|
|
@@ -717,7 +717,7 @@ With multiple IDs, returns a {items:[...], total:N} envelope.`)
|
|
|
717
717
|
const r = data;
|
|
718
718
|
if (r.id)
|
|
719
719
|
r.alias = tagAlias(ALIAS_PREFIX.iteration, String(r.id));
|
|
720
|
-
|
|
720
|
+
enrichIterationParticipants(r);
|
|
721
721
|
return r;
|
|
722
722
|
}));
|
|
723
723
|
if (globals.json) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ish mcp — wire the ish MCP server into local AI clients.
|
|
3
|
+
*
|
|
4
|
+
* Three verbs:
|
|
5
|
+
* - `ish mcp add` Wire ish MCP into selected clients (idempotent).
|
|
6
|
+
* - `ish mcp list` Show detected clients + ish-MCP wiring status.
|
|
7
|
+
* - `ish mcp remove` Inverse of add — cleanly unwire the ish block.
|
|
8
|
+
*
|
|
9
|
+
* Design choices (per plan workstream C):
|
|
10
|
+
* - No API call required — all writes are local JSON files. We use
|
|
11
|
+
* `runInline` instead of `withClient`.
|
|
12
|
+
* - Atomic writes (tmp file + rename), deep-equal idempotence, and
|
|
13
|
+
* unrelated keys in the target config are preserved verbatim.
|
|
14
|
+
* - Never embed access tokens in the file. The hosted ish MCP server
|
|
15
|
+
* handles OAuth on first connect; we only write the URL.
|
|
16
|
+
* - No interactive prompts by default (CLI is for autonomous agents).
|
|
17
|
+
* `--yes` is required to commit writes under `--json` / non-TTY.
|
|
18
|
+
*
|
|
19
|
+
* Per-client config paths + per-client server-block shapes live in
|
|
20
|
+
* `src/lib/mcp-clients.ts` (MCP_CLIENT_TARGETS).
|
|
21
|
+
*/
|
|
22
|
+
import type { Command } from "commander";
|
|
23
|
+
export declare function registerMcpCommands(program: Command): void;
|