@growthub/cli 0.7.9 → 0.8.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/assets/worker-kits/creative-strategist-v1/SKILL.md +95 -0
- package/assets/worker-kits/creative-strategist-v1/bundles/creative-strategist-v1.json +12 -2
- package/assets/worker-kits/creative-strategist-v1/helpers/README.md +29 -0
- package/assets/worker-kits/creative-strategist-v1/helpers/extract-muse-frames.sh +81 -0
- package/assets/worker-kits/creative-strategist-v1/helpers/grep-hooks.sh +62 -0
- package/assets/worker-kits/creative-strategist-v1/kit.json +20 -2
- package/assets/worker-kits/creative-strategist-v1/skills/README.md +23 -0
- package/assets/worker-kits/creative-strategist-v1/skills/frame-analysis/SKILL.md +88 -0
- package/assets/worker-kits/creative-strategist-v1/templates/project.md +48 -0
- package/assets/worker-kits/creative-strategist-v1/templates/self-eval.md +67 -0
- package/assets/worker-kits/creative-strategist-v1/workers/creative-strategist/CLAUDE.md +22 -0
- package/assets/worker-kits/growthub-ai-website-cloner-v1/SKILL.md +89 -0
- package/assets/worker-kits/growthub-ai-website-cloner-v1/bundles/growthub-ai-website-cloner-v1.json +9 -2
- package/assets/worker-kits/growthub-ai-website-cloner-v1/helpers/README.md +29 -0
- package/assets/worker-kits/growthub-ai-website-cloner-v1/kit.json +14 -2
- package/assets/worker-kits/growthub-ai-website-cloner-v1/skills/README.md +23 -0
- package/assets/worker-kits/growthub-ai-website-cloner-v1/templates/project.md +48 -0
- package/assets/worker-kits/growthub-ai-website-cloner-v1/templates/self-eval.md +67 -0
- package/assets/worker-kits/growthub-ai-website-cloner-v1/workers/ai-website-cloner-operator/CLAUDE.md +22 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/SKILL.md +122 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/bundles/growthub-custom-workspace-starter-v1.json +14 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/docs/governed-workspace-primitives.md +182 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/helpers/README.md +44 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +16 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/skills/README.md +55 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/project.md +55 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/templates/self-eval.md +67 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/workers/custom-workspace-operator/CLAUDE.md +24 -2
- package/assets/worker-kits/growthub-email-marketing-v1/SKILL.md +90 -0
- package/assets/worker-kits/growthub-email-marketing-v1/bundles/growthub-email-marketing-v1.json +9 -2
- package/assets/worker-kits/growthub-email-marketing-v1/helpers/README.md +29 -0
- package/assets/worker-kits/growthub-email-marketing-v1/kit.json +14 -2
- package/assets/worker-kits/growthub-email-marketing-v1/skills/README.md +23 -0
- package/assets/worker-kits/growthub-email-marketing-v1/templates/project.md +48 -0
- package/assets/worker-kits/growthub-email-marketing-v1/templates/self-eval.md +67 -0
- package/assets/worker-kits/growthub-email-marketing-v1/workers/email-marketing-strategist/CLAUDE.md +22 -0
- package/assets/worker-kits/growthub-geo-seo-v1/SKILL.md +90 -0
- package/assets/worker-kits/growthub-geo-seo-v1/bundles/growthub-geo-seo-v1.json +12 -3
- package/assets/worker-kits/growthub-geo-seo-v1/helpers/README.md +29 -0
- package/assets/worker-kits/growthub-geo-seo-v1/kit.json +23 -5
- package/assets/worker-kits/growthub-geo-seo-v1/skills/README.md +23 -0
- package/assets/worker-kits/growthub-geo-seo-v1/templates/project.md +48 -0
- package/assets/worker-kits/growthub-geo-seo-v1/templates/self-eval.md +67 -0
- package/assets/worker-kits/growthub-geo-seo-v1/workers/geo-seo-operator/CLAUDE.md +22 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/SKILL.md +89 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/bundles/growthub-hyperframes-studio-v1.json +6 -1
- package/assets/worker-kits/growthub-hyperframes-studio-v1/helpers/README.md +29 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/kit.json +14 -2
- package/assets/worker-kits/growthub-hyperframes-studio-v1/skills/README.md +23 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/templates/project.md +48 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/templates/self-eval.md +67 -0
- package/assets/worker-kits/growthub-hyperframes-studio-v1/workers/hyperframes-studio-operator/CLAUDE.md +22 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/SKILL.md +90 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/bundles/growthub-marketing-skills-v1.json +12 -3
- package/assets/worker-kits/growthub-marketing-skills-v1/helpers/README.md +29 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/kit.json +23 -5
- package/assets/worker-kits/growthub-marketing-skills-v1/skills/README.md +23 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/templates/project.md +48 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/templates/self-eval.md +67 -0
- package/assets/worker-kits/growthub-marketing-skills-v1/workers/marketing-operator/CLAUDE.md +22 -0
- package/assets/worker-kits/growthub-open-higgsfield-studio-v1/SKILL.md +89 -0
- package/assets/worker-kits/growthub-open-higgsfield-studio-v1/bundles/growthub-open-higgsfield-studio-v1.json +9 -2
- package/assets/worker-kits/growthub-open-higgsfield-studio-v1/helpers/README.md +29 -0
- package/assets/worker-kits/growthub-open-higgsfield-studio-v1/kit.json +14 -2
- package/assets/worker-kits/growthub-open-higgsfield-studio-v1/skills/README.md +23 -0
- package/assets/worker-kits/growthub-open-higgsfield-studio-v1/templates/project.md +48 -0
- package/assets/worker-kits/growthub-open-higgsfield-studio-v1/templates/self-eval.md +67 -0
- package/assets/worker-kits/growthub-open-higgsfield-studio-v1/workers/open-higgsfield-studio-operator/CLAUDE.md +22 -0
- package/assets/worker-kits/growthub-open-montage-studio-v1/SKILL.md +89 -0
- package/assets/worker-kits/growthub-open-montage-studio-v1/bundles/growthub-open-montage-studio-v1.json +9 -2
- package/assets/worker-kits/growthub-open-montage-studio-v1/helpers/README.md +29 -0
- package/assets/worker-kits/growthub-open-montage-studio-v1/kit.json +14 -2
- package/assets/worker-kits/growthub-open-montage-studio-v1/skills/README.md +23 -0
- package/assets/worker-kits/growthub-open-montage-studio-v1/templates/project.md +48 -0
- package/assets/worker-kits/growthub-open-montage-studio-v1/templates/self-eval.md +67 -0
- package/assets/worker-kits/growthub-open-montage-studio-v1/workers/open-montage-studio-operator/CLAUDE.md +22 -0
- package/assets/worker-kits/growthub-postiz-social-v1/SKILL.md +90 -0
- package/assets/worker-kits/growthub-postiz-social-v1/bundles/growthub-postiz-social-v1.json +9 -2
- package/assets/worker-kits/growthub-postiz-social-v1/helpers/README.md +29 -0
- package/assets/worker-kits/growthub-postiz-social-v1/kit.json +14 -2
- package/assets/worker-kits/growthub-postiz-social-v1/skills/README.md +23 -0
- package/assets/worker-kits/growthub-postiz-social-v1/templates/project.md +48 -0
- package/assets/worker-kits/growthub-postiz-social-v1/templates/self-eval.md +67 -0
- package/assets/worker-kits/growthub-postiz-social-v1/workers/postiz-social-operator/CLAUDE.md +22 -0
- package/assets/worker-kits/growthub-twenty-crm-v1/SKILL.md +89 -0
- package/assets/worker-kits/growthub-twenty-crm-v1/bundles/growthub-twenty-crm-v1.json +9 -2
- package/assets/worker-kits/growthub-twenty-crm-v1/helpers/README.md +29 -0
- package/assets/worker-kits/growthub-twenty-crm-v1/kit.json +14 -2
- package/assets/worker-kits/growthub-twenty-crm-v1/skills/README.md +23 -0
- package/assets/worker-kits/growthub-twenty-crm-v1/templates/project.md +48 -0
- package/assets/worker-kits/growthub-twenty-crm-v1/templates/self-eval.md +67 -0
- package/assets/worker-kits/growthub-twenty-crm-v1/workers/twenty-crm-operator/CLAUDE.md +22 -0
- package/assets/worker-kits/growthub-video-use-studio-v1/SKILL.md +89 -0
- package/assets/worker-kits/growthub-video-use-studio-v1/bundles/growthub-video-use-studio-v1.json +6 -1
- package/assets/worker-kits/growthub-video-use-studio-v1/helpers/README.md +29 -0
- package/assets/worker-kits/growthub-video-use-studio-v1/kit.json +14 -2
- package/assets/worker-kits/growthub-video-use-studio-v1/skills/README.md +23 -0
- package/assets/worker-kits/growthub-video-use-studio-v1/templates/project.md +48 -0
- package/assets/worker-kits/growthub-video-use-studio-v1/templates/self-eval.md +67 -0
- package/assets/worker-kits/growthub-video-use-studio-v1/workers/video-use-studio-operator/CLAUDE.md +22 -0
- package/assets/worker-kits/growthub-zernio-social-v1/SKILL.md +90 -0
- package/assets/worker-kits/growthub-zernio-social-v1/bundles/growthub-zernio-social-v1.json +9 -2
- package/assets/worker-kits/growthub-zernio-social-v1/helpers/README.md +29 -0
- package/assets/worker-kits/growthub-zernio-social-v1/kit.json +14 -2
- package/assets/worker-kits/growthub-zernio-social-v1/skills/README.md +23 -0
- package/assets/worker-kits/growthub-zernio-social-v1/templates/project.md +48 -0
- package/assets/worker-kits/growthub-zernio-social-v1/templates/self-eval.md +67 -0
- package/assets/worker-kits/growthub-zernio-social-v1/workers/zernio-social-operator/CLAUDE.md +22 -0
- package/dist/index.js +961 -232
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -8182,8 +8182,8 @@ var init_onboard = __esm({
|
|
|
8182
8182
|
|
|
8183
8183
|
// src/client/http.ts
|
|
8184
8184
|
import { URL as URL2 } from "node:url";
|
|
8185
|
-
function buildUrl(apiBase,
|
|
8186
|
-
const normalizedPath =
|
|
8185
|
+
function buildUrl(apiBase, path67) {
|
|
8186
|
+
const normalizedPath = path67.startsWith("/") ? path67 : `/${path67}`;
|
|
8187
8187
|
const [pathname, query] = normalizedPath.split("?");
|
|
8188
8188
|
const url = new URL2(apiBase);
|
|
8189
8189
|
url.pathname = `${url.pathname.replace(/\/+$/, "")}${pathname}`;
|
|
@@ -8245,26 +8245,26 @@ var init_http = __esm({
|
|
|
8245
8245
|
this.runId = opts.runId?.trim() || void 0;
|
|
8246
8246
|
this.userId = opts.userId?.trim() || void 0;
|
|
8247
8247
|
}
|
|
8248
|
-
get(
|
|
8249
|
-
return this.request(
|
|
8248
|
+
get(path67, opts) {
|
|
8249
|
+
return this.request(path67, { method: "GET" }, opts);
|
|
8250
8250
|
}
|
|
8251
|
-
post(
|
|
8252
|
-
return this.request(
|
|
8251
|
+
post(path67, body, opts) {
|
|
8252
|
+
return this.request(path67, {
|
|
8253
8253
|
method: "POST",
|
|
8254
8254
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
8255
8255
|
}, opts);
|
|
8256
8256
|
}
|
|
8257
|
-
patch(
|
|
8258
|
-
return this.request(
|
|
8257
|
+
patch(path67, body, opts) {
|
|
8258
|
+
return this.request(path67, {
|
|
8259
8259
|
method: "PATCH",
|
|
8260
8260
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
8261
8261
|
}, opts);
|
|
8262
8262
|
}
|
|
8263
|
-
delete(
|
|
8264
|
-
return this.request(
|
|
8263
|
+
delete(path67, opts) {
|
|
8264
|
+
return this.request(path67, { method: "DELETE" }, opts);
|
|
8265
8265
|
}
|
|
8266
|
-
async request(
|
|
8267
|
-
const url = buildUrl(this.apiBase,
|
|
8266
|
+
async request(path67, init, opts) {
|
|
8267
|
+
const url = buildUrl(this.apiBase, path67);
|
|
8268
8268
|
const headers = {
|
|
8269
8269
|
accept: "application/json",
|
|
8270
8270
|
...toStringRecord(init.headers)
|
|
@@ -9711,9 +9711,9 @@ async function fetchHostedIntegrations(session) {
|
|
|
9711
9711
|
}
|
|
9712
9712
|
async function fetchHostedIntegrationCredential(session, providerId) {
|
|
9713
9713
|
const client = toApiClient2(session);
|
|
9714
|
-
const
|
|
9714
|
+
const path67 = `${DEFAULT_INTEGRATION_CREDENTIAL_PATH}&provider=${encodeURIComponent(providerId)}`;
|
|
9715
9715
|
try {
|
|
9716
|
-
return await client.get(
|
|
9716
|
+
return await client.get(path67, { ignoreNotFound: true });
|
|
9717
9717
|
} catch (err) {
|
|
9718
9718
|
if (err instanceof ApiRequestError && (err.status === 404 || err.status === 501)) {
|
|
9719
9719
|
throw new HostedEndpointUnavailableError(err.status, err.message);
|
|
@@ -10287,13 +10287,43 @@ var init_github = __esm({
|
|
|
10287
10287
|
}
|
|
10288
10288
|
});
|
|
10289
10289
|
|
|
10290
|
-
// src/starter/
|
|
10290
|
+
// src/starter/scaffold-session-memory.ts
|
|
10291
10291
|
import fs43 from "node:fs";
|
|
10292
10292
|
import path51 from "node:path";
|
|
10293
|
+
function scaffoldSessionMemory(input) {
|
|
10294
|
+
const forkPath = path51.resolve(input.forkPath);
|
|
10295
|
+
const templatePath = path51.join(forkPath, TEMPLATE_RELATIVE);
|
|
10296
|
+
const projectMdPath = path51.join(forkPath, PROJECT_MD_RELATIVE);
|
|
10297
|
+
if (!fs43.existsSync(templatePath)) {
|
|
10298
|
+
return { written: false, projectMdPath, templatePath: null };
|
|
10299
|
+
}
|
|
10300
|
+
if (fs43.existsSync(projectMdPath)) {
|
|
10301
|
+
return { written: false, projectMdPath, templatePath };
|
|
10302
|
+
}
|
|
10303
|
+
const template = fs43.readFileSync(templatePath, "utf8");
|
|
10304
|
+
const startedAt = input.startedAt ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
10305
|
+
const sourceRef = input.sourceRef ?? "";
|
|
10306
|
+
const seeded = template.replaceAll("{{KIT_ID}}", input.kitId).replaceAll("{{FORK_ID}}", input.forkId).replaceAll("{{STARTED_AT}}", startedAt).replaceAll("{{SOURCE}}", input.source).replaceAll("{{SOURCE_REF}}", sourceRef);
|
|
10307
|
+
fs43.mkdirSync(path51.dirname(projectMdPath), { recursive: true });
|
|
10308
|
+
fs43.writeFileSync(projectMdPath, seeded, "utf8");
|
|
10309
|
+
return { written: true, projectMdPath, templatePath };
|
|
10310
|
+
}
|
|
10311
|
+
var PROJECT_MD_RELATIVE, TEMPLATE_RELATIVE;
|
|
10312
|
+
var init_scaffold_session_memory = __esm({
|
|
10313
|
+
"src/starter/scaffold-session-memory.ts"() {
|
|
10314
|
+
"use strict";
|
|
10315
|
+
PROJECT_MD_RELATIVE = ".growthub-fork/project.md";
|
|
10316
|
+
TEMPLATE_RELATIVE = "templates/project.md";
|
|
10317
|
+
}
|
|
10318
|
+
});
|
|
10319
|
+
|
|
10320
|
+
// src/starter/init.ts
|
|
10321
|
+
import fs44 from "node:fs";
|
|
10322
|
+
import path52 from "node:path";
|
|
10293
10323
|
async function initStarterWorkspace(opts) {
|
|
10294
10324
|
const kitId = opts.kitId ?? DEFAULT_STARTER_KIT_ID;
|
|
10295
|
-
const absOut =
|
|
10296
|
-
if (
|
|
10325
|
+
const absOut = path52.resolve(opts.out);
|
|
10326
|
+
if (fs44.existsSync(absOut) && fs44.readdirSync(absOut).length > 0) {
|
|
10297
10327
|
throw new Error(`Destination ${absOut} already exists and is not empty.`);
|
|
10298
10328
|
}
|
|
10299
10329
|
const info = getBundledKitSourceInfo(kitId);
|
|
@@ -10302,7 +10332,7 @@ async function initStarterWorkspace(opts) {
|
|
|
10302
10332
|
forkPath: absOut,
|
|
10303
10333
|
kitId: info.id,
|
|
10304
10334
|
baseVersion: info.version,
|
|
10305
|
-
label: opts.name?.trim() ||
|
|
10335
|
+
label: opts.name?.trim() || path52.basename(absOut)
|
|
10306
10336
|
});
|
|
10307
10337
|
const policy = {
|
|
10308
10338
|
...makeDefaultKitForkPolicy(),
|
|
@@ -10322,6 +10352,22 @@ async function initStarterWorkspace(opts) {
|
|
|
10322
10352
|
type: "policy_updated",
|
|
10323
10353
|
summary: `Initial policy seeded (remoteSyncMode=${policy.remoteSyncMode})`
|
|
10324
10354
|
});
|
|
10355
|
+
const sessionSeed = scaffoldSessionMemory({
|
|
10356
|
+
forkPath: absOut,
|
|
10357
|
+
kitId: info.id,
|
|
10358
|
+
forkId: reg.forkId,
|
|
10359
|
+
source: "greenfield",
|
|
10360
|
+
sourceRef: ""
|
|
10361
|
+
});
|
|
10362
|
+
if (sessionSeed.written) {
|
|
10363
|
+
appendKitForkTraceEvent(absOut, {
|
|
10364
|
+
forkId: reg.forkId,
|
|
10365
|
+
kitId: reg.kitId,
|
|
10366
|
+
type: "skills_scaffolded",
|
|
10367
|
+
summary: "Seeded .growthub-fork/project.md from templates/project.md",
|
|
10368
|
+
detail: { projectMd: sessionSeed.projectMdPath }
|
|
10369
|
+
});
|
|
10370
|
+
}
|
|
10325
10371
|
let remote;
|
|
10326
10372
|
if (opts.upstream) {
|
|
10327
10373
|
const resolved = await resolveGithubAccessToken();
|
|
@@ -10383,6 +10429,7 @@ var init_init = __esm({
|
|
|
10383
10429
|
init_fork_remote();
|
|
10384
10430
|
init_github_resolver();
|
|
10385
10431
|
init_client2();
|
|
10432
|
+
init_scaffold_session_memory();
|
|
10386
10433
|
DEFAULT_STARTER_KIT_ID = "growthub-custom-workspace-starter-v1";
|
|
10387
10434
|
}
|
|
10388
10435
|
});
|
|
@@ -10395,8 +10442,8 @@ var init_types2 = __esm({
|
|
|
10395
10442
|
});
|
|
10396
10443
|
|
|
10397
10444
|
// src/starter/source-import/github-source.ts
|
|
10398
|
-
import
|
|
10399
|
-
import
|
|
10445
|
+
import fs45 from "node:fs";
|
|
10446
|
+
import path53 from "node:path";
|
|
10400
10447
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
10401
10448
|
function baseHeaders() {
|
|
10402
10449
|
return {
|
|
@@ -10527,12 +10574,12 @@ function cloneGithubRepo(input) {
|
|
|
10527
10574
|
if (!gitAvailable()) {
|
|
10528
10575
|
throw new Error("`git` is not available on PATH \u2014 cannot clone.");
|
|
10529
10576
|
}
|
|
10530
|
-
if (
|
|
10577
|
+
if (fs45.existsSync(input.destination)) {
|
|
10531
10578
|
throw new Error(`Clone destination already exists: ${input.destination}`);
|
|
10532
10579
|
}
|
|
10533
10580
|
const cloneUrl = input.token ? buildTokenCloneUrl(input.probe.repo, input.token) : input.probe.cloneUrl;
|
|
10534
|
-
const parent =
|
|
10535
|
-
|
|
10581
|
+
const parent = path53.dirname(input.destination);
|
|
10582
|
+
fs45.mkdirSync(parent, { recursive: true });
|
|
10536
10583
|
const depth = input.depth ?? 1;
|
|
10537
10584
|
const branch = input.branch ?? input.probe.defaultBranch;
|
|
10538
10585
|
const args = ["clone"];
|
|
@@ -10559,17 +10606,17 @@ function cloneGithubRepo(input) {
|
|
|
10559
10606
|
function narrowToSubdirectory(rootDir, subdirectory) {
|
|
10560
10607
|
const normalizedSub = subdirectory.replace(/^\/+|\/+$/g, "");
|
|
10561
10608
|
if (!normalizedSub) return;
|
|
10562
|
-
const abs =
|
|
10563
|
-
if (!
|
|
10609
|
+
const abs = path53.resolve(rootDir, normalizedSub);
|
|
10610
|
+
if (!fs45.existsSync(abs) || !fs45.statSync(abs).isDirectory()) {
|
|
10564
10611
|
throw new Error(`Subdirectory not found in cloned repo: ${subdirectory}`);
|
|
10565
10612
|
}
|
|
10566
|
-
const tmp =
|
|
10567
|
-
|
|
10568
|
-
`.${
|
|
10613
|
+
const tmp = path53.resolve(
|
|
10614
|
+
path53.dirname(rootDir),
|
|
10615
|
+
`.${path53.basename(rootDir)}-narrow-${Date.now().toString(36)}`
|
|
10569
10616
|
);
|
|
10570
|
-
|
|
10571
|
-
|
|
10572
|
-
|
|
10617
|
+
fs45.renameSync(abs, tmp);
|
|
10618
|
+
fs45.rmSync(rootDir, { recursive: true, force: true });
|
|
10619
|
+
fs45.renameSync(tmp, rootDir);
|
|
10573
10620
|
}
|
|
10574
10621
|
var GITHUB_API_BASE2;
|
|
10575
10622
|
var init_github_source = __esm({
|
|
@@ -10583,9 +10630,9 @@ var init_github_source = __esm({
|
|
|
10583
10630
|
});
|
|
10584
10631
|
|
|
10585
10632
|
// src/starter/source-import/skills-source.ts
|
|
10586
|
-
import
|
|
10633
|
+
import fs46 from "node:fs";
|
|
10587
10634
|
import os11 from "node:os";
|
|
10588
|
-
import
|
|
10635
|
+
import path54 from "node:path";
|
|
10589
10636
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
10590
10637
|
function resolveBase() {
|
|
10591
10638
|
const raw = process.env.SKILLS_SH_BASE?.trim();
|
|
@@ -10851,9 +10898,9 @@ async function probeSkillsSource(input) {
|
|
|
10851
10898
|
};
|
|
10852
10899
|
}
|
|
10853
10900
|
function assertInsidePayloadRoot(root, candidate) {
|
|
10854
|
-
const abs =
|
|
10855
|
-
const rootAbs =
|
|
10856
|
-
if (!abs.startsWith(rootAbs +
|
|
10901
|
+
const abs = path54.resolve(candidate);
|
|
10902
|
+
const rootAbs = path54.resolve(root);
|
|
10903
|
+
if (!abs.startsWith(rootAbs + path54.sep) && abs !== rootAbs) {
|
|
10857
10904
|
throw new Error(`Refusing to write outside payload root: ${candidate}`);
|
|
10858
10905
|
}
|
|
10859
10906
|
}
|
|
@@ -10869,24 +10916,24 @@ function runGit3(args, cwd) {
|
|
|
10869
10916
|
};
|
|
10870
10917
|
}
|
|
10871
10918
|
function skillDirectoryMatches(dir, skillSlug) {
|
|
10872
|
-
const skillFile =
|
|
10873
|
-
if (!
|
|
10919
|
+
const skillFile = path54.resolve(dir, "SKILL.md");
|
|
10920
|
+
if (!fs46.existsSync(skillFile) || !fs46.statSync(skillFile).isFile()) {
|
|
10874
10921
|
return false;
|
|
10875
10922
|
}
|
|
10876
|
-
if (
|
|
10923
|
+
if (path54.basename(dir) === skillSlug) {
|
|
10877
10924
|
return true;
|
|
10878
10925
|
}
|
|
10879
|
-
const content =
|
|
10926
|
+
const content = fs46.readFileSync(skillFile, "utf8");
|
|
10880
10927
|
const nameMatch = content.match(/(?:^|\n)name:\s*["']?([A-Za-z0-9._:-]+)["']?\s*(?:\n|$)/i);
|
|
10881
10928
|
return nameMatch?.[1] === skillSlug;
|
|
10882
10929
|
}
|
|
10883
10930
|
function locateSkillDirectory(root, skillSlug) {
|
|
10884
10931
|
const preferred = [
|
|
10885
|
-
|
|
10886
|
-
|
|
10932
|
+
path54.resolve(root, "skills", skillSlug),
|
|
10933
|
+
path54.resolve(root, skillSlug)
|
|
10887
10934
|
];
|
|
10888
10935
|
for (const candidate of preferred) {
|
|
10889
|
-
if (
|
|
10936
|
+
if (fs46.existsSync(candidate) && fs46.statSync(candidate).isDirectory() && skillDirectoryMatches(candidate, skillSlug)) {
|
|
10890
10937
|
return candidate;
|
|
10891
10938
|
}
|
|
10892
10939
|
}
|
|
@@ -10896,12 +10943,12 @@ function locateSkillDirectory(root, skillSlug) {
|
|
|
10896
10943
|
if (skillDirectoryMatches(current, skillSlug)) {
|
|
10897
10944
|
return current;
|
|
10898
10945
|
}
|
|
10899
|
-
for (const entry of
|
|
10946
|
+
for (const entry of fs46.readdirSync(current, { withFileTypes: true })) {
|
|
10900
10947
|
if (!entry.isDirectory()) continue;
|
|
10901
10948
|
if ([".git", "node_modules", ".next", "dist", "build", "coverage"].includes(entry.name)) {
|
|
10902
10949
|
continue;
|
|
10903
10950
|
}
|
|
10904
|
-
queue.push(
|
|
10951
|
+
queue.push(path54.resolve(current, entry.name));
|
|
10905
10952
|
}
|
|
10906
10953
|
}
|
|
10907
10954
|
return null;
|
|
@@ -10911,18 +10958,18 @@ function copySkillTree(sourceDir, destination) {
|
|
|
10911
10958
|
const stack = [{ from: sourceDir, to: destination }];
|
|
10912
10959
|
while (stack.length > 0) {
|
|
10913
10960
|
const current = stack.pop();
|
|
10914
|
-
|
|
10915
|
-
for (const entry of
|
|
10916
|
-
const fromPath =
|
|
10917
|
-
const toPath =
|
|
10961
|
+
fs46.mkdirSync(current.to, { recursive: true });
|
|
10962
|
+
for (const entry of fs46.readdirSync(current.from, { withFileTypes: true })) {
|
|
10963
|
+
const fromPath = path54.resolve(current.from, entry.name);
|
|
10964
|
+
const toPath = path54.resolve(current.to, entry.name);
|
|
10918
10965
|
assertInsidePayloadRoot(destination, toPath);
|
|
10919
10966
|
if (entry.isDirectory()) {
|
|
10920
10967
|
stack.push({ from: fromPath, to: toPath });
|
|
10921
10968
|
continue;
|
|
10922
10969
|
}
|
|
10923
|
-
const data =
|
|
10924
|
-
|
|
10925
|
-
|
|
10970
|
+
const data = fs46.readFileSync(fromPath);
|
|
10971
|
+
fs46.mkdirSync(path54.dirname(toPath), { recursive: true });
|
|
10972
|
+
fs46.writeFileSync(toPath, data, { mode: 420 });
|
|
10926
10973
|
written += 1;
|
|
10927
10974
|
}
|
|
10928
10975
|
}
|
|
@@ -10933,7 +10980,7 @@ async function fetchSkillPayload(input) {
|
|
|
10933
10980
|
if (!gitAvailable()) {
|
|
10934
10981
|
throw new Error("`git` is not available on PATH \u2014 cannot materialize a skills.sh payload.");
|
|
10935
10982
|
}
|
|
10936
|
-
if (
|
|
10983
|
+
if (fs46.existsSync(destination)) {
|
|
10937
10984
|
throw new Error(`Skill payload destination already exists: ${destination}`);
|
|
10938
10985
|
}
|
|
10939
10986
|
const repoSource = probe.repoUrl ?? (probe.repository ? `https://github.com/${probe.repository}` : void 0);
|
|
@@ -10941,11 +10988,11 @@ async function fetchSkillPayload(input) {
|
|
|
10941
10988
|
if (!repoSource || !skillSlug) {
|
|
10942
10989
|
throw new Error(`Skill '${probe.skillId}' is missing repository metadata \u2014 cannot materialize payload.`);
|
|
10943
10990
|
}
|
|
10944
|
-
const cloneRoot =
|
|
10945
|
-
|
|
10991
|
+
const cloneRoot = fs46.mkdtempSync(
|
|
10992
|
+
path54.join(os11.tmpdir(), "growthub-skills-source-")
|
|
10946
10993
|
);
|
|
10947
10994
|
try {
|
|
10948
|
-
const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot],
|
|
10995
|
+
const cloneRes = runGit3(["clone", "--depth", "1", repoSource, cloneRoot], path54.dirname(cloneRoot));
|
|
10949
10996
|
if (!cloneRes.ok) {
|
|
10950
10997
|
throw new Error(`git clone failed: ${cloneRes.stderr || "unable to clone skill repository"}`);
|
|
10951
10998
|
}
|
|
@@ -10958,7 +11005,7 @@ async function fetchSkillPayload(input) {
|
|
|
10958
11005
|
const fileCount = copySkillTree(skillDir, destination);
|
|
10959
11006
|
return { destination, fileCount };
|
|
10960
11007
|
} finally {
|
|
10961
|
-
|
|
11008
|
+
fs46.rmSync(cloneRoot, { recursive: true, force: true });
|
|
10962
11009
|
}
|
|
10963
11010
|
}
|
|
10964
11011
|
var DEFAULT_BASE, COMMENT_PATTERN;
|
|
@@ -10972,13 +11019,13 @@ var init_skills_source = __esm({
|
|
|
10972
11019
|
});
|
|
10973
11020
|
|
|
10974
11021
|
// src/starter/source-import/detect.ts
|
|
10975
|
-
import
|
|
10976
|
-
import
|
|
11022
|
+
import fs47 from "node:fs";
|
|
11023
|
+
import path55 from "node:path";
|
|
10977
11024
|
function safeReadPackageJson(dir) {
|
|
10978
|
-
const p35 =
|
|
10979
|
-
if (!
|
|
11025
|
+
const p35 = path55.resolve(dir, "package.json");
|
|
11026
|
+
if (!fs47.existsSync(p35)) return null;
|
|
10980
11027
|
try {
|
|
10981
|
-
return JSON.parse(
|
|
11028
|
+
return JSON.parse(fs47.readFileSync(p35, "utf8"));
|
|
10982
11029
|
} catch {
|
|
10983
11030
|
return null;
|
|
10984
11031
|
}
|
|
@@ -10988,10 +11035,10 @@ function detectPackageManager(dir, pkg) {
|
|
|
10988
11035
|
if (pkg?.packageManager?.startsWith("yarn")) return "yarn";
|
|
10989
11036
|
if (pkg?.packageManager?.startsWith("npm")) return "npm";
|
|
10990
11037
|
if (pkg?.packageManager?.startsWith("bun")) return "bun";
|
|
10991
|
-
if (
|
|
10992
|
-
if (
|
|
10993
|
-
if (
|
|
10994
|
-
if (
|
|
11038
|
+
if (fs47.existsSync(path55.resolve(dir, "pnpm-lock.yaml"))) return "pnpm";
|
|
11039
|
+
if (fs47.existsSync(path55.resolve(dir, "yarn.lock"))) return "yarn";
|
|
11040
|
+
if (fs47.existsSync(path55.resolve(dir, "bun.lockb"))) return "bun";
|
|
11041
|
+
if (fs47.existsSync(path55.resolve(dir, "package-lock.json"))) return "npm";
|
|
10995
11042
|
return "unknown";
|
|
10996
11043
|
}
|
|
10997
11044
|
function collectDeps(pkg) {
|
|
@@ -11005,12 +11052,12 @@ function collectDeps(pkg) {
|
|
|
11005
11052
|
}
|
|
11006
11053
|
function looksLikeSkillPayload(rootDir) {
|
|
11007
11054
|
const markers = ["SKILL.md", "skill.md", "skill.json", "skill.yml", "skill.yaml", "prompt.md"];
|
|
11008
|
-
return markers.some((name) =>
|
|
11055
|
+
return markers.some((name) => fs47.existsSync(path55.resolve(rootDir, name)));
|
|
11009
11056
|
}
|
|
11010
11057
|
function detectFramework(rootDir, pkg) {
|
|
11011
11058
|
if (!pkg) {
|
|
11012
11059
|
if (looksLikeSkillPayload(rootDir)) return "skill";
|
|
11013
|
-
if (
|
|
11060
|
+
if (fs47.existsSync(path55.resolve(rootDir, "docs"))) return "docs";
|
|
11014
11061
|
return "unknown";
|
|
11015
11062
|
}
|
|
11016
11063
|
const deps = collectDeps(pkg);
|
|
@@ -11019,8 +11066,8 @@ function detectFramework(rootDir, pkg) {
|
|
|
11019
11066
|
"vite.config.ts",
|
|
11020
11067
|
"vite.config.mjs",
|
|
11021
11068
|
"vite.config.cjs"
|
|
11022
|
-
].some((name) =>
|
|
11023
|
-
if (deps.has("next") ||
|
|
11069
|
+
].some((name) => fs47.existsSync(path55.resolve(rootDir, name)));
|
|
11070
|
+
if (deps.has("next") || fs47.existsSync(path55.resolve(rootDir, "next.config.js")) || fs47.existsSync(path55.resolve(rootDir, "next.config.mjs"))) {
|
|
11024
11071
|
return "next";
|
|
11025
11072
|
}
|
|
11026
11073
|
if (deps.has("vite") || hasViteConfig) return "vite";
|
|
@@ -11046,15 +11093,15 @@ function pickScripts(pkg) {
|
|
|
11046
11093
|
return out;
|
|
11047
11094
|
}
|
|
11048
11095
|
function listEnvFiles(dir) {
|
|
11049
|
-
if (!
|
|
11050
|
-
return
|
|
11096
|
+
if (!fs47.existsSync(dir)) return [];
|
|
11097
|
+
return fs47.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).filter((name) => name === ".env" || name.startsWith(".env.") || name === ".env.example");
|
|
11051
11098
|
}
|
|
11052
11099
|
function findAppRoot(rootDir, pkg) {
|
|
11053
11100
|
if (pkg) return ".";
|
|
11054
11101
|
const candidates = ["app", "src", "apps", "packages"];
|
|
11055
11102
|
for (const candidate of candidates) {
|
|
11056
|
-
const abs =
|
|
11057
|
-
if (
|
|
11103
|
+
const abs = path55.resolve(rootDir, candidate);
|
|
11104
|
+
if (fs47.existsSync(abs) && fs47.statSync(abs).isDirectory()) {
|
|
11058
11105
|
const child = safeReadPackageJson(abs);
|
|
11059
11106
|
if (child) return candidate;
|
|
11060
11107
|
}
|
|
@@ -11070,12 +11117,12 @@ function computeConfidence(framework, manager, pkg) {
|
|
|
11070
11117
|
return Math.min(1, Number(score.toFixed(2)));
|
|
11071
11118
|
}
|
|
11072
11119
|
function detectSourceShape(rootDir) {
|
|
11073
|
-
if (!
|
|
11120
|
+
if (!fs47.existsSync(rootDir) || !fs47.statSync(rootDir).isDirectory()) {
|
|
11074
11121
|
throw new Error(`Detection target is not a directory: ${rootDir}`);
|
|
11075
11122
|
}
|
|
11076
11123
|
const rootPkg = safeReadPackageJson(rootDir);
|
|
11077
11124
|
const appRootRel = findAppRoot(rootDir, rootPkg);
|
|
11078
|
-
const appRootAbs =
|
|
11125
|
+
const appRootAbs = path55.resolve(rootDir, appRootRel);
|
|
11079
11126
|
const appPkg = appRootRel === "." ? rootPkg : safeReadPackageJson(appRootAbs);
|
|
11080
11127
|
const framework = detectFramework(appRootAbs, appPkg ?? rootPkg);
|
|
11081
11128
|
const packageManager = detectPackageManager(rootDir, rootPkg ?? appPkg);
|
|
@@ -11115,18 +11162,18 @@ var init_detect = __esm({
|
|
|
11115
11162
|
});
|
|
11116
11163
|
|
|
11117
11164
|
// src/starter/source-import/security.ts
|
|
11118
|
-
import
|
|
11119
|
-
import
|
|
11165
|
+
import fs48 from "node:fs";
|
|
11166
|
+
import path56 from "node:path";
|
|
11120
11167
|
function isLikelyTextFile(filename) {
|
|
11121
|
-
const ext =
|
|
11168
|
+
const ext = path56.extname(filename).toLowerCase();
|
|
11122
11169
|
if (!ext) return true;
|
|
11123
11170
|
return TEXT_EXTENSIONS.has(ext);
|
|
11124
11171
|
}
|
|
11125
11172
|
function isSuspiciousBinary(filename) {
|
|
11126
|
-
return SUSPICIOUS_BINARY_EXTENSIONS.has(
|
|
11173
|
+
return SUSPICIOUS_BINARY_EXTENSIONS.has(path56.extname(filename).toLowerCase());
|
|
11127
11174
|
}
|
|
11128
11175
|
function isUnexpectedArchive(filename) {
|
|
11129
|
-
const ext =
|
|
11176
|
+
const ext = path56.extname(filename).toLowerCase();
|
|
11130
11177
|
return ARCHIVE_EXTENSIONS.has(ext) || filename.toLowerCase().endsWith(".tar.gz");
|
|
11131
11178
|
}
|
|
11132
11179
|
function shortExcerpt(line) {
|
|
@@ -11181,19 +11228,19 @@ function walkPayload(root, onFile, limits) {
|
|
|
11181
11228
|
if (!current) break;
|
|
11182
11229
|
let entries;
|
|
11183
11230
|
try {
|
|
11184
|
-
entries =
|
|
11231
|
+
entries = fs48.readdirSync(current, { withFileTypes: true });
|
|
11185
11232
|
} catch {
|
|
11186
11233
|
continue;
|
|
11187
11234
|
}
|
|
11188
11235
|
for (const entry of entries) {
|
|
11189
|
-
const abs =
|
|
11236
|
+
const abs = path56.resolve(current, entry.name);
|
|
11190
11237
|
if (entry.isDirectory()) {
|
|
11191
11238
|
if (entry.name === ".git" || entry.name === "node_modules") continue;
|
|
11192
11239
|
stack.push(abs);
|
|
11193
11240
|
continue;
|
|
11194
11241
|
}
|
|
11195
11242
|
if (!entry.isFile()) continue;
|
|
11196
|
-
const rel =
|
|
11243
|
+
const rel = path56.relative(root, abs);
|
|
11197
11244
|
onFile(abs, rel);
|
|
11198
11245
|
visited += 1;
|
|
11199
11246
|
if (visited >= limits.maxFiles) break;
|
|
@@ -11203,7 +11250,7 @@ function walkPayload(root, onFile, limits) {
|
|
|
11203
11250
|
}
|
|
11204
11251
|
function inspectSourcePayload(input) {
|
|
11205
11252
|
const { payloadRoot } = input;
|
|
11206
|
-
if (!
|
|
11253
|
+
if (!fs48.existsSync(payloadRoot) || !fs48.statSync(payloadRoot).isDirectory()) {
|
|
11207
11254
|
throw new Error(`Inspection target is not a directory: ${payloadRoot}`);
|
|
11208
11255
|
}
|
|
11209
11256
|
const findings = [];
|
|
@@ -11213,7 +11260,7 @@ function inspectSourcePayload(input) {
|
|
|
11213
11260
|
(abs, rel) => {
|
|
11214
11261
|
let size = 0;
|
|
11215
11262
|
try {
|
|
11216
|
-
size =
|
|
11263
|
+
size = fs48.statSync(abs).size;
|
|
11217
11264
|
} catch {
|
|
11218
11265
|
return;
|
|
11219
11266
|
}
|
|
@@ -11222,7 +11269,7 @@ function inspectSourcePayload(input) {
|
|
|
11222
11269
|
category: "suspicious-binary",
|
|
11223
11270
|
severity: "high-risk",
|
|
11224
11271
|
path: rel,
|
|
11225
|
-
message: `Payload ships a precompiled binary (${
|
|
11272
|
+
message: `Payload ships a precompiled binary (${path56.extname(rel)}). Review provenance before use.`
|
|
11226
11273
|
});
|
|
11227
11274
|
return;
|
|
11228
11275
|
}
|
|
@@ -11231,7 +11278,7 @@ function inspectSourcePayload(input) {
|
|
|
11231
11278
|
category: "unexpected-archive",
|
|
11232
11279
|
severity: "caution",
|
|
11233
11280
|
path: rel,
|
|
11234
|
-
message: `Payload ships an archive (${
|
|
11281
|
+
message: `Payload ships an archive (${path56.extname(rel)}) \u2014 expand and review contents before use.`
|
|
11235
11282
|
});
|
|
11236
11283
|
return;
|
|
11237
11284
|
}
|
|
@@ -11239,12 +11286,12 @@ function inspectSourcePayload(input) {
|
|
|
11239
11286
|
if (bytesInspected + Math.min(size, MAX_BYTES_PER_FILE) > MAX_TOTAL_BYTES) return;
|
|
11240
11287
|
let buf;
|
|
11241
11288
|
try {
|
|
11242
|
-
const handle =
|
|
11289
|
+
const handle = fs48.openSync(abs, "r");
|
|
11243
11290
|
try {
|
|
11244
11291
|
buf = Buffer.alloc(Math.min(size, MAX_BYTES_PER_FILE));
|
|
11245
|
-
|
|
11292
|
+
fs48.readSync(handle, buf, 0, buf.length, 0);
|
|
11246
11293
|
} finally {
|
|
11247
|
-
|
|
11294
|
+
fs48.closeSync(handle);
|
|
11248
11295
|
}
|
|
11249
11296
|
} catch {
|
|
11250
11297
|
return;
|
|
@@ -11453,18 +11500,18 @@ var init_security = __esm({
|
|
|
11453
11500
|
});
|
|
11454
11501
|
|
|
11455
11502
|
// src/starter/source-import/plan.ts
|
|
11456
|
-
import
|
|
11457
|
-
import
|
|
11503
|
+
import fs49 from "node:fs";
|
|
11504
|
+
import path57 from "node:path";
|
|
11458
11505
|
function generateImportId() {
|
|
11459
11506
|
return `si-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
11460
11507
|
}
|
|
11461
11508
|
function destinationState(absDest) {
|
|
11462
|
-
if (!
|
|
11463
|
-
const stats =
|
|
11509
|
+
if (!fs49.existsSync(absDest)) return { exists: false, nonEmpty: false };
|
|
11510
|
+
const stats = fs49.statSync(absDest);
|
|
11464
11511
|
if (!stats.isDirectory()) {
|
|
11465
11512
|
throw new Error(`Destination is not a directory: ${absDest}`);
|
|
11466
11513
|
}
|
|
11467
|
-
const entries =
|
|
11514
|
+
const entries = fs49.readdirSync(absDest);
|
|
11468
11515
|
return { exists: true, nonEmpty: entries.length > 0 };
|
|
11469
11516
|
}
|
|
11470
11517
|
function describeSource(probe) {
|
|
@@ -11474,7 +11521,7 @@ function describeSource(probe) {
|
|
|
11474
11521
|
return `skill ${probe.skillId}@${probe.version} (skills.sh)`;
|
|
11475
11522
|
}
|
|
11476
11523
|
function buildSourceImportPlan(input) {
|
|
11477
|
-
const absDest =
|
|
11524
|
+
const absDest = path57.resolve(input.destination);
|
|
11478
11525
|
const state = destinationState(absDest);
|
|
11479
11526
|
const payloadPath = "imported";
|
|
11480
11527
|
const warnings = [...input.probe.warnings];
|
|
@@ -11587,8 +11634,8 @@ var init_plan = __esm({
|
|
|
11587
11634
|
});
|
|
11588
11635
|
|
|
11589
11636
|
// src/starter/source-import/summarize.ts
|
|
11590
|
-
import
|
|
11591
|
-
import
|
|
11637
|
+
import fs50 from "node:fs";
|
|
11638
|
+
import path58 from "node:path";
|
|
11592
11639
|
function sourceHeading(manifest) {
|
|
11593
11640
|
const src = manifest.source;
|
|
11594
11641
|
if (src.kind === "github-repo") {
|
|
@@ -11643,7 +11690,7 @@ function nextStepsSection(manifest) {
|
|
|
11643
11690
|
}
|
|
11644
11691
|
function writeImportSummary(input) {
|
|
11645
11692
|
const { forkPath, summaryRelativePath, manifest } = input;
|
|
11646
|
-
const summaryPath =
|
|
11693
|
+
const summaryPath = path58.resolve(forkPath, summaryRelativePath);
|
|
11647
11694
|
const body = [
|
|
11648
11695
|
`# Source Import Summary`,
|
|
11649
11696
|
``,
|
|
@@ -11673,8 +11720,8 @@ function writeImportSummary(input) {
|
|
|
11673
11720
|
`Generated by the Growthub Source Import Agent. Canonical manifest lives at \`.growthub-fork/source-import.json\`.`,
|
|
11674
11721
|
``
|
|
11675
11722
|
].join("\n");
|
|
11676
|
-
|
|
11677
|
-
|
|
11723
|
+
fs50.mkdirSync(path58.dirname(summaryPath), { recursive: true });
|
|
11724
|
+
fs50.writeFileSync(summaryPath, body, "utf8");
|
|
11678
11725
|
return summaryPath;
|
|
11679
11726
|
}
|
|
11680
11727
|
var init_summarize = __esm({
|
|
@@ -11684,16 +11731,16 @@ var init_summarize = __esm({
|
|
|
11684
11731
|
});
|
|
11685
11732
|
|
|
11686
11733
|
// src/starter/source-import/materialize.ts
|
|
11687
|
-
import
|
|
11734
|
+
import fs51 from "node:fs";
|
|
11688
11735
|
import os12 from "node:os";
|
|
11689
|
-
import
|
|
11736
|
+
import path59 from "node:path";
|
|
11690
11737
|
function resolveSourceKind(probe) {
|
|
11691
11738
|
return probe.kind === "github-repo" ? "github-repo" : "skills-skill";
|
|
11692
11739
|
}
|
|
11693
11740
|
function stagingDirFor(forkPath) {
|
|
11694
|
-
return
|
|
11741
|
+
return path59.join(
|
|
11695
11742
|
os12.tmpdir(),
|
|
11696
|
-
`growthub-source-import-${
|
|
11743
|
+
`growthub-source-import-${path59.basename(forkPath)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`
|
|
11697
11744
|
);
|
|
11698
11745
|
}
|
|
11699
11746
|
async function fetchPayload(probe, stagingDir, opts) {
|
|
@@ -11725,18 +11772,18 @@ async function fetchPayload(probe, stagingDir, opts) {
|
|
|
11725
11772
|
return { payloadRoot: stagingDir };
|
|
11726
11773
|
}
|
|
11727
11774
|
function movePayloadIntoFork(payloadRoot, forkPath, payloadRelativePath) {
|
|
11728
|
-
const target =
|
|
11729
|
-
if (
|
|
11730
|
-
|
|
11775
|
+
const target = path59.resolve(forkPath, payloadRelativePath);
|
|
11776
|
+
if (fs51.existsSync(target)) {
|
|
11777
|
+
fs51.rmSync(target, { recursive: true, force: true });
|
|
11731
11778
|
}
|
|
11732
|
-
|
|
11733
|
-
|
|
11779
|
+
fs51.mkdirSync(path59.dirname(target), { recursive: true });
|
|
11780
|
+
fs51.renameSync(payloadRoot, target);
|
|
11734
11781
|
return target;
|
|
11735
11782
|
}
|
|
11736
11783
|
function writeManifest(forkPath, manifest) {
|
|
11737
|
-
const p35 =
|
|
11738
|
-
|
|
11739
|
-
|
|
11784
|
+
const p35 = path59.resolve(forkPath, MANIFEST_RELATIVE_PATH);
|
|
11785
|
+
fs51.mkdirSync(path59.dirname(p35), { recursive: true });
|
|
11786
|
+
fs51.writeFileSync(p35, JSON.stringify(manifest, null, 2) + "\n", "utf8");
|
|
11740
11787
|
return p35;
|
|
11741
11788
|
}
|
|
11742
11789
|
function assertConfirmationsSatisfied(plan, confirmations) {
|
|
@@ -11776,7 +11823,7 @@ async function materializeImportPlan(input) {
|
|
|
11776
11823
|
requireSkillAcknowledgement: sourceKind === "skills-skill"
|
|
11777
11824
|
});
|
|
11778
11825
|
if (security.blocked) {
|
|
11779
|
-
|
|
11826
|
+
fs51.rmSync(fetchResult.payloadRoot, { recursive: true, force: true });
|
|
11780
11827
|
throw new Error(
|
|
11781
11828
|
`Security inspection blocked the fetched payload: ${security.summaryLines[0] ?? "blocking finding"}`
|
|
11782
11829
|
);
|
|
@@ -11855,6 +11902,22 @@ async function materializeImportPlan(input) {
|
|
|
11855
11902
|
detail: { gitSha: fetchResult.gitSha }
|
|
11856
11903
|
});
|
|
11857
11904
|
}
|
|
11905
|
+
const sessionSeed = scaffoldSessionMemory({
|
|
11906
|
+
forkPath,
|
|
11907
|
+
kitId: kitInfo.id,
|
|
11908
|
+
forkId: reg.forkId,
|
|
11909
|
+
source: sourceKind,
|
|
11910
|
+
sourceRef: plan.source.kind === "github-repo" ? `${plan.source.repo.owner}/${plan.source.repo.repo}${fetchResult.gitSha ? `@${fetchResult.gitSha.slice(0, 7)}` : ""}` : `${plan.source.skillId}@${plan.source.version}`
|
|
11911
|
+
});
|
|
11912
|
+
if (sessionSeed.written) {
|
|
11913
|
+
appendKitForkTraceEvent(forkPath, {
|
|
11914
|
+
forkId: reg.forkId,
|
|
11915
|
+
kitId: reg.kitId,
|
|
11916
|
+
type: "skills_scaffolded",
|
|
11917
|
+
summary: "Seeded .growthub-fork/project.md from templates/project.md",
|
|
11918
|
+
detail: { projectMd: sessionSeed.projectMdPath }
|
|
11919
|
+
});
|
|
11920
|
+
}
|
|
11858
11921
|
const summaryPath = writeImportSummary({
|
|
11859
11922
|
forkPath,
|
|
11860
11923
|
summaryRelativePath: SUMMARY_RELATIVE_PATH,
|
|
@@ -11891,6 +11954,7 @@ var init_materialize = __esm({
|
|
|
11891
11954
|
init_detect();
|
|
11892
11955
|
init_security();
|
|
11893
11956
|
init_summarize();
|
|
11957
|
+
init_scaffold_session_memory();
|
|
11894
11958
|
MANIFEST_RELATIVE_PATH = ".growthub-fork/source-import.json";
|
|
11895
11959
|
SUMMARY_RELATIVE_PATH = "IMPORT_SUMMARY.md";
|
|
11896
11960
|
PendingConfirmationError = class extends Error {
|
|
@@ -11907,26 +11971,26 @@ var init_materialize = __esm({
|
|
|
11907
11971
|
});
|
|
11908
11972
|
|
|
11909
11973
|
// src/starter/source-import/agent.ts
|
|
11910
|
-
import
|
|
11911
|
-
import
|
|
11974
|
+
import fs52 from "node:fs";
|
|
11975
|
+
import path60 from "node:path";
|
|
11912
11976
|
function resolveJobsDir() {
|
|
11913
|
-
return
|
|
11977
|
+
return path60.resolve(resolveKitForksHomeDir(), "source-import-jobs");
|
|
11914
11978
|
}
|
|
11915
11979
|
function resolveJobPath2(jobId) {
|
|
11916
|
-
return
|
|
11980
|
+
return path60.resolve(resolveJobsDir(), `${jobId}.json`);
|
|
11917
11981
|
}
|
|
11918
11982
|
function generateJobId2() {
|
|
11919
11983
|
return `sij-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
|
|
11920
11984
|
}
|
|
11921
11985
|
function writeJob2(job) {
|
|
11922
11986
|
const p35 = resolveJobPath2(job.jobId);
|
|
11923
|
-
|
|
11924
|
-
|
|
11987
|
+
fs52.mkdirSync(path60.dirname(p35), { recursive: true });
|
|
11988
|
+
fs52.writeFileSync(p35, JSON.stringify(job, null, 2) + "\n", "utf8");
|
|
11925
11989
|
}
|
|
11926
11990
|
function readJobFile(p35) {
|
|
11927
|
-
if (!
|
|
11991
|
+
if (!fs52.existsSync(p35)) return null;
|
|
11928
11992
|
try {
|
|
11929
|
-
return JSON.parse(
|
|
11993
|
+
return JSON.parse(fs52.readFileSync(p35, "utf8"));
|
|
11930
11994
|
} catch {
|
|
11931
11995
|
return null;
|
|
11932
11996
|
}
|
|
@@ -11936,7 +12000,7 @@ function patchJob2(jobId, status, patch = {}) {
|
|
|
11936
12000
|
const job = readJobFile(p35);
|
|
11937
12001
|
if (!job) return null;
|
|
11938
12002
|
const updated = { ...job, ...patch, status };
|
|
11939
|
-
|
|
12003
|
+
fs52.writeFileSync(p35, JSON.stringify(updated, null, 2) + "\n", "utf8");
|
|
11940
12004
|
return updated;
|
|
11941
12005
|
}
|
|
11942
12006
|
function getSourceImportJob(jobId) {
|
|
@@ -11955,7 +12019,7 @@ async function probeAndPlan(input, destination) {
|
|
|
11955
12019
|
}
|
|
11956
12020
|
async function runSourceImportJob(input) {
|
|
11957
12021
|
const jobId = generateJobId2();
|
|
11958
|
-
const destination =
|
|
12022
|
+
const destination = path60.resolve(input.out);
|
|
11959
12023
|
const sourceKind = input.source.kind;
|
|
11960
12024
|
const initial = {
|
|
11961
12025
|
jobId,
|
|
@@ -12103,6 +12167,357 @@ var init_source_import = __esm({
|
|
|
12103
12167
|
}
|
|
12104
12168
|
});
|
|
12105
12169
|
|
|
12170
|
+
// src/skills/frontmatter.ts
|
|
12171
|
+
function splitFrontmatter(text69) {
|
|
12172
|
+
const normalised = text69.replace(/\r\n/g, "\n");
|
|
12173
|
+
if (!normalised.startsWith("---\n")) {
|
|
12174
|
+
return { frontmatter: null, body: normalised };
|
|
12175
|
+
}
|
|
12176
|
+
const end = normalised.indexOf("\n---\n", 4);
|
|
12177
|
+
if (end === -1) {
|
|
12178
|
+
return { frontmatter: null, body: normalised };
|
|
12179
|
+
}
|
|
12180
|
+
return {
|
|
12181
|
+
frontmatter: normalised.slice(4, end),
|
|
12182
|
+
body: normalised.slice(end + 5)
|
|
12183
|
+
};
|
|
12184
|
+
}
|
|
12185
|
+
function parseScalar(raw) {
|
|
12186
|
+
const trimmed = raw.trim();
|
|
12187
|
+
if (trimmed === "") return "";
|
|
12188
|
+
if (trimmed === "true") return true;
|
|
12189
|
+
if (trimmed === "false") return false;
|
|
12190
|
+
if (trimmed === "null" || trimmed === "~") return null;
|
|
12191
|
+
if (/^-?\d+$/.test(trimmed)) return Number(trimmed);
|
|
12192
|
+
if (/^-?\d*\.\d+$/.test(trimmed)) return Number(trimmed);
|
|
12193
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
12194
|
+
return trimmed.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, "\\");
|
|
12195
|
+
}
|
|
12196
|
+
if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
12197
|
+
return trimmed.slice(1, -1).replace(/''/g, "'");
|
|
12198
|
+
}
|
|
12199
|
+
return trimmed;
|
|
12200
|
+
}
|
|
12201
|
+
function indentWidth(line) {
|
|
12202
|
+
const match = line.match(/^( *)/);
|
|
12203
|
+
return match ? match[1].length : 0;
|
|
12204
|
+
}
|
|
12205
|
+
function parseFrontmatter(text69) {
|
|
12206
|
+
const lines = text69.split("\n");
|
|
12207
|
+
const root = {};
|
|
12208
|
+
let i = 0;
|
|
12209
|
+
function readBlock(baseIndent) {
|
|
12210
|
+
const out = {};
|
|
12211
|
+
const arr = [];
|
|
12212
|
+
let mode = null;
|
|
12213
|
+
while (i < lines.length) {
|
|
12214
|
+
const line = lines[i];
|
|
12215
|
+
if (line.trim() === "" || line.trimStart().startsWith("#")) {
|
|
12216
|
+
i++;
|
|
12217
|
+
continue;
|
|
12218
|
+
}
|
|
12219
|
+
const indent = indentWidth(line);
|
|
12220
|
+
if (indent < baseIndent) break;
|
|
12221
|
+
if (indent > baseIndent) {
|
|
12222
|
+
i++;
|
|
12223
|
+
continue;
|
|
12224
|
+
}
|
|
12225
|
+
const trimmed = line.slice(baseIndent);
|
|
12226
|
+
if (trimmed.startsWith("- ")) {
|
|
12227
|
+
mode = "array";
|
|
12228
|
+
const after = trimmed.slice(2);
|
|
12229
|
+
const colonAt2 = findTopLevelColon(after);
|
|
12230
|
+
if (colonAt2 === -1) {
|
|
12231
|
+
arr.push(parseScalar(after));
|
|
12232
|
+
i++;
|
|
12233
|
+
} else {
|
|
12234
|
+
const firstKey = after.slice(0, colonAt2).trim();
|
|
12235
|
+
const firstRaw = after.slice(colonAt2 + 1).trim();
|
|
12236
|
+
const obj = {};
|
|
12237
|
+
if (firstRaw === "") {
|
|
12238
|
+
i++;
|
|
12239
|
+
obj[firstKey] = readBlock(baseIndent + 2 + 2);
|
|
12240
|
+
} else {
|
|
12241
|
+
obj[firstKey] = parseScalar(firstRaw);
|
|
12242
|
+
i++;
|
|
12243
|
+
}
|
|
12244
|
+
while (i < lines.length) {
|
|
12245
|
+
const peek = lines[i];
|
|
12246
|
+
if (peek.trim() === "" || peek.trimStart().startsWith("#")) {
|
|
12247
|
+
i++;
|
|
12248
|
+
continue;
|
|
12249
|
+
}
|
|
12250
|
+
const pInd = indentWidth(peek);
|
|
12251
|
+
if (pInd < baseIndent + 2) break;
|
|
12252
|
+
if (peek.slice(baseIndent).startsWith("- ")) break;
|
|
12253
|
+
const cTrimmed = peek.slice(baseIndent + 2);
|
|
12254
|
+
const cColon = findTopLevelColon(cTrimmed);
|
|
12255
|
+
if (cColon === -1) {
|
|
12256
|
+
i++;
|
|
12257
|
+
continue;
|
|
12258
|
+
}
|
|
12259
|
+
const cKey = cTrimmed.slice(0, cColon).trim();
|
|
12260
|
+
const cRaw = cTrimmed.slice(cColon + 1).trim();
|
|
12261
|
+
if (cRaw === "") {
|
|
12262
|
+
i++;
|
|
12263
|
+
obj[cKey] = readBlock(baseIndent + 2 + 2);
|
|
12264
|
+
} else {
|
|
12265
|
+
obj[cKey] = parseScalar(cRaw);
|
|
12266
|
+
i++;
|
|
12267
|
+
}
|
|
12268
|
+
}
|
|
12269
|
+
arr.push(obj);
|
|
12270
|
+
}
|
|
12271
|
+
continue;
|
|
12272
|
+
}
|
|
12273
|
+
mode = "object";
|
|
12274
|
+
const colonAt = findTopLevelColon(trimmed);
|
|
12275
|
+
if (colonAt === -1) {
|
|
12276
|
+
throw new Error(`Malformed frontmatter (expected "key: value") at line ${i + 1}: ${JSON.stringify(line)}`);
|
|
12277
|
+
}
|
|
12278
|
+
const key = trimmed.slice(0, colonAt).trim();
|
|
12279
|
+
const rest = trimmed.slice(colonAt + 1).trim();
|
|
12280
|
+
if (rest === "") {
|
|
12281
|
+
i++;
|
|
12282
|
+
out[key] = readBlock(baseIndent + 2);
|
|
12283
|
+
} else if (rest.startsWith("[") && rest.endsWith("]")) {
|
|
12284
|
+
const inner = rest.slice(1, -1).trim();
|
|
12285
|
+
out[key] = inner === "" ? [] : inner.split(",").map((s) => parseScalar(s));
|
|
12286
|
+
i++;
|
|
12287
|
+
} else {
|
|
12288
|
+
out[key] = parseScalar(rest);
|
|
12289
|
+
i++;
|
|
12290
|
+
}
|
|
12291
|
+
}
|
|
12292
|
+
if (mode === "array") return arr;
|
|
12293
|
+
return out;
|
|
12294
|
+
}
|
|
12295
|
+
const parsed = readBlock(0);
|
|
12296
|
+
if (Array.isArray(parsed)) {
|
|
12297
|
+
throw new Error("Frontmatter root must be an object, not a list.");
|
|
12298
|
+
}
|
|
12299
|
+
Object.assign(root, parsed);
|
|
12300
|
+
return root;
|
|
12301
|
+
}
|
|
12302
|
+
function findTopLevelColon(s) {
|
|
12303
|
+
let inDouble = false;
|
|
12304
|
+
let inSingle = false;
|
|
12305
|
+
for (let idx = 0; idx < s.length; idx++) {
|
|
12306
|
+
const ch = s[idx];
|
|
12307
|
+
const prev = idx > 0 ? s[idx - 1] : "";
|
|
12308
|
+
if (ch === '"' && prev !== "\\" && !inSingle) inDouble = !inDouble;
|
|
12309
|
+
if (ch === "'" && !inDouble) inSingle = !inSingle;
|
|
12310
|
+
if (ch === ":" && !inDouble && !inSingle) {
|
|
12311
|
+
if (idx + 1 >= s.length || s[idx + 1] === " " || s[idx + 1] === " ") return idx;
|
|
12312
|
+
}
|
|
12313
|
+
}
|
|
12314
|
+
return -1;
|
|
12315
|
+
}
|
|
12316
|
+
function readFrontmatter(text69) {
|
|
12317
|
+
const split = splitFrontmatter(text69);
|
|
12318
|
+
if (split.frontmatter === null) return { frontmatter: null, body: split.body };
|
|
12319
|
+
return { frontmatter: parseFrontmatter(split.frontmatter), body: split.body };
|
|
12320
|
+
}
|
|
12321
|
+
var init_frontmatter = __esm({
|
|
12322
|
+
"src/skills/frontmatter.ts"() {
|
|
12323
|
+
"use strict";
|
|
12324
|
+
}
|
|
12325
|
+
});
|
|
12326
|
+
|
|
12327
|
+
// src/skills/catalog.ts
|
|
12328
|
+
var catalog_exports = {};
|
|
12329
|
+
__export(catalog_exports, {
|
|
12330
|
+
readSkillCatalog: () => readSkillCatalog
|
|
12331
|
+
});
|
|
12332
|
+
import fs53 from "node:fs";
|
|
12333
|
+
import path61 from "node:path";
|
|
12334
|
+
function exists(p35) {
|
|
12335
|
+
try {
|
|
12336
|
+
fs53.accessSync(p35);
|
|
12337
|
+
return true;
|
|
12338
|
+
} catch {
|
|
12339
|
+
return false;
|
|
12340
|
+
}
|
|
12341
|
+
}
|
|
12342
|
+
function isDir(p35) {
|
|
12343
|
+
try {
|
|
12344
|
+
return fs53.statSync(p35).isDirectory();
|
|
12345
|
+
} catch {
|
|
12346
|
+
return false;
|
|
12347
|
+
}
|
|
12348
|
+
}
|
|
12349
|
+
function safeRead(p35) {
|
|
12350
|
+
try {
|
|
12351
|
+
return fs53.readFileSync(p35, "utf8");
|
|
12352
|
+
} catch {
|
|
12353
|
+
return null;
|
|
12354
|
+
}
|
|
12355
|
+
}
|
|
12356
|
+
function coerceManifest(raw, source) {
|
|
12357
|
+
const name = raw.name;
|
|
12358
|
+
const description = raw.description;
|
|
12359
|
+
if (typeof name !== "string" || name.trim() === "") {
|
|
12360
|
+
return { reason: "frontmatter missing required field 'name'" };
|
|
12361
|
+
}
|
|
12362
|
+
if (typeof description !== "string" || description.trim() === "") {
|
|
12363
|
+
return { reason: "frontmatter missing required field 'description'" };
|
|
12364
|
+
}
|
|
12365
|
+
const manifest = {
|
|
12366
|
+
name: name.trim(),
|
|
12367
|
+
description: description.trim(),
|
|
12368
|
+
source
|
|
12369
|
+
};
|
|
12370
|
+
if (Array.isArray(raw.triggers)) {
|
|
12371
|
+
manifest.triggers = raw.triggers.filter((v) => typeof v === "string");
|
|
12372
|
+
}
|
|
12373
|
+
if (typeof raw.progressiveDisclosure === "boolean") {
|
|
12374
|
+
manifest.progressiveDisclosure = raw.progressiveDisclosure;
|
|
12375
|
+
}
|
|
12376
|
+
if (Array.isArray(raw.helpers)) {
|
|
12377
|
+
manifest.helpers = raw.helpers.filter((v) => typeof v === "object" && v !== null).map((v) => ({
|
|
12378
|
+
path: String(v.path ?? ""),
|
|
12379
|
+
description: String(v.description ?? "")
|
|
12380
|
+
})).filter((h) => h.path.length > 0);
|
|
12381
|
+
}
|
|
12382
|
+
if (Array.isArray(raw.subSkills)) {
|
|
12383
|
+
manifest.subSkills = raw.subSkills.filter((v) => typeof v === "object" && v !== null).map((v) => ({
|
|
12384
|
+
name: String(v.name ?? ""),
|
|
12385
|
+
path: String(v.path ?? "")
|
|
12386
|
+
})).filter((s) => s.name.length > 0 && s.path.length > 0);
|
|
12387
|
+
}
|
|
12388
|
+
if (typeof raw.selfEval === "object" && raw.selfEval !== null && !Array.isArray(raw.selfEval)) {
|
|
12389
|
+
const se = raw.selfEval;
|
|
12390
|
+
const criteria = Array.isArray(se.criteria) ? se.criteria.filter((v) => typeof v === "string") : [];
|
|
12391
|
+
const maxRetries = typeof se.maxRetries === "number" ? se.maxRetries : 3;
|
|
12392
|
+
manifest.selfEval = {
|
|
12393
|
+
criteria,
|
|
12394
|
+
maxRetries,
|
|
12395
|
+
...typeof se.traceTo === "string" ? { traceTo: se.traceTo } : {}
|
|
12396
|
+
};
|
|
12397
|
+
}
|
|
12398
|
+
if (typeof raw.sessionMemory === "object" && raw.sessionMemory !== null && !Array.isArray(raw.sessionMemory)) {
|
|
12399
|
+
const sm = raw.sessionMemory;
|
|
12400
|
+
if (typeof sm.path === "string" && sm.path.length > 0) {
|
|
12401
|
+
manifest.sessionMemory = { path: sm.path };
|
|
12402
|
+
}
|
|
12403
|
+
}
|
|
12404
|
+
if (Array.isArray(raw.mcpTools)) {
|
|
12405
|
+
manifest.mcpTools = raw.mcpTools.filter((v) => typeof v === "string");
|
|
12406
|
+
}
|
|
12407
|
+
return { manifest };
|
|
12408
|
+
}
|
|
12409
|
+
function readOne(skillPath, source) {
|
|
12410
|
+
const body = safeRead(skillPath);
|
|
12411
|
+
if (body === null) {
|
|
12412
|
+
return { warning: { skillPath, reason: "unreadable SKILL.md" } };
|
|
12413
|
+
}
|
|
12414
|
+
let parsed;
|
|
12415
|
+
try {
|
|
12416
|
+
parsed = readFrontmatter(body);
|
|
12417
|
+
} catch (err) {
|
|
12418
|
+
return {
|
|
12419
|
+
warning: {
|
|
12420
|
+
skillPath,
|
|
12421
|
+
reason: `frontmatter parse error: ${err instanceof Error ? err.message : String(err)}`
|
|
12422
|
+
}
|
|
12423
|
+
};
|
|
12424
|
+
}
|
|
12425
|
+
if (parsed.frontmatter === null) {
|
|
12426
|
+
return { warning: { skillPath, reason: "SKILL.md has no YAML frontmatter" } };
|
|
12427
|
+
}
|
|
12428
|
+
const coerced = coerceManifest(parsed.frontmatter, source);
|
|
12429
|
+
if ("reason" in coerced) {
|
|
12430
|
+
return { warning: { skillPath, reason: coerced.reason } };
|
|
12431
|
+
}
|
|
12432
|
+
return { entry: { manifest: coerced.manifest, skillPath, source } };
|
|
12433
|
+
}
|
|
12434
|
+
function readSkillDirs(baseDir, source, out, warnings) {
|
|
12435
|
+
if (!isDir(baseDir)) return;
|
|
12436
|
+
for (const child of fs53.readdirSync(baseDir).sort()) {
|
|
12437
|
+
const dir = path61.join(baseDir, child);
|
|
12438
|
+
if (!isDir(dir)) continue;
|
|
12439
|
+
const skill = path61.join(dir, "SKILL.md");
|
|
12440
|
+
if (!exists(skill)) continue;
|
|
12441
|
+
const res = readOne(skill, source);
|
|
12442
|
+
if ("entry" in res) out.push(res.entry);
|
|
12443
|
+
else warnings.push(res.warning);
|
|
12444
|
+
}
|
|
12445
|
+
}
|
|
12446
|
+
function readKitSubSkills(kitDir, out, warnings, depth = 0) {
|
|
12447
|
+
const base = path61.join(kitDir, "skills");
|
|
12448
|
+
if (!isDir(base)) return;
|
|
12449
|
+
const walk = (dir, d) => {
|
|
12450
|
+
if (d > MAX_DEPTH) return;
|
|
12451
|
+
for (const entry of fs53.readdirSync(dir, { withFileTypes: true })) {
|
|
12452
|
+
if (entry.isDirectory()) {
|
|
12453
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
12454
|
+
walk(path61.join(dir, entry.name), d + 1);
|
|
12455
|
+
} else if (entry.isFile() && entry.name === "SKILL.md") {
|
|
12456
|
+
const res = readOne(path61.join(dir, entry.name), "worker-kit-sub");
|
|
12457
|
+
if ("entry" in res) out.push(res.entry);
|
|
12458
|
+
else warnings.push(res.warning);
|
|
12459
|
+
}
|
|
12460
|
+
}
|
|
12461
|
+
};
|
|
12462
|
+
walk(base, depth);
|
|
12463
|
+
}
|
|
12464
|
+
function readSkillCatalog(opts) {
|
|
12465
|
+
const root = path61.resolve(opts.root);
|
|
12466
|
+
const entries = [];
|
|
12467
|
+
const warnings = [];
|
|
12468
|
+
if (opts.includeProjectRoot !== false) {
|
|
12469
|
+
const rootSkill = path61.join(root, "SKILL.md");
|
|
12470
|
+
if (exists(rootSkill)) {
|
|
12471
|
+
const res = readOne(rootSkill, "project-root");
|
|
12472
|
+
if ("entry" in res) entries.push(res.entry);
|
|
12473
|
+
else warnings.push(res.warning);
|
|
12474
|
+
}
|
|
12475
|
+
}
|
|
12476
|
+
if (opts.includeClaudeSkills !== false) {
|
|
12477
|
+
readSkillDirs(path61.join(root, ".claude/skills"), "claude-skills", entries, warnings);
|
|
12478
|
+
}
|
|
12479
|
+
if (opts.includeWorkerKits !== false) {
|
|
12480
|
+
const kitsDir = path61.join(root, "cli/assets/worker-kits");
|
|
12481
|
+
readSkillDirs(kitsDir, "worker-kit", entries, warnings);
|
|
12482
|
+
if (isDir(kitsDir)) {
|
|
12483
|
+
for (const kit of fs53.readdirSync(kitsDir).sort()) {
|
|
12484
|
+
const kitPath = path61.join(kitsDir, kit);
|
|
12485
|
+
if (!isDir(kitPath)) continue;
|
|
12486
|
+
readKitSubSkills(kitPath, entries, warnings);
|
|
12487
|
+
}
|
|
12488
|
+
}
|
|
12489
|
+
}
|
|
12490
|
+
if (opts.includeForkRootSkills !== false) {
|
|
12491
|
+
readKitSubSkills(root, entries, warnings);
|
|
12492
|
+
}
|
|
12493
|
+
const catalog = {
|
|
12494
|
+
version: 1,
|
|
12495
|
+
skills: entries.map((e) => e.manifest),
|
|
12496
|
+
readAt: Date.now(),
|
|
12497
|
+
root
|
|
12498
|
+
};
|
|
12499
|
+
return { catalog, entries, warnings };
|
|
12500
|
+
}
|
|
12501
|
+
var MAX_DEPTH, SKIP_DIRS;
|
|
12502
|
+
var init_catalog2 = __esm({
|
|
12503
|
+
"src/skills/catalog.ts"() {
|
|
12504
|
+
"use strict";
|
|
12505
|
+
init_frontmatter();
|
|
12506
|
+
MAX_DEPTH = 6;
|
|
12507
|
+
SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
12508
|
+
"node_modules",
|
|
12509
|
+
".git",
|
|
12510
|
+
"dist",
|
|
12511
|
+
"build",
|
|
12512
|
+
".next",
|
|
12513
|
+
".vite",
|
|
12514
|
+
".pnpm-store",
|
|
12515
|
+
"coverage",
|
|
12516
|
+
".growthub-fork"
|
|
12517
|
+
]);
|
|
12518
|
+
}
|
|
12519
|
+
});
|
|
12520
|
+
|
|
12106
12521
|
// src/commands/source-import-discovery.ts
|
|
12107
12522
|
var source_import_discovery_exports = {};
|
|
12108
12523
|
__export(source_import_discovery_exports, {
|
|
@@ -12111,9 +12526,9 @@ __export(source_import_discovery_exports, {
|
|
|
12111
12526
|
startSourceImportFlow: () => startSourceImportFlow
|
|
12112
12527
|
});
|
|
12113
12528
|
import * as p33 from "@clack/prompts";
|
|
12114
|
-
import
|
|
12115
|
-
import
|
|
12116
|
-
import
|
|
12529
|
+
import pc48 from "picocolors";
|
|
12530
|
+
import fs58 from "node:fs";
|
|
12531
|
+
import path65 from "node:path";
|
|
12117
12532
|
import { pathToFileURL as pathToFileURL4 } from "node:url";
|
|
12118
12533
|
function slugifyWorkspaceName(input) {
|
|
12119
12534
|
const slug = input.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -12141,10 +12556,10 @@ async function promptForInteractiveWorkspacePath(input) {
|
|
|
12141
12556
|
});
|
|
12142
12557
|
if (p33.isCancel(raw) || !raw) return null;
|
|
12143
12558
|
const trimmed = String(raw).trim();
|
|
12144
|
-
const expanded = trimmed.startsWith("~/") ?
|
|
12145
|
-
const resolved =
|
|
12146
|
-
if (
|
|
12147
|
-
const finalPath =
|
|
12559
|
+
const expanded = trimmed.startsWith("~/") ? path65.join(process.env.HOME ?? "~", trimmed.slice(2)) : trimmed;
|
|
12560
|
+
const resolved = path65.resolve(expanded);
|
|
12561
|
+
if (fs58.existsSync(resolved) && fs58.statSync(resolved).isDirectory()) {
|
|
12562
|
+
const finalPath = path65.join(resolved, suggestedName);
|
|
12148
12563
|
p33.note(
|
|
12149
12564
|
[
|
|
12150
12565
|
`You selected an existing folder: ${resolved}`,
|
|
@@ -12320,14 +12735,14 @@ async function startSkillsSourceImportFlow() {
|
|
|
12320
12735
|
function renderSuccess(result, jobId) {
|
|
12321
12736
|
const sourceLine = result.source.kind === "github-repo" ? `${result.source.repo.owner}/${result.source.repo.repo}` : `${result.source.skillId}@${result.source.version}`;
|
|
12322
12737
|
p33.outro(
|
|
12323
|
-
`Imported ${sourceLine} into ${
|
|
12324
|
-
jobId: ${
|
|
12325
|
-
forkId: ${
|
|
12738
|
+
`Imported ${sourceLine} into ${pc48.cyan(result.forkPath)}
|
|
12739
|
+
jobId: ${pc48.cyan(jobId)}
|
|
12740
|
+
forkId: ${pc48.cyan(result.forkId)}
|
|
12326
12741
|
risk: ${result.security.riskClass} (${result.security.findings.length} findings)
|
|
12327
12742
|
detection: framework=${result.detection.framework} pm=${result.detection.packageManager}
|
|
12328
12743
|
open: ${folderOpenLabel3(result.forkPath)}
|
|
12329
|
-
summary: ${
|
|
12330
|
-
manifest: ${
|
|
12744
|
+
summary: ${pc48.dim(result.summaryPath)}
|
|
12745
|
+
manifest: ${pc48.dim(result.manifestPath)}`
|
|
12331
12746
|
);
|
|
12332
12747
|
}
|
|
12333
12748
|
async function confirmTwice(job) {
|
|
@@ -12414,9 +12829,9 @@ var init_source_import_discovery = __esm({
|
|
|
12414
12829
|
// src/index.ts
|
|
12415
12830
|
import { Command } from "commander";
|
|
12416
12831
|
import * as p34 from "@clack/prompts";
|
|
12417
|
-
import
|
|
12418
|
-
import
|
|
12419
|
-
import
|
|
12832
|
+
import pc49 from "picocolors";
|
|
12833
|
+
import fs59 from "node:fs";
|
|
12834
|
+
import path66 from "node:path";
|
|
12420
12835
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
12421
12836
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
12422
12837
|
|
|
@@ -13217,8 +13632,8 @@ function printItemCompleted(item) {
|
|
|
13217
13632
|
const changes = Array.isArray(item.changes) ? item.changes : [];
|
|
13218
13633
|
const entries = changes.map((changeRaw) => asRecord(changeRaw)).filter((change) => Boolean(change)).map((change) => {
|
|
13219
13634
|
const kind = asString(change.kind, "update");
|
|
13220
|
-
const
|
|
13221
|
-
return `${kind} ${
|
|
13635
|
+
const path67 = asString(change.path, "unknown");
|
|
13636
|
+
return `${kind} ${path67}`;
|
|
13222
13637
|
});
|
|
13223
13638
|
const preview = entries.length > 0 ? entries.slice(0, 6).join(", ") : "none";
|
|
13224
13639
|
const more = entries.length > 6 ? ` (+${entries.length - 6} more)` : "";
|
|
@@ -14505,10 +14920,10 @@ async function heartbeatRun(opts) {
|
|
|
14505
14920
|
for (const event of Array.isArray(events) ? events : []) {
|
|
14506
14921
|
handleEvent(event);
|
|
14507
14922
|
}
|
|
14508
|
-
const
|
|
14923
|
+
const runList2 = await api.get(
|
|
14509
14924
|
`/api/companies/${agent.companyId}/heartbeat-runs?agentId=${agent.id}`
|
|
14510
14925
|
) || [];
|
|
14511
|
-
const currentRun =
|
|
14926
|
+
const currentRun = runList2.find((r) => r && r.id === activeRunId) ?? null;
|
|
14512
14927
|
if (!currentRun) {
|
|
14513
14928
|
console.error(pc18.red("Heartbeat run disappeared"));
|
|
14514
14929
|
break;
|
|
@@ -16052,8 +16467,8 @@ function registerIssueCommands(program2) {
|
|
|
16052
16467
|
if (opts.assigneeAgentId) params.set("assigneeAgentId", opts.assigneeAgentId);
|
|
16053
16468
|
if (opts.projectId) params.set("projectId", opts.projectId);
|
|
16054
16469
|
const query = params.toString();
|
|
16055
|
-
const
|
|
16056
|
-
const rows = await ctx.api.get(
|
|
16470
|
+
const path67 = `/api/companies/${ctx.companyId}/issues${query ? `?${query}` : ""}`;
|
|
16471
|
+
const rows = await ctx.api.get(path67) ?? [];
|
|
16057
16472
|
const filtered = filterIssueRows(rows, opts.match);
|
|
16058
16473
|
if (ctx.json) {
|
|
16059
16474
|
printOutput(filtered, { json: true });
|
|
@@ -16659,8 +17074,8 @@ function registerActivityCommands(program2) {
|
|
|
16659
17074
|
if (opts.entityType) params.set("entityType", opts.entityType);
|
|
16660
17075
|
if (opts.entityId) params.set("entityId", opts.entityId);
|
|
16661
17076
|
const query = params.toString();
|
|
16662
|
-
const
|
|
16663
|
-
const rows = await ctx.api.get(
|
|
17077
|
+
const path67 = `/api/companies/${ctx.companyId}/activity${query ? `?${query}` : ""}`;
|
|
17078
|
+
const rows = await ctx.api.get(path67) ?? [];
|
|
16664
17079
|
if (ctx.json) {
|
|
16665
17080
|
printOutput(rows, { json: true });
|
|
16666
17081
|
return;
|
|
@@ -22836,31 +23251,31 @@ function resolveManifestBaseUrl(opts = {}) {
|
|
|
22836
23251
|
function isRecord(value) {
|
|
22837
23252
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22838
23253
|
}
|
|
22839
|
-
function assertRecord(value,
|
|
23254
|
+
function assertRecord(value, path67) {
|
|
22840
23255
|
if (!isRecord(value)) {
|
|
22841
|
-
throw new ManifestMalformedError(`Expected object at \`${
|
|
23256
|
+
throw new ManifestMalformedError(`Expected object at \`${path67}\`.`);
|
|
22842
23257
|
}
|
|
22843
23258
|
return value;
|
|
22844
23259
|
}
|
|
22845
|
-
function assertArray(value,
|
|
23260
|
+
function assertArray(value, path67) {
|
|
22846
23261
|
if (!Array.isArray(value)) {
|
|
22847
|
-
throw new ManifestMalformedError(`Expected array at \`${
|
|
23262
|
+
throw new ManifestMalformedError(`Expected array at \`${path67}\`.`);
|
|
22848
23263
|
}
|
|
22849
23264
|
return value;
|
|
22850
23265
|
}
|
|
22851
|
-
function assertString(value,
|
|
23266
|
+
function assertString(value, path67) {
|
|
22852
23267
|
if (typeof value !== "string" || value.length === 0) {
|
|
22853
|
-
throw new ManifestMalformedError(`Expected non-empty string at \`${
|
|
23268
|
+
throw new ManifestMalformedError(`Expected non-empty string at \`${path67}\`.`);
|
|
22854
23269
|
}
|
|
22855
23270
|
return value;
|
|
22856
23271
|
}
|
|
22857
|
-
function normalizeProvenance(value,
|
|
22858
|
-
const record = assertRecord(value,
|
|
22859
|
-
const originType = assertString(record.originType, `${
|
|
23272
|
+
function normalizeProvenance(value, path67) {
|
|
23273
|
+
const record = assertRecord(value, path67);
|
|
23274
|
+
const originType = assertString(record.originType, `${path67}.originType`);
|
|
22860
23275
|
const allowed = ["hosted", "local-extension", "derived-from-workflow"];
|
|
22861
23276
|
if (!allowed.includes(originType)) {
|
|
22862
23277
|
throw new ManifestMalformedError(
|
|
22863
|
-
`Unknown provenance originType at \`${
|
|
23278
|
+
`Unknown provenance originType at \`${path67}.originType\`: ${originType}`
|
|
22864
23279
|
);
|
|
22865
23280
|
}
|
|
22866
23281
|
return {
|
|
@@ -31510,14 +31925,303 @@ function registerStarterCommands(program2) {
|
|
|
31510
31925
|
});
|
|
31511
31926
|
}
|
|
31512
31927
|
|
|
31928
|
+
// src/commands/skills.ts
|
|
31929
|
+
init_catalog2();
|
|
31930
|
+
import fs55 from "node:fs";
|
|
31931
|
+
import path63 from "node:path";
|
|
31932
|
+
import pc46 from "picocolors";
|
|
31933
|
+
|
|
31934
|
+
// src/skills/session-memory.ts
|
|
31935
|
+
init_frontmatter();
|
|
31936
|
+
import fs54 from "node:fs";
|
|
31937
|
+
import path62 from "node:path";
|
|
31938
|
+
var PROJECT_MD_RELATIVE2 = ".growthub-fork/project.md";
|
|
31939
|
+
function resolveProjectMdPath(forkPath) {
|
|
31940
|
+
return path62.resolve(forkPath, PROJECT_MD_RELATIVE2);
|
|
31941
|
+
}
|
|
31942
|
+
function readSessionMemory(forkPath) {
|
|
31943
|
+
const projectMdPath = resolveProjectMdPath(forkPath);
|
|
31944
|
+
if (!fs54.existsSync(projectMdPath)) return null;
|
|
31945
|
+
const raw = fs54.readFileSync(projectMdPath, "utf8");
|
|
31946
|
+
const sizeBytes = Buffer.byteLength(raw, "utf8");
|
|
31947
|
+
try {
|
|
31948
|
+
const { frontmatter, body } = readFrontmatter(raw);
|
|
31949
|
+
return { path: projectMdPath, frontmatter, body, sizeBytes };
|
|
31950
|
+
} catch {
|
|
31951
|
+
const split = splitFrontmatter(raw);
|
|
31952
|
+
return { path: projectMdPath, frontmatter: null, body: split.body, sizeBytes };
|
|
31953
|
+
}
|
|
31954
|
+
}
|
|
31955
|
+
|
|
31956
|
+
// src/commands/skills.ts
|
|
31957
|
+
init_scaffold_session_memory();
|
|
31958
|
+
init_fork_trace();
|
|
31959
|
+
init_kit_forks_home();
|
|
31960
|
+
init_table_renderer();
|
|
31961
|
+
function readLocalForkHead(forkPath) {
|
|
31962
|
+
const p35 = resolveInForkRegistrationPath(forkPath);
|
|
31963
|
+
if (!fs55.existsSync(p35)) return null;
|
|
31964
|
+
try {
|
|
31965
|
+
const parsed = JSON.parse(fs55.readFileSync(p35, "utf8"));
|
|
31966
|
+
if (typeof parsed.forkId !== "string" || typeof parsed.kitId !== "string") return null;
|
|
31967
|
+
return { forkId: parsed.forkId, kitId: parsed.kitId };
|
|
31968
|
+
} catch {
|
|
31969
|
+
return null;
|
|
31970
|
+
}
|
|
31971
|
+
}
|
|
31972
|
+
function resolveRoot(optRoot) {
|
|
31973
|
+
if (optRoot) return path63.resolve(optRoot);
|
|
31974
|
+
return process.cwd();
|
|
31975
|
+
}
|
|
31976
|
+
function runList(opts) {
|
|
31977
|
+
const result = readSkillCatalog({ root: resolveRoot(opts.root) });
|
|
31978
|
+
if (opts.json) {
|
|
31979
|
+
console.log(JSON.stringify(
|
|
31980
|
+
{
|
|
31981
|
+
version: result.catalog.version,
|
|
31982
|
+
root: result.catalog.root,
|
|
31983
|
+
skills: result.entries.map((e) => ({
|
|
31984
|
+
...e.manifest,
|
|
31985
|
+
skillPath: e.skillPath
|
|
31986
|
+
})),
|
|
31987
|
+
warnings: result.warnings
|
|
31988
|
+
},
|
|
31989
|
+
null,
|
|
31990
|
+
2
|
|
31991
|
+
));
|
|
31992
|
+
return;
|
|
31993
|
+
}
|
|
31994
|
+
if (result.entries.length === 0) {
|
|
31995
|
+
console.log(pc46.dim(`No SKILL.md found under ${result.catalog.root}`));
|
|
31996
|
+
return;
|
|
31997
|
+
}
|
|
31998
|
+
const rows = result.entries.map((e) => ({
|
|
31999
|
+
name: e.manifest.name,
|
|
32000
|
+
source: e.source,
|
|
32001
|
+
skillPath: path63.relative(result.catalog.root ?? "", e.skillPath),
|
|
32002
|
+
triggers: e.manifest.triggers?.length ?? 0,
|
|
32003
|
+
helpers: e.manifest.helpers?.length ?? 0,
|
|
32004
|
+
subSkills: e.manifest.subSkills?.length ?? 0,
|
|
32005
|
+
maxRetries: e.manifest.selfEval ? String(e.manifest.selfEval.maxRetries) : "\u2014"
|
|
32006
|
+
}));
|
|
32007
|
+
console.log(renderTable({
|
|
32008
|
+
columns: [
|
|
32009
|
+
{ key: "name", label: "name", maxWidth: 42 },
|
|
32010
|
+
{ key: "source", label: "source" },
|
|
32011
|
+
{ key: "skillPath", label: "path", maxWidth: 60 },
|
|
32012
|
+
{ key: "triggers", label: "trg", align: "right" },
|
|
32013
|
+
{ key: "helpers", label: "hlp", align: "right" },
|
|
32014
|
+
{ key: "subSkills", label: "sub", align: "right" },
|
|
32015
|
+
{ key: "maxRetries", label: "maxR", align: "right" }
|
|
32016
|
+
],
|
|
32017
|
+
rows
|
|
32018
|
+
}));
|
|
32019
|
+
if (result.warnings.length > 0) {
|
|
32020
|
+
console.log("");
|
|
32021
|
+
console.log(pc46.yellow(`${result.warnings.length} warning(s):`));
|
|
32022
|
+
for (const w of result.warnings) {
|
|
32023
|
+
console.log(pc46.yellow(` - ${path63.relative(result.catalog.root ?? "", w.skillPath)}: ${w.reason}`));
|
|
32024
|
+
}
|
|
32025
|
+
}
|
|
32026
|
+
}
|
|
32027
|
+
function runValidate(opts) {
|
|
32028
|
+
const root = resolveRoot(opts.root);
|
|
32029
|
+
const result = readSkillCatalog({ root });
|
|
32030
|
+
const issues2 = [];
|
|
32031
|
+
for (const w of result.warnings) {
|
|
32032
|
+
issues2.push({ skillPath: w.skillPath, reason: w.reason, severity: "error" });
|
|
32033
|
+
}
|
|
32034
|
+
for (const entry of result.entries) {
|
|
32035
|
+
const m = entry.manifest;
|
|
32036
|
+
if (m.name.length > 64) {
|
|
32037
|
+
issues2.push({
|
|
32038
|
+
skillPath: entry.skillPath,
|
|
32039
|
+
reason: `name length ${m.name.length} exceeds 64-char limit`,
|
|
32040
|
+
severity: "error"
|
|
32041
|
+
});
|
|
32042
|
+
}
|
|
32043
|
+
if (m.description.length > 1024) {
|
|
32044
|
+
issues2.push({
|
|
32045
|
+
skillPath: entry.skillPath,
|
|
32046
|
+
reason: `description length ${m.description.length} exceeds 1024-char limit`,
|
|
32047
|
+
severity: "error"
|
|
32048
|
+
});
|
|
32049
|
+
}
|
|
32050
|
+
const skillDir = path63.dirname(entry.skillPath);
|
|
32051
|
+
for (const helper of m.helpers ?? []) {
|
|
32052
|
+
const helperPath = path63.resolve(skillDir, helper.path);
|
|
32053
|
+
if (!fs55.existsSync(helperPath)) {
|
|
32054
|
+
issues2.push({
|
|
32055
|
+
skillPath: entry.skillPath,
|
|
32056
|
+
reason: `helpers[].path missing on disk: ${helper.path}`,
|
|
32057
|
+
severity: "warning"
|
|
32058
|
+
});
|
|
32059
|
+
}
|
|
32060
|
+
}
|
|
32061
|
+
for (const sub of m.subSkills ?? []) {
|
|
32062
|
+
const subPath = path63.resolve(skillDir, sub.path);
|
|
32063
|
+
if (!fs55.existsSync(subPath)) {
|
|
32064
|
+
issues2.push({
|
|
32065
|
+
skillPath: entry.skillPath,
|
|
32066
|
+
reason: `subSkills[].path missing on disk: ${sub.path}`,
|
|
32067
|
+
severity: "error"
|
|
32068
|
+
});
|
|
32069
|
+
}
|
|
32070
|
+
}
|
|
32071
|
+
if (m.selfEval && (m.selfEval.maxRetries < 1 || m.selfEval.maxRetries > 10)) {
|
|
32072
|
+
issues2.push({
|
|
32073
|
+
skillPath: entry.skillPath,
|
|
32074
|
+
reason: `selfEval.maxRetries ${m.selfEval.maxRetries} outside recommended 1..10 range`,
|
|
32075
|
+
severity: "warning"
|
|
32076
|
+
});
|
|
32077
|
+
}
|
|
32078
|
+
}
|
|
32079
|
+
if (opts.json) {
|
|
32080
|
+
console.log(JSON.stringify(
|
|
32081
|
+
{
|
|
32082
|
+
root,
|
|
32083
|
+
skillsChecked: result.entries.length,
|
|
32084
|
+
issues: issues2,
|
|
32085
|
+
ok: issues2.filter((i) => i.severity === "error").length === 0
|
|
32086
|
+
},
|
|
32087
|
+
null,
|
|
32088
|
+
2
|
|
32089
|
+
));
|
|
32090
|
+
process.exitCode = issues2.filter((i) => i.severity === "error").length === 0 ? 0 : 1;
|
|
32091
|
+
return;
|
|
32092
|
+
}
|
|
32093
|
+
console.log(pc46.bold(`Validated ${result.entries.length} skill(s) under ${root}`));
|
|
32094
|
+
if (issues2.length === 0) {
|
|
32095
|
+
console.log(pc46.green("OK \u2014 no issues."));
|
|
32096
|
+
return;
|
|
32097
|
+
}
|
|
32098
|
+
for (const issue of issues2) {
|
|
32099
|
+
const rel = path63.relative(root, issue.skillPath);
|
|
32100
|
+
const tag = issue.severity === "error" ? pc46.red("[error] ") : pc46.yellow("[warning]");
|
|
32101
|
+
console.log(`${tag} ${rel}: ${issue.reason}`);
|
|
32102
|
+
}
|
|
32103
|
+
const errors = issues2.filter((i) => i.severity === "error").length;
|
|
32104
|
+
if (errors > 0) process.exitCode = 1;
|
|
32105
|
+
}
|
|
32106
|
+
function runSessionInit(opts) {
|
|
32107
|
+
const forkPath = resolveRoot(opts.fork);
|
|
32108
|
+
let kitId = opts.kit?.trim() ?? "";
|
|
32109
|
+
let forkId = "unknown";
|
|
32110
|
+
const forkHead = readLocalForkHead(forkPath);
|
|
32111
|
+
if (forkHead) {
|
|
32112
|
+
forkId = forkHead.forkId;
|
|
32113
|
+
if (!kitId) kitId = forkHead.kitId;
|
|
32114
|
+
}
|
|
32115
|
+
if (!kitId) {
|
|
32116
|
+
const err = "Pass --kit <id>, or run this inside a registered fork (`.growthub-fork/fork.json`).";
|
|
32117
|
+
if (opts.json) {
|
|
32118
|
+
console.log(JSON.stringify({ status: "error", error: err }));
|
|
32119
|
+
process.exitCode = 1;
|
|
32120
|
+
} else {
|
|
32121
|
+
console.error(pc46.red(err));
|
|
32122
|
+
process.exitCode = 1;
|
|
32123
|
+
}
|
|
32124
|
+
return;
|
|
32125
|
+
}
|
|
32126
|
+
const result = scaffoldSessionMemory({
|
|
32127
|
+
forkPath,
|
|
32128
|
+
kitId,
|
|
32129
|
+
forkId,
|
|
32130
|
+
source: "skills-session-init",
|
|
32131
|
+
sourceRef: ""
|
|
32132
|
+
});
|
|
32133
|
+
if (result.written && forkHead) {
|
|
32134
|
+
appendKitForkTraceEvent(forkPath, {
|
|
32135
|
+
forkId,
|
|
32136
|
+
kitId,
|
|
32137
|
+
type: "skills_scaffolded",
|
|
32138
|
+
summary: "Seeded .growthub-fork/project.md via 'growthub skills session init'",
|
|
32139
|
+
detail: { projectMd: result.projectMdPath }
|
|
32140
|
+
});
|
|
32141
|
+
}
|
|
32142
|
+
if (opts.json) {
|
|
32143
|
+
console.log(JSON.stringify(
|
|
32144
|
+
{ status: result.written ? "ok" : "already-initialised", ...result },
|
|
32145
|
+
null,
|
|
32146
|
+
2
|
|
32147
|
+
));
|
|
32148
|
+
return;
|
|
32149
|
+
}
|
|
32150
|
+
if (result.written) {
|
|
32151
|
+
console.log(pc46.green(`Seeded ${path63.relative(forkPath, result.projectMdPath)}`));
|
|
32152
|
+
} else if (!result.templatePath) {
|
|
32153
|
+
console.log(pc46.yellow(
|
|
32154
|
+
`Kit tree does not ship templates/project.md \u2014 this kit has not been upgraded to the v1.2 primitives yet. No seed written; session memory can still be maintained manually.`
|
|
32155
|
+
));
|
|
32156
|
+
} else {
|
|
32157
|
+
console.log(pc46.dim(`${path63.relative(forkPath, result.projectMdPath)} already present; left untouched.`));
|
|
32158
|
+
}
|
|
32159
|
+
}
|
|
32160
|
+
function runSessionShow(opts) {
|
|
32161
|
+
const forkPath = resolveRoot(opts.fork);
|
|
32162
|
+
const head = readSessionMemory(forkPath);
|
|
32163
|
+
if (!head) {
|
|
32164
|
+
const err = `No session memory at ${resolveProjectMdPath(forkPath)}. Run 'growthub skills session init'.`;
|
|
32165
|
+
if (opts.json) {
|
|
32166
|
+
console.log(JSON.stringify({ status: "missing", projectMdPath: resolveProjectMdPath(forkPath) }));
|
|
32167
|
+
process.exitCode = 1;
|
|
32168
|
+
return;
|
|
32169
|
+
}
|
|
32170
|
+
console.error(pc46.yellow(err));
|
|
32171
|
+
process.exitCode = 1;
|
|
32172
|
+
return;
|
|
32173
|
+
}
|
|
32174
|
+
if (opts.json) {
|
|
32175
|
+
console.log(JSON.stringify(
|
|
32176
|
+
{
|
|
32177
|
+
path: head.path,
|
|
32178
|
+
sizeBytes: head.sizeBytes,
|
|
32179
|
+
frontmatter: head.frontmatter,
|
|
32180
|
+
body: opts.body ? head.body : void 0
|
|
32181
|
+
},
|
|
32182
|
+
null,
|
|
32183
|
+
2
|
|
32184
|
+
));
|
|
32185
|
+
return;
|
|
32186
|
+
}
|
|
32187
|
+
console.log(pc46.bold(head.path));
|
|
32188
|
+
console.log(pc46.dim(`${head.sizeBytes} bytes`));
|
|
32189
|
+
if (head.frontmatter) {
|
|
32190
|
+
for (const [k, v] of Object.entries(head.frontmatter)) {
|
|
32191
|
+
if (Array.isArray(v)) {
|
|
32192
|
+
console.log(` ${k}: ${v.length === 0 ? "[]" : `[${v.length} entries]`}`);
|
|
32193
|
+
} else if (typeof v === "object" && v !== null) {
|
|
32194
|
+
console.log(` ${k}: { ${Object.keys(v).join(", ")} }`);
|
|
32195
|
+
} else {
|
|
32196
|
+
console.log(` ${k}: ${String(v)}`);
|
|
32197
|
+
}
|
|
32198
|
+
}
|
|
32199
|
+
}
|
|
32200
|
+
if (opts.body) {
|
|
32201
|
+
console.log("");
|
|
32202
|
+
console.log(head.body);
|
|
32203
|
+
} else {
|
|
32204
|
+
console.log("");
|
|
32205
|
+
console.log(pc46.dim("(pass --body to print the markdown body)"));
|
|
32206
|
+
}
|
|
32207
|
+
}
|
|
32208
|
+
function registerSkillsCommands(program2) {
|
|
32209
|
+
const skills = program2.command("skills").description("Discovery + session memory for SKILL.md + .growthub-fork/project.md");
|
|
32210
|
+
skills.command("list").description("Enumerate every SKILL.md reachable from the current tree").option("--root <path>", "Override the root to scan (default: cwd)").option("--json", "Emit machine-readable JSON").action((opts) => runList(opts));
|
|
32211
|
+
skills.command("validate").description("Strict frontmatter + helper/sub-skill path check; non-zero exit on error").option("--root <path>", "Override the root to scan (default: cwd)").option("--json", "Emit machine-readable JSON").action((opts) => runValidate(opts));
|
|
32212
|
+
const session = skills.command("session").description("Read / seed the fork's session memory (.growthub-fork/project.md)");
|
|
32213
|
+
session.command("init").description("Seed .growthub-fork/project.md from the kit's templates/project.md").option("--fork <path>", "Fork root (default: cwd)").option("--kit <id>", "Explicit kit id; read from fork.json when omitted inside a registered fork").option("--json", "Emit machine-readable JSON").action((opts) => runSessionInit(opts));
|
|
32214
|
+
session.command("show").description("Print the session-memory head for a fork").option("--fork <path>", "Fork root (default: cwd)").option("--body", "Also print the markdown body").option("--json", "Emit machine-readable JSON").action((opts) => runSessionShow(opts));
|
|
32215
|
+
}
|
|
32216
|
+
|
|
31513
32217
|
// src/commands/fleet.ts
|
|
31514
32218
|
init_fork_registry();
|
|
31515
32219
|
import * as p32 from "@clack/prompts";
|
|
31516
|
-
import
|
|
32220
|
+
import pc47 from "picocolors";
|
|
31517
32221
|
|
|
31518
32222
|
// src/fleet/summary.ts
|
|
31519
32223
|
init_fork_registry();
|
|
31520
|
-
import
|
|
32224
|
+
import fs56 from "node:fs";
|
|
31521
32225
|
init_fork_policy();
|
|
31522
32226
|
init_fork_trace();
|
|
31523
32227
|
function classifyHealth(drift, pendingConfirmationJobs, lastJobStatus) {
|
|
@@ -31537,7 +32241,7 @@ var REMOTE_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
31537
32241
|
"conflict_encountered"
|
|
31538
32242
|
]);
|
|
31539
32243
|
function buildForkSummary(reg) {
|
|
31540
|
-
if (!
|
|
32244
|
+
if (!fs56.existsSync(reg.forkPath)) {
|
|
31541
32245
|
return {
|
|
31542
32246
|
forkId: reg.forkId,
|
|
31543
32247
|
kitId: reg.kitId,
|
|
@@ -31846,17 +32550,17 @@ init_fork_policy();
|
|
|
31846
32550
|
function healthGlyph(level) {
|
|
31847
32551
|
switch (level) {
|
|
31848
32552
|
case "clean":
|
|
31849
|
-
return
|
|
32553
|
+
return pc47.green("\u25CF");
|
|
31850
32554
|
case "drift-minor":
|
|
31851
|
-
return
|
|
32555
|
+
return pc47.cyan("\u25CF");
|
|
31852
32556
|
case "drift-major":
|
|
31853
|
-
return
|
|
32557
|
+
return pc47.yellow("\u25CF");
|
|
31854
32558
|
case "awaiting-confirmation":
|
|
31855
|
-
return
|
|
32559
|
+
return pc47.magenta("\u25D0");
|
|
31856
32560
|
case "error":
|
|
31857
|
-
return
|
|
32561
|
+
return pc47.red("\u25CF");
|
|
31858
32562
|
default:
|
|
31859
|
-
return
|
|
32563
|
+
return pc47.dim("\u25CB");
|
|
31860
32564
|
}
|
|
31861
32565
|
}
|
|
31862
32566
|
function truncate4(s, n) {
|
|
@@ -31870,7 +32574,7 @@ async function fleetView(opts) {
|
|
|
31870
32574
|
return;
|
|
31871
32575
|
}
|
|
31872
32576
|
p32.log.message(
|
|
31873
|
-
`Fleet: ${
|
|
32577
|
+
`Fleet: ${pc47.cyan(String(fleet.totalForks))} fork(s) | remote=${fleet.forksWithRemote} awaiting=${fleet.forksAwaitingConfirmation} pending-approvals=${fleet.pendingApprovalCount}`
|
|
31874
32578
|
);
|
|
31875
32579
|
p32.log.message(
|
|
31876
32580
|
` Health \u2192 clean=${fleet.byHealth.clean} drift-minor=${fleet.byHealth["drift-minor"]} drift-major=${fleet.byHealth["drift-major"]} awaiting=${fleet.byHealth["awaiting-confirmation"]} error=${fleet.byHealth.error} unknown=${fleet.byHealth.unknown}`
|
|
@@ -31887,10 +32591,10 @@ function renderForkRow(f) {
|
|
|
31887
32591
|
const base = f.baseVersion.padEnd(8);
|
|
31888
32592
|
const upstream = (f.upstreamVersion ?? "?").padEnd(8);
|
|
31889
32593
|
const driftCounts = `files=${f.fileDriftCount} pkgs=${f.packageDriftCount}`;
|
|
31890
|
-
const pending = f.pendingConfirmationJobs > 0 ?
|
|
31891
|
-
const remote = f.remote ?
|
|
32594
|
+
const pending = f.pendingConfirmationJobs > 0 ? pc47.magenta(` awaits=${f.pendingConfirmationJobs}`) : "";
|
|
32595
|
+
const remote = f.remote ? pc47.dim(` ${f.remote.owner}/${f.remote.repo}`) : "";
|
|
31892
32596
|
p32.log.message(
|
|
31893
|
-
` ${healthGlyph(f.health)} ${label} ${
|
|
32597
|
+
` ${healthGlyph(f.health)} ${label} ${pc47.dim(kit)} ${base} \u2192 ${upstream} ${pc47.dim(driftCounts)}${pending}${remote}`
|
|
31894
32598
|
);
|
|
31895
32599
|
}
|
|
31896
32600
|
async function fleetDrift(opts) {
|
|
@@ -31905,7 +32609,7 @@ async function fleetDrift(opts) {
|
|
|
31905
32609
|
return;
|
|
31906
32610
|
}
|
|
31907
32611
|
p32.log.message(
|
|
31908
|
-
`Fleet drift: ${
|
|
32612
|
+
`Fleet drift: ${pc47.cyan(String(withDrift.length))} of ${fleet.totalForks} fork(s) have drift.`
|
|
31909
32613
|
);
|
|
31910
32614
|
p32.log.message(
|
|
31911
32615
|
` By severity \u2192 none=${fleet.bySeverity.none} info=${fleet.bySeverity.info} warning=${fleet.bySeverity.warning} critical=${fleet.bySeverity.critical}`
|
|
@@ -31925,7 +32629,7 @@ async function fleetDriftSummary(opts) {
|
|
|
31925
32629
|
console.log(JSON.stringify({ summary, narrative }, null, 2));
|
|
31926
32630
|
return;
|
|
31927
32631
|
}
|
|
31928
|
-
p32.log.message(
|
|
32632
|
+
p32.log.message(pc47.cyan(`Drift summary \u2014 ${reg.forkId} (${summary.fromVersion} \u2192 ${summary.toVersion})`));
|
|
31929
32633
|
for (const line of narrative) p32.log.message(` ${line}`);
|
|
31930
32634
|
const sections = [
|
|
31931
32635
|
["safe additions", summary.buckets.safeAdditions],
|
|
@@ -31938,13 +32642,13 @@ async function fleetDriftSummary(opts) {
|
|
|
31938
32642
|
];
|
|
31939
32643
|
for (const [label, items] of sections) {
|
|
31940
32644
|
if (items.length === 0) continue;
|
|
31941
|
-
p32.log.message(
|
|
31942
|
-
for (const item of items) p32.log.message(` \xB7 ${item.path} ${
|
|
32645
|
+
p32.log.message(pc47.dim(` \u2014 ${label} (${items.length}) \u2014`));
|
|
32646
|
+
for (const item of items) p32.log.message(` \xB7 ${item.path} ${pc47.dim(item.note)}`);
|
|
31943
32647
|
}
|
|
31944
32648
|
if (summary.buckets.packageAdditions.length || summary.buckets.packageUpgrades.length) {
|
|
31945
|
-
p32.log.message(
|
|
32649
|
+
p32.log.message(pc47.dim(` \u2014 dependency drift \u2014`));
|
|
31946
32650
|
for (const d of summary.buckets.packageAdditions) {
|
|
31947
|
-
p32.log.message(` + ${d.packageName}@${d.toVersion} ${
|
|
32651
|
+
p32.log.message(` + ${d.packageName}@${d.toVersion} ${pc47.dim("(added upstream)")}`);
|
|
31948
32652
|
}
|
|
31949
32653
|
for (const d of summary.buckets.packageUpgrades) {
|
|
31950
32654
|
p32.log.message(` \u2191 ${d.packageName} ${d.fromVersion ?? "?"} \u2192 ${d.toVersion}`);
|
|
@@ -31970,14 +32674,14 @@ async function fleetPolicy(opts) {
|
|
|
31970
32674
|
console.log(JSON.stringify({ count: rows.length, rows }, null, 2));
|
|
31971
32675
|
return;
|
|
31972
32676
|
}
|
|
31973
|
-
p32.log.message(
|
|
32677
|
+
p32.log.message(pc47.cyan(`Fleet policy matrix (${rows.length} fork(s))`));
|
|
31974
32678
|
for (const r of rows) {
|
|
31975
32679
|
const label = truncate4(r.label ?? r.forkId, 28).padEnd(28);
|
|
31976
32680
|
const aa = r.autoApprove.padEnd(9);
|
|
31977
32681
|
const ad = r.autoApproveDepUpdates.padEnd(9);
|
|
31978
32682
|
const rs = r.remoteSyncMode.padEnd(6);
|
|
31979
32683
|
const ut = String(r.untouchableCount).padStart(3);
|
|
31980
|
-
const remote = r.hasRemote ?
|
|
32684
|
+
const remote = r.hasRemote ? pc47.green("+") : pc47.dim("\xB7");
|
|
31981
32685
|
p32.log.message(
|
|
31982
32686
|
` ${label} autoApprove=${aa} deps=${ad} remote=${rs} untouchable=${ut} ${remote}`
|
|
31983
32687
|
);
|
|
@@ -31993,19 +32697,19 @@ async function fleetApprovals(opts) {
|
|
|
31993
32697
|
p32.log.success("Approval queue is empty.");
|
|
31994
32698
|
return;
|
|
31995
32699
|
}
|
|
31996
|
-
p32.log.message(
|
|
32700
|
+
p32.log.message(pc47.cyan(`Approval queue: ${queue.length} job(s) awaiting confirmation`));
|
|
31997
32701
|
for (const entry of queue) {
|
|
31998
32702
|
p32.log.message(
|
|
31999
|
-
` \xB7 ${
|
|
32703
|
+
` \xB7 ${pc47.cyan(entry.jobId)} fork=${entry.forkLabel ?? entry.forkId} created=${entry.createdAt.slice(0, 19)}`
|
|
32000
32704
|
);
|
|
32001
|
-
for (const
|
|
32002
|
-
p32.log.message(` ${
|
|
32705
|
+
for (const path67 of entry.pendingPaths.slice(0, 6)) {
|
|
32706
|
+
p32.log.message(` ${pc47.dim("awaits")} ${path67}`);
|
|
32003
32707
|
}
|
|
32004
32708
|
if (entry.pendingPaths.length > 6) {
|
|
32005
|
-
p32.log.message(` ${
|
|
32709
|
+
p32.log.message(` ${pc47.dim(`\u2026 +${entry.pendingPaths.length - 6} more`)}`);
|
|
32006
32710
|
}
|
|
32007
32711
|
p32.log.message(
|
|
32008
|
-
` ${
|
|
32712
|
+
` ${pc47.dim("resume:")} growthub kit fork confirm --job-id ${entry.jobId}`
|
|
32009
32713
|
);
|
|
32010
32714
|
}
|
|
32011
32715
|
}
|
|
@@ -32017,20 +32721,20 @@ async function fleetAgentPlan(opts) {
|
|
|
32017
32721
|
console.log(JSON.stringify(doc, null, 2));
|
|
32018
32722
|
return;
|
|
32019
32723
|
}
|
|
32020
|
-
p32.log.message(
|
|
32724
|
+
p32.log.message(pc47.cyan(`Agent heal plan \u2014 ${reg.forkId}`));
|
|
32021
32725
|
p32.log.message(` ${doc.summary}`);
|
|
32022
32726
|
for (const line of doc.narrative) p32.log.message(` ${line}`);
|
|
32023
32727
|
if (doc.awaitsConfirmation.length > 0) {
|
|
32024
|
-
p32.log.message(
|
|
32728
|
+
p32.log.message(pc47.magenta(` Awaiting confirmation on:`));
|
|
32025
32729
|
for (const p210 of doc.awaitsConfirmation) p32.log.message(` \xB7 ${p210}`);
|
|
32026
32730
|
p32.log.message(
|
|
32027
|
-
|
|
32731
|
+
pc47.dim(
|
|
32028
32732
|
` Next: growthub kit fork heal ${reg.forkId} (will park in awaiting_confirmation until resumed)`
|
|
32029
32733
|
)
|
|
32030
32734
|
);
|
|
32031
32735
|
} else if (doc.plan.actions.length > 0) {
|
|
32032
32736
|
p32.log.message(
|
|
32033
|
-
|
|
32737
|
+
pc47.dim(` Next: growthub kit fork heal ${reg.forkId} (${doc.plan.actions.length} safe action(s) ready)`)
|
|
32034
32738
|
);
|
|
32035
32739
|
}
|
|
32036
32740
|
}
|
|
@@ -32085,21 +32789,21 @@ var DEFAULT_MEMORY_PROVIDER_CONFIG = {
|
|
|
32085
32789
|
|
|
32086
32790
|
// src/runtime/memory/store.ts
|
|
32087
32791
|
init_home();
|
|
32088
|
-
import
|
|
32089
|
-
import
|
|
32792
|
+
import fs57 from "node:fs";
|
|
32793
|
+
import path64 from "node:path";
|
|
32090
32794
|
function toProjectSlug(project) {
|
|
32091
32795
|
return project.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "default";
|
|
32092
32796
|
}
|
|
32093
32797
|
function resolveProjectPath(project) {
|
|
32094
|
-
return
|
|
32798
|
+
return path64.resolve(resolveMemoryProjectsDir(), `${toProjectSlug(project)}.json`);
|
|
32095
32799
|
}
|
|
32096
32800
|
function loadMemoryDatabase(project) {
|
|
32097
32801
|
const filePath = resolveProjectPath(project);
|
|
32098
|
-
if (!
|
|
32802
|
+
if (!fs57.existsSync(filePath)) {
|
|
32099
32803
|
return { version: 1, project, observations: [], summaries: [], nextObservationId: 1, nextSummaryId: 1 };
|
|
32100
32804
|
}
|
|
32101
32805
|
try {
|
|
32102
|
-
const raw = JSON.parse(
|
|
32806
|
+
const raw = JSON.parse(fs57.readFileSync(filePath, "utf-8"));
|
|
32103
32807
|
return {
|
|
32104
32808
|
version: 1,
|
|
32105
32809
|
project,
|
|
@@ -32114,9 +32818,9 @@ function loadMemoryDatabase(project) {
|
|
|
32114
32818
|
}
|
|
32115
32819
|
function saveMemoryDatabase(db) {
|
|
32116
32820
|
const dir = resolveMemoryProjectsDir();
|
|
32117
|
-
|
|
32821
|
+
fs57.mkdirSync(dir, { recursive: true });
|
|
32118
32822
|
const filePath = resolveProjectPath(db.project);
|
|
32119
|
-
|
|
32823
|
+
fs57.writeFileSync(filePath, `${JSON.stringify(db, null, 2)}
|
|
32120
32824
|
`, "utf-8");
|
|
32121
32825
|
}
|
|
32122
32826
|
function addObservation(project, input) {
|
|
@@ -32210,8 +32914,8 @@ function incrementRelevanceCount(project, observationId) {
|
|
|
32210
32914
|
}
|
|
32211
32915
|
function listMemoryProjects() {
|
|
32212
32916
|
const dir = resolveMemoryProjectsDir();
|
|
32213
|
-
if (!
|
|
32214
|
-
return
|
|
32917
|
+
if (!fs57.existsSync(dir)) return [];
|
|
32918
|
+
return fs57.readdirSync(dir).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, "")).sort();
|
|
32215
32919
|
}
|
|
32216
32920
|
function getMemoryStats(project) {
|
|
32217
32921
|
const db = loadMemoryDatabase(project);
|
|
@@ -32223,15 +32927,15 @@ function getMemoryStats(project) {
|
|
|
32223
32927
|
};
|
|
32224
32928
|
}
|
|
32225
32929
|
function resolveProviderConfigPath() {
|
|
32226
|
-
return
|
|
32930
|
+
return path64.resolve(resolveMemoryDir(), "provider-config.json");
|
|
32227
32931
|
}
|
|
32228
32932
|
function readProviderConfig() {
|
|
32229
32933
|
const filePath = resolveProviderConfigPath();
|
|
32230
|
-
if (!
|
|
32934
|
+
if (!fs57.existsSync(filePath)) {
|
|
32231
32935
|
return { ...DEFAULT_MEMORY_PROVIDER_CONFIG };
|
|
32232
32936
|
}
|
|
32233
32937
|
try {
|
|
32234
|
-
const raw = JSON.parse(
|
|
32938
|
+
const raw = JSON.parse(fs57.readFileSync(filePath, "utf-8"));
|
|
32235
32939
|
return {
|
|
32236
32940
|
provider: validateProvider(raw.provider),
|
|
32237
32941
|
apiKey: typeof raw.apiKey === "string" ? raw.apiKey : void 0,
|
|
@@ -32244,9 +32948,9 @@ function readProviderConfig() {
|
|
|
32244
32948
|
}
|
|
32245
32949
|
function writeProviderConfig(config) {
|
|
32246
32950
|
const dir = resolveMemoryDir();
|
|
32247
|
-
|
|
32951
|
+
fs57.mkdirSync(dir, { recursive: true });
|
|
32248
32952
|
const filePath = resolveProviderConfigPath();
|
|
32249
|
-
|
|
32953
|
+
fs57.writeFileSync(filePath, `${JSON.stringify(config, null, 2)}
|
|
32250
32954
|
`, { mode: 384 });
|
|
32251
32955
|
}
|
|
32252
32956
|
function validateProvider(value) {
|
|
@@ -32625,14 +33329,14 @@ async function syncMemoriesToHosted(project, options) {
|
|
|
32625
33329
|
init_llm();
|
|
32626
33330
|
function resolveCliVersion() {
|
|
32627
33331
|
try {
|
|
32628
|
-
const moduleDir =
|
|
33332
|
+
const moduleDir = path66.dirname(fileURLToPath7(import.meta.url));
|
|
32629
33333
|
const candidates = [
|
|
32630
|
-
|
|
32631
|
-
|
|
33334
|
+
path66.resolve(moduleDir, "../package.json"),
|
|
33335
|
+
path66.resolve(moduleDir, "../../package.json")
|
|
32632
33336
|
];
|
|
32633
33337
|
for (const candidate of candidates) {
|
|
32634
|
-
if (!
|
|
32635
|
-
const parsed = JSON.parse(
|
|
33338
|
+
if (!fs59.existsSync(candidate)) continue;
|
|
33339
|
+
const parsed = JSON.parse(fs59.readFileSync(candidate, "utf8"));
|
|
32636
33340
|
if (parsed?.name === "@growthub/cli" && typeof parsed.version === "string") return parsed.version;
|
|
32637
33341
|
}
|
|
32638
33342
|
} catch {
|
|
@@ -32859,7 +33563,7 @@ async function runMarketingContextBuilder(baseUrl, model) {
|
|
|
32859
33563
|
});
|
|
32860
33564
|
if (p34.isCancel(projectDir)) return;
|
|
32861
33565
|
const dir = String(projectDir).trim() || process.cwd();
|
|
32862
|
-
if (!
|
|
33566
|
+
if (!fs59.existsSync(dir)) {
|
|
32863
33567
|
p34.note(`Directory not found: ${dir}`, "Marketing Context Builder");
|
|
32864
33568
|
return;
|
|
32865
33569
|
}
|
|
@@ -32895,10 +33599,10 @@ async function runMarketingContextBuilder(baseUrl, model) {
|
|
|
32895
33599
|
p34.note("Draft was not saved. You can copy it from the output above.", "Marketing Context Builder");
|
|
32896
33600
|
return;
|
|
32897
33601
|
}
|
|
32898
|
-
const outDir =
|
|
32899
|
-
|
|
32900
|
-
const outPath =
|
|
32901
|
-
|
|
33602
|
+
const outDir = path66.resolve(dir, ".agents");
|
|
33603
|
+
fs59.mkdirSync(outDir, { recursive: true });
|
|
33604
|
+
const outPath = path66.resolve(outDir, "product-marketing-context.md");
|
|
33605
|
+
fs59.writeFileSync(outPath, result.contextMarkdown, "utf-8");
|
|
32902
33606
|
p34.note(`Saved to: ${outPath}
|
|
32903
33607
|
|
|
32904
33608
|
Review the file and replace [NEEDS INPUT] placeholders with real data.`, "Marketing Context Builder");
|
|
@@ -33107,39 +33811,39 @@ function captureSessionSummary(project, sessionId, messages) {
|
|
|
33107
33811
|
}
|
|
33108
33812
|
}
|
|
33109
33813
|
function resolveLocalThreadsDir() {
|
|
33110
|
-
return
|
|
33814
|
+
return path66.resolve(resolvePaperclipHomeDir(), "native-intelligence", "threads");
|
|
33111
33815
|
}
|
|
33112
33816
|
function loadOrCreateLocalThread() {
|
|
33113
33817
|
const dir = resolveLocalThreadsDir();
|
|
33114
|
-
|
|
33115
|
-
const activePath =
|
|
33116
|
-
if (
|
|
33818
|
+
fs59.mkdirSync(dir, { recursive: true });
|
|
33819
|
+
const activePath = path66.resolve(dir, "active-thread.json");
|
|
33820
|
+
if (fs59.existsSync(activePath)) {
|
|
33117
33821
|
try {
|
|
33118
|
-
const parsed = JSON.parse(
|
|
33822
|
+
const parsed = JSON.parse(fs59.readFileSync(activePath, "utf-8"));
|
|
33119
33823
|
const id2 = typeof parsed.id === "string" && parsed.id.length > 0 ? parsed.id : `thread-${Date.now()}`;
|
|
33120
|
-
const threadFile =
|
|
33824
|
+
const threadFile = path66.resolve(dir, `${id2}.json`);
|
|
33121
33825
|
const messages = Array.isArray(parsed.messages) ? parsed.messages : [];
|
|
33122
33826
|
return { id: id2, filePath: threadFile, messages };
|
|
33123
33827
|
} catch {
|
|
33124
33828
|
}
|
|
33125
33829
|
}
|
|
33126
33830
|
const id = `thread-${Date.now()}`;
|
|
33127
|
-
const filePath =
|
|
33831
|
+
const filePath = path66.resolve(dir, `${id}.json`);
|
|
33128
33832
|
const thread = { id, filePath, messages: [] };
|
|
33129
33833
|
saveLocalThread(thread);
|
|
33130
33834
|
return thread;
|
|
33131
33835
|
}
|
|
33132
33836
|
function saveLocalThread(thread) {
|
|
33133
33837
|
const dir = resolveLocalThreadsDir();
|
|
33134
|
-
|
|
33135
|
-
|
|
33838
|
+
fs59.mkdirSync(dir, { recursive: true });
|
|
33839
|
+
fs59.writeFileSync(
|
|
33136
33840
|
thread.filePath,
|
|
33137
33841
|
`${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
|
|
33138
33842
|
`,
|
|
33139
33843
|
"utf-8"
|
|
33140
33844
|
);
|
|
33141
|
-
const activePath =
|
|
33142
|
-
|
|
33845
|
+
const activePath = path66.resolve(dir, "active-thread.json");
|
|
33846
|
+
fs59.writeFileSync(
|
|
33143
33847
|
activePath,
|
|
33144
33848
|
`${JSON.stringify({ id: thread.id, messages: thread.messages }, null, 2)}
|
|
33145
33849
|
`,
|
|
@@ -33285,7 +33989,7 @@ async function collectBindingsFromContract(contract, promptSeed) {
|
|
|
33285
33989
|
return bindings;
|
|
33286
33990
|
}
|
|
33287
33991
|
function resolveCurrentProject() {
|
|
33288
|
-
return
|
|
33992
|
+
return path66.basename(process.cwd());
|
|
33289
33993
|
}
|
|
33290
33994
|
async function runMemoryKnowledgeHub() {
|
|
33291
33995
|
const project = resolveCurrentProject();
|
|
@@ -33318,7 +34022,7 @@ async function runMemoryKnowledgeHub() {
|
|
|
33318
34022
|
},
|
|
33319
34023
|
{
|
|
33320
34024
|
value: "sync",
|
|
33321
|
-
label: syncStatus.available ? "Sync to Growthub" : "Sync to Growthub" +
|
|
34025
|
+
label: syncStatus.available ? "Sync to Growthub" : "Sync to Growthub" + pc49.dim(" (unavailable)"),
|
|
33322
34026
|
hint: syncStatus.available ? "push memories to hosted account" : syncStatus.reason
|
|
33323
34027
|
},
|
|
33324
34028
|
{ value: "__back_to_hub", label: "\u2190 Back to main menu" }
|
|
@@ -33457,7 +34161,7 @@ async function runDiscoveryHub(opts) {
|
|
|
33457
34161
|
},
|
|
33458
34162
|
{
|
|
33459
34163
|
value: "workflows",
|
|
33460
|
-
label: workflowAccess.state === "ready" ? "\u{1F517} Workflows" : "\u{1F517} Workflows" +
|
|
34164
|
+
label: workflowAccess.state === "ready" ? "\u{1F517} Workflows" : "\u{1F517} Workflows" + pc49.dim(" (locked)"),
|
|
33461
34165
|
hint: workflowAccess.state === "ready" ? "CMS contracts, dynamic pipelines, and saved workflows" : workflowAccess.reason
|
|
33462
34166
|
},
|
|
33463
34167
|
{
|
|
@@ -33480,6 +34184,11 @@ async function runDiscoveryHub(opts) {
|
|
|
33480
34184
|
label: "\u{1F4D6} Memory & Knowledge",
|
|
33481
34185
|
hint: "persistent memory, search, multi-provider config, Growthub sync"
|
|
33482
34186
|
},
|
|
34187
|
+
{
|
|
34188
|
+
value: "skills-catalog",
|
|
34189
|
+
label: "\u{1F4C7} Skills Catalog",
|
|
34190
|
+
hint: "enumerate SKILL.md across this tree + inspect .growthub-fork/project.md"
|
|
34191
|
+
},
|
|
33483
34192
|
{
|
|
33484
34193
|
value: "hosted-auth",
|
|
33485
34194
|
label: "\u{1F510} Connect Growthub Account",
|
|
@@ -33851,6 +34560,25 @@ async function runDiscoveryHub(opts) {
|
|
|
33851
34560
|
if (result2 === "back") continue;
|
|
33852
34561
|
return;
|
|
33853
34562
|
}
|
|
34563
|
+
if (surfaceChoice === "skills-catalog") {
|
|
34564
|
+
const { readSkillCatalog: readSkillCatalog2 } = await Promise.resolve().then(() => (init_catalog2(), catalog_exports));
|
|
34565
|
+
const catalog = readSkillCatalog2({ root: process.cwd() });
|
|
34566
|
+
p34.note(
|
|
34567
|
+
[
|
|
34568
|
+
`Root: ${pc49.cyan(catalog.catalog.root ?? process.cwd())}`,
|
|
34569
|
+
`Skills discovered: ${pc49.bold(String(catalog.entries.length))}`,
|
|
34570
|
+
catalog.warnings.length > 0 ? `Warnings: ${pc49.yellow(String(catalog.warnings.length))}` : `Warnings: 0`,
|
|
34571
|
+
"",
|
|
34572
|
+
"Invoke directly:",
|
|
34573
|
+
" growthub skills list --json",
|
|
34574
|
+
" growthub skills validate",
|
|
34575
|
+
" growthub skills session show",
|
|
34576
|
+
" growthub skills session init --kit <kit-id>"
|
|
34577
|
+
].join("\n"),
|
|
34578
|
+
"Skills Catalog"
|
|
34579
|
+
);
|
|
34580
|
+
continue;
|
|
34581
|
+
}
|
|
33854
34582
|
if (surfaceChoice === "hosted-auth") {
|
|
33855
34583
|
await runHostedBridgeEntry({ config: opts?.config, dataDir: opts?.dataDir });
|
|
33856
34584
|
continue;
|
|
@@ -33865,12 +34593,12 @@ function isInstallerMode() {
|
|
|
33865
34593
|
}
|
|
33866
34594
|
function listLocalSurfaces() {
|
|
33867
34595
|
const homeDir = resolvePaperclipHomeDir();
|
|
33868
|
-
const instancesDir =
|
|
33869
|
-
if (!
|
|
33870
|
-
return
|
|
34596
|
+
const instancesDir = path66.resolve(homeDir, "instances");
|
|
34597
|
+
if (!fs59.existsSync(instancesDir)) return [];
|
|
34598
|
+
return fs59.readdirSync(instancesDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => {
|
|
33871
34599
|
const instanceId = entry.name;
|
|
33872
|
-
const configPath =
|
|
33873
|
-
if (!
|
|
34600
|
+
const configPath = path66.resolve(instancesDir, instanceId, "config.json");
|
|
34601
|
+
if (!fs59.existsSync(configPath)) return null;
|
|
33874
34602
|
try {
|
|
33875
34603
|
const config = readConfig(configPath);
|
|
33876
34604
|
if (!config) return null;
|
|
@@ -34032,6 +34760,7 @@ registerGithubCommands(program);
|
|
|
34032
34760
|
registerIntegrationsCommands(program);
|
|
34033
34761
|
registerStatusCommands(program);
|
|
34034
34762
|
registerStarterCommands(program);
|
|
34763
|
+
registerSkillsCommands(program);
|
|
34035
34764
|
registerFleetCommands(program);
|
|
34036
34765
|
if (surfaceRuntime.capabilities.dxEnabled) {
|
|
34037
34766
|
registerDxCommands(program);
|