hubify 1.0.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +193 -75
- package/dist/index.js +2869 -519
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
} from "./chunk-FW7BS3MW.js";
|
|
8
8
|
|
|
9
9
|
// src/index.ts
|
|
10
|
-
import { Command as
|
|
11
|
-
import
|
|
12
|
-
import
|
|
10
|
+
import { Command as Command58 } from "commander";
|
|
11
|
+
import chalk61 from "chalk";
|
|
12
|
+
import fs28 from "fs";
|
|
13
13
|
|
|
14
14
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
15
15
|
var external_exports = {};
|
|
@@ -489,8 +489,8 @@ function getErrorMap() {
|
|
|
489
489
|
|
|
490
490
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
491
491
|
var makeIssue = (params) => {
|
|
492
|
-
const { data, path:
|
|
493
|
-
const fullPath = [...
|
|
492
|
+
const { data, path: path27, errorMaps, issueData } = params;
|
|
493
|
+
const fullPath = [...path27, ...issueData.path || []];
|
|
494
494
|
const fullIssue = {
|
|
495
495
|
...issueData,
|
|
496
496
|
path: fullPath
|
|
@@ -606,11 +606,11 @@ var errorUtil;
|
|
|
606
606
|
|
|
607
607
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js
|
|
608
608
|
var ParseInputLazyPath = class {
|
|
609
|
-
constructor(parent, value,
|
|
609
|
+
constructor(parent, value, path27, key) {
|
|
610
610
|
this._cachedPath = [];
|
|
611
611
|
this.parent = parent;
|
|
612
612
|
this.data = value;
|
|
613
|
-
this._path =
|
|
613
|
+
this._path = path27;
|
|
614
614
|
this._key = key;
|
|
615
615
|
}
|
|
616
616
|
get path() {
|
|
@@ -4410,7 +4410,7 @@ var rl = readline.createInterface({
|
|
|
4410
4410
|
input: process.stdin,
|
|
4411
4411
|
output: process.stdout
|
|
4412
4412
|
});
|
|
4413
|
-
var question = (prompt) => new Promise((
|
|
4413
|
+
var question = (prompt) => new Promise((resolve12) => rl.question(prompt, resolve12));
|
|
4414
4414
|
var initCommand = new Command("init").description("Initialize Hubify in the current project or globally").option("--agent-id <id>", "Set agent ID").option("--platform <name>", "Set platform (claude-code, cursor, etc.)").option("--global", "Initialize global config (~/.hubify)").option("--non-interactive", "Skip prompts, use defaults").action(async (options) => {
|
|
4415
4415
|
const spinner = ora();
|
|
4416
4416
|
const isGlobal = options.global;
|
|
@@ -5806,15 +5806,15 @@ var challengesCommand = new Command5("challenges").description("View and respond
|
|
|
5806
5806
|
var respondCommand = new Command5("respond").description("Respond to a verification challenge").argument("<challenge-id>", "Challenge ID to respond to").option("--agent-id <id>", "Agent ID (default: cli-user)").option("--output <text>", "Full output from re-execution").option("--output-file <path>", "Read output from file").action(async (challengeId, options) => {
|
|
5807
5807
|
const spinner = ora5();
|
|
5808
5808
|
const client = getClient();
|
|
5809
|
-
const
|
|
5809
|
+
const fs29 = await import("fs");
|
|
5810
5810
|
const resolvedAgentId = options.agentId || process.env.HUBIFY_AGENT_ID || "cli-user";
|
|
5811
5811
|
try {
|
|
5812
5812
|
let output = options.output;
|
|
5813
5813
|
if (options.outputFile) {
|
|
5814
|
-
if (!
|
|
5814
|
+
if (!fs29.existsSync(options.outputFile)) {
|
|
5815
5815
|
throw new Error(`Output file not found: ${options.outputFile}`);
|
|
5816
5816
|
}
|
|
5817
|
-
output =
|
|
5817
|
+
output = fs29.readFileSync(options.outputFile, "utf-8");
|
|
5818
5818
|
}
|
|
5819
5819
|
if (!output) {
|
|
5820
5820
|
throw new Error("Must provide --output or --output-file");
|
|
@@ -5923,11 +5923,11 @@ function formatVerificationLevel(level) {
|
|
|
5923
5923
|
}
|
|
5924
5924
|
async function readStdinJson() {
|
|
5925
5925
|
if (process.stdin.isTTY) return {};
|
|
5926
|
-
const raw = await new Promise((
|
|
5926
|
+
const raw = await new Promise((resolve12, reject) => {
|
|
5927
5927
|
let data = "";
|
|
5928
5928
|
process.stdin.setEncoding("utf8");
|
|
5929
5929
|
process.stdin.on("data", (chunk) => data += chunk);
|
|
5930
|
-
process.stdin.on("end", () =>
|
|
5930
|
+
process.stdin.on("end", () => resolve12(data));
|
|
5931
5931
|
process.stdin.on("error", reject);
|
|
5932
5932
|
});
|
|
5933
5933
|
const trimmed = raw.trim();
|
|
@@ -5974,7 +5974,7 @@ Skill not found: ${skill}
|
|
|
5974
5974
|
\u{1F504} Checking ${skillsToUpdate.length} skill(s) for updates...
|
|
5975
5975
|
`));
|
|
5976
5976
|
spinner.start("Checking for updates...");
|
|
5977
|
-
await new Promise((
|
|
5977
|
+
await new Promise((resolve12) => setTimeout(resolve12, 500));
|
|
5978
5978
|
spinner.stop();
|
|
5979
5979
|
const updates = skillsToUpdate.map((name) => ({
|
|
5980
5980
|
name,
|
|
@@ -6002,7 +6002,7 @@ Skill not found: ${skill}
|
|
|
6002
6002
|
}
|
|
6003
6003
|
spinner.start("Applying updates...");
|
|
6004
6004
|
for (const update of availableUpdates) {
|
|
6005
|
-
await new Promise((
|
|
6005
|
+
await new Promise((resolve12) => setTimeout(resolve12, 200));
|
|
6006
6006
|
}
|
|
6007
6007
|
spinner.succeed(`Updated ${availableUpdates.length} skill(s)`);
|
|
6008
6008
|
console.log();
|
|
@@ -6837,9 +6837,9 @@ async function fetchSkillMd(entry) {
|
|
|
6837
6837
|
`SKILL.md`
|
|
6838
6838
|
];
|
|
6839
6839
|
for (const branch of branches) {
|
|
6840
|
-
for (const
|
|
6840
|
+
for (const path27 of paths) {
|
|
6841
6841
|
try {
|
|
6842
|
-
const url = `https://raw.githubusercontent.com/${entry.owner}/${entry.repo}/${branch}/${
|
|
6842
|
+
const url = `https://raw.githubusercontent.com/${entry.owner}/${entry.repo}/${branch}/${path27}`;
|
|
6843
6843
|
const response = await fetch(url);
|
|
6844
6844
|
if (response.ok) {
|
|
6845
6845
|
return await response.text();
|
|
@@ -8119,7 +8119,7 @@ var createReadline = () => readline2.createInterface({
|
|
|
8119
8119
|
input: process.stdin,
|
|
8120
8120
|
output: process.stdout
|
|
8121
8121
|
});
|
|
8122
|
-
var question2 = (rl4, prompt) => new Promise((
|
|
8122
|
+
var question2 = (rl4, prompt) => new Promise((resolve12) => rl4.question(prompt, resolve12));
|
|
8123
8123
|
function getAnthropicApiKey() {
|
|
8124
8124
|
if (process.env.ANTHROPIC_API_KEY) {
|
|
8125
8125
|
return process.env.ANTHROPIC_API_KEY;
|
|
@@ -14580,11 +14580,11 @@ async function fetchGitHubApi2(endpoint) {
|
|
|
14580
14580
|
return null;
|
|
14581
14581
|
}
|
|
14582
14582
|
}
|
|
14583
|
-
async function fetchRawContent2(owner, repo,
|
|
14583
|
+
async function fetchRawContent2(owner, repo, path27, branch = "main") {
|
|
14584
14584
|
const branches = [branch, "master"];
|
|
14585
14585
|
for (const b of branches) {
|
|
14586
14586
|
try {
|
|
14587
|
-
const url = `https://raw.githubusercontent.com/${owner}/${repo}/${b}/${
|
|
14587
|
+
const url = `https://raw.githubusercontent.com/${owner}/${repo}/${b}/${path27}`;
|
|
14588
14588
|
const headers = { "User-Agent": "hubify-cli" };
|
|
14589
14589
|
const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
14590
14590
|
if (token) {
|
|
@@ -14934,7 +14934,7 @@ function formatDuration2(ms) {
|
|
|
14934
14934
|
}
|
|
14935
14935
|
async function runSeedCommand(source, options) {
|
|
14936
14936
|
const { spawn } = await import("child_process");
|
|
14937
|
-
return new Promise((
|
|
14937
|
+
return new Promise((resolve12) => {
|
|
14938
14938
|
const args = [source.commandName];
|
|
14939
14939
|
if (options.dryRun) args.push("--dry-run");
|
|
14940
14940
|
if (options.force) args.push("--force");
|
|
@@ -14955,7 +14955,7 @@ async function runSeedCommand(source, options) {
|
|
|
14955
14955
|
const importMatch = stdout.match(/Imported:\s*(\d+)/);
|
|
14956
14956
|
const skipMatch = stdout.match(/Skipped:\s*(\d+)/);
|
|
14957
14957
|
const failMatch = stdout.match(/Failed:\s*(\d+)/);
|
|
14958
|
-
|
|
14958
|
+
resolve12({
|
|
14959
14959
|
imported: importMatch ? parseInt(importMatch[1]) : 0,
|
|
14960
14960
|
skipped: skipMatch ? parseInt(skipMatch[1]) : 0,
|
|
14961
14961
|
failed: failMatch ? parseInt(failMatch[1]) : code !== 0 ? source.skillCount : 0,
|
|
@@ -14963,7 +14963,7 @@ async function runSeedCommand(source, options) {
|
|
|
14963
14963
|
});
|
|
14964
14964
|
});
|
|
14965
14965
|
proc.on("error", () => {
|
|
14966
|
-
|
|
14966
|
+
resolve12({
|
|
14967
14967
|
imported: 0,
|
|
14968
14968
|
skipped: 0,
|
|
14969
14969
|
failed: source.skillCount,
|
|
@@ -15673,8 +15673,8 @@ soulCommand.command("create").description("Create a new soul interactively").act
|
|
|
15673
15673
|
output: process.stdout
|
|
15674
15674
|
});
|
|
15675
15675
|
const question5 = (prompt) => {
|
|
15676
|
-
return new Promise((
|
|
15677
|
-
rl4.question(prompt,
|
|
15676
|
+
return new Promise((resolve12) => {
|
|
15677
|
+
rl4.question(prompt, resolve12);
|
|
15678
15678
|
});
|
|
15679
15679
|
};
|
|
15680
15680
|
try {
|
|
@@ -15911,8 +15911,8 @@ soulCommand.command("fork").argument("<name>", "Name of the soul to fork").descr
|
|
|
15911
15911
|
output: process.stdout
|
|
15912
15912
|
});
|
|
15913
15913
|
const question5 = (prompt) => {
|
|
15914
|
-
return new Promise((
|
|
15915
|
-
rl4.question(prompt,
|
|
15914
|
+
return new Promise((resolve12) => {
|
|
15915
|
+
rl4.question(prompt, resolve12);
|
|
15916
15916
|
});
|
|
15917
15917
|
};
|
|
15918
15918
|
const nameInput = await question5(
|
|
@@ -20869,11 +20869,11 @@ squadCommand.command("init").description("Initialize a local squad from a pack t
|
|
|
20869
20869
|
return;
|
|
20870
20870
|
}
|
|
20871
20871
|
spinner.text = "Generating local files...";
|
|
20872
|
-
const
|
|
20873
|
-
const
|
|
20872
|
+
const fs29 = await import("fs");
|
|
20873
|
+
const path27 = await import("path");
|
|
20874
20874
|
const dir = options.dir || ".hubify";
|
|
20875
|
-
const basePath =
|
|
20876
|
-
|
|
20875
|
+
const basePath = path27.resolve(process.cwd(), dir);
|
|
20876
|
+
fs29.mkdirSync(basePath, { recursive: true });
|
|
20877
20877
|
const squadMd = [
|
|
20878
20878
|
`# ${pack.display_name}`,
|
|
20879
20879
|
"",
|
|
@@ -20901,7 +20901,7 @@ squadCommand.command("init").description("Initialize a local squad from a pack t
|
|
|
20901
20901
|
""
|
|
20902
20902
|
].filter(Boolean).join("\n"))
|
|
20903
20903
|
].join("\n");
|
|
20904
|
-
|
|
20904
|
+
fs29.writeFileSync(path27.join(basePath, "squad.md"), squadMd);
|
|
20905
20905
|
const missionMd = [
|
|
20906
20906
|
`# Mission Brief`,
|
|
20907
20907
|
"",
|
|
@@ -20922,12 +20922,12 @@ ${pack.default_phases.map((p, i) => `${i + 1}. ${p.name}`).join("\n")}` : "",
|
|
|
20922
20922
|
`## Notes`,
|
|
20923
20923
|
`<!-- Additional context, constraints, or references -->`
|
|
20924
20924
|
].join("\n");
|
|
20925
|
-
|
|
20926
|
-
const agentsDir =
|
|
20927
|
-
|
|
20925
|
+
fs29.writeFileSync(path27.join(basePath, "mission.md"), missionMd);
|
|
20926
|
+
const agentsDir = path27.join(basePath, "agents");
|
|
20927
|
+
fs29.mkdirSync(agentsDir, { recursive: true });
|
|
20928
20928
|
for (const member of pack.members) {
|
|
20929
|
-
const memberDir =
|
|
20930
|
-
|
|
20929
|
+
const memberDir = path27.join(agentsDir, member.role);
|
|
20930
|
+
fs29.mkdirSync(memberDir, { recursive: true });
|
|
20931
20931
|
const agentMd = [
|
|
20932
20932
|
`# Agent: ${member.role}`,
|
|
20933
20933
|
"",
|
|
@@ -20943,7 +20943,7 @@ ${pack.default_phases.map((p, i) => `${i + 1}. ${p.name}`).join("\n")}` : "",
|
|
|
20943
20943
|
member.default_tools ? `## Tools
|
|
20944
20944
|
${member.default_tools.map((t) => `- ${t}`).join("\n")}` : ""
|
|
20945
20945
|
].join("\n");
|
|
20946
|
-
|
|
20946
|
+
fs29.writeFileSync(path27.join(memberDir, "agent.md"), agentMd);
|
|
20947
20947
|
const soulMd = [
|
|
20948
20948
|
`# Soul: ${member.soul_name}`,
|
|
20949
20949
|
"",
|
|
@@ -20956,7 +20956,7 @@ ${member.default_tools.map((t) => `- ${t}`).join("\n")}` : ""
|
|
|
20956
20956
|
`## Responsibilities`,
|
|
20957
20957
|
...member.responsibilities.map((r) => `- ${r}`)
|
|
20958
20958
|
].join("\n");
|
|
20959
|
-
|
|
20959
|
+
fs29.writeFileSync(path27.join(memberDir, "soul.md"), soulMd);
|
|
20960
20960
|
}
|
|
20961
20961
|
spinner.succeed(`Squad initialized in ${dir}/`);
|
|
20962
20962
|
console.log();
|
|
@@ -21084,6 +21084,70 @@ squadCommand.command("event").description("Post a manual event to the squad acti
|
|
|
21084
21084
|
process.exit(1);
|
|
21085
21085
|
}
|
|
21086
21086
|
});
|
|
21087
|
+
squadCommand.command("pause <squad-id>").description("Pause a deployed squad (disables standups and autonomy)").option("--owner <id>", "Owner user ID").action(async (squadId, options) => {
|
|
21088
|
+
const spinner = ora26();
|
|
21089
|
+
try {
|
|
21090
|
+
spinner.start("Pausing squad...");
|
|
21091
|
+
const client = getClient();
|
|
21092
|
+
await client.mutation(
|
|
21093
|
+
toFunctionName(api.squads.pauseSquad),
|
|
21094
|
+
{ squad_id: squadId, owner_id: options.owner || "" }
|
|
21095
|
+
);
|
|
21096
|
+
spinner.succeed("Squad paused");
|
|
21097
|
+
console.log(chalk29.yellow("\n Standups and autonomy processing disabled."));
|
|
21098
|
+
console.log(chalk29.gray(" Use `hubify squad resume` to re-enable.\n"));
|
|
21099
|
+
} catch (error) {
|
|
21100
|
+
spinner.fail("Failed to pause squad");
|
|
21101
|
+
console.error(chalk29.red(error instanceof Error ? error.message : "Unknown error"));
|
|
21102
|
+
process.exit(1);
|
|
21103
|
+
}
|
|
21104
|
+
});
|
|
21105
|
+
squadCommand.command("resume <squad-id>").description("Resume a paused squad (re-enables standups and autonomy)").option("--owner <id>", "Owner user ID").action(async (squadId, options) => {
|
|
21106
|
+
const spinner = ora26();
|
|
21107
|
+
try {
|
|
21108
|
+
spinner.start("Resuming squad...");
|
|
21109
|
+
const client = getClient();
|
|
21110
|
+
await client.mutation(
|
|
21111
|
+
toFunctionName(api.squads.resumeSquad),
|
|
21112
|
+
{ squad_id: squadId, owner_id: options.owner || "" }
|
|
21113
|
+
);
|
|
21114
|
+
spinner.succeed("Squad resumed");
|
|
21115
|
+
console.log(chalk29.green("\n Standups and autonomy processing re-enabled.\n"));
|
|
21116
|
+
} catch (error) {
|
|
21117
|
+
spinner.fail("Failed to resume squad");
|
|
21118
|
+
console.error(chalk29.red(error instanceof Error ? error.message : "Unknown error"));
|
|
21119
|
+
process.exit(1);
|
|
21120
|
+
}
|
|
21121
|
+
});
|
|
21122
|
+
squadCommand.command("remove <squad-id>").description("Remove a deployed squad from your workspace (marks as deprecated)").option("--owner <id>", "Owner user ID").option("--force", "Skip confirmation").action(async (squadId, options) => {
|
|
21123
|
+
const spinner = ora26();
|
|
21124
|
+
try {
|
|
21125
|
+
if (!options.force) {
|
|
21126
|
+
const readline7 = await import("readline");
|
|
21127
|
+
const rl4 = readline7.createInterface({ input: process.stdin, output: process.stdout });
|
|
21128
|
+
const answer = await new Promise((resolve12) => {
|
|
21129
|
+
rl4.question(chalk29.yellow(" Remove this squad? This cannot be undone. (y/N): "), resolve12);
|
|
21130
|
+
});
|
|
21131
|
+
rl4.close();
|
|
21132
|
+
if (answer.toLowerCase() !== "y") {
|
|
21133
|
+
console.log(chalk29.gray(" Cancelled.\n"));
|
|
21134
|
+
return;
|
|
21135
|
+
}
|
|
21136
|
+
}
|
|
21137
|
+
spinner.start("Removing squad...");
|
|
21138
|
+
const client = getClient();
|
|
21139
|
+
await client.mutation(
|
|
21140
|
+
toFunctionName(api.squads.removeSquad),
|
|
21141
|
+
{ squad_id: squadId, owner_id: options.owner || "" }
|
|
21142
|
+
);
|
|
21143
|
+
spinner.succeed("Squad removed");
|
|
21144
|
+
console.log(chalk29.red("\n Squad deprecated and removed from workspace.\n"));
|
|
21145
|
+
} catch (error) {
|
|
21146
|
+
spinner.fail("Failed to remove squad");
|
|
21147
|
+
console.error(chalk29.red(error instanceof Error ? error.message : "Unknown error"));
|
|
21148
|
+
process.exit(1);
|
|
21149
|
+
}
|
|
21150
|
+
});
|
|
21087
21151
|
|
|
21088
21152
|
// src/commands/skill-gen.ts
|
|
21089
21153
|
import { Command as Command27 } from "commander";
|
|
@@ -21663,7 +21727,7 @@ async function promptForResult() {
|
|
|
21663
21727
|
input: process.stdin,
|
|
21664
21728
|
output: process.stdout
|
|
21665
21729
|
});
|
|
21666
|
-
return new Promise((
|
|
21730
|
+
return new Promise((resolve12) => {
|
|
21667
21731
|
console.log();
|
|
21668
21732
|
console.log(chalk31.cyan(" Execution complete. Please report the result:"));
|
|
21669
21733
|
console.log();
|
|
@@ -21675,11 +21739,11 @@ async function promptForResult() {
|
|
|
21675
21739
|
rl4.close();
|
|
21676
21740
|
const choice = answer.toLowerCase().trim();
|
|
21677
21741
|
if (choice === "s" || choice === "success") {
|
|
21678
|
-
|
|
21742
|
+
resolve12(true);
|
|
21679
21743
|
} else if (choice === "f" || choice === "failed" || choice === "fail") {
|
|
21680
|
-
|
|
21744
|
+
resolve12(false);
|
|
21681
21745
|
} else {
|
|
21682
|
-
|
|
21746
|
+
resolve12(void 0);
|
|
21683
21747
|
}
|
|
21684
21748
|
});
|
|
21685
21749
|
});
|
|
@@ -21799,7 +21863,7 @@ var rl2 = readline5.createInterface({
|
|
|
21799
21863
|
input: process.stdin,
|
|
21800
21864
|
output: process.stdout
|
|
21801
21865
|
});
|
|
21802
|
-
var question3 = (prompt) => new Promise((
|
|
21866
|
+
var question3 = (prompt) => new Promise((resolve12) => rl2.question(prompt, resolve12));
|
|
21803
21867
|
var hubInitCommand = new Command29("workspace-init").description("Initialize a Hubify hub in the current directory").option("--name <name>", "Hub name (defaults to directory name)").option("--non-interactive", "Skip prompts, use defaults").action(async (options) => {
|
|
21804
21868
|
const spinner = ora29();
|
|
21805
21869
|
try {
|
|
@@ -22052,6 +22116,188 @@ function saveConnectState(state) {
|
|
|
22052
22116
|
if (!fs13.existsSync(dir)) fs13.mkdirSync(dir, { recursive: true });
|
|
22053
22117
|
fs13.writeFileSync(HUBIFY_STATE_FILE, JSON.stringify(state, null, 2));
|
|
22054
22118
|
}
|
|
22119
|
+
function collectDirFiles(dirPath, extensions) {
|
|
22120
|
+
const files = [];
|
|
22121
|
+
let bundle = "";
|
|
22122
|
+
let totalBytes = 0;
|
|
22123
|
+
if (!fs13.existsSync(dirPath)) return { files, bundle, totalBytes };
|
|
22124
|
+
const entries = fs13.readdirSync(dirPath);
|
|
22125
|
+
for (const entry of entries) {
|
|
22126
|
+
if (!extensions.some((ext) => entry.endsWith(ext))) continue;
|
|
22127
|
+
const fullPath = path13.join(dirPath, entry);
|
|
22128
|
+
try {
|
|
22129
|
+
const stat = fs13.statSync(fullPath);
|
|
22130
|
+
const content = fs13.readFileSync(fullPath, "utf-8");
|
|
22131
|
+
const hash = hashContent(content);
|
|
22132
|
+
files.push({ path: entry, size: stat.size, hash, mtime: stat.mtimeMs });
|
|
22133
|
+
bundle += `
|
|
22134
|
+
|
|
22135
|
+
### ${entry}
|
|
22136
|
+
|
|
22137
|
+
${content}`;
|
|
22138
|
+
totalBytes += stat.size;
|
|
22139
|
+
} catch {
|
|
22140
|
+
}
|
|
22141
|
+
}
|
|
22142
|
+
return { files, bundle: bundle.trimStart(), totalBytes };
|
|
22143
|
+
}
|
|
22144
|
+
async function pushContextFile(filePath, contextType, hubId, machineId) {
|
|
22145
|
+
if (!fs13.existsSync(filePath)) return null;
|
|
22146
|
+
const content = fs13.readFileSync(filePath, "utf-8");
|
|
22147
|
+
const stat = fs13.statSync(filePath);
|
|
22148
|
+
const fileHash = hashContent(content);
|
|
22149
|
+
const client = getClient();
|
|
22150
|
+
const result = await client.mutation(toFunctionName(api.workspaceSync.pushWorkspaceContext), {
|
|
22151
|
+
hub_id: hubId,
|
|
22152
|
+
local_machine_id: machineId,
|
|
22153
|
+
context_type: contextType,
|
|
22154
|
+
content,
|
|
22155
|
+
file_hash: fileHash,
|
|
22156
|
+
timestamp: stat.mtimeMs,
|
|
22157
|
+
metadata: {
|
|
22158
|
+
file_count: 1,
|
|
22159
|
+
total_size: content.length
|
|
22160
|
+
}
|
|
22161
|
+
});
|
|
22162
|
+
return { status: result.status, bytes: content.length };
|
|
22163
|
+
}
|
|
22164
|
+
async function pushContextDir(dirPath, contextType, extensions, hubId, machineId) {
|
|
22165
|
+
const { files, bundle, totalBytes } = collectDirFiles(dirPath, extensions);
|
|
22166
|
+
if (files.length === 0) return null;
|
|
22167
|
+
const bundleHash = hashContent(bundle);
|
|
22168
|
+
const client = getClient();
|
|
22169
|
+
const result = await client.mutation(toFunctionName(api.workspaceSync.pushWorkspaceContext), {
|
|
22170
|
+
hub_id: hubId,
|
|
22171
|
+
local_machine_id: machineId,
|
|
22172
|
+
context_type: contextType,
|
|
22173
|
+
content: bundle,
|
|
22174
|
+
file_hash: bundleHash,
|
|
22175
|
+
timestamp: Date.now(),
|
|
22176
|
+
metadata: {
|
|
22177
|
+
file_count: files.length,
|
|
22178
|
+
total_size: totalBytes,
|
|
22179
|
+
files
|
|
22180
|
+
}
|
|
22181
|
+
});
|
|
22182
|
+
return { status: result.status, fileCount: files.length, bytes: totalBytes };
|
|
22183
|
+
}
|
|
22184
|
+
async function pullWorkspaceContextToLocal(projectRoot, hubId) {
|
|
22185
|
+
const client = getClient();
|
|
22186
|
+
let pulled = 0;
|
|
22187
|
+
let unchanged = 0;
|
|
22188
|
+
let bytes = 0;
|
|
22189
|
+
const contexts = await client.query(toFunctionName(api.workspaceSync.pullWorkspaceContext), {
|
|
22190
|
+
hub_id: hubId
|
|
22191
|
+
});
|
|
22192
|
+
for (const [contextType, data] of Object.entries(contexts)) {
|
|
22193
|
+
if (!data?.content) continue;
|
|
22194
|
+
const fileMap = {
|
|
22195
|
+
soul: path13.join(projectRoot, "SOUL.md"),
|
|
22196
|
+
memory: path13.join(projectRoot, "MEMORY.md"),
|
|
22197
|
+
agents: path13.join(projectRoot, "AGENTS.md")
|
|
22198
|
+
};
|
|
22199
|
+
if (fileMap[contextType]) {
|
|
22200
|
+
const localPath = fileMap[contextType];
|
|
22201
|
+
const localHash = fs13.existsSync(localPath) ? hashContent(fs13.readFileSync(localPath, "utf-8")) : null;
|
|
22202
|
+
if (localHash === data.file_hash) {
|
|
22203
|
+
unchanged++;
|
|
22204
|
+
continue;
|
|
22205
|
+
}
|
|
22206
|
+
const localMtime = fs13.existsSync(localPath) ? fs13.statSync(localPath).mtimeMs : 0;
|
|
22207
|
+
if (data.file_timestamp && localMtime > data.file_timestamp) {
|
|
22208
|
+
unchanged++;
|
|
22209
|
+
continue;
|
|
22210
|
+
}
|
|
22211
|
+
fs13.writeFileSync(localPath, data.content, "utf-8");
|
|
22212
|
+
bytes += data.content.length;
|
|
22213
|
+
pulled++;
|
|
22214
|
+
} else if (contextType === "memories" && data.content) {
|
|
22215
|
+
const memoriesDir = path13.join(projectRoot, "memory");
|
|
22216
|
+
if (!fs13.existsSync(memoriesDir)) {
|
|
22217
|
+
fs13.mkdirSync(memoriesDir, { recursive: true });
|
|
22218
|
+
}
|
|
22219
|
+
const outPath = path13.join(memoriesDir, "cloud-sync.md");
|
|
22220
|
+
const localHash = fs13.existsSync(outPath) ? hashContent(fs13.readFileSync(outPath, "utf-8")) : null;
|
|
22221
|
+
if (localHash !== data.file_hash) {
|
|
22222
|
+
fs13.writeFileSync(outPath, data.content, "utf-8");
|
|
22223
|
+
bytes += data.content.length;
|
|
22224
|
+
pulled++;
|
|
22225
|
+
} else {
|
|
22226
|
+
unchanged++;
|
|
22227
|
+
}
|
|
22228
|
+
}
|
|
22229
|
+
}
|
|
22230
|
+
return { pulled, unchanged, bytes };
|
|
22231
|
+
}
|
|
22232
|
+
async function pushFullWorkspaceContext(projectRoot, hubId, machineId) {
|
|
22233
|
+
const contextTypes = [];
|
|
22234
|
+
let totalFiles = 0;
|
|
22235
|
+
let totalBytes = 0;
|
|
22236
|
+
let unchanged = 0;
|
|
22237
|
+
const soulResult = await pushContextFile(
|
|
22238
|
+
path13.join(projectRoot, "SOUL.md"),
|
|
22239
|
+
"soul",
|
|
22240
|
+
hubId,
|
|
22241
|
+
machineId
|
|
22242
|
+
).catch(() => null);
|
|
22243
|
+
if (soulResult) {
|
|
22244
|
+
if (soulResult.status === "unchanged") {
|
|
22245
|
+
unchanged++;
|
|
22246
|
+
} else {
|
|
22247
|
+
contextTypes.push("soul");
|
|
22248
|
+
totalFiles++;
|
|
22249
|
+
totalBytes += soulResult.bytes;
|
|
22250
|
+
}
|
|
22251
|
+
}
|
|
22252
|
+
const agentsResult = await pushContextFile(
|
|
22253
|
+
path13.join(projectRoot, "AGENTS.md"),
|
|
22254
|
+
"agents",
|
|
22255
|
+
hubId,
|
|
22256
|
+
machineId
|
|
22257
|
+
).catch(() => null);
|
|
22258
|
+
if (agentsResult) {
|
|
22259
|
+
if (agentsResult.status === "unchanged") {
|
|
22260
|
+
unchanged++;
|
|
22261
|
+
} else {
|
|
22262
|
+
contextTypes.push("agents");
|
|
22263
|
+
totalFiles++;
|
|
22264
|
+
totalBytes += agentsResult.bytes;
|
|
22265
|
+
}
|
|
22266
|
+
}
|
|
22267
|
+
const memResult = await pushContextDir(
|
|
22268
|
+
path13.join(projectRoot, "memory"),
|
|
22269
|
+
"memories",
|
|
22270
|
+
[".md"],
|
|
22271
|
+
hubId,
|
|
22272
|
+
machineId
|
|
22273
|
+
).catch(() => null);
|
|
22274
|
+
if (memResult) {
|
|
22275
|
+
if (memResult.status === "unchanged") {
|
|
22276
|
+
unchanged++;
|
|
22277
|
+
} else {
|
|
22278
|
+
contextTypes.push("memories");
|
|
22279
|
+
totalFiles += memResult.fileCount;
|
|
22280
|
+
totalBytes += memResult.bytes;
|
|
22281
|
+
}
|
|
22282
|
+
}
|
|
22283
|
+
const skillsResult = await pushContextDir(
|
|
22284
|
+
path13.join(projectRoot, "skills"),
|
|
22285
|
+
"skills",
|
|
22286
|
+
[".md", ".ts", ".js", ".yaml", ".yml"],
|
|
22287
|
+
hubId,
|
|
22288
|
+
machineId
|
|
22289
|
+
).catch(() => null);
|
|
22290
|
+
if (skillsResult) {
|
|
22291
|
+
if (skillsResult.status === "unchanged") {
|
|
22292
|
+
unchanged++;
|
|
22293
|
+
} else {
|
|
22294
|
+
contextTypes.push("skills");
|
|
22295
|
+
totalFiles += skillsResult.fileCount;
|
|
22296
|
+
totalBytes += skillsResult.bytes;
|
|
22297
|
+
}
|
|
22298
|
+
}
|
|
22299
|
+
return { contextTypes, totalFiles, totalBytes, unchanged };
|
|
22300
|
+
}
|
|
22055
22301
|
async function pushLocalMemoryToHub(memoryDir, hubId, agentId, platform2, cache) {
|
|
22056
22302
|
if (!fs13.existsSync(memoryDir)) return { pushed: 0, unchanged: 0 };
|
|
22057
22303
|
const client = getClient();
|
|
@@ -22144,7 +22390,7 @@ async function pullHubMemoryToLocal(memoryDir, hubId, agentId, cache) {
|
|
|
22144
22390
|
}
|
|
22145
22391
|
return { pulled };
|
|
22146
22392
|
}
|
|
22147
|
-
var createHubConnectCommand = () => new Command30("connect").description("Connect this machine to a Hubify hub and start local\u2194cloud memory sync").option("--platform <name>", "Platform name (openclaw, claude-code, cursor, etc.)").option("--hub-id <id>", "Hub ID (overrides HUB.yaml)").option("--subdomain <name>", "Hub subdomain (e.g. 'houston' for houston.hubify.com)").option("--memory-dir <path>", "Local memory directory to sync (default: ~/clawd/memory)").option("--watch", "Keep process running and watch for file changes").option("--once", "Sync once and exit (no watch loop)").action(async (options) => {
|
|
22393
|
+
var createHubConnectCommand = () => new Command30("connect").description("Connect this machine to a Hubify hub and start local\u2194cloud memory sync").option("--platform <name>", "Platform name (openclaw, claude-code, cursor, etc.)").option("--hub-id <id>", "Hub ID (overrides HUB.yaml)").option("--subdomain <name>", "Hub subdomain (e.g. 'houston' for houston.hubify.com)").option("--memory-dir <path>", "Local memory directory to sync (default: ~/clawd/memory)").option("--watch", "Keep process running and watch for file changes").option("--once", "Sync once and exit (no watch loop)").option("--full", "Sync full workspace context (SOUL.md, AGENTS.md, memory/, skills/) in addition to memory files").action(async (options) => {
|
|
22148
22394
|
const spinner = ora30();
|
|
22149
22395
|
try {
|
|
22150
22396
|
console.log(chalk33.cyan("\n\u{1F517} Hubify Connect \u2014 Local \u2194 Cloud Sync\n"));
|
|
@@ -22250,6 +22496,33 @@ var createHubConnectCommand = () => new Command30("connect").description("Connec
|
|
|
22250
22496
|
state.last_sync_at = Date.now();
|
|
22251
22497
|
saveConnectState(state);
|
|
22252
22498
|
console.log(chalk33.green(` \u2705 Pushed: ${pushResult.pushed} files | Pulled: ${pullResult.pulled} files | Unchanged: ${pushResult.unchanged}`));
|
|
22499
|
+
if (options.full) {
|
|
22500
|
+
console.log(chalk33.cyan("\n\u{1F4E6} Full workspace context sync...\n"));
|
|
22501
|
+
const projectRoot = process.cwd();
|
|
22502
|
+
try {
|
|
22503
|
+
const wsSpinner = ora30("Pushing workspace context (SOUL.md, AGENTS.md, memory/, skills/)...").start();
|
|
22504
|
+
const wsResult = await pushFullWorkspaceContext(projectRoot, hubId, machineId);
|
|
22505
|
+
wsSpinner.succeed(
|
|
22506
|
+
`Workspace push complete \u2014 ` + chalk33.green(`${wsResult.totalFiles} files`) + chalk33.gray(` (${(wsResult.totalBytes / 1024).toFixed(1)} KB)`) + (wsResult.unchanged > 0 ? chalk33.gray(` ${wsResult.unchanged} unchanged`) : "")
|
|
22507
|
+
);
|
|
22508
|
+
if (wsResult.contextTypes.length > 0) {
|
|
22509
|
+
console.log(chalk33.gray(` Context types: ${wsResult.contextTypes.join(", ")}`));
|
|
22510
|
+
}
|
|
22511
|
+
} catch (err) {
|
|
22512
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
22513
|
+
console.log(chalk33.yellow(` Warning: workspace context push failed (${msg})`));
|
|
22514
|
+
}
|
|
22515
|
+
try {
|
|
22516
|
+
const pullSpinner = ora30("Pulling workspace context from cloud...").start();
|
|
22517
|
+
const wsPull = await pullWorkspaceContextToLocal(projectRoot, hubId);
|
|
22518
|
+
pullSpinner.succeed(
|
|
22519
|
+
`Workspace pull complete \u2014 ` + chalk33.green(`${wsPull.pulled} files updated`) + chalk33.gray(` ${wsPull.unchanged} already in sync`) + (wsPull.bytes > 0 ? chalk33.gray(` (${(wsPull.bytes / 1024).toFixed(1)} KB)`) : "")
|
|
22520
|
+
);
|
|
22521
|
+
} catch (err) {
|
|
22522
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
22523
|
+
console.log(chalk33.yellow(` Warning: workspace context pull failed (${msg})`));
|
|
22524
|
+
}
|
|
22525
|
+
}
|
|
22253
22526
|
if (options.once) {
|
|
22254
22527
|
console.log(chalk33.gray("\n Sync complete (--once mode). Exiting.\n"));
|
|
22255
22528
|
process.exit(0);
|
|
@@ -22268,6 +22541,18 @@ var createHubConnectCommand = () => new Command30("connect").description("Connec
|
|
|
22268
22541
|
if (p.pushed > 0 || q.pulled > 0) {
|
|
22269
22542
|
console.log(chalk33.gray(` [${now}] \u2191 ${p.pushed} pushed \u2193 ${q.pulled} pulled`));
|
|
22270
22543
|
}
|
|
22544
|
+
if (options.full) {
|
|
22545
|
+
const projectRoot = process.cwd();
|
|
22546
|
+
const wsResult = await pushFullWorkspaceContext(projectRoot, hubId, machineId).catch(() => null);
|
|
22547
|
+
const wsPull = await pullWorkspaceContextToLocal(projectRoot, hubId).catch(() => null);
|
|
22548
|
+
if (wsResult && (wsResult.totalFiles > 0 || wsResult.unchanged === 0)) {
|
|
22549
|
+
const pulled = wsPull?.pulled ?? 0;
|
|
22550
|
+
const pushed = wsResult.totalFiles;
|
|
22551
|
+
if (pushed > 0 || pulled > 0) {
|
|
22552
|
+
console.log(chalk33.gray(` [${now}] workspace \u2191 ${pushed} ctx \u2193 ${pulled} ctx`));
|
|
22553
|
+
}
|
|
22554
|
+
}
|
|
22555
|
+
}
|
|
22271
22556
|
state.last_sync_at = Date.now();
|
|
22272
22557
|
saveConnectState(state);
|
|
22273
22558
|
} catch (err) {
|
|
@@ -22305,9 +22590,12 @@ var createHubConnectCommand = () => new Command30("connect").description("Connec
|
|
|
22305
22590
|
} else {
|
|
22306
22591
|
console.log(chalk33.green("\n\u2705 Hub connected!\n"));
|
|
22307
22592
|
console.log(chalk33.white("Next steps:"));
|
|
22308
|
-
console.log(chalk33.cyan(" hubify connect --watch") + chalk33.gray("
|
|
22309
|
-
console.log(chalk33.cyan(" hubify
|
|
22310
|
-
console.log(chalk33.cyan(" hubify
|
|
22593
|
+
console.log(chalk33.cyan(" hubify connect --watch") + chalk33.gray(" \u2014 keep running and auto-sync"));
|
|
22594
|
+
console.log(chalk33.cyan(" hubify connect --watch --full") + chalk33.gray(" \u2014 auto-sync + full workspace context"));
|
|
22595
|
+
console.log(chalk33.cyan(" hubify status") + chalk33.gray(" \u2014 show sync status & connected machines"));
|
|
22596
|
+
console.log(chalk33.cyan(" hubify sync push") + chalk33.gray(" \u2014 push workspace context once"));
|
|
22597
|
+
console.log(chalk33.cyan(" hubify sync pull") + chalk33.gray(" \u2014 pull workspace context from cloud"));
|
|
22598
|
+
console.log(chalk33.cyan(" hubify connect --once") + chalk33.gray(" \u2014 sync once and exit"));
|
|
22311
22599
|
console.log();
|
|
22312
22600
|
console.log(chalk33.gray(` State saved to: ${HUBIFY_STATE_FILE}`));
|
|
22313
22601
|
console.log();
|
|
@@ -22681,14 +22969,14 @@ ${content}`,
|
|
|
22681
22969
|
}
|
|
22682
22970
|
return stats;
|
|
22683
22971
|
}
|
|
22684
|
-
function createApiProxy(
|
|
22972
|
+
function createApiProxy(path27 = []) {
|
|
22685
22973
|
return new Proxy(() => {
|
|
22686
22974
|
}, {
|
|
22687
22975
|
get(_target, prop) {
|
|
22688
|
-
return createApiProxy([...
|
|
22976
|
+
return createApiProxy([...path27, prop]);
|
|
22689
22977
|
},
|
|
22690
22978
|
apply() {
|
|
22691
|
-
return
|
|
22979
|
+
return path27.join(":");
|
|
22692
22980
|
}
|
|
22693
22981
|
});
|
|
22694
22982
|
}
|
|
@@ -23307,7 +23595,7 @@ var rl3 = readline6.createInterface({
|
|
|
23307
23595
|
input: process.stdin,
|
|
23308
23596
|
output: process.stdout
|
|
23309
23597
|
});
|
|
23310
|
-
var question4 = (prompt) => new Promise((
|
|
23598
|
+
var question4 = (prompt) => new Promise((resolve12) => rl3.question(prompt, resolve12));
|
|
23311
23599
|
var hubCommand = new Command34("hub").description("Manage .hub manifest files for skills and agents");
|
|
23312
23600
|
var initSubcommand = new Command34("init").description("Create a .hub manifest file for a skill, agent, or soul").argument("[directory]", "Directory containing the skill/agent (default: current directory)").option("--type <type>", "Type: skill, agent, or soul", "skill").option("--name <name>", "Name for the .hub file").option("--non-interactive", "Skip prompts, use defaults").action(async (directory, options) => {
|
|
23313
23601
|
const spinner = ora34();
|
|
@@ -24166,6 +24454,8 @@ import chalk38 from "chalk";
|
|
|
24166
24454
|
import ora35 from "ora";
|
|
24167
24455
|
import fs19 from "fs";
|
|
24168
24456
|
import path19 from "path";
|
|
24457
|
+
import crypto5 from "crypto";
|
|
24458
|
+
import os10 from "os";
|
|
24169
24459
|
function readHubifyFrameworkType(workDir) {
|
|
24170
24460
|
const configPath = path19.join(workDir, "hubify.json");
|
|
24171
24461
|
if (!fs19.existsSync(configPath)) return null;
|
|
@@ -24384,9 +24674,406 @@ var statsCommand3 = new Command35("stats").description("Show sync statistics").a
|
|
|
24384
24674
|
throw error;
|
|
24385
24675
|
}
|
|
24386
24676
|
});
|
|
24677
|
+
function hashContent3(content) {
|
|
24678
|
+
return crypto5.createHash("sha256").update(content).digest("hex");
|
|
24679
|
+
}
|
|
24680
|
+
function collectDirFiles2(dirPath, extensions) {
|
|
24681
|
+
const files = [];
|
|
24682
|
+
let bundle = "";
|
|
24683
|
+
let totalBytes = 0;
|
|
24684
|
+
if (!fs19.existsSync(dirPath)) return { files, bundle, totalBytes };
|
|
24685
|
+
for (const entry of fs19.readdirSync(dirPath)) {
|
|
24686
|
+
if (!extensions.some((ext) => entry.endsWith(ext))) continue;
|
|
24687
|
+
const fullPath = path19.join(dirPath, entry);
|
|
24688
|
+
try {
|
|
24689
|
+
const stat = fs19.statSync(fullPath);
|
|
24690
|
+
const content = fs19.readFileSync(fullPath, "utf-8");
|
|
24691
|
+
files.push({ path: entry, size: stat.size, hash: hashContent3(content), mtime: stat.mtimeMs });
|
|
24692
|
+
bundle += `
|
|
24693
|
+
|
|
24694
|
+
### ${entry}
|
|
24695
|
+
|
|
24696
|
+
${content}`;
|
|
24697
|
+
totalBytes += stat.size;
|
|
24698
|
+
} catch {
|
|
24699
|
+
}
|
|
24700
|
+
}
|
|
24701
|
+
return { files, bundle: bundle.trimStart(), totalBytes };
|
|
24702
|
+
}
|
|
24703
|
+
function resolveHubId(options) {
|
|
24704
|
+
if (options.hub) return options.hub;
|
|
24705
|
+
const stateFile = path19.join(os10.homedir(), ".hubify", "connect-state.json");
|
|
24706
|
+
if (fs19.existsSync(stateFile)) {
|
|
24707
|
+
try {
|
|
24708
|
+
const state = JSON.parse(fs19.readFileSync(stateFile, "utf-8"));
|
|
24709
|
+
if (state.hub_id) return state.hub_id;
|
|
24710
|
+
} catch {
|
|
24711
|
+
}
|
|
24712
|
+
}
|
|
24713
|
+
let dir = process.cwd();
|
|
24714
|
+
while (dir !== "/" && dir !== os10.homedir()) {
|
|
24715
|
+
const yamlPath = path19.join(dir, "HUB.yaml");
|
|
24716
|
+
if (fs19.existsSync(yamlPath)) {
|
|
24717
|
+
try {
|
|
24718
|
+
const lines = fs19.readFileSync(yamlPath, "utf-8").split("\n");
|
|
24719
|
+
for (const line of lines) {
|
|
24720
|
+
const m = line.match(/^\s*id\s*:\s*(.+)/);
|
|
24721
|
+
if (m) return m[1].trim().replace(/^["']|["']$/g, "");
|
|
24722
|
+
}
|
|
24723
|
+
} catch {
|
|
24724
|
+
}
|
|
24725
|
+
}
|
|
24726
|
+
dir = path19.dirname(dir);
|
|
24727
|
+
}
|
|
24728
|
+
return null;
|
|
24729
|
+
}
|
|
24730
|
+
function getMachineId2() {
|
|
24731
|
+
const stateFile = path19.join(os10.homedir(), ".hubify", "connect-state.json");
|
|
24732
|
+
if (fs19.existsSync(stateFile)) {
|
|
24733
|
+
try {
|
|
24734
|
+
const state = JSON.parse(fs19.readFileSync(stateFile, "utf-8"));
|
|
24735
|
+
if (state.machine_id) return state.machine_id;
|
|
24736
|
+
} catch {
|
|
24737
|
+
}
|
|
24738
|
+
}
|
|
24739
|
+
return crypto5.createHash("sha256").update(os10.hostname()).digest("hex").slice(0, 16);
|
|
24740
|
+
}
|
|
24741
|
+
var pushCommand = new Command35("push").description("Push local workspace context (SOUL.md, AGENTS.md, memory/, skills/) to Convex hub").option("--hub <id>", "Hub ID (default: from connect-state or HUB.yaml)").option("--dir <path>", "Project root to sync from (default: current directory)").option("--dry-run", "Show what would be pushed without actually pushing").action(async (options) => {
|
|
24742
|
+
const spinner = ora35();
|
|
24743
|
+
const client = getClient();
|
|
24744
|
+
console.log(chalk38.cyan("\n Workspace Sync \u2014 Push\n"));
|
|
24745
|
+
const hubId = resolveHubId(options);
|
|
24746
|
+
if (!hubId) {
|
|
24747
|
+
console.log(chalk38.red(" No hub ID found."));
|
|
24748
|
+
console.log(chalk38.yellow(" Run 'hubify connect' first, or pass --hub <id>"));
|
|
24749
|
+
process.exit(1);
|
|
24750
|
+
}
|
|
24751
|
+
const machineId = getMachineId2();
|
|
24752
|
+
const projectRoot = options.dir ? path19.resolve(options.dir) : process.cwd();
|
|
24753
|
+
console.log(chalk38.gray(` Hub: ${hubId.slice(0, 20)}...`));
|
|
24754
|
+
console.log(chalk38.gray(` Root: ${projectRoot}`));
|
|
24755
|
+
if (options.dryRun) console.log(chalk38.yellow(" Mode: dry-run (no writes)\n"));
|
|
24756
|
+
else console.log();
|
|
24757
|
+
const results = [];
|
|
24758
|
+
let totalConflicts = 0;
|
|
24759
|
+
const soulPath = path19.join(projectRoot, "SOUL.md");
|
|
24760
|
+
spinner.start("Checking SOUL.md...");
|
|
24761
|
+
if (fs19.existsSync(soulPath)) {
|
|
24762
|
+
try {
|
|
24763
|
+
const content = fs19.readFileSync(soulPath, "utf-8");
|
|
24764
|
+
const stat = fs19.statSync(soulPath);
|
|
24765
|
+
const fileHash = hashContent3(content);
|
|
24766
|
+
const conflictCheck = await client.query(toFunctionName(api.workspaceSync.checkSyncStatus), {
|
|
24767
|
+
hub_id: hubId,
|
|
24768
|
+
local_file_hashes: { soul: fileHash }
|
|
24769
|
+
});
|
|
24770
|
+
if (!conflictCheck.in_sync) totalConflicts += conflictCheck.conflicts.length;
|
|
24771
|
+
if (!options.dryRun) {
|
|
24772
|
+
const res = await client.mutation(toFunctionName(api.workspaceSync.pushWorkspaceContext), {
|
|
24773
|
+
hub_id: hubId,
|
|
24774
|
+
local_machine_id: machineId,
|
|
24775
|
+
context_type: "soul",
|
|
24776
|
+
content,
|
|
24777
|
+
file_hash: fileHash,
|
|
24778
|
+
timestamp: stat.mtimeMs,
|
|
24779
|
+
metadata: { file_count: 1, total_size: content.length }
|
|
24780
|
+
});
|
|
24781
|
+
results.push({ type: "soul (SOUL.md)", status: res.status, files: 1, bytes: content.length });
|
|
24782
|
+
spinner.succeed(`SOUL.md \u2014 ${res.status} (${content.length} bytes)`);
|
|
24783
|
+
} else {
|
|
24784
|
+
results.push({ type: "soul (SOUL.md)", status: "dry-run", files: 1, bytes: content.length });
|
|
24785
|
+
spinner.info(`SOUL.md \u2014 would push (${content.length} bytes)`);
|
|
24786
|
+
}
|
|
24787
|
+
} catch (err) {
|
|
24788
|
+
spinner.fail(`SOUL.md failed: ${err instanceof Error ? err.message : err}`);
|
|
24789
|
+
}
|
|
24790
|
+
} else {
|
|
24791
|
+
spinner.info("SOUL.md \u2014 not found, skipping");
|
|
24792
|
+
}
|
|
24793
|
+
const agentsPath = path19.join(projectRoot, "AGENTS.md");
|
|
24794
|
+
spinner.start("Checking AGENTS.md...");
|
|
24795
|
+
if (fs19.existsSync(agentsPath)) {
|
|
24796
|
+
try {
|
|
24797
|
+
const content = fs19.readFileSync(agentsPath, "utf-8");
|
|
24798
|
+
const stat = fs19.statSync(agentsPath);
|
|
24799
|
+
const fileHash = hashContent3(content);
|
|
24800
|
+
const conflictCheck = await client.query(toFunctionName(api.workspaceSync.checkSyncStatus), {
|
|
24801
|
+
hub_id: hubId,
|
|
24802
|
+
local_file_hashes: { agents: fileHash }
|
|
24803
|
+
});
|
|
24804
|
+
if (!conflictCheck.in_sync) totalConflicts += conflictCheck.conflicts.length;
|
|
24805
|
+
if (!options.dryRun) {
|
|
24806
|
+
const res = await client.mutation(toFunctionName(api.workspaceSync.pushWorkspaceContext), {
|
|
24807
|
+
hub_id: hubId,
|
|
24808
|
+
local_machine_id: machineId,
|
|
24809
|
+
context_type: "agents",
|
|
24810
|
+
content,
|
|
24811
|
+
file_hash: fileHash,
|
|
24812
|
+
timestamp: stat.mtimeMs,
|
|
24813
|
+
metadata: { file_count: 1, total_size: content.length }
|
|
24814
|
+
});
|
|
24815
|
+
results.push({ type: "agents (AGENTS.md)", status: res.status, files: 1, bytes: content.length });
|
|
24816
|
+
spinner.succeed(`AGENTS.md \u2014 ${res.status} (${content.length} bytes)`);
|
|
24817
|
+
} else {
|
|
24818
|
+
results.push({ type: "agents (AGENTS.md)", status: "dry-run", files: 1, bytes: content.length });
|
|
24819
|
+
spinner.info(`AGENTS.md \u2014 would push (${content.length} bytes)`);
|
|
24820
|
+
}
|
|
24821
|
+
} catch (err) {
|
|
24822
|
+
spinner.fail(`AGENTS.md failed: ${err instanceof Error ? err.message : err}`);
|
|
24823
|
+
}
|
|
24824
|
+
} else {
|
|
24825
|
+
spinner.info("AGENTS.md \u2014 not found, skipping");
|
|
24826
|
+
}
|
|
24827
|
+
const memoryDir = path19.join(projectRoot, "memory");
|
|
24828
|
+
spinner.start("Checking memory/...");
|
|
24829
|
+
const memData = collectDirFiles2(memoryDir, [".md"]);
|
|
24830
|
+
if (memData.files.length > 0) {
|
|
24831
|
+
try {
|
|
24832
|
+
const bundleHash = hashContent3(memData.bundle);
|
|
24833
|
+
const conflictCheck = await client.query(toFunctionName(api.workspaceSync.checkSyncStatus), {
|
|
24834
|
+
hub_id: hubId,
|
|
24835
|
+
local_file_hashes: { memories: bundleHash }
|
|
24836
|
+
});
|
|
24837
|
+
if (!conflictCheck.in_sync) totalConflicts += conflictCheck.conflicts.length;
|
|
24838
|
+
if (!options.dryRun) {
|
|
24839
|
+
const res = await client.mutation(toFunctionName(api.workspaceSync.pushWorkspaceContext), {
|
|
24840
|
+
hub_id: hubId,
|
|
24841
|
+
local_machine_id: machineId,
|
|
24842
|
+
context_type: "memories",
|
|
24843
|
+
content: memData.bundle,
|
|
24844
|
+
file_hash: bundleHash,
|
|
24845
|
+
timestamp: Date.now(),
|
|
24846
|
+
metadata: { file_count: memData.files.length, total_size: memData.totalBytes, files: memData.files }
|
|
24847
|
+
});
|
|
24848
|
+
results.push({ type: "memories (memory/)", status: res.status, files: memData.files.length, bytes: memData.totalBytes });
|
|
24849
|
+
spinner.succeed(`memory/ \u2014 ${res.status} (${memData.files.length} files, ${(memData.totalBytes / 1024).toFixed(1)} KB)`);
|
|
24850
|
+
} else {
|
|
24851
|
+
results.push({ type: "memories (memory/)", status: "dry-run", files: memData.files.length, bytes: memData.totalBytes });
|
|
24852
|
+
spinner.info(`memory/ \u2014 would push (${memData.files.length} files, ${(memData.totalBytes / 1024).toFixed(1)} KB)`);
|
|
24853
|
+
}
|
|
24854
|
+
} catch (err) {
|
|
24855
|
+
spinner.fail(`memory/ failed: ${err instanceof Error ? err.message : err}`);
|
|
24856
|
+
}
|
|
24857
|
+
} else {
|
|
24858
|
+
spinner.info("memory/ \u2014 no .md files found, skipping");
|
|
24859
|
+
}
|
|
24860
|
+
const skillsDir = path19.join(projectRoot, "skills");
|
|
24861
|
+
spinner.start("Checking skills/...");
|
|
24862
|
+
const skillsData = collectDirFiles2(skillsDir, [".md", ".ts", ".js", ".yaml", ".yml"]);
|
|
24863
|
+
if (skillsData.files.length > 0) {
|
|
24864
|
+
try {
|
|
24865
|
+
const bundleHash = hashContent3(skillsData.bundle);
|
|
24866
|
+
const conflictCheck = await client.query(toFunctionName(api.workspaceSync.checkSyncStatus), {
|
|
24867
|
+
hub_id: hubId,
|
|
24868
|
+
local_file_hashes: { skills: bundleHash }
|
|
24869
|
+
});
|
|
24870
|
+
if (!conflictCheck.in_sync) totalConflicts += conflictCheck.conflicts.length;
|
|
24871
|
+
if (!options.dryRun) {
|
|
24872
|
+
const res = await client.mutation(toFunctionName(api.workspaceSync.pushWorkspaceContext), {
|
|
24873
|
+
hub_id: hubId,
|
|
24874
|
+
local_machine_id: machineId,
|
|
24875
|
+
context_type: "skills",
|
|
24876
|
+
content: skillsData.bundle,
|
|
24877
|
+
file_hash: bundleHash,
|
|
24878
|
+
timestamp: Date.now(),
|
|
24879
|
+
metadata: { file_count: skillsData.files.length, total_size: skillsData.totalBytes, files: skillsData.files }
|
|
24880
|
+
});
|
|
24881
|
+
results.push({ type: "skills (skills/)", status: res.status, files: skillsData.files.length, bytes: skillsData.totalBytes });
|
|
24882
|
+
spinner.succeed(`skills/ \u2014 ${res.status} (${skillsData.files.length} files, ${(skillsData.totalBytes / 1024).toFixed(1)} KB)`);
|
|
24883
|
+
} else {
|
|
24884
|
+
results.push({ type: "skills (skills/)", status: "dry-run", files: skillsData.files.length, bytes: skillsData.totalBytes });
|
|
24885
|
+
spinner.info(`skills/ \u2014 would push (${skillsData.files.length} files, ${(skillsData.totalBytes / 1024).toFixed(1)} KB)`);
|
|
24886
|
+
}
|
|
24887
|
+
} catch (err) {
|
|
24888
|
+
spinner.fail(`skills/ failed: ${err instanceof Error ? err.message : err}`);
|
|
24889
|
+
}
|
|
24890
|
+
} else {
|
|
24891
|
+
spinner.info("skills/ \u2014 no files found, skipping");
|
|
24892
|
+
}
|
|
24893
|
+
const totalFiles = results.reduce((n, r) => n + r.files, 0);
|
|
24894
|
+
const totalBytes = results.reduce((n, r) => n + r.bytes, 0);
|
|
24895
|
+
const pushed = results.filter((r) => r.status !== "unchanged" && r.status !== "dry-run").length;
|
|
24896
|
+
const unchanged = results.filter((r) => r.status === "unchanged").length;
|
|
24897
|
+
console.log(chalk38.cyan("\n Push Summary\n"));
|
|
24898
|
+
console.log(chalk38.white(" Context types: ") + chalk38.yellow(results.length.toString()));
|
|
24899
|
+
console.log(chalk38.white(" Files synced: ") + chalk38.green(totalFiles.toString()));
|
|
24900
|
+
console.log(chalk38.white(" Bytes pushed: ") + chalk38.gray(`${(totalBytes / 1024).toFixed(1)} KB`));
|
|
24901
|
+
console.log(chalk38.white(" Unchanged: ") + chalk38.gray(unchanged.toString()));
|
|
24902
|
+
if (totalConflicts > 0) {
|
|
24903
|
+
console.log(chalk38.white(" Conflicts: ") + chalk38.red(totalConflicts.toString()));
|
|
24904
|
+
console.log(chalk38.yellow("\n Resolve conflicts with: hubify sync resolve --hub <id>"));
|
|
24905
|
+
}
|
|
24906
|
+
console.log();
|
|
24907
|
+
process.exit(0);
|
|
24908
|
+
});
|
|
24909
|
+
var pullCommand = new Command35("pull").description("Pull latest workspace context from Convex hub to local files").option("--hub <id>", "Hub ID (default: from connect-state or HUB.yaml)").option("--dir <path>", "Project root to write files into (default: current directory)").option("--dry-run", "Show what would be written without making changes").option("--force", "Overwrite local files even if local version is newer").action(async (options) => {
|
|
24910
|
+
const spinner = ora35();
|
|
24911
|
+
const client = getClient();
|
|
24912
|
+
console.log(chalk38.cyan("\n Workspace Sync \u2014 Pull\n"));
|
|
24913
|
+
const hubId = resolveHubId(options);
|
|
24914
|
+
if (!hubId) {
|
|
24915
|
+
console.log(chalk38.red(" No hub ID found."));
|
|
24916
|
+
console.log(chalk38.yellow(" Run 'hubify connect' first, or pass --hub <id>"));
|
|
24917
|
+
process.exit(1);
|
|
24918
|
+
}
|
|
24919
|
+
const projectRoot = options.dir ? path19.resolve(options.dir) : process.cwd();
|
|
24920
|
+
console.log(chalk38.gray(` Hub: ${hubId.slice(0, 20)}...`));
|
|
24921
|
+
console.log(chalk38.gray(` Root: ${projectRoot}`));
|
|
24922
|
+
if (options.dryRun) console.log(chalk38.yellow(" Mode: dry-run (no writes)\n"));
|
|
24923
|
+
else console.log();
|
|
24924
|
+
spinner.start("Fetching workspace context from cloud...");
|
|
24925
|
+
let contexts;
|
|
24926
|
+
try {
|
|
24927
|
+
contexts = await client.query(toFunctionName(api.workspaceSync.pullWorkspaceContext), {
|
|
24928
|
+
hub_id: hubId
|
|
24929
|
+
});
|
|
24930
|
+
spinner.succeed(`Fetched ${Object.keys(contexts).length} context type(s) from cloud`);
|
|
24931
|
+
} catch (err) {
|
|
24932
|
+
spinner.fail(`Pull failed: ${err instanceof Error ? err.message : err}`);
|
|
24933
|
+
process.exit(1);
|
|
24934
|
+
}
|
|
24935
|
+
let pulled = 0;
|
|
24936
|
+
let unchanged = 0;
|
|
24937
|
+
let conflicts = 0;
|
|
24938
|
+
let totalBytes = 0;
|
|
24939
|
+
const writtenFiles = [];
|
|
24940
|
+
const fileMap = {
|
|
24941
|
+
soul: path19.join(projectRoot, "SOUL.md"),
|
|
24942
|
+
memory: path19.join(projectRoot, "MEMORY.md"),
|
|
24943
|
+
agents: path19.join(projectRoot, "AGENTS.md")
|
|
24944
|
+
};
|
|
24945
|
+
for (const [contextType, data] of Object.entries(contexts)) {
|
|
24946
|
+
if (!data?.content) {
|
|
24947
|
+
console.log(chalk38.gray(` ${contextType}: (empty, skipping)`));
|
|
24948
|
+
continue;
|
|
24949
|
+
}
|
|
24950
|
+
const syncedAt = new Date(data.synced_at).toLocaleString();
|
|
24951
|
+
if (fileMap[contextType]) {
|
|
24952
|
+
const localPath = fileMap[contextType];
|
|
24953
|
+
const localExists = fs19.existsSync(localPath);
|
|
24954
|
+
const localContent = localExists ? fs19.readFileSync(localPath, "utf-8") : null;
|
|
24955
|
+
const localHash = localContent ? hashContent3(localContent) : null;
|
|
24956
|
+
if (localHash === data.file_hash) {
|
|
24957
|
+
console.log(chalk38.gray(` ${path19.basename(localPath)} \u2014 already in sync`));
|
|
24958
|
+
unchanged++;
|
|
24959
|
+
continue;
|
|
24960
|
+
}
|
|
24961
|
+
const localMtime = localExists ? fs19.statSync(localPath).mtimeMs : 0;
|
|
24962
|
+
const isConflict = localExists && !options.force && data.file_timestamp && localMtime > data.file_timestamp;
|
|
24963
|
+
if (isConflict) {
|
|
24964
|
+
console.log(
|
|
24965
|
+
chalk38.yellow(` ${path19.basename(localPath)} \u2014 CONFLICT`) + chalk38.gray(` (local: ${new Date(localMtime).toLocaleString()}, cloud: ${syncedAt})`)
|
|
24966
|
+
);
|
|
24967
|
+
console.log(chalk38.gray(` Use --force to overwrite local, or 'hubify sync resolve' to choose`));
|
|
24968
|
+
conflicts++;
|
|
24969
|
+
continue;
|
|
24970
|
+
}
|
|
24971
|
+
if (!options.dryRun) {
|
|
24972
|
+
fs19.writeFileSync(localPath, data.content, "utf-8");
|
|
24973
|
+
writtenFiles.push(path19.basename(localPath));
|
|
24974
|
+
}
|
|
24975
|
+
console.log(
|
|
24976
|
+
chalk38.green(` ${path19.basename(localPath)} \u2014 ${options.dryRun ? "would update" : "updated"}`) + chalk38.gray(` (${data.content.length} bytes, synced ${syncedAt})`)
|
|
24977
|
+
);
|
|
24978
|
+
pulled++;
|
|
24979
|
+
totalBytes += data.content.length;
|
|
24980
|
+
} else if (contextType === "memories") {
|
|
24981
|
+
const memoriesDir = path19.join(projectRoot, "memory");
|
|
24982
|
+
if (data.metadata?.files && Array.isArray(data.metadata.files)) {
|
|
24983
|
+
if (!options.dryRun) fs19.mkdirSync(memoriesDir, { recursive: true });
|
|
24984
|
+
const sections = data.content.split(/\n\n### /);
|
|
24985
|
+
let fileCount = 0;
|
|
24986
|
+
for (const section of sections) {
|
|
24987
|
+
const newlineIdx = section.indexOf("\n");
|
|
24988
|
+
if (newlineIdx === -1) continue;
|
|
24989
|
+
const filename = section.slice(0, newlineIdx).replace(/^### /, "");
|
|
24990
|
+
const fileContent = section.slice(newlineIdx + 2);
|
|
24991
|
+
const outPath = path19.join(memoriesDir, filename);
|
|
24992
|
+
const localHash = fs19.existsSync(outPath) ? hashContent3(fs19.readFileSync(outPath, "utf-8")) : null;
|
|
24993
|
+
const cloudHash = hashContent3(fileContent);
|
|
24994
|
+
if (localHash === cloudHash) {
|
|
24995
|
+
unchanged++;
|
|
24996
|
+
continue;
|
|
24997
|
+
}
|
|
24998
|
+
const localMtime = fs19.existsSync(outPath) ? fs19.statSync(outPath).mtimeMs : 0;
|
|
24999
|
+
const isConflict = fs19.existsSync(outPath) && !options.force && data.file_timestamp && localMtime > data.file_timestamp;
|
|
25000
|
+
if (isConflict) {
|
|
25001
|
+
conflicts++;
|
|
25002
|
+
continue;
|
|
25003
|
+
}
|
|
25004
|
+
if (!options.dryRun) fs19.writeFileSync(outPath, fileContent, "utf-8");
|
|
25005
|
+
fileCount++;
|
|
25006
|
+
totalBytes += fileContent.length;
|
|
25007
|
+
}
|
|
25008
|
+
console.log(
|
|
25009
|
+
chalk38.green(` memory/ \u2014 ${options.dryRun ? "would update" : "updated"} ${fileCount} file(s)`) + chalk38.gray(` (synced ${syncedAt})`)
|
|
25010
|
+
);
|
|
25011
|
+
pulled += fileCount;
|
|
25012
|
+
} else {
|
|
25013
|
+
const outPath = path19.join(memoriesDir, "cloud-sync.md");
|
|
25014
|
+
if (!options.dryRun) {
|
|
25015
|
+
fs19.mkdirSync(memoriesDir, { recursive: true });
|
|
25016
|
+
fs19.writeFileSync(outPath, data.content, "utf-8");
|
|
25017
|
+
}
|
|
25018
|
+
console.log(
|
|
25019
|
+
chalk38.green(` memory/cloud-sync.md \u2014 ${options.dryRun ? "would update" : "updated"}`) + chalk38.gray(` (${data.content.length} bytes)`)
|
|
25020
|
+
);
|
|
25021
|
+
pulled++;
|
|
25022
|
+
totalBytes += data.content.length;
|
|
25023
|
+
}
|
|
25024
|
+
} else if (contextType === "skills") {
|
|
25025
|
+
const skillsDir = path19.join(projectRoot, "skills");
|
|
25026
|
+
if (!options.dryRun) fs19.mkdirSync(skillsDir, { recursive: true });
|
|
25027
|
+
const sections = data.content.split(/\n\n### /);
|
|
25028
|
+
let fileCount = 0;
|
|
25029
|
+
for (const section of sections) {
|
|
25030
|
+
const newlineIdx = section.indexOf("\n");
|
|
25031
|
+
if (newlineIdx === -1) continue;
|
|
25032
|
+
const filename = section.slice(0, newlineIdx).replace(/^### /, "");
|
|
25033
|
+
const fileContent = section.slice(newlineIdx + 2);
|
|
25034
|
+
const outPath = path19.join(skillsDir, filename);
|
|
25035
|
+
const localHash = fs19.existsSync(outPath) ? hashContent3(fs19.readFileSync(outPath, "utf-8")) : null;
|
|
25036
|
+
if (localHash === hashContent3(fileContent)) {
|
|
25037
|
+
unchanged++;
|
|
25038
|
+
continue;
|
|
25039
|
+
}
|
|
25040
|
+
const localMtime = fs19.existsSync(outPath) ? fs19.statSync(outPath).mtimeMs : 0;
|
|
25041
|
+
const isConflict = fs19.existsSync(outPath) && !options.force && data.file_timestamp && localMtime > data.file_timestamp;
|
|
25042
|
+
if (isConflict) {
|
|
25043
|
+
conflicts++;
|
|
25044
|
+
continue;
|
|
25045
|
+
}
|
|
25046
|
+
if (!options.dryRun) fs19.writeFileSync(outPath, fileContent, "utf-8");
|
|
25047
|
+
fileCount++;
|
|
25048
|
+
totalBytes += fileContent.length;
|
|
25049
|
+
}
|
|
25050
|
+
if (fileCount > 0) {
|
|
25051
|
+
console.log(
|
|
25052
|
+
chalk38.green(` skills/ \u2014 ${options.dryRun ? "would update" : "updated"} ${fileCount} file(s)`) + chalk38.gray(` (synced ${syncedAt})`)
|
|
25053
|
+
);
|
|
25054
|
+
pulled += fileCount;
|
|
25055
|
+
} else {
|
|
25056
|
+
console.log(chalk38.gray(` skills/ \u2014 already in sync (${sections.length - 1} skills)`));
|
|
25057
|
+
unchanged++;
|
|
25058
|
+
}
|
|
25059
|
+
}
|
|
25060
|
+
}
|
|
25061
|
+
console.log(chalk38.cyan("\n Pull Summary\n"));
|
|
25062
|
+
console.log(chalk38.white(" Files updated: ") + chalk38.green(pulled.toString()));
|
|
25063
|
+
console.log(chalk38.white(" Bytes received: ") + chalk38.gray(`${(totalBytes / 1024).toFixed(1)} KB`));
|
|
25064
|
+
console.log(chalk38.white(" Unchanged: ") + chalk38.gray(unchanged.toString()));
|
|
25065
|
+
if (conflicts > 0) {
|
|
25066
|
+
console.log(chalk38.white(" Conflicts: ") + chalk38.red(conflicts.toString()));
|
|
25067
|
+
console.log(chalk38.yellow("\n Use --force to overwrite local files, or resolve manually."));
|
|
25068
|
+
}
|
|
25069
|
+
console.log();
|
|
25070
|
+
process.exit(0);
|
|
25071
|
+
});
|
|
24387
25072
|
syncCommand.addCommand(statusCommand);
|
|
24388
25073
|
syncCommand.addCommand(logsCommand);
|
|
24389
25074
|
syncCommand.addCommand(statsCommand3);
|
|
25075
|
+
syncCommand.addCommand(pushCommand);
|
|
25076
|
+
syncCommand.addCommand(pullCommand);
|
|
24390
25077
|
|
|
24391
25078
|
// src/commands/test.ts
|
|
24392
25079
|
import { Command as Command36 } from "commander";
|
|
@@ -24547,7 +25234,7 @@ echo "=== Test Complete ==="
|
|
|
24547
25234
|
}
|
|
24548
25235
|
async function mockSandboxExecution(skillContent, skillName, criteria) {
|
|
24549
25236
|
console.log(chalk39.yellow("\n \u26A0 E2B API key not configured - running mock sandbox test\n"));
|
|
24550
|
-
await new Promise((
|
|
25237
|
+
await new Promise((resolve12) => setTimeout(resolve12, 1500));
|
|
24551
25238
|
const securityViolations = [];
|
|
24552
25239
|
if (/curl.*\|.*sh|wget.*\|.*bash|rm\s+-rf\s+\//i.test(skillContent)) {
|
|
24553
25240
|
securityViolations.push("Dangerous shell pattern detected");
|
|
@@ -25744,6 +26431,7 @@ function formatTrajectory(trajectory) {
|
|
|
25744
26431
|
import { Command as Command40 } from "commander";
|
|
25745
26432
|
import chalk43 from "chalk";
|
|
25746
26433
|
import ora40 from "ora";
|
|
26434
|
+
import { execSync } from "child_process";
|
|
25747
26435
|
function timeAgo(ts) {
|
|
25748
26436
|
const delta = Date.now() - ts;
|
|
25749
26437
|
if (delta < 6e4) return "just now";
|
|
@@ -26010,32 +26698,255 @@ var publishSubcommand = new Command40("publish").description("Publish research f
|
|
|
26010
26698
|
spinner.fail(error.message || "Failed to publish findings");
|
|
26011
26699
|
}
|
|
26012
26700
|
});
|
|
26013
|
-
|
|
26014
|
-
|
|
26015
|
-
|
|
26016
|
-
researchCommand.addCommand(joinSubcommand);
|
|
26017
|
-
researchCommand.addCommand(viewSubcommand);
|
|
26018
|
-
researchCommand.addCommand(approveSubcommand);
|
|
26019
|
-
researchCommand.addCommand(startSubcommand);
|
|
26020
|
-
researchCommand.addCommand(completeSubcommand);
|
|
26021
|
-
researchCommand.addCommand(publishSubcommand);
|
|
26022
|
-
|
|
26023
|
-
// src/commands/auth.ts
|
|
26024
|
-
import { Command as Command41 } from "commander";
|
|
26025
|
-
import chalk44 from "chalk";
|
|
26026
|
-
import ora41 from "ora";
|
|
26027
|
-
import fs21 from "fs";
|
|
26028
|
-
import path21 from "path";
|
|
26029
|
-
import os10 from "os";
|
|
26030
|
-
import crypto5 from "crypto";
|
|
26031
|
-
var CREDENTIALS_DIR = path21.join(os10.homedir(), ".hubify");
|
|
26032
|
-
var CREDENTIALS_FILE = path21.join(CREDENTIALS_DIR, "credentials.json");
|
|
26033
|
-
function hashToken(token) {
|
|
26034
|
-
return crypto5.createHash("sha256").update(token).digest("hex");
|
|
26035
|
-
}
|
|
26036
|
-
function loadCredentials() {
|
|
26701
|
+
var toolsCommand = new Command40("tools").description("Manage the hubify-research Python toolkit on workspaces");
|
|
26702
|
+
var toolsCheckSubcommand = new Command40("check").description("Validate research environment (API keys + connectivity)").option("--test", "Also run connectivity tests").action(async (options) => {
|
|
26703
|
+
const spinner = ora40("Checking research environment...").start();
|
|
26037
26704
|
try {
|
|
26038
|
-
|
|
26705
|
+
const cmd = options.test ? "python3 -m hubify_research check --test" : "python3 -m hubify_research check";
|
|
26706
|
+
const output = execSync(cmd, { encoding: "utf-8", timeout: 3e4 });
|
|
26707
|
+
spinner.stop();
|
|
26708
|
+
console.log(output);
|
|
26709
|
+
} catch (error) {
|
|
26710
|
+
spinner.fail("Environment check failed");
|
|
26711
|
+
if (error.stdout) console.log(error.stdout);
|
|
26712
|
+
if (error.stderr) console.error(chalk43.red(error.stderr));
|
|
26713
|
+
}
|
|
26714
|
+
});
|
|
26715
|
+
var toolsInstallSubcommand = new Command40("install").description("Install hubify-research toolkit (pip install)").option("--dev", "Install in development mode (editable)").action(async (options) => {
|
|
26716
|
+
const spinner = ora40("Installing hubify-research...").start();
|
|
26717
|
+
try {
|
|
26718
|
+
const cmd = options.dev ? "pip install -e packages/sdk/python/" : "pip install hubify-research";
|
|
26719
|
+
const output = execSync(cmd, { encoding: "utf-8", timeout: 12e4 });
|
|
26720
|
+
spinner.succeed("hubify-research installed");
|
|
26721
|
+
console.log(chalk43.gray(output.trim().split("\n").slice(-3).join("\n")));
|
|
26722
|
+
} catch (error) {
|
|
26723
|
+
spinner.fail("Installation failed");
|
|
26724
|
+
if (error.stderr) console.error(chalk43.red(error.stderr));
|
|
26725
|
+
}
|
|
26726
|
+
});
|
|
26727
|
+
toolsCommand.addCommand(toolsCheckSubcommand);
|
|
26728
|
+
toolsCommand.addCommand(toolsInstallSubcommand);
|
|
26729
|
+
var searchSubcommand2 = new Command40("search").description("Search literature across NASA ADS, arXiv, Semantic Scholar, Perplexity").argument("<query>", "Search query").option("--category <cat>", "arXiv category filter (e.g., cs.AI, astro-ph.CO)").action(async (query, options) => {
|
|
26730
|
+
const spinner = ora40("Searching literature...").start();
|
|
26731
|
+
try {
|
|
26732
|
+
const env = { ...process.env, HUBIFY_SEARCH_QUERY: query };
|
|
26733
|
+
if (options.category) env.HUBIFY_SEARCH_CATEGORY = options.category;
|
|
26734
|
+
const output = execSync(
|
|
26735
|
+
`python3 -c "import os, json; from hubify_research.literature import search; print(json.dumps(search(os.environ['HUBIFY_SEARCH_QUERY'], max_results=5, category=os.environ.get('HUBIFY_SEARCH_CATEGORY')), indent=2, default=str))"`,
|
|
26736
|
+
{ encoding: "utf-8", timeout: 6e4, env }
|
|
26737
|
+
);
|
|
26738
|
+
spinner.stop();
|
|
26739
|
+
console.log(output);
|
|
26740
|
+
} catch (error) {
|
|
26741
|
+
spinner.fail("Search failed");
|
|
26742
|
+
if (error.stdout) console.log(error.stdout);
|
|
26743
|
+
if (error.stderr) console.error(chalk43.red(error.stderr));
|
|
26744
|
+
}
|
|
26745
|
+
});
|
|
26746
|
+
var verifySubcommand = new Command40("verify").description("Verify a mathematical equation via Wolfram Alpha + DeepSeek R1").argument("<equation>", "Mathematical expression to verify").option("--expected <value>", "Expected result for comparison").action(async (equation, options) => {
|
|
26747
|
+
const spinner = ora40("Verifying equation...").start();
|
|
26748
|
+
try {
|
|
26749
|
+
const expected = options.expected || "";
|
|
26750
|
+
const output = execSync(
|
|
26751
|
+
`python3 -c "import os, json; from hubify_research.computation import verify_equation; print(json.dumps(verify_equation(os.environ['EQ'], os.environ['EX']), indent=2, default=str))"`,
|
|
26752
|
+
{
|
|
26753
|
+
encoding: "utf-8",
|
|
26754
|
+
timeout: 18e4,
|
|
26755
|
+
env: { ...process.env, EQ: equation, EX: expected }
|
|
26756
|
+
}
|
|
26757
|
+
);
|
|
26758
|
+
spinner.stop();
|
|
26759
|
+
console.log(output);
|
|
26760
|
+
} catch (error) {
|
|
26761
|
+
spinner.fail("Verification failed");
|
|
26762
|
+
if (error.stdout) console.log(error.stdout);
|
|
26763
|
+
if (error.stderr) console.error(chalk43.red(error.stderr));
|
|
26764
|
+
}
|
|
26765
|
+
});
|
|
26766
|
+
var gpuCommand = new Command40("gpu").description("Manage RunPod GPU pods for research");
|
|
26767
|
+
var gpuStatusSubcommand = new Command40("status").description("Show RunPod pod status").argument("[pod-id]", "Specific pod ID (optional)").action(async (podId) => {
|
|
26768
|
+
const spinner = ora40("Loading GPU status...").start();
|
|
26769
|
+
try {
|
|
26770
|
+
if (podId && !/^[a-zA-Z0-9_-]+$/.test(podId)) {
|
|
26771
|
+
spinner.fail("Invalid pod ID format");
|
|
26772
|
+
return;
|
|
26773
|
+
}
|
|
26774
|
+
const cmd = podId ? `python3 -m hubify_research gpu status ${podId}` : "python3 -m hubify_research gpu status";
|
|
26775
|
+
const output = execSync(cmd, { encoding: "utf-8", timeout: 3e4 });
|
|
26776
|
+
spinner.stop();
|
|
26777
|
+
console.log(output);
|
|
26778
|
+
} catch (error) {
|
|
26779
|
+
spinner.fail("Failed to get GPU status");
|
|
26780
|
+
if (error.stdout) console.log(error.stdout);
|
|
26781
|
+
if (error.stderr) console.error(chalk43.red(error.stderr));
|
|
26782
|
+
}
|
|
26783
|
+
});
|
|
26784
|
+
var gpuCreateSubcommand = new Command40("create").description("Create a new RunPod GPU pod").option("--name <name>", "Pod name", "hubify-research").option("--gpu <type>", "GPU type", "NVIDIA RTX 4090").action(async (options) => {
|
|
26785
|
+
const spinner = ora40(`Creating RunPod pod (${options.gpu})...`).start();
|
|
26786
|
+
try {
|
|
26787
|
+
const output = execSync(
|
|
26788
|
+
`python3 -c "import os, json; from hubify_research.gpu.runpod import create_pod; print(json.dumps(create_pod(name=os.environ['POD_NAME'], gpu_type=os.environ['GPU_TYPE']), indent=2, default=str))"`,
|
|
26789
|
+
{ encoding: "utf-8", timeout: 6e4, env: { ...process.env, POD_NAME: options.name, GPU_TYPE: options.gpu } }
|
|
26790
|
+
);
|
|
26791
|
+
spinner.succeed("Pod created");
|
|
26792
|
+
console.log(output);
|
|
26793
|
+
} catch (error) {
|
|
26794
|
+
spinner.fail("Failed to create pod");
|
|
26795
|
+
if (error.stdout) console.log(error.stdout);
|
|
26796
|
+
if (error.stderr) console.error(chalk43.red(error.stderr));
|
|
26797
|
+
}
|
|
26798
|
+
});
|
|
26799
|
+
var gpuListSubcommand = new Command40("list").description("List available GPU types with pricing").action(async () => {
|
|
26800
|
+
const spinner = ora40("Loading GPU types...").start();
|
|
26801
|
+
try {
|
|
26802
|
+
const output = execSync("python3 -m hubify_research gpu gpus", {
|
|
26803
|
+
encoding: "utf-8",
|
|
26804
|
+
timeout: 3e4
|
|
26805
|
+
});
|
|
26806
|
+
spinner.stop();
|
|
26807
|
+
console.log(output);
|
|
26808
|
+
} catch (error) {
|
|
26809
|
+
spinner.fail("Failed to list GPUs");
|
|
26810
|
+
if (error.stderr) console.error(chalk43.red(error.stderr));
|
|
26811
|
+
}
|
|
26812
|
+
});
|
|
26813
|
+
gpuCommand.addCommand(gpuStatusSubcommand);
|
|
26814
|
+
gpuCommand.addCommand(gpuCreateSubcommand);
|
|
26815
|
+
gpuCommand.addCommand(gpuListSubcommand);
|
|
26816
|
+
var labsCommand = new Command40("labs").description("Manage research labs \u2014 dedicated research workspaces");
|
|
26817
|
+
var labsCreateSubcommand = new Command40("create").description("Create a new research lab").argument("<name>", "Lab name").option("--compute <tier>", "Compute tier: e2b, fly, or runpod", "e2b").option("--budget-usd <amount>", "Budget in USD").option("--budget-hours <hours>", "Time budget in hours").action(async (name, options) => {
|
|
26818
|
+
const spinner = ora40("Creating research lab...").start();
|
|
26819
|
+
try {
|
|
26820
|
+
const client = getClient();
|
|
26821
|
+
const result = await client.mutation(toFunctionName(api.researchLabs.createResearchLab), {
|
|
26822
|
+
name,
|
|
26823
|
+
compute_tier: options.compute,
|
|
26824
|
+
auto_publish_findings: true,
|
|
26825
|
+
budget_usd: options.budgetUsd ? parseFloat(options.budgetUsd) : void 0,
|
|
26826
|
+
budget_hours: options.budgetHours ? parseFloat(options.budgetHours) : void 0
|
|
26827
|
+
});
|
|
26828
|
+
spinner.succeed(`Research lab created: ${name}`);
|
|
26829
|
+
console.log(chalk43.gray(` Hub ID: ${result.hub_id}`));
|
|
26830
|
+
} catch (error) {
|
|
26831
|
+
spinner.fail("Failed to create research lab");
|
|
26832
|
+
console.error(chalk43.red(error.message));
|
|
26833
|
+
}
|
|
26834
|
+
});
|
|
26835
|
+
var labsSummarySubcommand = new Command40("summary").description("Get research lab summary \u2014 missions, experiments, findings").argument("<hub-id>", "Hub ID of the research lab").action(async (hubId) => {
|
|
26836
|
+
const spinner = ora40("Loading lab summary...").start();
|
|
26837
|
+
try {
|
|
26838
|
+
const client = getClient();
|
|
26839
|
+
const summary = await client.query(toFunctionName(api.researchLabs.getLabSummary), {
|
|
26840
|
+
hub_id: hubId
|
|
26841
|
+
});
|
|
26842
|
+
spinner.stop();
|
|
26843
|
+
if (!summary) {
|
|
26844
|
+
console.log(chalk43.yellow("Research lab not found"));
|
|
26845
|
+
return;
|
|
26846
|
+
}
|
|
26847
|
+
console.log(chalk43.bold(`
|
|
26848
|
+
${summary.hub_name} (${summary.hub_type})`));
|
|
26849
|
+
console.log(chalk43.gray(" " + "\u2500".repeat(40)));
|
|
26850
|
+
console.log(` Missions: ${summary.missions.active} active / ${summary.missions.total} total`);
|
|
26851
|
+
console.log(` Experiments: ${summary.experiments.total} run ($${summary.experiments.total_cost_usd.toFixed(2)})`);
|
|
26852
|
+
console.log(` Best metric: ${summary.experiments.best_metric.toFixed(4)}`);
|
|
26853
|
+
console.log(` Findings: ${summary.knowledge.findings} published`);
|
|
26854
|
+
console.log(` Threads: ${summary.knowledge.total_threads} (score: ${summary.knowledge.total_score})`);
|
|
26855
|
+
} catch (error) {
|
|
26856
|
+
spinner.fail("Failed to load summary");
|
|
26857
|
+
console.error(chalk43.red(error.message));
|
|
26858
|
+
}
|
|
26859
|
+
});
|
|
26860
|
+
var labsListSubcommand = new Command40("list").description("List all research labs").action(async () => {
|
|
26861
|
+
const spinner = ora40("Loading research labs...").start();
|
|
26862
|
+
try {
|
|
26863
|
+
const client = getClient();
|
|
26864
|
+
const labs = await client.query(toFunctionName(api.researchLabs.listLabs), {});
|
|
26865
|
+
spinner.stop();
|
|
26866
|
+
if (labs.length === 0) {
|
|
26867
|
+
console.log(chalk43.yellow("No research labs found. Create one with: hubify research labs create <name>"));
|
|
26868
|
+
return;
|
|
26869
|
+
}
|
|
26870
|
+
console.log(chalk43.bold(`
|
|
26871
|
+
Research Labs (${labs.length})`));
|
|
26872
|
+
for (const lab of labs) {
|
|
26873
|
+
const tier = lab.research_config?.compute_tier ?? "\u2014";
|
|
26874
|
+
console.log(` ${chalk43.cyan(lab.name)} [${tier}] \u2014 ${lab._id}`);
|
|
26875
|
+
}
|
|
26876
|
+
} catch (error) {
|
|
26877
|
+
spinner.fail("Failed to list labs");
|
|
26878
|
+
console.error(chalk43.red(error.message));
|
|
26879
|
+
}
|
|
26880
|
+
});
|
|
26881
|
+
var labsToolkitSubcommand = new Command40("toolkit").description("Show research toolkit status and capabilities").action(async () => {
|
|
26882
|
+
const client = getClient();
|
|
26883
|
+
const status = await client.query(toFunctionName(api.researchLabs.getToolkitStatus), {});
|
|
26884
|
+
console.log(chalk43.bold(`
|
|
26885
|
+
${status.toolkit} v${status.version}`));
|
|
26886
|
+
console.log(chalk43.gray(" " + "\u2500".repeat(40)));
|
|
26887
|
+
for (const mod of status.modules) {
|
|
26888
|
+
console.log(` ${chalk43.cyan(mod.name.padEnd(16))} ${mod.description}`);
|
|
26889
|
+
}
|
|
26890
|
+
console.log(`
|
|
26891
|
+
Install: ${chalk43.green(status.install)}`);
|
|
26892
|
+
console.log(` Check: ${chalk43.green(status.cli)}`);
|
|
26893
|
+
});
|
|
26894
|
+
labsCommand.addCommand(labsCreateSubcommand);
|
|
26895
|
+
labsCommand.addCommand(labsSummarySubcommand);
|
|
26896
|
+
labsCommand.addCommand(labsListSubcommand);
|
|
26897
|
+
labsCommand.addCommand(labsToolkitSubcommand);
|
|
26898
|
+
var findingCommand = new Command40("finding").description("Publish a research finding to knowledge threads").argument("<title>", "Finding title").option("--hub <hub-id>", "Hub ID to publish to").option("--mission <mission-id>", "Link to research mission").option("--body <body>", "Finding body (detailed description)").option("--confidence <score>", "Confidence score 0-1").option("--tags <tags>", "Comma-separated tags").action(async (title, options) => {
|
|
26899
|
+
const spinner = ora40("Publishing finding...").start();
|
|
26900
|
+
try {
|
|
26901
|
+
const client = getClient();
|
|
26902
|
+
const result = await client.mutation(toFunctionName(api.researchLabs.publishFinding), {
|
|
26903
|
+
hub_id: options.hub,
|
|
26904
|
+
title,
|
|
26905
|
+
body: options.body || title,
|
|
26906
|
+
mission_id: options.mission || void 0,
|
|
26907
|
+
author_agent_id: "cli-user",
|
|
26908
|
+
tags: options.tags ? options.tags.split(",").map((t) => t.trim()) : void 0,
|
|
26909
|
+
confidence: options.confidence ? parseFloat(options.confidence) : void 0
|
|
26910
|
+
});
|
|
26911
|
+
spinner.succeed("Finding published");
|
|
26912
|
+
console.log(chalk43.gray(` Thread ID: ${result.thread_id}`));
|
|
26913
|
+
} catch (error) {
|
|
26914
|
+
spinner.fail("Failed to publish finding");
|
|
26915
|
+
console.error(chalk43.red(error.message));
|
|
26916
|
+
}
|
|
26917
|
+
});
|
|
26918
|
+
researchCommand.addCommand(listSubcommand2);
|
|
26919
|
+
researchCommand.addCommand(proposeSubcommand);
|
|
26920
|
+
researchCommand.addCommand(updateSubcommand2);
|
|
26921
|
+
researchCommand.addCommand(joinSubcommand);
|
|
26922
|
+
researchCommand.addCommand(viewSubcommand);
|
|
26923
|
+
researchCommand.addCommand(approveSubcommand);
|
|
26924
|
+
researchCommand.addCommand(startSubcommand);
|
|
26925
|
+
researchCommand.addCommand(completeSubcommand);
|
|
26926
|
+
researchCommand.addCommand(publishSubcommand);
|
|
26927
|
+
researchCommand.addCommand(toolsCommand);
|
|
26928
|
+
researchCommand.addCommand(searchSubcommand2);
|
|
26929
|
+
researchCommand.addCommand(verifySubcommand);
|
|
26930
|
+
researchCommand.addCommand(gpuCommand);
|
|
26931
|
+
researchCommand.addCommand(labsCommand);
|
|
26932
|
+
researchCommand.addCommand(findingCommand);
|
|
26933
|
+
|
|
26934
|
+
// src/commands/auth.ts
|
|
26935
|
+
import { Command as Command41 } from "commander";
|
|
26936
|
+
import chalk44 from "chalk";
|
|
26937
|
+
import ora41 from "ora";
|
|
26938
|
+
import fs21 from "fs";
|
|
26939
|
+
import path21 from "path";
|
|
26940
|
+
import os11 from "os";
|
|
26941
|
+
import crypto6 from "crypto";
|
|
26942
|
+
var CREDENTIALS_DIR = path21.join(os11.homedir(), ".hubify");
|
|
26943
|
+
var CREDENTIALS_FILE = path21.join(CREDENTIALS_DIR, "credentials.json");
|
|
26944
|
+
function hashToken(token) {
|
|
26945
|
+
return crypto6.createHash("sha256").update(token).digest("hex");
|
|
26946
|
+
}
|
|
26947
|
+
function loadCredentials() {
|
|
26948
|
+
try {
|
|
26949
|
+
if (fs21.existsSync(CREDENTIALS_FILE)) {
|
|
26039
26950
|
const raw = fs21.readFileSync(CREDENTIALS_FILE, "utf-8");
|
|
26040
26951
|
return JSON.parse(raw);
|
|
26041
26952
|
}
|
|
@@ -26919,7 +27830,7 @@ ingestCommand.command("logs").description("View ingest operation logs").option("
|
|
|
26919
27830
|
import { Command as Command46 } from "commander";
|
|
26920
27831
|
import chalk48 from "chalk";
|
|
26921
27832
|
import ora45 from "ora";
|
|
26922
|
-
var
|
|
27833
|
+
var labsCommand2 = new Command46("labs").description("Hubify Labs \u2014 autonomous experiment swarms and research DAGs").addCommand(labsStatusCommand()).addCommand(labsMissionCommand()).addCommand(labsFrontierCommand()).addCommand(labsDagCommand()).addCommand(labsBestPathCommand()).addCommand(labsChannelsCommand()).addCommand(labsLaunchCommand()).addCommand(labsLaunchResearchCommand()).addCommand(labsListExperimentableCommand()).addCommand(labsSuggestCommand());
|
|
26923
27834
|
function labsStatusCommand() {
|
|
26924
27835
|
return new Command46("status").description("Show active experiment missions").option("--limit <n>", "Max missions to show", "10").option("--json", "Output as JSON").action(async (options) => {
|
|
26925
27836
|
const spinner = ora45("Fetching experiment missions...").start();
|
|
@@ -27106,16 +28017,16 @@ function labsBestPathCommand() {
|
|
|
27106
28017
|
const spinner = ora45("Tracing best path...").start();
|
|
27107
28018
|
try {
|
|
27108
28019
|
const client = getClient();
|
|
27109
|
-
const
|
|
28020
|
+
const path27 = await client.query(
|
|
27110
28021
|
toFunctionName(api.experimentDag.getBestPath),
|
|
27111
28022
|
{ mission_id: missionId }
|
|
27112
28023
|
);
|
|
27113
28024
|
spinner.stop();
|
|
27114
28025
|
if (options.json) {
|
|
27115
|
-
console.log(JSON.stringify(
|
|
28026
|
+
console.log(JSON.stringify(path27, null, 2));
|
|
27116
28027
|
return;
|
|
27117
28028
|
}
|
|
27118
|
-
const nodes =
|
|
28029
|
+
const nodes = path27;
|
|
27119
28030
|
if (nodes.length === 0) {
|
|
27120
28031
|
console.log(chalk48.dim("\n No best path found.\n"));
|
|
27121
28032
|
return;
|
|
@@ -28170,57 +29081,78 @@ function typeIcon(type) {
|
|
|
28170
29081
|
return chalk55.blue("FINDING");
|
|
28171
29082
|
case "technique":
|
|
28172
29083
|
return chalk55.white("TECHNIQUE");
|
|
29084
|
+
case "experiment_result":
|
|
29085
|
+
return chalk55.yellow("EXPERIMENT");
|
|
29086
|
+
case "failure_lesson":
|
|
29087
|
+
return chalk55.red("FAILURE");
|
|
28173
29088
|
case "failure-lesson":
|
|
28174
29089
|
return chalk55.red("FAILURE");
|
|
28175
29090
|
default:
|
|
28176
29091
|
return chalk55.dim(type.toUpperCase());
|
|
28177
29092
|
}
|
|
28178
29093
|
}
|
|
29094
|
+
function toBackendType(type) {
|
|
29095
|
+
const map = {
|
|
29096
|
+
"failure-lesson": "failure_lesson",
|
|
29097
|
+
"experiment-result": "experiment_result"
|
|
29098
|
+
};
|
|
29099
|
+
return map[type] ?? type;
|
|
29100
|
+
}
|
|
28179
29101
|
function collectiveFeedCommand() {
|
|
28180
|
-
return new Command52("feed").description("Show the global collective intelligence feed").option("--limit <n>", "Max entries", "50").option("--type <type>", "Filter: learning,
|
|
29102
|
+
return new Command52("feed").description("Show the global collective intelligence feed").option("--limit <n>", "Max entries", "50").option("--type <type>", "Filter: learning, pattern, finding, technique, experiment-result, failure-lesson, all", "all").option("--json", "Output as JSON").action(async (options) => {
|
|
28181
29103
|
const spinner = ora51("Pulling collective intelligence feed...").start();
|
|
28182
29104
|
try {
|
|
28183
29105
|
const client = getClient();
|
|
28184
|
-
const
|
|
29106
|
+
const limit = parseInt(options.limit);
|
|
29107
|
+
const collectiveArgs = { limit };
|
|
29108
|
+
if (options.type !== "all") {
|
|
29109
|
+
collectiveArgs.type = toBackendType(options.type);
|
|
29110
|
+
}
|
|
29111
|
+
const [collectiveFeed, singularityFeed] = await Promise.all([
|
|
28185
29112
|
client.query(
|
|
28186
|
-
toFunctionName(api.
|
|
28187
|
-
|
|
29113
|
+
toFunctionName(api.collective.getFeed),
|
|
29114
|
+
collectiveArgs
|
|
28188
29115
|
),
|
|
28189
29116
|
client.query(
|
|
28190
|
-
toFunctionName(api.
|
|
28191
|
-
{ limit:
|
|
28192
|
-
)
|
|
29117
|
+
toFunctionName(api.singularity.getIntelligenceFeed),
|
|
29118
|
+
{ limit, type: options.type }
|
|
29119
|
+
).catch(() => [])
|
|
29120
|
+
// graceful fallback if singularity unavailable
|
|
28193
29121
|
]);
|
|
28194
29122
|
spinner.stop();
|
|
29123
|
+
const collectiveEntries = collectiveFeed;
|
|
29124
|
+
const singularityEntries = singularityFeed;
|
|
28195
29125
|
if (options.json) {
|
|
28196
|
-
console.log(JSON.stringify({
|
|
29126
|
+
console.log(JSON.stringify({ collective: collectiveEntries, singularity: singularityEntries }, null, 2));
|
|
28197
29127
|
return;
|
|
28198
29128
|
}
|
|
28199
|
-
const feedEntries = feed;
|
|
28200
|
-
const learningEntries = recentLearnings;
|
|
28201
29129
|
const unified = [];
|
|
28202
|
-
for (const entry of
|
|
29130
|
+
for (const entry of collectiveEntries) {
|
|
28203
29131
|
unified.push({
|
|
28204
29132
|
type: entry.type ?? "learning",
|
|
28205
|
-
title: entry.title ??
|
|
28206
|
-
detail: entry.
|
|
28207
|
-
source: entry.
|
|
28208
|
-
timestamp: entry.
|
|
29133
|
+
title: entry.title ?? "\u2014",
|
|
29134
|
+
detail: entry.content ?? "",
|
|
29135
|
+
source: entry.source_agent_id ?? "collective",
|
|
29136
|
+
timestamp: entry.created_at ?? entry._creationTime ?? Date.now(),
|
|
29137
|
+
origin: "collective"
|
|
28209
29138
|
});
|
|
28210
29139
|
}
|
|
28211
|
-
for (const
|
|
28212
|
-
const
|
|
28213
|
-
|
|
29140
|
+
for (const entry of singularityEntries) {
|
|
29141
|
+
const isDupe = unified.some(
|
|
29142
|
+
(u) => u.title === (entry.title ?? entry.skill_name) && Math.abs(u.timestamp - (entry.timestamp ?? 0)) < 1e3
|
|
29143
|
+
);
|
|
29144
|
+
if (isDupe) continue;
|
|
28214
29145
|
unified.push({
|
|
28215
|
-
type,
|
|
28216
|
-
title:
|
|
28217
|
-
detail:
|
|
28218
|
-
source:
|
|
28219
|
-
timestamp:
|
|
29146
|
+
type: entry.type ?? "learning",
|
|
29147
|
+
title: entry.title ?? entry.skill_name ?? "\u2014",
|
|
29148
|
+
detail: entry.details ?? entry.description ?? "",
|
|
29149
|
+
source: entry.agent_id ?? "network",
|
|
29150
|
+
timestamp: entry.timestamp ?? entry._creationTime ?? Date.now(),
|
|
29151
|
+
origin: "singularity"
|
|
28220
29152
|
});
|
|
28221
29153
|
}
|
|
28222
29154
|
unified.sort((a, b) => b.timestamp - a.timestamp);
|
|
28223
|
-
const display = unified.slice(0,
|
|
29155
|
+
const display = unified.slice(0, limit);
|
|
28224
29156
|
if (display.length === 0) {
|
|
28225
29157
|
console.log(chalk55.dim("\n No collective intelligence entries found.\n"));
|
|
28226
29158
|
return;
|
|
@@ -28237,7 +29169,7 @@ function collectiveFeedCommand() {
|
|
|
28237
29169
|
const detail = entry.detail.length > 120 ? entry.detail.slice(0, 117) + "..." : entry.detail;
|
|
28238
29170
|
console.log(` ${chalk55.dim(detail)}`);
|
|
28239
29171
|
}
|
|
28240
|
-
console.log(` ${chalk55.dim("source:")} ${chalk55.dim(entry.source)}`);
|
|
29172
|
+
console.log(` ${chalk55.dim("source:")} ${chalk55.dim(entry.source)} ${chalk55.dim("via")} ${chalk55.dim(entry.origin)}`);
|
|
28241
29173
|
console.log();
|
|
28242
29174
|
}
|
|
28243
29175
|
} catch (error) {
|
|
@@ -28247,8 +29179,8 @@ function collectiveFeedCommand() {
|
|
|
28247
29179
|
});
|
|
28248
29180
|
}
|
|
28249
29181
|
function collectiveShareCommand() {
|
|
28250
|
-
return new Command52("share").description("Share an insight to the collective intelligence").argument("<type>", "Insight type: learning, pattern, finding, technique, failure-lesson").argument("<content>", "The insight content to share").option("--skill <name>", "Link to a skill name").option("--confidence <n>", "Confidence score 0-1", "0.7").option("--tags <tags>", "Comma-separated tags").option("--context <ctx>", "What you were doing when you learned this").option("--agent <id>", "Agent ID (defaults to cli-agent)").option("--json", "Output as JSON").action(async (type, content, options) => {
|
|
28251
|
-
const validTypes = ["learning", "pattern", "finding", "technique", "failure-lesson"];
|
|
29182
|
+
return new Command52("share").description("Share an insight to the collective intelligence").argument("<type>", "Insight type: learning, pattern, finding, technique, experiment-result, failure-lesson").argument("<content>", "The insight content to share").option("--skill <name>", "Link to a skill name").option("--confidence <n>", "Confidence score 0-1", "0.7").option("--tags <tags>", "Comma-separated tags").option("--context <ctx>", "What you were doing when you learned this").option("--agent <id>", "Agent ID (defaults to cli-agent)").option("--workspace <id>", "Workspace ID").option("--json", "Output as JSON").action(async (type, content, options) => {
|
|
29183
|
+
const validTypes = ["learning", "pattern", "finding", "technique", "experiment-result", "failure-lesson"];
|
|
28252
29184
|
if (!validTypes.includes(type)) {
|
|
28253
29185
|
console.log(chalk55.red(`
|
|
28254
29186
|
Invalid type: ${type}`));
|
|
@@ -28259,32 +29191,23 @@ function collectiveShareCommand() {
|
|
|
28259
29191
|
const spinner = ora51("Sharing insight to collective...").start();
|
|
28260
29192
|
try {
|
|
28261
29193
|
const client = getClient();
|
|
28262
|
-
const skillName = options.skill ?? "general";
|
|
28263
29194
|
const agentId = options.agent ?? "cli-agent";
|
|
28264
29195
|
const confidence = parseFloat(options.confidence);
|
|
28265
|
-
const resultMap = {
|
|
28266
|
-
"learning": "success",
|
|
28267
|
-
"pattern": "success",
|
|
28268
|
-
"finding": "success",
|
|
28269
|
-
"technique": "success",
|
|
28270
|
-
"failure-lesson": "fail"
|
|
28271
|
-
};
|
|
28272
29196
|
const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : [];
|
|
28273
|
-
const
|
|
28274
|
-
|
|
28275
|
-
const tagNote = tags.length > 0 ? `
|
|
28276
|
-
[tags] ${tags.join(", ")}` : "";
|
|
28277
|
-
const fullNote = `[${type}] ${content}${contextNote}${tagNote}`;
|
|
29197
|
+
const title = content.length > 80 ? content.slice(0, 77) + "..." : content;
|
|
29198
|
+
const backendType = toBackendType(type);
|
|
28278
29199
|
const result = await client.mutation(
|
|
28279
|
-
toFunctionName(api.
|
|
29200
|
+
toFunctionName(api.collective.shareInsight),
|
|
28280
29201
|
{
|
|
28281
|
-
|
|
28282
|
-
|
|
28283
|
-
|
|
28284
|
-
|
|
28285
|
-
|
|
28286
|
-
|
|
28287
|
-
|
|
29202
|
+
type: backendType,
|
|
29203
|
+
title,
|
|
29204
|
+
content,
|
|
29205
|
+
context: options.context,
|
|
29206
|
+
source_agent_id: agentId,
|
|
29207
|
+
source_workspace_id: options.workspace,
|
|
29208
|
+
source_skill_name: options.skill,
|
|
29209
|
+
tags: tags.length > 0 ? tags : void 0,
|
|
29210
|
+
confidence
|
|
28288
29211
|
}
|
|
28289
29212
|
);
|
|
28290
29213
|
spinner.stop();
|
|
@@ -28293,10 +29216,13 @@ function collectiveShareCommand() {
|
|
|
28293
29216
|
return;
|
|
28294
29217
|
}
|
|
28295
29218
|
console.log(chalk55.bold("\n Insight shared to collective"));
|
|
29219
|
+
console.log(` ${chalk55.dim("id:")} ${result?.id ?? "\u2014"}`);
|
|
28296
29220
|
console.log(` ${chalk55.dim("type:")} ${typeIcon(type)}`);
|
|
28297
|
-
|
|
29221
|
+
if (options.skill) {
|
|
29222
|
+
console.log(` ${chalk55.dim("skill:")} ${options.skill}`);
|
|
29223
|
+
}
|
|
28298
29224
|
console.log(` ${chalk55.dim("confidence:")} ${confidence}`);
|
|
28299
|
-
console.log(` ${chalk55.dim("content:")} ${
|
|
29225
|
+
console.log(` ${chalk55.dim("content:")} ${title}`);
|
|
28300
29226
|
if (tags.length > 0) {
|
|
28301
29227
|
console.log(` ${chalk55.dim("tags:")} ${tags.join(", ")}`);
|
|
28302
29228
|
}
|
|
@@ -28309,8 +29235,6 @@ function collectiveShareCommand() {
|
|
|
28309
29235
|
} catch (error) {
|
|
28310
29236
|
spinner.fail("Failed to share insight");
|
|
28311
29237
|
console.error(chalk55.red(error.message));
|
|
28312
|
-
console.log(chalk55.dim("\n Make sure the skill exists in the registry."));
|
|
28313
|
-
console.log(chalk55.dim(" Use --skill <name> to link to a specific skill.\n"));
|
|
28314
29238
|
}
|
|
28315
29239
|
});
|
|
28316
29240
|
}
|
|
@@ -28319,28 +29243,50 @@ function collectiveTrendingCommand() {
|
|
|
28319
29243
|
const spinner = ora51("Analyzing network trends...").start();
|
|
28320
29244
|
try {
|
|
28321
29245
|
const client = getClient();
|
|
28322
|
-
const
|
|
29246
|
+
const days = parseInt(options.days);
|
|
29247
|
+
const limit = parseInt(options.limit);
|
|
29248
|
+
const [collectiveTrending, singularityTrending] = await Promise.all([
|
|
28323
29249
|
client.query(
|
|
28324
|
-
toFunctionName(api.
|
|
28325
|
-
{ days
|
|
29250
|
+
toFunctionName(api.collective.getTrending),
|
|
29251
|
+
{ days, limit }
|
|
28326
29252
|
),
|
|
28327
29253
|
client.query(
|
|
28328
|
-
toFunctionName(api.singularity.
|
|
28329
|
-
{
|
|
28330
|
-
)
|
|
29254
|
+
toFunctionName(api.singularity.getTrendingSkills),
|
|
29255
|
+
{ days, limit }
|
|
29256
|
+
).catch(() => [])
|
|
28331
29257
|
]);
|
|
28332
29258
|
spinner.stop();
|
|
29259
|
+
const insights = collectiveTrending;
|
|
29260
|
+
const skills = singularityTrending;
|
|
28333
29261
|
if (options.json) {
|
|
28334
|
-
console.log(JSON.stringify({
|
|
29262
|
+
console.log(JSON.stringify({ insights, skills }, null, 2));
|
|
28335
29263
|
return;
|
|
28336
29264
|
}
|
|
28337
|
-
const skills = trending;
|
|
28338
|
-
const feedEntries = feed;
|
|
28339
29265
|
console.log(chalk55.bold(`
|
|
28340
|
-
Trending in the Collective (last ${
|
|
29266
|
+
Trending in the Collective (last ${days} days)
|
|
28341
29267
|
`));
|
|
29268
|
+
if (insights.length > 0) {
|
|
29269
|
+
console.log(chalk55.dim(" --- Top validated insights ---\n"));
|
|
29270
|
+
for (let i = 0; i < insights.length; i++) {
|
|
29271
|
+
const ins = insights[i];
|
|
29272
|
+
const impact = (ins.validations ?? 0) + (ins.applications ?? 0);
|
|
29273
|
+
const time = relativeTime(ins.created_at ?? ins._creationTime ?? Date.now());
|
|
29274
|
+
console.log(
|
|
29275
|
+
` ${chalk55.dim(`${(i + 1).toString().padStart(2)}.`)} ${typeIcon(ins.type)} ${chalk55.bold(ins.title ?? "\u2014")} ${chalk55.dim(time)}`
|
|
29276
|
+
);
|
|
29277
|
+
console.log(
|
|
29278
|
+
` ${ins.validations ?? 0} validations ${ins.applications ?? 0} applications impact: ${impact} conf: ${((ins.confidence ?? 0) * 100).toFixed(0)}%`
|
|
29279
|
+
);
|
|
29280
|
+
if (ins.content) {
|
|
29281
|
+
const content = ins.content.length > 100 ? ins.content.slice(0, 97) + "..." : ins.content;
|
|
29282
|
+
console.log(` ${chalk55.dim(content)}`);
|
|
29283
|
+
}
|
|
29284
|
+
}
|
|
29285
|
+
} else {
|
|
29286
|
+
console.log(chalk55.dim(" No trending insights in the last " + days + " days."));
|
|
29287
|
+
}
|
|
28342
29288
|
if (skills.length > 0) {
|
|
28343
|
-
console.log(chalk55.dim(" ---
|
|
29289
|
+
console.log(chalk55.dim("\n --- Trending skills (network-wide) ---\n"));
|
|
28344
29290
|
for (let i = 0; i < skills.length; i++) {
|
|
28345
29291
|
const s = skills[i];
|
|
28346
29292
|
const rate = (s.success_rate * 100).toFixed(0);
|
|
@@ -28352,20 +29298,6 @@ function collectiveTrendingCommand() {
|
|
|
28352
29298
|
` ${s.executions} executions ${bar} ${rate}% success ${s.unique_agents} agents`
|
|
28353
29299
|
);
|
|
28354
29300
|
}
|
|
28355
|
-
} else {
|
|
28356
|
-
console.log(chalk55.dim(" No trending skills in the last " + options.days + " days."));
|
|
28357
|
-
}
|
|
28358
|
-
if (feedEntries.length > 0) {
|
|
28359
|
-
console.log(chalk55.dim("\n --- Active intelligence ---\n"));
|
|
28360
|
-
for (const entry of feedEntries) {
|
|
28361
|
-
const time = relativeTime(entry.timestamp ?? entry._creationTime ?? Date.now());
|
|
28362
|
-
console.log(
|
|
28363
|
-
` ${typeIcon(entry.type ?? "learning")} ${chalk55.bold(entry.title ?? "\u2014")} ${chalk55.dim(time)}`
|
|
28364
|
-
);
|
|
28365
|
-
if (entry.details) {
|
|
28366
|
-
console.log(` ${chalk55.dim(entry.details)}`);
|
|
28367
|
-
}
|
|
28368
|
-
}
|
|
28369
29301
|
}
|
|
28370
29302
|
console.log();
|
|
28371
29303
|
} catch (error) {
|
|
@@ -28375,57 +29307,34 @@ function collectiveTrendingCommand() {
|
|
|
28375
29307
|
});
|
|
28376
29308
|
}
|
|
28377
29309
|
function collectiveSearchCommand() {
|
|
28378
|
-
return new Command52("search").description("Search collective knowledge").argument("<query>", "Search query").option("--limit <n>", "Max results", "20").option("--json", "Output as JSON").action(async (query, options) => {
|
|
29310
|
+
return new Command52("search").description("Search collective knowledge").argument("<query>", "Search query").option("--type <type>", "Filter by type").option("--limit <n>", "Max results", "20").option("--json", "Output as JSON").action(async (query, options) => {
|
|
28379
29311
|
const spinner = ora51(`Searching collective for "${query}"...`).start();
|
|
28380
29312
|
try {
|
|
28381
29313
|
const client = getClient();
|
|
28382
29314
|
const limit = parseInt(options.limit);
|
|
28383
29315
|
let results = [];
|
|
28384
|
-
let source = "
|
|
29316
|
+
let source = "collective";
|
|
28385
29317
|
try {
|
|
28386
|
-
const
|
|
28387
|
-
|
|
28388
|
-
|
|
29318
|
+
const searchArgs = { query, limit };
|
|
29319
|
+
if (options.type) {
|
|
29320
|
+
searchArgs.type = toBackendType(options.type);
|
|
29321
|
+
}
|
|
29322
|
+
const collectiveResults = await client.query(
|
|
29323
|
+
toFunctionName(api.collective.search),
|
|
29324
|
+
searchArgs
|
|
28389
29325
|
);
|
|
28390
|
-
results =
|
|
29326
|
+
results = collectiveResults;
|
|
28391
29327
|
} catch {
|
|
28392
|
-
source = "
|
|
28393
|
-
|
|
28394
|
-
client.query(
|
|
28395
|
-
toFunctionName(api.
|
|
28396
|
-
{
|
|
28397
|
-
)
|
|
28398
|
-
|
|
28399
|
-
|
|
28400
|
-
|
|
28401
|
-
|
|
28402
|
-
]);
|
|
28403
|
-
const feedEntries = feed;
|
|
28404
|
-
const learningEntries = learnings;
|
|
28405
|
-
const queryLower = query.toLowerCase();
|
|
28406
|
-
for (const entry of feedEntries) {
|
|
28407
|
-
const text = `${entry.title ?? ""} ${entry.description ?? ""} ${entry.details ?? ""}`.toLowerCase();
|
|
28408
|
-
if (text.includes(queryLower)) {
|
|
28409
|
-
results.push({
|
|
28410
|
-
type: entry.type ?? "learning",
|
|
28411
|
-
title: entry.title ?? "\u2014",
|
|
28412
|
-
content: entry.description ?? entry.details ?? "",
|
|
28413
|
-
timestamp: entry.timestamp,
|
|
28414
|
-
source: "feed"
|
|
28415
|
-
});
|
|
28416
|
-
}
|
|
28417
|
-
}
|
|
28418
|
-
for (const log of learningEntries) {
|
|
28419
|
-
const text = `${log.skillName ?? ""} ${log.note ?? ""} ${log.improvement ?? ""} ${log.error_message ?? ""}`.toLowerCase();
|
|
28420
|
-
if (text.includes(queryLower)) {
|
|
28421
|
-
results.push({
|
|
28422
|
-
type: log.improvement ? "improvement" : "learning",
|
|
28423
|
-
title: log.skillName ?? log.skill_name ?? "\u2014",
|
|
28424
|
-
content: log.note ?? log.improvement ?? log.error_message ?? "",
|
|
28425
|
-
timestamp: log._creationTime,
|
|
28426
|
-
source: log.agent_id ?? "unknown"
|
|
28427
|
-
});
|
|
28428
|
-
}
|
|
29328
|
+
source = "knowledge";
|
|
29329
|
+
try {
|
|
29330
|
+
const knowledgeResults = await client.query(
|
|
29331
|
+
toFunctionName(api.hubKnowledge.searchAll),
|
|
29332
|
+
{ query, limit }
|
|
29333
|
+
);
|
|
29334
|
+
results = knowledgeResults;
|
|
29335
|
+
} catch {
|
|
29336
|
+
source = "none";
|
|
29337
|
+
results = [];
|
|
28429
29338
|
}
|
|
28430
29339
|
}
|
|
28431
29340
|
spinner.stop();
|
|
@@ -28443,7 +29352,7 @@ function collectiveSearchCommand() {
|
|
|
28443
29352
|
Collective Search: "${query}" (${results.length} results, via ${source})
|
|
28444
29353
|
`));
|
|
28445
29354
|
for (const r of results.slice(0, limit)) {
|
|
28446
|
-
const time = r.timestamp ? relativeTime(r.timestamp) : "";
|
|
29355
|
+
const time = r.created_at ?? r.timestamp ?? r._creationTime ? relativeTime(r.created_at ?? r.timestamp ?? r._creationTime) : "";
|
|
28447
29356
|
const rType = r.type ?? r.kind ?? "knowledge";
|
|
28448
29357
|
console.log(
|
|
28449
29358
|
` ${typeIcon(rType)} ${chalk55.bold(r.title ?? r.name ?? "\u2014")} ${chalk55.dim(time)}`
|
|
@@ -28453,8 +29362,11 @@ function collectiveSearchCommand() {
|
|
|
28453
29362
|
const truncated = content.length > 120 ? content.slice(0, 117) + "..." : content;
|
|
28454
29363
|
console.log(` ${chalk55.dim(truncated)}`);
|
|
28455
29364
|
}
|
|
28456
|
-
if (r.source) {
|
|
28457
|
-
console.log(` ${chalk55.dim("source:")} ${r.source}`);
|
|
29365
|
+
if (r.source_agent_id ?? r.source) {
|
|
29366
|
+
console.log(` ${chalk55.dim("source:")} ${r.source_agent_id ?? r.source}`);
|
|
29367
|
+
}
|
|
29368
|
+
if (r.tags && r.tags.length > 0) {
|
|
29369
|
+
console.log(` ${chalk55.dim("tags:")} ${r.tags.join(", ")}`);
|
|
28458
29370
|
}
|
|
28459
29371
|
console.log();
|
|
28460
29372
|
}
|
|
@@ -28465,105 +29377,96 @@ function collectiveSearchCommand() {
|
|
|
28465
29377
|
});
|
|
28466
29378
|
}
|
|
28467
29379
|
function collectiveInsightsCommand() {
|
|
28468
|
-
return new Command52("insights").description("Aggregated view:
|
|
29380
|
+
return new Command52("insights").description("Aggregated view: breakdown by type, validations, applications, unique agents/workspaces").option("--days <n>", "Lookback period for trending", "30").option("--json", "Output as JSON").action(async (options) => {
|
|
28469
29381
|
const spinner = ora51("Aggregating collective insights...").start();
|
|
28470
29382
|
try {
|
|
28471
29383
|
const client = getClient();
|
|
28472
29384
|
const days = parseInt(options.days);
|
|
28473
|
-
const [
|
|
29385
|
+
const [stats, trending] = await Promise.all([
|
|
28474
29386
|
client.query(
|
|
28475
|
-
toFunctionName(api.
|
|
28476
|
-
{
|
|
28477
|
-
),
|
|
28478
|
-
client.query(
|
|
28479
|
-
toFunctionName(api.singularity.getIntelligenceFeed),
|
|
28480
|
-
{ limit: 10, type: "all" }
|
|
29387
|
+
toFunctionName(api.collective.getStats),
|
|
29388
|
+
{}
|
|
28481
29389
|
),
|
|
28482
29390
|
client.query(
|
|
28483
|
-
toFunctionName(api.
|
|
28484
|
-
{ limit: 50 }
|
|
29391
|
+
toFunctionName(api.collective.getTrending),
|
|
29392
|
+
{ days, limit: 50 }
|
|
28485
29393
|
)
|
|
28486
29394
|
]);
|
|
28487
29395
|
spinner.stop();
|
|
28488
|
-
const
|
|
28489
|
-
const
|
|
28490
|
-
const learnings = recentLearnings;
|
|
29396
|
+
const s = stats;
|
|
29397
|
+
const trendingInsights = trending;
|
|
28491
29398
|
if (options.json) {
|
|
28492
|
-
console.log(JSON.stringify({
|
|
29399
|
+
console.log(JSON.stringify({ stats: s, trending: trendingInsights }, null, 2));
|
|
28493
29400
|
return;
|
|
28494
29401
|
}
|
|
28495
29402
|
console.log(chalk55.bold(`
|
|
28496
|
-
Collective
|
|
29403
|
+
Collective Intelligence Overview
|
|
28497
29404
|
`));
|
|
28498
|
-
const
|
|
28499
|
-
|
|
28500
|
-
|
|
28501
|
-
|
|
28502
|
-
|
|
29405
|
+
const byType = s.by_type ?? {};
|
|
29406
|
+
const typeEntries = Object.entries(byType).sort((a, b) => b[1] - a[1]);
|
|
29407
|
+
if (typeEntries.length > 0) {
|
|
29408
|
+
console.log(chalk55.dim(" --- Insight breakdown by type ---\n"));
|
|
29409
|
+
for (const [typeName, count] of typeEntries) {
|
|
29410
|
+
const bar = miniBar(Math.min(count / Math.max(s.total_insights, 1), 1));
|
|
28503
29411
|
console.log(
|
|
28504
|
-
` ${
|
|
29412
|
+
` ${typeIcon(typeName)} ${chalk55.bold(String(count).padStart(5))} ${bar} ${typeName}`
|
|
28505
29413
|
);
|
|
28506
29414
|
}
|
|
28507
29415
|
console.log();
|
|
28508
29416
|
}
|
|
28509
|
-
|
|
28510
|
-
|
|
28511
|
-
|
|
28512
|
-
|
|
28513
|
-
|
|
28514
|
-
}
|
|
28515
|
-
|
|
28516
|
-
if (
|
|
28517
|
-
console.log(chalk55.dim(
|
|
28518
|
-
|
|
29417
|
+
console.log(chalk55.dim(" --- Network summary ---\n"));
|
|
29418
|
+
console.log(` ${chalk55.dim("total insights:")} ${s.total_insights ?? 0}`);
|
|
29419
|
+
console.log(` ${chalk55.dim("total validations:")} ${s.total_validations ?? 0}`);
|
|
29420
|
+
console.log(` ${chalk55.dim("total applications:")} ${s.total_applications ?? 0}`);
|
|
29421
|
+
console.log(` ${chalk55.dim("unique agents:")} ${s.unique_agents ?? 0}`);
|
|
29422
|
+
console.log(` ${chalk55.dim("unique workspaces:")} ${s.unique_workspaces ?? 0}`);
|
|
29423
|
+
console.log();
|
|
29424
|
+
if (trendingInsights.length > 0) {
|
|
29425
|
+
console.log(chalk55.dim(` --- Top trending (last ${days} days) ---
|
|
29426
|
+
`));
|
|
29427
|
+
for (let i = 0; i < Math.min(trendingInsights.length, 10); i++) {
|
|
29428
|
+
const ins = trendingInsights[i];
|
|
29429
|
+
const impact = (ins.validations ?? 0) + (ins.applications ?? 0);
|
|
29430
|
+
const time = relativeTime(ins.created_at ?? ins._creationTime ?? Date.now());
|
|
29431
|
+
console.log(
|
|
29432
|
+
` ${chalk55.dim(`${(i + 1).toString().padStart(2)}.`)} ${typeIcon(ins.type)} ${chalk55.bold(ins.title ?? "\u2014")} ${chalk55.dim(time)}`
|
|
29433
|
+
);
|
|
28519
29434
|
console.log(
|
|
28520
|
-
`
|
|
29435
|
+
` ${ins.validations ?? 0} validations, ${ins.applications ?? 0} applications, impact: ${impact}`
|
|
28521
29436
|
);
|
|
28522
|
-
|
|
28523
|
-
|
|
28524
|
-
|
|
28525
|
-
console.log(` ${chalk55.dim(err)}`);
|
|
29437
|
+
if (ins.content) {
|
|
29438
|
+
const content = ins.content.length > 100 ? ins.content.slice(0, 97) + "..." : ins.content;
|
|
29439
|
+
console.log(` ${chalk55.dim(content)}`);
|
|
28526
29440
|
}
|
|
28527
29441
|
}
|
|
28528
29442
|
console.log();
|
|
28529
29443
|
}
|
|
28530
|
-
const
|
|
28531
|
-
|
|
28532
|
-
|
|
28533
|
-
|
|
28534
|
-
|
|
28535
|
-
|
|
28536
|
-
const time = relativeTime(imp._creationTime ?? Date.now());
|
|
29444
|
+
const validated = [...trendingInsights].sort((a, b) => (b.validations ?? 0) - (a.validations ?? 0)).slice(0, 5);
|
|
29445
|
+
const hasValidated = validated.some((v) => (v.validations ?? 0) > 0);
|
|
29446
|
+
if (hasValidated) {
|
|
29447
|
+
console.log(chalk55.dim(" --- Most validated ---\n"));
|
|
29448
|
+
for (const v of validated) {
|
|
29449
|
+
if ((v.validations ?? 0) === 0) continue;
|
|
28537
29450
|
console.log(
|
|
28538
|
-
` ${chalk55.
|
|
29451
|
+
` ${chalk55.green("+")} ${chalk55.bold(v.title ?? "\u2014")} \u2014 ${v.validations} validations from ${v.source_agent_id ?? "network"}`
|
|
28539
29452
|
);
|
|
28540
|
-
console.log(` ${chalk55.dim(text)}`);
|
|
28541
29453
|
}
|
|
28542
29454
|
console.log();
|
|
28543
29455
|
}
|
|
28544
|
-
|
|
28545
|
-
|
|
28546
|
-
|
|
29456
|
+
const failures = trendingInsights.filter((i) => i.type === "failure_lesson");
|
|
29457
|
+
if (failures.length > 0) {
|
|
29458
|
+
console.log(chalk55.dim(" --- Recent failure lessons ---\n"));
|
|
29459
|
+
for (const f of failures.slice(0, 5)) {
|
|
28547
29460
|
console.log(
|
|
28548
|
-
` ${
|
|
29461
|
+
` ${chalk55.red("x")} ${chalk55.bold(f.title ?? "\u2014")}`
|
|
28549
29462
|
);
|
|
28550
|
-
if (
|
|
28551
|
-
|
|
29463
|
+
if (f.content) {
|
|
29464
|
+
const text = f.content.length > 100 ? f.content.slice(0, 97) + "..." : f.content;
|
|
29465
|
+
console.log(` ${chalk55.dim(text)}`);
|
|
28552
29466
|
}
|
|
28553
29467
|
}
|
|
28554
29468
|
console.log();
|
|
28555
29469
|
}
|
|
28556
|
-
const totalExec = skills.reduce((sum, s) => sum + s.executions, 0);
|
|
28557
|
-
const avgSuccess = skills.length > 0 ? skills.reduce((sum, s) => sum + s.success_rate, 0) / skills.length : 0;
|
|
28558
|
-
const totalAgents = new Set(skills.flatMap((s) => Array.from({ length: s.unique_agents }, (_, i) => `${s.name}-${i}`))).size;
|
|
28559
|
-
console.log(chalk55.dim(" --- Network summary ---\n"));
|
|
28560
|
-
console.log(` ${chalk55.dim("total skills tracked:")} ${skills.length}`);
|
|
28561
|
-
console.log(` ${chalk55.dim("total executions:")} ${totalExec}`);
|
|
28562
|
-
console.log(` ${chalk55.dim("avg success rate:")} ${(avgSuccess * 100).toFixed(1)}%`);
|
|
28563
|
-
console.log(` ${chalk55.dim("recent learnings:")} ${learnings.length}`);
|
|
28564
|
-
console.log(` ${chalk55.dim("improvements pending:")} ${improvements.length}`);
|
|
28565
|
-
console.log(` ${chalk55.dim("failures tracked:")} ${failures.length}`);
|
|
28566
|
-
console.log();
|
|
28567
29470
|
} catch (error) {
|
|
28568
29471
|
spinner.fail("Failed to aggregate insights");
|
|
28569
29472
|
console.error(chalk55.red(error.message));
|
|
@@ -28571,164 +29474,95 @@ function collectiveInsightsCommand() {
|
|
|
28571
29474
|
});
|
|
28572
29475
|
}
|
|
28573
29476
|
function collectiveSyncCommand() {
|
|
28574
|
-
return new Command52("sync").description("Pull latest
|
|
29477
|
+
return new Command52("sync").description("Pull latest insights from collective into local workspace as organized markdown").option("--limit <n>", "Max insights to pull", "50").option("--dir <path>", "Output directory", ".hubify/collective").option("--json", "Output as JSON").action(async (options) => {
|
|
28575
29478
|
const spinner = ora51("Syncing collective intelligence to local workspace...").start();
|
|
28576
29479
|
try {
|
|
28577
29480
|
const client = getClient();
|
|
28578
29481
|
const limit = parseInt(options.limit);
|
|
28579
|
-
const
|
|
28580
|
-
|
|
28581
|
-
|
|
28582
|
-
|
|
28583
|
-
),
|
|
28584
|
-
client.query(
|
|
28585
|
-
toFunctionName(api.singularity.getTrendingSkills),
|
|
28586
|
-
{ days: 7, limit }
|
|
28587
|
-
),
|
|
28588
|
-
client.query(
|
|
28589
|
-
toFunctionName(api.learning.getRecentEnriched),
|
|
28590
|
-
{ limit }
|
|
28591
|
-
)
|
|
28592
|
-
]);
|
|
29482
|
+
const collectiveFeed = await client.query(
|
|
29483
|
+
toFunctionName(api.collective.getFeed),
|
|
29484
|
+
{ limit }
|
|
29485
|
+
);
|
|
28593
29486
|
spinner.stop();
|
|
28594
|
-
const
|
|
28595
|
-
const skills = trending;
|
|
28596
|
-
const learnings = recentLearnings;
|
|
29487
|
+
const entries = collectiveFeed;
|
|
28597
29488
|
if (options.json) {
|
|
28598
|
-
console.log(JSON.stringify(
|
|
29489
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
28599
29490
|
return;
|
|
28600
29491
|
}
|
|
28601
29492
|
const outDir = path25.resolve(process.cwd(), options.dir);
|
|
28602
29493
|
fs25.mkdirSync(outDir, { recursive: true });
|
|
28603
29494
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
28604
29495
|
let filesWritten = 0;
|
|
28605
|
-
|
|
28606
|
-
|
|
28607
|
-
|
|
28608
|
-
|
|
28609
|
-
|
|
28610
|
-
|
|
28611
|
-
|
|
28612
|
-
|
|
28613
|
-
|
|
28614
|
-
|
|
28615
|
-
|
|
28616
|
-
|
|
28617
|
-
|
|
28618
|
-
|
|
28619
|
-
|
|
28620
|
-
|
|
28621
|
-
|
|
28622
|
-
|
|
28623
|
-
|
|
28624
|
-
|
|
28625
|
-
|
|
28626
|
-
|
|
28627
|
-
|
|
28628
|
-
|
|
28629
|
-
|
|
28630
|
-
|
|
28631
|
-
`;
|
|
28632
|
-
md += `_${time}_ | agent: ${l.agent_id ?? "unknown"} | platform: ${l.platform ?? "unknown"}_
|
|
28633
|
-
|
|
28634
|
-
`;
|
|
28635
|
-
if (l.note) md += `> ${l.note}
|
|
28636
|
-
|
|
28637
|
-
`;
|
|
28638
|
-
if (l.improvement) md += `**Improvement:** ${l.improvement}
|
|
28639
|
-
|
|
28640
|
-
`;
|
|
28641
|
-
if (l.error_message) md += `**Error:** ${l.error_message}
|
|
28642
|
-
|
|
28643
|
-
`;
|
|
28644
|
-
md += `---
|
|
28645
|
-
|
|
28646
|
-
`;
|
|
28647
|
-
}
|
|
28648
|
-
fs25.writeFileSync(path25.join(outDir, "recent-learnings.md"), md);
|
|
28649
|
-
filesWritten++;
|
|
28650
|
-
}
|
|
28651
|
-
if (feedEntries.length > 0) {
|
|
28652
|
-
let md = `# Active Experiments
|
|
28653
|
-
|
|
28654
|
-
_Synced: ${now}_
|
|
28655
|
-
|
|
28656
|
-
`;
|
|
28657
|
-
for (const entry of feedEntries) {
|
|
28658
|
-
md += `## ${entry.title ?? "Untitled"}
|
|
28659
|
-
`;
|
|
28660
|
-
md += `Type: ${entry.type ?? "unknown"}
|
|
28661
|
-
|
|
28662
|
-
`;
|
|
28663
|
-
if (entry.description) md += `${entry.description}
|
|
28664
|
-
|
|
28665
|
-
`;
|
|
28666
|
-
if (entry.details) md += `Progress: ${entry.details}
|
|
28667
|
-
|
|
28668
|
-
`;
|
|
28669
|
-
md += `---
|
|
28670
|
-
|
|
28671
|
-
`;
|
|
28672
|
-
}
|
|
28673
|
-
fs25.writeFileSync(path25.join(outDir, "active-experiments.md"), md);
|
|
28674
|
-
filesWritten++;
|
|
28675
|
-
}
|
|
28676
|
-
const improvements = learnings.filter((l) => l.improvement);
|
|
28677
|
-
if (improvements.length > 0) {
|
|
28678
|
-
let md = `# Patterns & Improvements
|
|
29496
|
+
const grouped = {};
|
|
29497
|
+
for (const entry of entries) {
|
|
29498
|
+
const type = entry.type ?? "learning";
|
|
29499
|
+
if (!grouped[type]) grouped[type] = [];
|
|
29500
|
+
grouped[type].push(entry);
|
|
29501
|
+
}
|
|
29502
|
+
const fileMap = {
|
|
29503
|
+
learning: "learnings.md",
|
|
29504
|
+
pattern: "patterns.md",
|
|
29505
|
+
finding: "findings.md",
|
|
29506
|
+
technique: "techniques.md",
|
|
29507
|
+
experiment_result: "experiment-results.md",
|
|
29508
|
+
failure_lesson: "failure-lessons.md"
|
|
29509
|
+
};
|
|
29510
|
+
const headerMap = {
|
|
29511
|
+
learning: "Learnings",
|
|
29512
|
+
pattern: "Patterns",
|
|
29513
|
+
finding: "Findings",
|
|
29514
|
+
technique: "Techniques",
|
|
29515
|
+
experiment_result: "Experiment Results",
|
|
29516
|
+
failure_lesson: "Failure Lessons"
|
|
29517
|
+
};
|
|
29518
|
+
for (const [type, items] of Object.entries(grouped)) {
|
|
29519
|
+
const filename = fileMap[type] ?? `${type}.md`;
|
|
29520
|
+
const header = headerMap[type] ?? type.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
29521
|
+
let md = `# ${header}
|
|
28679
29522
|
|
|
28680
29523
|
_Synced: ${now}_
|
|
29524
|
+
_Source: Hubify Collective Intelligence_
|
|
28681
29525
|
|
|
28682
29526
|
`;
|
|
28683
|
-
|
|
28684
|
-
|
|
28685
|
-
|
|
28686
|
-
|
|
28687
|
-
const
|
|
28688
|
-
md +=
|
|
28689
|
-
`;
|
|
28690
|
-
md += `${imp.improvement}
|
|
28691
|
-
|
|
29527
|
+
for (const item of items) {
|
|
29528
|
+
const time = new Date(item.created_at ?? item._creationTime ?? Date.now()).toISOString();
|
|
29529
|
+
const confidence = item.confidence ? ` | confidence: ${(item.confidence * 100).toFixed(0)}%` : "";
|
|
29530
|
+
const validations = item.validations ? ` | ${item.validations} validations` : "";
|
|
29531
|
+
const applications = item.applications ? ` | ${item.applications} applications` : "";
|
|
29532
|
+
md += `## ${item.title ?? "Untitled"}
|
|
28692
29533
|
`;
|
|
28693
|
-
|
|
28694
|
-
fs25.writeFileSync(path25.join(outDir, "patterns.md"), md);
|
|
28695
|
-
filesWritten++;
|
|
28696
|
-
}
|
|
28697
|
-
const failures = learnings.filter((l) => l.result === "fail");
|
|
28698
|
-
if (failures.length > 0) {
|
|
28699
|
-
let md = `# Failure Lessons
|
|
28700
|
-
|
|
28701
|
-
_Synced: ${now}_
|
|
29534
|
+
md += `_${time}_ | agent: ${item.source_agent_id ?? "unknown"}${confidence}${validations}${applications}
|
|
28702
29535
|
|
|
28703
29536
|
`;
|
|
28704
|
-
|
|
29537
|
+
if (item.content) {
|
|
29538
|
+
md += `${item.content}
|
|
28705
29539
|
|
|
28706
29540
|
`;
|
|
28707
|
-
|
|
28708
|
-
|
|
28709
|
-
|
|
28710
|
-
`;
|
|
28711
|
-
if (f.error_message) md += `**Error:** ${f.error_message}
|
|
29541
|
+
}
|
|
29542
|
+
if (item.context) {
|
|
29543
|
+
md += `**Context:** ${item.context}
|
|
28712
29544
|
|
|
28713
29545
|
`;
|
|
28714
|
-
|
|
29546
|
+
}
|
|
29547
|
+
if (item.tags && item.tags.length > 0) {
|
|
29548
|
+
md += `**Tags:** ${item.tags.join(", ")}
|
|
28715
29549
|
|
|
28716
29550
|
`;
|
|
29551
|
+
}
|
|
28717
29552
|
md += `---
|
|
28718
29553
|
|
|
28719
29554
|
`;
|
|
28720
29555
|
}
|
|
28721
|
-
fs25.writeFileSync(path25.join(outDir,
|
|
29556
|
+
fs25.writeFileSync(path25.join(outDir, filename), md);
|
|
28722
29557
|
filesWritten++;
|
|
28723
29558
|
}
|
|
29559
|
+
const totalEntries = entries.length;
|
|
29560
|
+
const typeBreakdown = Object.entries(grouped).map(([type, items]) => `${type}: ${items.length}`).join(", ");
|
|
28724
29561
|
console.log(chalk55.bold("\n Collective intelligence synced to local workspace\n"));
|
|
28725
29562
|
console.log(` ${chalk55.dim("directory:")} ${outDir}`);
|
|
28726
29563
|
console.log(` ${chalk55.dim("files:")} ${filesWritten} files written`);
|
|
28727
|
-
console.log(` ${chalk55.dim("
|
|
28728
|
-
console.log(` ${chalk55.dim("
|
|
28729
|
-
console.log(` ${chalk55.dim("experiments:")} ${feedEntries.length} active`);
|
|
28730
|
-
console.log(` ${chalk55.dim("patterns:")} ${improvements.length} improvement suggestions`);
|
|
28731
|
-
console.log(` ${chalk55.dim("failures:")} ${failures.length} tracked`);
|
|
29564
|
+
console.log(` ${chalk55.dim("insights:")} ${totalEntries} total`);
|
|
29565
|
+
console.log(` ${chalk55.dim("breakdown:")} ${typeBreakdown || "none"}`);
|
|
28732
29566
|
console.log();
|
|
28733
29567
|
console.log(chalk55.dim(" Files are markdown \u2014 ready for agent consumption."));
|
|
28734
29568
|
console.log(chalk55.dim(` Add ${options.dir}/ to your agent's context for smarter decisions.`));
|
|
@@ -28745,64 +29579,1491 @@ function miniBar(ratio) {
|
|
|
28745
29579
|
return chalk55.green("\u2588".repeat(filled)) + chalk55.dim("\u2591".repeat(empty));
|
|
28746
29580
|
}
|
|
28747
29581
|
|
|
28748
|
-
// src/
|
|
28749
|
-
|
|
28750
|
-
|
|
28751
|
-
|
|
28752
|
-
|
|
28753
|
-
|
|
28754
|
-
|
|
28755
|
-
|
|
28756
|
-
|
|
28757
|
-
|
|
28758
|
-
|
|
28759
|
-
|
|
28760
|
-
|
|
28761
|
-
|
|
28762
|
-
|
|
28763
|
-
|
|
28764
|
-
|
|
28765
|
-
|
|
29582
|
+
// src/commands/vault.ts
|
|
29583
|
+
import { Command as Command53 } from "commander";
|
|
29584
|
+
import chalk56 from "chalk";
|
|
29585
|
+
import ora52 from "ora";
|
|
29586
|
+
import crypto7 from "crypto";
|
|
29587
|
+
function relativeTime2(ts) {
|
|
29588
|
+
const diff = Date.now() - ts;
|
|
29589
|
+
const mins = Math.floor(diff / 6e4);
|
|
29590
|
+
const hrs = Math.floor(mins / 60);
|
|
29591
|
+
const days = Math.floor(hrs / 24);
|
|
29592
|
+
if (days > 0) return `${days}d ago`;
|
|
29593
|
+
if (hrs > 0) return `${hrs}h ago`;
|
|
29594
|
+
if (mins > 0) return `${mins}m ago`;
|
|
29595
|
+
return "just now";
|
|
29596
|
+
}
|
|
29597
|
+
function encrypt(text, passphrase) {
|
|
29598
|
+
const key = crypto7.scryptSync(passphrase, "hubify-vault-salt", 32);
|
|
29599
|
+
const iv = crypto7.randomBytes(16);
|
|
29600
|
+
const cipher = crypto7.createCipheriv("aes-256-cbc", key, iv);
|
|
29601
|
+
let encrypted = cipher.update(text, "utf8", "hex");
|
|
29602
|
+
encrypted += cipher.final("hex");
|
|
29603
|
+
return iv.toString("hex") + ":" + encrypted;
|
|
29604
|
+
}
|
|
29605
|
+
function decrypt(encrypted, passphrase) {
|
|
29606
|
+
const key = crypto7.scryptSync(passphrase, "hubify-vault-salt", 32);
|
|
29607
|
+
const [ivHex, encHex] = encrypted.split(":");
|
|
29608
|
+
const iv = Buffer.from(ivHex, "hex");
|
|
29609
|
+
const decipher = crypto7.createDecipheriv("aes-256-cbc", key, iv);
|
|
29610
|
+
let decrypted = decipher.update(encHex, "hex", "utf8");
|
|
29611
|
+
decrypted += decipher.final("utf8");
|
|
29612
|
+
return decrypted;
|
|
29613
|
+
}
|
|
29614
|
+
function vaultListCommand() {
|
|
29615
|
+
return new Command53("list").description("List vault entries for a workspace").argument("<hub_id>", "Hub/workspace ID").option("--owner <id>", "Owner ID", "user_default").option("--json", "Output as JSON").action(async (hubId, options) => {
|
|
29616
|
+
const spinner = ora52("Fetching vault entries...").start();
|
|
29617
|
+
try {
|
|
29618
|
+
const client = getClient();
|
|
29619
|
+
const entries = await client.query(
|
|
29620
|
+
toFunctionName(api.vault.listVaultEntries),
|
|
29621
|
+
{ hub_id: hubId, owner_id: options.owner }
|
|
29622
|
+
);
|
|
29623
|
+
spinner.stop();
|
|
29624
|
+
if (options.json) {
|
|
29625
|
+
console.log(JSON.stringify(entries, null, 2));
|
|
29626
|
+
return;
|
|
29627
|
+
}
|
|
29628
|
+
if (!entries || entries.length === 0) {
|
|
29629
|
+
console.log(chalk56.gray("\n No vault entries. Add one with:"));
|
|
29630
|
+
console.log(chalk56.cyan(" hubify vault store <hub_id> --service github --type api_key --value <key>\n"));
|
|
29631
|
+
return;
|
|
29632
|
+
}
|
|
29633
|
+
console.log(chalk56.cyan(`
|
|
29634
|
+
Vault (${entries.length} entries)
|
|
29635
|
+
`));
|
|
29636
|
+
for (const entry of entries) {
|
|
29637
|
+
const typeColor = entry.type === "api_key" ? chalk56.yellow : entry.type === "mcp" ? chalk56.blue : chalk56.green;
|
|
29638
|
+
console.log(
|
|
29639
|
+
` ${typeColor(entry.type.padEnd(12))} ${chalk56.white(entry.service.padEnd(20))} ` + chalk56.gray(`agents: ${entry.granted_agents.join(", ") || "none"}`) + (entry.last_accessed ? chalk56.dim(` last used ${relativeTime2(entry.last_accessed)}`) : "")
|
|
29640
|
+
);
|
|
29641
|
+
}
|
|
29642
|
+
console.log();
|
|
29643
|
+
} catch (error) {
|
|
29644
|
+
spinner.fail("Failed to list vault entries");
|
|
29645
|
+
console.error(chalk56.red(` ${error.message}`));
|
|
29646
|
+
}
|
|
29647
|
+
});
|
|
29648
|
+
}
|
|
29649
|
+
function vaultStoreCommand() {
|
|
29650
|
+
return new Command53("store").description("Store a credential in the vault").argument("<hub_id>", "Hub/workspace ID").requiredOption("--service <name>", "Service name (e.g., github, openai, slack)").requiredOption("--type <type>", "Entry type: api_key, mcp, oauth_token").requiredOption("--value <value>", "The secret value to store").option("--owner <id>", "Owner ID", "user_default").option("--agents <agents>", "Comma-separated agent IDs granted access", "all").option("--passphrase <p>", "Encryption passphrase", "hubify-default-key").action(async (hubId, options) => {
|
|
29651
|
+
const validTypes = ["api_key", "mcp", "oauth_token"];
|
|
29652
|
+
if (!validTypes.includes(options.type)) {
|
|
29653
|
+
console.log(chalk56.red(`
|
|
29654
|
+
Invalid type: ${options.type}. Must be one of: ${validTypes.join(", ")}
|
|
29655
|
+
`));
|
|
29656
|
+
process.exit(1);
|
|
29657
|
+
}
|
|
29658
|
+
const spinner = ora52(`Encrypting and storing ${options.service} credential...`).start();
|
|
29659
|
+
try {
|
|
29660
|
+
const client = getClient();
|
|
29661
|
+
const encrypted = encrypt(options.value, options.passphrase);
|
|
29662
|
+
const agents = options.agents.split(",").map((a) => a.trim());
|
|
29663
|
+
await client.mutation(
|
|
29664
|
+
toFunctionName(api.vault.addVaultEntry),
|
|
29665
|
+
{
|
|
29666
|
+
hub_id: hubId,
|
|
29667
|
+
owner_id: options.owner,
|
|
29668
|
+
entry: {
|
|
29669
|
+
id: `${options.service}-${options.type}-${Date.now()}`,
|
|
29670
|
+
type: options.type,
|
|
29671
|
+
service: options.service,
|
|
29672
|
+
encrypted_config: encrypted,
|
|
29673
|
+
granted_agents: agents
|
|
29674
|
+
}
|
|
29675
|
+
}
|
|
29676
|
+
);
|
|
29677
|
+
spinner.succeed(`Stored ${options.service} credential`);
|
|
29678
|
+
console.log(chalk56.gray(` Type: ${options.type}`));
|
|
29679
|
+
console.log(chalk56.gray(` Agents: ${agents.join(", ")}`));
|
|
29680
|
+
console.log(chalk56.gray(` Hub: ${hubId}`));
|
|
29681
|
+
console.log();
|
|
29682
|
+
} catch (error) {
|
|
29683
|
+
spinner.fail("Failed to store credential");
|
|
29684
|
+
console.error(chalk56.red(` ${error.message}`));
|
|
29685
|
+
}
|
|
29686
|
+
});
|
|
29687
|
+
}
|
|
29688
|
+
function vaultGetCommand() {
|
|
29689
|
+
return new Command53("get").description("Retrieve a credential from the vault (requires agent access)").argument("<hub_id>", "Hub/workspace ID").requiredOption("--service <name>", "Service name").option("--agent <id>", "Requesting agent ID", "cli-user").option("--passphrase <p>", "Decryption passphrase", "hubify-default-key").option("--json", "Output as JSON").action(async (hubId, options) => {
|
|
29690
|
+
const spinner = ora52(`Retrieving ${options.service} credential...`).start();
|
|
29691
|
+
try {
|
|
29692
|
+
const client = getClient();
|
|
29693
|
+
const result = await client.mutation(
|
|
29694
|
+
toFunctionName(api.vault.getVaultEntry),
|
|
29695
|
+
{
|
|
29696
|
+
hub_id: hubId,
|
|
29697
|
+
service: options.service,
|
|
29698
|
+
requesting_agent: options.agent
|
|
29699
|
+
}
|
|
29700
|
+
);
|
|
29701
|
+
spinner.stop();
|
|
29702
|
+
if (!result) {
|
|
29703
|
+
console.log(chalk56.yellow(`
|
|
29704
|
+
No credential found for service: ${options.service}
|
|
29705
|
+
`));
|
|
29706
|
+
return;
|
|
29707
|
+
}
|
|
29708
|
+
if (result.error) {
|
|
29709
|
+
console.log(chalk56.red(`
|
|
29710
|
+
Access denied: ${result.error}
|
|
29711
|
+
`));
|
|
29712
|
+
process.exit(1);
|
|
29713
|
+
}
|
|
29714
|
+
const decrypted = decrypt(result.encrypted_config, options.passphrase);
|
|
29715
|
+
if (options.json) {
|
|
29716
|
+
console.log(JSON.stringify({ service: result.service, type: result.type, value: decrypted }, null, 2));
|
|
29717
|
+
return;
|
|
29718
|
+
}
|
|
29719
|
+
console.log(chalk56.cyan(`
|
|
29720
|
+
${result.service} (${result.type})
|
|
29721
|
+
`));
|
|
29722
|
+
console.log(chalk56.white(` ${decrypted}`));
|
|
29723
|
+
console.log();
|
|
29724
|
+
} catch (error) {
|
|
29725
|
+
spinner.fail("Failed to retrieve credential");
|
|
29726
|
+
console.error(chalk56.red(` ${error.message}`));
|
|
29727
|
+
}
|
|
29728
|
+
});
|
|
29729
|
+
}
|
|
29730
|
+
function vaultRevokeCommand() {
|
|
29731
|
+
return new Command53("revoke").description("Delete a vault entry").argument("<hub_id>", "Hub/workspace ID").requiredOption("--entry-id <id>", "Entry ID to revoke").action(async (hubId, options) => {
|
|
29732
|
+
const spinner = ora52("Revoking vault entry...").start();
|
|
29733
|
+
try {
|
|
29734
|
+
const client = getClient();
|
|
29735
|
+
await client.mutation(
|
|
29736
|
+
toFunctionName(api.vault.deleteVaultEntry),
|
|
29737
|
+
{ hub_id: hubId, entry_id: options.entryId }
|
|
29738
|
+
);
|
|
29739
|
+
spinner.succeed("Vault entry revoked");
|
|
29740
|
+
console.log(chalk56.gray(` Entry ${options.entryId} has been removed.
|
|
29741
|
+
`));
|
|
29742
|
+
} catch (error) {
|
|
29743
|
+
spinner.fail("Failed to revoke entry");
|
|
29744
|
+
console.error(chalk56.red(` ${error.message}`));
|
|
29745
|
+
}
|
|
29746
|
+
});
|
|
29747
|
+
}
|
|
29748
|
+
function vaultGrantCommand() {
|
|
29749
|
+
return new Command53("grant").description("Update which agents can access a vault entry").argument("<hub_id>", "Hub/workspace ID").requiredOption("--entry-id <id>", "Entry ID").requiredOption("--agents <agents>", "Comma-separated agent IDs (or 'all')").action(async (hubId, options) => {
|
|
29750
|
+
const spinner = ora52("Updating access grants...").start();
|
|
29751
|
+
try {
|
|
29752
|
+
const client = getClient();
|
|
29753
|
+
const agents = options.agents.split(",").map((a) => a.trim());
|
|
29754
|
+
await client.mutation(
|
|
29755
|
+
toFunctionName(api.vault.updateVaultEntryGrants),
|
|
29756
|
+
{ hub_id: hubId, entry_id: options.entryId, granted_agents: agents }
|
|
29757
|
+
);
|
|
29758
|
+
spinner.succeed("Access grants updated");
|
|
29759
|
+
console.log(chalk56.gray(` Agents: ${agents.join(", ")}
|
|
29760
|
+
`));
|
|
29761
|
+
} catch (error) {
|
|
29762
|
+
spinner.fail("Failed to update grants");
|
|
29763
|
+
console.error(chalk56.red(` ${error.message}`));
|
|
29764
|
+
}
|
|
29765
|
+
});
|
|
29766
|
+
}
|
|
29767
|
+
function vaultAuditCommand() {
|
|
29768
|
+
return new Command53("audit").description("View access log for a vault service").argument("<hub_id>", "Hub/workspace ID").requiredOption("--service <name>", "Service name").option("--limit <n>", "Max entries", "20").option("--json", "Output as JSON").action(async (hubId, options) => {
|
|
29769
|
+
const spinner = ora52("Fetching access log...").start();
|
|
29770
|
+
try {
|
|
29771
|
+
const client = getClient();
|
|
29772
|
+
const logs = await client.query(
|
|
29773
|
+
toFunctionName(api.vault.getVaultAccessLog),
|
|
29774
|
+
{ hub_id: hubId, service: options.service, limit: parseInt(options.limit) }
|
|
29775
|
+
);
|
|
29776
|
+
spinner.stop();
|
|
29777
|
+
if (options.json) {
|
|
29778
|
+
console.log(JSON.stringify(logs, null, 2));
|
|
29779
|
+
return;
|
|
29780
|
+
}
|
|
29781
|
+
if (!logs || logs.length === 0) {
|
|
29782
|
+
console.log(chalk56.gray(`
|
|
29783
|
+
No access logs for ${options.service}
|
|
29784
|
+
`));
|
|
29785
|
+
return;
|
|
29786
|
+
}
|
|
29787
|
+
console.log(chalk56.cyan(`
|
|
29788
|
+
Access Log: ${options.service} (${logs.length} entries)
|
|
29789
|
+
`));
|
|
29790
|
+
for (const log of logs) {
|
|
29791
|
+
console.log(
|
|
29792
|
+
` ${chalk56.gray(relativeTime2(log.timestamp).padEnd(12))} ${chalk56.white(log.agent_id.padEnd(20))} ` + chalk56.dim(log.operation)
|
|
29793
|
+
);
|
|
29794
|
+
}
|
|
29795
|
+
console.log();
|
|
29796
|
+
} catch (error) {
|
|
29797
|
+
spinner.fail("Failed to fetch access log");
|
|
29798
|
+
console.error(chalk56.red(` ${error.message}`));
|
|
29799
|
+
}
|
|
29800
|
+
});
|
|
29801
|
+
}
|
|
29802
|
+
var vaultCommand = new Command53("vault").description("Manage tool credentials and API keys for workspace agents").addCommand(vaultListCommand()).addCommand(vaultStoreCommand()).addCommand(vaultGetCommand()).addCommand(vaultRevokeCommand()).addCommand(vaultGrantCommand()).addCommand(vaultAuditCommand());
|
|
29803
|
+
|
|
29804
|
+
// src/commands/reports.ts
|
|
29805
|
+
import { Command as Command54 } from "commander";
|
|
29806
|
+
import chalk57 from "chalk";
|
|
29807
|
+
import ora53 from "ora";
|
|
29808
|
+
var reportsCommand = new Command54("reports").description("Weekly intelligence reports \u2014 network activity, skills, experiments").addCommand(reportsLatestCommand()).addCommand(reportsListCommand()).addCommand(reportsViewCommand());
|
|
29809
|
+
function reportsLatestCommand() {
|
|
29810
|
+
return new Command54("latest").description("Show the most recent weekly intelligence report").option("--json", "Output as raw JSON").action(async (options) => {
|
|
29811
|
+
const spinner = ora53("Fetching latest report...").start();
|
|
29812
|
+
try {
|
|
29813
|
+
const client = getClient();
|
|
29814
|
+
const report = await client.query(
|
|
29815
|
+
toFunctionName("weeklyReport:getLatestReport"),
|
|
29816
|
+
{}
|
|
29817
|
+
);
|
|
29818
|
+
spinner.stop();
|
|
29819
|
+
if (!report) {
|
|
29820
|
+
console.log(chalk57.dim(" No weekly reports available yet."));
|
|
29821
|
+
return;
|
|
29822
|
+
}
|
|
29823
|
+
if (options.json) {
|
|
29824
|
+
console.log(JSON.stringify(report, null, 2));
|
|
29825
|
+
return;
|
|
29826
|
+
}
|
|
29827
|
+
printFullReport(report);
|
|
29828
|
+
} catch (error) {
|
|
29829
|
+
spinner.fail("Failed to fetch latest report");
|
|
29830
|
+
console.error(chalk57.red(error.message));
|
|
29831
|
+
}
|
|
29832
|
+
});
|
|
29833
|
+
}
|
|
29834
|
+
function reportsListCommand() {
|
|
29835
|
+
return new Command54("list").description("List available weekly reports").option("--limit <n>", "Number of reports to show", "10").option("--json", "Output as raw JSON").action(async (options) => {
|
|
29836
|
+
const spinner = ora53("Fetching report list...").start();
|
|
29837
|
+
try {
|
|
29838
|
+
const client = getClient();
|
|
29839
|
+
const reports = await client.query(
|
|
29840
|
+
toFunctionName("weeklyReport:listReports"),
|
|
29841
|
+
{ limit: parseInt(options.limit) }
|
|
29842
|
+
);
|
|
29843
|
+
spinner.stop();
|
|
29844
|
+
if (options.json) {
|
|
29845
|
+
console.log(JSON.stringify(reports, null, 2));
|
|
29846
|
+
return;
|
|
29847
|
+
}
|
|
29848
|
+
const list = reports;
|
|
29849
|
+
if (list.length === 0) {
|
|
29850
|
+
console.log(chalk57.dim(" No weekly reports available yet."));
|
|
29851
|
+
return;
|
|
29852
|
+
}
|
|
29853
|
+
console.log(chalk57.bold(`
|
|
29854
|
+
Weekly Intelligence Reports (${list.length})
|
|
29855
|
+
`));
|
|
29856
|
+
console.log(
|
|
29857
|
+
chalk57.dim(" ") + chalk57.white("Week Start".padEnd(14)) + chalk57.white("Week End".padEnd(14)) + chalk57.white("Generated".padEnd(24)) + chalk57.white("Executions".padEnd(12)) + chalk57.white("New Skills".padEnd(12)) + chalk57.white("Experiments")
|
|
29858
|
+
);
|
|
29859
|
+
console.log(chalk57.dim(" " + "\u2500".repeat(88)));
|
|
29860
|
+
for (const r of list) {
|
|
29861
|
+
const weekStart = formatDate3(r.week_start).padEnd(14);
|
|
29862
|
+
const weekEnd = formatDate3(r.week_end).padEnd(14);
|
|
29863
|
+
const generated = new Date(r.generated_at).toLocaleString().padEnd(24);
|
|
29864
|
+
const executions = String(r.stats?.total_executions ?? "\u2014").padEnd(12);
|
|
29865
|
+
const newSkills = String(r.stats?.new_skills ?? "\u2014").padEnd(12);
|
|
29866
|
+
const experiments = String(r.stats?.active_experiments ?? "\u2014");
|
|
29867
|
+
console.log(
|
|
29868
|
+
chalk57.dim(" ") + chalk57.cyan(weekStart) + chalk57.dim(weekEnd) + chalk57.dim(generated) + chalk57.yellow(executions) + chalk57.green(newSkills) + chalk57.dim(experiments)
|
|
29869
|
+
);
|
|
29870
|
+
}
|
|
29871
|
+
console.log();
|
|
29872
|
+
console.log(chalk57.dim(" Use `hubify reports view --date <YYYY-MM-DD>` to view a specific week."));
|
|
29873
|
+
console.log();
|
|
29874
|
+
} catch (error) {
|
|
29875
|
+
spinner.fail("Failed to fetch report list");
|
|
29876
|
+
console.error(chalk57.red(error.message));
|
|
29877
|
+
}
|
|
29878
|
+
});
|
|
29879
|
+
}
|
|
29880
|
+
function reportsViewCommand() {
|
|
29881
|
+
return new Command54("view").description("View report for a specific week").requiredOption("--date <date>", "ISO date string within the week (e.g. 2026-03-10)").option("--json", "Output as raw JSON").action(async (options) => {
|
|
29882
|
+
const spinner = ora53(`Fetching report for ${options.date}...`).start();
|
|
29883
|
+
try {
|
|
29884
|
+
const parsed = Date.parse(options.date);
|
|
29885
|
+
if (isNaN(parsed)) {
|
|
29886
|
+
spinner.fail("Invalid date");
|
|
29887
|
+
console.error(chalk57.red(` Could not parse date: ${options.date}`));
|
|
29888
|
+
console.error(chalk57.dim(" Expected ISO format, e.g. 2026-03-10"));
|
|
29889
|
+
process.exit(1);
|
|
29890
|
+
}
|
|
29891
|
+
const client = getClient();
|
|
29892
|
+
const report = await client.query(
|
|
29893
|
+
toFunctionName("weeklyReport:getReportByWeek"),
|
|
29894
|
+
{ date: parsed }
|
|
29895
|
+
);
|
|
29896
|
+
spinner.stop();
|
|
29897
|
+
if (!report) {
|
|
29898
|
+
console.log(chalk57.dim(` No report found for the week containing ${options.date}.`));
|
|
29899
|
+
return;
|
|
29900
|
+
}
|
|
29901
|
+
if (options.json) {
|
|
29902
|
+
console.log(JSON.stringify(report, null, 2));
|
|
29903
|
+
return;
|
|
29904
|
+
}
|
|
29905
|
+
printFullReport(report);
|
|
29906
|
+
} catch (error) {
|
|
29907
|
+
spinner.fail("Failed to fetch report");
|
|
29908
|
+
console.error(chalk57.red(error.message));
|
|
29909
|
+
}
|
|
29910
|
+
});
|
|
29911
|
+
}
|
|
29912
|
+
function printFullReport(r) {
|
|
29913
|
+
const weekStart = formatDate3(r.week_start);
|
|
29914
|
+
const weekEnd = formatDate3(r.week_end);
|
|
29915
|
+
const generated = new Date(r.generated_at).toLocaleString();
|
|
29916
|
+
console.log();
|
|
29917
|
+
console.log(chalk57.bold(` Weekly Intelligence Report`));
|
|
29918
|
+
console.log(chalk57.dim(` ${weekStart} \u2192 ${weekEnd} \xB7 generated ${generated}`));
|
|
29919
|
+
console.log(chalk57.dim(" " + "\u2500".repeat(60)));
|
|
29920
|
+
const s = r.stats ?? {};
|
|
29921
|
+
console.log();
|
|
29922
|
+
console.log(chalk57.bold(" Overview"));
|
|
29923
|
+
console.log();
|
|
29924
|
+
const delta = s.execution_delta_pct ?? 0;
|
|
29925
|
+
const deltaStr = delta >= 0 ? chalk57.green(`+${delta.toFixed(1)}%`) : chalk57.red(`${delta.toFixed(1)}%`);
|
|
29926
|
+
console.log(` ${chalk57.dim("Executions: ")} ${chalk57.yellow(s.total_executions ?? 0)} ${deltaStr} vs last week`);
|
|
29927
|
+
console.log(` ${chalk57.dim("New skills: ")} ${chalk57.cyan(s.new_skills ?? 0)}`);
|
|
29928
|
+
console.log(` ${chalk57.dim("Skills evolved: ")} ${chalk57.cyan(s.skills_evolved ?? 0)}`);
|
|
29929
|
+
console.log(` ${chalk57.dim("New learnings: ")} ${s.new_learnings ?? 0}`);
|
|
29930
|
+
console.log(` ${chalk57.dim("New agents: ")} ${s.new_agents ?? 0}`);
|
|
29931
|
+
console.log(` ${chalk57.dim("Security scans: ")} ${s.security_scans ?? 0}`);
|
|
29932
|
+
console.log(` ${chalk57.dim("Collective insights: ")} ${s.collective_insights_shared ?? 0}`);
|
|
29933
|
+
console.log(` ${chalk57.dim("Active experiments: ")} ${s.active_experiments ?? 0}`);
|
|
29934
|
+
if (r.top_used_skills?.length) {
|
|
29935
|
+
console.log();
|
|
29936
|
+
console.log(chalk57.bold(" Top Used Skills"));
|
|
29937
|
+
console.log();
|
|
29938
|
+
console.log(
|
|
29939
|
+
chalk57.dim(" ") + chalk57.dim("Skill".padEnd(36)) + chalk57.dim("Executions".padEnd(12)) + chalk57.dim("Success Rate")
|
|
29940
|
+
);
|
|
29941
|
+
console.log(chalk57.dim(" " + "\u2500".repeat(62)));
|
|
29942
|
+
for (let i = 0; i < r.top_used_skills.length; i++) {
|
|
29943
|
+
const sk = r.top_used_skills[i];
|
|
29944
|
+
const rank = chalk57.dim(`${i + 1}.`.padEnd(4));
|
|
29945
|
+
const name = chalk57.cyan(sk.name.padEnd(32));
|
|
29946
|
+
const execs = chalk57.yellow(String(sk.executions).padEnd(12));
|
|
29947
|
+
const rate = formatSuccessRate(sk.success_rate);
|
|
29948
|
+
console.log(` ${rank}${name}${execs}${rate}`);
|
|
29949
|
+
}
|
|
29950
|
+
}
|
|
29951
|
+
if (r.top_improving_skills?.length) {
|
|
29952
|
+
console.log();
|
|
29953
|
+
console.log(chalk57.bold(" Top Improving Skills"));
|
|
29954
|
+
console.log();
|
|
29955
|
+
console.log(
|
|
29956
|
+
chalk57.dim(" ") + chalk57.dim("Skill".padEnd(36)) + chalk57.dim("Confidence".padEnd(12)) + chalk57.dim("Delta")
|
|
29957
|
+
);
|
|
29958
|
+
console.log(chalk57.dim(" " + "\u2500".repeat(62)));
|
|
29959
|
+
for (let i = 0; i < r.top_improving_skills.length; i++) {
|
|
29960
|
+
const sk = r.top_improving_skills[i];
|
|
29961
|
+
const rank = chalk57.dim(`${i + 1}.`.padEnd(4));
|
|
29962
|
+
const name = chalk57.cyan(sk.name.padEnd(32));
|
|
29963
|
+
const conf = `${(sk.confidence * 100).toFixed(1)}%`.padEnd(12);
|
|
29964
|
+
const delta2 = sk.confidence_delta >= 0 ? chalk57.green(`+${(sk.confidence_delta * 100).toFixed(1)}%`) : chalk57.red(`${(sk.confidence_delta * 100).toFixed(1)}%`);
|
|
29965
|
+
console.log(` ${rank}${name}${conf}${delta2}`);
|
|
29966
|
+
}
|
|
29967
|
+
}
|
|
29968
|
+
if (r.new_skills_list?.length) {
|
|
29969
|
+
console.log();
|
|
29970
|
+
console.log(chalk57.bold(" New Skills This Week"));
|
|
29971
|
+
console.log();
|
|
29972
|
+
for (const sk of r.new_skills_list) {
|
|
29973
|
+
const conf = `${(sk.confidence * 100).toFixed(0)}%`;
|
|
29974
|
+
console.log(
|
|
29975
|
+
` ${chalk57.green("+")} ${chalk57.cyan(sk.name)} ` + chalk57.dim(`conf ${conf} executions ${sk.executions}`)
|
|
29976
|
+
);
|
|
29977
|
+
}
|
|
29978
|
+
}
|
|
29979
|
+
if (r.evolution_events?.length) {
|
|
29980
|
+
console.log();
|
|
29981
|
+
console.log(chalk57.bold(" Evolution Events"));
|
|
29982
|
+
console.log();
|
|
29983
|
+
for (const ev of r.evolution_events) {
|
|
29984
|
+
const time = new Date(ev.timestamp).toLocaleString();
|
|
29985
|
+
const typeLabel = formatEvolutionType(ev.event_type);
|
|
29986
|
+
console.log(` ${typeLabel} ${chalk57.cyan(ev.skill_name)} ${chalk57.dim(time)}`);
|
|
29987
|
+
}
|
|
29988
|
+
}
|
|
29989
|
+
if (r.top_platforms?.length) {
|
|
29990
|
+
console.log();
|
|
29991
|
+
console.log(chalk57.bold(" Top Platforms"));
|
|
29992
|
+
console.log();
|
|
29993
|
+
for (const p of r.top_platforms) {
|
|
29994
|
+
console.log(` ${chalk57.dim("\xB7")} ${p.platform.padEnd(20)} ${chalk57.yellow(p.executions)} executions`);
|
|
29995
|
+
}
|
|
29996
|
+
}
|
|
29997
|
+
if (r.failure_patterns?.length) {
|
|
29998
|
+
console.log();
|
|
29999
|
+
console.log(chalk57.bold(" Failure Patterns"));
|
|
30000
|
+
console.log();
|
|
30001
|
+
for (const fp of r.failure_patterns) {
|
|
30002
|
+
console.log(` ${chalk57.red("!")} ${fp.pattern} ${chalk57.dim(`(${fp.count} occurrences)`)}`);
|
|
30003
|
+
if (fp.affected_skills?.length) {
|
|
30004
|
+
console.log(` ${chalk57.dim("affected:")} ${fp.affected_skills.join(", ")}`);
|
|
30005
|
+
}
|
|
30006
|
+
}
|
|
30007
|
+
}
|
|
30008
|
+
if (r.experiment_missions?.length) {
|
|
30009
|
+
console.log();
|
|
30010
|
+
console.log(chalk57.bold(" Experiment Missions"));
|
|
30011
|
+
console.log();
|
|
30012
|
+
for (const m of r.experiment_missions) {
|
|
30013
|
+
const statusColor = m.status === "completed" ? chalk57.green : m.status === "active" ? chalk57.cyan : chalk57.dim;
|
|
30014
|
+
console.log(` ${statusColor(`[${m.status}]`)} ${chalk57.bold(m.title)}`);
|
|
30015
|
+
console.log(` ${chalk57.dim("experiments completed:")} ${m.experiments_completed}`);
|
|
30016
|
+
if (m.best_metric != null) {
|
|
30017
|
+
console.log(` ${chalk57.dim("best metric:")} ${m.best_metric}`);
|
|
30018
|
+
}
|
|
30019
|
+
}
|
|
30020
|
+
}
|
|
30021
|
+
if (r.highlights?.length) {
|
|
30022
|
+
console.log();
|
|
30023
|
+
console.log(chalk57.bold(" Highlights"));
|
|
30024
|
+
console.log();
|
|
30025
|
+
for (const h of r.highlights) {
|
|
30026
|
+
console.log(` ${chalk57.yellow("\u203A")} ${h}`);
|
|
30027
|
+
}
|
|
30028
|
+
}
|
|
30029
|
+
console.log();
|
|
30030
|
+
}
|
|
30031
|
+
function formatDate3(ts) {
|
|
30032
|
+
return new Date(ts).toISOString().slice(0, 10);
|
|
30033
|
+
}
|
|
30034
|
+
function formatSuccessRate(rate) {
|
|
30035
|
+
const pct = `${(rate * 100).toFixed(1)}%`;
|
|
30036
|
+
if (rate >= 0.8) return chalk57.green(pct);
|
|
30037
|
+
if (rate >= 0.5) return chalk57.yellow(pct);
|
|
30038
|
+
return chalk57.red(pct);
|
|
30039
|
+
}
|
|
30040
|
+
function formatEvolutionType(eventType) {
|
|
30041
|
+
switch (eventType) {
|
|
30042
|
+
case "promoted":
|
|
30043
|
+
return chalk57.green("[promoted]");
|
|
30044
|
+
case "canary":
|
|
30045
|
+
return chalk57.yellow("[canary] ");
|
|
30046
|
+
case "merged":
|
|
30047
|
+
return chalk57.cyan("[merged] ");
|
|
30048
|
+
case "rejected":
|
|
30049
|
+
return chalk57.red("[rejected]");
|
|
30050
|
+
default:
|
|
30051
|
+
return chalk57.dim(`[${eventType}]`);
|
|
30052
|
+
}
|
|
30053
|
+
}
|
|
30054
|
+
|
|
30055
|
+
// src/commands/mission.ts
|
|
30056
|
+
import { Command as Command55 } from "commander";
|
|
30057
|
+
import chalk58 from "chalk";
|
|
30058
|
+
import ora54 from "ora";
|
|
30059
|
+
var missionCommand = new Command55("mission").description("Autonomous Mission Runner \u2014 Ralph Loop").addCommand(missionListCommand()).addCommand(missionCreateCommand()).addCommand(missionRunCommand()).addCommand(missionStatusCommand()).addCommand(missionStopCommand()).addCommand(missionAgentsCommand());
|
|
30060
|
+
function missionListCommand() {
|
|
30061
|
+
return new Command55("list").description("List missions").option("--status <status>", "Filter by status (proposed|approved|active|completed)").option("--limit <n>", "Max results", "20").option("--json", "Output as JSON").action(async (options) => {
|
|
30062
|
+
const client = getClient();
|
|
30063
|
+
const spinner = ora54("Fetching missions...").start();
|
|
30064
|
+
try {
|
|
30065
|
+
const missions = await client.query(toFunctionName(api.research.listMissions), {
|
|
30066
|
+
status: options.status,
|
|
30067
|
+
limit: parseInt(options.limit)
|
|
30068
|
+
});
|
|
30069
|
+
spinner.stop();
|
|
30070
|
+
if (options.json) {
|
|
30071
|
+
console.log(JSON.stringify(missions, null, 2));
|
|
30072
|
+
return;
|
|
30073
|
+
}
|
|
30074
|
+
if (!missions || missions.length === 0) {
|
|
30075
|
+
console.log(chalk58.dim("No missions found."));
|
|
30076
|
+
return;
|
|
30077
|
+
}
|
|
30078
|
+
console.log(chalk58.bold(`
|
|
30079
|
+
Missions (${missions.length})
|
|
30080
|
+
`));
|
|
30081
|
+
for (const m of missions) {
|
|
30082
|
+
const statusColor = m.status === "active" ? chalk58.green : m.status === "completed" ? chalk58.blue : m.status === "proposed" ? chalk58.yellow : chalk58.dim;
|
|
30083
|
+
console.log(` ${statusColor("\u25CF")} ${chalk58.bold(m.title)}`);
|
|
30084
|
+
console.log(` ${chalk58.dim("ID:")} ${m._id}`);
|
|
30085
|
+
console.log(` ${chalk58.dim("Type:")} ${m.mission_type} ${chalk58.dim("Status:")} ${statusColor(m.status)}`);
|
|
30086
|
+
if (m.experiment_config?.experiments_completed !== void 0) {
|
|
30087
|
+
console.log(` ${chalk58.dim("Experiments:")} ${m.experiment_config.experiments_completed}/${m.experiment_config.max_experiments}`);
|
|
30088
|
+
}
|
|
30089
|
+
console.log();
|
|
30090
|
+
}
|
|
30091
|
+
} catch (e) {
|
|
30092
|
+
spinner.fail(e.message);
|
|
30093
|
+
}
|
|
30094
|
+
});
|
|
30095
|
+
}
|
|
30096
|
+
function missionCreateCommand() {
|
|
30097
|
+
return new Command55("create").description("Create a new research mission").argument("<hub_id>", "Hub ID to create mission in").requiredOption("--title <title>", "Mission title").requiredOption("--question <question>", "Research question").option("--type <type>", "Mission type", "technical").option("--methodology <method>", "Research methodology").option("--description <desc>", "Detailed description").option("--max-experiments <n>", "Max experiments", "50").option("--time-hours <n>", "Time budget in hours", "24").option("--max-cost <n>", "Max cost in USD", "5.00").option("--metric <name>", "Primary metric name", "quality_score").option("--direction <dir>", "Metric direction (maximize|minimize)", "maximize").option("--json", "Output as JSON").action(async (hubId, options) => {
|
|
30098
|
+
const client = getClient();
|
|
30099
|
+
const spinner = ora54("Creating mission...").start();
|
|
30100
|
+
try {
|
|
30101
|
+
const result = await client.mutation(toFunctionName(api.research.proposeMission), {
|
|
30102
|
+
hub_id: hubId,
|
|
30103
|
+
title: options.title,
|
|
30104
|
+
description: options.description || options.title,
|
|
30105
|
+
research_question: options.question,
|
|
30106
|
+
methodology: options.methodology,
|
|
30107
|
+
mission_type: options.type,
|
|
30108
|
+
proposed_by: "cli-user",
|
|
30109
|
+
experiment_config: {
|
|
30110
|
+
enabled: true,
|
|
30111
|
+
primary_metric: options.metric,
|
|
30112
|
+
metric_direction: options.direction,
|
|
30113
|
+
budget_minutes_per_experiment: 15,
|
|
30114
|
+
max_experiments: parseInt(options.maxExperiments),
|
|
30115
|
+
time_budget_hours: parseInt(options.timeHours),
|
|
30116
|
+
max_cost_usd: parseFloat(options.maxCost),
|
|
30117
|
+
minimum_improvement_threshold: 0.01
|
|
30118
|
+
}
|
|
30119
|
+
});
|
|
30120
|
+
spinner.succeed("Mission created");
|
|
30121
|
+
if (options.json) {
|
|
30122
|
+
console.log(JSON.stringify(result, null, 2));
|
|
30123
|
+
} else {
|
|
30124
|
+
console.log(`
|
|
30125
|
+
${chalk58.bold("Mission ID:")} ${result}`);
|
|
30126
|
+
console.log(` ${chalk58.dim("Next:")} hubify mission run ${result}`);
|
|
30127
|
+
}
|
|
30128
|
+
} catch (e) {
|
|
30129
|
+
spinner.fail(e.message);
|
|
30130
|
+
}
|
|
30131
|
+
});
|
|
30132
|
+
}
|
|
30133
|
+
function missionRunCommand() {
|
|
30134
|
+
return new Command55("run").description("Run autonomous mission loop (Ralph Loop)").argument("<mission_id>", "Mission ID to run").option("--cycles <n>", "Max cycles before stopping (0 = until budget exhausted)", "0").option("--interval <ms>", "Delay between cycles in ms", "2000").option("--dry-run", "Show what would happen without executing").action(async (missionId, options) => {
|
|
30135
|
+
const client = getClient();
|
|
30136
|
+
const maxCycles = parseInt(options.cycles);
|
|
30137
|
+
const interval = parseInt(options.interval);
|
|
30138
|
+
console.log(chalk58.bold("\n Ralph Loop \u2014 Autonomous Mission Runner\n"));
|
|
30139
|
+
console.log(` ${chalk58.dim("Mission:")} ${missionId}`);
|
|
30140
|
+
console.log(` ${chalk58.dim("Max cycles:")} ${maxCycles || "unlimited (budget-limited)"}`);
|
|
30141
|
+
console.log(` ${chalk58.dim("Interval:")} ${interval}ms`);
|
|
30142
|
+
console.log();
|
|
30143
|
+
const spinner = ora54("Loading mission...").start();
|
|
30144
|
+
let mission;
|
|
30145
|
+
try {
|
|
30146
|
+
mission = await client.query(toFunctionName(api.research.getMission), {
|
|
30147
|
+
id: missionId
|
|
30148
|
+
});
|
|
30149
|
+
if (!mission) {
|
|
30150
|
+
spinner.fail("Mission not found");
|
|
30151
|
+
return;
|
|
30152
|
+
}
|
|
30153
|
+
spinner.succeed(`${mission.title} (${mission.status})`);
|
|
30154
|
+
} catch (e) {
|
|
30155
|
+
spinner.fail(e.message);
|
|
30156
|
+
return;
|
|
30157
|
+
}
|
|
30158
|
+
if (mission.status === "proposed") {
|
|
30159
|
+
const approveSpinner = ora54("Auto-approving mission...").start();
|
|
30160
|
+
try {
|
|
30161
|
+
await client.mutation(toFunctionName(api.research.approveMission), {
|
|
30162
|
+
mission_id: missionId,
|
|
30163
|
+
approved_by: "ralph-loop"
|
|
30164
|
+
});
|
|
30165
|
+
approveSpinner.succeed("Mission approved");
|
|
30166
|
+
} catch (e) {
|
|
30167
|
+
approveSpinner.fail(e.message);
|
|
30168
|
+
}
|
|
30169
|
+
}
|
|
30170
|
+
if (mission.status === "proposed" || mission.status === "approved") {
|
|
30171
|
+
const startSpinner = ora54("Starting mission...").start();
|
|
30172
|
+
try {
|
|
30173
|
+
await client.mutation(toFunctionName(api.research.startMission), {
|
|
30174
|
+
mission_id: missionId
|
|
30175
|
+
});
|
|
30176
|
+
startSpinner.succeed("Mission started");
|
|
30177
|
+
} catch (e) {
|
|
30178
|
+
startSpinner.fail(e.message);
|
|
30179
|
+
return;
|
|
30180
|
+
}
|
|
30181
|
+
}
|
|
30182
|
+
const config = mission.experiment_config;
|
|
30183
|
+
if (config) {
|
|
30184
|
+
console.log(chalk58.dim("\n Budget:"));
|
|
30185
|
+
console.log(` Experiments: ${config.experiments_completed || 0}/${config.max_experiments}`);
|
|
30186
|
+
console.log(` Time: ${config.time_budget_hours}h`);
|
|
30187
|
+
console.log(` Cost: $${(config.cost_spent_usd || 0).toFixed(2)}/$${config.max_cost_usd}`);
|
|
30188
|
+
console.log(` Metric: ${config.primary_metric} (${config.metric_direction})`);
|
|
30189
|
+
if (config.best_metric_value !== void 0) {
|
|
30190
|
+
console.log(` Best: ${config.best_metric_value}`);
|
|
30191
|
+
}
|
|
30192
|
+
}
|
|
30193
|
+
if (options.dryRun) {
|
|
30194
|
+
console.log(chalk58.yellow("\n Dry run \u2014 no experiments will be executed."));
|
|
30195
|
+
return;
|
|
30196
|
+
}
|
|
30197
|
+
console.log(chalk58.green("\n \u26A1 Entering Ralph Loop...\n"));
|
|
30198
|
+
let cycle = 0;
|
|
30199
|
+
let consecutiveFailures = 0;
|
|
30200
|
+
const maxConsecutiveFailures = 5;
|
|
30201
|
+
while (true) {
|
|
30202
|
+
cycle++;
|
|
30203
|
+
if (maxCycles > 0 && cycle > maxCycles) {
|
|
30204
|
+
console.log(chalk58.yellow(`
|
|
30205
|
+
Reached max cycles (${maxCycles}). Stopping.`));
|
|
30206
|
+
break;
|
|
30207
|
+
}
|
|
30208
|
+
const cycleSpinner = ora54(`Cycle ${cycle}: Checking frontier...`).start();
|
|
30209
|
+
try {
|
|
30210
|
+
const summary = await client.query(toFunctionName(api.research.getExperimentSummary), {
|
|
30211
|
+
mission_id: missionId
|
|
30212
|
+
});
|
|
30213
|
+
if (!summary) {
|
|
30214
|
+
cycleSpinner.fail("Could not fetch experiment summary");
|
|
30215
|
+
break;
|
|
30216
|
+
}
|
|
30217
|
+
const expConfig = summary.config;
|
|
30218
|
+
if (expConfig) {
|
|
30219
|
+
if (expConfig.experiments_completed >= expConfig.max_experiments) {
|
|
30220
|
+
cycleSpinner.info(`Budget exhausted: ${expConfig.experiments_completed}/${expConfig.max_experiments} experiments`);
|
|
30221
|
+
break;
|
|
30222
|
+
}
|
|
30223
|
+
if (expConfig.cost_spent_usd >= expConfig.max_cost_usd) {
|
|
30224
|
+
cycleSpinner.info(`Cost budget exhausted: $${expConfig.cost_spent_usd.toFixed(2)}/$${expConfig.max_cost_usd}`);
|
|
30225
|
+
break;
|
|
30226
|
+
}
|
|
30227
|
+
}
|
|
30228
|
+
const frontier = await client.query(toFunctionName(api.experimentDag.getLeaves), {
|
|
30229
|
+
mission_id: missionId,
|
|
30230
|
+
limit: 5
|
|
30231
|
+
});
|
|
30232
|
+
if (!frontier || frontier.length === 0) {
|
|
30233
|
+
cycleSpinner.warn("No frontier nodes available \u2014 waiting...");
|
|
30234
|
+
consecutiveFailures++;
|
|
30235
|
+
if (consecutiveFailures >= maxConsecutiveFailures) {
|
|
30236
|
+
console.log(chalk58.yellow(`
|
|
30237
|
+
${maxConsecutiveFailures} consecutive failures. Stopping.`));
|
|
30238
|
+
break;
|
|
30239
|
+
}
|
|
30240
|
+
await sleep2(interval * 2);
|
|
30241
|
+
continue;
|
|
30242
|
+
}
|
|
30243
|
+
cycleSpinner.text = `Cycle ${cycle}: Triggering experiment (frontier: ${frontier.length} nodes)...`;
|
|
30244
|
+
const triggerResult = await client.mutation(toFunctionName(api.experimentRunner.triggerCycle), {
|
|
30245
|
+
mission_id: missionId
|
|
30246
|
+
});
|
|
30247
|
+
if (!triggerResult.triggered) {
|
|
30248
|
+
cycleSpinner.info(`Cycle ${cycle}: Budget exhausted (${triggerResult.reason})`);
|
|
30249
|
+
break;
|
|
30250
|
+
}
|
|
30251
|
+
consecutiveFailures = 0;
|
|
30252
|
+
const completed = triggerResult.experiments_completed || 0;
|
|
30253
|
+
const max = triggerResult.max_experiments || 0;
|
|
30254
|
+
const cost = triggerResult.cost_spent_usd || 0;
|
|
30255
|
+
const maxCost = triggerResult.max_cost_usd || 0;
|
|
30256
|
+
const best = expConfig?.best_metric_value;
|
|
30257
|
+
const pct = max > 0 ? Math.round(completed / max * 100) : 0;
|
|
30258
|
+
const bar = progressBar3(pct, 20);
|
|
30259
|
+
cycleSpinner.succeed(
|
|
30260
|
+
`Cycle ${cycle}: ${bar} ${completed}/${max} experiments \xB7 $${cost.toFixed(2)}/$${maxCost.toFixed(2)} \xB7 best: ${best ?? "\u2014"}`
|
|
30261
|
+
);
|
|
30262
|
+
} catch (e) {
|
|
30263
|
+
cycleSpinner.fail(`Cycle ${cycle}: ${e.message}`);
|
|
30264
|
+
consecutiveFailures++;
|
|
30265
|
+
if (consecutiveFailures >= maxConsecutiveFailures) {
|
|
30266
|
+
console.log(chalk58.red(`
|
|
30267
|
+
${maxConsecutiveFailures} consecutive failures. Stopping.`));
|
|
30268
|
+
break;
|
|
30269
|
+
}
|
|
30270
|
+
}
|
|
30271
|
+
await sleep2(interval);
|
|
30272
|
+
}
|
|
30273
|
+
console.log(chalk58.bold("\n Ralph Loop completed.\n"));
|
|
30274
|
+
try {
|
|
30275
|
+
const finalSummary = await client.query(toFunctionName(api.research.getExperimentSummary), {
|
|
30276
|
+
mission_id: missionId
|
|
30277
|
+
});
|
|
30278
|
+
if (finalSummary?.config) {
|
|
30279
|
+
const c = finalSummary.config;
|
|
30280
|
+
console.log(` Experiments: ${c.experiments_completed}/${c.max_experiments}`);
|
|
30281
|
+
console.log(` Cost: $${(c.cost_spent_usd || 0).toFixed(2)}/$${c.max_cost_usd}`);
|
|
30282
|
+
console.log(` Best metric: ${c.best_metric_value ?? "none"}`);
|
|
30283
|
+
if (c.best_node_id) {
|
|
30284
|
+
console.log(` Best node: ${c.best_node_id}`);
|
|
30285
|
+
}
|
|
30286
|
+
}
|
|
30287
|
+
if (finalSummary?.stats) {
|
|
30288
|
+
console.log(` DAG: ${finalSummary.stats.total_nodes} nodes, depth ${finalSummary.stats.max_depth}`);
|
|
30289
|
+
console.log(` Success rate: ${Math.round(finalSummary.stats.completed_nodes / Math.max(finalSummary.stats.total_nodes, 1) * 100)}%`);
|
|
30290
|
+
}
|
|
30291
|
+
} catch {
|
|
30292
|
+
}
|
|
30293
|
+
console.log(`
|
|
30294
|
+
${chalk58.dim("View results:")} hubify mission status ${missionId}`);
|
|
30295
|
+
console.log(` ${chalk58.dim("Best path:")} hubify labs best-path ${missionId}
|
|
30296
|
+
`);
|
|
30297
|
+
});
|
|
30298
|
+
}
|
|
30299
|
+
function missionStatusCommand() {
|
|
30300
|
+
return new Command55("status").description("Show mission progress and DAG stats").argument("<mission_id>", "Mission ID").option("--json", "Output as JSON").action(async (missionId, options) => {
|
|
30301
|
+
const client = getClient();
|
|
30302
|
+
const spinner = ora54("Fetching mission status...").start();
|
|
30303
|
+
try {
|
|
30304
|
+
const [mission, summary, dagStats] = await Promise.all([
|
|
30305
|
+
client.query(toFunctionName(api.research.getMission), { id: missionId }),
|
|
30306
|
+
client.query(toFunctionName(api.research.getExperimentSummary), { mission_id: missionId }),
|
|
30307
|
+
client.query(toFunctionName(api.experimentDag.getDAGStats), { mission_id: missionId })
|
|
30308
|
+
]);
|
|
30309
|
+
spinner.stop();
|
|
30310
|
+
if (!mission) {
|
|
30311
|
+
console.log(chalk58.red("Mission not found."));
|
|
30312
|
+
return;
|
|
30313
|
+
}
|
|
30314
|
+
if (options.json) {
|
|
30315
|
+
console.log(JSON.stringify({ mission, summary, dagStats }, null, 2));
|
|
30316
|
+
return;
|
|
30317
|
+
}
|
|
30318
|
+
const statusColor = mission.status === "active" ? chalk58.green : mission.status === "completed" ? chalk58.blue : chalk58.yellow;
|
|
30319
|
+
console.log(chalk58.bold(`
|
|
30320
|
+
${mission.title}`));
|
|
30321
|
+
console.log(` ${chalk58.dim("Status:")} ${statusColor(mission.status)} ${chalk58.dim("Type:")} ${mission.mission_type}`);
|
|
30322
|
+
console.log(` ${chalk58.dim("Question:")} ${mission.research_question}`);
|
|
30323
|
+
if (summary?.config) {
|
|
30324
|
+
const c = summary.config;
|
|
30325
|
+
const pct = c.max_experiments > 0 ? Math.round(c.experiments_completed / c.max_experiments * 100) : 0;
|
|
30326
|
+
console.log(chalk58.bold("\n Experiment Budget"));
|
|
30327
|
+
console.log(` ${progressBar3(pct, 30)} ${c.experiments_completed}/${c.max_experiments} experiments (${pct}%)`);
|
|
30328
|
+
console.log(` Cost: $${(c.cost_spent_usd || 0).toFixed(2)} / $${c.max_cost_usd}`);
|
|
30329
|
+
console.log(` Metric: ${c.primary_metric} (${c.metric_direction})`);
|
|
30330
|
+
if (c.best_metric_value !== void 0) {
|
|
30331
|
+
console.log(` ${chalk58.green("Best:")} ${c.best_metric_value}`);
|
|
30332
|
+
}
|
|
30333
|
+
}
|
|
30334
|
+
if (dagStats) {
|
|
30335
|
+
console.log(chalk58.bold("\n DAG Stats"));
|
|
30336
|
+
console.log(` Total nodes: ${dagStats.total_nodes}`);
|
|
30337
|
+
console.log(` Completed: ${dagStats.completed_nodes} / Running: ${dagStats.running_nodes} / Reverted: ${dagStats.reverted_nodes}`);
|
|
30338
|
+
console.log(` Max depth: ${dagStats.max_depth}`);
|
|
30339
|
+
console.log(` Frontier: ${dagStats.frontier_size} nodes`);
|
|
30340
|
+
console.log(` Agents: ${dagStats.unique_agents}`);
|
|
30341
|
+
}
|
|
30342
|
+
console.log();
|
|
30343
|
+
} catch (e) {
|
|
30344
|
+
spinner.fail(e.message);
|
|
30345
|
+
}
|
|
30346
|
+
});
|
|
30347
|
+
}
|
|
30348
|
+
function missionStopCommand() {
|
|
30349
|
+
return new Command55("stop").description("Complete a mission").argument("<mission_id>", "Mission ID").option("--conclusion <text>", "Conclusion summary").action(async (missionId, options) => {
|
|
30350
|
+
const client = getClient();
|
|
30351
|
+
const spinner = ora54("Completing mission...").start();
|
|
30352
|
+
try {
|
|
30353
|
+
await client.mutation(toFunctionName(api.research.completeMission), {
|
|
30354
|
+
mission_id: missionId,
|
|
30355
|
+
conclusion: options.conclusion || "Mission completed via Ralph Loop CLI."
|
|
30356
|
+
});
|
|
30357
|
+
spinner.succeed("Mission completed");
|
|
30358
|
+
} catch (e) {
|
|
30359
|
+
spinner.fail(e.message);
|
|
30360
|
+
}
|
|
30361
|
+
});
|
|
30362
|
+
}
|
|
30363
|
+
function missionAgentsCommand() {
|
|
30364
|
+
return new Command55("agents").description("Show best-fit agents for a mission (uses agent dispatch scoring)").argument("<mission_id>", "Mission ID").option("--limit <n>", "Max agents", "10").option("--json", "Output as JSON").action(async (missionId, options) => {
|
|
30365
|
+
const client = getClient();
|
|
30366
|
+
const spinner = ora54("Scoring agents for mission...").start();
|
|
30367
|
+
try {
|
|
30368
|
+
const [candidates, available] = await Promise.all([
|
|
30369
|
+
client.query(toFunctionName(api.agentDispatch.pickBestAgent), {
|
|
30370
|
+
mission_id: missionId
|
|
30371
|
+
}).catch(() => null),
|
|
30372
|
+
client.query(toFunctionName(api.agentDispatch.listAvailableAgents), {
|
|
30373
|
+
limit: parseInt(options.limit)
|
|
30374
|
+
})
|
|
30375
|
+
]);
|
|
30376
|
+
spinner.stop();
|
|
30377
|
+
if (options.json) {
|
|
30378
|
+
console.log(JSON.stringify({ candidates, available }, null, 2));
|
|
30379
|
+
return;
|
|
30380
|
+
}
|
|
30381
|
+
if (candidates && candidates.length > 0) {
|
|
30382
|
+
console.log(chalk58.bold("\n Best-fit agents for this mission\n"));
|
|
30383
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
30384
|
+
const c = candidates[i];
|
|
30385
|
+
const score = ((c.score ?? 0) * 100).toFixed(0);
|
|
30386
|
+
console.log(
|
|
30387
|
+
` ${chalk58.dim(`${(i + 1).toString().padStart(2)}.`)} ${chalk58.bold(c.agent_id || c.username || "unknown")} ${chalk58.green(`${score}%`)} match ${chalk58.dim(`rep: ${((c.reputation ?? 0) * 100).toFixed(0)}%`)}`
|
|
30388
|
+
);
|
|
30389
|
+
}
|
|
30390
|
+
console.log();
|
|
30391
|
+
}
|
|
30392
|
+
const agents = available;
|
|
30393
|
+
if (agents && agents.length > 0) {
|
|
30394
|
+
console.log(chalk58.bold(` Available agents (${agents.length})
|
|
30395
|
+
`));
|
|
30396
|
+
for (const a of agents) {
|
|
30397
|
+
const rep = ((a.reputation ?? 0) * 100).toFixed(0);
|
|
30398
|
+
const load = a.running_tasks ?? 0;
|
|
30399
|
+
console.log(
|
|
30400
|
+
` ${chalk58.white((a.agent_id || a.username || "\u2014").padEnd(24))} rep: ${chalk58.green(`${rep}%`.padEnd(6))} load: ${load === 0 ? chalk58.green("idle") : chalk58.yellow(`${load} tasks`)} ${chalk58.dim(a.platform || "")}`
|
|
30401
|
+
);
|
|
30402
|
+
}
|
|
30403
|
+
console.log();
|
|
30404
|
+
} else {
|
|
30405
|
+
console.log(chalk58.dim("\n No available agents found.\n"));
|
|
30406
|
+
}
|
|
30407
|
+
} catch (e) {
|
|
30408
|
+
spinner.fail(e.message);
|
|
30409
|
+
}
|
|
30410
|
+
});
|
|
30411
|
+
}
|
|
30412
|
+
function sleep2(ms) {
|
|
30413
|
+
return new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
30414
|
+
}
|
|
30415
|
+
function progressBar3(pct, width) {
|
|
30416
|
+
const filled = Math.round(pct / 100 * width);
|
|
30417
|
+
const empty = width - filled;
|
|
30418
|
+
return chalk58.green("\u2588".repeat(filled)) + chalk58.dim("\u2591".repeat(empty));
|
|
30419
|
+
}
|
|
30420
|
+
|
|
30421
|
+
// src/commands/meta.ts
|
|
30422
|
+
import { Command as Command56 } from "commander";
|
|
30423
|
+
import chalk59 from "chalk";
|
|
30424
|
+
import ora55 from "ora";
|
|
30425
|
+
import * as fs26 from "fs";
|
|
30426
|
+
import * as path26 from "path";
|
|
30427
|
+
import { parse as parseYaml10 } from "yaml";
|
|
30428
|
+
function getMetaSkillsDir() {
|
|
30429
|
+
const thisDir = path26.dirname(new URL(import.meta.url).pathname);
|
|
30430
|
+
const pkgRoot = path26.resolve(thisDir, "..", "..");
|
|
30431
|
+
return path26.join(pkgRoot, "meta-skills");
|
|
30432
|
+
}
|
|
30433
|
+
function getProjectSkillsDir() {
|
|
30434
|
+
return path26.join(process.cwd(), ".hubify", "skills");
|
|
30435
|
+
}
|
|
30436
|
+
function parseFrontmatter3(content) {
|
|
30437
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
30438
|
+
if (!match) return {};
|
|
30439
|
+
try {
|
|
30440
|
+
return parseYaml10(match[1]);
|
|
30441
|
+
} catch {
|
|
30442
|
+
return {};
|
|
30443
|
+
}
|
|
30444
|
+
}
|
|
30445
|
+
function loadMetaSkills() {
|
|
30446
|
+
const metaDir = getMetaSkillsDir();
|
|
30447
|
+
if (!fs26.existsSync(metaDir)) {
|
|
30448
|
+
return [];
|
|
30449
|
+
}
|
|
30450
|
+
const skills = [];
|
|
30451
|
+
const entries = fs26.readdirSync(metaDir, { withFileTypes: true });
|
|
30452
|
+
for (const entry of entries) {
|
|
30453
|
+
if (!entry.isDirectory()) continue;
|
|
30454
|
+
const skillMdPath = path26.join(metaDir, entry.name, "SKILL.md");
|
|
30455
|
+
if (!fs26.existsSync(skillMdPath)) continue;
|
|
30456
|
+
const content = fs26.readFileSync(skillMdPath, "utf-8");
|
|
30457
|
+
const fm = parseFrontmatter3(content);
|
|
30458
|
+
skills.push({
|
|
30459
|
+
name: fm.name || `hubify/${entry.name}`,
|
|
30460
|
+
dir: entry.name,
|
|
30461
|
+
version: typeof fm.version === "string" ? fm.version : String(fm.version || "1.0.0"),
|
|
30462
|
+
description: fm.description || "",
|
|
30463
|
+
category: fm.category || "meta",
|
|
30464
|
+
triggers: Array.isArray(fm.triggers) ? fm.triggers : [],
|
|
30465
|
+
outputs: Array.isArray(fm.outputs) ? fm.outputs : []
|
|
30466
|
+
});
|
|
30467
|
+
}
|
|
30468
|
+
return skills;
|
|
30469
|
+
}
|
|
30470
|
+
function metaListCommand() {
|
|
30471
|
+
return new Command56("list").description("List available meta-skills").option("--json", "Output as JSON").action(async (options) => {
|
|
30472
|
+
const skills = loadMetaSkills();
|
|
30473
|
+
if (skills.length === 0) {
|
|
30474
|
+
console.log(chalk59.dim("\n No meta-skills found.\n"));
|
|
30475
|
+
return;
|
|
30476
|
+
}
|
|
30477
|
+
if (options.json) {
|
|
30478
|
+
console.log(JSON.stringify(skills, null, 2));
|
|
30479
|
+
return;
|
|
30480
|
+
}
|
|
30481
|
+
console.log(chalk59.bold("\n Hubify Meta-Skills\n"));
|
|
30482
|
+
console.log(chalk59.gray(" System skills that help agents interact with the Hubify platform.\n"));
|
|
30483
|
+
for (const skill of skills) {
|
|
30484
|
+
console.log(
|
|
30485
|
+
` ${chalk59.cyan(skill.name.padEnd(26))} ${chalk59.white(`v${skill.version}`)} ${chalk59.gray(skill.description)}`
|
|
30486
|
+
);
|
|
30487
|
+
if (skill.triggers.length > 0) {
|
|
30488
|
+
console.log(
|
|
30489
|
+
` ${" ".repeat(26)} ${chalk59.dim("triggers:")} ${chalk59.dim(skill.triggers.slice(0, 3).join(", "))}`
|
|
30490
|
+
);
|
|
30491
|
+
}
|
|
30492
|
+
console.log();
|
|
30493
|
+
}
|
|
30494
|
+
const projectDir = getProjectSkillsDir();
|
|
30495
|
+
if (fs26.existsSync(projectDir)) {
|
|
30496
|
+
const installed = skills.filter(
|
|
30497
|
+
(s) => fs26.existsSync(path26.join(projectDir, s.dir, "SKILL.md"))
|
|
30498
|
+
);
|
|
30499
|
+
if (installed.length > 0) {
|
|
30500
|
+
console.log(
|
|
30501
|
+
chalk59.green(` ${installed.length}/${skills.length} installed in this project`)
|
|
30502
|
+
);
|
|
30503
|
+
} else {
|
|
30504
|
+
console.log(chalk59.dim(" None installed in this project."));
|
|
30505
|
+
console.log(chalk59.dim(" Run: hubify meta install"));
|
|
30506
|
+
}
|
|
30507
|
+
} else {
|
|
30508
|
+
console.log(chalk59.dim(" Not a Hubify project. Run: hubify init"));
|
|
30509
|
+
}
|
|
30510
|
+
console.log();
|
|
30511
|
+
});
|
|
30512
|
+
}
|
|
30513
|
+
function metaInstallCommand() {
|
|
30514
|
+
return new Command56("install").description("Install meta-skills into the current project").argument("[name]", "Specific meta-skill to install (e.g., find-skills). Installs all if omitted.").option("--force", "Overwrite existing meta-skills").action(async (name, options) => {
|
|
30515
|
+
const spinner = ora55();
|
|
30516
|
+
const skills = loadMetaSkills();
|
|
30517
|
+
if (skills.length === 0) {
|
|
30518
|
+
console.log(chalk59.red("\n No meta-skills found in package.\n"));
|
|
30519
|
+
process.exit(1);
|
|
30520
|
+
}
|
|
30521
|
+
const toInstall = name ? skills.filter((s) => s.dir === name || s.name === name || s.name === `hubify/${name}`) : skills;
|
|
30522
|
+
if (toInstall.length === 0) {
|
|
30523
|
+
console.log(chalk59.red(`
|
|
30524
|
+
Meta-skill "${name}" not found.`));
|
|
30525
|
+
console.log(chalk59.gray(" Available: " + skills.map((s) => s.dir).join(", ")));
|
|
30526
|
+
console.log();
|
|
30527
|
+
process.exit(1);
|
|
30528
|
+
}
|
|
30529
|
+
const projectDir = getProjectSkillsDir();
|
|
30530
|
+
const metaDir = getMetaSkillsDir();
|
|
30531
|
+
if (!fs26.existsSync(projectDir)) {
|
|
30532
|
+
fs26.mkdirSync(projectDir, { recursive: true });
|
|
30533
|
+
}
|
|
30534
|
+
spinner.start(`Installing ${toInstall.length} meta-skill(s)...`);
|
|
30535
|
+
let installed = 0;
|
|
30536
|
+
let skipped = 0;
|
|
30537
|
+
for (const skill of toInstall) {
|
|
30538
|
+
const srcDir = path26.join(metaDir, skill.dir);
|
|
30539
|
+
const destDir = path26.join(projectDir, skill.dir);
|
|
30540
|
+
if (fs26.existsSync(destDir) && !options.force) {
|
|
30541
|
+
skipped++;
|
|
30542
|
+
continue;
|
|
30543
|
+
}
|
|
30544
|
+
fs26.mkdirSync(destDir, { recursive: true });
|
|
30545
|
+
const srcFile = path26.join(srcDir, "SKILL.md");
|
|
30546
|
+
const destFile = path26.join(destDir, "SKILL.md");
|
|
30547
|
+
fs26.copyFileSync(srcFile, destFile);
|
|
30548
|
+
installed++;
|
|
30549
|
+
}
|
|
30550
|
+
if (installed > 0 && skipped > 0) {
|
|
30551
|
+
spinner.succeed(
|
|
30552
|
+
`Installed ${installed} meta-skill(s), skipped ${skipped} (already installed)`
|
|
30553
|
+
);
|
|
30554
|
+
} else if (installed > 0) {
|
|
30555
|
+
spinner.succeed(`Installed ${installed} meta-skill(s)`);
|
|
30556
|
+
} else {
|
|
30557
|
+
spinner.info(`All ${skipped} meta-skill(s) already installed (use --force to overwrite)`);
|
|
30558
|
+
}
|
|
30559
|
+
console.log(chalk59.gray(`
|
|
30560
|
+
Location: ${projectDir}`));
|
|
30561
|
+
if (installed > 0) {
|
|
30562
|
+
console.log(chalk59.gray(" Installed:"));
|
|
30563
|
+
for (const skill of toInstall) {
|
|
30564
|
+
const destDir = path26.join(projectDir, skill.dir);
|
|
30565
|
+
if (fs26.existsSync(path26.join(destDir, "SKILL.md"))) {
|
|
30566
|
+
console.log(` ${chalk59.cyan(skill.name)}`);
|
|
30567
|
+
}
|
|
30568
|
+
}
|
|
30569
|
+
}
|
|
30570
|
+
console.log();
|
|
30571
|
+
});
|
|
30572
|
+
}
|
|
30573
|
+
function metaRunCommand() {
|
|
30574
|
+
return new Command56("run").description("Execute a meta-skill").argument("<name>", "Meta-skill name (e.g., find-skills, create-skill)").argument("[args...]", "Arguments to pass to the meta-skill").option("--json", "Output as JSON").action(async (name, args, options) => {
|
|
30575
|
+
const spinner = ora55();
|
|
30576
|
+
const skills = loadMetaSkills();
|
|
30577
|
+
const skill = skills.find(
|
|
30578
|
+
(s) => s.dir === name || s.name === name || s.name === `hubify/${name}`
|
|
30579
|
+
);
|
|
30580
|
+
if (!skill) {
|
|
30581
|
+
console.log(chalk59.red(`
|
|
30582
|
+
Meta-skill "${name}" not found.`));
|
|
30583
|
+
console.log(chalk59.gray(" Available: " + skills.map((s) => s.dir).join(", ")));
|
|
30584
|
+
console.log();
|
|
30585
|
+
process.exit(1);
|
|
30586
|
+
}
|
|
30587
|
+
const query = args.join(" ");
|
|
30588
|
+
switch (skill.dir) {
|
|
30589
|
+
case "find-skills":
|
|
30590
|
+
await runFindSkills(query, options, spinner);
|
|
30591
|
+
break;
|
|
30592
|
+
case "create-skill":
|
|
30593
|
+
await runCreateSkill(query, options, spinner);
|
|
30594
|
+
break;
|
|
30595
|
+
case "update-skills":
|
|
30596
|
+
await runUpdateSkills(query, options, spinner);
|
|
30597
|
+
break;
|
|
30598
|
+
case "chain-skills":
|
|
30599
|
+
await runChainSkills(query, options, spinner);
|
|
30600
|
+
break;
|
|
30601
|
+
default:
|
|
30602
|
+
console.log(chalk59.yellow(`
|
|
30603
|
+
Meta-skill "${skill.name}" does not have a run handler yet.`));
|
|
30604
|
+
console.log(chalk59.gray(" You can read its instructions:"));
|
|
30605
|
+
console.log(chalk59.cyan(` cat ${path26.join(getMetaSkillsDir(), skill.dir, "SKILL.md")}`));
|
|
30606
|
+
console.log();
|
|
30607
|
+
}
|
|
30608
|
+
});
|
|
30609
|
+
}
|
|
30610
|
+
async function runFindSkills(query, options, spinner) {
|
|
30611
|
+
if (!query) {
|
|
30612
|
+
console.log(chalk59.red("\n Usage: hubify meta run find-skills <search query>"));
|
|
30613
|
+
console.log(chalk59.gray(" Example: hubify meta run find-skills web scraping\n"));
|
|
30614
|
+
process.exit(1);
|
|
30615
|
+
}
|
|
30616
|
+
spinner.start(`Searching registry for "${query}"...`);
|
|
30617
|
+
try {
|
|
30618
|
+
const client = getClient();
|
|
30619
|
+
const results = await client.query(toFunctionName(api.skills.search), {
|
|
30620
|
+
query,
|
|
30621
|
+
limit: 10
|
|
30622
|
+
});
|
|
30623
|
+
spinner.stop();
|
|
30624
|
+
const skills = results;
|
|
30625
|
+
if (options.json) {
|
|
30626
|
+
console.log(JSON.stringify(skills, null, 2));
|
|
30627
|
+
return;
|
|
30628
|
+
}
|
|
30629
|
+
if (!skills || skills.length === 0) {
|
|
30630
|
+
console.log(chalk59.dim(`
|
|
30631
|
+
No skills found for "${query}".`));
|
|
30632
|
+
console.log(chalk59.dim(" Try broader search terms or different keywords.\n"));
|
|
30633
|
+
return;
|
|
30634
|
+
}
|
|
30635
|
+
console.log(chalk59.bold(`
|
|
30636
|
+
Results for "${query}" (${skills.length} found)
|
|
30637
|
+
`));
|
|
30638
|
+
for (let i = 0; i < skills.length; i++) {
|
|
30639
|
+
const s = skills[i];
|
|
30640
|
+
const confidence = typeof s.confidence === "number" ? s.confidence.toFixed(2) : "0.00";
|
|
30641
|
+
const executions = s.executions || 0;
|
|
30642
|
+
const verified = s.verification_level ? `L${s.verification_level}` : "L0";
|
|
30643
|
+
console.log(
|
|
30644
|
+
` ${chalk59.dim(`${i + 1}.`)} ${chalk59.bold(s.name)}`
|
|
30645
|
+
);
|
|
30646
|
+
console.log(
|
|
30647
|
+
` ${chalk59.dim("Confidence:")} ${chalk59.white(confidence)} ${chalk59.dim("|")} ${chalk59.dim("Verified:")} ${chalk59.white(verified)} ${chalk59.dim("|")} ${chalk59.dim("Executions:")} ${chalk59.white(String(executions))}`
|
|
30648
|
+
);
|
|
30649
|
+
if (s.description) {
|
|
30650
|
+
console.log(` ${chalk59.gray(s.description)}`);
|
|
30651
|
+
}
|
|
30652
|
+
console.log();
|
|
30653
|
+
}
|
|
30654
|
+
console.log(chalk59.dim(" Install a skill: hubify install <skill-name>"));
|
|
30655
|
+
console.log(chalk59.dim(" More details: hubify info <skill-name>\n"));
|
|
30656
|
+
} catch (error) {
|
|
30657
|
+
spinner.fail("Search failed");
|
|
30658
|
+
console.error(chalk59.red(` ${error.message}
|
|
30659
|
+
`));
|
|
30660
|
+
}
|
|
30661
|
+
}
|
|
30662
|
+
async function runCreateSkill(description, options, spinner) {
|
|
30663
|
+
if (!description) {
|
|
30664
|
+
console.log(chalk59.red("\n Usage: hubify meta run create-skill <description>"));
|
|
30665
|
+
console.log(chalk59.gray(' Example: hubify meta run create-skill "skill for parsing CSV files"\n'));
|
|
30666
|
+
process.exit(1);
|
|
30667
|
+
}
|
|
30668
|
+
console.log(chalk59.bold("\n Create Skill\n"));
|
|
30669
|
+
console.log(chalk59.gray(" Delegating to the skill generator...\n"));
|
|
30670
|
+
console.log(chalk59.white(" Run this command directly:"));
|
|
30671
|
+
console.log(chalk59.cyan(` hubify generate "${description}"
|
|
30672
|
+
`));
|
|
30673
|
+
console.log(chalk59.gray(" Options:"));
|
|
30674
|
+
console.log(chalk59.gray(" --category <cat> Skill category"));
|
|
30675
|
+
console.log(chalk59.gray(" --name <name> Override auto-generated name"));
|
|
30676
|
+
console.log(chalk59.gray(" --publish Auto-publish after generation"));
|
|
30677
|
+
console.log(chalk59.gray(" --template Use template-based generation (no AI)"));
|
|
30678
|
+
console.log(chalk59.gray(" --dry-run Preview without saving"));
|
|
30679
|
+
try {
|
|
30680
|
+
spinner.start("Queuing generation in registry...");
|
|
30681
|
+
const client = getClient();
|
|
30682
|
+
const result = await client.mutation(
|
|
30683
|
+
toFunctionName(api.skillGeneration.queueGeneration),
|
|
30684
|
+
{
|
|
30685
|
+
prompt: description,
|
|
30686
|
+
requested_by: process.env.USER || "cli",
|
|
30687
|
+
category: "coding"
|
|
30688
|
+
}
|
|
30689
|
+
);
|
|
30690
|
+
spinner.succeed("Generation queued in registry");
|
|
30691
|
+
const r = result;
|
|
30692
|
+
if (r && r._id) {
|
|
30693
|
+
console.log(chalk59.gray(`
|
|
30694
|
+
Generation ID: ${r._id}`));
|
|
30695
|
+
console.log(chalk59.gray(" Check status: hubify skill-gen list\n"));
|
|
30696
|
+
}
|
|
30697
|
+
} catch {
|
|
30698
|
+
spinner.info("Remote generation not available. Use hubify generate locally.");
|
|
30699
|
+
console.log();
|
|
30700
|
+
}
|
|
30701
|
+
}
|
|
30702
|
+
async function runUpdateSkills(skillName, options, spinner) {
|
|
30703
|
+
const projectDir = getProjectSkillsDir();
|
|
30704
|
+
if (!fs26.existsSync(projectDir)) {
|
|
30705
|
+
console.log(chalk59.red("\n No .hubify/skills directory found."));
|
|
30706
|
+
console.log(chalk59.gray(" Run: hubify init\n"));
|
|
30707
|
+
process.exit(1);
|
|
30708
|
+
}
|
|
30709
|
+
spinner.start("Scanning installed skills...");
|
|
30710
|
+
const entries = fs26.readdirSync(projectDir, { withFileTypes: true });
|
|
30711
|
+
const installed = [];
|
|
30712
|
+
for (const entry of entries) {
|
|
30713
|
+
if (!entry.isDirectory()) continue;
|
|
30714
|
+
const skillMdPath = path26.join(projectDir, entry.name, "SKILL.md");
|
|
30715
|
+
if (!fs26.existsSync(skillMdPath)) continue;
|
|
30716
|
+
const content = fs26.readFileSync(skillMdPath, "utf-8");
|
|
30717
|
+
const fm = parseFrontmatter3(content);
|
|
30718
|
+
const name = fm.name || entry.name;
|
|
30719
|
+
const version = typeof fm.version === "string" ? fm.version : String(fm.version || "0.0.0");
|
|
30720
|
+
if (skillName && name !== skillName && entry.name !== skillName) continue;
|
|
30721
|
+
installed.push({ name, localVersion: version, dir: entry.name });
|
|
30722
|
+
}
|
|
30723
|
+
if (installed.length === 0) {
|
|
30724
|
+
spinner.info(skillName ? `Skill "${skillName}" not found locally.` : "No installed skills found.");
|
|
30725
|
+
console.log();
|
|
30726
|
+
return;
|
|
30727
|
+
}
|
|
30728
|
+
spinner.text = `Checking updates for ${installed.length} skill(s)...`;
|
|
30729
|
+
const client = getClient();
|
|
30730
|
+
const updates = [];
|
|
30731
|
+
const upToDate = [];
|
|
30732
|
+
const notInRegistry = [];
|
|
30733
|
+
for (const skill of installed) {
|
|
30734
|
+
try {
|
|
30735
|
+
const lookupName = skill.name.replace(/^hubify\//, "");
|
|
30736
|
+
const remote = await client.query(toFunctionName(api.skills.getByName), {
|
|
30737
|
+
name: lookupName
|
|
30738
|
+
});
|
|
30739
|
+
if (!remote) {
|
|
30740
|
+
notInRegistry.push(skill.name);
|
|
30741
|
+
continue;
|
|
30742
|
+
}
|
|
30743
|
+
const r = remote;
|
|
30744
|
+
const remoteVersion = r.version || "0.0.0";
|
|
30745
|
+
const confidence = r.confidence || 0;
|
|
30746
|
+
if (remoteVersion !== skill.localVersion) {
|
|
30747
|
+
updates.push({
|
|
30748
|
+
name: skill.name,
|
|
30749
|
+
local: skill.localVersion,
|
|
30750
|
+
remote: remoteVersion,
|
|
30751
|
+
confidence
|
|
30752
|
+
});
|
|
30753
|
+
} else {
|
|
30754
|
+
upToDate.push(skill.name);
|
|
30755
|
+
}
|
|
30756
|
+
} catch {
|
|
30757
|
+
notInRegistry.push(skill.name);
|
|
30758
|
+
}
|
|
30759
|
+
}
|
|
30760
|
+
spinner.stop();
|
|
30761
|
+
if (options.json) {
|
|
30762
|
+
console.log(JSON.stringify({ updates, upToDate, notInRegistry }, null, 2));
|
|
30763
|
+
return;
|
|
30764
|
+
}
|
|
30765
|
+
console.log(chalk59.bold(`
|
|
30766
|
+
Skill Update Check (${installed.length} installed)
|
|
30767
|
+
`));
|
|
30768
|
+
if (updates.length > 0) {
|
|
30769
|
+
console.log(chalk59.yellow(` Updates available (${updates.length}):
|
|
30770
|
+
`));
|
|
30771
|
+
for (const u of updates) {
|
|
30772
|
+
console.log(` ${chalk59.bold(u.name)}`);
|
|
30773
|
+
console.log(
|
|
30774
|
+
` ${chalk59.dim("Local:")} ${u.local} ${chalk59.dim("->")} ${chalk59.green(u.remote)} ${chalk59.dim("Confidence:")} ${u.confidence.toFixed(2)}`
|
|
30775
|
+
);
|
|
30776
|
+
}
|
|
30777
|
+
console.log();
|
|
30778
|
+
console.log(chalk59.dim(" Update a skill: hubify install <skill-name>"));
|
|
30779
|
+
} else {
|
|
30780
|
+
console.log(chalk59.green(" All skills are up to date."));
|
|
30781
|
+
}
|
|
30782
|
+
if (upToDate.length > 0) {
|
|
30783
|
+
console.log(chalk59.dim(`
|
|
30784
|
+
Up to date (${upToDate.length}): ${upToDate.join(", ")}`));
|
|
30785
|
+
}
|
|
30786
|
+
if (notInRegistry.length > 0) {
|
|
30787
|
+
console.log(chalk59.dim(`
|
|
30788
|
+
Not in registry (${notInRegistry.length}): ${notInRegistry.join(", ")}`));
|
|
30789
|
+
}
|
|
30790
|
+
console.log();
|
|
30791
|
+
}
|
|
30792
|
+
async function runChainSkills(skillList, options, spinner) {
|
|
30793
|
+
if (!skillList) {
|
|
30794
|
+
console.log(chalk59.red("\n Usage: hubify meta run chain-skills <skill1,skill2,skill3>"));
|
|
30795
|
+
console.log(chalk59.gray(" Example: hubify meta run chain-skills web-research,data-analysis,email-draft\n"));
|
|
30796
|
+
process.exit(1);
|
|
30797
|
+
}
|
|
30798
|
+
const skillNames = skillList.split(",").map((s) => s.trim()).filter(Boolean);
|
|
30799
|
+
if (skillNames.length < 2) {
|
|
30800
|
+
console.log(chalk59.red("\n A chain requires at least 2 skills."));
|
|
30801
|
+
console.log(chalk59.gray(" Separate skill names with commas.\n"));
|
|
30802
|
+
process.exit(1);
|
|
30803
|
+
}
|
|
30804
|
+
spinner.start(`Validating ${skillNames.length} skills...`);
|
|
30805
|
+
const client = getClient();
|
|
30806
|
+
const validSkills = [];
|
|
30807
|
+
const missing = [];
|
|
30808
|
+
for (const name of skillNames) {
|
|
30809
|
+
try {
|
|
30810
|
+
const result = await client.query(toFunctionName(api.skills.getByName), { name });
|
|
30811
|
+
if (result) {
|
|
30812
|
+
const r = result;
|
|
30813
|
+
validSkills.push({
|
|
30814
|
+
name: r.name,
|
|
30815
|
+
description: r.description || "",
|
|
30816
|
+
confidence: r.confidence || 0
|
|
30817
|
+
});
|
|
30818
|
+
} else {
|
|
30819
|
+
missing.push(name);
|
|
30820
|
+
}
|
|
30821
|
+
} catch {
|
|
30822
|
+
missing.push(name);
|
|
30823
|
+
}
|
|
30824
|
+
}
|
|
30825
|
+
spinner.stop();
|
|
30826
|
+
if (missing.length > 0) {
|
|
30827
|
+
console.log(chalk59.yellow(`
|
|
30828
|
+
Warning: ${missing.length} skill(s) not found in registry:`));
|
|
30829
|
+
for (const m of missing) {
|
|
30830
|
+
console.log(chalk59.dim(` - ${m}`));
|
|
30831
|
+
}
|
|
30832
|
+
console.log(chalk59.dim(" These steps will still be included in the chain definition.\n"));
|
|
30833
|
+
}
|
|
30834
|
+
const chainName = skillNames.slice(0, 3).join("-then-");
|
|
30835
|
+
const steps = skillNames.map((name, i) => ({
|
|
30836
|
+
id: `step_${i + 1}`,
|
|
30837
|
+
skill_name: name,
|
|
30838
|
+
depends_on: i === 0 ? [] : [`step_${i}`],
|
|
30839
|
+
on_fail: i === skillNames.length - 1 ? "abort" : "retry"
|
|
30840
|
+
}));
|
|
30841
|
+
const chainDef = {
|
|
30842
|
+
name: chainName,
|
|
30843
|
+
description: `Chain: ${skillNames.join(" -> ")}`,
|
|
30844
|
+
steps
|
|
30845
|
+
};
|
|
30846
|
+
if (options.json) {
|
|
30847
|
+
console.log(JSON.stringify(chainDef, null, 2));
|
|
30848
|
+
return;
|
|
30849
|
+
}
|
|
30850
|
+
console.log(chalk59.bold(`
|
|
30851
|
+
Skill Chain: ${chainName}
|
|
30852
|
+
`));
|
|
30853
|
+
console.log(chalk59.gray(` Flow: ${skillNames.join(chalk59.dim(" -> "))}
|
|
30854
|
+
`));
|
|
30855
|
+
for (let i = 0; i < steps.length; i++) {
|
|
30856
|
+
const step = steps[i];
|
|
30857
|
+
const valid = validSkills.find((s) => s.name === step.skill_name);
|
|
30858
|
+
const statusIcon = valid ? chalk59.green("[OK]") : chalk59.yellow("[??]");
|
|
30859
|
+
const conf = valid ? chalk59.dim(`(${valid.confidence.toFixed(2)})`) : "";
|
|
30860
|
+
console.log(
|
|
30861
|
+
` ${chalk59.dim(`${i + 1}.`)} ${statusIcon} ${chalk59.bold(step.skill_name)} ${conf}`
|
|
30862
|
+
);
|
|
30863
|
+
if (valid?.description) {
|
|
30864
|
+
console.log(` ${chalk59.gray(valid.description)}`);
|
|
30865
|
+
}
|
|
30866
|
+
console.log(
|
|
30867
|
+
` ${chalk59.dim("on_fail:")} ${step.on_fail} ${chalk59.dim("| depends:")} ${step.depends_on.length === 0 ? "none" : step.depends_on.join(", ")}`
|
|
30868
|
+
);
|
|
30869
|
+
}
|
|
30870
|
+
console.log(chalk59.gray("\n To create this workflow:"));
|
|
30871
|
+
console.log(chalk59.cyan(` hubify workflow create --name "${chainName}" --steps '${JSON.stringify(steps)}'`));
|
|
30872
|
+
console.log(chalk59.gray("\n To run an existing workflow:"));
|
|
30873
|
+
console.log(chalk59.cyan(` hubify workflow run ${chainName}
|
|
30874
|
+
`));
|
|
30875
|
+
}
|
|
30876
|
+
var metaCommand = new Command56("meta").description("Meta-skills \u2014 system skills for interacting with the Hubify platform").addCommand(metaListCommand()).addCommand(metaInstallCommand()).addCommand(metaRunCommand());
|
|
30877
|
+
|
|
30878
|
+
// src/commands/self-update.ts
|
|
30879
|
+
import { Command as Command57 } from "commander";
|
|
30880
|
+
import chalk60 from "chalk";
|
|
30881
|
+
import ora56 from "ora";
|
|
30882
|
+
import { execSync as execSync2 } from "child_process";
|
|
30883
|
+
import fs27 from "fs";
|
|
30884
|
+
function getCliVersion() {
|
|
30885
|
+
try {
|
|
30886
|
+
const pkgUrl = new URL("../../package.json", import.meta.url);
|
|
30887
|
+
const pkg = JSON.parse(fs27.readFileSync(pkgUrl, "utf8"));
|
|
30888
|
+
return pkg.version || "0.0.0";
|
|
30889
|
+
} catch {
|
|
30890
|
+
return "0.0.0";
|
|
30891
|
+
}
|
|
30892
|
+
}
|
|
30893
|
+
function getLatestNpmVersion(packageName) {
|
|
30894
|
+
try {
|
|
30895
|
+
const output = execSync2(`npm view ${packageName} version`, {
|
|
30896
|
+
encoding: "utf-8",
|
|
30897
|
+
timeout: 15e3,
|
|
30898
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
30899
|
+
});
|
|
30900
|
+
return output.trim();
|
|
30901
|
+
} catch {
|
|
30902
|
+
return null;
|
|
30903
|
+
}
|
|
30904
|
+
}
|
|
30905
|
+
function isNewerVersion(current, latest) {
|
|
30906
|
+
const c = current.split(".").map(Number);
|
|
30907
|
+
const l = latest.split(".").map(Number);
|
|
30908
|
+
for (let i = 0; i < 3; i++) {
|
|
30909
|
+
if ((l[i] ?? 0) > (c[i] ?? 0)) return true;
|
|
30910
|
+
if ((l[i] ?? 0) < (c[i] ?? 0)) return false;
|
|
30911
|
+
}
|
|
30912
|
+
return false;
|
|
30913
|
+
}
|
|
30914
|
+
var selfUpdateCommand = new Command57("self-update").description("Check for and install CLI updates").option("--check", "Only check for updates, do not install").option("--force", "Force reinstall even if already up to date").action(async (options) => {
|
|
30915
|
+
const currentVersion = getCliVersion();
|
|
30916
|
+
console.log();
|
|
30917
|
+
console.log(
|
|
30918
|
+
chalk60.cyan.bold(" Hubify Self-Update")
|
|
30919
|
+
);
|
|
30920
|
+
console.log(
|
|
30921
|
+
chalk60.gray(` Current version: ${currentVersion}`)
|
|
30922
|
+
);
|
|
30923
|
+
console.log();
|
|
30924
|
+
const spinner = ora56("Checking for updates...").start();
|
|
30925
|
+
const latestVersion = getLatestNpmVersion("hubify");
|
|
30926
|
+
if (!latestVersion) {
|
|
30927
|
+
spinner.fail("Could not reach npm registry.");
|
|
30928
|
+
console.log(
|
|
30929
|
+
chalk60.gray(
|
|
30930
|
+
"\n Check your internet connection and try again.\n"
|
|
30931
|
+
)
|
|
30932
|
+
);
|
|
30933
|
+
return;
|
|
30934
|
+
}
|
|
30935
|
+
spinner.stop();
|
|
30936
|
+
const hasUpdate = isNewerVersion(currentVersion, latestVersion);
|
|
30937
|
+
if (!hasUpdate && !options.force) {
|
|
30938
|
+
console.log(
|
|
30939
|
+
chalk60.green(` Already up to date (v${currentVersion}).`)
|
|
30940
|
+
);
|
|
30941
|
+
console.log();
|
|
30942
|
+
return;
|
|
30943
|
+
}
|
|
30944
|
+
if (hasUpdate) {
|
|
30945
|
+
console.log(
|
|
30946
|
+
chalk60.white(
|
|
30947
|
+
` Update available: ${chalk60.gray(currentVersion)} -> ${chalk60.green(latestVersion)}`
|
|
30948
|
+
)
|
|
30949
|
+
);
|
|
30950
|
+
console.log();
|
|
30951
|
+
} else if (options.force) {
|
|
30952
|
+
console.log(
|
|
30953
|
+
chalk60.yellow(` Forcing reinstall of v${latestVersion}...`)
|
|
30954
|
+
);
|
|
30955
|
+
console.log();
|
|
30956
|
+
}
|
|
30957
|
+
if (options.check) {
|
|
30958
|
+
if (hasUpdate) {
|
|
30959
|
+
console.log(
|
|
30960
|
+
chalk60.gray(
|
|
30961
|
+
" Run " + chalk60.cyan("hubify self-update") + " to install the update."
|
|
30962
|
+
)
|
|
30963
|
+
);
|
|
30964
|
+
}
|
|
30965
|
+
console.log();
|
|
30966
|
+
return;
|
|
30967
|
+
}
|
|
30968
|
+
const updateSpinner = ora56(
|
|
30969
|
+
`Installing hubify@${latestVersion}...`
|
|
30970
|
+
).start();
|
|
30971
|
+
try {
|
|
30972
|
+
execSync2(`npm install -g hubify@${latestVersion}`, {
|
|
30973
|
+
encoding: "utf-8",
|
|
30974
|
+
timeout: 12e4,
|
|
30975
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
30976
|
+
});
|
|
30977
|
+
updateSpinner.succeed(
|
|
30978
|
+
`Updated to hubify@${latestVersion}`
|
|
30979
|
+
);
|
|
30980
|
+
console.log();
|
|
30981
|
+
console.log(
|
|
30982
|
+
chalk60.green(" Update complete!") + chalk60.gray(
|
|
30983
|
+
" Run " + chalk60.cyan("hubify --version") + " to confirm."
|
|
30984
|
+
)
|
|
30985
|
+
);
|
|
30986
|
+
console.log();
|
|
30987
|
+
} catch (err) {
|
|
30988
|
+
updateSpinner.fail("Update failed.");
|
|
30989
|
+
console.log();
|
|
30990
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
30991
|
+
if (errMsg.includes("EACCES") || errMsg.includes("permission")) {
|
|
30992
|
+
console.log(
|
|
30993
|
+
chalk60.yellow(
|
|
30994
|
+
" Permission denied. Try running with sudo:"
|
|
30995
|
+
)
|
|
30996
|
+
);
|
|
30997
|
+
console.log(
|
|
30998
|
+
chalk60.cyan(
|
|
30999
|
+
` sudo npm install -g hubify@${latestVersion}`
|
|
31000
|
+
)
|
|
31001
|
+
);
|
|
31002
|
+
} else {
|
|
31003
|
+
console.log(chalk60.red(` ${errMsg}`));
|
|
31004
|
+
}
|
|
31005
|
+
console.log();
|
|
31006
|
+
}
|
|
31007
|
+
});
|
|
31008
|
+
|
|
31009
|
+
// src/index.ts
|
|
31010
|
+
function getCliVersion2() {
|
|
31011
|
+
try {
|
|
31012
|
+
const pkgUrl = new URL("../package.json", import.meta.url);
|
|
31013
|
+
const pkg = JSON.parse(fs28.readFileSync(pkgUrl, "utf8"));
|
|
31014
|
+
return pkg.version || "0.0.0";
|
|
31015
|
+
} catch {
|
|
31016
|
+
return "0.0.0";
|
|
31017
|
+
}
|
|
31018
|
+
}
|
|
31019
|
+
var VERSION = getCliVersion2();
|
|
31020
|
+
var BANNER = `
|
|
31021
|
+
${chalk61.cyan(" __ __ __ _ ____ ")}
|
|
31022
|
+
${chalk61.cyan(" / / / /_ __/ /_ (_) __/_ __")}
|
|
31023
|
+
${chalk61.cyan(" / /_/ / / / / __ \\/ / /_/ / / /")}
|
|
31024
|
+
${chalk61.cyan(" / __ / /_/ / /_/ / / __/ /_/ / ")}
|
|
31025
|
+
${chalk61.cyan("/_/ /_/\\__,_/_.___/_/_/ \\__, / ")}
|
|
31026
|
+
${chalk61.cyan(" /____/ ")}
|
|
28766
31027
|
`;
|
|
28767
|
-
var TAGLINE =
|
|
31028
|
+
var TAGLINE = chalk61.gray(" The living intelligence layer for Agent Skills");
|
|
28768
31029
|
function showWelcome() {
|
|
28769
31030
|
console.log(BANNER);
|
|
28770
31031
|
console.log(TAGLINE);
|
|
28771
31032
|
console.log();
|
|
28772
|
-
console.log(
|
|
31033
|
+
console.log(chalk61.white.bold(" Welcome to Hubify!"));
|
|
28773
31034
|
console.log();
|
|
28774
|
-
console.log(
|
|
28775
|
-
console.log(
|
|
31035
|
+
console.log(chalk61.gray(" Hubify is the universal skill registry for AI coding agents."));
|
|
31036
|
+
console.log(chalk61.gray(" Discover, install, and share agent skills with trust metrics."));
|
|
28776
31037
|
console.log();
|
|
28777
|
-
console.log(
|
|
28778
|
-
console.log(
|
|
31038
|
+
console.log(chalk61.cyan.bold(" Quick Start"));
|
|
31039
|
+
console.log(chalk61.gray(" -----------"));
|
|
28779
31040
|
console.log();
|
|
28780
|
-
console.log(
|
|
28781
|
-
console.log(
|
|
31041
|
+
console.log(chalk61.white(" 1. Initialize Hubify in your project:"));
|
|
31042
|
+
console.log(chalk61.cyan(" $ hubify init"));
|
|
28782
31043
|
console.log();
|
|
28783
|
-
console.log(
|
|
28784
|
-
console.log(
|
|
31044
|
+
console.log(chalk61.white(" 2. Search for skills:"));
|
|
31045
|
+
console.log(chalk61.cyan(' $ hubify search "react best practices"'));
|
|
28785
31046
|
console.log();
|
|
28786
|
-
console.log(
|
|
28787
|
-
console.log(
|
|
31047
|
+
console.log(chalk61.white(" 3. Install a skill:"));
|
|
31048
|
+
console.log(chalk61.cyan(" $ hubify install vercel-react-best-practices"));
|
|
28788
31049
|
console.log();
|
|
28789
|
-
console.log(
|
|
28790
|
-
console.log(
|
|
31050
|
+
console.log(chalk61.white(" 4. Report your execution results:"));
|
|
31051
|
+
console.log(chalk61.cyan(" $ hubify report vercel-react-best-practices --success"));
|
|
28791
31052
|
console.log();
|
|
28792
|
-
console.log(
|
|
28793
|
-
console.log(
|
|
31053
|
+
console.log(chalk61.cyan.bold(" Common Commands"));
|
|
31054
|
+
console.log(chalk61.gray(" ---------------"));
|
|
28794
31055
|
console.log();
|
|
28795
|
-
console.log(
|
|
28796
|
-
console.log(
|
|
28797
|
-
console.log(
|
|
28798
|
-
console.log(
|
|
28799
|
-
console.log(
|
|
28800
|
-
console.log(
|
|
31056
|
+
console.log(chalk61.white(" hubify search <query> ") + chalk61.gray("Search for skills"));
|
|
31057
|
+
console.log(chalk61.white(" hubify install <skill> ") + chalk61.gray("Install a skill locally"));
|
|
31058
|
+
console.log(chalk61.white(" hubify info <skill> ") + chalk61.gray("View skill details"));
|
|
31059
|
+
console.log(chalk61.white(" hubify list ") + chalk61.gray("List installed skills"));
|
|
31060
|
+
console.log(chalk61.white(" hubify publish <path> ") + chalk61.gray("Publish your skill"));
|
|
31061
|
+
console.log(chalk61.white(" hubify stats ") + chalk61.gray("View registry statistics"));
|
|
28801
31062
|
console.log();
|
|
28802
|
-
console.log(
|
|
28803
|
-
console.log(
|
|
31063
|
+
console.log(chalk61.gray(" Run ") + chalk61.cyan("hubify --help") + chalk61.gray(" for all commands."));
|
|
31064
|
+
console.log(chalk61.gray(" Run ") + chalk61.cyan("hubify <command> --help") + chalk61.gray(" for command details."));
|
|
28804
31065
|
console.log();
|
|
28805
|
-
console.log(
|
|
31066
|
+
console.log(chalk61.gray(" ") + chalk61.dim(`v${VERSION}`) + chalk61.gray(" | ") + chalk61.dim(REGISTRY_URL));
|
|
28806
31067
|
console.log();
|
|
28807
31068
|
}
|
|
28808
31069
|
function formatGroupedHelp() {
|
|
@@ -28810,10 +31071,10 @@ function formatGroupedHelp() {
|
|
|
28810
31071
|
sections.push(BANNER);
|
|
28811
31072
|
sections.push(TAGLINE);
|
|
28812
31073
|
sections.push("");
|
|
28813
|
-
sections.push(
|
|
31074
|
+
sections.push(chalk61.white.bold("Usage:") + chalk61.gray(" hubify [command] [options]"));
|
|
28814
31075
|
sections.push("");
|
|
28815
|
-
sections.push(
|
|
28816
|
-
sections.push(
|
|
31076
|
+
sections.push(chalk61.cyan.bold("Souls"));
|
|
31077
|
+
sections.push(chalk61.gray(" AI personality and behavior templates"));
|
|
28817
31078
|
sections.push("");
|
|
28818
31079
|
sections.push(formatCommand("soul list", "List all available souls"));
|
|
28819
31080
|
sections.push(formatCommand("soul info <name>", "View detailed information about a soul"));
|
|
@@ -28822,8 +31083,8 @@ function formatGroupedHelp() {
|
|
|
28822
31083
|
sections.push(formatCommand("soul publish <path>", "Publish a soul to the registry"));
|
|
28823
31084
|
sections.push(formatCommand("soul search <query>", "Search for souls by description"));
|
|
28824
31085
|
sections.push("");
|
|
28825
|
-
sections.push(
|
|
28826
|
-
sections.push(
|
|
31086
|
+
sections.push(chalk61.cyan.bold("Tools"));
|
|
31087
|
+
sections.push(chalk61.gray(" Standardized tool definitions and integrations"));
|
|
28827
31088
|
sections.push("");
|
|
28828
31089
|
sections.push(formatCommand("tool list", "List all tools in the registry"));
|
|
28829
31090
|
sections.push(formatCommand("tool info <name>", "View detailed information about a tool"));
|
|
@@ -28831,8 +31092,8 @@ function formatGroupedHelp() {
|
|
|
28831
31092
|
sections.push(formatCommand("tool search <query>", "Search for tools by description"));
|
|
28832
31093
|
sections.push(formatCommand("tool stats", "Show tool registry statistics"));
|
|
28833
31094
|
sections.push("");
|
|
28834
|
-
sections.push(
|
|
28835
|
-
sections.push(
|
|
31095
|
+
sections.push(chalk61.cyan.bold("Skills"));
|
|
31096
|
+
sections.push(chalk61.gray(" Discovery and management of agent skills"));
|
|
28836
31097
|
sections.push("");
|
|
28837
31098
|
sections.push(formatCommand("search <query>", "Search for skills by name, description, or tags"));
|
|
28838
31099
|
sections.push(formatCommand("info <skill>", "View detailed information about a skill"));
|
|
@@ -28841,8 +31102,8 @@ function formatGroupedHelp() {
|
|
|
28841
31102
|
sections.push(formatCommand("list", "List installed skills in project or globally"));
|
|
28842
31103
|
sections.push(formatCommand("update [skill]", "Update installed skills to latest versions"));
|
|
28843
31104
|
sections.push("");
|
|
28844
|
-
sections.push(
|
|
28845
|
-
sections.push(
|
|
31105
|
+
sections.push(chalk61.cyan.bold("Publishing"));
|
|
31106
|
+
sections.push(chalk61.gray(" Create and share your own skills"));
|
|
28846
31107
|
sections.push("");
|
|
28847
31108
|
sections.push(formatCommand("init", "Initialize Hubify in your project"));
|
|
28848
31109
|
sections.push(formatCommand("publish <path>", "Publish a skill to the registry"));
|
|
@@ -28850,31 +31111,31 @@ function formatGroupedHelp() {
|
|
|
28850
31111
|
sections.push(formatCommand("evolve <skill>", "Evolve a skill based on feedback"));
|
|
28851
31112
|
sections.push(formatCommand("import <url>", "Import a skill from external source"));
|
|
28852
31113
|
sections.push("");
|
|
28853
|
-
sections.push(
|
|
28854
|
-
sections.push(
|
|
31114
|
+
sections.push(chalk61.cyan.bold(".hub Manifests"));
|
|
31115
|
+
sections.push(chalk61.gray(" Manage skill/agent manifest files"));
|
|
28855
31116
|
sections.push("");
|
|
28856
31117
|
sections.push(formatCommand("hub init [dir]", "Create a .hub manifest file"));
|
|
28857
31118
|
sections.push(formatCommand("hub validate <file>", "Validate a .hub file against schema"));
|
|
28858
31119
|
sections.push(formatCommand("hub update <file>", "Update hashes and timestamps"));
|
|
28859
31120
|
sections.push(formatCommand("hub info <file>", "Display .hub file information"));
|
|
28860
31121
|
sections.push("");
|
|
28861
|
-
sections.push(
|
|
28862
|
-
sections.push(
|
|
31122
|
+
sections.push(chalk61.cyan.bold("Analytics"));
|
|
31123
|
+
sections.push(chalk61.gray(" Track and report skill executions"));
|
|
28863
31124
|
sections.push("");
|
|
28864
31125
|
sections.push(formatCommand("report <skill>", "Report skill execution results"));
|
|
28865
31126
|
sections.push(formatCommand("report stats [skill]", "View execution statistics"));
|
|
28866
31127
|
sections.push(formatCommand("report leaderboard", "Show top skills by executions"));
|
|
28867
31128
|
sections.push(formatCommand("stats", "View registry-wide statistics"));
|
|
28868
31129
|
sections.push("");
|
|
28869
|
-
sections.push(
|
|
28870
|
-
sections.push(
|
|
31130
|
+
sections.push(chalk61.cyan.bold("Authentication"));
|
|
31131
|
+
sections.push(chalk61.gray(" Manage CLI authentication and API tokens"));
|
|
28871
31132
|
sections.push("");
|
|
28872
31133
|
sections.push(formatCommand("auth login --token <t>", "Authenticate with an API token"));
|
|
28873
31134
|
sections.push(formatCommand("auth whoami", "Show current identity and token status"));
|
|
28874
31135
|
sections.push(formatCommand("auth logout", "Remove stored credentials"));
|
|
28875
31136
|
sections.push("");
|
|
28876
|
-
sections.push(
|
|
28877
|
-
sections.push(
|
|
31137
|
+
sections.push(chalk61.cyan.bold("Learning & Evolution"));
|
|
31138
|
+
sections.push(chalk61.gray(" Skill intelligence, evolution tracking, and canary management"));
|
|
28878
31139
|
sections.push("");
|
|
28879
31140
|
sections.push(formatCommand("learn stats <skill>", "Get learning statistics for a skill"));
|
|
28880
31141
|
sections.push(formatCommand("learn lineage <skill>", "Show full evolution tree"));
|
|
@@ -28883,8 +31144,8 @@ function formatGroupedHelp() {
|
|
|
28883
31144
|
sections.push(formatCommand("learn triggers <skill>", "Detect evolution triggers"));
|
|
28884
31145
|
sections.push(formatCommand("learn status <skill>", "Evolution status overview"));
|
|
28885
31146
|
sections.push("");
|
|
28886
|
-
sections.push(
|
|
28887
|
-
sections.push(
|
|
31147
|
+
sections.push(chalk61.cyan.bold("Labs"));
|
|
31148
|
+
sections.push(chalk61.gray(" Experiment swarms, DAGs, and frontier discovery"));
|
|
28888
31149
|
sections.push("");
|
|
28889
31150
|
sections.push(formatCommand("labs status", "Show active experiment missions"));
|
|
28890
31151
|
sections.push(formatCommand("labs launch <skill>", "Launch an experiment swarm"));
|
|
@@ -28893,16 +31154,16 @@ function formatGroupedHelp() {
|
|
|
28893
31154
|
sections.push(formatCommand("labs best-path <id>", "Show best path through DAG"));
|
|
28894
31155
|
sections.push(formatCommand("labs suggest <id>", "AI-suggested next experiments"));
|
|
28895
31156
|
sections.push("");
|
|
28896
|
-
sections.push(
|
|
28897
|
-
sections.push(
|
|
31157
|
+
sections.push(chalk61.cyan.bold("Research"));
|
|
31158
|
+
sections.push(chalk61.gray(" Multi-agent research missions"));
|
|
28898
31159
|
sections.push("");
|
|
28899
31160
|
sections.push(formatCommand("research list", "Browse research missions"));
|
|
28900
31161
|
sections.push(formatCommand("research propose <hub>", "Propose a new research mission"));
|
|
28901
31162
|
sections.push(formatCommand("research view <id>", "View mission details"));
|
|
28902
31163
|
sections.push(formatCommand("research publish <id>", "Publish findings as knowledge"));
|
|
28903
31164
|
sections.push("");
|
|
28904
|
-
sections.push(
|
|
28905
|
-
sections.push(
|
|
31165
|
+
sections.push(chalk61.cyan.bold("Collective Intelligence"));
|
|
31166
|
+
sections.push(chalk61.gray(" Cross-workspace knowledge sharing and discovery"));
|
|
28906
31167
|
sections.push("");
|
|
28907
31168
|
sections.push(formatCommand("collective feed", "Global collective intelligence feed"));
|
|
28908
31169
|
sections.push(formatCommand("collective share <t> <c>", "Share an insight to the collective"));
|
|
@@ -28910,8 +31171,18 @@ function formatGroupedHelp() {
|
|
|
28910
31171
|
sections.push(formatCommand("collective search <q>", "Search collective knowledge"));
|
|
28911
31172
|
sections.push(formatCommand("collective sync", "Pull latest insights into workspace"));
|
|
28912
31173
|
sections.push("");
|
|
28913
|
-
sections.push(
|
|
28914
|
-
sections.push(
|
|
31174
|
+
sections.push(chalk61.cyan.bold("Vault"));
|
|
31175
|
+
sections.push(chalk61.gray(" Manage tool credentials and API keys for workspace agents"));
|
|
31176
|
+
sections.push("");
|
|
31177
|
+
sections.push(formatCommand("vault list <hub>", "List vault entries for a workspace"));
|
|
31178
|
+
sections.push(formatCommand("vault store <hub>", "Store an encrypted credential"));
|
|
31179
|
+
sections.push(formatCommand("vault get <hub>", "Retrieve a credential (agent-scoped)"));
|
|
31180
|
+
sections.push(formatCommand("vault revoke <hub>", "Delete a vault entry"));
|
|
31181
|
+
sections.push(formatCommand("vault grant <hub>", "Update agent access for an entry"));
|
|
31182
|
+
sections.push(formatCommand("vault audit <hub>", "View access log for a service"));
|
|
31183
|
+
sections.push("");
|
|
31184
|
+
sections.push(chalk61.cyan.bold("Network"));
|
|
31185
|
+
sections.push(chalk61.gray(" Singularity layer, workspace management, and knowledge"));
|
|
28915
31186
|
sections.push("");
|
|
28916
31187
|
sections.push(formatCommand("network feed", "Global intelligence feed"));
|
|
28917
31188
|
sections.push(formatCommand("network trending", "Trending skills across network"));
|
|
@@ -28920,8 +31191,38 @@ function formatGroupedHelp() {
|
|
|
28920
31191
|
sections.push(formatCommand("knowledge search <q>", "Search hub knowledge"));
|
|
28921
31192
|
sections.push(formatCommand("usage", "View usage breakdown"));
|
|
28922
31193
|
sections.push("");
|
|
28923
|
-
sections.push(
|
|
28924
|
-
sections.push(
|
|
31194
|
+
sections.push(chalk61.cyan.bold("Reports"));
|
|
31195
|
+
sections.push(chalk61.gray(" Weekly network intelligence reports"));
|
|
31196
|
+
sections.push("");
|
|
31197
|
+
sections.push(formatCommand("reports latest", "View the latest weekly report"));
|
|
31198
|
+
sections.push(formatCommand("reports list", "List available weekly reports"));
|
|
31199
|
+
sections.push(formatCommand("reports view --date <d>", "View report for a specific week"));
|
|
31200
|
+
sections.push("");
|
|
31201
|
+
sections.push(chalk61.cyan.bold("Missions"));
|
|
31202
|
+
sections.push(chalk61.gray(" Autonomous mission runner \u2014 Ralph Loop"));
|
|
31203
|
+
sections.push("");
|
|
31204
|
+
sections.push(formatCommand("mission list", "List active missions"));
|
|
31205
|
+
sections.push(formatCommand("mission create <hub>", "Create a new mission"));
|
|
31206
|
+
sections.push(formatCommand("mission run <id>", "Run autonomous loop until budget exhausted"));
|
|
31207
|
+
sections.push(formatCommand("mission status <id>", "Show mission progress and DAG stats"));
|
|
31208
|
+
sections.push(formatCommand("mission agents <id>", "Best-fit agents for a mission"));
|
|
31209
|
+
sections.push(formatCommand("mission stop <id>", "Complete a mission"));
|
|
31210
|
+
sections.push("");
|
|
31211
|
+
sections.push(chalk61.cyan.bold("Meta-Skills"));
|
|
31212
|
+
sections.push(chalk61.gray(" System skills for interacting with the Hubify platform"));
|
|
31213
|
+
sections.push("");
|
|
31214
|
+
sections.push(formatCommand("meta list", "List available meta-skills"));
|
|
31215
|
+
sections.push(formatCommand("meta install [name]", "Install meta-skills into project"));
|
|
31216
|
+
sections.push(formatCommand("meta run <name> [args]", "Execute a meta-skill"));
|
|
31217
|
+
sections.push("");
|
|
31218
|
+
sections.push(chalk61.cyan.bold("System"));
|
|
31219
|
+
sections.push(chalk61.gray(" Self-update and system maintenance"));
|
|
31220
|
+
sections.push("");
|
|
31221
|
+
sections.push(formatCommand("self-update", "Check for and install CLI updates"));
|
|
31222
|
+
sections.push(formatCommand("self-update --check", "Only check, don't install"));
|
|
31223
|
+
sections.push("");
|
|
31224
|
+
sections.push(chalk61.cyan.bold("Advanced"));
|
|
31225
|
+
sections.push(chalk61.gray(" Seeding and administration"));
|
|
28925
31226
|
sections.push("");
|
|
28926
31227
|
sections.push(formatCommand("ingest skills-sh", "Ingest skills from skills.sh canonical registry"));
|
|
28927
31228
|
sections.push(formatCommand("ingest status", "Check status of ingest operations"));
|
|
@@ -28936,48 +31237,48 @@ function formatGroupedHelp() {
|
|
|
28936
31237
|
sections.push(formatCommand("seed-souls", "Seed 30 built-in AI souls"));
|
|
28937
31238
|
sections.push(formatCommand("seed-tools", "Seed 40 core tools"));
|
|
28938
31239
|
sections.push("");
|
|
28939
|
-
sections.push(
|
|
31240
|
+
sections.push(chalk61.cyan.bold("Examples"));
|
|
28940
31241
|
sections.push("");
|
|
28941
|
-
sections.push(
|
|
28942
|
-
sections.push(
|
|
31242
|
+
sections.push(chalk61.gray(" # Search for TypeScript skills"));
|
|
31243
|
+
sections.push(chalk61.white(" $ hubify search typescript --category coding"));
|
|
28943
31244
|
sections.push("");
|
|
28944
|
-
sections.push(
|
|
28945
|
-
sections.push(
|
|
31245
|
+
sections.push(chalk61.gray(" # Install a skill with minimum confidence"));
|
|
31246
|
+
sections.push(chalk61.white(" $ hubify install react-hooks --min-confidence 0.8"));
|
|
28946
31247
|
sections.push("");
|
|
28947
|
-
sections.push(
|
|
28948
|
-
sections.push(
|
|
31248
|
+
sections.push(chalk61.gray(" # Report a successful execution"));
|
|
31249
|
+
sections.push(chalk61.white(" $ hubify report my-skill --success --duration 1500"));
|
|
28949
31250
|
sections.push("");
|
|
28950
|
-
sections.push(
|
|
28951
|
-
sections.push(
|
|
31251
|
+
sections.push(chalk61.gray(" # Publish a skill with dry-run"));
|
|
31252
|
+
sections.push(chalk61.white(" $ hubify publish ./my-skill --dry-run"));
|
|
28952
31253
|
sections.push("");
|
|
28953
|
-
sections.push(
|
|
31254
|
+
sections.push(chalk61.cyan.bold("Shell Completions"));
|
|
28954
31255
|
sections.push("");
|
|
28955
|
-
sections.push(
|
|
31256
|
+
sections.push(chalk61.gray(" Enable tab completion for hubify commands:"));
|
|
28956
31257
|
sections.push("");
|
|
28957
|
-
sections.push(
|
|
28958
|
-
sections.push(
|
|
31258
|
+
sections.push(chalk61.gray(" # Bash"));
|
|
31259
|
+
sections.push(chalk61.white(" $ hubify completion bash >> ~/.bashrc"));
|
|
28959
31260
|
sections.push("");
|
|
28960
|
-
sections.push(
|
|
28961
|
-
sections.push(
|
|
31261
|
+
sections.push(chalk61.gray(" # Zsh"));
|
|
31262
|
+
sections.push(chalk61.white(" $ hubify completion zsh >> ~/.zshrc"));
|
|
28962
31263
|
sections.push("");
|
|
28963
|
-
sections.push(
|
|
28964
|
-
sections.push(
|
|
31264
|
+
sections.push(chalk61.gray(" # Fish"));
|
|
31265
|
+
sections.push(chalk61.white(" $ hubify completion fish > ~/.config/fish/completions/hubify.fish"));
|
|
28965
31266
|
sections.push("");
|
|
28966
|
-
sections.push(
|
|
28967
|
-
sections.push(
|
|
28968
|
-
sections.push(
|
|
31267
|
+
sections.push(chalk61.gray("Version: ") + chalk61.white(VERSION));
|
|
31268
|
+
sections.push(chalk61.gray("Documentation: ") + chalk61.cyan("https://hubify.com/docs"));
|
|
31269
|
+
sections.push(chalk61.gray("Report issues: ") + chalk61.cyan("https://github.com/houstongolden/hubify/issues"));
|
|
28969
31270
|
sections.push("");
|
|
28970
31271
|
return sections.join("\n");
|
|
28971
31272
|
}
|
|
28972
31273
|
function formatCommand(name, desc) {
|
|
28973
31274
|
const paddedName = name.padEnd(24);
|
|
28974
|
-
return
|
|
31275
|
+
return chalk61.white(` ${paddedName}`) + chalk61.gray(desc);
|
|
28975
31276
|
}
|
|
28976
31277
|
function generateBashCompletion() {
|
|
28977
31278
|
return `# hubify bash completion
|
|
28978
31279
|
_hubify_completions() {
|
|
28979
31280
|
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
28980
|
-
local commands="soul tool search install execute run info list init publish report update generate evolve import integrate workflow squad stats auth learn labs research collective network memory workspace knowledge usage seed-all seed-anthropic seed-clawhub seed-skillssh seed-github seed-community seed-bootstrap seed-souls seed-tools completion"
|
|
31281
|
+
local commands="soul tool search install execute run info list init publish report update generate evolve import integrate workflow squad stats auth learn labs research collective network memory workspace knowledge usage vault reports mission meta hub hub-connect sync test agent collaborate scan audit detect ingest seed-all seed-anthropic seed-clawhub seed-skillssh seed-github seed-community seed-bootstrap seed-souls seed-tools completion"
|
|
28981
31282
|
|
|
28982
31283
|
if [[ \${COMP_CWORD} -eq 1 ]]; then
|
|
28983
31284
|
COMPREPLY=( $(compgen -W "\${commands}" -- "\${cur}") )
|
|
@@ -29008,6 +31309,28 @@ _hubify() {
|
|
|
29008
31309
|
'evolve:Evolve a skill'
|
|
29009
31310
|
'import:Import from external source'
|
|
29010
31311
|
'stats:View registry statistics'
|
|
31312
|
+
'auth:Manage authentication'
|
|
31313
|
+
'learn:Learning and evolution'
|
|
31314
|
+
'labs:Experiment swarms'
|
|
31315
|
+
'research:Research missions'
|
|
31316
|
+
'collective:Collective intelligence'
|
|
31317
|
+
'network:Singularity network'
|
|
31318
|
+
'memory:Agent memory'
|
|
31319
|
+
'workspace:Workspace management'
|
|
31320
|
+
'knowledge:Hub knowledge'
|
|
31321
|
+
'usage:Usage and billing'
|
|
31322
|
+
'vault:Credential vault'
|
|
31323
|
+
'reports:Weekly intelligence reports'
|
|
31324
|
+
'mission:Autonomous missions'
|
|
31325
|
+
'meta:Meta-skills for Hubify platform'
|
|
31326
|
+
'hub:Hub manifests'
|
|
31327
|
+
'hub-connect:Connect to hub'
|
|
31328
|
+
'sync:Sync operations'
|
|
31329
|
+
'agent:Agent management'
|
|
31330
|
+
'collaborate:Collaboration sessions'
|
|
31331
|
+
'scan:Security scanning'
|
|
31332
|
+
'audit:Skills audit'
|
|
31333
|
+
'ingest:Ingest from registries'
|
|
29011
31334
|
'seed-all:Seed from all sources'
|
|
29012
31335
|
'seed-anthropic:Seed from Anthropic'
|
|
29013
31336
|
'seed-clawhub:Seed from ClawHub'
|
|
@@ -29053,6 +31376,28 @@ complete -c hubify -n "__fish_use_subcommand" -a "generate" -d "Generate a new s
|
|
|
29053
31376
|
complete -c hubify -n "__fish_use_subcommand" -a "evolve" -d "Evolve a skill"
|
|
29054
31377
|
complete -c hubify -n "__fish_use_subcommand" -a "import" -d "Import from external source"
|
|
29055
31378
|
complete -c hubify -n "__fish_use_subcommand" -a "stats" -d "View registry statistics"
|
|
31379
|
+
complete -c hubify -n "__fish_use_subcommand" -a "auth" -d "Manage authentication"
|
|
31380
|
+
complete -c hubify -n "__fish_use_subcommand" -a "learn" -d "Learning and evolution"
|
|
31381
|
+
complete -c hubify -n "__fish_use_subcommand" -a "labs" -d "Experiment swarms"
|
|
31382
|
+
complete -c hubify -n "__fish_use_subcommand" -a "research" -d "Research missions"
|
|
31383
|
+
complete -c hubify -n "__fish_use_subcommand" -a "collective" -d "Collective intelligence"
|
|
31384
|
+
complete -c hubify -n "__fish_use_subcommand" -a "network" -d "Singularity network"
|
|
31385
|
+
complete -c hubify -n "__fish_use_subcommand" -a "memory" -d "Agent memory"
|
|
31386
|
+
complete -c hubify -n "__fish_use_subcommand" -a "workspace" -d "Workspace management"
|
|
31387
|
+
complete -c hubify -n "__fish_use_subcommand" -a "knowledge" -d "Hub knowledge"
|
|
31388
|
+
complete -c hubify -n "__fish_use_subcommand" -a "usage" -d "Usage and billing"
|
|
31389
|
+
complete -c hubify -n "__fish_use_subcommand" -a "vault" -d "Credential vault"
|
|
31390
|
+
complete -c hubify -n "__fish_use_subcommand" -a "reports" -d "Weekly intelligence reports"
|
|
31391
|
+
complete -c hubify -n "__fish_use_subcommand" -a "mission" -d "Autonomous missions"
|
|
31392
|
+
complete -c hubify -n "__fish_use_subcommand" -a "meta" -d "Meta-skills"
|
|
31393
|
+
complete -c hubify -n "__fish_use_subcommand" -a "hub" -d "Hub manifests"
|
|
31394
|
+
complete -c hubify -n "__fish_use_subcommand" -a "hub-connect" -d "Connect to hub"
|
|
31395
|
+
complete -c hubify -n "__fish_use_subcommand" -a "sync" -d "Sync operations"
|
|
31396
|
+
complete -c hubify -n "__fish_use_subcommand" -a "agent" -d "Agent management"
|
|
31397
|
+
complete -c hubify -n "__fish_use_subcommand" -a "collaborate" -d "Collaboration sessions"
|
|
31398
|
+
complete -c hubify -n "__fish_use_subcommand" -a "scan" -d "Security scanning"
|
|
31399
|
+
complete -c hubify -n "__fish_use_subcommand" -a "audit" -d "Skills audit"
|
|
31400
|
+
complete -c hubify -n "__fish_use_subcommand" -a "ingest" -d "Ingest from registries"
|
|
29056
31401
|
complete -c hubify -n "__fish_use_subcommand" -a "seed-all" -d "Seed from all sources"
|
|
29057
31402
|
complete -c hubify -n "__fish_use_subcommand" -a "seed-anthropic" -d "Seed from Anthropic"
|
|
29058
31403
|
complete -c hubify -n "__fish_use_subcommand" -a "seed-clawhub" -d "Seed from ClawHub"
|
|
@@ -29064,7 +31409,7 @@ complete -c hubify -n "__fish_use_subcommand" -a "seed-tools" -d "Seed core tool
|
|
|
29064
31409
|
complete -c hubify -n "__fish_use_subcommand" -a "completion" -d "Generate shell completions"
|
|
29065
31410
|
`;
|
|
29066
31411
|
}
|
|
29067
|
-
var program = new
|
|
31412
|
+
var program = new Command58();
|
|
29068
31413
|
program.name("hubify").description("The living intelligence layer for Agent Skills").version(VERSION, "-v, --version", "Display version number").configureHelp({
|
|
29069
31414
|
formatHelp: () => formatGroupedHelp()
|
|
29070
31415
|
});
|
|
@@ -29110,14 +31455,19 @@ program.addCommand(researchCommand);
|
|
|
29110
31455
|
program.addCommand(authCommand);
|
|
29111
31456
|
program.addCommand(scanCommand);
|
|
29112
31457
|
program.addCommand(auditCommand);
|
|
29113
|
-
program.addCommand(
|
|
31458
|
+
program.addCommand(labsCommand2);
|
|
29114
31459
|
program.addCommand(networkCommand);
|
|
29115
31460
|
program.addCommand(memoryCommand);
|
|
29116
31461
|
program.addCommand(workspaceCommand);
|
|
29117
31462
|
program.addCommand(knowledgeCommand);
|
|
29118
31463
|
program.addCommand(usageCommand);
|
|
29119
31464
|
program.addCommand(collectiveCommand);
|
|
29120
|
-
|
|
31465
|
+
program.addCommand(vaultCommand);
|
|
31466
|
+
program.addCommand(reportsCommand);
|
|
31467
|
+
program.addCommand(missionCommand);
|
|
31468
|
+
program.addCommand(metaCommand);
|
|
31469
|
+
program.addCommand(selfUpdateCommand);
|
|
31470
|
+
var completionCommand = new Command58("completion").description("Generate shell completion scripts").argument("<shell>", "Shell type: bash, zsh, or fish").action((shell) => {
|
|
29121
31471
|
switch (shell.toLowerCase()) {
|
|
29122
31472
|
case "bash":
|
|
29123
31473
|
console.log(generateBashCompletion());
|
|
@@ -29129,16 +31479,16 @@ var completionCommand = new Command53("completion").description("Generate shell
|
|
|
29129
31479
|
console.log(generateFishCompletion());
|
|
29130
31480
|
break;
|
|
29131
31481
|
default:
|
|
29132
|
-
console.error(
|
|
29133
|
-
console.error(
|
|
31482
|
+
console.error(chalk61.red(`Unknown shell: ${shell}`));
|
|
31483
|
+
console.error(chalk61.gray("Supported shells: bash, zsh, fish"));
|
|
29134
31484
|
process.exit(1);
|
|
29135
31485
|
}
|
|
29136
31486
|
});
|
|
29137
31487
|
program.addCommand(completionCommand);
|
|
29138
|
-
var doctorCommand = new
|
|
31488
|
+
var doctorCommand = new Command58("doctor").description("Diagnose Hubify configuration and connectivity").action(async () => {
|
|
29139
31489
|
console.log(BANNER);
|
|
29140
|
-
console.log(
|
|
29141
|
-
console.log(
|
|
31490
|
+
console.log(chalk61.cyan.bold(" Hubify Doctor"));
|
|
31491
|
+
console.log(chalk61.gray(" Checking your setup...\n"));
|
|
29142
31492
|
const checks = [];
|
|
29143
31493
|
const nodeVersion = process.version;
|
|
29144
31494
|
const majorVersion = parseInt(nodeVersion.slice(1).split(".")[0]);
|
|
@@ -29147,17 +31497,17 @@ var doctorCommand = new Command53("doctor").description("Diagnose Hubify configu
|
|
|
29147
31497
|
} else {
|
|
29148
31498
|
checks.push({ name: "Node.js version", status: "fail", message: `${nodeVersion} (>= 20 required)` });
|
|
29149
31499
|
}
|
|
29150
|
-
const
|
|
29151
|
-
const
|
|
29152
|
-
const
|
|
29153
|
-
const globalConfigPath =
|
|
29154
|
-
if (
|
|
31500
|
+
const os12 = await import("os");
|
|
31501
|
+
const fs29 = await import("fs");
|
|
31502
|
+
const path27 = await import("path");
|
|
31503
|
+
const globalConfigPath = path27.join(os12.homedir(), ".hubify", "config.yaml");
|
|
31504
|
+
if (fs29.existsSync(globalConfigPath)) {
|
|
29155
31505
|
checks.push({ name: "Global config", status: "pass", message: globalConfigPath });
|
|
29156
31506
|
} else {
|
|
29157
31507
|
checks.push({ name: "Global config", status: "warn", message: "Not found (run: hubify init --global)" });
|
|
29158
31508
|
}
|
|
29159
|
-
const localConfigPath =
|
|
29160
|
-
if (
|
|
31509
|
+
const localConfigPath = path27.join(process.cwd(), ".hubify", "config.yaml");
|
|
31510
|
+
if (fs29.existsSync(localConfigPath)) {
|
|
29161
31511
|
checks.push({ name: "Project config", status: "pass", message: localConfigPath });
|
|
29162
31512
|
} else {
|
|
29163
31513
|
checks.push({ name: "Project config", status: "warn", message: "Not found (run: hubify init)" });
|
|
@@ -29176,26 +31526,26 @@ var doctorCommand = new Command53("doctor").description("Diagnose Hubify configu
|
|
|
29176
31526
|
let color;
|
|
29177
31527
|
switch (check.status) {
|
|
29178
31528
|
case "pass":
|
|
29179
|
-
icon =
|
|
29180
|
-
color =
|
|
31529
|
+
icon = chalk61.green(" [PASS]");
|
|
31530
|
+
color = chalk61.white;
|
|
29181
31531
|
break;
|
|
29182
31532
|
case "fail":
|
|
29183
|
-
icon =
|
|
29184
|
-
color =
|
|
31533
|
+
icon = chalk61.red(" [FAIL]");
|
|
31534
|
+
color = chalk61.red;
|
|
29185
31535
|
hasFailure = true;
|
|
29186
31536
|
break;
|
|
29187
31537
|
case "warn":
|
|
29188
|
-
icon =
|
|
29189
|
-
color =
|
|
31538
|
+
icon = chalk61.yellow(" [WARN]");
|
|
31539
|
+
color = chalk61.yellow;
|
|
29190
31540
|
break;
|
|
29191
31541
|
}
|
|
29192
|
-
console.log(`${icon} ${
|
|
31542
|
+
console.log(`${icon} ${chalk61.white(check.name.padEnd(20))} ${color(check.message)}`);
|
|
29193
31543
|
}
|
|
29194
31544
|
console.log();
|
|
29195
31545
|
if (hasFailure) {
|
|
29196
|
-
console.log(
|
|
31546
|
+
console.log(chalk61.red(" Some checks failed. Please resolve the issues above."));
|
|
29197
31547
|
} else {
|
|
29198
|
-
console.log(
|
|
31548
|
+
console.log(chalk61.green(" All checks passed! Hubify is ready to use."));
|
|
29199
31549
|
}
|
|
29200
31550
|
console.log();
|
|
29201
31551
|
});
|
|
@@ -29207,7 +31557,7 @@ program.exitOverride((err) => {
|
|
|
29207
31557
|
if (err.code === "commander.unknownCommand") {
|
|
29208
31558
|
const unknownCommand = process.argv[2];
|
|
29209
31559
|
console.log();
|
|
29210
|
-
console.log(
|
|
31560
|
+
console.log(chalk61.red(` Error: Unknown command '${unknownCommand}'`));
|
|
29211
31561
|
console.log();
|
|
29212
31562
|
const commands = [
|
|
29213
31563
|
"search",
|
|
@@ -29237,26 +31587,26 @@ program.exitOverride((err) => {
|
|
|
29237
31587
|
(cmd) => cmd.includes(unknownCommand) || unknownCommand.includes(cmd.slice(0, 3)) || levenshteinDistance(cmd, unknownCommand) <= 2
|
|
29238
31588
|
);
|
|
29239
31589
|
if (similar.length > 0) {
|
|
29240
|
-
console.log(
|
|
31590
|
+
console.log(chalk61.gray(" Did you mean one of these?"));
|
|
29241
31591
|
for (const cmd of similar.slice(0, 3)) {
|
|
29242
|
-
console.log(
|
|
31592
|
+
console.log(chalk61.cyan(` hubify ${cmd}`));
|
|
29243
31593
|
}
|
|
29244
31594
|
console.log();
|
|
29245
31595
|
}
|
|
29246
|
-
console.log(
|
|
31596
|
+
console.log(chalk61.gray(" Run ") + chalk61.cyan("hubify --help") + chalk61.gray(" to see all available commands."));
|
|
29247
31597
|
console.log();
|
|
29248
31598
|
process.exit(1);
|
|
29249
31599
|
}
|
|
29250
31600
|
if (err.code === "commander.missingArgument") {
|
|
29251
31601
|
console.log();
|
|
29252
|
-
console.log(
|
|
31602
|
+
console.log(chalk61.red(` Error: ${err.message}`));
|
|
29253
31603
|
console.log();
|
|
29254
|
-
console.log(
|
|
31604
|
+
console.log(chalk61.gray(" Run ") + chalk61.cyan(`hubify ${process.argv[2]} --help`) + chalk61.gray(" for usage details."));
|
|
29255
31605
|
console.log();
|
|
29256
31606
|
process.exit(1);
|
|
29257
31607
|
}
|
|
29258
31608
|
console.error();
|
|
29259
|
-
console.error(
|
|
31609
|
+
console.error(chalk61.red(" Error:"), err.message);
|
|
29260
31610
|
console.error();
|
|
29261
31611
|
process.exit(1);
|
|
29262
31612
|
});
|
|
@@ -29285,19 +31635,19 @@ function levenshteinDistance(a, b) {
|
|
|
29285
31635
|
}
|
|
29286
31636
|
process.on("unhandledRejection", (reason) => {
|
|
29287
31637
|
console.error();
|
|
29288
|
-
console.error(
|
|
31638
|
+
console.error(chalk61.red(" Unexpected error:"));
|
|
29289
31639
|
if (reason instanceof Error) {
|
|
29290
|
-
console.error(
|
|
31640
|
+
console.error(chalk61.red(` ${reason.message}`));
|
|
29291
31641
|
if (reason.message.includes("ECONNREFUSED") || reason.message.includes("fetch")) {
|
|
29292
31642
|
console.error();
|
|
29293
|
-
console.error(
|
|
31643
|
+
console.error(chalk61.yellow(" Tip: Check your internet connection or run 'hubify doctor' to diagnose."));
|
|
29294
31644
|
}
|
|
29295
31645
|
if (reason.message.includes("not found")) {
|
|
29296
31646
|
console.error();
|
|
29297
|
-
console.error(
|
|
31647
|
+
console.error(chalk61.yellow(" Tip: Try 'hubify search <query>' to find available skills."));
|
|
29298
31648
|
}
|
|
29299
31649
|
} else {
|
|
29300
|
-
console.error(
|
|
31650
|
+
console.error(chalk61.red(` ${String(reason)}`));
|
|
29301
31651
|
}
|
|
29302
31652
|
console.error();
|
|
29303
31653
|
process.exit(1);
|