@growthub/cli 0.4.3 → 0.5.1
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/dist/index.js +1577 -727
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6067,8 +6067,8 @@ function sanitizeRestoreErrorMessage(error) {
|
|
|
6067
6067
|
return error instanceof Error ? error.message : String(error);
|
|
6068
6068
|
}
|
|
6069
6069
|
function timestamp56(date2 = /* @__PURE__ */ new Date()) {
|
|
6070
|
-
const
|
|
6071
|
-
return `${date2.getFullYear()}${
|
|
6070
|
+
const pad2 = (n) => String(n).padStart(2, "0");
|
|
6071
|
+
return `${date2.getFullYear()}${pad2(date2.getMonth() + 1)}${pad2(date2.getDate())}-${pad2(date2.getHours())}${pad2(date2.getMinutes())}${pad2(date2.getSeconds())}`;
|
|
6072
6072
|
}
|
|
6073
6073
|
function pruneOldBackups(backupDir, retentionDays, filenamePrefix) {
|
|
6074
6074
|
if (!existsSync(backupDir)) return 0;
|
|
@@ -8023,16 +8023,16 @@ function buildUrl(apiBase, path47) {
|
|
|
8023
8023
|
if (query) url.search = query;
|
|
8024
8024
|
return url.toString();
|
|
8025
8025
|
}
|
|
8026
|
-
function safeParseJson(
|
|
8026
|
+
function safeParseJson(text65) {
|
|
8027
8027
|
try {
|
|
8028
|
-
return JSON.parse(
|
|
8028
|
+
return JSON.parse(text65);
|
|
8029
8029
|
} catch {
|
|
8030
|
-
return
|
|
8030
|
+
return text65;
|
|
8031
8031
|
}
|
|
8032
8032
|
}
|
|
8033
8033
|
async function toApiError(response) {
|
|
8034
|
-
const
|
|
8035
|
-
const parsed = safeParseJson(
|
|
8034
|
+
const text65 = await response.text();
|
|
8035
|
+
const parsed = safeParseJson(text65);
|
|
8036
8036
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
8037
8037
|
const body = parsed;
|
|
8038
8038
|
const message = typeof body.error === "string" && body.error.trim() || typeof body.message === "string" && body.message.trim() || `Request failed with status ${response.status}`;
|
|
@@ -8127,11 +8127,11 @@ var init_http = __esm({
|
|
|
8127
8127
|
if (response.status === 204) {
|
|
8128
8128
|
return null;
|
|
8129
8129
|
}
|
|
8130
|
-
const
|
|
8131
|
-
if (!
|
|
8130
|
+
const text65 = await response.text();
|
|
8131
|
+
if (!text65.trim()) {
|
|
8132
8132
|
return null;
|
|
8133
8133
|
}
|
|
8134
|
-
return safeParseJson(
|
|
8134
|
+
return safeParseJson(text65);
|
|
8135
8135
|
}
|
|
8136
8136
|
};
|
|
8137
8137
|
}
|
|
@@ -9604,9 +9604,9 @@ __export(github_exports, {
|
|
|
9604
9604
|
registerGithubCommands: () => registerGithubCommands
|
|
9605
9605
|
});
|
|
9606
9606
|
import * as p26 from "@clack/prompts";
|
|
9607
|
-
import
|
|
9607
|
+
import pc40 from "picocolors";
|
|
9608
9608
|
import open2 from "open";
|
|
9609
|
-
async function
|
|
9609
|
+
async function sleep2(ms) {
|
|
9610
9610
|
await new Promise((r) => setTimeout(r, ms));
|
|
9611
9611
|
}
|
|
9612
9612
|
async function githubLogin(opts) {
|
|
@@ -9627,14 +9627,14 @@ async function githubLogin(opts) {
|
|
|
9627
9627
|
if (opts.json) {
|
|
9628
9628
|
console.log(JSON.stringify({ status: "ok", mode: "pat", login: profile.login }, null, 2));
|
|
9629
9629
|
} else {
|
|
9630
|
-
p26.log.success(`Connected to GitHub as ${
|
|
9630
|
+
p26.log.success(`Connected to GitHub as ${pc40.cyan(profile.login)} (PAT).`);
|
|
9631
9631
|
}
|
|
9632
9632
|
return;
|
|
9633
9633
|
}
|
|
9634
|
-
p26.intro(
|
|
9634
|
+
p26.intro(pc40.cyan("GitHub device flow login"));
|
|
9635
9635
|
const start = await startDeviceFlow();
|
|
9636
9636
|
p26.log.step(
|
|
9637
|
-
`Open ${
|
|
9637
|
+
`Open ${pc40.cyan(start.verificationUri)} and enter code ${pc40.yellow(start.userCode)}`
|
|
9638
9638
|
);
|
|
9639
9639
|
if (!opts.noBrowser) {
|
|
9640
9640
|
try {
|
|
@@ -9648,7 +9648,7 @@ async function githubLogin(opts) {
|
|
|
9648
9648
|
const spinner12 = p26.spinner();
|
|
9649
9649
|
spinner12.start("Waiting for GitHub authorization...");
|
|
9650
9650
|
while (Date.now() - startedAt < maxMs) {
|
|
9651
|
-
await
|
|
9651
|
+
await sleep2(interval * 1e3);
|
|
9652
9652
|
const poll = await pollDeviceFlow(start.deviceCode);
|
|
9653
9653
|
if (poll.status === "authorized" && poll.token) {
|
|
9654
9654
|
spinner12.stop("Authorization received.");
|
|
@@ -9663,7 +9663,7 @@ async function githubLogin(opts) {
|
|
|
9663
9663
|
if (opts.json) {
|
|
9664
9664
|
console.log(JSON.stringify({ status: "ok", mode: "device-flow", login: profile.login }));
|
|
9665
9665
|
} else {
|
|
9666
|
-
p26.outro(`Connected to GitHub as ${
|
|
9666
|
+
p26.outro(`Connected to GitHub as ${pc40.cyan(profile.login)}.`);
|
|
9667
9667
|
}
|
|
9668
9668
|
return;
|
|
9669
9669
|
}
|
|
@@ -9727,7 +9727,7 @@ async function githubWhoami(opts = {}) {
|
|
|
9727
9727
|
return;
|
|
9728
9728
|
}
|
|
9729
9729
|
if (token) {
|
|
9730
|
-
const status = directExpired ?
|
|
9730
|
+
const status = directExpired ? pc40.red("expired") : pc40.green("active");
|
|
9731
9731
|
p26.log.message(
|
|
9732
9732
|
`GitHub (direct): ${status} login=${profile?.login ?? token.login ?? "?"} mode=${token.authMode} scopes=[${token.scopes.join(", ")}]`
|
|
9733
9733
|
);
|
|
@@ -9735,7 +9735,7 @@ async function githubWhoami(opts = {}) {
|
|
|
9735
9735
|
if (bridge.growthubConnected) {
|
|
9736
9736
|
if (bridgeGithub) {
|
|
9737
9737
|
p26.log.message(
|
|
9738
|
-
`GitHub (via Growthub bridge): ${
|
|
9738
|
+
`GitHub (via Growthub bridge): ${pc40.green("connected")} handle=${bridgeGithub.handle ?? "?"} growthub=${bridge.growthubLogin ?? "?"} scopes=[${(bridgeGithub.scopes ?? []).join(", ")}]`
|
|
9739
9739
|
);
|
|
9740
9740
|
} else if (bridge.bridgeAvailable) {
|
|
9741
9741
|
p26.log.info(
|
|
@@ -9743,7 +9743,7 @@ async function githubWhoami(opts = {}) {
|
|
|
9743
9743
|
);
|
|
9744
9744
|
}
|
|
9745
9745
|
}
|
|
9746
|
-
p26.log.message(`Effective auth source: ${
|
|
9746
|
+
p26.log.message(`Effective auth source: ${pc40.cyan(effectiveSource)}`);
|
|
9747
9747
|
}
|
|
9748
9748
|
function githubLogout(opts = {}) {
|
|
9749
9749
|
clearGithubToken();
|
|
@@ -9780,7 +9780,7 @@ init_onboard();
|
|
|
9780
9780
|
init_doctor();
|
|
9781
9781
|
import { Command } from "commander";
|
|
9782
9782
|
import * as p31 from "@clack/prompts";
|
|
9783
|
-
import
|
|
9783
|
+
import pc45 from "picocolors";
|
|
9784
9784
|
import fs38 from "node:fs";
|
|
9785
9785
|
import path46 from "node:path";
|
|
9786
9786
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
@@ -10309,8 +10309,8 @@ function printClaudeStreamEvent(raw, debug) {
|
|
|
10309
10309
|
const block = blockRaw;
|
|
10310
10310
|
const blockType = typeof block.type === "string" ? block.type : "";
|
|
10311
10311
|
if (blockType === "text") {
|
|
10312
|
-
const
|
|
10313
|
-
if (
|
|
10312
|
+
const text65 = typeof block.text === "string" ? block.text : "";
|
|
10313
|
+
if (text65) console.log(pc9.green(`assistant: ${text65}`));
|
|
10314
10314
|
} else if (blockType === "tool_use") {
|
|
10315
10315
|
const name = typeof block.name === "string" ? block.name : "unknown";
|
|
10316
10316
|
console.log(pc9.yellow(`tool_call: ${name}`));
|
|
@@ -10402,13 +10402,13 @@ function printItemStarted(item) {
|
|
|
10402
10402
|
function printItemCompleted(item) {
|
|
10403
10403
|
const itemType = asString(item.type);
|
|
10404
10404
|
if (itemType === "agent_message") {
|
|
10405
|
-
const
|
|
10406
|
-
if (
|
|
10405
|
+
const text65 = asString(item.text);
|
|
10406
|
+
if (text65) console.log(pc10.green(`assistant: ${text65}`));
|
|
10407
10407
|
return true;
|
|
10408
10408
|
}
|
|
10409
10409
|
if (itemType === "reasoning") {
|
|
10410
|
-
const
|
|
10411
|
-
if (
|
|
10410
|
+
const text65 = asString(item.text);
|
|
10411
|
+
if (text65) console.log(pc10.gray(`thinking: ${text65}`));
|
|
10412
10412
|
return true;
|
|
10413
10413
|
}
|
|
10414
10414
|
if (itemType === "tool_use") {
|
|
@@ -10458,9 +10458,9 @@ function printItemCompleted(item) {
|
|
|
10458
10458
|
}
|
|
10459
10459
|
if (itemType === "tool_result") {
|
|
10460
10460
|
const isError = item.is_error === true || asString(item.status) === "error";
|
|
10461
|
-
const
|
|
10461
|
+
const text65 = asString(item.content) || asString(item.result) || asString(item.output);
|
|
10462
10462
|
console.log((isError ? pc10.red : pc10.cyan)(`tool_result${isError ? " (error)" : ""}`));
|
|
10463
|
-
if (
|
|
10463
|
+
if (text65) console.log((isError ? pc10.red : pc10.gray)(text65));
|
|
10464
10464
|
return true;
|
|
10465
10465
|
}
|
|
10466
10466
|
return false;
|
|
@@ -10579,8 +10579,8 @@ function stringifyUnknown(value) {
|
|
|
10579
10579
|
}
|
|
10580
10580
|
function printUserMessage(messageRaw) {
|
|
10581
10581
|
if (typeof messageRaw === "string") {
|
|
10582
|
-
const
|
|
10583
|
-
if (
|
|
10582
|
+
const text65 = messageRaw.trim();
|
|
10583
|
+
if (text65) console.log(pc11.gray(`user: ${text65}`));
|
|
10584
10584
|
return;
|
|
10585
10585
|
}
|
|
10586
10586
|
const message = asRecord2(messageRaw);
|
|
@@ -10593,14 +10593,14 @@ function printUserMessage(messageRaw) {
|
|
|
10593
10593
|
if (!part) continue;
|
|
10594
10594
|
const type = asString2(part.type).trim();
|
|
10595
10595
|
if (type !== "output_text" && type !== "text") continue;
|
|
10596
|
-
const
|
|
10597
|
-
if (
|
|
10596
|
+
const text65 = asString2(part.text).trim();
|
|
10597
|
+
if (text65) console.log(pc11.gray(`user: ${text65}`));
|
|
10598
10598
|
}
|
|
10599
10599
|
}
|
|
10600
10600
|
function printAssistantMessage(messageRaw) {
|
|
10601
10601
|
if (typeof messageRaw === "string") {
|
|
10602
|
-
const
|
|
10603
|
-
if (
|
|
10602
|
+
const text65 = messageRaw.trim();
|
|
10603
|
+
if (text65) console.log(pc11.green(`assistant: ${text65}`));
|
|
10604
10604
|
return;
|
|
10605
10605
|
}
|
|
10606
10606
|
const message = asRecord2(messageRaw);
|
|
@@ -10613,13 +10613,13 @@ function printAssistantMessage(messageRaw) {
|
|
|
10613
10613
|
if (!part) continue;
|
|
10614
10614
|
const type = asString2(part.type).trim();
|
|
10615
10615
|
if (type === "output_text" || type === "text") {
|
|
10616
|
-
const
|
|
10617
|
-
if (
|
|
10616
|
+
const text65 = asString2(part.text).trim();
|
|
10617
|
+
if (text65) console.log(pc11.green(`assistant: ${text65}`));
|
|
10618
10618
|
continue;
|
|
10619
10619
|
}
|
|
10620
10620
|
if (type === "thinking") {
|
|
10621
|
-
const
|
|
10622
|
-
if (
|
|
10621
|
+
const text65 = asString2(part.text).trim();
|
|
10622
|
+
if (text65) console.log(pc11.gray(`thinking: ${text65}`));
|
|
10623
10623
|
continue;
|
|
10624
10624
|
}
|
|
10625
10625
|
if (type === "tool_call") {
|
|
@@ -10739,8 +10739,8 @@ function printCursorStreamEvent(raw, _debug) {
|
|
|
10739
10739
|
return;
|
|
10740
10740
|
}
|
|
10741
10741
|
if (type === "thinking") {
|
|
10742
|
-
const
|
|
10743
|
-
if (
|
|
10742
|
+
const text65 = asString2(parsed.text).trim() || asString2(asRecord2(parsed.delta)?.text).trim();
|
|
10743
|
+
if (text65) console.log(pc11.gray(`thinking: ${text65}`));
|
|
10744
10744
|
return;
|
|
10745
10745
|
}
|
|
10746
10746
|
if (type === "tool_call") {
|
|
@@ -10778,8 +10778,8 @@ function printCursorStreamEvent(raw, _debug) {
|
|
|
10778
10778
|
}
|
|
10779
10779
|
if (type === "text") {
|
|
10780
10780
|
const part = asRecord2(parsed.part);
|
|
10781
|
-
const
|
|
10782
|
-
if (
|
|
10781
|
+
const text65 = asString2(part?.text);
|
|
10782
|
+
if (text65) console.log(pc11.green(`assistant: ${text65}`));
|
|
10783
10783
|
return;
|
|
10784
10784
|
}
|
|
10785
10785
|
if (type === "tool_use") {
|
|
@@ -10842,8 +10842,8 @@ function errorText2(value) {
|
|
|
10842
10842
|
}
|
|
10843
10843
|
function printTextMessage(prefix, colorize, messageRaw) {
|
|
10844
10844
|
if (typeof messageRaw === "string") {
|
|
10845
|
-
const
|
|
10846
|
-
if (
|
|
10845
|
+
const text65 = messageRaw.trim();
|
|
10846
|
+
if (text65) console.log(colorize(`${prefix}: ${text65}`));
|
|
10847
10847
|
return;
|
|
10848
10848
|
}
|
|
10849
10849
|
const message = asRecord3(messageRaw);
|
|
@@ -10856,13 +10856,13 @@ function printTextMessage(prefix, colorize, messageRaw) {
|
|
|
10856
10856
|
if (!part) continue;
|
|
10857
10857
|
const type = asString3(part.type).trim();
|
|
10858
10858
|
if (type === "output_text" || type === "text" || type === "content") {
|
|
10859
|
-
const
|
|
10860
|
-
if (
|
|
10859
|
+
const text65 = asString3(part.text).trim() || asString3(part.content).trim();
|
|
10860
|
+
if (text65) console.log(colorize(`${prefix}: ${text65}`));
|
|
10861
10861
|
continue;
|
|
10862
10862
|
}
|
|
10863
10863
|
if (type === "thinking") {
|
|
10864
|
-
const
|
|
10865
|
-
if (
|
|
10864
|
+
const text65 = asString3(part.text).trim();
|
|
10865
|
+
if (text65) console.log(pc12.gray(`thinking: ${text65}`));
|
|
10866
10866
|
continue;
|
|
10867
10867
|
}
|
|
10868
10868
|
if (type === "tool_call") {
|
|
@@ -10914,8 +10914,8 @@ function printGeminiStreamEvent(raw, _debug) {
|
|
|
10914
10914
|
return;
|
|
10915
10915
|
}
|
|
10916
10916
|
if (subtype === "error") {
|
|
10917
|
-
const
|
|
10918
|
-
if (
|
|
10917
|
+
const text65 = errorText2(parsed.error ?? parsed.message ?? parsed.detail);
|
|
10918
|
+
if (text65) console.log(pc12.red(`error: ${text65}`));
|
|
10919
10919
|
return;
|
|
10920
10920
|
}
|
|
10921
10921
|
console.log(pc12.blue(`system: ${subtype || "event"}`));
|
|
@@ -10930,8 +10930,8 @@ function printGeminiStreamEvent(raw, _debug) {
|
|
|
10930
10930
|
return;
|
|
10931
10931
|
}
|
|
10932
10932
|
if (type === "thinking") {
|
|
10933
|
-
const
|
|
10934
|
-
if (
|
|
10933
|
+
const text65 = asString3(parsed.text).trim() || asString3(asRecord3(parsed.delta)?.text).trim();
|
|
10934
|
+
if (text65) console.log(pc12.gray(`thinking: ${text65}`));
|
|
10935
10935
|
return;
|
|
10936
10936
|
}
|
|
10937
10937
|
if (type === "tool_call") {
|
|
@@ -10967,8 +10967,8 @@ function printGeminiStreamEvent(raw, _debug) {
|
|
|
10967
10967
|
return;
|
|
10968
10968
|
}
|
|
10969
10969
|
if (type === "error") {
|
|
10970
|
-
const
|
|
10971
|
-
if (
|
|
10970
|
+
const text65 = errorText2(parsed.error ?? parsed.message ?? parsed.detail);
|
|
10971
|
+
if (text65) console.log(pc12.red(`error: ${text65}`));
|
|
10972
10972
|
return;
|
|
10973
10973
|
}
|
|
10974
10974
|
console.log(line);
|
|
@@ -10976,9 +10976,9 @@ function printGeminiStreamEvent(raw, _debug) {
|
|
|
10976
10976
|
|
|
10977
10977
|
// ../packages/adapters/opencode-local/src/cli/format-event.ts
|
|
10978
10978
|
import pc13 from "picocolors";
|
|
10979
|
-
function safeJsonParse(
|
|
10979
|
+
function safeJsonParse(text65) {
|
|
10980
10980
|
try {
|
|
10981
|
-
return JSON.parse(
|
|
10981
|
+
return JSON.parse(text65);
|
|
10982
10982
|
} catch {
|
|
10983
10983
|
return null;
|
|
10984
10984
|
}
|
|
@@ -11022,14 +11022,14 @@ function printOpenCodeStreamEvent(raw, _debug) {
|
|
|
11022
11022
|
}
|
|
11023
11023
|
if (type === "text") {
|
|
11024
11024
|
const part = asRecord4(parsed.part);
|
|
11025
|
-
const
|
|
11026
|
-
if (
|
|
11025
|
+
const text65 = asString4(part?.text).trim();
|
|
11026
|
+
if (text65) console.log(pc13.green(`assistant: ${text65}`));
|
|
11027
11027
|
return;
|
|
11028
11028
|
}
|
|
11029
11029
|
if (type === "reasoning") {
|
|
11030
11030
|
const part = asRecord4(parsed.part);
|
|
11031
|
-
const
|
|
11032
|
-
if (
|
|
11031
|
+
const text65 = asString4(part?.text).trim();
|
|
11032
|
+
if (text65) console.log(pc13.gray(`thinking: ${text65}`));
|
|
11033
11033
|
return;
|
|
11034
11034
|
}
|
|
11035
11035
|
if (type === "tool_use") {
|
|
@@ -11077,9 +11077,9 @@ function printOpenCodeStreamEvent(raw, _debug) {
|
|
|
11077
11077
|
|
|
11078
11078
|
// ../packages/adapters/pi-local/src/cli/format-event.ts
|
|
11079
11079
|
import pc14 from "picocolors";
|
|
11080
|
-
function safeJsonParse2(
|
|
11080
|
+
function safeJsonParse2(text65) {
|
|
11081
11081
|
try {
|
|
11082
|
-
return JSON.parse(
|
|
11082
|
+
return JSON.parse(text65);
|
|
11083
11083
|
} catch {
|
|
11084
11084
|
return null;
|
|
11085
11085
|
}
|
|
@@ -11121,9 +11121,9 @@ function printPiStreamEvent(raw, _debug) {
|
|
|
11121
11121
|
const message = asRecord5(parsed.message);
|
|
11122
11122
|
if (message) {
|
|
11123
11123
|
const content = message.content;
|
|
11124
|
-
const
|
|
11125
|
-
if (
|
|
11126
|
-
console.log(pc14.green(`assistant: ${
|
|
11124
|
+
const text65 = extractTextContent(content);
|
|
11125
|
+
if (text65) {
|
|
11126
|
+
console.log(pc14.green(`assistant: ${text65}`));
|
|
11127
11127
|
}
|
|
11128
11128
|
}
|
|
11129
11129
|
return;
|
|
@@ -13032,17 +13032,17 @@ function assertDeleteConfirmation(company, opts) {
|
|
|
13032
13032
|
if (!opts.yes) {
|
|
13033
13033
|
throw new Error("Deletion requires --yes.");
|
|
13034
13034
|
}
|
|
13035
|
-
const
|
|
13036
|
-
if (!
|
|
13035
|
+
const confirm15 = opts.confirm?.trim();
|
|
13036
|
+
if (!confirm15) {
|
|
13037
13037
|
throw new Error(
|
|
13038
13038
|
"Deletion requires --confirm <value> where value matches the company ID or issue prefix."
|
|
13039
13039
|
);
|
|
13040
13040
|
}
|
|
13041
|
-
const confirmsById =
|
|
13042
|
-
const confirmsByPrefix =
|
|
13041
|
+
const confirmsById = confirm15 === company.id;
|
|
13042
|
+
const confirmsByPrefix = confirm15.toUpperCase() === company.issuePrefix.toUpperCase();
|
|
13043
13043
|
if (!confirmsById && !confirmsByPrefix) {
|
|
13044
13044
|
throw new Error(
|
|
13045
|
-
`Confirmation '${
|
|
13045
|
+
`Confirmation '${confirm15}' does not match target company. Expected ID '${company.id}' or prefix '${company.issuePrefix}'.`
|
|
13046
13046
|
);
|
|
13047
13047
|
}
|
|
13048
13048
|
}
|
|
@@ -13433,8 +13433,8 @@ function filterIssueRows(rows, match) {
|
|
|
13433
13433
|
if (!match?.trim()) return rows;
|
|
13434
13434
|
const needle = match.trim().toLowerCase();
|
|
13435
13435
|
return rows.filter((row) => {
|
|
13436
|
-
const
|
|
13437
|
-
return
|
|
13436
|
+
const text65 = [row.identifier, row.title, row.description].filter((part) => Boolean(part)).join("\n").toLowerCase();
|
|
13437
|
+
return text65.includes(needle);
|
|
13438
13438
|
});
|
|
13439
13439
|
}
|
|
13440
13440
|
|
|
@@ -15321,7 +15321,7 @@ init_banner();
|
|
|
15321
15321
|
import path33 from "node:path";
|
|
15322
15322
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
15323
15323
|
import * as p19 from "@clack/prompts";
|
|
15324
|
-
import
|
|
15324
|
+
import pc30 from "picocolors";
|
|
15325
15325
|
|
|
15326
15326
|
// src/commands/kit-fork.ts
|
|
15327
15327
|
import * as p18 from "@clack/prompts";
|
|
@@ -16636,22 +16636,47 @@ async function kitForkConnect(opts) {
|
|
|
16636
16636
|
async function kitForkPolicyCommand(opts) {
|
|
16637
16637
|
const reg = findRegistrationOrThrow(opts.forkId);
|
|
16638
16638
|
let policy = readKitForkPolicy(reg.forkPath);
|
|
16639
|
+
const before = JSON.parse(JSON.stringify(policy));
|
|
16639
16640
|
if (opts.set && opts.set.length > 0) {
|
|
16640
16641
|
for (const entry of opts.set) {
|
|
16641
16642
|
policy = applyPolicyAssignment(policy, entry);
|
|
16642
16643
|
}
|
|
16643
|
-
|
|
16644
|
-
|
|
16645
|
-
|
|
16646
|
-
|
|
16647
|
-
|
|
16648
|
-
|
|
16649
|
-
|
|
16644
|
+
if (!opts.dryRun) {
|
|
16645
|
+
writeKitForkPolicy(reg.forkPath, policy);
|
|
16646
|
+
appendKitForkTraceEvent(reg.forkPath, {
|
|
16647
|
+
forkId: reg.forkId,
|
|
16648
|
+
kitId: reg.kitId,
|
|
16649
|
+
type: "policy_updated",
|
|
16650
|
+
summary: `Policy updated via CLI: ${opts.set.join(", ")}`,
|
|
16651
|
+
detail: { changedFields: diffPolicyFields(before, policy) }
|
|
16652
|
+
});
|
|
16653
|
+
}
|
|
16654
|
+
}
|
|
16655
|
+
if (opts.edit) {
|
|
16656
|
+
const next = await runInteractivePolicyEditor(reg.forkId, policy);
|
|
16657
|
+
if (!next) {
|
|
16658
|
+
p17.log.info("Policy edit cancelled \u2014 no changes written.");
|
|
16659
|
+
return;
|
|
16660
|
+
}
|
|
16661
|
+
policy = next;
|
|
16662
|
+
if (!opts.dryRun) {
|
|
16663
|
+
writeKitForkPolicy(reg.forkPath, policy);
|
|
16664
|
+
appendKitForkTraceEvent(reg.forkPath, {
|
|
16665
|
+
forkId: reg.forkId,
|
|
16666
|
+
kitId: reg.kitId,
|
|
16667
|
+
type: "policy_updated",
|
|
16668
|
+
summary: `Policy updated via interactive editor`,
|
|
16669
|
+
detail: { changedFields: diffPolicyFields(before, policy) }
|
|
16670
|
+
});
|
|
16671
|
+
}
|
|
16650
16672
|
}
|
|
16651
16673
|
if (opts.json) {
|
|
16652
16674
|
console.log(JSON.stringify(policy, null, 2));
|
|
16653
16675
|
return;
|
|
16654
16676
|
}
|
|
16677
|
+
if (opts.dryRun) {
|
|
16678
|
+
p17.log.info(`${pc26.yellow("Dry run \u2014 no changes written.")} Preview below:`);
|
|
16679
|
+
}
|
|
16655
16680
|
p17.log.message(
|
|
16656
16681
|
`Policy for fork ${pc26.cyan(reg.forkId)}:
|
|
16657
16682
|
autoApprove: ${policy.autoApprove}
|
|
@@ -16663,6 +16688,150 @@ async function kitForkPolicyCommand(opts) {
|
|
|
16663
16688
|
allowedScripts: [${policy.allowedScripts.join(", ")}]`
|
|
16664
16689
|
);
|
|
16665
16690
|
}
|
|
16691
|
+
function diffPolicyFields(before, after) {
|
|
16692
|
+
const fields = [
|
|
16693
|
+
"autoApprove",
|
|
16694
|
+
"autoApproveDepUpdates",
|
|
16695
|
+
"remoteSyncMode",
|
|
16696
|
+
"interactiveConflicts",
|
|
16697
|
+
"untouchablePaths",
|
|
16698
|
+
"confirmBeforeChange",
|
|
16699
|
+
"allowedScripts"
|
|
16700
|
+
];
|
|
16701
|
+
const changed = [];
|
|
16702
|
+
for (const f of fields) {
|
|
16703
|
+
const a = JSON.stringify(before[f]);
|
|
16704
|
+
const b = JSON.stringify(after[f]);
|
|
16705
|
+
if (a !== b) changed.push(String(f));
|
|
16706
|
+
}
|
|
16707
|
+
return changed;
|
|
16708
|
+
}
|
|
16709
|
+
function validateProtectedPath(raw) {
|
|
16710
|
+
const v = raw.trim();
|
|
16711
|
+
if (!v) return "Path cannot be empty.";
|
|
16712
|
+
if (v.startsWith("/")) return "Use a relative path (no leading slash).";
|
|
16713
|
+
if (v.includes("\0")) return "Path contains an invalid character.";
|
|
16714
|
+
return void 0;
|
|
16715
|
+
}
|
|
16716
|
+
async function runInteractivePolicyEditor(forkId, current) {
|
|
16717
|
+
p17.intro(pc26.bold(`Edit policy: ${forkId}`));
|
|
16718
|
+
const autoApprove = await p17.select({
|
|
16719
|
+
message: "Auto-approve scaffold additions and modifications:",
|
|
16720
|
+
initialValue: current.autoApprove,
|
|
16721
|
+
options: [
|
|
16722
|
+
{ value: "none", label: "none", hint: "every action requires confirmation (safest)" },
|
|
16723
|
+
{ value: "additive", label: "additive", hint: "new files auto-approve; modifications confirm" },
|
|
16724
|
+
{ value: "all", label: "all", hint: "all safe actions auto-approve (still honours protected paths)" }
|
|
16725
|
+
]
|
|
16726
|
+
});
|
|
16727
|
+
if (p17.isCancel(autoApprove)) {
|
|
16728
|
+
p17.cancel("Cancelled.");
|
|
16729
|
+
return null;
|
|
16730
|
+
}
|
|
16731
|
+
const autoApproveDepUpdates = await p17.select({
|
|
16732
|
+
message: "Auto-approve package.json dependency changes:",
|
|
16733
|
+
initialValue: current.autoApproveDepUpdates,
|
|
16734
|
+
options: [
|
|
16735
|
+
{ value: "none", label: "none", hint: "never auto-approve dep changes" },
|
|
16736
|
+
{ value: "additive", label: "additive", hint: "new deps auto-approve, upgrades need confirm" },
|
|
16737
|
+
{ value: "all", label: "all", hint: "even upgrades auto-approve (additive-only, never removes)" }
|
|
16738
|
+
]
|
|
16739
|
+
});
|
|
16740
|
+
if (p17.isCancel(autoApproveDepUpdates)) {
|
|
16741
|
+
p17.cancel("Cancelled.");
|
|
16742
|
+
return null;
|
|
16743
|
+
}
|
|
16744
|
+
const remoteSyncMode = await p17.select({
|
|
16745
|
+
message: "Remote sync mode:",
|
|
16746
|
+
initialValue: current.remoteSyncMode,
|
|
16747
|
+
options: [
|
|
16748
|
+
{ value: "off", label: "off", hint: "purely local \u2014 no GitHub interaction" },
|
|
16749
|
+
{ value: "branch", label: "branch", hint: "push heal branches, no PR" },
|
|
16750
|
+
{ value: "pr", label: "pr", hint: "push branch + open draft PR" }
|
|
16751
|
+
]
|
|
16752
|
+
});
|
|
16753
|
+
if (p17.isCancel(remoteSyncMode)) {
|
|
16754
|
+
p17.cancel("Cancelled.");
|
|
16755
|
+
return null;
|
|
16756
|
+
}
|
|
16757
|
+
const interactiveConflicts = await p17.confirm({
|
|
16758
|
+
message: "Pause on remote conflicts for interactive resolution?",
|
|
16759
|
+
initialValue: current.interactiveConflicts
|
|
16760
|
+
});
|
|
16761
|
+
if (p17.isCancel(interactiveConflicts)) {
|
|
16762
|
+
p17.cancel("Cancelled.");
|
|
16763
|
+
return null;
|
|
16764
|
+
}
|
|
16765
|
+
let untouchablePaths = [...current.untouchablePaths];
|
|
16766
|
+
while (true) {
|
|
16767
|
+
const currentList = untouchablePaths.length > 0 ? untouchablePaths.join(", ") : pc26.dim("(none)");
|
|
16768
|
+
const action = await p17.select({
|
|
16769
|
+
message: `Protected paths \u2014 never modified by heal [current: ${currentList}]`,
|
|
16770
|
+
options: [
|
|
16771
|
+
{ value: "__done", label: "\u2713 Done" },
|
|
16772
|
+
{ value: "__add", label: "+ Add path" },
|
|
16773
|
+
...untouchablePaths.length > 0 ? [{ value: "__remove", label: "\u2212 Remove path" }] : []
|
|
16774
|
+
]
|
|
16775
|
+
});
|
|
16776
|
+
if (p17.isCancel(action)) {
|
|
16777
|
+
p17.cancel("Cancelled.");
|
|
16778
|
+
return null;
|
|
16779
|
+
}
|
|
16780
|
+
if (action === "__done") break;
|
|
16781
|
+
if (action === "__add") {
|
|
16782
|
+
const next = await p17.text({
|
|
16783
|
+
message: "New protected path (glob prefix, relative):",
|
|
16784
|
+
placeholder: "skills/ or .env.local",
|
|
16785
|
+
validate: validateProtectedPath
|
|
16786
|
+
});
|
|
16787
|
+
if (p17.isCancel(next)) {
|
|
16788
|
+
p17.cancel("Cancelled.");
|
|
16789
|
+
return null;
|
|
16790
|
+
}
|
|
16791
|
+
const clean = next.trim();
|
|
16792
|
+
if (clean && !untouchablePaths.includes(clean)) {
|
|
16793
|
+
untouchablePaths = [...untouchablePaths, clean];
|
|
16794
|
+
}
|
|
16795
|
+
}
|
|
16796
|
+
if (action === "__remove") {
|
|
16797
|
+
const toRemove = await p17.select({
|
|
16798
|
+
message: "Select path to remove:",
|
|
16799
|
+
options: untouchablePaths.map((v) => ({ value: v, label: v }))
|
|
16800
|
+
});
|
|
16801
|
+
if (p17.isCancel(toRemove)) {
|
|
16802
|
+
p17.cancel("Cancelled.");
|
|
16803
|
+
return null;
|
|
16804
|
+
}
|
|
16805
|
+
untouchablePaths = untouchablePaths.filter((x) => x !== toRemove);
|
|
16806
|
+
}
|
|
16807
|
+
}
|
|
16808
|
+
p17.note(
|
|
16809
|
+
[
|
|
16810
|
+
` autoApprove: ${autoApprove}`,
|
|
16811
|
+
` autoApproveDepUpdates: ${autoApproveDepUpdates}`,
|
|
16812
|
+
` remoteSyncMode: ${remoteSyncMode}`,
|
|
16813
|
+
` interactiveConflicts: ${interactiveConflicts}`,
|
|
16814
|
+
` untouchablePaths: [${untouchablePaths.join(", ")}]`
|
|
16815
|
+
].join("\n"),
|
|
16816
|
+
"New policy"
|
|
16817
|
+
);
|
|
16818
|
+
const confirmed = await p17.confirm({
|
|
16819
|
+
message: "Apply these settings?",
|
|
16820
|
+
initialValue: true
|
|
16821
|
+
});
|
|
16822
|
+
if (p17.isCancel(confirmed) || !confirmed) {
|
|
16823
|
+
p17.cancel("No changes written.");
|
|
16824
|
+
return null;
|
|
16825
|
+
}
|
|
16826
|
+
return {
|
|
16827
|
+
...current,
|
|
16828
|
+
autoApprove,
|
|
16829
|
+
autoApproveDepUpdates,
|
|
16830
|
+
remoteSyncMode,
|
|
16831
|
+
interactiveConflicts: Boolean(interactiveConflicts),
|
|
16832
|
+
untouchablePaths
|
|
16833
|
+
};
|
|
16834
|
+
}
|
|
16666
16835
|
function applyPolicyAssignment(policy, assignment) {
|
|
16667
16836
|
const plusMatch = assignment.match(/^([a-zA-Z]+)\+=(.+)$/);
|
|
16668
16837
|
const eqMatch = assignment.match(/^([a-zA-Z]+)=(.+)$/);
|
|
@@ -16744,8 +16913,26 @@ function registerKitForkRemoteSubcommands(kitFork) {
|
|
|
16744
16913
|
kitFork.command("connect").description("Bind an existing registered fork to a GitHub remote repository.").requiredOption("--fork-id <id>", "Registered fork id").requiredOption("--remote <owner/repo>", "GitHub repository to connect as origin").option("--default-branch <name>", "Remote default branch (default: main)").option("--json", "Emit machine-readable output").action(async (opts) => {
|
|
16745
16914
|
await kitForkConnect({ forkId: opts.forkId, remote: opts.remote, defaultBranch: opts.defaultBranch, json: opts.json });
|
|
16746
16915
|
});
|
|
16747
|
-
kitFork.command("policy").description("View or
|
|
16748
|
-
|
|
16916
|
+
kitFork.command("policy").description("View, edit interactively, or set fields on the per-fork heal policy.").argument("[fork-id]", "Registered fork id (takes precedence over --fork-id)").option("--fork-id <id>", "Registered fork id (alternative to positional arg)").option("--set <field=value...>", "Update one or more fields (e.g. autoApprove=none untouchablePaths+=custom/)").option("--edit", "Launch interactive editor (clack prompts)").option("--dry-run", "Show planned changes without writing policy.json").option("--json", "Emit machine-readable output").addHelpText("after", `
|
|
16917
|
+
Examples:
|
|
16918
|
+
$ growthub kit fork policy <fork-id> # view policy
|
|
16919
|
+
$ growthub kit fork policy <fork-id> --edit # interactive editor
|
|
16920
|
+
$ growthub kit fork policy <fork-id> --set autoApprove=all
|
|
16921
|
+
$ growthub kit fork policy <fork-id> --set untouchablePaths+=skills/ --dry-run
|
|
16922
|
+
`).action(async (forkIdArg, opts) => {
|
|
16923
|
+
const forkId = forkIdArg ?? opts.forkId;
|
|
16924
|
+
if (!forkId) {
|
|
16925
|
+
console.error(pc26.red("Missing fork-id. Pass it as an argument or with --fork-id."));
|
|
16926
|
+
process.exitCode = 1;
|
|
16927
|
+
return;
|
|
16928
|
+
}
|
|
16929
|
+
await kitForkPolicyCommand({
|
|
16930
|
+
forkId,
|
|
16931
|
+
set: opts.set,
|
|
16932
|
+
edit: opts.edit,
|
|
16933
|
+
dryRun: opts.dryRun,
|
|
16934
|
+
json: opts.json
|
|
16935
|
+
});
|
|
16749
16936
|
});
|
|
16750
16937
|
kitFork.command("trace").description("Show the append-only event log for a fork.").requiredOption("--fork-id <id>", "Registered fork id").option("--tail <n>", "Only show the last N events", (v) => Number(v)).option("--json", "Emit machine-readable output").action((opts) => {
|
|
16751
16938
|
kitForkTraceCommand({ forkId: opts.forkId, tail: opts.tail, json: opts.json });
|
|
@@ -16757,72 +16944,176 @@ function registerKitForkRemoteSubcommands(kitFork) {
|
|
|
16757
16944
|
|
|
16758
16945
|
// src/commands/kit-fork.ts
|
|
16759
16946
|
init_banner();
|
|
16947
|
+
import pc29 from "picocolors";
|
|
16948
|
+
|
|
16949
|
+
// src/utils/table-renderer.ts
|
|
16760
16950
|
import pc27 from "picocolors";
|
|
16951
|
+
var ANSI_PATTERN = /\x1b\[[0-9;]*m/g;
|
|
16952
|
+
function stripAnsi(input) {
|
|
16953
|
+
return input.replace(ANSI_PATTERN, "");
|
|
16954
|
+
}
|
|
16955
|
+
function visibleLength(input) {
|
|
16956
|
+
return stripAnsi(input).length;
|
|
16957
|
+
}
|
|
16958
|
+
function truncate(value, max) {
|
|
16959
|
+
if (max <= 0) return "";
|
|
16960
|
+
if (visibleLength(value) <= max) return value;
|
|
16961
|
+
if (max <= 1) return value.slice(0, max);
|
|
16962
|
+
const plain = stripAnsi(value);
|
|
16963
|
+
const keep = Math.max(0, max - 1);
|
|
16964
|
+
return plain.slice(0, keep) + "\u2026";
|
|
16965
|
+
}
|
|
16966
|
+
function pad(value, width, align) {
|
|
16967
|
+
const vw = visibleLength(value);
|
|
16968
|
+
if (vw >= width) return value;
|
|
16969
|
+
const delta = width - vw;
|
|
16970
|
+
if (align === "right") return " ".repeat(delta) + value;
|
|
16971
|
+
if (align === "center") {
|
|
16972
|
+
const left = Math.floor(delta / 2);
|
|
16973
|
+
const right = delta - left;
|
|
16974
|
+
return " ".repeat(left) + value + " ".repeat(right);
|
|
16975
|
+
}
|
|
16976
|
+
return value + " ".repeat(delta);
|
|
16977
|
+
}
|
|
16978
|
+
function computeColumnWidths(columns, cells) {
|
|
16979
|
+
return columns.map((col, colIdx) => {
|
|
16980
|
+
if (col.width && col.width > 0) return col.width;
|
|
16981
|
+
const contentMax = cells.reduce(
|
|
16982
|
+
(acc, row) => Math.max(acc, visibleLength(row[colIdx] ?? "")),
|
|
16983
|
+
visibleLength(col.label)
|
|
16984
|
+
);
|
|
16985
|
+
if (col.maxWidth && contentMax > col.maxWidth) return col.maxWidth;
|
|
16986
|
+
return contentMax;
|
|
16987
|
+
});
|
|
16988
|
+
}
|
|
16989
|
+
function renderTable(opts) {
|
|
16990
|
+
const { columns, rows, showHeader = true, emptyText } = opts;
|
|
16991
|
+
if (rows.length === 0 && emptyText) {
|
|
16992
|
+
return pc27.dim(" " + emptyText);
|
|
16993
|
+
}
|
|
16994
|
+
const rawCells = rows.map(
|
|
16995
|
+
(row) => columns.map((col) => {
|
|
16996
|
+
const raw = row[col.key];
|
|
16997
|
+
if (col.format) return col.format(raw, row);
|
|
16998
|
+
if (raw === void 0 || raw === null) return "";
|
|
16999
|
+
return String(raw);
|
|
17000
|
+
})
|
|
17001
|
+
);
|
|
17002
|
+
const widths = computeColumnWidths(columns, rawCells);
|
|
17003
|
+
const lines = [];
|
|
17004
|
+
if (showHeader) {
|
|
17005
|
+
const header = columns.map(
|
|
17006
|
+
(col, i) => pad(pc27.bold(truncate(col.label, widths[i])), widths[i], col.align ?? "left")
|
|
17007
|
+
).join(" ");
|
|
17008
|
+
lines.push(" " + header);
|
|
17009
|
+
lines.push(" " + widths.map((w) => pc27.dim("\u2500".repeat(w))).join(" "));
|
|
17010
|
+
}
|
|
17011
|
+
for (const cells of rawCells) {
|
|
17012
|
+
const line = columns.map(
|
|
17013
|
+
(col, i) => pad(truncate(cells[i] ?? "", widths[i]), widths[i], col.align ?? "left")
|
|
17014
|
+
).join(" ");
|
|
17015
|
+
lines.push(" " + line);
|
|
17016
|
+
}
|
|
17017
|
+
return lines.join("\n");
|
|
17018
|
+
}
|
|
17019
|
+
|
|
17020
|
+
// src/utils/progress.ts
|
|
17021
|
+
import pc28 from "picocolors";
|
|
17022
|
+
function renderProgressBar(current, total, opts = {}) {
|
|
17023
|
+
const width = opts.width ?? 24;
|
|
17024
|
+
const filled = opts.filledChar ?? "\u2588";
|
|
17025
|
+
const empty = opts.emptyChar ?? "\u2591";
|
|
17026
|
+
const showCounts = opts.showCounts ?? true;
|
|
17027
|
+
const color = opts.color ?? true;
|
|
17028
|
+
const safeTotal = Math.max(0, total);
|
|
17029
|
+
const safeCurrent = Math.min(Math.max(0, current), safeTotal || current);
|
|
17030
|
+
const ratio = safeTotal > 0 ? safeCurrent / safeTotal : 0;
|
|
17031
|
+
const filledCells = Math.round(ratio * width);
|
|
17032
|
+
const bar = filled.repeat(filledCells) + empty.repeat(Math.max(0, width - filledCells));
|
|
17033
|
+
const pct = Math.round(ratio * 100);
|
|
17034
|
+
const painted = color ? pct >= 80 ? pc28.green(bar) : pct >= 50 ? pc28.yellow(bar) : pct > 0 ? pc28.red(bar) : pc28.dim(bar) : bar;
|
|
17035
|
+
if (!showCounts) return `[${painted}]`;
|
|
17036
|
+
return `[${painted}] ${safeCurrent}/${safeTotal || 0} (${pct}%)`;
|
|
17037
|
+
}
|
|
17038
|
+
function formatRelative(iso, nowMs = Date.now()) {
|
|
17039
|
+
if (!iso) return "\u2014";
|
|
17040
|
+
const ts = new Date(iso).getTime();
|
|
17041
|
+
if (!Number.isFinite(ts)) return "\u2014";
|
|
17042
|
+
const diff = Math.max(0, nowMs - ts);
|
|
17043
|
+
if (diff < 1e4) return "just now";
|
|
17044
|
+
if (diff < 6e4) return `${Math.floor(diff / 1e3)}s ago`;
|
|
17045
|
+
if (diff < 36e5) return `${Math.floor(diff / 6e4)}m ago`;
|
|
17046
|
+
if (diff < 864e5) return `${Math.floor(diff / 36e5)}h ago`;
|
|
17047
|
+
if (diff < 30 * 864e5) return `${Math.floor(diff / 864e5)}d ago`;
|
|
17048
|
+
return new Date(iso).toISOString().slice(0, 10);
|
|
17049
|
+
}
|
|
17050
|
+
|
|
17051
|
+
// src/commands/kit-fork.ts
|
|
16761
17052
|
init_service();
|
|
16762
17053
|
function hr(width = 72) {
|
|
16763
|
-
return
|
|
17054
|
+
return pc29.dim("\u2500".repeat(width));
|
|
16764
17055
|
}
|
|
16765
17056
|
function severityBadge(s) {
|
|
16766
17057
|
switch (s) {
|
|
16767
17058
|
case "critical":
|
|
16768
|
-
return
|
|
17059
|
+
return pc29.red("\u25CF critical");
|
|
16769
17060
|
case "warning":
|
|
16770
|
-
return
|
|
17061
|
+
return pc29.yellow("\u25CF warning");
|
|
16771
17062
|
case "info":
|
|
16772
|
-
return
|
|
17063
|
+
return pc29.cyan("\u25CF info");
|
|
16773
17064
|
default:
|
|
16774
|
-
return
|
|
17065
|
+
return pc29.green("\u25CF in-sync");
|
|
16775
17066
|
}
|
|
16776
17067
|
}
|
|
16777
17068
|
function jobStatusBadge(status) {
|
|
16778
17069
|
switch (status) {
|
|
16779
17070
|
case "running":
|
|
16780
|
-
return
|
|
17071
|
+
return pc29.cyan("\u27F3 running");
|
|
16781
17072
|
case "completed":
|
|
16782
|
-
return
|
|
17073
|
+
return pc29.green("\u2713 completed");
|
|
16783
17074
|
case "failed":
|
|
16784
|
-
return
|
|
17075
|
+
return pc29.red("\u2717 failed");
|
|
16785
17076
|
case "cancelled":
|
|
16786
|
-
return
|
|
17077
|
+
return pc29.dim("\u25CB cancelled");
|
|
16787
17078
|
default:
|
|
16788
|
-
return
|
|
17079
|
+
return pc29.dim("\u2026 pending");
|
|
16789
17080
|
}
|
|
16790
17081
|
}
|
|
16791
17082
|
function formatDate(iso) {
|
|
16792
|
-
if (!iso) return
|
|
17083
|
+
if (!iso) return pc29.dim("\u2014");
|
|
16793
17084
|
return new Date(iso).toLocaleString();
|
|
16794
17085
|
}
|
|
16795
17086
|
function printDriftReport(report) {
|
|
16796
17087
|
console.log("");
|
|
16797
17088
|
console.log(
|
|
16798
|
-
|
|
17089
|
+
pc29.bold(`Fork: ${report.forkId}`) + " " + severityBadge(report.overallSeverity)
|
|
16799
17090
|
);
|
|
16800
17091
|
console.log(
|
|
16801
|
-
|
|
17092
|
+
pc29.dim(`Kit: ${report.kitId}`) + " " + pc29.dim(`fork v${report.forkVersion} \u2192 upstream v${report.upstreamVersion}`)
|
|
16802
17093
|
);
|
|
16803
17094
|
console.log(hr());
|
|
16804
17095
|
if (report.fileDrifts.length === 0 && report.packageDrifts.length === 0) {
|
|
16805
|
-
console.log(
|
|
17096
|
+
console.log(pc29.green(" No drift detected \u2014 fork is in sync."));
|
|
16806
17097
|
}
|
|
16807
17098
|
if (report.fileDrifts.length > 0) {
|
|
16808
|
-
console.log(
|
|
17099
|
+
console.log(pc29.bold("\n File Drift:"));
|
|
16809
17100
|
for (const d of report.fileDrifts) {
|
|
16810
|
-
const badge2 = d.changeType === "added" ?
|
|
16811
|
-
console.log(`${badge2} ${d.relativePath} ${
|
|
17101
|
+
const badge2 = d.changeType === "added" ? pc29.cyan(" +") : d.changeType === "modified" ? pc29.yellow(" ~") : pc29.red(" -");
|
|
17102
|
+
console.log(`${badge2} ${d.relativePath} ${pc29.dim(d.description)}`);
|
|
16812
17103
|
}
|
|
16813
17104
|
}
|
|
16814
17105
|
if (report.packageDrifts.length > 0) {
|
|
16815
|
-
console.log(
|
|
17106
|
+
console.log(pc29.bold("\n Package Drift:"));
|
|
16816
17107
|
for (const d of report.packageDrifts) {
|
|
16817
|
-
const badge2 = d.changeType === "added" ?
|
|
17108
|
+
const badge2 = d.changeType === "added" ? pc29.cyan(" +") : d.changeType === "updated" ? pc29.yellow(" ~") : pc29.red(" -");
|
|
16818
17109
|
const ver = d.forkVersion ? `${d.forkVersion} \u2192 ${d.upstreamVersion}` : `(new) ${d.upstreamVersion}`;
|
|
16819
|
-
console.log(`${badge2} ${d.packageName} ${
|
|
17110
|
+
console.log(`${badge2} ${d.packageName} ${pc29.dim(ver)}`);
|
|
16820
17111
|
}
|
|
16821
17112
|
}
|
|
16822
17113
|
if (report.customSkillsDetected.length > 0) {
|
|
16823
|
-
console.log(
|
|
17114
|
+
console.log(pc29.bold("\n Custom Skills Detected (always preserved):"));
|
|
16824
17115
|
for (const s of report.customSkillsDetected) {
|
|
16825
|
-
console.log(` ${
|
|
17116
|
+
console.log(` ${pc29.magenta("\u2691")} ${s}`);
|
|
16826
17117
|
}
|
|
16827
17118
|
}
|
|
16828
17119
|
console.log("");
|
|
@@ -16831,56 +17122,381 @@ function printDriftReport(report) {
|
|
|
16831
17122
|
function printHealPlan(plan) {
|
|
16832
17123
|
console.log("");
|
|
16833
17124
|
console.log(
|
|
16834
|
-
|
|
17125
|
+
pc29.bold(`Heal Plan: ${plan.forkId}`) + pc29.dim(` v${plan.fromVersion} \u2192 v${plan.toVersion}`)
|
|
16835
17126
|
);
|
|
16836
|
-
console.log(
|
|
17127
|
+
console.log(pc29.dim("Estimated risk: ") + severityBadge(plan.estimatedRisk));
|
|
16837
17128
|
console.log(hr());
|
|
16838
17129
|
if (plan.actions.length === 0) {
|
|
16839
|
-
console.log(
|
|
17130
|
+
console.log(pc29.green(" No actions needed."));
|
|
16840
17131
|
} else {
|
|
16841
|
-
console.log(
|
|
17132
|
+
console.log(pc29.bold(` ${plan.actions.length} action(s) planned:`));
|
|
16842
17133
|
for (const a of plan.actions) {
|
|
16843
|
-
const icon = a.actionType === "skip_user_modified" ?
|
|
17134
|
+
const icon = a.actionType === "skip_user_modified" ? pc29.dim(" \u25CB") : pc29.cyan(" \u2192");
|
|
16844
17135
|
console.log(`${icon} ${a.description}`);
|
|
16845
17136
|
}
|
|
16846
17137
|
}
|
|
16847
17138
|
if (plan.preservedPaths.length > 0) {
|
|
16848
|
-
console.log(
|
|
17139
|
+
console.log(pc29.bold(`
|
|
16849
17140
|
${plan.preservedPaths.length} path(s) preserved (user modifications kept):`));
|
|
16850
17141
|
for (const pp of plan.preservedPaths) {
|
|
16851
|
-
console.log(
|
|
17142
|
+
console.log(pc29.dim(` \u2691 ${pp}`));
|
|
16852
17143
|
}
|
|
16853
17144
|
}
|
|
16854
17145
|
console.log("");
|
|
16855
17146
|
console.log(hr());
|
|
16856
17147
|
}
|
|
17148
|
+
function statusIconForDrift(severity) {
|
|
17149
|
+
switch (severity) {
|
|
17150
|
+
case "critical":
|
|
17151
|
+
return pc29.red("\u2717 drift-major");
|
|
17152
|
+
case "warning":
|
|
17153
|
+
return pc29.yellow("\u26A0 drift-warn");
|
|
17154
|
+
case "info":
|
|
17155
|
+
return pc29.cyan("~ drift-minor");
|
|
17156
|
+
default:
|
|
17157
|
+
return pc29.green("\u2713 synced");
|
|
17158
|
+
}
|
|
17159
|
+
}
|
|
17160
|
+
function summarizeFork(fork, opts = {}) {
|
|
17161
|
+
let severity = "unknown";
|
|
17162
|
+
let upstreamVersion = null;
|
|
17163
|
+
let lastHealAt = fork.lastSyncedAt ?? null;
|
|
17164
|
+
if (!opts.skipUpstreamCheck) {
|
|
17165
|
+
try {
|
|
17166
|
+
const report = detectKitForkDrift(fork);
|
|
17167
|
+
severity = report.overallSeverity;
|
|
17168
|
+
upstreamVersion = report.upstreamVersion;
|
|
17169
|
+
} catch {
|
|
17170
|
+
severity = "unknown";
|
|
17171
|
+
}
|
|
17172
|
+
}
|
|
17173
|
+
let protectedPaths = [];
|
|
17174
|
+
try {
|
|
17175
|
+
const policy = readKitForkPolicy(fork.forkPath);
|
|
17176
|
+
protectedPaths = policy.untouchablePaths.slice(0, 3);
|
|
17177
|
+
} catch {
|
|
17178
|
+
}
|
|
17179
|
+
if (!lastHealAt) {
|
|
17180
|
+
try {
|
|
17181
|
+
const events = readKitForkTrace(fork.forkPath);
|
|
17182
|
+
const last = [...events].reverse().find((e) => e.type === "heal_applied");
|
|
17183
|
+
if (last) lastHealAt = last.timestamp;
|
|
17184
|
+
} catch {
|
|
17185
|
+
}
|
|
17186
|
+
}
|
|
17187
|
+
return {
|
|
17188
|
+
forkId: fork.forkId,
|
|
17189
|
+
label: fork.label ?? null,
|
|
17190
|
+
kitId: fork.kitId,
|
|
17191
|
+
baseVersion: fork.baseVersion,
|
|
17192
|
+
forkPath: fork.forkPath,
|
|
17193
|
+
severity,
|
|
17194
|
+
upstreamVersion,
|
|
17195
|
+
protectedPaths,
|
|
17196
|
+
lastHealAt,
|
|
17197
|
+
hasRemote: Boolean(fork.remote)
|
|
17198
|
+
};
|
|
17199
|
+
}
|
|
17200
|
+
function renderForkTable(summaries) {
|
|
17201
|
+
return renderTable({
|
|
17202
|
+
columns: [
|
|
17203
|
+
{
|
|
17204
|
+
key: "forkId",
|
|
17205
|
+
label: "Fork ID",
|
|
17206
|
+
maxWidth: 26,
|
|
17207
|
+
format: (v, row) => pc29.cyan(row.label ?? String(v))
|
|
17208
|
+
},
|
|
17209
|
+
{
|
|
17210
|
+
key: "kitId",
|
|
17211
|
+
label: "Kit",
|
|
17212
|
+
maxWidth: 22
|
|
17213
|
+
},
|
|
17214
|
+
{
|
|
17215
|
+
key: "baseVersion",
|
|
17216
|
+
label: "Base",
|
|
17217
|
+
format: (v) => `v${v}`
|
|
17218
|
+
},
|
|
17219
|
+
{
|
|
17220
|
+
key: "upstreamVersion",
|
|
17221
|
+
label: "Upstream",
|
|
17222
|
+
format: (v) => v ? `v${v}` : pc29.dim("\u2014")
|
|
17223
|
+
},
|
|
17224
|
+
{
|
|
17225
|
+
key: "severity",
|
|
17226
|
+
label: "Status",
|
|
17227
|
+
format: (v) => v === "unknown" ? pc29.dim("\u25CB unknown") : statusIconForDrift(v)
|
|
17228
|
+
},
|
|
17229
|
+
{
|
|
17230
|
+
key: "protectedPaths",
|
|
17231
|
+
label: "Protected",
|
|
17232
|
+
maxWidth: 18,
|
|
17233
|
+
format: (v) => {
|
|
17234
|
+
const arr = v;
|
|
17235
|
+
if (!arr || arr.length === 0) return pc29.dim("\u2014");
|
|
17236
|
+
return arr.join(",");
|
|
17237
|
+
}
|
|
17238
|
+
},
|
|
17239
|
+
{
|
|
17240
|
+
key: "lastHealAt",
|
|
17241
|
+
label: "Last Heal",
|
|
17242
|
+
format: (v) => v ? formatRelative(v) : pc29.dim("\u2014")
|
|
17243
|
+
}
|
|
17244
|
+
],
|
|
17245
|
+
rows: summaries,
|
|
17246
|
+
emptyText: "No forks registered yet. Run `growthub kit fork register <path>` to get started."
|
|
17247
|
+
});
|
|
17248
|
+
}
|
|
16857
17249
|
function printForkList(forks) {
|
|
16858
17250
|
if (forks.length === 0) {
|
|
16859
|
-
console.log(
|
|
17251
|
+
console.log(pc29.dim(" No forks registered yet. Run `growthub kit fork register <path>` to get started."));
|
|
16860
17252
|
return;
|
|
16861
17253
|
}
|
|
17254
|
+
const summaries = forks.map((f) => summarizeFork(f, { skipUpstreamCheck: true }));
|
|
16862
17255
|
console.log("");
|
|
16863
|
-
console.log(
|
|
17256
|
+
console.log(pc29.bold("Registered Kit Forks") + pc29.dim(` ${forks.length} total`));
|
|
16864
17257
|
console.log(hr());
|
|
16865
|
-
|
|
16866
|
-
|
|
16867
|
-
|
|
16868
|
-
|
|
16869
|
-
|
|
16870
|
-
|
|
16871
|
-
|
|
16872
|
-
|
|
17258
|
+
console.log(renderForkTable(summaries));
|
|
17259
|
+
console.log("");
|
|
17260
|
+
console.log(hr());
|
|
17261
|
+
}
|
|
17262
|
+
function groupHealActions(plan) {
|
|
17263
|
+
const safeAdd = [];
|
|
17264
|
+
const safeUpdate = [];
|
|
17265
|
+
const protectedActions = [];
|
|
17266
|
+
const unresolved = [];
|
|
17267
|
+
for (const a of plan.actions) {
|
|
17268
|
+
if (a.actionType === "skip_user_modified") {
|
|
17269
|
+
protectedActions.push(a);
|
|
17270
|
+
continue;
|
|
17271
|
+
}
|
|
17272
|
+
if (a.needsConfirmation) {
|
|
17273
|
+
unresolved.push(a);
|
|
17274
|
+
continue;
|
|
17275
|
+
}
|
|
17276
|
+
if (a.actionType === "add_file" || a.actionType === "add_custom_skill") {
|
|
17277
|
+
safeAdd.push(a);
|
|
17278
|
+
continue;
|
|
17279
|
+
}
|
|
17280
|
+
safeUpdate.push(a);
|
|
17281
|
+
}
|
|
17282
|
+
return { safeAdd, safeUpdate, protected: protectedActions, unresolved };
|
|
17283
|
+
}
|
|
17284
|
+
function printRichHealPreview(plan, policy) {
|
|
17285
|
+
const g = groupHealActions(plan);
|
|
17286
|
+
console.log("");
|
|
17287
|
+
console.log(
|
|
17288
|
+
pc29.bold(`Heal Plan: ${plan.forkId}`) + pc29.dim(` v${plan.fromVersion} \u2192 v${plan.toVersion}`)
|
|
17289
|
+
);
|
|
17290
|
+
console.log(pc29.dim("Estimated risk: ") + severityBadge(plan.estimatedRisk));
|
|
17291
|
+
console.log(hr());
|
|
17292
|
+
if (g.safeAdd.length > 0) {
|
|
17293
|
+
console.log(pc29.bold(`
|
|
17294
|
+
SAFE ADDITIONS (${g.safeAdd.length}):`));
|
|
17295
|
+
for (const a of g.safeAdd) {
|
|
17296
|
+
console.log(` ${pc29.green("+")} ${a.targetPath} ${pc29.dim(a.description)}`);
|
|
17297
|
+
}
|
|
17298
|
+
}
|
|
17299
|
+
if (g.safeUpdate.length > 0) {
|
|
17300
|
+
console.log(pc29.bold(`
|
|
17301
|
+
SAFE UPDATES (${g.safeUpdate.length}):`));
|
|
17302
|
+
for (const a of g.safeUpdate) {
|
|
17303
|
+
console.log(` ${pc29.yellow("~")} ${a.targetPath} ${pc29.dim(a.description)}`);
|
|
17304
|
+
}
|
|
17305
|
+
}
|
|
17306
|
+
if (g.protected.length > 0 || plan.preservedPaths.length > 0) {
|
|
17307
|
+
const paths = /* @__PURE__ */ new Set([
|
|
17308
|
+
...g.protected.map((a) => a.targetPath),
|
|
17309
|
+
...plan.preservedPaths
|
|
17310
|
+
]);
|
|
17311
|
+
console.log(pc29.bold(`
|
|
17312
|
+
PROTECTED (${paths.size}):`));
|
|
17313
|
+
for (const pth of paths) {
|
|
17314
|
+
const reason = policy && isUntouchable(policy, pth) ? "policy.untouchablePaths" : "user-modified";
|
|
17315
|
+
console.log(` ${pc29.dim("\u25CB")} ${pth} ${pc29.dim(`(${reason})`)}`);
|
|
17316
|
+
}
|
|
17317
|
+
}
|
|
17318
|
+
if (g.unresolved.length > 0) {
|
|
17319
|
+
console.log(pc29.bold(`
|
|
17320
|
+
UNRESOLVED \u2014 needs confirmation (${g.unresolved.length}):`));
|
|
17321
|
+
for (const a of g.unresolved) {
|
|
17322
|
+
console.log(` ${pc29.red("!")} ${a.targetPath} ${pc29.dim(a.confirmationReason ?? "policy.confirmBeforeChange")}`);
|
|
17323
|
+
}
|
|
17324
|
+
}
|
|
17325
|
+
console.log("");
|
|
17326
|
+
console.log(pc29.bold(" DECISION:"));
|
|
17327
|
+
const applyCount = g.safeAdd.length + g.safeUpdate.length;
|
|
17328
|
+
const skipCount = g.protected.length + plan.preservedPaths.length;
|
|
17329
|
+
console.log(` ${pc29.green(String(applyCount))} will apply \xB7 ${pc29.dim(String(skipCount) + " protected")} \xB7 ${g.unresolved.length > 0 ? pc29.red(String(g.unresolved.length) + " unresolved") : pc29.dim("0 unresolved")}`);
|
|
17330
|
+
console.log(hr());
|
|
17331
|
+
}
|
|
17332
|
+
function printNextStepsAfterStatus(forkId, hasDrift) {
|
|
17333
|
+
console.log("");
|
|
17334
|
+
console.log(pc29.bold(" Next steps:"));
|
|
17335
|
+
if (hasDrift) {
|
|
17336
|
+
console.log(` ${pc29.cyan("growthub kit fork heal " + forkId + " --preview")} ${pc29.dim("rich heal preview")}`);
|
|
17337
|
+
console.log(` ${pc29.cyan("growthub kit fork heal " + forkId)} ${pc29.dim("apply interactively")}`);
|
|
17338
|
+
}
|
|
17339
|
+
console.log(` ${pc29.cyan("growthub kit fork policy " + forkId)} ${pc29.dim("interactive policy editor")}`);
|
|
17340
|
+
console.log(` ${pc29.cyan("growthub kit fork history " + forkId)} ${pc29.dim("audit timeline")}`);
|
|
17341
|
+
console.log("");
|
|
17342
|
+
}
|
|
17343
|
+
function estimateJobProgress(job) {
|
|
17344
|
+
if (!job.healPlan) return null;
|
|
17345
|
+
const total = job.healPlan.actions.length;
|
|
17346
|
+
if (total === 0) return null;
|
|
17347
|
+
if (job.healResult) {
|
|
17348
|
+
const done = job.healResult.appliedCount + job.healResult.skippedCount;
|
|
17349
|
+
return { current: Math.min(done, total), total };
|
|
17350
|
+
}
|
|
17351
|
+
return { current: 0, total };
|
|
17352
|
+
}
|
|
17353
|
+
function renderJobTable(jobs) {
|
|
17354
|
+
return renderTable({
|
|
17355
|
+
columns: [
|
|
17356
|
+
{
|
|
17357
|
+
key: "jobId",
|
|
17358
|
+
label: "Job ID",
|
|
17359
|
+
maxWidth: 28,
|
|
17360
|
+
format: (v) => pc29.cyan(String(v))
|
|
17361
|
+
},
|
|
17362
|
+
{
|
|
17363
|
+
key: "forkId",
|
|
17364
|
+
label: "Fork ID",
|
|
17365
|
+
maxWidth: 22,
|
|
17366
|
+
format: (v) => pc29.dim(String(v))
|
|
17367
|
+
},
|
|
17368
|
+
{
|
|
17369
|
+
key: "status",
|
|
17370
|
+
label: "Status",
|
|
17371
|
+
format: (v) => jobStatusBadge(v)
|
|
17372
|
+
},
|
|
17373
|
+
{
|
|
17374
|
+
key: "healPlan",
|
|
17375
|
+
label: "Progress",
|
|
17376
|
+
maxWidth: 28,
|
|
17377
|
+
format: (_v, row) => {
|
|
17378
|
+
const prog = estimateJobProgress(row);
|
|
17379
|
+
if (!prog) {
|
|
17380
|
+
if (row.status === "completed") return pc29.green("\u2713 100%");
|
|
17381
|
+
if (row.status === "failed") return pc29.red("\u2717");
|
|
17382
|
+
return pc29.dim("\u2014");
|
|
17383
|
+
}
|
|
17384
|
+
return renderProgressBar(prog.current, prog.total, { width: 14, showCounts: true });
|
|
17385
|
+
}
|
|
17386
|
+
},
|
|
17387
|
+
{
|
|
17388
|
+
key: "createdAt",
|
|
17389
|
+
label: "Age",
|
|
17390
|
+
format: (_v, row) => formatRelative(row.completedAt ?? row.createdAt)
|
|
17391
|
+
}
|
|
17392
|
+
],
|
|
17393
|
+
rows: jobs,
|
|
17394
|
+
emptyText: "No jobs found."
|
|
17395
|
+
});
|
|
17396
|
+
}
|
|
17397
|
+
function printJobDetail(job) {
|
|
17398
|
+
console.log(` ${jobStatusBadge(job.status)} ${pc29.cyan(job.jobId)} ${pc29.dim(job.forkId)}`);
|
|
17399
|
+
const prog = estimateJobProgress(job);
|
|
17400
|
+
if (prog) {
|
|
17401
|
+
console.log(` ${pc29.dim("Progress:")} ${renderProgressBar(prog.current, prog.total, { width: 24 })}`);
|
|
17402
|
+
}
|
|
17403
|
+
if (job.healPlan) {
|
|
17404
|
+
console.log(` ${pc29.dim("Plan:")} ${job.healPlan.actions.length} action(s), risk=${job.healPlan.estimatedRisk}`);
|
|
17405
|
+
}
|
|
17406
|
+
if (job.startedAt) console.log(` ${pc29.dim("Started:")} ${formatRelative(job.startedAt)} (${job.startedAt})`);
|
|
17407
|
+
if (job.completedAt) console.log(` ${pc29.dim("Completed:")} ${formatRelative(job.completedAt)} (${job.completedAt})`);
|
|
17408
|
+
if (job.error) console.log(` ${pc29.red("Error:")} ${job.error}`);
|
|
17409
|
+
}
|
|
17410
|
+
var TERMINAL_JOB_STATUSES = [
|
|
17411
|
+
"completed",
|
|
17412
|
+
"failed",
|
|
17413
|
+
"cancelled"
|
|
17414
|
+
];
|
|
17415
|
+
async function watchJob(jobId, jsonMode) {
|
|
17416
|
+
const initial = getKitForkSyncJob(jobId);
|
|
17417
|
+
if (!initial) {
|
|
17418
|
+
console.error(pc29.red(`Job not found: ${jobId}`));
|
|
17419
|
+
process.exitCode = 1;
|
|
17420
|
+
return;
|
|
17421
|
+
}
|
|
17422
|
+
if (jsonMode) {
|
|
17423
|
+
const snapshot = await pollUntilTerminal(jobId);
|
|
17424
|
+
console.log(JSON.stringify(snapshot, null, 2));
|
|
17425
|
+
return;
|
|
17426
|
+
}
|
|
17427
|
+
console.log("");
|
|
17428
|
+
console.log(pc29.bold(`Watching job ${pc29.cyan(jobId)}`));
|
|
17429
|
+
console.log(hr());
|
|
17430
|
+
let lastStatus = null;
|
|
17431
|
+
let lastProgress = -1;
|
|
17432
|
+
while (true) {
|
|
17433
|
+
const job = getKitForkSyncJob(jobId);
|
|
17434
|
+
if (!job) {
|
|
17435
|
+
console.error(pc29.red(` Job disappeared: ${jobId}`));
|
|
17436
|
+
process.exitCode = 1;
|
|
17437
|
+
return;
|
|
16873
17438
|
}
|
|
16874
|
-
|
|
16875
|
-
|
|
17439
|
+
const prog = estimateJobProgress(job);
|
|
17440
|
+
const progNum = prog ? Math.round(prog.current / (prog.total || 1) * 100) : -1;
|
|
17441
|
+
if (job.status !== lastStatus || progNum !== lastProgress) {
|
|
17442
|
+
printJobDetail(job);
|
|
17443
|
+
lastStatus = job.status;
|
|
17444
|
+
lastProgress = progNum;
|
|
16876
17445
|
}
|
|
17446
|
+
if (TERMINAL_JOB_STATUSES.includes(job.status)) {
|
|
17447
|
+
console.log("");
|
|
17448
|
+
if (job.status === "failed") process.exitCode = 1;
|
|
17449
|
+
return;
|
|
17450
|
+
}
|
|
17451
|
+
await sleep(500);
|
|
17452
|
+
}
|
|
17453
|
+
}
|
|
17454
|
+
async function pollUntilTerminal(jobId) {
|
|
17455
|
+
while (true) {
|
|
17456
|
+
const job = getKitForkSyncJob(jobId);
|
|
17457
|
+
if (!job) throw new Error(`Job disappeared: ${jobId}`);
|
|
17458
|
+
if (TERMINAL_JOB_STATUSES.includes(job.status)) return job;
|
|
17459
|
+
await sleep(500);
|
|
17460
|
+
}
|
|
17461
|
+
}
|
|
17462
|
+
function sleep(ms) {
|
|
17463
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
17464
|
+
}
|
|
17465
|
+
function tailJobTrace(jobId, limit, jsonMode) {
|
|
17466
|
+
const job = getKitForkSyncJob(jobId);
|
|
17467
|
+
if (!job) {
|
|
17468
|
+
console.error(pc29.red(`Job not found: ${jobId}`));
|
|
17469
|
+
process.exitCode = 1;
|
|
17470
|
+
return;
|
|
17471
|
+
}
|
|
17472
|
+
const reg = listKitForkRegistrations().find((f) => f.forkId === job.forkId);
|
|
17473
|
+
if (!reg) {
|
|
17474
|
+
console.error(pc29.red(`Fork not registered for job ${jobId}: ${job.forkId}`));
|
|
17475
|
+
process.exitCode = 1;
|
|
17476
|
+
return;
|
|
17477
|
+
}
|
|
17478
|
+
const events = readKitForkTrace(reg.forkPath).filter((e) => e.jobId === jobId).slice(-Math.max(1, limit));
|
|
17479
|
+
if (jsonMode) {
|
|
17480
|
+
console.log(JSON.stringify(events, null, 2));
|
|
17481
|
+
return;
|
|
16877
17482
|
}
|
|
16878
17483
|
console.log("");
|
|
17484
|
+
console.log(pc29.bold(`Trace tail: ${pc29.cyan(jobId)}`) + pc29.dim(` ${events.length} event(s)`));
|
|
16879
17485
|
console.log(hr());
|
|
17486
|
+
if (events.length === 0) {
|
|
17487
|
+
console.log(pc29.dim(` No trace events recorded for this job yet.`));
|
|
17488
|
+
console.log("");
|
|
17489
|
+
return;
|
|
17490
|
+
}
|
|
17491
|
+
for (const e of events) {
|
|
17492
|
+
const ts = e.timestamp.replace("T", " ").replace(/\..+Z$/, "Z");
|
|
17493
|
+
console.log(` ${pc29.dim(ts)} ${pc29.cyan("[" + e.type + "]")} ${e.summary ?? ""}`);
|
|
17494
|
+
}
|
|
17495
|
+
console.log("");
|
|
16880
17496
|
}
|
|
16881
17497
|
async function runKitForkHub(opts = {}) {
|
|
16882
17498
|
printPaperclipCliBanner();
|
|
16883
|
-
p18.intro(
|
|
17499
|
+
p18.intro(pc29.bold("Kit Fork Sync Agent"));
|
|
16884
17500
|
while (true) {
|
|
16885
17501
|
const choice = await p18.select({
|
|
16886
17502
|
message: "What do you want to do?",
|
|
@@ -16958,7 +17574,7 @@ async function runRegisterFlow() {
|
|
|
16958
17574
|
options: [
|
|
16959
17575
|
...kits.map((k) => ({
|
|
16960
17576
|
value: k.id,
|
|
16961
|
-
label: k.id + " " +
|
|
17577
|
+
label: k.id + " " + pc29.dim("v" + k.version),
|
|
16962
17578
|
hint: k.description
|
|
16963
17579
|
})),
|
|
16964
17580
|
{ value: "__cancel", label: "\u2190 Cancel" }
|
|
@@ -16986,19 +17602,19 @@ async function runRegisterFlow() {
|
|
|
16986
17602
|
baseVersion: kitVersion,
|
|
16987
17603
|
label: labelInput.trim() || void 0
|
|
16988
17604
|
});
|
|
16989
|
-
spinner12.stop(
|
|
17605
|
+
spinner12.stop(pc29.green("Fork registered."));
|
|
16990
17606
|
p18.note(
|
|
16991
17607
|
[
|
|
16992
|
-
`Fork ID: ${
|
|
17608
|
+
`Fork ID: ${pc29.cyan(reg.forkId)}`,
|
|
16993
17609
|
`Kit: ${reg.kitId} v${reg.baseVersion}`,
|
|
16994
17610
|
`Path: ${reg.forkPath}`,
|
|
16995
17611
|
"",
|
|
16996
|
-
`Next: ${
|
|
17612
|
+
`Next: ${pc29.cyan("growthub kit fork status " + reg.forkId)}`
|
|
16997
17613
|
].join("\n"),
|
|
16998
17614
|
"Registration complete"
|
|
16999
17615
|
);
|
|
17000
17616
|
} catch (err) {
|
|
17001
|
-
spinner12.stop(
|
|
17617
|
+
spinner12.stop(pc29.red("Registration failed."));
|
|
17002
17618
|
p18.log.error(err.message);
|
|
17003
17619
|
}
|
|
17004
17620
|
}
|
|
@@ -17016,7 +17632,7 @@ async function runStatusFlow() {
|
|
|
17016
17632
|
options: [
|
|
17017
17633
|
...forks.map((f) => ({
|
|
17018
17634
|
value: f.forkId,
|
|
17019
|
-
label: (f.label ?? f.forkId) + " " +
|
|
17635
|
+
label: (f.label ?? f.forkId) + " " + pc29.dim("v" + f.baseVersion),
|
|
17020
17636
|
hint: f.kitId
|
|
17021
17637
|
})),
|
|
17022
17638
|
{ value: "__back", label: "\u2190 Back" }
|
|
@@ -17032,10 +17648,10 @@ async function runStatusFlow() {
|
|
|
17032
17648
|
spinner12.start("Detecting drift...");
|
|
17033
17649
|
try {
|
|
17034
17650
|
const report = detectKitForkDrift(reg);
|
|
17035
|
-
spinner12.stop(
|
|
17651
|
+
spinner12.stop(pc29.green("Analysis complete."));
|
|
17036
17652
|
printDriftReport(report);
|
|
17037
17653
|
} catch (err) {
|
|
17038
|
-
spinner12.stop(
|
|
17654
|
+
spinner12.stop(pc29.red("Drift detection failed."));
|
|
17039
17655
|
p18.log.error(err.message);
|
|
17040
17656
|
}
|
|
17041
17657
|
}
|
|
@@ -17050,7 +17666,7 @@ async function runHealFlow() {
|
|
|
17050
17666
|
options: [
|
|
17051
17667
|
...forks.map((f) => ({
|
|
17052
17668
|
value: f.forkId,
|
|
17053
|
-
label: (f.label ?? f.forkId) + " " +
|
|
17669
|
+
label: (f.label ?? f.forkId) + " " + pc29.dim("v" + f.baseVersion),
|
|
17054
17670
|
hint: f.kitId
|
|
17055
17671
|
})),
|
|
17056
17672
|
{ value: "__back", label: "\u2190 Back" }
|
|
@@ -17069,7 +17685,7 @@ async function runHealFlow() {
|
|
|
17069
17685
|
driftReport = detectKitForkDrift(reg);
|
|
17070
17686
|
driftSpinner.stop("Drift analysis complete.");
|
|
17071
17687
|
} catch (err) {
|
|
17072
|
-
driftSpinner.stop(
|
|
17688
|
+
driftSpinner.stop(pc29.red("Drift detection failed."));
|
|
17073
17689
|
p18.log.error(err.message);
|
|
17074
17690
|
return;
|
|
17075
17691
|
}
|
|
@@ -17098,9 +17714,9 @@ async function runHealFlow() {
|
|
|
17098
17714
|
const jobId = dispatchKitForkSyncJobBackground(reg.forkId, reg.kitId);
|
|
17099
17715
|
p18.note(
|
|
17100
17716
|
[
|
|
17101
|
-
`Job ID: ${
|
|
17717
|
+
`Job ID: ${pc29.cyan(jobId)}`,
|
|
17102
17718
|
"",
|
|
17103
|
-
`Check: ${
|
|
17719
|
+
`Check: ${pc29.cyan("growthub kit fork jobs")}`
|
|
17104
17720
|
].join("\n"),
|
|
17105
17721
|
"Background sync dispatched"
|
|
17106
17722
|
);
|
|
@@ -17116,16 +17732,16 @@ async function runHealFlow() {
|
|
|
17116
17732
|
}
|
|
17117
17733
|
});
|
|
17118
17734
|
healSpinner.stop(
|
|
17119
|
-
job.status === "completed" ?
|
|
17735
|
+
job.status === "completed" ? pc29.green(isDryRun ? "Dry run complete." : "Heal complete.") : pc29.red("Heal encountered errors.")
|
|
17120
17736
|
);
|
|
17121
17737
|
if (job.healResult) {
|
|
17122
17738
|
const r = job.healResult;
|
|
17123
17739
|
console.log("");
|
|
17124
|
-
console.log(
|
|
17125
|
-
console.log(` ${
|
|
17740
|
+
console.log(pc29.bold("Result:"));
|
|
17741
|
+
console.log(` ${pc29.green("Applied:")} ${r.appliedCount} ${pc29.dim("Skipped:")} ${r.skippedCount} ${r.errorCount > 0 ? pc29.red("Errors: " + r.errorCount) : pc29.dim("Errors: 0")}`);
|
|
17126
17742
|
for (const ar of r.actionResults) {
|
|
17127
17743
|
if (ar.status === "error") {
|
|
17128
|
-
console.log(` ${
|
|
17744
|
+
console.log(` ${pc29.red(" \u2717")} ${ar.action.targetPath}: ${ar.detail}`);
|
|
17129
17745
|
}
|
|
17130
17746
|
}
|
|
17131
17747
|
console.log("");
|
|
@@ -17142,12 +17758,12 @@ async function runJobsFlow() {
|
|
|
17142
17758
|
return;
|
|
17143
17759
|
}
|
|
17144
17760
|
console.log("");
|
|
17145
|
-
console.log(
|
|
17761
|
+
console.log(pc29.bold("Kit Fork Sync Jobs") + pc29.dim(` ${jobs.length} total`));
|
|
17146
17762
|
console.log(hr());
|
|
17147
17763
|
for (const job of jobs.slice(-10).reverse()) {
|
|
17148
|
-
console.log(` ${jobStatusBadge(job.status)} ${
|
|
17149
|
-
if (job.completedAt) console.log(` ${
|
|
17150
|
-
if (job.error) console.log(` ${
|
|
17764
|
+
console.log(` ${jobStatusBadge(job.status)} ${pc29.cyan(job.jobId)} ${pc29.dim(job.forkId)}`);
|
|
17765
|
+
if (job.completedAt) console.log(` ${pc29.dim("Completed:")} ${formatDate(job.completedAt)}`);
|
|
17766
|
+
if (job.error) console.log(` ${pc29.red("Error:")} ${job.error}`);
|
|
17151
17767
|
}
|
|
17152
17768
|
console.log("");
|
|
17153
17769
|
console.log(hr());
|
|
@@ -17184,15 +17800,44 @@ async function runJobsFlow() {
|
|
|
17184
17800
|
function registerKitForkSubcommands(kitCommand) {
|
|
17185
17801
|
const forkCmd = kitCommand.command("fork").description("Fork Sync Agent \u2014 register, track, and self-heal forked worker kits").addHelpText("after", `
|
|
17186
17802
|
Examples:
|
|
17187
|
-
|
|
17188
|
-
$ growthub kit fork
|
|
17189
|
-
|
|
17190
|
-
|
|
17191
|
-
$ growthub kit fork
|
|
17192
|
-
$ growthub kit fork
|
|
17803
|
+
# Interactive hub
|
|
17804
|
+
$ growthub kit fork # start interactive fork menu
|
|
17805
|
+
|
|
17806
|
+
# List all forks (with drift + policy at a glance)
|
|
17807
|
+
$ growthub kit fork list # beautiful table
|
|
17808
|
+
$ growthub kit fork list --json # machine-readable
|
|
17809
|
+
$ growthub kit fork list --filter status=drift-major
|
|
17810
|
+
$ growthub kit fork list --sort-by last-heal
|
|
17811
|
+
|
|
17812
|
+
# Status with policy eval + heal plan preview + next steps
|
|
17813
|
+
$ growthub kit fork status <fork-id>
|
|
17814
|
+
$ growthub kit fork status <fork-id> --policy-only
|
|
17815
|
+
$ growthub kit fork status <fork-id> --no-upstream-check
|
|
17816
|
+
|
|
17817
|
+
# Edit policy (interactive) or set fields non-interactively
|
|
17818
|
+
$ growthub kit fork policy <fork-id> --edit
|
|
17819
|
+
$ growthub kit fork policy <fork-id> --set autoApprove=all --set untouchablePaths+=skills/
|
|
17820
|
+
|
|
17821
|
+
# Heal (preview, apply, or background)
|
|
17822
|
+
$ growthub kit fork heal <fork-id> --preview # rich grouped preview + decision prompt
|
|
17823
|
+
$ growthub kit fork heal <fork-id> # apply (foreground)
|
|
17193
17824
|
$ growthub kit fork heal <fork-id> --background
|
|
17194
|
-
$ growthub kit fork
|
|
17195
|
-
|
|
17825
|
+
$ growthub kit fork heal <fork-id> --dry-run
|
|
17826
|
+
|
|
17827
|
+
# Monitor background jobs
|
|
17828
|
+
$ growthub kit fork jobs # table view with progress
|
|
17829
|
+
$ growthub kit fork jobs --watch <job-id> # live progress until terminal
|
|
17830
|
+
$ growthub kit fork jobs --tail <job-id> # trace events for a job
|
|
17831
|
+
$ growthub kit fork jobs --filter status=running
|
|
17832
|
+
|
|
17833
|
+
# Audit history
|
|
17834
|
+
$ growthub kit fork history <fork-id> # timeline
|
|
17835
|
+
$ growthub kit fork history <fork-id> --csv > audit.csv
|
|
17836
|
+
$ growthub kit fork history <fork-id> --since 2024-01-01
|
|
17837
|
+
|
|
17838
|
+
# Manage forks
|
|
17839
|
+
$ growthub kit fork register ./my-fork # register existing fork
|
|
17840
|
+
$ growthub kit fork deregister <fork-id>
|
|
17196
17841
|
`);
|
|
17197
17842
|
forkCmd.action(async () => {
|
|
17198
17843
|
await runKitForkHub();
|
|
@@ -17204,13 +17849,13 @@ function registerKitForkCommands(program2) {
|
|
|
17204
17849
|
const forkSync = program2.command("fork-sync").description("Kit Fork Sync Agent \u2014 alias for `growthub kit fork`").addHelpText("after", `
|
|
17205
17850
|
Examples:
|
|
17206
17851
|
$ growthub fork-sync # interactive hub
|
|
17207
|
-
$ growthub fork-sync
|
|
17208
|
-
$ growthub fork-sync
|
|
17209
|
-
$ growthub fork-sync
|
|
17210
|
-
$ growthub fork-sync heal <fork-id>
|
|
17211
|
-
$ growthub fork-sync heal <fork-id> --dry-run
|
|
17852
|
+
$ growthub fork-sync list # beautiful table of all forks
|
|
17853
|
+
$ growthub fork-sync status <fork-id> # drift + policy + next steps
|
|
17854
|
+
$ growthub fork-sync policy <fork-id> --edit # interactive policy editor
|
|
17855
|
+
$ growthub fork-sync heal <fork-id> --preview # rich heal preview
|
|
17212
17856
|
$ growthub fork-sync heal <fork-id> --background
|
|
17213
|
-
$ growthub fork-sync jobs
|
|
17857
|
+
$ growthub fork-sync jobs --watch <job-id> # live job progress
|
|
17858
|
+
$ growthub fork-sync history <fork-id> # audit timeline
|
|
17214
17859
|
`);
|
|
17215
17860
|
forkSync.action(async () => {
|
|
17216
17861
|
await runKitForkHub();
|
|
@@ -17235,14 +17880,14 @@ function addForkSubcommands(parentCmd) {
|
|
|
17235
17880
|
}
|
|
17236
17881
|
}
|
|
17237
17882
|
if (!kitId) {
|
|
17238
|
-
console.error(
|
|
17239
|
-
console.error(
|
|
17883
|
+
console.error(pc29.yellow("Could not auto-detect kit ID from kit.json. Use --kit <kit-id>."));
|
|
17884
|
+
console.error(pc29.dim("Available: " + kits.map((k) => k.id).join(", ")));
|
|
17240
17885
|
process.exitCode = 1;
|
|
17241
17886
|
return;
|
|
17242
17887
|
}
|
|
17243
17888
|
const kit = kits.find((k) => k.id === kitId);
|
|
17244
17889
|
if (!kit) {
|
|
17245
|
-
console.error(
|
|
17890
|
+
console.error(pc29.red(`Unknown kit: ${kitId}`));
|
|
17246
17891
|
process.exitCode = 1;
|
|
17247
17892
|
return;
|
|
17248
17893
|
}
|
|
@@ -17253,111 +17898,316 @@ function addForkSubcommands(parentCmd) {
|
|
|
17253
17898
|
baseVersion: kit.version,
|
|
17254
17899
|
label: opts.label
|
|
17255
17900
|
});
|
|
17256
|
-
console.log(
|
|
17257
|
-
console.log(
|
|
17258
|
-
console.log(
|
|
17901
|
+
console.log(pc29.green("Fork registered:"), reg.forkId);
|
|
17902
|
+
console.log(pc29.dim("Kit: "), reg.kitId, "v" + reg.baseVersion);
|
|
17903
|
+
console.log(pc29.dim("Path: "), reg.forkPath);
|
|
17259
17904
|
} catch (err) {
|
|
17260
|
-
console.error(
|
|
17905
|
+
console.error(pc29.red(err.message));
|
|
17261
17906
|
process.exitCode = 1;
|
|
17262
17907
|
}
|
|
17263
17908
|
});
|
|
17264
|
-
parentCmd.command("list").description("List all registered forks").option("--kit <kit-id>", "Filter by kit ID").option("--json", "Output raw JSON").action((opts) => {
|
|
17265
|
-
const
|
|
17909
|
+
parentCmd.command("list").description("List all registered forks with rich drift + policy summary").option("--kit <kit-id>", "Filter by kit ID").option("--filter <expr>", "Filter expression, e.g. status=synced, status=drift-major, kit=<id>").option("--sort-by <key>", "Sort by: id | kit | status | last-heal (default: id)").option("--no-upstream-check", "Skip upstream drift detection (faster, shows cached status only)").option("--json", "Output raw JSON").action((opts) => {
|
|
17910
|
+
const raw = listKitForkRegistrations(opts.kit);
|
|
17911
|
+
const skipUpstream = opts.upstreamCheck === false;
|
|
17912
|
+
let summaries = raw.map((f) => summarizeFork(f, { skipUpstreamCheck: skipUpstream }));
|
|
17913
|
+
if (opts.filter) {
|
|
17914
|
+
const [key, value] = opts.filter.split("=").map((s) => s.trim());
|
|
17915
|
+
if (!key || !value) {
|
|
17916
|
+
console.error(pc29.red(`Invalid --filter expression: ${opts.filter}`));
|
|
17917
|
+
console.error(pc29.dim("Use --filter status=<synced|drift-minor|drift-warn|drift-major|unknown> or kit=<id>"));
|
|
17918
|
+
process.exitCode = 1;
|
|
17919
|
+
return;
|
|
17920
|
+
}
|
|
17921
|
+
summaries = summaries.filter((s) => {
|
|
17922
|
+
if (key === "kit") return s.kitId === value;
|
|
17923
|
+
if (key === "status") {
|
|
17924
|
+
const mapping = {
|
|
17925
|
+
synced: "none",
|
|
17926
|
+
"drift-minor": "info",
|
|
17927
|
+
"drift-warn": "warning",
|
|
17928
|
+
"drift-major": "critical",
|
|
17929
|
+
unknown: "unknown"
|
|
17930
|
+
};
|
|
17931
|
+
const want = mapping[value] ?? value;
|
|
17932
|
+
return s.severity === want;
|
|
17933
|
+
}
|
|
17934
|
+
return true;
|
|
17935
|
+
});
|
|
17936
|
+
}
|
|
17937
|
+
const sortKey = opts.sortBy ?? "id";
|
|
17938
|
+
summaries.sort((a, b) => {
|
|
17939
|
+
if (sortKey === "kit") return a.kitId.localeCompare(b.kitId);
|
|
17940
|
+
if (sortKey === "status") {
|
|
17941
|
+
const order = ["none", "info", "warning", "critical", "unknown"];
|
|
17942
|
+
return order.indexOf(a.severity) - order.indexOf(b.severity);
|
|
17943
|
+
}
|
|
17944
|
+
if (sortKey === "last-heal") {
|
|
17945
|
+
return (b.lastHealAt ?? "").localeCompare(a.lastHealAt ?? "");
|
|
17946
|
+
}
|
|
17947
|
+
return a.forkId.localeCompare(b.forkId);
|
|
17948
|
+
});
|
|
17266
17949
|
if (opts.json) {
|
|
17267
|
-
console.log(JSON.stringify(
|
|
17950
|
+
console.log(JSON.stringify(summaries, null, 2));
|
|
17951
|
+
return;
|
|
17952
|
+
}
|
|
17953
|
+
if (summaries.length === 0) {
|
|
17954
|
+
console.log(pc29.dim(" No forks match the given filters."));
|
|
17268
17955
|
return;
|
|
17269
17956
|
}
|
|
17270
|
-
|
|
17957
|
+
console.log("");
|
|
17958
|
+
console.log(pc29.bold("Registered Kit Forks") + pc29.dim(` ${summaries.length} total`));
|
|
17959
|
+
console.log(hr());
|
|
17960
|
+
console.log(renderForkTable(summaries));
|
|
17961
|
+
console.log("");
|
|
17962
|
+
console.log(hr());
|
|
17271
17963
|
});
|
|
17272
|
-
parentCmd.command("status").description("Detect drift
|
|
17964
|
+
parentCmd.command("status").description("Detect drift + show policy evaluation + heal plan preview + next steps").argument("<fork-id>", "Fork ID from list").option("--policy-only", "Show only the fork's policy (skip drift detection)").option("--no-upstream-check", "Use cached registration fields; do not query the upstream bundled kit").option("--json", "Output raw JSON drift report").action(async (forkId, opts) => {
|
|
17273
17965
|
const reg = listKitForkRegistrations().find((f) => f.forkId === forkId);
|
|
17274
17966
|
if (!reg) {
|
|
17275
|
-
console.error(
|
|
17967
|
+
console.error(pc29.red(`Fork not found: ${forkId}`));
|
|
17968
|
+
console.error(pc29.dim("Hint: run `growthub kit fork list` to see registered forks."));
|
|
17276
17969
|
process.exitCode = 1;
|
|
17277
17970
|
return;
|
|
17278
17971
|
}
|
|
17972
|
+
const policy = readKitForkPolicy(reg.forkPath);
|
|
17973
|
+
if (opts.policyOnly) {
|
|
17974
|
+
if (opts.json) {
|
|
17975
|
+
console.log(JSON.stringify(policy, null, 2));
|
|
17976
|
+
return;
|
|
17977
|
+
}
|
|
17978
|
+
console.log("");
|
|
17979
|
+
console.log(pc29.bold(`Policy: ${reg.forkId}`));
|
|
17980
|
+
console.log(hr());
|
|
17981
|
+
console.log(` ${pc29.dim("autoApprove:")} ${policy.autoApprove}`);
|
|
17982
|
+
console.log(` ${pc29.dim("autoApproveDepUpdates:")} ${policy.autoApproveDepUpdates}`);
|
|
17983
|
+
console.log(` ${pc29.dim("remoteSyncMode:")} ${policy.remoteSyncMode}`);
|
|
17984
|
+
console.log(` ${pc29.dim("interactiveConflicts:")} ${policy.interactiveConflicts}`);
|
|
17985
|
+
console.log(` ${pc29.dim("untouchablePaths:")} [${policy.untouchablePaths.join(", ")}]`);
|
|
17986
|
+
console.log(` ${pc29.dim("confirmBeforeChange:")} [${policy.confirmBeforeChange.join(", ")}]`);
|
|
17987
|
+
console.log(` ${pc29.dim("allowedScripts:")} [${policy.allowedScripts.join(", ")}]`);
|
|
17988
|
+
console.log("");
|
|
17989
|
+
return;
|
|
17990
|
+
}
|
|
17991
|
+
if (opts.upstreamCheck === false) {
|
|
17992
|
+
if (opts.json) {
|
|
17993
|
+
console.log(JSON.stringify({ forkId: reg.forkId, kitId: reg.kitId, forkVersion: reg.baseVersion, policy }, null, 2));
|
|
17994
|
+
return;
|
|
17995
|
+
}
|
|
17996
|
+
console.log("");
|
|
17997
|
+
console.log(pc29.bold(`Fork: ${reg.forkId}`) + " " + pc29.dim("(no upstream check)"));
|
|
17998
|
+
console.log(pc29.dim(`Kit: ${reg.kitId} v${reg.baseVersion}`));
|
|
17999
|
+
console.log(hr());
|
|
18000
|
+
console.log(` ${pc29.dim("Path:")} ${reg.forkPath}`);
|
|
18001
|
+
console.log(` ${pc29.dim("Policy:")} autoApprove=${policy.autoApprove}, remoteSyncMode=${policy.remoteSyncMode}`);
|
|
18002
|
+
if (policy.untouchablePaths.length > 0) {
|
|
18003
|
+
console.log(` ${pc29.dim("Protected paths:")} ${policy.untouchablePaths.join(", ")}`);
|
|
18004
|
+
}
|
|
18005
|
+
console.log("");
|
|
18006
|
+
console.log(hr());
|
|
18007
|
+
printNextStepsAfterStatus(reg.forkId, true);
|
|
18008
|
+
return;
|
|
18009
|
+
}
|
|
17279
18010
|
try {
|
|
17280
18011
|
const report = detectKitForkDrift(reg);
|
|
18012
|
+
appendKitForkTraceEvent(reg.forkPath, {
|
|
18013
|
+
forkId: reg.forkId,
|
|
18014
|
+
kitId: reg.kitId,
|
|
18015
|
+
type: "status_ran",
|
|
18016
|
+
summary: `status inspected \u2014 severity=${report.overallSeverity}`
|
|
18017
|
+
});
|
|
17281
18018
|
if (opts.json) {
|
|
17282
|
-
|
|
18019
|
+
const plan = buildKitForkHealPlan(report, { policy });
|
|
18020
|
+
console.log(JSON.stringify({ drift: report, plan, policy }, null, 2));
|
|
17283
18021
|
return;
|
|
17284
18022
|
}
|
|
17285
18023
|
printDriftReport(report);
|
|
18024
|
+
const hasDrift = report.hasUpstreamUpdate || report.overallSeverity !== "none";
|
|
18025
|
+
if (hasDrift) {
|
|
18026
|
+
const plan = buildKitForkHealPlan(report, { policy });
|
|
18027
|
+
printRichHealPreview(plan, policy);
|
|
18028
|
+
}
|
|
18029
|
+
printNextStepsAfterStatus(reg.forkId, hasDrift);
|
|
17286
18030
|
} catch (err) {
|
|
17287
|
-
console.error(
|
|
18031
|
+
console.error(pc29.red(err.message));
|
|
17288
18032
|
process.exitCode = 1;
|
|
17289
18033
|
}
|
|
17290
18034
|
});
|
|
17291
|
-
parentCmd.command("heal").description("Apply a safe non-destructive heal to bring a fork up to date").argument("<fork-id>", "Fork ID from list").option("--dry-run", "Preview the plan without writing any files").option("--background", "Dispatch as an async background job").option("--skip <paths>", "Comma-separated relative paths to skip").option("--json", "Output heal plan as JSON (implies --dry-run)").action(async (forkId, opts) => {
|
|
18035
|
+
parentCmd.command("heal").description("Apply a safe non-destructive heal to bring a fork up to date").argument("<fork-id>", "Fork ID from list").option("--preview", "Rich grouped preview (safe additions / updates / protected / unresolved); does not write").option("--dry-run", "Preview the plan without writing any files").option("--background", "Dispatch as an async background job").option("--skip <paths>", "Comma-separated relative paths to skip").option("--json", "Output heal plan as JSON (implies --dry-run)").action(async (forkId, opts) => {
|
|
17292
18036
|
const reg = listKitForkRegistrations().find((f) => f.forkId === forkId);
|
|
17293
18037
|
if (!reg) {
|
|
17294
|
-
console.error(
|
|
18038
|
+
console.error(pc29.red(`Fork not found: ${forkId}`));
|
|
18039
|
+
console.error(pc29.dim("Hint: run `growthub kit fork list` to see registered forks."));
|
|
17295
18040
|
process.exitCode = 1;
|
|
17296
18041
|
return;
|
|
17297
18042
|
}
|
|
18043
|
+
const policy = readKitForkPolicy(reg.forkPath);
|
|
17298
18044
|
if (opts.json) {
|
|
17299
18045
|
const report = detectKitForkDrift(reg);
|
|
17300
|
-
const plan = buildKitForkHealPlan(report);
|
|
18046
|
+
const plan = buildKitForkHealPlan(report, { policy });
|
|
17301
18047
|
console.log(JSON.stringify(plan, null, 2));
|
|
17302
18048
|
return;
|
|
17303
18049
|
}
|
|
18050
|
+
if (opts.preview) {
|
|
18051
|
+
const report = detectKitForkDrift(reg);
|
|
18052
|
+
const plan = buildKitForkHealPlan(report, { policy });
|
|
18053
|
+
appendKitForkTraceEvent(reg.forkPath, {
|
|
18054
|
+
forkId: reg.forkId,
|
|
18055
|
+
kitId: reg.kitId,
|
|
18056
|
+
type: "heal_proposed",
|
|
18057
|
+
summary: `preview requested \u2014 ${plan.actions.length} action(s), risk=${plan.estimatedRisk}`,
|
|
18058
|
+
detail: { mode: "preview" }
|
|
18059
|
+
});
|
|
18060
|
+
printRichHealPreview(plan, policy);
|
|
18061
|
+
if (plan.actions.length === 0) {
|
|
18062
|
+
console.log(pc29.dim(" No actions needed. Fork is structurally clean."));
|
|
18063
|
+
return;
|
|
18064
|
+
}
|
|
18065
|
+
const decision = await p18.select({
|
|
18066
|
+
message: "Apply this plan?",
|
|
18067
|
+
options: [
|
|
18068
|
+
{ value: "apply", label: "\u25B6 Yes, apply now", hint: "Run synchronously in this terminal" },
|
|
18069
|
+
{ value: "background", label: "\u2699\uFE0F Run in background", hint: "Dispatch as an async job" },
|
|
18070
|
+
{ value: "cancel", label: "\u2190 No, cancel" }
|
|
18071
|
+
]
|
|
18072
|
+
});
|
|
18073
|
+
if (p18.isCancel(decision) || decision === "cancel") {
|
|
18074
|
+
console.log(pc29.dim(" Cancelled. Plan was not applied."));
|
|
18075
|
+
console.log(pc29.dim(` Next: ${pc29.cyan("growthub kit fork policy " + reg.forkId)} or ${pc29.cyan("growthub kit fork heal " + reg.forkId)}`));
|
|
18076
|
+
return;
|
|
18077
|
+
}
|
|
18078
|
+
if (decision === "background") {
|
|
18079
|
+
const jobId = dispatchKitForkSyncJobBackground(reg.forkId, reg.kitId, {
|
|
18080
|
+
skipFiles: opts.skip?.split(",").map((s) => s.trim())
|
|
18081
|
+
});
|
|
18082
|
+
console.log("");
|
|
18083
|
+
console.log(pc29.green(" \u2713 Background heal dispatched"));
|
|
18084
|
+
console.log(` Job ID: ${pc29.cyan(jobId)}`);
|
|
18085
|
+
console.log(` Watch: ${pc29.cyan("growthub kit fork jobs --watch " + jobId)}`);
|
|
18086
|
+
return;
|
|
18087
|
+
}
|
|
18088
|
+
opts.dryRun = false;
|
|
18089
|
+
}
|
|
17304
18090
|
if (opts.background) {
|
|
17305
18091
|
const jobId = dispatchKitForkSyncJobBackground(reg.forkId, reg.kitId, {
|
|
17306
18092
|
dryRun: opts.dryRun,
|
|
17307
18093
|
skipFiles: opts.skip?.split(",").map((s) => s.trim())
|
|
17308
18094
|
});
|
|
17309
|
-
console.log(
|
|
17310
|
-
console.log(
|
|
18095
|
+
console.log(pc29.green("Background job dispatched:"), jobId);
|
|
18096
|
+
console.log(pc29.dim(` Watch: growthub kit fork jobs --watch ${jobId}`));
|
|
18097
|
+
console.log(pc29.dim(` List: growthub kit fork jobs`));
|
|
17311
18098
|
return;
|
|
17312
18099
|
}
|
|
17313
18100
|
const job = await runKitForkSyncJob(reg.forkId, reg.kitId, {
|
|
17314
18101
|
dryRun: opts.dryRun,
|
|
17315
18102
|
skipFiles: opts.skip?.split(",").map((s) => s.trim()),
|
|
17316
|
-
onProgress: (step) => process.stderr.write(
|
|
18103
|
+
onProgress: (step) => process.stderr.write(pc29.dim(step) + "\n")
|
|
17317
18104
|
});
|
|
17318
18105
|
const r = job.healResult;
|
|
17319
18106
|
if (r) {
|
|
17320
|
-
console.log(
|
|
17321
|
-
console.log(` Applied: ${r.appliedCount} Skipped: ${r.skippedCount} Errors:
|
|
18107
|
+
console.log(pc29.bold("Heal result:"));
|
|
18108
|
+
console.log(` ${pc29.green("Applied:")} ${r.appliedCount} ${pc29.dim("Skipped:")} ${r.skippedCount} ${r.errorCount > 0 ? pc29.red("Errors: " + r.errorCount) : pc29.dim("Errors: 0")}`);
|
|
17322
18109
|
}
|
|
17323
18110
|
if (job.status === "failed") {
|
|
17324
|
-
console.error(
|
|
18111
|
+
console.error(pc29.red("Heal failed: " + (job.error ?? "unknown error")));
|
|
18112
|
+
console.error(pc29.dim(` Review: growthub kit fork history ${reg.forkId}`));
|
|
17325
18113
|
process.exitCode = 1;
|
|
17326
18114
|
}
|
|
17327
18115
|
});
|
|
17328
|
-
parentCmd.command("jobs").description("List background fork-sync jobs").option("--fork <fork-id>", "Filter by fork ID").option("--status <status>", "Filter by status").option("--json", "Output raw JSON").action((opts) => {
|
|
18116
|
+
parentCmd.command("jobs").description("List background fork-sync jobs, watch live progress, or tail a job's trace").option("--fork <fork-id>", "Filter by fork ID").option("--status <status>", "Filter by status (pending | running | completed | failed | cancelled | awaiting_confirmation)").option("--filter <expr>", "Filter expression, e.g. status=running").option("--watch <job-id>", "Poll the given job and print live progress until terminal").option("--tail <job-id>", "Print trace events from the fork of the given job (default 50)").option("--limit <n>", "Limit entries when using --tail (default 50)", (v) => parseInt(v, 10)).option("--json", "Output raw JSON").action(async (opts) => {
|
|
18117
|
+
if (opts.watch) {
|
|
18118
|
+
await watchJob(opts.watch, Boolean(opts.json));
|
|
18119
|
+
return;
|
|
18120
|
+
}
|
|
18121
|
+
if (opts.tail) {
|
|
18122
|
+
tailJobTrace(opts.tail, opts.limit ?? 50, Boolean(opts.json));
|
|
18123
|
+
return;
|
|
18124
|
+
}
|
|
18125
|
+
let effectiveStatus = opts.status;
|
|
18126
|
+
if (!effectiveStatus && opts.filter) {
|
|
18127
|
+
const [key, value] = opts.filter.split("=").map((s) => s.trim());
|
|
18128
|
+
if (key === "status" && value) effectiveStatus = value;
|
|
18129
|
+
}
|
|
17329
18130
|
const jobs = listKitForkSyncJobs({
|
|
17330
18131
|
forkId: opts.fork,
|
|
17331
|
-
status:
|
|
18132
|
+
status: effectiveStatus
|
|
17332
18133
|
});
|
|
17333
18134
|
if (opts.json) {
|
|
17334
18135
|
console.log(JSON.stringify(jobs, null, 2));
|
|
17335
18136
|
return;
|
|
17336
18137
|
}
|
|
17337
18138
|
if (jobs.length === 0) {
|
|
17338
|
-
console.log(
|
|
18139
|
+
console.log(pc29.dim(" No jobs found."));
|
|
17339
18140
|
return;
|
|
17340
18141
|
}
|
|
17341
18142
|
console.log("");
|
|
17342
|
-
console.log(
|
|
18143
|
+
console.log(pc29.bold("Kit Fork Sync Jobs") + pc29.dim(` ${jobs.length} total`));
|
|
17343
18144
|
console.log(hr());
|
|
17344
|
-
|
|
17345
|
-
|
|
17346
|
-
|
|
17347
|
-
|
|
18145
|
+
console.log(renderJobTable(jobs));
|
|
18146
|
+
console.log("");
|
|
18147
|
+
console.log(hr());
|
|
18148
|
+
console.log(pc29.dim(" Live progress: growthub kit fork jobs --watch <job-id>"));
|
|
18149
|
+
console.log(pc29.dim(" Trace events: growthub kit fork jobs --tail <job-id>"));
|
|
18150
|
+
console.log("");
|
|
18151
|
+
});
|
|
18152
|
+
parentCmd.command("history").description("Export fork operation history from trace.jsonl").argument("<fork-id>", "Fork ID from list").option("--since <iso>", "ISO-8601 start date (inclusive)").option("--until <iso>", "ISO-8601 end date (inclusive)").option("--event-type <type>", "Filter by event type (e.g. heal_applied, policy_updated)").option("--limit <n>", "Return at most N events (applied after filters)", (v) => parseInt(v, 10)).option("--json", "Emit machine-readable JSON").option("--csv", "Emit CSV for compliance tools").action((forkId, opts) => {
|
|
18153
|
+
const reg = listKitForkRegistrations().find((f) => f.forkId === forkId);
|
|
18154
|
+
if (!reg) {
|
|
18155
|
+
console.error(pc29.red(`Fork not found: ${forkId}`));
|
|
18156
|
+
console.error(pc29.dim("Hint: run `growthub kit fork list` to see registered forks."));
|
|
18157
|
+
process.exitCode = 1;
|
|
18158
|
+
return;
|
|
18159
|
+
}
|
|
18160
|
+
const all = readKitForkTrace(reg.forkPath);
|
|
18161
|
+
const sinceMs = opts.since ? new Date(opts.since).getTime() : void 0;
|
|
18162
|
+
const untilMs = opts.until ? new Date(opts.until).getTime() : void 0;
|
|
18163
|
+
let events = all.filter((e) => {
|
|
18164
|
+
const ts = new Date(e.timestamp).getTime();
|
|
18165
|
+
if (sinceMs !== void 0 && ts < sinceMs) return false;
|
|
18166
|
+
if (untilMs !== void 0 && ts > untilMs) return false;
|
|
18167
|
+
if (opts.eventType && e.type !== opts.eventType) return false;
|
|
18168
|
+
return true;
|
|
18169
|
+
});
|
|
18170
|
+
if (opts.limit && opts.limit > 0) {
|
|
18171
|
+
events = events.slice(-opts.limit);
|
|
18172
|
+
}
|
|
18173
|
+
if (opts.json) {
|
|
18174
|
+
console.log(JSON.stringify(events, null, 2));
|
|
18175
|
+
return;
|
|
18176
|
+
}
|
|
18177
|
+
if (opts.csv) {
|
|
18178
|
+
console.log("timestamp,forkId,kitId,jobId,type,summary");
|
|
18179
|
+
for (const e of events) {
|
|
18180
|
+
const summary = (e.summary ?? "").replace(/"/g, '""');
|
|
18181
|
+
console.log(`${e.timestamp},${e.forkId},${e.kitId},${e.jobId ?? ""},${e.type},"${summary}"`);
|
|
18182
|
+
}
|
|
18183
|
+
return;
|
|
18184
|
+
}
|
|
18185
|
+
console.log("");
|
|
18186
|
+
console.log(pc29.bold(`Fork History: ${reg.forkId}`) + pc29.dim(` ${events.length} event(s)`));
|
|
18187
|
+
console.log(hr());
|
|
18188
|
+
if (events.length === 0) {
|
|
18189
|
+
console.log(pc29.dim(" No matching events."));
|
|
18190
|
+
console.log("");
|
|
18191
|
+
return;
|
|
17348
18192
|
}
|
|
18193
|
+
for (const e of events) {
|
|
18194
|
+
const ts = e.timestamp.replace("T", " ").replace(/\..+Z$/, "Z");
|
|
18195
|
+
console.log(` ${pc29.dim(ts)} ${pc29.cyan("[" + e.type + "]")} ${e.summary ?? ""}`);
|
|
18196
|
+
}
|
|
18197
|
+
console.log("");
|
|
18198
|
+
console.log(hr());
|
|
17349
18199
|
console.log("");
|
|
17350
18200
|
});
|
|
17351
18201
|
parentCmd.command("deregister").description("Remove a fork registration (does not delete your fork directory)").argument("<fork-id>", "Fork ID to deregister").action(async (forkId) => {
|
|
17352
18202
|
const allForks = listKitForkRegistrations();
|
|
17353
18203
|
const reg = allForks.find((f) => f.forkId === forkId);
|
|
17354
18204
|
if (!reg) {
|
|
17355
|
-
console.error(
|
|
18205
|
+
console.error(pc29.red(`Fork not found: ${forkId}`));
|
|
17356
18206
|
process.exitCode = 1;
|
|
17357
18207
|
return;
|
|
17358
18208
|
}
|
|
17359
18209
|
const confirmed = await p18.confirm({
|
|
17360
|
-
message: `Remove registration for ${
|
|
18210
|
+
message: `Remove registration for ${pc29.cyan(forkId)}? (Your fork directory will not be touched)`,
|
|
17361
18211
|
initialValue: false
|
|
17362
18212
|
});
|
|
17363
18213
|
if (p18.isCancel(confirmed) || !confirmed) {
|
|
@@ -17366,9 +18216,9 @@ function addForkSubcommands(parentCmd) {
|
|
|
17366
18216
|
}
|
|
17367
18217
|
const ok = deregisterKitFork(reg.kitId, forkId);
|
|
17368
18218
|
if (ok) {
|
|
17369
|
-
console.log(
|
|
18219
|
+
console.log(pc29.green("Fork deregistered:"), forkId);
|
|
17370
18220
|
} else {
|
|
17371
|
-
console.error(
|
|
18221
|
+
console.error(pc29.red("Deregistration failed."));
|
|
17372
18222
|
process.exitCode = 1;
|
|
17373
18223
|
}
|
|
17374
18224
|
});
|
|
@@ -17376,18 +18226,18 @@ function addForkSubcommands(parentCmd) {
|
|
|
17376
18226
|
|
|
17377
18227
|
// src/commands/kit.ts
|
|
17378
18228
|
var TYPE_CONFIG = {
|
|
17379
|
-
studio: { color:
|
|
17380
|
-
specialized_agents: { color:
|
|
17381
|
-
ops: { color:
|
|
18229
|
+
studio: { color: pc30.cyan, emoji: "\u{1F6E0}\uFE0F", label: "Custom Workspaces" },
|
|
18230
|
+
specialized_agents: { color: pc30.magenta, emoji: "\u{1F9E0}", label: "Specialized Agents" },
|
|
18231
|
+
ops: { color: pc30.yellow, emoji: "\u2699\uFE0F ", label: "Ops" }
|
|
17382
18232
|
};
|
|
17383
18233
|
function displayTypeForFamily(family) {
|
|
17384
18234
|
if (family === "workflow" || family === "operator") return "specialized_agents";
|
|
17385
18235
|
if (family === "studio" || family === "ops") return family;
|
|
17386
18236
|
return family;
|
|
17387
18237
|
}
|
|
17388
|
-
function typeColor(family,
|
|
18238
|
+
function typeColor(family, text65) {
|
|
17389
18239
|
const type = displayTypeForFamily(family);
|
|
17390
|
-
return TYPE_CONFIG[type]?.color(
|
|
18240
|
+
return TYPE_CONFIG[type]?.color(text65) ?? text65;
|
|
17391
18241
|
}
|
|
17392
18242
|
function typeBadge(family) {
|
|
17393
18243
|
const type = displayTypeForFamily(family);
|
|
@@ -17395,7 +18245,7 @@ function typeBadge(family) {
|
|
|
17395
18245
|
if (!cfg) return String(type);
|
|
17396
18246
|
return cfg.color(`${cfg.emoji} ${cfg.label}`);
|
|
17397
18247
|
}
|
|
17398
|
-
function
|
|
18248
|
+
function truncate2(str, max) {
|
|
17399
18249
|
if (str.length <= max) return str;
|
|
17400
18250
|
return str.slice(0, max - 1) + "\u2026";
|
|
17401
18251
|
}
|
|
@@ -17403,20 +18253,20 @@ function displayKitName(name) {
|
|
|
17403
18253
|
return name.replace(/^Growthub Agent Worker Kit\s+[—-]\s+/u, "").trim();
|
|
17404
18254
|
}
|
|
17405
18255
|
function hr2(width = 72) {
|
|
17406
|
-
return
|
|
18256
|
+
return pc30.dim("\u2500".repeat(width));
|
|
17407
18257
|
}
|
|
17408
18258
|
function box(lines) {
|
|
17409
18259
|
const padded = lines.map((l) => " " + l);
|
|
17410
|
-
const width = Math.max(...padded.map((l) =>
|
|
17411
|
-
const top =
|
|
17412
|
-
const bottom =
|
|
18260
|
+
const width = Math.max(...padded.map((l) => stripAnsi2(l).length)) + 4;
|
|
18261
|
+
const top = pc30.dim("\u250C" + "\u2500".repeat(width) + "\u2510");
|
|
18262
|
+
const bottom = pc30.dim("\u2514" + "\u2500".repeat(width) + "\u2518");
|
|
17413
18263
|
const body = padded.map((l) => {
|
|
17414
|
-
const
|
|
17415
|
-
return
|
|
18264
|
+
const pad2 = width - stripAnsi2(l).length;
|
|
18265
|
+
return pc30.dim("\u2502") + l + " ".repeat(pad2) + pc30.dim("\u2502");
|
|
17416
18266
|
});
|
|
17417
18267
|
return [top, ...body, bottom].join("\n");
|
|
17418
18268
|
}
|
|
17419
|
-
function
|
|
18269
|
+
function stripAnsi2(str) {
|
|
17420
18270
|
return str.replace(/\x1B\[[0-9;]*m/g, "");
|
|
17421
18271
|
}
|
|
17422
18272
|
function terminalLink(label, href) {
|
|
@@ -17427,13 +18277,13 @@ function folderOpenLabel(folderPath) {
|
|
|
17427
18277
|
const label = process.platform === "darwin" ? "Open in Finder" : process.platform === "win32" ? "Open in Explorer" : "Open folder";
|
|
17428
18278
|
return terminalLink(label, href);
|
|
17429
18279
|
}
|
|
17430
|
-
function
|
|
18280
|
+
function renderProgressBar2(progress) {
|
|
17431
18281
|
if (!process.stdout.isTTY) return;
|
|
17432
18282
|
const width = 24;
|
|
17433
18283
|
const filled = Math.max(0, Math.min(width, Math.round(progress.percent / 100 * width)));
|
|
17434
18284
|
const bar = `${"=".repeat(filled)}${"-".repeat(width - filled)}`;
|
|
17435
|
-
const detail =
|
|
17436
|
-
const line = `\r${
|
|
18285
|
+
const detail = truncate2(progress.detail, 48);
|
|
18286
|
+
const line = `\r${pc30.cyan("Exporting kit")} ${pc30.dim("[")}${pc30.green(bar)}${pc30.dim("]")} ${String(progress.percent).padStart(3)}% ${pc30.dim(detail)}`;
|
|
17437
18287
|
process.stdout.write(line);
|
|
17438
18288
|
if (progress.phase === "done") {
|
|
17439
18289
|
process.stdout.write("\n");
|
|
@@ -17443,12 +18293,12 @@ function printKitCard(item) {
|
|
|
17443
18293
|
const badge2 = typeBadge(item.family);
|
|
17444
18294
|
console.log("");
|
|
17445
18295
|
console.log(box([
|
|
17446
|
-
`${
|
|
17447
|
-
`${badge2} ${
|
|
18296
|
+
`${pc30.bold(item.name)} ${pc30.dim("v" + item.version)}`,
|
|
18297
|
+
`${badge2} ${pc30.dim(item.id)}`,
|
|
17448
18298
|
"",
|
|
17449
|
-
|
|
18299
|
+
truncate2(item.description, 62),
|
|
17450
18300
|
"",
|
|
17451
|
-
`${
|
|
18301
|
+
`${pc30.dim("Brief:")} ${pc30.dim(item.briefType)} ${pc30.dim("Mode:")} ${pc30.dim(item.executionMode)}`
|
|
17452
18302
|
]));
|
|
17453
18303
|
}
|
|
17454
18304
|
function getActionLabel(action) {
|
|
@@ -17462,10 +18312,10 @@ async function confirmKitActions(input) {
|
|
|
17462
18312
|
return getActionLabel(action);
|
|
17463
18313
|
});
|
|
17464
18314
|
const summaryLines = [
|
|
17465
|
-
|
|
18315
|
+
pc30.bold("Selected kits"),
|
|
17466
18316
|
...input.kits.map((kit) => `${typeBadge(kit.family)} ${displayKitName(kit.name)}`),
|
|
17467
18317
|
"",
|
|
17468
|
-
|
|
18318
|
+
pc30.bold("Selected actions"),
|
|
17469
18319
|
actionLabels.join(", ")
|
|
17470
18320
|
];
|
|
17471
18321
|
console.log("");
|
|
@@ -17490,28 +18340,28 @@ function printGroupedList(kits) {
|
|
|
17490
18340
|
const totalTypes = types.length;
|
|
17491
18341
|
console.log("");
|
|
17492
18342
|
console.log(
|
|
17493
|
-
|
|
18343
|
+
pc30.bold("Growthub Agent Worker Kits") + pc30.dim(` ${kits.length} kit${kits.length !== 1 ? "s" : ""} \xB7 ${totalTypes} type${totalTypes !== 1 ? "s" : ""}`)
|
|
17494
18344
|
);
|
|
17495
18345
|
console.log(hr2());
|
|
17496
18346
|
for (const type of types) {
|
|
17497
18347
|
const groupKits = byType[type];
|
|
17498
18348
|
const header = typeBadge(type);
|
|
17499
18349
|
console.log(`
|
|
17500
|
-
${header} ${
|
|
18350
|
+
${header} ${pc30.dim("(" + groupKits.length + ")")}`);
|
|
17501
18351
|
for (const kit of groupKits) {
|
|
17502
|
-
console.log(` ${typeColor(kit.family,
|
|
17503
|
-
console.log(` ${
|
|
17504
|
-
console.log(` ${
|
|
18352
|
+
console.log(` ${typeColor(kit.family, pc30.bold(kit.id))} ${pc30.dim("v" + kit.version)}`);
|
|
18353
|
+
console.log(` ${pc30.dim(truncate2(kit.description, 62))}`);
|
|
18354
|
+
console.log(` ${pc30.dim("\u2192")} ${pc30.cyan("growthub kit download " + kit.id)}`);
|
|
17505
18355
|
console.log("");
|
|
17506
18356
|
}
|
|
17507
18357
|
}
|
|
17508
18358
|
console.log(hr2());
|
|
17509
|
-
console.log(
|
|
18359
|
+
console.log(pc30.dim(" growthub kit download <id> \xB7 growthub kit inspect <id> \xB7 growthub kit families"));
|
|
17510
18360
|
console.log("");
|
|
17511
18361
|
}
|
|
17512
18362
|
async function runInteractivePicker(opts) {
|
|
17513
18363
|
printPaperclipCliBanner();
|
|
17514
|
-
p19.intro(
|
|
18364
|
+
p19.intro(pc30.bold("Growthub Agent Worker Kits"));
|
|
17515
18365
|
let kits;
|
|
17516
18366
|
try {
|
|
17517
18367
|
kits = listBundledKits();
|
|
@@ -17553,8 +18403,8 @@ async function runInteractivePicker(opts) {
|
|
|
17553
18403
|
options: [
|
|
17554
18404
|
...filtered.map((k) => ({
|
|
17555
18405
|
value: k.id,
|
|
17556
|
-
label: (showTypeBadgeInKitChoices ? typeBadge(k.family) + " " : "") +
|
|
17557
|
-
hint:
|
|
18406
|
+
label: (showTypeBadgeInKitChoices ? typeBadge(k.family) + " " : "") + pc30.bold(displayKitName(k.name)) + " " + pc30.dim("v" + k.version),
|
|
18407
|
+
hint: truncate2(k.description, 55)
|
|
17558
18408
|
})),
|
|
17559
18409
|
{ value: "__back_to_type", label: "\u2190 Back to type filter" }
|
|
17560
18410
|
]
|
|
@@ -17618,16 +18468,16 @@ async function runInteractivePicker(opts) {
|
|
|
17618
18468
|
}
|
|
17619
18469
|
if (action === "copy-id") {
|
|
17620
18470
|
console.log(selected.id);
|
|
17621
|
-
p19.outro(
|
|
18471
|
+
p19.outro(pc30.dim("Kit ID printed above."));
|
|
17622
18472
|
return "done";
|
|
17623
18473
|
}
|
|
17624
18474
|
if (action === "inspect") {
|
|
17625
18475
|
runInspect(selected.id, opts.out);
|
|
17626
|
-
p19.outro(
|
|
18476
|
+
p19.outro(pc30.dim("Done."));
|
|
17627
18477
|
return "done";
|
|
17628
18478
|
}
|
|
17629
18479
|
await runDownload(selected.id, opts);
|
|
17630
|
-
p19.outro(
|
|
18480
|
+
p19.outro(pc30.green("Kit exported successfully."));
|
|
17631
18481
|
return "done";
|
|
17632
18482
|
}
|
|
17633
18483
|
}
|
|
@@ -17636,58 +18486,58 @@ async function runInteractivePicker(opts) {
|
|
|
17636
18486
|
async function runDownload(kitId, opts) {
|
|
17637
18487
|
const resolvedId = fuzzyResolveKitId(kitId);
|
|
17638
18488
|
if (!resolvedId) {
|
|
17639
|
-
console.error(
|
|
18489
|
+
console.error(pc30.red("Unknown kit '" + kitId + "'.") + pc30.dim(" Run `growthub kit list` to browse."));
|
|
17640
18490
|
process.exit(1);
|
|
17641
18491
|
}
|
|
17642
18492
|
if (resolvedId !== kitId) {
|
|
17643
|
-
console.log(
|
|
18493
|
+
console.log(pc30.dim("Resolved '" + kitId + "' \u2192 " + resolvedId));
|
|
17644
18494
|
}
|
|
17645
18495
|
const kits = listBundledKits();
|
|
17646
18496
|
const item = kits.find((k) => k.id === resolvedId);
|
|
17647
18497
|
printKitCard(item);
|
|
17648
18498
|
if (!opts.yes) {
|
|
17649
|
-
const confirmed = await p19.confirm({ message: "Download " +
|
|
18499
|
+
const confirmed = await p19.confirm({ message: "Download " + pc30.bold(displayKitName(item.name)) + "?" });
|
|
17650
18500
|
if (p19.isCancel(confirmed) || !confirmed) {
|
|
17651
18501
|
p19.cancel("Cancelled.");
|
|
17652
18502
|
process.exit(0);
|
|
17653
18503
|
}
|
|
17654
18504
|
}
|
|
17655
18505
|
const result = downloadBundledKit(resolvedId, opts.out, {
|
|
17656
|
-
onProgress:
|
|
18506
|
+
onProgress: renderProgressBar2
|
|
17657
18507
|
});
|
|
17658
18508
|
console.log("");
|
|
17659
|
-
console.log(
|
|
18509
|
+
console.log(pc30.green(pc30.bold("Kit exported successfully.")));
|
|
17660
18510
|
console.log("");
|
|
17661
18511
|
const nextSteps = [
|
|
17662
|
-
|
|
18512
|
+
pc30.bold("Next steps"),
|
|
17663
18513
|
"",
|
|
17664
|
-
|
|
17665
|
-
" " +
|
|
18514
|
+
pc30.dim("1.") + " Point Working Directory at:",
|
|
18515
|
+
" " + pc30.cyan(result.folderPath),
|
|
17666
18516
|
"",
|
|
17667
|
-
|
|
17668
|
-
|
|
17669
|
-
|
|
18517
|
+
pc30.dim("2.") + " " + pc30.cyan("cp .env.example .env") + " \u2192 add your API key",
|
|
18518
|
+
pc30.dim("3.") + " " + pc30.cyan("bash setup/clone-fork.sh") + " \u2192 boot local studio",
|
|
18519
|
+
pc30.dim("4.") + " Open Growthub local \u2014 the agent loads automatically",
|
|
17670
18520
|
"",
|
|
17671
|
-
|
|
18521
|
+
pc30.dim("Docs: QUICKSTART.md \xB7 validation-checklist.md")
|
|
17672
18522
|
];
|
|
17673
18523
|
console.log("");
|
|
17674
18524
|
console.log(box(nextSteps));
|
|
17675
18525
|
console.log("");
|
|
17676
|
-
console.log(
|
|
17677
|
-
console.log(
|
|
18526
|
+
console.log(pc30.bold("Open folder: ") + folderOpenLabel(result.folderPath));
|
|
18527
|
+
console.log(pc30.dim("Folder: ") + result.folderPath);
|
|
17678
18528
|
console.log("");
|
|
17679
|
-
console.log(
|
|
18529
|
+
console.log(pc30.dim("Zip: ") + result.zipPath);
|
|
17680
18530
|
console.log("");
|
|
17681
18531
|
}
|
|
17682
18532
|
function runInspect(kitId, outDir) {
|
|
17683
18533
|
const info = inspectBundledKit(kitId, outDir);
|
|
17684
|
-
const kv = (label, value) => console.log(" " +
|
|
18534
|
+
const kv = (label, value) => console.log(" " + pc30.bold(label.padEnd(24)) + " " + value);
|
|
17685
18535
|
console.log("");
|
|
17686
|
-
console.log(
|
|
17687
|
-
console.log(typeBadge(info.family) +
|
|
18536
|
+
console.log(pc30.bold("Kit: " + info.id) + pc30.dim(" v" + info.version));
|
|
18537
|
+
console.log(typeBadge(info.family) + pc30.dim(" schema v" + info.schemaVersion));
|
|
17688
18538
|
console.log(hr2());
|
|
17689
18539
|
kv("Name:", info.name);
|
|
17690
|
-
kv("Description:",
|
|
18540
|
+
kv("Description:", truncate2(info.description, 55));
|
|
17691
18541
|
kv("Entrypoint:", info.entrypointPath);
|
|
17692
18542
|
kv("Agent Contract:", info.agentContractPath);
|
|
17693
18543
|
kv("Bundle:", info.bundleId + " @ " + info.bundleVersion);
|
|
@@ -17700,8 +18550,8 @@ function runInspect(kitId, outDir) {
|
|
|
17700
18550
|
kv("Compatibility:", JSON.stringify(info.compatibility));
|
|
17701
18551
|
}
|
|
17702
18552
|
console.log(hr2());
|
|
17703
|
-
console.log(
|
|
17704
|
-
for (const rp of info.requiredPaths) console.log(" " +
|
|
18553
|
+
console.log(pc30.bold(" Required Paths:"));
|
|
18554
|
+
for (const rp of info.requiredPaths) console.log(" " + pc30.dim("\xB7") + " " + rp);
|
|
17705
18555
|
console.log("");
|
|
17706
18556
|
}
|
|
17707
18557
|
function registerKitCommands(program2) {
|
|
@@ -17737,8 +18587,8 @@ Examples:
|
|
|
17737
18587
|
const wanted = opts.family.split(",").map((f) => f.trim().toLowerCase());
|
|
17738
18588
|
kits = kits.filter((k) => wanted.includes(k.family));
|
|
17739
18589
|
if (kits.length === 0) {
|
|
17740
|
-
console.error(
|
|
17741
|
-
console.error(
|
|
18590
|
+
console.error(pc30.yellow("No kits found for family: " + opts.family));
|
|
18591
|
+
console.error(pc30.dim("Valid families: studio, workflow, operator, ops"));
|
|
17742
18592
|
process.exitCode = 1;
|
|
17743
18593
|
return;
|
|
17744
18594
|
}
|
|
@@ -17756,7 +18606,7 @@ Examples:
|
|
|
17756
18606
|
`).action((kitId, opts) => {
|
|
17757
18607
|
const resolvedId = fuzzyResolveKitId(kitId);
|
|
17758
18608
|
if (!resolvedId) {
|
|
17759
|
-
console.error(
|
|
18609
|
+
console.error(pc30.red("Unknown kit '" + kitId + "'.") + pc30.dim(" Run `growthub kit list` to browse."));
|
|
17760
18610
|
process.exitCode = 1;
|
|
17761
18611
|
return;
|
|
17762
18612
|
}
|
|
@@ -17780,23 +18630,23 @@ Examples:
|
|
|
17780
18630
|
}
|
|
17781
18631
|
const resolvedId = fuzzyResolveKitId(kitId);
|
|
17782
18632
|
if (!resolvedId) {
|
|
17783
|
-
console.error(
|
|
18633
|
+
console.error(pc30.red("Unknown kit '" + kitId + "'.") + pc30.dim(" Run `growthub kit list` to browse."));
|
|
17784
18634
|
process.exitCode = 1;
|
|
17785
18635
|
return;
|
|
17786
18636
|
}
|
|
17787
18637
|
if (opts.yes) {
|
|
17788
18638
|
const result = downloadBundledKit(resolvedId, opts.out, {
|
|
17789
|
-
onProgress:
|
|
18639
|
+
onProgress: renderProgressBar2
|
|
17790
18640
|
});
|
|
17791
18641
|
console.log("");
|
|
17792
|
-
console.log(
|
|
17793
|
-
console.log(
|
|
17794
|
-
console.log(
|
|
18642
|
+
console.log(pc30.bold("Exported folder:"), pc30.cyan(result.folderPath));
|
|
18643
|
+
console.log(pc30.bold("Open folder: "), folderOpenLabel(result.folderPath));
|
|
18644
|
+
console.log(pc30.bold("Zip: "), pc30.dim(result.zipPath));
|
|
17795
18645
|
console.log("");
|
|
17796
|
-
console.log(
|
|
17797
|
-
console.log(" 1. Point Working Directory at: " +
|
|
17798
|
-
console.log(" 2. " +
|
|
17799
|
-
console.log(" 3. " +
|
|
18646
|
+
console.log(pc30.bold("Next steps:"));
|
|
18647
|
+
console.log(" 1. Point Working Directory at: " + pc30.cyan(result.folderPath));
|
|
18648
|
+
console.log(" 2. " + pc30.cyan("cp .env.example .env") + " \u2192 add your API key");
|
|
18649
|
+
console.log(" 3. " + pc30.cyan("bash setup/clone-fork.sh") + " \u2192 boot local studio");
|
|
17800
18650
|
console.log(" 4. Open Growthub local \u2014 the agent loads automatically");
|
|
17801
18651
|
console.log("");
|
|
17802
18652
|
return;
|
|
@@ -17806,7 +18656,7 @@ Examples:
|
|
|
17806
18656
|
kit.command("path").description("Resolve the expected export folder path without exporting").argument("<kit-id>", "Kit id or fuzzy slug").option("--out <path>", "Override the export root").action((kitId, opts) => {
|
|
17807
18657
|
const resolvedId = fuzzyResolveKitId(kitId);
|
|
17808
18658
|
if (!resolvedId) {
|
|
17809
|
-
console.error(
|
|
18659
|
+
console.error(pc30.red("Unknown kit '" + kitId + "'."));
|
|
17810
18660
|
process.exitCode = 1;
|
|
17811
18661
|
return;
|
|
17812
18662
|
}
|
|
@@ -17820,20 +18670,20 @@ Examples:
|
|
|
17820
18670
|
const resolvedPath = path33.resolve(kitPath);
|
|
17821
18671
|
const result = validateKitDirectory(resolvedPath);
|
|
17822
18672
|
console.log("");
|
|
17823
|
-
console.log(
|
|
18673
|
+
console.log(pc30.bold("Kit: " + result.kitId) + pc30.dim(" schema v" + result.schemaVersion));
|
|
17824
18674
|
console.log(hr2());
|
|
17825
18675
|
for (const w of result.warnings) {
|
|
17826
|
-
console.log(
|
|
18676
|
+
console.log(pc30.yellow(" WARN " + w.field + ": " + w.message));
|
|
17827
18677
|
}
|
|
17828
18678
|
for (const e of result.errors) {
|
|
17829
|
-
console.log(
|
|
18679
|
+
console.log(pc30.red(" ERROR " + e.field + ": " + e.message));
|
|
17830
18680
|
}
|
|
17831
18681
|
if (result.errors.length > 0) {
|
|
17832
18682
|
console.log("");
|
|
17833
|
-
console.log(
|
|
18683
|
+
console.log(pc30.red(pc30.bold(" Result: INVALID")) + pc30.dim(" (" + result.errors.length + " error" + (result.errors.length !== 1 ? "s" : "") + ")"));
|
|
17834
18684
|
process.exitCode = 1;
|
|
17835
18685
|
} else {
|
|
17836
|
-
console.log(
|
|
18686
|
+
console.log(pc30.green(pc30.bold(" Result: VALID")));
|
|
17837
18687
|
}
|
|
17838
18688
|
console.log("");
|
|
17839
18689
|
});
|
|
@@ -17845,17 +18695,17 @@ Examples:
|
|
|
17845
18695
|
{ family: "ops", tagline: "Infrastructure / toolchain operator (provider optional)", surfaces: "local-fork (primary)", example: "(coming soon)" }
|
|
17846
18696
|
];
|
|
17847
18697
|
console.log("");
|
|
17848
|
-
console.log(
|
|
18698
|
+
console.log(pc30.bold("Kit Family Taxonomy"));
|
|
17849
18699
|
console.log(hr2());
|
|
17850
18700
|
for (const def of defs) {
|
|
17851
18701
|
console.log("\n " + typeBadge(def.family));
|
|
17852
|
-
console.log(" " +
|
|
17853
|
-
console.log(" " +
|
|
17854
|
-
console.log(" " +
|
|
18702
|
+
console.log(" " + pc30.dim(def.tagline));
|
|
18703
|
+
console.log(" " + pc30.dim("Surfaces: ") + pc30.dim(def.surfaces));
|
|
18704
|
+
console.log(" " + pc30.dim("Example: ") + pc30.cyan(def.example));
|
|
17855
18705
|
}
|
|
17856
18706
|
console.log("");
|
|
17857
18707
|
console.log(hr2());
|
|
17858
|
-
console.log(
|
|
18708
|
+
console.log(pc30.dim(" growthub kit list --family <family> to filter by internal family"));
|
|
17859
18709
|
console.log("");
|
|
17860
18710
|
});
|
|
17861
18711
|
registerKitForkSubcommands(kit);
|
|
@@ -17864,7 +18714,7 @@ Examples:
|
|
|
17864
18714
|
// src/commands/template.ts
|
|
17865
18715
|
import path35 from "node:path";
|
|
17866
18716
|
import * as p20 from "@clack/prompts";
|
|
17867
|
-
import
|
|
18717
|
+
import pc31 from "picocolors";
|
|
17868
18718
|
|
|
17869
18719
|
// src/templates/service.ts
|
|
17870
18720
|
import fs26 from "node:fs";
|
|
@@ -18209,44 +19059,44 @@ function getCatalogStats() {
|
|
|
18209
19059
|
}
|
|
18210
19060
|
|
|
18211
19061
|
// src/commands/template.ts
|
|
18212
|
-
function
|
|
19062
|
+
function stripAnsi3(s) {
|
|
18213
19063
|
return s.replace(/\x1B\[[0-9;]*m/g, "");
|
|
18214
19064
|
}
|
|
18215
19065
|
function hr3(w = 72) {
|
|
18216
|
-
return
|
|
19066
|
+
return pc31.dim("\u2500".repeat(w));
|
|
18217
19067
|
}
|
|
18218
|
-
function
|
|
19068
|
+
function truncate3(s, max) {
|
|
18219
19069
|
return s.length <= max ? s : s.slice(0, max - 1) + "\u2026";
|
|
18220
19070
|
}
|
|
18221
19071
|
function box2(lines) {
|
|
18222
19072
|
const padded = lines.map((l) => " " + l);
|
|
18223
|
-
const width = Math.max(...padded.map((l) =>
|
|
18224
|
-
const top =
|
|
18225
|
-
const bottom =
|
|
18226
|
-
const body = padded.map((l) =>
|
|
19073
|
+
const width = Math.max(...padded.map((l) => stripAnsi3(l).length)) + 4;
|
|
19074
|
+
const top = pc31.dim("\u250C" + "\u2500".repeat(width) + "\u2510");
|
|
19075
|
+
const bottom = pc31.dim("\u2514" + "\u2500".repeat(width) + "\u2518");
|
|
19076
|
+
const body = padded.map((l) => pc31.dim("\u2502") + l + " ".repeat(width - stripAnsi3(l).length) + pc31.dim("\u2502"));
|
|
18227
19077
|
return [top, ...body, bottom].join("\n");
|
|
18228
19078
|
}
|
|
18229
19079
|
function badge(a) {
|
|
18230
|
-
if (a.type === "ad-format") return
|
|
19080
|
+
if (a.type === "ad-format") return pc31.cyan("\u{1F3AC} Ad Format");
|
|
18231
19081
|
if (a.type === "scene-module") {
|
|
18232
|
-
if (a.subtype === "hook") return
|
|
18233
|
-
if (a.subtype === "body") return
|
|
18234
|
-
if (a.subtype === "cta") return
|
|
19082
|
+
if (a.subtype === "hook") return pc31.yellow("\u{1FA9D} Hook");
|
|
19083
|
+
if (a.subtype === "body") return pc31.blue("\u{1F9E9} Body");
|
|
19084
|
+
if (a.subtype === "cta") return pc31.green("\u{1F3AF} CTA");
|
|
18235
19085
|
}
|
|
18236
|
-
return
|
|
19086
|
+
return pc31.magenta("\u{1F9E9} Module");
|
|
18237
19087
|
}
|
|
18238
19088
|
function printCard(a) {
|
|
18239
|
-
const compat = a.compatibleFormats.length ?
|
|
19089
|
+
const compat = a.compatibleFormats.length ? pc31.dim("Works with: ") + a.compatibleFormats.map((f) => pc31.cyan(f)).join(", ") : pc31.dim("Works with: any format");
|
|
18240
19090
|
const rows = [
|
|
18241
|
-
|
|
18242
|
-
`${badge(a)} ${
|
|
19091
|
+
pc31.bold(a.name),
|
|
19092
|
+
`${badge(a)} ${pc31.dim(a.id)}`,
|
|
18243
19093
|
"",
|
|
18244
|
-
|
|
19094
|
+
truncate3(a.category, 62),
|
|
18245
19095
|
"",
|
|
18246
19096
|
compat
|
|
18247
19097
|
];
|
|
18248
19098
|
if (a.type === "ad-format" && a.scenes != null) {
|
|
18249
|
-
rows.push(
|
|
19099
|
+
rows.push(pc31.dim("Scenes: ") + a.scenes + (a.hookVariations ? pc31.dim(" \xB7 Hook variations: ") + a.hookVariations : ""));
|
|
18250
19100
|
}
|
|
18251
19101
|
console.log("");
|
|
18252
19102
|
console.log(box2(rows));
|
|
@@ -18254,32 +19104,32 @@ function printCard(a) {
|
|
|
18254
19104
|
function printSummary2(filter) {
|
|
18255
19105
|
const artifacts = listArtifacts(filter);
|
|
18256
19106
|
if (!artifacts.length) {
|
|
18257
|
-
console.log(
|
|
19107
|
+
console.log(pc31.yellow("No templates matched. Try: growthub template list"));
|
|
18258
19108
|
return;
|
|
18259
19109
|
}
|
|
18260
19110
|
const stats = getCatalogStats();
|
|
18261
19111
|
const groups = groupArtifacts(artifacts);
|
|
18262
19112
|
console.log("");
|
|
18263
|
-
console.log(
|
|
18264
|
-
console.log(
|
|
19113
|
+
console.log(pc31.bold("Growthub Shared Template Library") + pc31.dim(` ${artifacts.length} of ${stats.total} artifacts`));
|
|
19114
|
+
console.log(pc31.dim(" " + Object.entries(stats.byFamily).map(([f, n]) => `${f} (${n})`).join(" \xB7 ")));
|
|
18265
19115
|
console.log(hr3());
|
|
18266
19116
|
for (const g of groups) {
|
|
18267
19117
|
console.log(`
|
|
18268
|
-
${
|
|
18269
|
-
console.log(
|
|
19118
|
+
${pc31.bold(g.label)} ${pc31.dim("(" + g.count + ")")}`);
|
|
19119
|
+
console.log(pc31.dim(" " + g.description));
|
|
18270
19120
|
console.log("");
|
|
18271
19121
|
for (const a of g.artifacts) {
|
|
18272
|
-
const compat = a.compatibleFormats.length ?
|
|
18273
|
-
console.log(` ${
|
|
18274
|
-
console.log(` ${
|
|
19122
|
+
const compat = a.compatibleFormats.length ? pc31.dim(" \xB7 " + a.compatibleFormats.join(", ")) : "";
|
|
19123
|
+
console.log(` ${pc31.cyan(pc31.bold(a.name))}${compat}`);
|
|
19124
|
+
console.log(` ${pc31.dim("growthub template get " + a.slug)}`);
|
|
18275
19125
|
console.log("");
|
|
18276
19126
|
}
|
|
18277
19127
|
}
|
|
18278
19128
|
console.log(hr3());
|
|
18279
|
-
console.log(
|
|
18280
|
-
console.log(
|
|
18281
|
-
console.log(
|
|
18282
|
-
console.log(
|
|
19129
|
+
console.log(pc31.dim(" growthub template get <slug>"));
|
|
19130
|
+
console.log(pc31.dim(" growthub template list --type ad-formats"));
|
|
19131
|
+
console.log(pc31.dim(" growthub template list --type scene-modules --subtype hooks"));
|
|
19132
|
+
console.log(pc31.dim(" growthub template (interactive picker)"));
|
|
18283
19133
|
console.log("");
|
|
18284
19134
|
}
|
|
18285
19135
|
var TEMPLATE_FAMILY_META = {
|
|
@@ -18305,7 +19155,7 @@ var TEMPLATE_FAMILY_META = {
|
|
|
18305
19155
|
}
|
|
18306
19156
|
};
|
|
18307
19157
|
async function runTemplatePicker(opts) {
|
|
18308
|
-
p20.intro(
|
|
19158
|
+
p20.intro(pc31.bold("Growthub Shared Template Library"));
|
|
18309
19159
|
let artifacts;
|
|
18310
19160
|
try {
|
|
18311
19161
|
artifacts = listArtifacts();
|
|
@@ -18357,8 +19207,8 @@ async function runTemplatePicker(opts) {
|
|
|
18357
19207
|
message: `Select from: ${group.label}`,
|
|
18358
19208
|
options: group.artifacts.map((a) => ({
|
|
18359
19209
|
value: a.id,
|
|
18360
|
-
label:
|
|
18361
|
-
hint:
|
|
19210
|
+
label: pc31.bold(a.name),
|
|
19211
|
+
hint: truncate3(a.category, 52)
|
|
18362
19212
|
}))
|
|
18363
19213
|
});
|
|
18364
19214
|
if (p20.isCancel(artifactChoice)) {
|
|
@@ -18382,7 +19232,7 @@ async function runTemplatePicker(opts) {
|
|
|
18382
19232
|
}
|
|
18383
19233
|
if (action === "slug") {
|
|
18384
19234
|
console.log(selected.slug);
|
|
18385
|
-
p20.outro(
|
|
19235
|
+
p20.outro(pc31.dim("Use with: growthub template get " + selected.slug));
|
|
18386
19236
|
return "done";
|
|
18387
19237
|
}
|
|
18388
19238
|
if (action === "print") {
|
|
@@ -18390,7 +19240,7 @@ async function runTemplatePicker(opts) {
|
|
|
18390
19240
|
console.log("\n" + hr3());
|
|
18391
19241
|
console.log(r.content);
|
|
18392
19242
|
console.log(hr3());
|
|
18393
|
-
p20.outro(
|
|
19243
|
+
p20.outro(pc31.dim("Source: " + r.absolutePath));
|
|
18394
19244
|
return "done";
|
|
18395
19245
|
}
|
|
18396
19246
|
if (action === "copy") {
|
|
@@ -18405,7 +19255,7 @@ async function runTemplatePicker(opts) {
|
|
|
18405
19255
|
}
|
|
18406
19256
|
const destDir = path35.resolve(destInput.replace(/^~/, process.env["HOME"] ?? ""));
|
|
18407
19257
|
const destPath = copyArtifact(selected.id, destDir);
|
|
18408
|
-
p20.outro(
|
|
19258
|
+
p20.outro(pc31.green("Copied \u2192 ") + destPath);
|
|
18409
19259
|
return "done";
|
|
18410
19260
|
}
|
|
18411
19261
|
return "done";
|
|
@@ -18432,7 +19282,7 @@ Any agent or kit resolves them by slug.
|
|
|
18432
19282
|
if (opts.type) {
|
|
18433
19283
|
const t = opts.type.replace(/s$/, "");
|
|
18434
19284
|
if (t !== "ad-format" && t !== "scene-module") {
|
|
18435
|
-
console.error(
|
|
19285
|
+
console.error(pc31.red(`Unknown --type '${opts.type}'.`) + pc31.dim(" Valid: ad-formats, scene-modules"));
|
|
18436
19286
|
process.exitCode = 1;
|
|
18437
19287
|
return;
|
|
18438
19288
|
}
|
|
@@ -18441,7 +19291,7 @@ Any agent or kit resolves them by slug.
|
|
|
18441
19291
|
if (opts.subtype) {
|
|
18442
19292
|
const sub = opts.subtype.replace(/s$/, "");
|
|
18443
19293
|
if (!["hook", "body", "cta"].includes(sub)) {
|
|
18444
|
-
console.error(
|
|
19294
|
+
console.error(pc31.red(`Unknown --subtype '${opts.subtype}'.`) + pc31.dim(" Valid: hooks, body, cta"));
|
|
18445
19295
|
process.exitCode = 1;
|
|
18446
19296
|
return;
|
|
18447
19297
|
}
|
|
@@ -18457,18 +19307,18 @@ Any agent or kit resolves them by slug.
|
|
|
18457
19307
|
cmd.command("get").description("Print or copy a template \u2014 fuzzy slug resolution").argument("<slug>", "Artifact slug (e.g. villain-animation, meme-overlay)").option("--out <path>", "Copy to this directory").option("--json", "Artifact metadata + content as JSON").action((slug, opts) => {
|
|
18458
19308
|
const artifact = resolveSlug(slug);
|
|
18459
19309
|
if (!artifact) {
|
|
18460
|
-
console.error(
|
|
19310
|
+
console.error(pc31.red(`Unknown template '${slug}'.`) + pc31.dim(" Run `growthub template list` to browse."));
|
|
18461
19311
|
process.exitCode = 1;
|
|
18462
19312
|
return;
|
|
18463
19313
|
}
|
|
18464
19314
|
if (artifact.id !== slug && artifact.slug !== slug) {
|
|
18465
|
-
console.error(
|
|
19315
|
+
console.error(pc31.dim(`Resolved '${slug}' \u2192 ${artifact.slug}`));
|
|
18466
19316
|
}
|
|
18467
19317
|
let resolved;
|
|
18468
19318
|
try {
|
|
18469
19319
|
resolved = getArtifact(artifact.id);
|
|
18470
19320
|
} catch (err) {
|
|
18471
|
-
console.error(
|
|
19321
|
+
console.error(pc31.red(err.message));
|
|
18472
19322
|
process.exitCode = 1;
|
|
18473
19323
|
return;
|
|
18474
19324
|
}
|
|
@@ -18480,9 +19330,9 @@ Any agent or kit resolves them by slug.
|
|
|
18480
19330
|
const destDir = path35.resolve(opts.out.replace(/^~/, process.env["HOME"] ?? ""));
|
|
18481
19331
|
try {
|
|
18482
19332
|
const dest = copyArtifact(artifact.id, destDir);
|
|
18483
|
-
console.log(
|
|
19333
|
+
console.log(pc31.green("Copied \u2192 ") + dest);
|
|
18484
19334
|
} catch (err) {
|
|
18485
|
-
console.error(
|
|
19335
|
+
console.error(pc31.red(err.message));
|
|
18486
19336
|
process.exitCode = 1;
|
|
18487
19337
|
}
|
|
18488
19338
|
return;
|
|
@@ -18491,14 +19341,14 @@ Any agent or kit resolves them by slug.
|
|
|
18491
19341
|
console.log(hr3());
|
|
18492
19342
|
console.log(resolved.content);
|
|
18493
19343
|
console.log(hr3());
|
|
18494
|
-
console.log(
|
|
19344
|
+
console.log(pc31.dim("Source: " + resolved.absolutePath));
|
|
18495
19345
|
console.log("");
|
|
18496
19346
|
});
|
|
18497
19347
|
}
|
|
18498
19348
|
|
|
18499
19349
|
// src/commands/capability.ts
|
|
18500
19350
|
import * as p21 from "@clack/prompts";
|
|
18501
|
-
import
|
|
19351
|
+
import pc32 from "picocolors";
|
|
18502
19352
|
|
|
18503
19353
|
// src/runtime/hosted-execution-client/index.ts
|
|
18504
19354
|
init_http();
|
|
@@ -18877,15 +19727,15 @@ function summarizeExecution(executionLog) {
|
|
|
18877
19727
|
async function toHostedExecutionError(response) {
|
|
18878
19728
|
let message = `Request failed with status ${response.status}`;
|
|
18879
19729
|
try {
|
|
18880
|
-
const
|
|
18881
|
-
if (
|
|
18882
|
-
const parsed = JSON.parse(
|
|
19730
|
+
const text65 = await response.text();
|
|
19731
|
+
if (text65.trim()) {
|
|
19732
|
+
const parsed = JSON.parse(text65);
|
|
18883
19733
|
if (typeof parsed.error === "string" && parsed.error.trim()) {
|
|
18884
19734
|
message = parsed.error;
|
|
18885
19735
|
} else if (typeof parsed.message === "string" && parsed.message.trim()) {
|
|
18886
19736
|
message = parsed.message;
|
|
18887
19737
|
} else {
|
|
18888
|
-
message =
|
|
19738
|
+
message = text65;
|
|
18889
19739
|
}
|
|
18890
19740
|
}
|
|
18891
19741
|
} catch {
|
|
@@ -19257,12 +20107,12 @@ function getWorkflowAccess() {
|
|
|
19257
20107
|
// src/commands/capability.ts
|
|
19258
20108
|
init_banner();
|
|
19259
20109
|
var FAMILY_CONFIG = {
|
|
19260
|
-
video: { color:
|
|
19261
|
-
image: { color:
|
|
19262
|
-
slides: { color:
|
|
19263
|
-
text: { color:
|
|
19264
|
-
data: { color:
|
|
19265
|
-
ops: { color:
|
|
20110
|
+
video: { color: pc32.magenta, emoji: "\u{1F3AC}", label: "Video" },
|
|
20111
|
+
image: { color: pc32.cyan, emoji: "\u{1F5BC}\uFE0F ", label: "Image" },
|
|
20112
|
+
slides: { color: pc32.yellow, emoji: "\u{1F4CA}", label: "Slides" },
|
|
20113
|
+
text: { color: pc32.green, emoji: "\u{1F4DD}", label: "Text" },
|
|
20114
|
+
data: { color: pc32.blue, emoji: "\u{1F4E6}", label: "Data" },
|
|
20115
|
+
ops: { color: pc32.red, emoji: "\u2699\uFE0F ", label: "Ops" }
|
|
19266
20116
|
};
|
|
19267
20117
|
function familyBadge(family) {
|
|
19268
20118
|
const cfg = FAMILY_CONFIG[family];
|
|
@@ -19270,25 +20120,25 @@ function familyBadge(family) {
|
|
|
19270
20120
|
return cfg.color(`${cfg.emoji} ${cfg.label}`);
|
|
19271
20121
|
}
|
|
19272
20122
|
function executionKindLabel(kind) {
|
|
19273
|
-
if (kind === "hosted-execute") return
|
|
19274
|
-
if (kind === "provider-assembly") return
|
|
19275
|
-
if (kind === "local-only") return
|
|
20123
|
+
if (kind === "hosted-execute") return pc32.cyan("hosted");
|
|
20124
|
+
if (kind === "provider-assembly") return pc32.yellow("provider");
|
|
20125
|
+
if (kind === "local-only") return pc32.green("local");
|
|
19276
20126
|
return kind;
|
|
19277
20127
|
}
|
|
19278
20128
|
function hr4(width = 72) {
|
|
19279
|
-
return
|
|
20129
|
+
return pc32.dim("\u2500".repeat(width));
|
|
19280
20130
|
}
|
|
19281
|
-
function
|
|
20131
|
+
function stripAnsi4(str) {
|
|
19282
20132
|
return str.replace(/\x1B\[[0-9;]*m/g, "");
|
|
19283
20133
|
}
|
|
19284
20134
|
function box3(lines) {
|
|
19285
20135
|
const padded = lines.map((l) => " " + l);
|
|
19286
|
-
const width = Math.max(...padded.map((l) =>
|
|
19287
|
-
const top =
|
|
19288
|
-
const bottom =
|
|
20136
|
+
const width = Math.max(...padded.map((l) => stripAnsi4(l).length)) + 4;
|
|
20137
|
+
const top = pc32.dim("\u250C" + "\u2500".repeat(width) + "\u2510");
|
|
20138
|
+
const bottom = pc32.dim("\u2514" + "\u2500".repeat(width) + "\u2518");
|
|
19289
20139
|
const body = padded.map((l) => {
|
|
19290
|
-
const
|
|
19291
|
-
return
|
|
20140
|
+
const pad2 = width - stripAnsi4(l).length;
|
|
20141
|
+
return pc32.dim("\u2502") + l + " ".repeat(pad2) + pc32.dim("\u2502");
|
|
19292
20142
|
});
|
|
19293
20143
|
return [top, ...body, bottom].join("\n");
|
|
19294
20144
|
}
|
|
@@ -19301,48 +20151,48 @@ function printGroupedCapabilities(nodes) {
|
|
|
19301
20151
|
const totalFamilies = families.length;
|
|
19302
20152
|
console.log("");
|
|
19303
20153
|
console.log(
|
|
19304
|
-
|
|
20154
|
+
pc32.bold("CMS Capability Registry") + pc32.dim(` ${nodes.length} capabilit${nodes.length !== 1 ? "ies" : "y"} \xB7 ${totalFamilies} ${totalFamilies !== 1 ? "families" : "family"}`)
|
|
19305
20155
|
);
|
|
19306
20156
|
console.log(hr4());
|
|
19307
20157
|
for (const family of families) {
|
|
19308
20158
|
const groupNodes = byFamily[family];
|
|
19309
20159
|
const header = familyBadge(family);
|
|
19310
20160
|
console.log(`
|
|
19311
|
-
${header} ${
|
|
20161
|
+
${header} ${pc32.dim("(" + groupNodes.length + ")")}`);
|
|
19312
20162
|
for (const node of groupNodes) {
|
|
19313
|
-
const enabledTag = node.enabled ?
|
|
19314
|
-
console.log(` ${
|
|
19315
|
-
console.log(` ${
|
|
20163
|
+
const enabledTag = node.enabled ? pc32.green("enabled") : pc32.red("disabled");
|
|
20164
|
+
console.log(` ${pc32.bold(node.slug)} ${pc32.dim(node.displayName)} ${enabledTag}`);
|
|
20165
|
+
console.log(` ${pc32.dim("Execution:")} ${executionKindLabel(node.executionKind)} ${pc32.dim("Outputs:")} ${pc32.dim(node.outputTypes.join(", "))}`);
|
|
19316
20166
|
if (node.description) {
|
|
19317
|
-
console.log(` ${
|
|
20167
|
+
console.log(` ${pc32.dim(node.description)}`);
|
|
19318
20168
|
}
|
|
19319
20169
|
console.log("");
|
|
19320
20170
|
}
|
|
19321
20171
|
}
|
|
19322
20172
|
console.log(hr4());
|
|
19323
|
-
console.log(
|
|
20173
|
+
console.log(pc32.dim(" growthub capability inspect <slug> \xB7 growthub capability resolve"));
|
|
19324
20174
|
console.log("");
|
|
19325
20175
|
}
|
|
19326
20176
|
function printCapabilityCard(node) {
|
|
19327
20177
|
const iconPrefix = node.icon ? `${node.icon} ` : "";
|
|
19328
20178
|
const lines = [
|
|
19329
|
-
`${iconPrefix}${
|
|
19330
|
-
`${familyBadge(node.family)} ${node.enabled ?
|
|
20179
|
+
`${iconPrefix}${pc32.bold(node.displayName)} ${pc32.dim(node.slug)}`,
|
|
20180
|
+
`${familyBadge(node.family)} ${node.enabled ? pc32.green("enabled") : pc32.red("disabled")}`,
|
|
19331
20181
|
"",
|
|
19332
|
-
`${
|
|
19333
|
-
`${
|
|
19334
|
-
`${
|
|
19335
|
-
`${
|
|
19336
|
-
`${
|
|
19337
|
-
`${
|
|
19338
|
-
`${
|
|
20182
|
+
`${pc32.dim("Category:")} ${node.category}`,
|
|
20183
|
+
`${pc32.dim("Node Type:")} ${node.nodeType}`,
|
|
20184
|
+
`${pc32.dim("Execution Kind:")} ${executionKindLabel(node.executionKind)}`,
|
|
20185
|
+
`${pc32.dim("Execution Strategy:")} ${node.executionBinding.strategy}`,
|
|
20186
|
+
`${pc32.dim("Tool Name:")} ${node.executionTokens.tool_name}`,
|
|
20187
|
+
`${pc32.dim("Output Types:")} ${node.outputTypes.join(", ")}`,
|
|
20188
|
+
`${pc32.dim("Required Bindings:")} ${node.requiredBindings.length > 0 ? node.requiredBindings.join(", ") : pc32.dim("(none)")}`
|
|
19339
20189
|
];
|
|
19340
20190
|
if (node.description) {
|
|
19341
|
-
lines.push("",
|
|
20191
|
+
lines.push("", pc32.dim(node.description));
|
|
19342
20192
|
}
|
|
19343
20193
|
const inputKeys = Object.keys(node.executionTokens.input_template);
|
|
19344
20194
|
if (inputKeys.length > 0) {
|
|
19345
|
-
lines.push("", `${
|
|
20195
|
+
lines.push("", `${pc32.dim("Input fields:")} ${inputKeys.join(", ")}`);
|
|
19346
20196
|
}
|
|
19347
20197
|
console.log("");
|
|
19348
20198
|
console.log(box3(lines));
|
|
@@ -19350,7 +20200,7 @@ function printCapabilityCard(node) {
|
|
|
19350
20200
|
}
|
|
19351
20201
|
async function runCapabilityPicker(opts) {
|
|
19352
20202
|
printPaperclipCliBanner();
|
|
19353
|
-
p21.intro(
|
|
20203
|
+
p21.intro(pc32.bold("CMS Capability Registry"));
|
|
19354
20204
|
const access = getWorkflowAccess();
|
|
19355
20205
|
if (access.state !== "ready") {
|
|
19356
20206
|
p21.note(
|
|
@@ -19401,7 +20251,7 @@ async function runCapabilityPicker(opts) {
|
|
|
19401
20251
|
options: [
|
|
19402
20252
|
...result.nodes.map((n) => ({
|
|
19403
20253
|
value: n.slug,
|
|
19404
|
-
label: `${familyBadge(n.family)} ` +
|
|
20254
|
+
label: `${familyBadge(n.family)} ` + pc32.bold(n.displayName) + " " + pc32.dim(n.slug),
|
|
19405
20255
|
hint: n.description ? n.description.slice(0, 55) : void 0
|
|
19406
20256
|
})),
|
|
19407
20257
|
{ value: "__back_to_family", label: "\u2190 Back to family filter" }
|
|
@@ -19432,13 +20282,13 @@ async function runCapabilityPicker(opts) {
|
|
|
19432
20282
|
const resolver = createMachineCapabilityResolver();
|
|
19433
20283
|
const binding = await resolver.resolveCapability(selected.slug);
|
|
19434
20284
|
if (binding) {
|
|
19435
|
-
const statusColor3 = binding.allowed ?
|
|
20285
|
+
const statusColor3 = binding.allowed ? pc32.green : pc32.red;
|
|
19436
20286
|
console.log("");
|
|
19437
20287
|
console.log(box3([
|
|
19438
|
-
`${
|
|
19439
|
-
`${
|
|
19440
|
-
`${
|
|
19441
|
-
...binding.machineConnectionId ? [`${
|
|
20288
|
+
`${pc32.bold("Machine Binding:")} ${selected.slug}`,
|
|
20289
|
+
`${pc32.dim("Allowed:")} ${statusColor3(String(binding.allowed))}`,
|
|
20290
|
+
`${pc32.dim("Reason:")} ${binding.reason ?? "\u2014"}`,
|
|
20291
|
+
...binding.machineConnectionId ? [`${pc32.dim("Connection:")} ${binding.machineConnectionId}`] : []
|
|
19442
20292
|
]));
|
|
19443
20293
|
console.log("");
|
|
19444
20294
|
}
|
|
@@ -19465,7 +20315,7 @@ Examples:
|
|
|
19465
20315
|
cap.command("list").description("List all CMS-backed runtime node capabilities").option("--family <family>", "Filter by family (video, image, slides, text, data, ops)").option("--json", "Output raw JSON for scripting").action(async (opts) => {
|
|
19466
20316
|
const access = getWorkflowAccess();
|
|
19467
20317
|
if (access.state !== "ready") {
|
|
19468
|
-
console.error(
|
|
20318
|
+
console.error(pc32.red(`${access.reason}.`));
|
|
19469
20319
|
process.exitCode = 1;
|
|
19470
20320
|
return;
|
|
19471
20321
|
}
|
|
@@ -19478,23 +20328,23 @@ Examples:
|
|
|
19478
20328
|
return;
|
|
19479
20329
|
}
|
|
19480
20330
|
if (nodes.length === 0) {
|
|
19481
|
-
console.error(
|
|
19482
|
-
console.error(
|
|
20331
|
+
console.error(pc32.yellow("No capabilities found" + (opts.family ? ` for family: ${opts.family}` : "") + "."));
|
|
20332
|
+
console.error(pc32.dim("Valid families: " + CAPABILITY_FAMILIES.join(", ")));
|
|
19483
20333
|
process.exitCode = 1;
|
|
19484
20334
|
return;
|
|
19485
20335
|
}
|
|
19486
20336
|
printGroupedCapabilities(nodes);
|
|
19487
|
-
console.log(
|
|
20337
|
+
console.log(pc32.dim(` Source: ${meta.source} \xB7 Fetched: ${meta.fetchedAt}`));
|
|
19488
20338
|
console.log("");
|
|
19489
20339
|
} catch (err) {
|
|
19490
|
-
console.error(
|
|
20340
|
+
console.error(pc32.red("Failed to list capabilities: " + err.message));
|
|
19491
20341
|
process.exitCode = 1;
|
|
19492
20342
|
}
|
|
19493
20343
|
});
|
|
19494
20344
|
cap.command("inspect").description("Inspect a specific CMS capability node").argument("<slug>", "Capability slug (e.g. 'video-gen', 'text-gen')").option("--json", "Output raw JSON").action(async (slug, opts) => {
|
|
19495
20345
|
const access = getWorkflowAccess();
|
|
19496
20346
|
if (access.state !== "ready") {
|
|
19497
|
-
console.error(
|
|
20347
|
+
console.error(pc32.red(`${access.reason}.`));
|
|
19498
20348
|
process.exitCode = 1;
|
|
19499
20349
|
return;
|
|
19500
20350
|
}
|
|
@@ -19502,7 +20352,7 @@ Examples:
|
|
|
19502
20352
|
try {
|
|
19503
20353
|
const node = await registry.getCapability(slug);
|
|
19504
20354
|
if (!node) {
|
|
19505
|
-
console.error(
|
|
20355
|
+
console.error(pc32.red(`Unknown capability: "${slug}".`) + pc32.dim(" Run `growthub capability list` to browse."));
|
|
19506
20356
|
process.exitCode = 1;
|
|
19507
20357
|
return;
|
|
19508
20358
|
}
|
|
@@ -19512,14 +20362,14 @@ Examples:
|
|
|
19512
20362
|
}
|
|
19513
20363
|
printCapabilityCard(node);
|
|
19514
20364
|
} catch (err) {
|
|
19515
|
-
console.error(
|
|
20365
|
+
console.error(pc32.red("Failed to inspect capability: " + err.message));
|
|
19516
20366
|
process.exitCode = 1;
|
|
19517
20367
|
}
|
|
19518
20368
|
});
|
|
19519
20369
|
cap.command("resolve").description("Resolve machine-scoped capability bindings for all capabilities").option("--json", "Output raw JSON").action(async (opts) => {
|
|
19520
20370
|
const access = getWorkflowAccess();
|
|
19521
20371
|
if (access.state !== "ready") {
|
|
19522
|
-
console.error(
|
|
20372
|
+
console.error(pc32.red(`${access.reason}.`));
|
|
19523
20373
|
process.exitCode = 1;
|
|
19524
20374
|
return;
|
|
19525
20375
|
}
|
|
@@ -19531,28 +20381,28 @@ Examples:
|
|
|
19531
20381
|
return;
|
|
19532
20382
|
}
|
|
19533
20383
|
console.log("");
|
|
19534
|
-
console.log(
|
|
20384
|
+
console.log(pc32.bold("Machine Capability Resolution"));
|
|
19535
20385
|
console.log(hr4());
|
|
19536
|
-
console.log(` ${
|
|
19537
|
-
console.log(` ${
|
|
19538
|
-
console.log(` ${
|
|
20386
|
+
console.log(` ${pc32.dim("Hostname:")} ${result.machineContext.hostname}`);
|
|
20387
|
+
console.log(` ${pc32.dim("Instance:")} ${result.machineContext.instanceId}`);
|
|
20388
|
+
console.log(` ${pc32.dim("Session:")} ${result.machineContext.hasActiveSession ? pc32.green("active") : pc32.red("none")}`);
|
|
19539
20389
|
if (result.machineContext.machineLabel) {
|
|
19540
|
-
console.log(` ${
|
|
20390
|
+
console.log(` ${pc32.dim("Machine:")} ${result.machineContext.machineLabel}`);
|
|
19541
20391
|
}
|
|
19542
|
-
console.log(` ${
|
|
20392
|
+
console.log(` ${pc32.dim("Entitlements:")} ${result.entitlements.length > 0 ? result.entitlements.join(", ") : pc32.dim("(none)")}`);
|
|
19543
20393
|
console.log(hr4());
|
|
19544
20394
|
for (const binding of result.bindings) {
|
|
19545
|
-
const statusColor3 = binding.allowed ?
|
|
20395
|
+
const statusColor3 = binding.allowed ? pc32.green : pc32.red;
|
|
19546
20396
|
const statusIcon = binding.allowed ? "\u2713" : "\u2717";
|
|
19547
20397
|
console.log(
|
|
19548
|
-
` ${statusColor3(statusIcon)} ${
|
|
20398
|
+
` ${statusColor3(statusIcon)} ${pc32.bold(binding.capabilitySlug)} ${pc32.dim(binding.reason ?? "")}`
|
|
19549
20399
|
);
|
|
19550
20400
|
}
|
|
19551
20401
|
console.log("");
|
|
19552
|
-
console.log(
|
|
20402
|
+
console.log(pc32.dim(` Resolved at: ${result.resolvedAt}`));
|
|
19553
20403
|
console.log("");
|
|
19554
20404
|
} catch (err) {
|
|
19555
|
-
console.error(
|
|
20405
|
+
console.error(pc32.red("Failed to resolve capabilities: " + err.message));
|
|
19556
20406
|
process.exitCode = 1;
|
|
19557
20407
|
}
|
|
19558
20408
|
});
|
|
@@ -19564,7 +20414,7 @@ init_hosted_client();
|
|
|
19564
20414
|
import fs29 from "node:fs";
|
|
19565
20415
|
import path38 from "node:path";
|
|
19566
20416
|
import * as p22 from "@clack/prompts";
|
|
19567
|
-
import
|
|
20417
|
+
import pc34 from "picocolors";
|
|
19568
20418
|
|
|
19569
20419
|
// src/runtime/dynamic-registry-pipeline/index.ts
|
|
19570
20420
|
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
@@ -19953,10 +20803,10 @@ function compileToHostedWorkflowConfig(pipeline, opts) {
|
|
|
19953
20803
|
}
|
|
19954
20804
|
|
|
19955
20805
|
// src/runtime/cms-node-contracts/presenter.ts
|
|
19956
|
-
import
|
|
20806
|
+
import pc33 from "picocolors";
|
|
19957
20807
|
function renderInputLine(input) {
|
|
19958
|
-
const required = input.required ?
|
|
19959
|
-
return `${
|
|
20808
|
+
const required = input.required ? pc33.red("required") : pc33.green("optional");
|
|
20809
|
+
return `${pc33.dim("\xB7")} ${input.label} ${pc33.dim(`(${input.type})`)} ${required}`;
|
|
19960
20810
|
}
|
|
19961
20811
|
function countNodeAssets(bindings) {
|
|
19962
20812
|
let count = 0;
|
|
@@ -19969,20 +20819,20 @@ function countNodeAssets(bindings) {
|
|
|
19969
20819
|
}
|
|
19970
20820
|
function renderContractCard(contract) {
|
|
19971
20821
|
const lines = [
|
|
19972
|
-
`${
|
|
19973
|
-
`${
|
|
19974
|
-
`${
|
|
19975
|
-
`${
|
|
19976
|
-
`${
|
|
20822
|
+
`${pc33.bold(contract.displayName)} ${pc33.dim(contract.slug)}`,
|
|
20823
|
+
`${pc33.dim("Family:")} ${contract.family} ${pc33.dim("Execution:")} ${contract.executionStrategy}`,
|
|
20824
|
+
`${pc33.dim("Kind:")} ${contract.executionKind} ${pc33.dim("Node Type:")} ${contract.nodeType}`,
|
|
20825
|
+
`${pc33.dim("Bindings:")} ${contract.requiredBindings.length > 0 ? contract.requiredBindings.join(", ") : "none"}`,
|
|
20826
|
+
`${pc33.dim("Outputs:")} ${contract.outputTypes.length > 0 ? contract.outputTypes.join(", ") : "none"}`
|
|
19977
20827
|
];
|
|
19978
20828
|
if (contract.inputs.length > 0) {
|
|
19979
|
-
lines.push("",
|
|
20829
|
+
lines.push("", pc33.bold("Input Contract"));
|
|
19980
20830
|
lines.push(...contract.inputs.map(renderInputLine));
|
|
19981
20831
|
}
|
|
19982
20832
|
if (contract.outputs.length > 0) {
|
|
19983
|
-
lines.push("",
|
|
20833
|
+
lines.push("", pc33.bold("Output Contract"));
|
|
19984
20834
|
lines.push(
|
|
19985
|
-
...contract.outputs.map((output) => `${
|
|
20835
|
+
...contract.outputs.map((output) => `${pc33.dim("\xB7")} ${output.key} ${pc33.dim(`(${output.type})`)}`)
|
|
19986
20836
|
);
|
|
19987
20837
|
}
|
|
19988
20838
|
return lines;
|
|
@@ -20039,33 +20889,33 @@ function buildPreExecutionSummary(input) {
|
|
|
20039
20889
|
}
|
|
20040
20890
|
function renderPreExecutionSummary(summary) {
|
|
20041
20891
|
const lines = [
|
|
20042
|
-
`${
|
|
20043
|
-
`${
|
|
20044
|
-
`${
|
|
20892
|
+
`${pc33.bold("Pre-Execution Contract Summary")} ${pc33.dim(summary.pipelineId)}`,
|
|
20893
|
+
`${pc33.dim("Mode:")} ${summary.executionMode} ${pc33.dim("Nodes:")} ${summary.nodeCount}`,
|
|
20894
|
+
`${pc33.dim("Compiled:")} ${summary.compiledConfig.nodes.length} nodes / ${summary.compiledConfig.edges.length} edges`,
|
|
20045
20895
|
""
|
|
20046
20896
|
];
|
|
20047
20897
|
for (const [index51, node] of summary.nodes.entries()) {
|
|
20048
|
-
const missing = node.requiredMissing.length > 0 ?
|
|
20898
|
+
const missing = node.requiredMissing.length > 0 ? pc33.red(`missing: ${node.requiredMissing.join(", ")}`) : pc33.green("ready");
|
|
20049
20899
|
const outputs = node.outputTypes.length > 0 ? node.outputTypes.join(", ") : "none";
|
|
20050
20900
|
lines.push(
|
|
20051
|
-
`${
|
|
20901
|
+
`${pc33.dim(`${index51 + 1}.`)} ${pc33.bold(node.slug)} ${pc33.dim(node.nodeId)} \xB7 bindings=${node.bindingCount} \xB7 assets=${node.assetCount} \xB7 outputs=${outputs} \xB7 ${missing}`
|
|
20052
20902
|
);
|
|
20053
20903
|
}
|
|
20054
20904
|
if (summary.warnings.length > 0) {
|
|
20055
|
-
lines.push("",
|
|
20056
|
-
lines.push(...summary.warnings.map((warning) => `${
|
|
20905
|
+
lines.push("", pc33.yellow("Warnings"));
|
|
20906
|
+
lines.push(...summary.warnings.map((warning) => `${pc33.dim("\xB7")} ${warning}`));
|
|
20057
20907
|
}
|
|
20058
20908
|
return lines;
|
|
20059
20909
|
}
|
|
20060
20910
|
function renderPreSaveReview(input) {
|
|
20061
20911
|
const lines = [
|
|
20062
|
-
`${
|
|
20063
|
-
`${
|
|
20064
|
-
`${
|
|
20065
|
-
`${
|
|
20912
|
+
`${pc33.bold("Pre-Save Workflow Review")} ${pc33.dim(input.workflowName)}`,
|
|
20913
|
+
`${pc33.dim("Pipeline:")} ${input.summary.pipelineId}`,
|
|
20914
|
+
`${pc33.dim("Mode:")} ${input.summary.executionMode}`,
|
|
20915
|
+
`${pc33.dim("Compiled:")} ${input.summary.compiledConfig.nodes.length} nodes / ${input.summary.compiledConfig.edges.length} edges`
|
|
20066
20916
|
];
|
|
20067
20917
|
if (input.summary.warnings.length > 0) {
|
|
20068
|
-
lines.push("",
|
|
20918
|
+
lines.push("", pc33.yellow(`Warnings: ${input.summary.warnings.length}`));
|
|
20069
20919
|
}
|
|
20070
20920
|
return lines;
|
|
20071
20921
|
}
|
|
@@ -20276,9 +21126,9 @@ function createNativeIntelligenceBackend(config) {
|
|
|
20276
21126
|
throw lastError ?? new NativeIntelligenceBackendError(502, "Model backend returned no response.");
|
|
20277
21127
|
}
|
|
20278
21128
|
const latencyMs = Date.now() - startMs;
|
|
20279
|
-
const
|
|
21129
|
+
const text65 = extractCompletionText(result);
|
|
20280
21130
|
return {
|
|
20281
|
-
text:
|
|
21131
|
+
text: text65,
|
|
20282
21132
|
usage: result.usage ? {
|
|
20283
21133
|
promptTokens: result.usage.prompt_tokens ?? 0,
|
|
20284
21134
|
completionTokens: result.usage.completion_tokens ?? 0,
|
|
@@ -20533,9 +21383,9 @@ function buildSummarizerPrompt(input) {
|
|
|
20533
21383
|
}
|
|
20534
21384
|
return sections.join("\n");
|
|
20535
21385
|
}
|
|
20536
|
-
function parseJsonSafe(
|
|
21386
|
+
function parseJsonSafe(text65) {
|
|
20537
21387
|
try {
|
|
20538
|
-
const trimmed =
|
|
21388
|
+
const trimmed = text65.trim();
|
|
20539
21389
|
const jsonStart = trimmed.indexOf("{");
|
|
20540
21390
|
const jsonEnd = trimmed.lastIndexOf("}");
|
|
20541
21391
|
if (jsonStart >= 0 && jsonEnd > jsonStart) {
|
|
@@ -20787,9 +21637,9 @@ function validateAction(action) {
|
|
|
20787
21637
|
}
|
|
20788
21638
|
return "kept";
|
|
20789
21639
|
}
|
|
20790
|
-
function parseJsonSafe2(
|
|
21640
|
+
function parseJsonSafe2(text65) {
|
|
20791
21641
|
try {
|
|
20792
|
-
const trimmed =
|
|
21642
|
+
const trimmed = text65.trim();
|
|
20793
21643
|
const jsonStart = trimmed.indexOf("{");
|
|
20794
21644
|
const jsonEnd = trimmed.lastIndexOf("}");
|
|
20795
21645
|
if (jsonStart >= 0 && jsonEnd > jsonStart) {
|
|
@@ -21030,9 +21880,9 @@ function validateStrategy(strategy) {
|
|
|
21030
21880
|
}
|
|
21031
21881
|
return "synthesize-new";
|
|
21032
21882
|
}
|
|
21033
|
-
function parseJsonSafe3(
|
|
21883
|
+
function parseJsonSafe3(text65) {
|
|
21034
21884
|
try {
|
|
21035
|
-
const trimmed =
|
|
21885
|
+
const trimmed = text65.trim();
|
|
21036
21886
|
const jsonStart = trimmed.indexOf("{");
|
|
21037
21887
|
const jsonEnd = trimmed.lastIndexOf("}");
|
|
21038
21888
|
if (jsonStart >= 0 && jsonEnd > jsonStart) {
|
|
@@ -21278,9 +22128,9 @@ function validatePlanningResult(raw, input) {
|
|
|
21278
22128
|
warnings
|
|
21279
22129
|
};
|
|
21280
22130
|
}
|
|
21281
|
-
function parseJsonSafe4(
|
|
22131
|
+
function parseJsonSafe4(text65) {
|
|
21282
22132
|
try {
|
|
21283
|
-
const trimmed =
|
|
22133
|
+
const trimmed = text65.trim();
|
|
21284
22134
|
const jsonStart = trimmed.indexOf("{");
|
|
21285
22135
|
const jsonEnd = trimmed.lastIndexOf("}");
|
|
21286
22136
|
if (jsonStart >= 0 && jsonEnd > jsonStart) {
|
|
@@ -21350,25 +22200,25 @@ function createNativeIntelligenceProvider(configOverride) {
|
|
|
21350
22200
|
// src/commands/pipeline.ts
|
|
21351
22201
|
init_banner();
|
|
21352
22202
|
function hr5(width = 72) {
|
|
21353
|
-
return
|
|
22203
|
+
return pc34.dim("\u2500".repeat(width));
|
|
21354
22204
|
}
|
|
21355
|
-
function
|
|
22205
|
+
function stripAnsi5(str) {
|
|
21356
22206
|
return str.replace(/\x1B\[[0-9;]*m/g, "");
|
|
21357
22207
|
}
|
|
21358
22208
|
function box4(lines) {
|
|
21359
22209
|
const padded = lines.map((l) => " " + l);
|
|
21360
|
-
const width = Math.max(...padded.map((l) =>
|
|
21361
|
-
const top =
|
|
21362
|
-
const bottom =
|
|
22210
|
+
const width = Math.max(...padded.map((l) => stripAnsi5(l).length)) + 4;
|
|
22211
|
+
const top = pc34.dim("\u250C" + "\u2500".repeat(width) + "\u2510");
|
|
22212
|
+
const bottom = pc34.dim("\u2514" + "\u2500".repeat(width) + "\u2518");
|
|
21363
22213
|
const body = padded.map((l) => {
|
|
21364
|
-
const
|
|
21365
|
-
return
|
|
22214
|
+
const pad2 = width - stripAnsi5(l).length;
|
|
22215
|
+
return pc34.dim("\u2502") + l + " ".repeat(pad2) + pc34.dim("\u2502");
|
|
21366
22216
|
});
|
|
21367
22217
|
return [top, ...body, bottom].join("\n");
|
|
21368
22218
|
}
|
|
21369
22219
|
async function runPipelineAssembler(opts) {
|
|
21370
22220
|
printPaperclipCliBanner();
|
|
21371
|
-
p22.intro(
|
|
22221
|
+
p22.intro(pc34.bold("Dynamic Registry Pipeline Assembler"));
|
|
21372
22222
|
p22.note(
|
|
21373
22223
|
[
|
|
21374
22224
|
"Dynamic pipeline creation flow:",
|
|
@@ -21417,7 +22267,7 @@ async function runPipelineAssembler(opts) {
|
|
|
21417
22267
|
capabilities = result.nodes;
|
|
21418
22268
|
capabilitiesSpinner.stop(`Loaded ${capabilities.length} capabilities.`);
|
|
21419
22269
|
} catch (err) {
|
|
21420
|
-
capabilitiesSpinner.stop(
|
|
22270
|
+
capabilitiesSpinner.stop(pc34.red("Failed to load capabilities."));
|
|
21421
22271
|
p22.log.error("Failed to load capabilities: " + err.message);
|
|
21422
22272
|
return "done";
|
|
21423
22273
|
}
|
|
@@ -21459,7 +22309,7 @@ async function runPipelineAssembler(opts) {
|
|
|
21459
22309
|
options: [
|
|
21460
22310
|
...capabilities.map((c) => ({
|
|
21461
22311
|
value: c.slug,
|
|
21462
|
-
label: `${
|
|
22312
|
+
label: `${pc34.bold(c.displayName)} ${pc34.dim(c.slug)}`,
|
|
21463
22313
|
hint: `${c.family} \xB7 ${c.executionKind}`
|
|
21464
22314
|
})),
|
|
21465
22315
|
{ value: "__back", label: "\u2190 Back" }
|
|
@@ -21508,19 +22358,19 @@ async function runPipelineAssembler(opts) {
|
|
|
21508
22358
|
}
|
|
21509
22359
|
const normalizedBindings = normalizeNodeBindings(bindings, cap);
|
|
21510
22360
|
const nodeId = builder.addNode(capChoice, normalizedBindings.bindings, upstreamNodeIds);
|
|
21511
|
-
p22.log.success(`Added node ${
|
|
22361
|
+
p22.log.success(`Added node ${pc34.bold(cap.displayName)} (${pc34.dim(nodeId)})`);
|
|
21512
22362
|
continue;
|
|
21513
22363
|
}
|
|
21514
22364
|
if (action === "preview") {
|
|
21515
22365
|
const pipeline = builder.build();
|
|
21516
22366
|
console.log("");
|
|
21517
22367
|
console.log(box4([
|
|
21518
|
-
`${
|
|
21519
|
-
`${
|
|
22368
|
+
`${pc34.bold("Pipeline:")} ${pipeline.pipelineId}`,
|
|
22369
|
+
`${pc34.dim("Mode:")} ${pipeline.executionMode} ${pc34.dim("Nodes:")} ${pipeline.nodes.length}`,
|
|
21520
22370
|
"",
|
|
21521
22371
|
...pipeline.nodes.map((n, i) => {
|
|
21522
|
-
const upstream = n.upstreamNodeIds?.length ?
|
|
21523
|
-
return `${
|
|
22372
|
+
const upstream = n.upstreamNodeIds?.length ? pc34.dim(` \u2190 ${n.upstreamNodeIds.join(", ")}`) : "";
|
|
22373
|
+
return `${pc34.dim(String(i + 1) + ".")} ${pc34.bold(n.slug)} ${pc34.dim(n.id)}${upstream}`;
|
|
21524
22374
|
})
|
|
21525
22375
|
]));
|
|
21526
22376
|
console.log("");
|
|
@@ -21535,7 +22385,7 @@ async function runPipelineAssembler(opts) {
|
|
|
21535
22385
|
p22.log.error("Pipeline validation failed.");
|
|
21536
22386
|
}
|
|
21537
22387
|
for (const issue of result.issues) {
|
|
21538
|
-
const prefix = issue.severity === "error" ?
|
|
22388
|
+
const prefix = issue.severity === "error" ? pc34.red("ERROR") : pc34.yellow("WARN");
|
|
21539
22389
|
const nodeRef = issue.nodeId ? ` [${issue.nodeId}]` : "";
|
|
21540
22390
|
console.log(` ${prefix}${nodeRef}: ${issue.message}`);
|
|
21541
22391
|
}
|
|
@@ -21592,7 +22442,7 @@ async function runPipelineAssembler(opts) {
|
|
|
21592
22442
|
throw new Error("Hosted workflow save returned no workflow id.");
|
|
21593
22443
|
}
|
|
21594
22444
|
p22.log.success(
|
|
21595
|
-
`Saved to workflow registry as ${
|
|
22445
|
+
`Saved to workflow registry as ${pc34.bold(workflowName)} (${pc34.dim(saveResult.workflowId)} \xB7 v${saveResult.version}).`
|
|
21596
22446
|
);
|
|
21597
22447
|
} catch (err) {
|
|
21598
22448
|
if (err instanceof HostedEndpointUnavailableError) {
|
|
@@ -21608,7 +22458,7 @@ async function runPipelineAssembler(opts) {
|
|
|
21608
22458
|
if (!validation.valid) {
|
|
21609
22459
|
p22.log.error("Pipeline is not valid. Fix errors before executing.");
|
|
21610
22460
|
for (const issue of validation.issues.filter((i) => i.severity === "error")) {
|
|
21611
|
-
console.log(` ${
|
|
22461
|
+
console.log(` ${pc34.red("ERROR")}: ${issue.message}`);
|
|
21612
22462
|
}
|
|
21613
22463
|
continue;
|
|
21614
22464
|
}
|
|
@@ -21638,7 +22488,7 @@ async function runPipelineAssembler(opts) {
|
|
|
21638
22488
|
const executionClient = createHostedExecutionClient();
|
|
21639
22489
|
const pipeline2 = builder.build();
|
|
21640
22490
|
const pkg = await builder.package();
|
|
21641
|
-
p22.log.info(`Executing pipeline ${
|
|
22491
|
+
p22.log.info(`Executing pipeline ${pc34.bold(pipeline2.pipelineId)} (${pkg.executionRoute})...`);
|
|
21642
22492
|
const result = await executionClient.executeWorkflow({
|
|
21643
22493
|
pipelineId: pipeline2.pipelineId,
|
|
21644
22494
|
threadId: pipeline2.threadId,
|
|
@@ -21651,7 +22501,7 @@ async function runPipelineAssembler(opts) {
|
|
|
21651
22501
|
executionMode: pipeline2.executionMode,
|
|
21652
22502
|
metadata: pipeline2.metadata
|
|
21653
22503
|
});
|
|
21654
|
-
p22.log.success(`Execution ${
|
|
22504
|
+
p22.log.success(`Execution ${pc34.bold(result.executionId)}: ${result.status}`);
|
|
21655
22505
|
const artifactStore = createArtifactStore();
|
|
21656
22506
|
for (const artRef of result.artifacts) {
|
|
21657
22507
|
const nodeResult = result.nodeResults[artRef.nodeId];
|
|
@@ -21696,7 +22546,7 @@ function renderExecutionProgress(completed, total, detail) {
|
|
|
21696
22546
|
const percent = total <= 0 ? 0 : Math.round(safeCompleted / total * 100);
|
|
21697
22547
|
const filled = Math.max(0, Math.min(width, Math.round(percent / 100 * width)));
|
|
21698
22548
|
const bar = `${"=".repeat(filled)}${"-".repeat(width - filled)}`;
|
|
21699
|
-
const line = `\r${
|
|
22549
|
+
const line = `\r${pc34.cyan("Workflow run")} ${pc34.dim("[")}${pc34.green(bar)}${pc34.dim("]")} ${String(percent).padStart(3)}% ${pc34.dim(detail)}`;
|
|
21700
22550
|
process.stdout.write(line);
|
|
21701
22551
|
if (safeCompleted >= total) {
|
|
21702
22552
|
process.stdout.write("\n");
|
|
@@ -21769,50 +22619,50 @@ async function executeHostedPipeline(pipeline, opts) {
|
|
|
21769
22619
|
return;
|
|
21770
22620
|
}
|
|
21771
22621
|
console.log("");
|
|
21772
|
-
console.log(
|
|
22622
|
+
console.log(pc34.bold("Pipeline Execution Result"));
|
|
21773
22623
|
console.log(hr5());
|
|
21774
|
-
console.log(` ${
|
|
21775
|
-
if (result.threadId) console.log(` ${
|
|
21776
|
-
console.log(` ${
|
|
21777
|
-
if (result.startedAt) console.log(` ${
|
|
21778
|
-
if (result.completedAt) console.log(` ${
|
|
22624
|
+
console.log(` ${pc34.dim("Execution ID:")} ${result.executionId}`);
|
|
22625
|
+
if (result.threadId) console.log(` ${pc34.dim("Thread ID:")} ${result.threadId}`);
|
|
22626
|
+
console.log(` ${pc34.dim("Status:")} ${result.status === "succeeded" ? pc34.green(result.status) : pc34.red(result.status)}`);
|
|
22627
|
+
if (result.startedAt) console.log(` ${pc34.dim("Started:")} ${result.startedAt}`);
|
|
22628
|
+
if (result.completedAt) console.log(` ${pc34.dim("Completed:")} ${result.completedAt}`);
|
|
21779
22629
|
console.log(hr5());
|
|
21780
22630
|
for (const [nodeId, nodeResult] of Object.entries(result.nodeResults)) {
|
|
21781
|
-
const statusColor3 = nodeResult.status === "succeeded" ?
|
|
21782
|
-
console.log(` ${statusColor3(nodeResult.status)} ${
|
|
22631
|
+
const statusColor3 = nodeResult.status === "succeeded" ? pc34.green : pc34.red;
|
|
22632
|
+
console.log(` ${statusColor3(nodeResult.status)} ${pc34.bold(nodeResult.slug)} (${pc34.dim(nodeId)})`);
|
|
21783
22633
|
if (nodeResult.error) {
|
|
21784
|
-
console.log(` ${
|
|
22634
|
+
console.log(` ${pc34.red(nodeResult.error)}`);
|
|
21785
22635
|
}
|
|
21786
22636
|
}
|
|
21787
22637
|
if (result.artifacts.length > 0) {
|
|
21788
22638
|
console.log("");
|
|
21789
|
-
console.log(
|
|
22639
|
+
console.log(pc34.bold(" Artifacts:"));
|
|
21790
22640
|
for (const art of result.artifacts) {
|
|
21791
|
-
console.log(` ${
|
|
22641
|
+
console.log(` ${pc34.dim("\xB7")} ${art.artifactType} (${art.artifactId})`);
|
|
21792
22642
|
}
|
|
21793
22643
|
}
|
|
21794
22644
|
if (result.summary) {
|
|
21795
22645
|
console.log("");
|
|
21796
|
-
console.log(
|
|
21797
|
-
if (result.summary.outputText) console.log(` ${
|
|
21798
|
-
if (typeof result.summary.imageCount === "number") console.log(` ${
|
|
21799
|
-
if (typeof result.summary.slideCount === "number") console.log(` ${
|
|
21800
|
-
if (typeof result.summary.videoCount === "number") console.log(` ${
|
|
21801
|
-
if (result.summary.workflowRunId) console.log(` ${
|
|
21802
|
-
if (result.summary.keyboardShortcutHint) console.log(` ${
|
|
22646
|
+
console.log(pc34.bold(" Summary:"));
|
|
22647
|
+
if (result.summary.outputText) console.log(` ${pc34.dim("\xB7")} ${result.summary.outputText}`);
|
|
22648
|
+
if (typeof result.summary.imageCount === "number") console.log(` ${pc34.dim("\xB7")} images: ${result.summary.imageCount}`);
|
|
22649
|
+
if (typeof result.summary.slideCount === "number") console.log(` ${pc34.dim("\xB7")} slides: ${result.summary.slideCount}`);
|
|
22650
|
+
if (typeof result.summary.videoCount === "number") console.log(` ${pc34.dim("\xB7")} videos: ${result.summary.videoCount}`);
|
|
22651
|
+
if (result.summary.workflowRunId) console.log(` ${pc34.dim("\xB7")} workflow_run_id: ${result.summary.workflowRunId}`);
|
|
22652
|
+
if (result.summary.keyboardShortcutHint) console.log(` ${pc34.dim("\xB7")} ${result.summary.keyboardShortcutHint}`);
|
|
21803
22653
|
}
|
|
21804
22654
|
try {
|
|
21805
22655
|
const credits = await fetchHostedCredits(session);
|
|
21806
22656
|
if (credits) {
|
|
21807
22657
|
console.log("");
|
|
21808
|
-
console.log(
|
|
21809
|
-
console.log(` ${
|
|
21810
|
-
console.log(` ${
|
|
22658
|
+
console.log(pc34.bold(" Credits:"));
|
|
22659
|
+
console.log(` ${pc34.dim("\xB7")} available: $${credits.totalAvailable.toFixed(2)}`);
|
|
22660
|
+
console.log(` ${pc34.dim("\xB7")} used this period: $${credits.creditsUsedThisPeriod.toFixed(2)} / $${credits.creditsPerMonth.toFixed(2)}`);
|
|
21811
22661
|
}
|
|
21812
22662
|
} catch (err) {
|
|
21813
22663
|
if (err instanceof HostedEndpointUnavailableError) {
|
|
21814
22664
|
console.log("");
|
|
21815
|
-
console.log(
|
|
22665
|
+
console.log(pc34.yellow(" Credits unavailable on this hosted surface."));
|
|
21816
22666
|
} else {
|
|
21817
22667
|
throw err;
|
|
21818
22668
|
}
|
|
@@ -21870,31 +22720,31 @@ async function renderIntelligenceSummary(pipeline, capabilities, phase) {
|
|
|
21870
22720
|
};
|
|
21871
22721
|
const result = await provider.summarizeExecution(input);
|
|
21872
22722
|
const lines = [
|
|
21873
|
-
`${
|
|
22723
|
+
`${pc34.bold("Intelligence Summary")} ${pc34.dim(result.title)}`,
|
|
21874
22724
|
result.explanation
|
|
21875
22725
|
];
|
|
21876
22726
|
if (result.runtimeModeNote) {
|
|
21877
|
-
lines.push(`${
|
|
22727
|
+
lines.push(`${pc34.dim("Runtime:")} ${result.runtimeModeNote}`);
|
|
21878
22728
|
}
|
|
21879
22729
|
if (result.outputExpectation) {
|
|
21880
|
-
lines.push(`${
|
|
22730
|
+
lines.push(`${pc34.dim("Expected:")} ${result.outputExpectation}`);
|
|
21881
22731
|
}
|
|
21882
22732
|
if (result.missingBindingGuidance.length > 0) {
|
|
21883
|
-
lines.push("",
|
|
22733
|
+
lines.push("", pc34.yellow("Missing Binding Guidance"));
|
|
21884
22734
|
for (const guidance of result.missingBindingGuidance) {
|
|
21885
|
-
lines.push(` ${
|
|
22735
|
+
lines.push(` ${pc34.dim("\xB7")} ${guidance}`);
|
|
21886
22736
|
}
|
|
21887
22737
|
}
|
|
21888
22738
|
if (result.costLatencyCautions.length > 0) {
|
|
21889
|
-
lines.push("",
|
|
22739
|
+
lines.push("", pc34.yellow("Cost/Latency Notes"));
|
|
21890
22740
|
for (const caution of result.costLatencyCautions) {
|
|
21891
|
-
lines.push(` ${
|
|
22741
|
+
lines.push(` ${pc34.dim("\xB7")} ${caution}`);
|
|
21892
22742
|
}
|
|
21893
22743
|
}
|
|
21894
22744
|
if (result.warnings.length > 0) {
|
|
21895
|
-
lines.push("",
|
|
22745
|
+
lines.push("", pc34.yellow("Warnings"));
|
|
21896
22746
|
for (const warning of result.warnings) {
|
|
21897
|
-
lines.push(` ${
|
|
22747
|
+
lines.push(` ${pc34.dim("\xB7")} ${warning}`);
|
|
21898
22748
|
}
|
|
21899
22749
|
}
|
|
21900
22750
|
return lines;
|
|
@@ -21919,7 +22769,7 @@ Examples:
|
|
|
21919
22769
|
pipe.command("validate").description("Validate a pipeline from a JSON file or inline JSON").argument("<file-or-json>", "Path to pipeline JSON file or inline JSON string").option("--json", "Output raw JSON").action(async (input, opts) => {
|
|
21920
22770
|
const access = getWorkflowAccess();
|
|
21921
22771
|
if (access.state !== "ready") {
|
|
21922
|
-
console.error(
|
|
22772
|
+
console.error(pc34.red(`${access.reason}.`));
|
|
21923
22773
|
process.exitCode = 1;
|
|
21924
22774
|
return;
|
|
21925
22775
|
}
|
|
@@ -21939,25 +22789,25 @@ Examples:
|
|
|
21939
22789
|
return;
|
|
21940
22790
|
}
|
|
21941
22791
|
if (result.valid) {
|
|
21942
|
-
console.log(
|
|
22792
|
+
console.log(pc34.green(pc34.bold("Pipeline is valid.")));
|
|
21943
22793
|
} else {
|
|
21944
|
-
console.log(
|
|
22794
|
+
console.log(pc34.red(pc34.bold("Pipeline validation failed.")));
|
|
21945
22795
|
}
|
|
21946
22796
|
for (const issue of result.issues) {
|
|
21947
|
-
const prefix = issue.severity === "error" ?
|
|
22797
|
+
const prefix = issue.severity === "error" ? pc34.red(" ERROR") : pc34.yellow(" WARN");
|
|
21948
22798
|
const nodeRef = issue.nodeId ? ` [${issue.nodeId}]` : "";
|
|
21949
22799
|
console.log(`${prefix}${nodeRef}: ${issue.message}`);
|
|
21950
22800
|
}
|
|
21951
22801
|
if (!result.valid) process.exitCode = 1;
|
|
21952
22802
|
} catch (err) {
|
|
21953
|
-
console.error(
|
|
22803
|
+
console.error(pc34.red("Validation failed: " + err.message));
|
|
21954
22804
|
process.exitCode = 1;
|
|
21955
22805
|
}
|
|
21956
22806
|
});
|
|
21957
22807
|
pipe.command("execute").description("Execute a pipeline from a JSON file or inline JSON").argument("<file-or-json>", "Path to pipeline JSON file or inline JSON string").option("--json", "Output raw JSON").action(async (input, opts) => {
|
|
21958
22808
|
const access = getWorkflowAccess();
|
|
21959
22809
|
if (access.state !== "ready") {
|
|
21960
|
-
console.error(
|
|
22810
|
+
console.error(pc34.red(`${access.reason}.`));
|
|
21961
22811
|
process.exitCode = 1;
|
|
21962
22812
|
return;
|
|
21963
22813
|
}
|
|
@@ -22027,45 +22877,45 @@ Examples:
|
|
|
22027
22877
|
return;
|
|
22028
22878
|
}
|
|
22029
22879
|
console.log("");
|
|
22030
|
-
console.log(
|
|
22880
|
+
console.log(pc34.bold("Pipeline Execution Result"));
|
|
22031
22881
|
console.log(hr5());
|
|
22032
|
-
console.log(` ${
|
|
22033
|
-
if (result.threadId) console.log(` ${
|
|
22034
|
-
console.log(` ${
|
|
22035
|
-
if (result.startedAt) console.log(` ${
|
|
22036
|
-
if (result.completedAt) console.log(` ${
|
|
22882
|
+
console.log(` ${pc34.dim("Execution ID:")} ${result.executionId}`);
|
|
22883
|
+
if (result.threadId) console.log(` ${pc34.dim("Thread ID:")} ${result.threadId}`);
|
|
22884
|
+
console.log(` ${pc34.dim("Status:")} ${result.status === "succeeded" ? pc34.green(result.status) : pc34.red(result.status)}`);
|
|
22885
|
+
if (result.startedAt) console.log(` ${pc34.dim("Started:")} ${result.startedAt}`);
|
|
22886
|
+
if (result.completedAt) console.log(` ${pc34.dim("Completed:")} ${result.completedAt}`);
|
|
22037
22887
|
console.log(hr5());
|
|
22038
22888
|
for (const [nodeId, nodeResult] of Object.entries(result.nodeResults)) {
|
|
22039
|
-
const statusColor3 = nodeResult.status === "succeeded" ?
|
|
22040
|
-
console.log(` ${statusColor3(nodeResult.status)} ${
|
|
22889
|
+
const statusColor3 = nodeResult.status === "succeeded" ? pc34.green : pc34.red;
|
|
22890
|
+
console.log(` ${statusColor3(nodeResult.status)} ${pc34.bold(nodeResult.slug)} (${pc34.dim(nodeId)})`);
|
|
22041
22891
|
if (nodeResult.error) {
|
|
22042
|
-
console.log(` ${
|
|
22892
|
+
console.log(` ${pc34.red(nodeResult.error)}`);
|
|
22043
22893
|
}
|
|
22044
22894
|
}
|
|
22045
22895
|
if (result.artifacts.length > 0) {
|
|
22046
22896
|
console.log("");
|
|
22047
|
-
console.log(
|
|
22897
|
+
console.log(pc34.bold(" Artifacts:"));
|
|
22048
22898
|
for (const art of result.artifacts) {
|
|
22049
|
-
console.log(` ${
|
|
22899
|
+
console.log(` ${pc34.dim("\xB7")} ${art.artifactType} (${art.artifactId})`);
|
|
22050
22900
|
}
|
|
22051
22901
|
}
|
|
22052
22902
|
if (result.summary) {
|
|
22053
22903
|
console.log("");
|
|
22054
|
-
console.log(
|
|
22055
|
-
if (result.summary.outputText) console.log(` ${
|
|
22056
|
-
if (typeof result.summary.imageCount === "number") console.log(` ${
|
|
22057
|
-
if (typeof result.summary.slideCount === "number") console.log(` ${
|
|
22058
|
-
if (typeof result.summary.videoCount === "number") console.log(` ${
|
|
22059
|
-
if (result.summary.workflowRunId) console.log(` ${
|
|
22060
|
-
if (result.summary.keyboardShortcutHint) console.log(` ${
|
|
22904
|
+
console.log(pc34.bold(" Summary:"));
|
|
22905
|
+
if (result.summary.outputText) console.log(` ${pc34.dim("\xB7")} ${result.summary.outputText}`);
|
|
22906
|
+
if (typeof result.summary.imageCount === "number") console.log(` ${pc34.dim("\xB7")} images: ${result.summary.imageCount}`);
|
|
22907
|
+
if (typeof result.summary.slideCount === "number") console.log(` ${pc34.dim("\xB7")} slides: ${result.summary.slideCount}`);
|
|
22908
|
+
if (typeof result.summary.videoCount === "number") console.log(` ${pc34.dim("\xB7")} videos: ${result.summary.videoCount}`);
|
|
22909
|
+
if (result.summary.workflowRunId) console.log(` ${pc34.dim("\xB7")} workflow_run_id: ${result.summary.workflowRunId}`);
|
|
22910
|
+
if (result.summary.keyboardShortcutHint) console.log(` ${pc34.dim("\xB7")} ${result.summary.keyboardShortcutHint}`);
|
|
22061
22911
|
}
|
|
22062
22912
|
try {
|
|
22063
22913
|
const credits = await fetchHostedCredits(session);
|
|
22064
22914
|
if (credits) {
|
|
22065
22915
|
console.log("");
|
|
22066
|
-
console.log(
|
|
22067
|
-
console.log(` ${
|
|
22068
|
-
console.log(` ${
|
|
22916
|
+
console.log(pc34.bold(" Credits:"));
|
|
22917
|
+
console.log(` ${pc34.dim("\xB7")} available: $${credits.totalAvailable.toFixed(2)}`);
|
|
22918
|
+
console.log(` ${pc34.dim("\xB7")} used this period: $${credits.creditsUsedThisPeriod.toFixed(2)} / $${credits.creditsPerMonth.toFixed(2)}`);
|
|
22069
22919
|
}
|
|
22070
22920
|
} catch (err) {
|
|
22071
22921
|
if (!(err instanceof HostedEndpointUnavailableError)) {
|
|
@@ -22087,24 +22937,24 @@ Examples:
|
|
|
22087
22937
|
}
|
|
22088
22938
|
console.log("");
|
|
22089
22939
|
} catch (err) {
|
|
22090
|
-
console.error(
|
|
22940
|
+
console.error(pc34.red("Execution failed: " + err.message));
|
|
22091
22941
|
process.exitCode = 1;
|
|
22092
22942
|
}
|
|
22093
22943
|
});
|
|
22094
22944
|
}
|
|
22095
22945
|
|
|
22096
22946
|
// src/commands/artifact.ts
|
|
22097
|
-
import
|
|
22947
|
+
import pc35 from "picocolors";
|
|
22098
22948
|
function hr6(width = 72) {
|
|
22099
|
-
return
|
|
22949
|
+
return pc35.dim("\u2500".repeat(width));
|
|
22100
22950
|
}
|
|
22101
22951
|
var ARTIFACT_TYPE_CONFIG = {
|
|
22102
|
-
video: { color:
|
|
22103
|
-
image: { color:
|
|
22104
|
-
slides: { color:
|
|
22105
|
-
text: { color:
|
|
22106
|
-
report: { color:
|
|
22107
|
-
pipeline: { color:
|
|
22952
|
+
video: { color: pc35.magenta, emoji: "\u{1F3AC}" },
|
|
22953
|
+
image: { color: pc35.cyan, emoji: "\u{1F5BC}\uFE0F " },
|
|
22954
|
+
slides: { color: pc35.yellow, emoji: "\u{1F4CA}" },
|
|
22955
|
+
text: { color: pc35.green, emoji: "\u{1F4DD}" },
|
|
22956
|
+
report: { color: pc35.blue, emoji: "\u{1F4CB}" },
|
|
22957
|
+
pipeline: { color: pc35.red, emoji: "\u{1F517}" }
|
|
22108
22958
|
};
|
|
22109
22959
|
function artifactTypeBadge(type) {
|
|
22110
22960
|
const cfg = ARTIFACT_TYPE_CONFIG[type];
|
|
@@ -22112,21 +22962,21 @@ function artifactTypeBadge(type) {
|
|
|
22112
22962
|
return cfg.color(`${cfg.emoji} ${type}`);
|
|
22113
22963
|
}
|
|
22114
22964
|
function statusColor(status) {
|
|
22115
|
-
if (status === "ready") return
|
|
22116
|
-
if (status === "generating" || status === "pending") return
|
|
22117
|
-
if (status === "failed") return
|
|
22118
|
-
if (status === "archived") return
|
|
22965
|
+
if (status === "ready") return pc35.green(status);
|
|
22966
|
+
if (status === "generating" || status === "pending") return pc35.yellow(status);
|
|
22967
|
+
if (status === "failed") return pc35.red(status);
|
|
22968
|
+
if (status === "archived") return pc35.dim(status);
|
|
22119
22969
|
return status;
|
|
22120
22970
|
}
|
|
22121
22971
|
function printArtifactTable(artifacts) {
|
|
22122
22972
|
console.log("");
|
|
22123
22973
|
console.log(
|
|
22124
|
-
|
|
22974
|
+
pc35.bold("Pipeline Artifacts") + pc35.dim(` ${artifacts.length} artifact${artifacts.length !== 1 ? "s" : ""}`)
|
|
22125
22975
|
);
|
|
22126
22976
|
console.log(hr6());
|
|
22127
22977
|
if (artifacts.length === 0) {
|
|
22128
|
-
console.log(
|
|
22129
|
-
console.log(
|
|
22978
|
+
console.log(pc35.dim(" No artifacts found."));
|
|
22979
|
+
console.log(pc35.dim(" Run `growthub pipeline execute` to produce artifacts."));
|
|
22130
22980
|
console.log("");
|
|
22131
22981
|
return;
|
|
22132
22982
|
}
|
|
@@ -22134,25 +22984,25 @@ function printArtifactTable(artifacts) {
|
|
|
22134
22984
|
const badge2 = artifactTypeBadge(art.artifactType);
|
|
22135
22985
|
const status = statusColor(art.status);
|
|
22136
22986
|
console.log(
|
|
22137
|
-
` ${badge2} ${
|
|
22987
|
+
` ${badge2} ${pc35.bold(art.id)} ${status} ${pc35.dim(art.sourceNodeSlug)} ${pc35.dim(art.executionContext)}`
|
|
22138
22988
|
);
|
|
22139
22989
|
if (art.pipelineId) {
|
|
22140
|
-
console.log(` ${
|
|
22990
|
+
console.log(` ${pc35.dim("Pipeline:")} ${art.pipelineId}`);
|
|
22141
22991
|
}
|
|
22142
|
-
console.log(` ${
|
|
22992
|
+
console.log(` ${pc35.dim("Created:")} ${art.createdAt}`);
|
|
22143
22993
|
console.log("");
|
|
22144
22994
|
}
|
|
22145
22995
|
console.log(hr6());
|
|
22146
|
-
console.log(
|
|
22996
|
+
console.log(pc35.dim(" growthub artifact inspect <id> \xB7 growthub artifact list --type <type>"));
|
|
22147
22997
|
console.log("");
|
|
22148
22998
|
}
|
|
22149
22999
|
function printArtifactDetail(art) {
|
|
22150
23000
|
console.log("");
|
|
22151
|
-
console.log(
|
|
23001
|
+
console.log(pc35.bold("Artifact: " + art.id));
|
|
22152
23002
|
console.log(hr6());
|
|
22153
23003
|
const kv = (label, value) => {
|
|
22154
23004
|
if (value === void 0) return;
|
|
22155
|
-
console.log(` ${
|
|
23005
|
+
console.log(` ${pc35.bold(label.padEnd(22))} ${value}`);
|
|
22156
23006
|
};
|
|
22157
23007
|
kv("Type:", artifactTypeBadge(art.artifactType));
|
|
22158
23008
|
kv("Status:", statusColor(art.status));
|
|
@@ -22166,7 +23016,7 @@ function printArtifactDetail(art) {
|
|
|
22166
23016
|
kv("Updated:", art.updatedAt);
|
|
22167
23017
|
if (art.metadata && Object.keys(art.metadata).length > 0) {
|
|
22168
23018
|
console.log("");
|
|
22169
|
-
console.log(
|
|
23019
|
+
console.log(pc35.bold(" Metadata:"));
|
|
22170
23020
|
console.log(" " + JSON.stringify(art.metadata, null, 2).split("\n").join("\n "));
|
|
22171
23021
|
}
|
|
22172
23022
|
console.log(hr6());
|
|
@@ -22203,14 +23053,14 @@ Examples:
|
|
|
22203
23053
|
return;
|
|
22204
23054
|
}
|
|
22205
23055
|
printArtifactTable(artifacts);
|
|
22206
|
-
console.log(
|
|
23056
|
+
console.log(pc35.dim(` Store: ${store.getStorePath()} \xB7 Source: ${meta.source}`));
|
|
22207
23057
|
console.log("");
|
|
22208
23058
|
});
|
|
22209
23059
|
art.command("inspect").description("Inspect a specific pipeline artifact").argument("<id>", "Artifact ID (e.g. art_xxxxxxxxxxxx)").option("--json", "Output raw JSON").action((artifactId, opts) => {
|
|
22210
23060
|
const store = createArtifactStore();
|
|
22211
23061
|
const artifact = store.get(artifactId);
|
|
22212
23062
|
if (!artifact) {
|
|
22213
|
-
console.error(
|
|
23063
|
+
console.error(pc35.red(`Artifact not found: "${artifactId}".`) + pc35.dim(" Run `growthub artifact list` to browse."));
|
|
22214
23064
|
process.exitCode = 1;
|
|
22215
23065
|
return;
|
|
22216
23066
|
}
|
|
@@ -22226,7 +23076,7 @@ Examples:
|
|
|
22226
23076
|
import fs31 from "node:fs";
|
|
22227
23077
|
import path40 from "node:path";
|
|
22228
23078
|
import * as p23 from "@clack/prompts";
|
|
22229
|
-
import
|
|
23079
|
+
import pc37 from "picocolors";
|
|
22230
23080
|
init_session_store();
|
|
22231
23081
|
init_hosted_client();
|
|
22232
23082
|
|
|
@@ -22291,11 +23141,11 @@ function createWorkflowHygieneStore() {
|
|
|
22291
23141
|
}
|
|
22292
23142
|
|
|
22293
23143
|
// src/runtime/workflow-hygiene/summaries.ts
|
|
22294
|
-
import
|
|
23144
|
+
import pc36 from "picocolors";
|
|
22295
23145
|
function renderWorkflowLabel(label) {
|
|
22296
|
-
if (label === "canonical") return
|
|
22297
|
-
if (label === "archived") return
|
|
22298
|
-
return
|
|
23146
|
+
if (label === "canonical") return pc36.green("canonical");
|
|
23147
|
+
if (label === "archived") return pc36.dim("archived");
|
|
23148
|
+
return pc36.yellow("experimental");
|
|
22299
23149
|
}
|
|
22300
23150
|
function enrichWorkflowSummaries(entries, store) {
|
|
22301
23151
|
return entries.map((entry) => {
|
|
@@ -22309,14 +23159,14 @@ init_banner();
|
|
|
22309
23159
|
init_home();
|
|
22310
23160
|
var PAGE_SIZE = 10;
|
|
22311
23161
|
var FAMILY_CONFIG2 = {
|
|
22312
|
-
video: { color:
|
|
22313
|
-
image: { color:
|
|
22314
|
-
slides: { color:
|
|
22315
|
-
text: { color:
|
|
22316
|
-
data: { color:
|
|
22317
|
-
ops: { color:
|
|
22318
|
-
research: { color:
|
|
22319
|
-
vision: { color:
|
|
23162
|
+
video: { color: pc37.magenta, label: "Video" },
|
|
23163
|
+
image: { color: pc37.cyan, label: "Image" },
|
|
23164
|
+
slides: { color: pc37.yellow, label: "Slides" },
|
|
23165
|
+
text: { color: pc37.green, label: "Text" },
|
|
23166
|
+
data: { color: pc37.blue, label: "Data" },
|
|
23167
|
+
ops: { color: pc37.red, label: "Ops" },
|
|
23168
|
+
research: { color: pc37.blue, label: "Research" },
|
|
23169
|
+
vision: { color: pc37.cyan, label: "Vision" }
|
|
22320
23170
|
};
|
|
22321
23171
|
var FAMILY_EMOJI = {
|
|
22322
23172
|
video: "\u{1F3AC}",
|
|
@@ -22333,19 +23183,19 @@ function familyLabel(family) {
|
|
|
22333
23183
|
return cfg ? cfg.color(cfg.label) : family;
|
|
22334
23184
|
}
|
|
22335
23185
|
function hr7(width = 72) {
|
|
22336
|
-
return
|
|
23186
|
+
return pc37.dim("\u2500".repeat(width));
|
|
22337
23187
|
}
|
|
22338
|
-
function
|
|
23188
|
+
function stripAnsi6(str) {
|
|
22339
23189
|
return str.replace(/\x1B\[[0-9;]*m/g, "");
|
|
22340
23190
|
}
|
|
22341
23191
|
function box5(lines) {
|
|
22342
23192
|
const padded = lines.map((l) => " " + l);
|
|
22343
|
-
const width = Math.max(...padded.map((l) =>
|
|
22344
|
-
const top =
|
|
22345
|
-
const bottom =
|
|
23193
|
+
const width = Math.max(...padded.map((l) => stripAnsi6(l).length)) + 4;
|
|
23194
|
+
const top = pc37.dim("\u250C" + "\u2500".repeat(width) + "\u2510");
|
|
23195
|
+
const bottom = pc37.dim("\u2514" + "\u2500".repeat(width) + "\u2518");
|
|
22346
23196
|
const body = padded.map((l) => {
|
|
22347
|
-
const
|
|
22348
|
-
return
|
|
23197
|
+
const pad2 = width - stripAnsi6(l).length;
|
|
23198
|
+
return pc37.dim("\u2502") + l + " ".repeat(pad2) + pc37.dim("\u2502");
|
|
22349
23199
|
});
|
|
22350
23200
|
return [top, ...body, bottom].join("\n");
|
|
22351
23201
|
}
|
|
@@ -22576,7 +23426,7 @@ async function paginatedSelect(message, allOptions, opts) {
|
|
|
22576
23426
|
const hasPrev = offset > 0;
|
|
22577
23427
|
const totalPages = Math.ceil(filtered.length / PAGE_SIZE);
|
|
22578
23428
|
const currentPage = Math.floor(offset / PAGE_SIZE) + 1;
|
|
22579
|
-
const pageInfo = filtered.length > PAGE_SIZE ?
|
|
23429
|
+
const pageInfo = filtered.length > PAGE_SIZE ? pc37.dim(` (${currentPage}/${totalPages} \xB7 ${filtered.length} total)`) : "";
|
|
22580
23430
|
const options = [
|
|
22581
23431
|
...page.map((o) => ({
|
|
22582
23432
|
value: o.value,
|
|
@@ -22585,13 +23435,13 @@ async function paginatedSelect(message, allOptions, opts) {
|
|
|
22585
23435
|
}))
|
|
22586
23436
|
];
|
|
22587
23437
|
if (hasMore) {
|
|
22588
|
-
options.push({ value: "__next_page", label:
|
|
23438
|
+
options.push({ value: "__next_page", label: pc37.dim("\u2192 Next page") });
|
|
22589
23439
|
}
|
|
22590
23440
|
if (hasPrev) {
|
|
22591
|
-
options.push({ value: "__prev_page", label:
|
|
23441
|
+
options.push({ value: "__prev_page", label: pc37.dim("\u2190 Previous page") });
|
|
22592
23442
|
}
|
|
22593
23443
|
if (opts?.searchEnabled) {
|
|
22594
|
-
options.push({ value: "__search", label:
|
|
23444
|
+
options.push({ value: "__search", label: pc37.dim("\u{1F50E} Search") });
|
|
22595
23445
|
}
|
|
22596
23446
|
options.push({
|
|
22597
23447
|
value: opts?.backValue ?? "__back",
|
|
@@ -22639,8 +23489,8 @@ async function paginatedSelect(message, allOptions, opts) {
|
|
|
22639
23489
|
function printTemplateCard(node) {
|
|
22640
23490
|
const contract = introspectNodeContract(node);
|
|
22641
23491
|
const lines = renderContractCard(contract);
|
|
22642
|
-
lines.splice(1, 0, `${familyLabel(node.family)} ${node.enabled ?
|
|
22643
|
-
if (node.description) lines.push("",
|
|
23492
|
+
lines.splice(1, 0, `${familyLabel(node.family)} ${node.enabled ? pc37.green("enabled") : pc37.red("disabled")}`);
|
|
23493
|
+
if (node.description) lines.push("", pc37.dim(node.description));
|
|
22644
23494
|
console.log("");
|
|
22645
23495
|
console.log(box5(lines));
|
|
22646
23496
|
console.log("");
|
|
@@ -22654,9 +23504,9 @@ function renderTemplateTree(templates) {
|
|
|
22654
23504
|
byFamily.set(key, existing);
|
|
22655
23505
|
}
|
|
22656
23506
|
const families = [...byFamily.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
22657
|
-
const lines = [
|
|
23507
|
+
const lines = [pc37.bold("Public CMS Node Tree")];
|
|
22658
23508
|
for (const [family, nodes] of families) {
|
|
22659
|
-
lines.push(`${
|
|
23509
|
+
lines.push(`${pc37.cyan("\u2022")} ${pc37.bold(family)}`);
|
|
22660
23510
|
const sorted = [...nodes].sort((a, b) => a.slug.localeCompare(b.slug));
|
|
22661
23511
|
for (const [index51, node] of sorted.entries()) {
|
|
22662
23512
|
const branch = index51 === sorted.length - 1 ? "\u2514\u2500" : "\u251C\u2500";
|
|
@@ -22664,12 +23514,12 @@ function renderTemplateTree(templates) {
|
|
|
22664
23514
|
const requiredInputs = contract.inputs.filter((input) => input.required).length;
|
|
22665
23515
|
const optionalInputs = contract.inputs.length - requiredInputs;
|
|
22666
23516
|
lines.push(
|
|
22667
|
-
` ${branch} ${node.slug} ${
|
|
23517
|
+
` ${branch} ${node.slug} ${pc37.dim(`(req:${requiredInputs} opt:${optionalInputs} out:${contract.outputTypes.length})`)}`
|
|
22668
23518
|
);
|
|
22669
23519
|
}
|
|
22670
23520
|
}
|
|
22671
23521
|
lines.push("");
|
|
22672
|
-
lines.push(
|
|
23522
|
+
lines.push(pc37.dim("Shortcut: growthub workflow saved --json"));
|
|
22673
23523
|
return lines;
|
|
22674
23524
|
}
|
|
22675
23525
|
function renderWorkflowContractDiscoveryTree(nodes) {
|
|
@@ -22681,10 +23531,10 @@ function renderWorkflowContractDiscoveryTree(nodes) {
|
|
|
22681
23531
|
byFamily.set(key, group);
|
|
22682
23532
|
}
|
|
22683
23533
|
const families = [...byFamily.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
22684
|
-
const lines = [
|
|
23534
|
+
const lines = [pc37.bold("CMS Node Contract Discovery")];
|
|
22685
23535
|
for (const [family, familyNodes] of families) {
|
|
22686
23536
|
const emoji = FAMILY_EMOJI[family] ?? "\u2022";
|
|
22687
|
-
lines.push(`${emoji} ${
|
|
23537
|
+
lines.push(`${emoji} ${pc37.bold(familyLabel(family))} ${pc37.dim(`(${familyNodes.length})`)}`);
|
|
22688
23538
|
const sorted = [...familyNodes].sort((a, b) => a.slug.localeCompare(b.slug));
|
|
22689
23539
|
for (const [index51, node] of sorted.entries()) {
|
|
22690
23540
|
const branch = index51 === sorted.length - 1 ? "\u2514\u2500" : "\u251C\u2500";
|
|
@@ -22692,7 +23542,7 @@ function renderWorkflowContractDiscoveryTree(nodes) {
|
|
|
22692
23542
|
const requiredInputs = contract.inputs.filter((input) => input.required).length;
|
|
22693
23543
|
const optionalInputs = contract.inputs.length - requiredInputs;
|
|
22694
23544
|
lines.push(
|
|
22695
|
-
` ${branch} ${node.slug} ${
|
|
23545
|
+
` ${branch} ${node.slug} ${pc37.dim(`req:${requiredInputs} opt:${optionalInputs} bindings:${contract.requiredBindings.length} outputs:${contract.outputTypes.length}`)}`
|
|
22696
23546
|
);
|
|
22697
23547
|
}
|
|
22698
23548
|
}
|
|
@@ -22705,7 +23555,7 @@ function buildTemplateOption(template, viewMode) {
|
|
|
22705
23555
|
if (viewMode === "expanded") {
|
|
22706
23556
|
return {
|
|
22707
23557
|
value: template.slug,
|
|
22708
|
-
label: `${template.icon} ${template.displayName} ${
|
|
23558
|
+
label: `${template.icon} ${template.displayName} ${pc37.dim(template.slug)}`,
|
|
22709
23559
|
hint: `req:${requiredInputs} opt:${optionalInputs} outputs:${contract.outputTypes.join(", ") || "none"} exec:${contract.executionStrategy}`
|
|
22710
23560
|
};
|
|
22711
23561
|
}
|
|
@@ -22727,11 +23577,11 @@ async function runWorkflowPicker(opts) {
|
|
|
22727
23577
|
const hygieneStore = createWorkflowHygieneStore();
|
|
22728
23578
|
const access = getWorkflowAccess();
|
|
22729
23579
|
if (access.state === "unauthenticated") {
|
|
22730
|
-
p23.intro(
|
|
23580
|
+
p23.intro(pc37.bold("Workflows") + pc37.dim(" (not connected)"));
|
|
22731
23581
|
p23.note(
|
|
22732
23582
|
[
|
|
22733
23583
|
"Workflow assembly requires an authenticated Growthub session.",
|
|
22734
|
-
"Run " +
|
|
23584
|
+
"Run " + pc37.cyan("growthub auth login") + " to connect your account.",
|
|
22735
23585
|
"",
|
|
22736
23586
|
"Once connected you can:",
|
|
22737
23587
|
" - Browse CMS node contracts",
|
|
@@ -22743,7 +23593,7 @@ async function runWorkflowPicker(opts) {
|
|
|
22743
23593
|
if (opts.allowBackToHub) return "back";
|
|
22744
23594
|
return "done";
|
|
22745
23595
|
}
|
|
22746
|
-
p23.intro(
|
|
23596
|
+
p23.intro(pc37.bold("Workflows"));
|
|
22747
23597
|
while (true) {
|
|
22748
23598
|
const refreshedAccess = getWorkflowAccess();
|
|
22749
23599
|
const topChoice = await p23.select({
|
|
@@ -22751,12 +23601,12 @@ async function runWorkflowPicker(opts) {
|
|
|
22751
23601
|
options: [
|
|
22752
23602
|
{
|
|
22753
23603
|
value: "contracts",
|
|
22754
|
-
label: refreshedAccess.state === "ready" ? "0. CMS Node Contracts" :
|
|
23604
|
+
label: refreshedAccess.state === "ready" ? "0. CMS Node Contracts" : pc37.dim("0. CMS Node Contracts (locked)"),
|
|
22755
23605
|
hint: refreshedAccess.state === "ready" ? "Discovery tree for CMS node primitives" : refreshedAccess.reason
|
|
22756
23606
|
},
|
|
22757
23607
|
{
|
|
22758
23608
|
value: "pipelines",
|
|
22759
|
-
label: refreshedAccess.state === "ready" ? "1. Dynamic Pipelines" :
|
|
23609
|
+
label: refreshedAccess.state === "ready" ? "1. Dynamic Pipelines" : pc37.dim("1. Dynamic Pipelines (locked)"),
|
|
22760
23610
|
hint: refreshedAccess.state === "ready" ? "Create new pipelines and route into Saved Workflows" : refreshedAccess.reason
|
|
22761
23611
|
},
|
|
22762
23612
|
{
|
|
@@ -22823,7 +23673,7 @@ async function runWorkflowPicker(opts) {
|
|
|
22823
23673
|
const requiredInputs = contract.inputs.filter((input) => input.required).length;
|
|
22824
23674
|
return {
|
|
22825
23675
|
value: node.slug,
|
|
22826
|
-
label: `${node.icon} ${node.displayName} ${
|
|
23676
|
+
label: `${node.icon} ${node.displayName} ${pc37.dim(node.slug)}`,
|
|
22827
23677
|
hint: `${node.family} \xB7 required:${requiredInputs} \xB7 bindings:${contract.requiredBindings.length} \xB7 outputs:${contract.outputTypes.length}`
|
|
22828
23678
|
};
|
|
22829
23679
|
});
|
|
@@ -22864,7 +23714,7 @@ async function runWorkflowPicker(opts) {
|
|
|
22864
23714
|
}
|
|
22865
23715
|
}
|
|
22866
23716
|
} catch (err) {
|
|
22867
|
-
contractsSpinner.stop(
|
|
23717
|
+
contractsSpinner.stop(pc37.red("Failed to load CMS node contracts."));
|
|
22868
23718
|
p23.log.error("Failed to load CMS node contracts: " + err.message);
|
|
22869
23719
|
}
|
|
22870
23720
|
continue;
|
|
@@ -22899,14 +23749,14 @@ async function runWorkflowPicker(opts) {
|
|
|
22899
23749
|
saved = withEffectiveWorkflowLabels(enriched, hygieneStore);
|
|
22900
23750
|
savedSpinner.stop(`Loaded ${saved.length} saved workflow${saved.length === 1 ? "" : "s"}.`);
|
|
22901
23751
|
} catch (err) {
|
|
22902
|
-
savedSpinner.stop(
|
|
23752
|
+
savedSpinner.stop(pc37.red("Failed to load saved workflows."));
|
|
22903
23753
|
throw err;
|
|
22904
23754
|
}
|
|
22905
23755
|
if (saved.length === 0) {
|
|
22906
23756
|
p23.note(
|
|
22907
23757
|
[
|
|
22908
23758
|
"No saved workflows found.",
|
|
22909
|
-
"Use " +
|
|
23759
|
+
"Use " + pc37.cyan("growthub pipeline assemble") + " to create a new workflow pipeline."
|
|
22910
23760
|
].join("\n"),
|
|
22911
23761
|
"Nothing saved"
|
|
22912
23762
|
);
|
|
@@ -22914,7 +23764,7 @@ async function runWorkflowPicker(opts) {
|
|
|
22914
23764
|
}
|
|
22915
23765
|
const allOptions = saved.map((w) => ({
|
|
22916
23766
|
value: w.workflowId,
|
|
22917
|
-
label: `${w.name} ${
|
|
23767
|
+
label: `${w.name} ${pc37.dim(`[${renderWorkflowLabel(w.workflowLabel)}]`)} ${pc37.dim(`${w.nodeCount} node${w.nodeCount !== 1 ? "s" : ""}`)}`,
|
|
22918
23768
|
hint: `${w.executionMode} \xB7 ${w.updatedAt?.slice(0, 10) ?? w.createdAt.slice(0, 10)}`
|
|
22919
23769
|
}));
|
|
22920
23770
|
const choice = await paginatedSelect("Select a saved workflow", allOptions, {
|
|
@@ -22935,7 +23785,7 @@ async function runWorkflowPicker(opts) {
|
|
|
22935
23785
|
detail = await loadSavedWorkflowDetail(entry);
|
|
22936
23786
|
detailSpinner.stop(`Loaded ${entry.name}.`);
|
|
22937
23787
|
} catch (err) {
|
|
22938
|
-
detailSpinner.stop(
|
|
23788
|
+
detailSpinner.stop(pc37.red(`Failed to load ${entry.name}.`));
|
|
22939
23789
|
p23.log.error(err.message);
|
|
22940
23790
|
continue;
|
|
22941
23791
|
}
|
|
@@ -22943,14 +23793,14 @@ async function runWorkflowPicker(opts) {
|
|
|
22943
23793
|
const nodes = Array.isArray(pipeline.nodes) ? pipeline.nodes : [];
|
|
22944
23794
|
console.log("");
|
|
22945
23795
|
console.log(box5([
|
|
22946
|
-
`${
|
|
22947
|
-
`${
|
|
22948
|
-
`${
|
|
22949
|
-
`${
|
|
22950
|
-
`${
|
|
23796
|
+
`${pc37.bold("Workflow:")} ${entry.name}`,
|
|
23797
|
+
`${pc37.dim("ID:")} ${entry.workflowId}`,
|
|
23798
|
+
`${pc37.dim("Mode:")} hosted ${pc37.dim("Nodes:")} ${nodes.length}`,
|
|
23799
|
+
`${pc37.dim("Label:")} ${renderWorkflowLabel(entry.workflowLabel ?? "experimental")}`,
|
|
23800
|
+
`${pc37.dim("Created:")} ${detail.createdAt || "\u2014"}`,
|
|
22951
23801
|
"",
|
|
22952
23802
|
...nodes.map(
|
|
22953
|
-
(n, i) => `${
|
|
23803
|
+
(n, i) => `${pc37.dim(String(i + 1) + ".")} ${pc37.bold(n.data?.slug ?? n.slug ?? n.id)} ${pc37.dim(n.id)}`
|
|
22954
23804
|
)
|
|
22955
23805
|
]));
|
|
22956
23806
|
console.log("");
|
|
@@ -22961,7 +23811,7 @@ async function runWorkflowPicker(opts) {
|
|
|
22961
23811
|
{ value: "set_label", label: "Set workflow label" },
|
|
22962
23812
|
{ value: "archive", label: "Archive workflow" },
|
|
22963
23813
|
{ value: "unarchive", label: "Unarchive workflow" },
|
|
22964
|
-
{ value: "delete", label:
|
|
23814
|
+
{ value: "delete", label: pc37.red("Delete workflow") },
|
|
22965
23815
|
{ value: "back_to_saved", label: "\u2190 Back to saved workflows" }
|
|
22966
23816
|
]
|
|
22967
23817
|
});
|
|
@@ -23006,7 +23856,7 @@ async function runWorkflowPicker(opts) {
|
|
|
23006
23856
|
console.log("");
|
|
23007
23857
|
}
|
|
23008
23858
|
await executeHostedPipeline(executablePipeline);
|
|
23009
|
-
p23.log.success(`Saved workflow execution completed for ${
|
|
23859
|
+
p23.log.success(`Saved workflow execution completed for ${pc37.bold(entry.name)}.`);
|
|
23010
23860
|
} catch (err) {
|
|
23011
23861
|
p23.log.error("Saved workflow execution failed: " + err.message);
|
|
23012
23862
|
}
|
|
@@ -23025,7 +23875,7 @@ async function runWorkflowPicker(opts) {
|
|
|
23025
23875
|
continue;
|
|
23026
23876
|
}
|
|
23027
23877
|
hygieneStore.setLabel(entry.workflowId, labelChoice);
|
|
23028
|
-
p23.log.success(`Updated label for ${
|
|
23878
|
+
p23.log.success(`Updated label for ${pc37.bold(entry.name)} to ${renderWorkflowLabel(labelChoice)}.`);
|
|
23029
23879
|
continue;
|
|
23030
23880
|
}
|
|
23031
23881
|
if (nextAction === "archive") {
|
|
@@ -23039,10 +23889,10 @@ async function runWorkflowPicker(opts) {
|
|
|
23039
23889
|
try {
|
|
23040
23890
|
await archiveSavedWorkflow(entry);
|
|
23041
23891
|
hygieneStore.setLabel(entry.workflowId, "archived");
|
|
23042
|
-
p23.log.success(`Archived ${
|
|
23892
|
+
p23.log.success(`Archived ${pc37.bold(entry.name)}.`);
|
|
23043
23893
|
} catch {
|
|
23044
23894
|
hygieneStore.setLabel(entry.workflowId, "archived");
|
|
23045
|
-
p23.log.success(`Archived ${
|
|
23895
|
+
p23.log.success(`Archived ${pc37.bold(entry.name)} (local fallback).`);
|
|
23046
23896
|
}
|
|
23047
23897
|
continue;
|
|
23048
23898
|
}
|
|
@@ -23064,7 +23914,7 @@ async function runWorkflowPicker(opts) {
|
|
|
23064
23914
|
}
|
|
23065
23915
|
hygieneStore.setLabel(entry.workflowId, restoreChoice);
|
|
23066
23916
|
p23.log.success(
|
|
23067
|
-
`Unarchived ${
|
|
23917
|
+
`Unarchived ${pc37.bold(entry.name)} to ${renderWorkflowLabel(restoreChoice)}.`
|
|
23068
23918
|
);
|
|
23069
23919
|
continue;
|
|
23070
23920
|
}
|
|
@@ -23085,10 +23935,10 @@ async function runWorkflowPicker(opts) {
|
|
|
23085
23935
|
}
|
|
23086
23936
|
try {
|
|
23087
23937
|
await deleteSavedWorkflow(entry);
|
|
23088
|
-
p23.log.success(`Deleted ${
|
|
23938
|
+
p23.log.success(`Deleted ${pc37.bold(entry.name)}.`);
|
|
23089
23939
|
} catch {
|
|
23090
23940
|
markWorkflowDeletedLocally(entry.workflowId);
|
|
23091
|
-
p23.log.success(`Deleted ${
|
|
23941
|
+
p23.log.success(`Deleted ${pc37.bold(entry.name)} (local fallback).`);
|
|
23092
23942
|
}
|
|
23093
23943
|
continue;
|
|
23094
23944
|
}
|
|
@@ -23205,12 +24055,12 @@ async function runWorkflowPicker(opts) {
|
|
|
23205
24055
|
const resolver = createMachineCapabilityResolver();
|
|
23206
24056
|
const binding = await resolver.resolveCapability(selected.slug);
|
|
23207
24057
|
if (binding) {
|
|
23208
|
-
const statusColor3 = binding.allowed ?
|
|
24058
|
+
const statusColor3 = binding.allowed ? pc37.green : pc37.red;
|
|
23209
24059
|
console.log("");
|
|
23210
24060
|
console.log(box5([
|
|
23211
|
-
`${
|
|
23212
|
-
`${
|
|
23213
|
-
`${
|
|
24061
|
+
`${pc37.bold("Machine Binding:")} ${selected.slug}`,
|
|
24062
|
+
`${pc37.dim("Allowed:")} ${statusColor3(String(binding.allowed))}`,
|
|
24063
|
+
`${pc37.dim("Reason:")} ${binding.reason ?? "\u2014"}`
|
|
23214
24064
|
]));
|
|
23215
24065
|
console.log("");
|
|
23216
24066
|
}
|
|
@@ -23245,7 +24095,7 @@ async function runWorkflowPicker(opts) {
|
|
|
23245
24095
|
"Input normalization"
|
|
23246
24096
|
);
|
|
23247
24097
|
const nodeId = builder.addNode(selected.slug, normalized.bindings);
|
|
23248
|
-
p23.log.success(`Added ${
|
|
24098
|
+
p23.log.success(`Added ${pc37.bold(selected.displayName)} (${pc37.dim(nodeId)})`);
|
|
23249
24099
|
const next = await p23.select({
|
|
23250
24100
|
message: "Pipeline has 1 node. What next?",
|
|
23251
24101
|
options: [
|
|
@@ -23283,7 +24133,7 @@ async function runWorkflowPicker(opts) {
|
|
|
23283
24133
|
throw new Error("Hosted workflow save returned no payload.");
|
|
23284
24134
|
}
|
|
23285
24135
|
p23.log.success(
|
|
23286
|
-
`Hosted workflow saved as ${
|
|
24136
|
+
`Hosted workflow saved as ${pc37.bold(workflowName)} (${pc37.dim(saveResult.workflowId)} \xB7 v${saveResult.version})`
|
|
23287
24137
|
);
|
|
23288
24138
|
}
|
|
23289
24139
|
break;
|
|
@@ -23333,31 +24183,31 @@ async function renderWorkflowIntelligenceSummary(pipeline, capabilities, phase)
|
|
|
23333
24183
|
};
|
|
23334
24184
|
const result = await provider.summarizeExecution(input);
|
|
23335
24185
|
const lines = [
|
|
23336
|
-
`${
|
|
24186
|
+
`${pc37.bold("Intelligence Summary")} ${pc37.dim(result.title)}`,
|
|
23337
24187
|
result.explanation
|
|
23338
24188
|
];
|
|
23339
24189
|
if (result.runtimeModeNote) {
|
|
23340
|
-
lines.push(`${
|
|
24190
|
+
lines.push(`${pc37.dim("Runtime:")} ${result.runtimeModeNote}`);
|
|
23341
24191
|
}
|
|
23342
24192
|
if (result.outputExpectation) {
|
|
23343
|
-
lines.push(`${
|
|
24193
|
+
lines.push(`${pc37.dim("Expected:")} ${result.outputExpectation}`);
|
|
23344
24194
|
}
|
|
23345
24195
|
if (result.missingBindingGuidance.length > 0) {
|
|
23346
|
-
lines.push("",
|
|
24196
|
+
lines.push("", pc37.yellow("Missing Binding Guidance"));
|
|
23347
24197
|
for (const guidance of result.missingBindingGuidance) {
|
|
23348
|
-
lines.push(` ${
|
|
24198
|
+
lines.push(` ${pc37.dim("\xB7")} ${guidance}`);
|
|
23349
24199
|
}
|
|
23350
24200
|
}
|
|
23351
24201
|
if (result.costLatencyCautions.length > 0) {
|
|
23352
|
-
lines.push("",
|
|
24202
|
+
lines.push("", pc37.yellow("Cost/Latency Notes"));
|
|
23353
24203
|
for (const caution of result.costLatencyCautions) {
|
|
23354
|
-
lines.push(` ${
|
|
24204
|
+
lines.push(` ${pc37.dim("\xB7")} ${caution}`);
|
|
23355
24205
|
}
|
|
23356
24206
|
}
|
|
23357
24207
|
if (result.warnings.length > 0) {
|
|
23358
|
-
lines.push("",
|
|
24208
|
+
lines.push("", pc37.yellow("Warnings"));
|
|
23359
24209
|
for (const warning of result.warnings) {
|
|
23360
|
-
lines.push(` ${
|
|
24210
|
+
lines.push(` ${pc37.dim("\xB7")} ${warning}`);
|
|
23361
24211
|
}
|
|
23362
24212
|
}
|
|
23363
24213
|
return lines;
|
|
@@ -23380,7 +24230,7 @@ Examples:
|
|
|
23380
24230
|
wf.command("templates").description("List CMS workflow node starter templates").option("--family <family>", "Filter by family").option("--search <term>", "Search templates").option("--view <mode>", "List view mode: condensed | expanded | tree").option("--json", "Output raw JSON").action(async (opts) => {
|
|
23381
24231
|
const access = getWorkflowAccess();
|
|
23382
24232
|
if (access.state !== "ready") {
|
|
23383
|
-
console.error(
|
|
24233
|
+
console.error(pc37.red(`${access.reason}.`));
|
|
23384
24234
|
process.exitCode = 1;
|
|
23385
24235
|
return;
|
|
23386
24236
|
}
|
|
@@ -23397,24 +24247,24 @@ Examples:
|
|
|
23397
24247
|
return;
|
|
23398
24248
|
}
|
|
23399
24249
|
if (nodes.length === 0) {
|
|
23400
|
-
console.error(
|
|
24250
|
+
console.error(pc37.yellow("No templates found."));
|
|
23401
24251
|
process.exitCode = 1;
|
|
23402
24252
|
return;
|
|
23403
24253
|
}
|
|
23404
24254
|
const viewMode = opts.view ?? "condensed";
|
|
23405
24255
|
console.log("");
|
|
23406
24256
|
console.log(
|
|
23407
|
-
|
|
24257
|
+
pc37.bold("Workflow Node Templates") + pc37.dim(` ${nodes.length} template${nodes.length !== 1 ? "s" : ""}`)
|
|
23408
24258
|
);
|
|
23409
24259
|
console.log(hr7());
|
|
23410
|
-
console.log(
|
|
23411
|
-
console.log(
|
|
23412
|
-
console.log(
|
|
24260
|
+
console.log(pc37.bold("Step 1: CMS Node Contract Validation"));
|
|
24261
|
+
console.log(pc37.dim("Validate contract visibility before template selection."));
|
|
24262
|
+
console.log(pc37.dim(`View mode: ${viewMode}`));
|
|
23413
24263
|
console.log("");
|
|
23414
24264
|
if (viewMode === "tree") {
|
|
23415
24265
|
console.log(box5(renderTemplateTree(nodes)));
|
|
23416
24266
|
console.log(hr7());
|
|
23417
|
-
console.log(
|
|
24267
|
+
console.log(pc37.dim(` Source: ${meta.source} \xB7 growthub workflow`));
|
|
23418
24268
|
console.log("");
|
|
23419
24269
|
return;
|
|
23420
24270
|
}
|
|
@@ -23422,24 +24272,24 @@ Examples:
|
|
|
23422
24272
|
const contract = introspectNodeContract(node);
|
|
23423
24273
|
const requiredInputs = contract.inputs.filter((input) => input.required).length;
|
|
23424
24274
|
const optionalInputs = contract.inputs.length - requiredInputs;
|
|
23425
|
-
const enabledTag = node.enabled ?
|
|
23426
|
-
console.log(` ${node.icon} ${
|
|
24275
|
+
const enabledTag = node.enabled ? pc37.green("enabled") : pc37.red("disabled");
|
|
24276
|
+
console.log(` ${node.icon} ${pc37.bold(node.displayName)} ${pc37.dim(node.slug)} ${enabledTag}`);
|
|
23427
24277
|
console.log(
|
|
23428
|
-
` ${
|
|
24278
|
+
` ${pc37.dim("Contract:")} ${pc37.dim("required")}=${requiredInputs} ${pc37.dim("optional")}=${optionalInputs} ${pc37.dim("bindings")}=${contract.requiredBindings.length} ${pc37.dim("outputs")}=${contract.outputTypes.length}`
|
|
23429
24279
|
);
|
|
23430
24280
|
console.log(
|
|
23431
|
-
` ${
|
|
24281
|
+
` ${pc37.dim("Execution:")} ${contract.executionStrategy} \xB7 ${contract.executionKind}`
|
|
23432
24282
|
);
|
|
23433
24283
|
if (node.description) {
|
|
23434
|
-
console.log(` ${
|
|
24284
|
+
console.log(` ${pc37.dim(node.description)}`);
|
|
23435
24285
|
}
|
|
23436
24286
|
console.log("");
|
|
23437
24287
|
}
|
|
23438
24288
|
console.log(hr7());
|
|
23439
|
-
console.log(
|
|
24289
|
+
console.log(pc37.dim(` Source: ${meta.source} \xB7 growthub workflow`));
|
|
23440
24290
|
console.log("");
|
|
23441
24291
|
} catch (err) {
|
|
23442
|
-
console.error(
|
|
24292
|
+
console.error(pc37.red("Failed: " + err.message));
|
|
23443
24293
|
process.exitCode = 1;
|
|
23444
24294
|
}
|
|
23445
24295
|
});
|
|
@@ -23459,34 +24309,34 @@ Examples:
|
|
|
23459
24309
|
return;
|
|
23460
24310
|
}
|
|
23461
24311
|
if (visibleSaved.length === 0) {
|
|
23462
|
-
console.log(
|
|
24312
|
+
console.log(pc37.dim("No saved workflows. Run `growthub workflow` to assemble one."));
|
|
23463
24313
|
return;
|
|
23464
24314
|
}
|
|
23465
24315
|
console.log("");
|
|
23466
24316
|
console.log(
|
|
23467
|
-
|
|
24317
|
+
pc37.bold("Saved Workflows") + pc37.dim(` ${visibleSaved.length} workflow${visibleSaved.length !== 1 ? "s" : ""}`)
|
|
23468
24318
|
);
|
|
23469
24319
|
if (!opts.includeArchived) {
|
|
23470
24320
|
const hiddenArchivedCount = saved.length - visibleSaved.length;
|
|
23471
24321
|
if (hiddenArchivedCount > 0) {
|
|
23472
|
-
console.log(
|
|
24322
|
+
console.log(pc37.dim(` Archived hidden: ${hiddenArchivedCount} (use --include-archived to show)`));
|
|
23473
24323
|
}
|
|
23474
24324
|
}
|
|
23475
24325
|
console.log(hr7());
|
|
23476
24326
|
for (const w of visibleSaved) {
|
|
23477
24327
|
console.log(
|
|
23478
|
-
` ${
|
|
24328
|
+
` ${pc37.bold(w.name)} ` + pc37.dim(`[${renderWorkflowLabel(w.workflowLabel)}] `) + pc37.dim(`${w.nodeCount} node${w.nodeCount !== 1 ? "s" : ""} \xB7 ${w.executionMode} \xB7 ${w.updatedAt?.slice(0, 10) ?? w.createdAt.slice(0, 10)}`)
|
|
23479
24329
|
);
|
|
23480
24330
|
}
|
|
23481
24331
|
console.log("");
|
|
23482
|
-
console.log(
|
|
24332
|
+
console.log(pc37.dim(` Source: ${visibleSaved[0]?.source === "hosted" ? "hosted workflow registry" : resolveSavedWorkflowsDir()}`));
|
|
23483
24333
|
console.log("");
|
|
23484
24334
|
});
|
|
23485
24335
|
}
|
|
23486
24336
|
|
|
23487
24337
|
// src/commands/open-agents.ts
|
|
23488
24338
|
import * as p24 from "@clack/prompts";
|
|
23489
|
-
import
|
|
24339
|
+
import pc38 from "picocolors";
|
|
23490
24340
|
|
|
23491
24341
|
// src/runtime/agent-harness/auth-store.ts
|
|
23492
24342
|
init_home();
|
|
@@ -23819,49 +24669,49 @@ function validateAuthMode(value) {
|
|
|
23819
24669
|
// src/commands/open-agents.ts
|
|
23820
24670
|
init_banner();
|
|
23821
24671
|
function statusColor2(status) {
|
|
23822
|
-
if (status === "running") return
|
|
23823
|
-
if (status === "completed") return
|
|
23824
|
-
if (status === "failed" || status === "cancelled") return
|
|
23825
|
-
if (status === "waiting" || status === "idle") return
|
|
23826
|
-
return
|
|
24672
|
+
if (status === "running") return pc38.green(status);
|
|
24673
|
+
if (status === "completed") return pc38.cyan(status);
|
|
24674
|
+
if (status === "failed" || status === "cancelled") return pc38.red(status);
|
|
24675
|
+
if (status === "waiting" || status === "idle") return pc38.yellow(status);
|
|
24676
|
+
return pc38.dim(status);
|
|
23827
24677
|
}
|
|
23828
24678
|
function sandboxBadge(state) {
|
|
23829
|
-
if (state === "running") return
|
|
23830
|
-
if (state === "hibernating") return
|
|
23831
|
-
if (state === "stopped") return
|
|
23832
|
-
if (state === "error") return
|
|
23833
|
-
return
|
|
24679
|
+
if (state === "running") return pc38.green("running");
|
|
24680
|
+
if (state === "hibernating") return pc38.yellow("hibernating");
|
|
24681
|
+
if (state === "stopped") return pc38.dim("stopped");
|
|
24682
|
+
if (state === "error") return pc38.red("error");
|
|
24683
|
+
return pc38.dim(state);
|
|
23834
24684
|
}
|
|
23835
24685
|
function hr8(width = 72) {
|
|
23836
|
-
return
|
|
24686
|
+
return pc38.dim("\u2500".repeat(width));
|
|
23837
24687
|
}
|
|
23838
|
-
function
|
|
24688
|
+
function stripAnsi7(str) {
|
|
23839
24689
|
return str.replace(/\x1B\[[0-9;]*m/g, "");
|
|
23840
24690
|
}
|
|
23841
24691
|
function box6(lines) {
|
|
23842
24692
|
const padded = lines.map((l) => " " + l);
|
|
23843
|
-
const width = Math.max(...padded.map((l) =>
|
|
23844
|
-
const top =
|
|
23845
|
-
const bottom =
|
|
24693
|
+
const width = Math.max(...padded.map((l) => stripAnsi7(l).length)) + 4;
|
|
24694
|
+
const top = pc38.dim("\u250C" + "\u2500".repeat(width) + "\u2510");
|
|
24695
|
+
const bottom = pc38.dim("\u2514" + "\u2500".repeat(width) + "\u2518");
|
|
23846
24696
|
const body = padded.map((l) => {
|
|
23847
|
-
const
|
|
23848
|
-
return
|
|
24697
|
+
const pad2 = width - stripAnsi7(l).length;
|
|
24698
|
+
return pc38.dim("\u2502") + l + " ".repeat(pad2) + pc38.dim("\u2502");
|
|
23849
24699
|
});
|
|
23850
24700
|
return [top, ...body, bottom].join("\n");
|
|
23851
24701
|
}
|
|
23852
24702
|
function printSessionCard(session) {
|
|
23853
24703
|
const lines = [
|
|
23854
|
-
`${
|
|
23855
|
-
`${
|
|
23856
|
-
`${
|
|
23857
|
-
`${
|
|
23858
|
-
`${
|
|
24704
|
+
`${pc38.bold("Session")} ${pc38.dim(session.sessionId)}`,
|
|
24705
|
+
`${pc38.dim("Status:")} ${statusColor2(session.status)}`,
|
|
24706
|
+
`${pc38.dim("Sandbox:")} ${sandboxBadge(session.sandboxState)}`,
|
|
24707
|
+
`${pc38.dim("Events:")} ${session.eventCount}`,
|
|
24708
|
+
`${pc38.dim("Created:")} ${session.createdAt}`
|
|
23859
24709
|
];
|
|
23860
|
-
if (session.repoUrl) lines.push(`${
|
|
23861
|
-
if (session.branch) lines.push(`${
|
|
24710
|
+
if (session.repoUrl) lines.push(`${pc38.dim("Repo:")} ${session.repoUrl}`);
|
|
24711
|
+
if (session.branch) lines.push(`${pc38.dim("Branch:")} ${session.branch}`);
|
|
23862
24712
|
if (session.prompt) {
|
|
23863
24713
|
const truncated = session.prompt.length > 80 ? session.prompt.slice(0, 77) + "..." : session.prompt;
|
|
23864
|
-
lines.push(`${
|
|
24714
|
+
lines.push(`${pc38.dim("Prompt:")} ${truncated}`);
|
|
23865
24715
|
}
|
|
23866
24716
|
console.log("");
|
|
23867
24717
|
console.log(box6(lines));
|
|
@@ -23888,12 +24738,12 @@ var EVENT_EMOJI = {
|
|
|
23888
24738
|
};
|
|
23889
24739
|
function printEvent(event) {
|
|
23890
24740
|
const emoji = EVENT_EMOJI[event.type] ?? "\xB7";
|
|
23891
|
-
const ts =
|
|
24741
|
+
const ts = pc38.dim(event.timestamp.split("T")[1]?.slice(0, 8) ?? "");
|
|
23892
24742
|
console.log(` ${emoji} ${ts} ${event.detail}`);
|
|
23893
24743
|
}
|
|
23894
24744
|
async function runOpenAgentsHub(opts) {
|
|
23895
24745
|
printPaperclipCliBanner();
|
|
23896
|
-
p24.intro(
|
|
24746
|
+
p24.intro(pc38.bold("Open Agents"));
|
|
23897
24747
|
while (true) {
|
|
23898
24748
|
const config = readOpenAgentsConfig();
|
|
23899
24749
|
const action = await p24.select({
|
|
@@ -24060,7 +24910,7 @@ async function runSessionListFlow(config) {
|
|
|
24060
24910
|
options: [
|
|
24061
24911
|
...sessions.map((s) => ({
|
|
24062
24912
|
value: s.sessionId,
|
|
24063
|
-
label: `${statusColor2(s.status)} ${
|
|
24913
|
+
label: `${statusColor2(s.status)} ${pc38.dim(s.sessionId.slice(0, 12))}`,
|
|
24064
24914
|
hint: s.prompt ? s.prompt.slice(0, 50) : void 0
|
|
24065
24915
|
})),
|
|
24066
24916
|
{ value: "__back", label: "\u2190 Back" }
|
|
@@ -24085,7 +24935,7 @@ async function runSessionListFlow(config) {
|
|
|
24085
24935
|
p24.note("No events recorded yet.", "Empty");
|
|
24086
24936
|
} else {
|
|
24087
24937
|
console.log("");
|
|
24088
|
-
console.log(
|
|
24938
|
+
console.log(pc38.bold("Recent Events") + pc38.dim(` (${events.length})`));
|
|
24089
24939
|
console.log(hr8());
|
|
24090
24940
|
for (const event of events.slice(-20)) {
|
|
24091
24941
|
printEvent(event);
|
|
@@ -24151,7 +25001,7 @@ async function runResumeSessionFlow(config) {
|
|
|
24151
25001
|
printSessionCard(session);
|
|
24152
25002
|
const events = await pollSessionEvents(config, session.sessionId);
|
|
24153
25003
|
if (events.length > 0) {
|
|
24154
|
-
console.log(
|
|
25004
|
+
console.log(pc38.bold("Latest Events") + pc38.dim(` (${events.length})`));
|
|
24155
25005
|
console.log(hr8());
|
|
24156
25006
|
for (const event of events.slice(-20)) {
|
|
24157
25007
|
printEvent(event);
|
|
@@ -24198,7 +25048,7 @@ Examples:
|
|
|
24198
25048
|
if (opts.json) {
|
|
24199
25049
|
console.log(JSON.stringify(updated, null, 2));
|
|
24200
25050
|
} else {
|
|
24201
|
-
console.log(
|
|
25051
|
+
console.log(pc38.green("Configuration updated."));
|
|
24202
25052
|
}
|
|
24203
25053
|
return;
|
|
24204
25054
|
}
|
|
@@ -24207,15 +25057,15 @@ Examples:
|
|
|
24207
25057
|
return;
|
|
24208
25058
|
}
|
|
24209
25059
|
console.log("");
|
|
24210
|
-
console.log(
|
|
25060
|
+
console.log(pc38.bold("Open Agents Configuration"));
|
|
24211
25061
|
console.log(hr8());
|
|
24212
|
-
console.log(` ${
|
|
24213
|
-
console.log(` ${
|
|
24214
|
-
console.log(` ${
|
|
24215
|
-
console.log(` ${
|
|
24216
|
-
console.log(` ${
|
|
24217
|
-
console.log(` ${
|
|
24218
|
-
console.log(` ${
|
|
25062
|
+
console.log(` ${pc38.dim("Backend:")} ${config.backendType}`);
|
|
25063
|
+
console.log(` ${pc38.dim("Auth Mode:")} ${config.authMode ?? "none"}`);
|
|
25064
|
+
console.log(` ${pc38.dim("Endpoint:")} ${config.endpoint}`);
|
|
25065
|
+
console.log(` ${pc38.dim("API Key:")} ${config.apiKey ? maskSecret(config.apiKey) : pc38.dim("(none)")}`);
|
|
25066
|
+
console.log(` ${pc38.dim("Repo:")} ${config.defaultRepo ?? pc38.dim("(none)")}`);
|
|
25067
|
+
console.log(` ${pc38.dim("Branch:")} ${config.defaultBranch ?? pc38.dim("(none)")}`);
|
|
25068
|
+
console.log(` ${pc38.dim("Timeout:")} ${config.timeoutMs ?? 3e4}ms`);
|
|
24219
25069
|
console.log(hr8());
|
|
24220
25070
|
console.log("");
|
|
24221
25071
|
});
|
|
@@ -24228,12 +25078,12 @@ Examples:
|
|
|
24228
25078
|
}
|
|
24229
25079
|
if (health.available) {
|
|
24230
25080
|
console.log(
|
|
24231
|
-
|
|
25081
|
+
pc38.green("\u2713") + ` Backend reachable at ${config.endpoint} (${health.latencyMs}ms)` + (health.version ? ` version: ${health.version}` : "")
|
|
24232
25082
|
);
|
|
24233
25083
|
} else {
|
|
24234
|
-
console.log(
|
|
25084
|
+
console.log(pc38.red("\u2717") + ` Backend unavailable at ${config.endpoint} (${health.latencyMs}ms)`);
|
|
24235
25085
|
if (health.error) {
|
|
24236
|
-
console.log(
|
|
25086
|
+
console.log(pc38.dim(` ${health.error}`));
|
|
24237
25087
|
}
|
|
24238
25088
|
process.exitCode = 1;
|
|
24239
25089
|
}
|
|
@@ -24247,22 +25097,22 @@ Examples:
|
|
|
24247
25097
|
return;
|
|
24248
25098
|
}
|
|
24249
25099
|
if (sessions.length === 0) {
|
|
24250
|
-
console.log(
|
|
25100
|
+
console.log(pc38.yellow("No sessions found.") + pc38.dim(" Run `growthub open-agents create` to start one."));
|
|
24251
25101
|
return;
|
|
24252
25102
|
}
|
|
24253
25103
|
console.log("");
|
|
24254
|
-
console.log(
|
|
25104
|
+
console.log(pc38.bold("Agent Sessions") + pc38.dim(` (${sessions.length})`));
|
|
24255
25105
|
console.log(hr8());
|
|
24256
25106
|
for (const session of sessions) {
|
|
24257
|
-
const truncatedPrompt = session.prompt ?
|
|
25107
|
+
const truncatedPrompt = session.prompt ? pc38.dim(session.prompt.slice(0, 50)) : "";
|
|
24258
25108
|
console.log(
|
|
24259
|
-
` ${statusColor2(session.status)} ${
|
|
25109
|
+
` ${statusColor2(session.status)} ${pc38.dim(session.sessionId.slice(0, 12))} ${sandboxBadge(session.sandboxState)} ${truncatedPrompt}`
|
|
24260
25110
|
);
|
|
24261
25111
|
}
|
|
24262
25112
|
console.log(hr8());
|
|
24263
25113
|
console.log("");
|
|
24264
25114
|
} catch (err) {
|
|
24265
|
-
console.error(
|
|
25115
|
+
console.error(pc38.red("Failed to list sessions: " + err.message));
|
|
24266
25116
|
process.exitCode = 1;
|
|
24267
25117
|
}
|
|
24268
25118
|
});
|
|
@@ -24284,7 +25134,7 @@ Examples:
|
|
|
24284
25134
|
}
|
|
24285
25135
|
printSessionCard(session);
|
|
24286
25136
|
} catch (err) {
|
|
24287
|
-
console.error(
|
|
25137
|
+
console.error(pc38.red("Failed to create session: " + err.message));
|
|
24288
25138
|
process.exitCode = 1;
|
|
24289
25139
|
}
|
|
24290
25140
|
});
|
|
@@ -24302,7 +25152,7 @@ Examples:
|
|
|
24302
25152
|
}
|
|
24303
25153
|
printSessionCard(session);
|
|
24304
25154
|
} catch (err) {
|
|
24305
|
-
console.error(
|
|
25155
|
+
console.error(pc38.red("Failed to create session: " + err.message));
|
|
24306
25156
|
process.exitCode = 1;
|
|
24307
25157
|
}
|
|
24308
25158
|
});
|
|
@@ -24317,7 +25167,7 @@ Examples:
|
|
|
24317
25167
|
printSessionCard(session);
|
|
24318
25168
|
const events = await pollSessionEvents(config, session.sessionId);
|
|
24319
25169
|
if (events.length > 0) {
|
|
24320
|
-
console.log(
|
|
25170
|
+
console.log(pc38.bold("Latest Events") + pc38.dim(` (${events.length})`));
|
|
24321
25171
|
console.log(hr8());
|
|
24322
25172
|
for (const event of events.slice(-20)) {
|
|
24323
25173
|
printEvent(event);
|
|
@@ -24326,7 +25176,7 @@ Examples:
|
|
|
24326
25176
|
console.log("");
|
|
24327
25177
|
}
|
|
24328
25178
|
} catch (err) {
|
|
24329
|
-
console.error(
|
|
25179
|
+
console.error(pc38.red("Failed to resume session: " + err.message));
|
|
24330
25180
|
process.exitCode = 1;
|
|
24331
25181
|
}
|
|
24332
25182
|
});
|
|
@@ -24341,7 +25191,7 @@ Examples:
|
|
|
24341
25191
|
printSessionCard(session);
|
|
24342
25192
|
const events = await pollSessionEvents(config, session.sessionId);
|
|
24343
25193
|
if (events.length > 0) {
|
|
24344
|
-
console.log(
|
|
25194
|
+
console.log(pc38.bold("Latest Events") + pc38.dim(` (${events.length})`));
|
|
24345
25195
|
console.log(hr8());
|
|
24346
25196
|
for (const event of events.slice(-20)) {
|
|
24347
25197
|
printEvent(event);
|
|
@@ -24350,7 +25200,7 @@ Examples:
|
|
|
24350
25200
|
console.log("");
|
|
24351
25201
|
}
|
|
24352
25202
|
} catch (err) {
|
|
24353
|
-
console.error(
|
|
25203
|
+
console.error(pc38.red("Failed to chat/resume session: " + err.message));
|
|
24354
25204
|
process.exitCode = 1;
|
|
24355
25205
|
}
|
|
24356
25206
|
});
|
|
@@ -24358,7 +25208,7 @@ Examples:
|
|
|
24358
25208
|
|
|
24359
25209
|
// src/commands/qwen-code.ts
|
|
24360
25210
|
import * as p25 from "@clack/prompts";
|
|
24361
|
-
import
|
|
25211
|
+
import pc39 from "picocolors";
|
|
24362
25212
|
|
|
24363
25213
|
// src/runtime/qwen-code/index.ts
|
|
24364
25214
|
init_home();
|
|
@@ -24653,7 +25503,7 @@ async function runQwenCodeHub(opts) {
|
|
|
24653
25503
|
while (true) {
|
|
24654
25504
|
const config = readQwenCodeConfig();
|
|
24655
25505
|
const health = checkHealth(config.binaryPath, config.env);
|
|
24656
|
-
const statusHint = health.status === "available" ?
|
|
25506
|
+
const statusHint = health.status === "available" ? pc39.green("ready") : health.status === "degraded" ? pc39.yellow("degraded") : pc39.red("unavailable");
|
|
24657
25507
|
const action = await p25.select({
|
|
24658
25508
|
message: `Qwen Code CLI (${statusHint})`,
|
|
24659
25509
|
options: [
|
|
@@ -24877,7 +25727,7 @@ init_github();
|
|
|
24877
25727
|
// src/commands/integrations.ts
|
|
24878
25728
|
init_bridge();
|
|
24879
25729
|
import * as p27 from "@clack/prompts";
|
|
24880
|
-
import
|
|
25730
|
+
import pc41 from "picocolors";
|
|
24881
25731
|
async function integrationsStatus(opts = {}) {
|
|
24882
25732
|
const status = await describeIntegrationBridge();
|
|
24883
25733
|
if (opts.json) {
|
|
@@ -24888,7 +25738,7 @@ async function integrationsStatus(opts = {}) {
|
|
|
24888
25738
|
p27.log.warn(status.notice ?? "Not logged into Growthub.");
|
|
24889
25739
|
return;
|
|
24890
25740
|
}
|
|
24891
|
-
p27.log.message(`Growthub: ${
|
|
25741
|
+
p27.log.message(`Growthub: ${pc41.green("connected")} as ${status.growthubLogin ?? "?"}`);
|
|
24892
25742
|
if (!status.bridgeAvailable) {
|
|
24893
25743
|
p27.log.info(status.notice ?? "Hosted integrations endpoint not available.");
|
|
24894
25744
|
return;
|
|
@@ -24898,9 +25748,9 @@ async function integrationsStatus(opts = {}) {
|
|
|
24898
25748
|
return;
|
|
24899
25749
|
}
|
|
24900
25750
|
for (const i of status.integrations) {
|
|
24901
|
-
const ready = i.ready ?
|
|
25751
|
+
const ready = i.ready ? pc41.green("ready") : pc41.yellow("reauth needed");
|
|
24902
25752
|
p27.log.message(
|
|
24903
|
-
` \u2022 ${
|
|
25753
|
+
` \u2022 ${pc41.cyan(i.provider)} ${ready} handle=${i.handle ?? "?"} scopes=[${(i.scopes ?? []).join(", ")}]`
|
|
24904
25754
|
);
|
|
24905
25755
|
}
|
|
24906
25756
|
}
|
|
@@ -24915,7 +25765,7 @@ async function integrationsList(opts = {}) {
|
|
|
24915
25765
|
return;
|
|
24916
25766
|
}
|
|
24917
25767
|
for (const i of integrations) {
|
|
24918
|
-
p27.log.message(`${
|
|
25768
|
+
p27.log.message(`${pc41.cyan(i.provider)} ${i.handle ?? ""} (ready=${i.ready})`);
|
|
24919
25769
|
}
|
|
24920
25770
|
}
|
|
24921
25771
|
async function integrationsProbe(opts) {
|
|
@@ -24938,7 +25788,7 @@ async function integrationsProbe(opts) {
|
|
|
24938
25788
|
return;
|
|
24939
25789
|
}
|
|
24940
25790
|
p27.log.success(
|
|
24941
|
-
`Resolved ${
|
|
25791
|
+
`Resolved ${pc41.cyan(opts.provider)} credential via ${cred.source} handle=${cred.handle ?? "?"} scopes=[${(cred.scopes ?? []).join(", ")}]`
|
|
24942
25792
|
);
|
|
24943
25793
|
}
|
|
24944
25794
|
function registerIntegrationsCommands(program2) {
|
|
@@ -24956,7 +25806,7 @@ function registerIntegrationsCommands(program2) {
|
|
|
24956
25806
|
|
|
24957
25807
|
// src/commands/status.ts
|
|
24958
25808
|
import * as p28 from "@clack/prompts";
|
|
24959
|
-
import
|
|
25809
|
+
import pc42 from "picocolors";
|
|
24960
25810
|
|
|
24961
25811
|
// src/status/probes.ts
|
|
24962
25812
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
@@ -25389,25 +26239,25 @@ async function runStatuspageReport(opts = {}) {
|
|
|
25389
26239
|
function levelGlyph(level) {
|
|
25390
26240
|
switch (level) {
|
|
25391
26241
|
case "operational":
|
|
25392
|
-
return
|
|
26242
|
+
return pc42.green("\u25CF");
|
|
25393
26243
|
case "degraded":
|
|
25394
|
-
return
|
|
26244
|
+
return pc42.yellow("\u25CF");
|
|
25395
26245
|
case "outage":
|
|
25396
|
-
return
|
|
26246
|
+
return pc42.red("\u25CF");
|
|
25397
26247
|
default:
|
|
25398
|
-
return
|
|
26248
|
+
return pc42.dim("\u25CB");
|
|
25399
26249
|
}
|
|
25400
26250
|
}
|
|
25401
26251
|
function overallBanner(report) {
|
|
25402
26252
|
switch (report.overallLevel) {
|
|
25403
26253
|
case "operational":
|
|
25404
|
-
return
|
|
26254
|
+
return pc42.green("\u2713 All systems operational");
|
|
25405
26255
|
case "degraded":
|
|
25406
|
-
return
|
|
26256
|
+
return pc42.yellow("\u26A0 Degraded \u2014 non-critical issues detected");
|
|
25407
26257
|
case "outage":
|
|
25408
|
-
return
|
|
26258
|
+
return pc42.red("\u2717 Outage \u2014 at least one critical component is down");
|
|
25409
26259
|
default:
|
|
25410
|
-
return
|
|
26260
|
+
return pc42.dim("? Status indeterminate");
|
|
25411
26261
|
}
|
|
25412
26262
|
}
|
|
25413
26263
|
function renderHuman(report) {
|
|
@@ -25417,14 +26267,14 @@ function renderHuman(report) {
|
|
|
25417
26267
|
bucket.push(c);
|
|
25418
26268
|
byCategory.set(c.category, bucket);
|
|
25419
26269
|
}
|
|
25420
|
-
p28.log.message(`${overallBanner(report)} ${
|
|
26270
|
+
p28.log.message(`${overallBanner(report)} ${pc42.dim(`(${report.summary})`)}`);
|
|
25421
26271
|
for (const [category, list] of byCategory) {
|
|
25422
|
-
p28.log.message(
|
|
26272
|
+
p28.log.message(pc42.cyan(`
|
|
25423
26273
|
${category}`));
|
|
25424
26274
|
for (const c of list) {
|
|
25425
|
-
const crit = c.critical ?
|
|
25426
|
-
const lat = c.latencyMs !== void 0 ?
|
|
25427
|
-
const sa = c.superAdminOnly ?
|
|
26275
|
+
const crit = c.critical ? pc42.red("!") : pc42.dim("\xB7");
|
|
26276
|
+
const lat = c.latencyMs !== void 0 ? pc42.dim(` ${c.latencyMs}ms`) : "";
|
|
26277
|
+
const sa = c.superAdminOnly ? pc42.magenta(" [super-admin]") : "";
|
|
25428
26278
|
p28.log.message(` ${levelGlyph(c.level)} ${crit} ${c.label.padEnd(30)} ${c.summary}${lat}${sa}`);
|
|
25429
26279
|
}
|
|
25430
26280
|
}
|
|
@@ -25452,7 +26302,7 @@ function registerStatusCommands(program2) {
|
|
|
25452
26302
|
|
|
25453
26303
|
// src/commands/starter.ts
|
|
25454
26304
|
import * as p29 from "@clack/prompts";
|
|
25455
|
-
import
|
|
26305
|
+
import pc43 from "picocolors";
|
|
25456
26306
|
|
|
25457
26307
|
// src/starter/init.ts
|
|
25458
26308
|
init_service();
|
|
@@ -25552,14 +26402,14 @@ async function runStarterInit(opts) {
|
|
|
25552
26402
|
return;
|
|
25553
26403
|
}
|
|
25554
26404
|
p29.outro(
|
|
25555
|
-
`Workspace scaffolded at ${
|
|
26405
|
+
`Workspace scaffolded at ${pc43.cyan(result.forkPath)}
|
|
25556
26406
|
kitId: ${result.kitId}
|
|
25557
|
-
forkId: ${
|
|
26407
|
+
forkId: ${pc43.cyan(result.forkId)}
|
|
25558
26408
|
baseVersion: ${result.baseVersion}
|
|
25559
26409
|
policyMode: remoteSyncMode=${result.policyMode}` + (result.remote ? `
|
|
25560
|
-
remote: ${
|
|
26410
|
+
remote: ${pc43.cyan(result.remote.htmlUrl)}` : "") + `
|
|
25561
26411
|
|
|
25562
|
-
Next: ${
|
|
26412
|
+
Next: ${pc43.dim(`growthub kit fork status ${result.forkId}`)}`
|
|
25563
26413
|
);
|
|
25564
26414
|
} catch (err) {
|
|
25565
26415
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -25590,7 +26440,7 @@ function registerStarterCommands(program2) {
|
|
|
25590
26440
|
|
|
25591
26441
|
// src/commands/fleet.ts
|
|
25592
26442
|
import * as p30 from "@clack/prompts";
|
|
25593
|
-
import
|
|
26443
|
+
import pc44 from "picocolors";
|
|
25594
26444
|
|
|
25595
26445
|
// src/fleet/summary.ts
|
|
25596
26446
|
import fs37 from "node:fs";
|
|
@@ -25915,20 +26765,20 @@ function buildAgentHealPlanDocument(reg, opts = {}) {
|
|
|
25915
26765
|
function healthGlyph(level) {
|
|
25916
26766
|
switch (level) {
|
|
25917
26767
|
case "clean":
|
|
25918
|
-
return
|
|
26768
|
+
return pc44.green("\u25CF");
|
|
25919
26769
|
case "drift-minor":
|
|
25920
|
-
return
|
|
26770
|
+
return pc44.cyan("\u25CF");
|
|
25921
26771
|
case "drift-major":
|
|
25922
|
-
return
|
|
26772
|
+
return pc44.yellow("\u25CF");
|
|
25923
26773
|
case "awaiting-confirmation":
|
|
25924
|
-
return
|
|
26774
|
+
return pc44.magenta("\u25D0");
|
|
25925
26775
|
case "error":
|
|
25926
|
-
return
|
|
26776
|
+
return pc44.red("\u25CF");
|
|
25927
26777
|
default:
|
|
25928
|
-
return
|
|
26778
|
+
return pc44.dim("\u25CB");
|
|
25929
26779
|
}
|
|
25930
26780
|
}
|
|
25931
|
-
function
|
|
26781
|
+
function truncate4(s, n) {
|
|
25932
26782
|
if (!s) return "";
|
|
25933
26783
|
return s.length <= n ? s : s.slice(0, n - 1) + "\u2026";
|
|
25934
26784
|
}
|
|
@@ -25939,7 +26789,7 @@ async function fleetView(opts) {
|
|
|
25939
26789
|
return;
|
|
25940
26790
|
}
|
|
25941
26791
|
p30.log.message(
|
|
25942
|
-
`Fleet: ${
|
|
26792
|
+
`Fleet: ${pc44.cyan(String(fleet.totalForks))} fork(s) | remote=${fleet.forksWithRemote} awaiting=${fleet.forksAwaitingConfirmation} pending-approvals=${fleet.pendingApprovalCount}`
|
|
25943
26793
|
);
|
|
25944
26794
|
p30.log.message(
|
|
25945
26795
|
` Health \u2192 clean=${fleet.byHealth.clean} drift-minor=${fleet.byHealth["drift-minor"]} drift-major=${fleet.byHealth["drift-major"]} awaiting=${fleet.byHealth["awaiting-confirmation"]} error=${fleet.byHealth.error} unknown=${fleet.byHealth.unknown}`
|
|
@@ -25951,15 +26801,15 @@ async function fleetView(opts) {
|
|
|
25951
26801
|
for (const f of fleet.forks) renderForkRow(f);
|
|
25952
26802
|
}
|
|
25953
26803
|
function renderForkRow(f) {
|
|
25954
|
-
const label =
|
|
25955
|
-
const kit =
|
|
26804
|
+
const label = truncate4(f.label ?? f.forkId, 28).padEnd(28);
|
|
26805
|
+
const kit = truncate4(f.kitId, 34).padEnd(34);
|
|
25956
26806
|
const base = f.baseVersion.padEnd(8);
|
|
25957
26807
|
const upstream = (f.upstreamVersion ?? "?").padEnd(8);
|
|
25958
26808
|
const driftCounts = `files=${f.fileDriftCount} pkgs=${f.packageDriftCount}`;
|
|
25959
|
-
const pending = f.pendingConfirmationJobs > 0 ?
|
|
25960
|
-
const remote = f.remote ?
|
|
26809
|
+
const pending = f.pendingConfirmationJobs > 0 ? pc44.magenta(` awaits=${f.pendingConfirmationJobs}`) : "";
|
|
26810
|
+
const remote = f.remote ? pc44.dim(` ${f.remote.owner}/${f.remote.repo}`) : "";
|
|
25961
26811
|
p30.log.message(
|
|
25962
|
-
` ${healthGlyph(f.health)} ${label} ${
|
|
26812
|
+
` ${healthGlyph(f.health)} ${label} ${pc44.dim(kit)} ${base} \u2192 ${upstream} ${pc44.dim(driftCounts)}${pending}${remote}`
|
|
25963
26813
|
);
|
|
25964
26814
|
}
|
|
25965
26815
|
async function fleetDrift(opts) {
|
|
@@ -25974,7 +26824,7 @@ async function fleetDrift(opts) {
|
|
|
25974
26824
|
return;
|
|
25975
26825
|
}
|
|
25976
26826
|
p30.log.message(
|
|
25977
|
-
`Fleet drift: ${
|
|
26827
|
+
`Fleet drift: ${pc44.cyan(String(withDrift.length))} of ${fleet.totalForks} fork(s) have drift.`
|
|
25978
26828
|
);
|
|
25979
26829
|
p30.log.message(
|
|
25980
26830
|
` By severity \u2192 none=${fleet.bySeverity.none} info=${fleet.bySeverity.info} warning=${fleet.bySeverity.warning} critical=${fleet.bySeverity.critical}`
|
|
@@ -25994,7 +26844,7 @@ async function fleetDriftSummary(opts) {
|
|
|
25994
26844
|
console.log(JSON.stringify({ summary, narrative }, null, 2));
|
|
25995
26845
|
return;
|
|
25996
26846
|
}
|
|
25997
|
-
p30.log.message(
|
|
26847
|
+
p30.log.message(pc44.cyan(`Drift summary \u2014 ${reg.forkId} (${summary.fromVersion} \u2192 ${summary.toVersion})`));
|
|
25998
26848
|
for (const line of narrative) p30.log.message(` ${line}`);
|
|
25999
26849
|
const sections = [
|
|
26000
26850
|
["safe additions", summary.buckets.safeAdditions],
|
|
@@ -26007,13 +26857,13 @@ async function fleetDriftSummary(opts) {
|
|
|
26007
26857
|
];
|
|
26008
26858
|
for (const [label, items] of sections) {
|
|
26009
26859
|
if (items.length === 0) continue;
|
|
26010
|
-
p30.log.message(
|
|
26011
|
-
for (const item of items) p30.log.message(` \xB7 ${item.path} ${
|
|
26860
|
+
p30.log.message(pc44.dim(` \u2014 ${label} (${items.length}) \u2014`));
|
|
26861
|
+
for (const item of items) p30.log.message(` \xB7 ${item.path} ${pc44.dim(item.note)}`);
|
|
26012
26862
|
}
|
|
26013
26863
|
if (summary.buckets.packageAdditions.length || summary.buckets.packageUpgrades.length) {
|
|
26014
|
-
p30.log.message(
|
|
26864
|
+
p30.log.message(pc44.dim(` \u2014 dependency drift \u2014`));
|
|
26015
26865
|
for (const d of summary.buckets.packageAdditions) {
|
|
26016
|
-
p30.log.message(` + ${d.packageName}@${d.toVersion} ${
|
|
26866
|
+
p30.log.message(` + ${d.packageName}@${d.toVersion} ${pc44.dim("(added upstream)")}`);
|
|
26017
26867
|
}
|
|
26018
26868
|
for (const d of summary.buckets.packageUpgrades) {
|
|
26019
26869
|
p30.log.message(` \u2191 ${d.packageName} ${d.fromVersion ?? "?"} \u2192 ${d.toVersion}`);
|
|
@@ -26039,14 +26889,14 @@ async function fleetPolicy(opts) {
|
|
|
26039
26889
|
console.log(JSON.stringify({ count: rows.length, rows }, null, 2));
|
|
26040
26890
|
return;
|
|
26041
26891
|
}
|
|
26042
|
-
p30.log.message(
|
|
26892
|
+
p30.log.message(pc44.cyan(`Fleet policy matrix (${rows.length} fork(s))`));
|
|
26043
26893
|
for (const r of rows) {
|
|
26044
|
-
const label =
|
|
26894
|
+
const label = truncate4(r.label ?? r.forkId, 28).padEnd(28);
|
|
26045
26895
|
const aa = r.autoApprove.padEnd(9);
|
|
26046
26896
|
const ad = r.autoApproveDepUpdates.padEnd(9);
|
|
26047
26897
|
const rs = r.remoteSyncMode.padEnd(6);
|
|
26048
26898
|
const ut = String(r.untouchableCount).padStart(3);
|
|
26049
|
-
const remote = r.hasRemote ?
|
|
26899
|
+
const remote = r.hasRemote ? pc44.green("+") : pc44.dim("\xB7");
|
|
26050
26900
|
p30.log.message(
|
|
26051
26901
|
` ${label} autoApprove=${aa} deps=${ad} remote=${rs} untouchable=${ut} ${remote}`
|
|
26052
26902
|
);
|
|
@@ -26062,19 +26912,19 @@ async function fleetApprovals(opts) {
|
|
|
26062
26912
|
p30.log.success("Approval queue is empty.");
|
|
26063
26913
|
return;
|
|
26064
26914
|
}
|
|
26065
|
-
p30.log.message(
|
|
26915
|
+
p30.log.message(pc44.cyan(`Approval queue: ${queue.length} job(s) awaiting confirmation`));
|
|
26066
26916
|
for (const entry of queue) {
|
|
26067
26917
|
p30.log.message(
|
|
26068
|
-
` \xB7 ${
|
|
26918
|
+
` \xB7 ${pc44.cyan(entry.jobId)} fork=${entry.forkLabel ?? entry.forkId} created=${entry.createdAt.slice(0, 19)}`
|
|
26069
26919
|
);
|
|
26070
26920
|
for (const path47 of entry.pendingPaths.slice(0, 6)) {
|
|
26071
|
-
p30.log.message(` ${
|
|
26921
|
+
p30.log.message(` ${pc44.dim("awaits")} ${path47}`);
|
|
26072
26922
|
}
|
|
26073
26923
|
if (entry.pendingPaths.length > 6) {
|
|
26074
|
-
p30.log.message(` ${
|
|
26924
|
+
p30.log.message(` ${pc44.dim(`\u2026 +${entry.pendingPaths.length - 6} more`)}`);
|
|
26075
26925
|
}
|
|
26076
26926
|
p30.log.message(
|
|
26077
|
-
` ${
|
|
26927
|
+
` ${pc44.dim("resume:")} growthub kit fork confirm --job-id ${entry.jobId}`
|
|
26078
26928
|
);
|
|
26079
26929
|
}
|
|
26080
26930
|
}
|
|
@@ -26086,20 +26936,20 @@ async function fleetAgentPlan(opts) {
|
|
|
26086
26936
|
console.log(JSON.stringify(doc, null, 2));
|
|
26087
26937
|
return;
|
|
26088
26938
|
}
|
|
26089
|
-
p30.log.message(
|
|
26939
|
+
p30.log.message(pc44.cyan(`Agent heal plan \u2014 ${reg.forkId}`));
|
|
26090
26940
|
p30.log.message(` ${doc.summary}`);
|
|
26091
26941
|
for (const line of doc.narrative) p30.log.message(` ${line}`);
|
|
26092
26942
|
if (doc.awaitsConfirmation.length > 0) {
|
|
26093
|
-
p30.log.message(
|
|
26943
|
+
p30.log.message(pc44.magenta(` Awaiting confirmation on:`));
|
|
26094
26944
|
for (const p210 of doc.awaitsConfirmation) p30.log.message(` \xB7 ${p210}`);
|
|
26095
26945
|
p30.log.message(
|
|
26096
|
-
|
|
26946
|
+
pc44.dim(
|
|
26097
26947
|
` Next: growthub kit fork heal ${reg.forkId} (will park in awaiting_confirmation until resumed)`
|
|
26098
26948
|
)
|
|
26099
26949
|
);
|
|
26100
26950
|
} else if (doc.plan.actions.length > 0) {
|
|
26101
26951
|
p30.log.message(
|
|
26102
|
-
|
|
26952
|
+
pc44.dim(` Next: growthub kit fork heal ${reg.forkId} (${doc.plan.actions.length} safe action(s) ready)`)
|
|
26103
26953
|
);
|
|
26104
26954
|
}
|
|
26105
26955
|
}
|
|
@@ -26641,7 +27491,7 @@ async function runDiscoveryHub(opts) {
|
|
|
26641
27491
|
},
|
|
26642
27492
|
{
|
|
26643
27493
|
value: "workflows",
|
|
26644
|
-
label: workflowAccess.state === "ready" ? "\u{1F517} Workflows" : "\u{1F517} Workflows" +
|
|
27494
|
+
label: workflowAccess.state === "ready" ? "\u{1F517} Workflows" : "\u{1F517} Workflows" + pc45.dim(" (locked)"),
|
|
26645
27495
|
hint: workflowAccess.state === "ready" ? "CMS contracts, dynamic pipelines, and saved workflows" : workflowAccess.reason
|
|
26646
27496
|
},
|
|
26647
27497
|
{
|