@scheduler-systems/gal-run 0.0.371 → 0.0.373
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +208 -5
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3970,7 +3970,7 @@ var cliVersion, defaultApiUrl, BUILD_CONSTANTS, constants_default;
|
|
|
3970
3970
|
var init_constants = __esm({
|
|
3971
3971
|
"src/constants.ts"() {
|
|
3972
3972
|
"use strict";
|
|
3973
|
-
cliVersion = true ? "0.0.
|
|
3973
|
+
cliVersion = true ? "0.0.373" : "0.0.0-dev";
|
|
3974
3974
|
defaultApiUrl = true ? "https://api.gal.run" : "http://localhost:3000";
|
|
3975
3975
|
BUILD_CONSTANTS = Object.freeze([cliVersion, defaultApiUrl]);
|
|
3976
3976
|
constants_default = BUILD_CONSTANTS;
|
|
@@ -4880,7 +4880,7 @@ function detectEnvironment() {
|
|
|
4880
4880
|
return "dev";
|
|
4881
4881
|
}
|
|
4882
4882
|
try {
|
|
4883
|
-
const version2 = true ? "0.0.
|
|
4883
|
+
const version2 = true ? "0.0.373" : void 0;
|
|
4884
4884
|
if (version2 && version2.includes("-local")) {
|
|
4885
4885
|
return "dev";
|
|
4886
4886
|
}
|
|
@@ -5249,7 +5249,7 @@ function getId() {
|
|
|
5249
5249
|
}
|
|
5250
5250
|
function getCliVersion() {
|
|
5251
5251
|
try {
|
|
5252
|
-
return true ? "0.0.
|
|
5252
|
+
return true ? "0.0.373" : "0.0.0-dev";
|
|
5253
5253
|
} catch {
|
|
5254
5254
|
return "0.0.0-dev";
|
|
5255
5255
|
}
|
|
@@ -48299,6 +48299,188 @@ function displaySyncedConfigs(syncedItems, orgName, version2, policy) {
|
|
|
48299
48299
|
const summaryText = summaryParts.length > 0 ? summaryParts.join(", ") : `${totalItems} file${totalItems > 1 ? "s" : ""}`;
|
|
48300
48300
|
console.log(source_default.green(`[GAL] `) + source_default.white(`${summaryText} synced from ${orgName} v${version2}`) + policyText);
|
|
48301
48301
|
}
|
|
48302
|
+
async function pushLearnings(directory, orgName, apiUrl, headers, options) {
|
|
48303
|
+
const spinner = ora("Scanning project for learnings...").start();
|
|
48304
|
+
try {
|
|
48305
|
+
let repoSlug;
|
|
48306
|
+
let sessionId = process.env.CLAUDE_SESSION_ID || `cli-${Date.now()}`;
|
|
48307
|
+
try {
|
|
48308
|
+
const { execSync: execSync14 } = await import("child_process");
|
|
48309
|
+
const remoteUrl = execSync14("git remote get-url origin", { cwd: directory, encoding: "utf-8" }).trim();
|
|
48310
|
+
const match = remoteUrl.match(/github\.com[:/]([^/]+\/[^/]+?)(\.git)?$/);
|
|
48311
|
+
if (match) {
|
|
48312
|
+
repoSlug = match[1];
|
|
48313
|
+
}
|
|
48314
|
+
} catch {
|
|
48315
|
+
}
|
|
48316
|
+
const learningFiles = [];
|
|
48317
|
+
const claudeMdPath = (0, import_path22.join)(directory, ".claude", "CLAUDE.md");
|
|
48318
|
+
if ((0, import_fs23.existsSync)(claudeMdPath)) {
|
|
48319
|
+
learningFiles.push({ path: claudeMdPath, type: "instruction" });
|
|
48320
|
+
}
|
|
48321
|
+
const rootClaudeMdPath = (0, import_path22.join)(directory, "CLAUDE.md");
|
|
48322
|
+
if ((0, import_fs23.existsSync)(rootClaudeMdPath)) {
|
|
48323
|
+
learningFiles.push({ path: rootClaudeMdPath, type: "memory" });
|
|
48324
|
+
}
|
|
48325
|
+
const rulesDir = (0, import_path22.join)(directory, ".claude", "rules");
|
|
48326
|
+
if ((0, import_fs23.existsSync)(rulesDir)) {
|
|
48327
|
+
const ruleFiles = (0, import_fs23.readdirSync)(rulesDir).filter((f) => f.endsWith(".md"));
|
|
48328
|
+
for (const file of ruleFiles) {
|
|
48329
|
+
learningFiles.push({ path: (0, import_path22.join)(rulesDir, file), type: "instruction" });
|
|
48330
|
+
}
|
|
48331
|
+
}
|
|
48332
|
+
const commandsDir = (0, import_path22.join)(directory, ".claude", "commands");
|
|
48333
|
+
if ((0, import_fs23.existsSync)(commandsDir)) {
|
|
48334
|
+
const cmdFiles = (0, import_fs23.readdirSync)(commandsDir).filter((f) => f.endsWith(".md"));
|
|
48335
|
+
for (const file of cmdFiles) {
|
|
48336
|
+
learningFiles.push({ path: (0, import_path22.join)(commandsDir, file), type: "command" });
|
|
48337
|
+
}
|
|
48338
|
+
}
|
|
48339
|
+
spinner.text = `Found ${learningFiles.length} source files, extracting learnings...`;
|
|
48340
|
+
const learnings = [];
|
|
48341
|
+
for (const { path: path8, type } of learningFiles) {
|
|
48342
|
+
const content = (0, import_fs23.readFileSync)(path8, "utf-8");
|
|
48343
|
+
const relPath = path8.replace(directory + "/", "");
|
|
48344
|
+
const learningPattern = /^##\s+(.*(?:Learning|Anti-pattern|Best Practice|CRITICAL).*?)$/i;
|
|
48345
|
+
const hasLearnings = content.match(new RegExp(learningPattern.source, "im"));
|
|
48346
|
+
if (!hasLearnings) continue;
|
|
48347
|
+
const lines = content.split("\n");
|
|
48348
|
+
for (let i = 0; i < lines.length; i++) {
|
|
48349
|
+
const line = lines[i];
|
|
48350
|
+
if (learningPattern.test(line)) {
|
|
48351
|
+
const title = line.replace(/^##\s+/, "").trim();
|
|
48352
|
+
let learningContent = "";
|
|
48353
|
+
let j = i + 1;
|
|
48354
|
+
while (j < lines.length && !lines[j].startsWith("##")) {
|
|
48355
|
+
learningContent += lines[j] + "\n";
|
|
48356
|
+
j++;
|
|
48357
|
+
}
|
|
48358
|
+
if (learningContent.trim().length > 0) {
|
|
48359
|
+
learningContent = sanitizeLearningContent(learningContent);
|
|
48360
|
+
const category = categorizeLearning(title, learningContent);
|
|
48361
|
+
learnings.push({
|
|
48362
|
+
title,
|
|
48363
|
+
content: learningContent.trim(),
|
|
48364
|
+
category,
|
|
48365
|
+
sourceFile: relPath,
|
|
48366
|
+
sourceKind: type === "memory" ? "memory_file" : "instruction_file"
|
|
48367
|
+
});
|
|
48368
|
+
}
|
|
48369
|
+
}
|
|
48370
|
+
}
|
|
48371
|
+
}
|
|
48372
|
+
spinner.succeed(`Extracted ${learnings.length} learnings`);
|
|
48373
|
+
if (learnings.length === 0) {
|
|
48374
|
+
console.log(source_default.yellow("\nNo learnings found to push."));
|
|
48375
|
+
console.log(source_default.dim("Learnings are extracted from sections with headers containing:"));
|
|
48376
|
+
console.log(source_default.dim(' - "Learning", "Anti-pattern", "Best Practice", or "CRITICAL"'));
|
|
48377
|
+
return;
|
|
48378
|
+
}
|
|
48379
|
+
if (options.dryRun) {
|
|
48380
|
+
console.log(source_default.cyan("\n=== DRY RUN - Would publish these learnings ===\n"));
|
|
48381
|
+
for (const learning of learnings) {
|
|
48382
|
+
console.log(source_default.bold(` ${learning.title}`));
|
|
48383
|
+
console.log(source_default.dim(` Category: ${learning.category}`));
|
|
48384
|
+
console.log(source_default.dim(` Source: ${learning.sourceFile}`));
|
|
48385
|
+
console.log(source_default.dim(` Size: ${learning.content.length} chars`));
|
|
48386
|
+
console.log();
|
|
48387
|
+
}
|
|
48388
|
+
return;
|
|
48389
|
+
}
|
|
48390
|
+
const pushSpinner = ora(`Publishing ${learnings.length} learnings to ${orgName}...`).start();
|
|
48391
|
+
let published = 0;
|
|
48392
|
+
let skipped = 0;
|
|
48393
|
+
let errors = 0;
|
|
48394
|
+
for (const learning of learnings) {
|
|
48395
|
+
try {
|
|
48396
|
+
const payload = {
|
|
48397
|
+
sessionId,
|
|
48398
|
+
provider: "cli",
|
|
48399
|
+
repo: repoSlug || "unknown",
|
|
48400
|
+
category: learning.category,
|
|
48401
|
+
title: learning.title,
|
|
48402
|
+
content: learning.content,
|
|
48403
|
+
sourceFile: learning.sourceFile,
|
|
48404
|
+
sourceKind: learning.sourceKind,
|
|
48405
|
+
scope: "organization"
|
|
48406
|
+
};
|
|
48407
|
+
const res = await fetch(
|
|
48408
|
+
`${apiUrl}/api/orgs/${encodeURIComponent(orgName)}/learnings`,
|
|
48409
|
+
{
|
|
48410
|
+
method: "POST",
|
|
48411
|
+
headers,
|
|
48412
|
+
body: JSON.stringify(payload)
|
|
48413
|
+
}
|
|
48414
|
+
);
|
|
48415
|
+
if (res.ok) {
|
|
48416
|
+
published++;
|
|
48417
|
+
} else if (res.status === 409) {
|
|
48418
|
+
skipped++;
|
|
48419
|
+
} else {
|
|
48420
|
+
errors++;
|
|
48421
|
+
const errData = await res.json().catch(() => ({ error: res.statusText }));
|
|
48422
|
+
console.error(source_default.dim(`
|
|
48423
|
+
Failed to publish "${learning.title}": ${errData.error || res.statusText}`));
|
|
48424
|
+
}
|
|
48425
|
+
} catch (err) {
|
|
48426
|
+
errors++;
|
|
48427
|
+
console.error(source_default.dim(`
|
|
48428
|
+
Failed to publish "${learning.title}": ${err instanceof Error ? err.message : String(err)}`));
|
|
48429
|
+
}
|
|
48430
|
+
}
|
|
48431
|
+
pushSpinner.stop();
|
|
48432
|
+
console.log();
|
|
48433
|
+
console.log(source_default.green(`\u2713 Published ${published} learnings to ${orgName}`));
|
|
48434
|
+
if (skipped > 0) {
|
|
48435
|
+
console.log(source_default.dim(` ${skipped} duplicates skipped`));
|
|
48436
|
+
}
|
|
48437
|
+
if (errors > 0) {
|
|
48438
|
+
console.log(source_default.yellow(` ${errors} failed to publish`));
|
|
48439
|
+
}
|
|
48440
|
+
console.log();
|
|
48441
|
+
} catch (error3) {
|
|
48442
|
+
spinner.fail(source_default.red("Failed to push learnings"));
|
|
48443
|
+
throw error3;
|
|
48444
|
+
}
|
|
48445
|
+
}
|
|
48446
|
+
function sanitizeLearningContent(content) {
|
|
48447
|
+
let sanitized = content;
|
|
48448
|
+
sanitized = sanitized.replace(/\/Users\/[^/\s]+/g, "<project>");
|
|
48449
|
+
sanitized = sanitized.replace(/\/home\/[^/\s]+/g, "<project>");
|
|
48450
|
+
sanitized = sanitized.replace(/C:\\Users\\[^\\s]+/g, "<project>");
|
|
48451
|
+
sanitized = sanitized.replace(/(\w+)=([^\s\n]+)/g, (match, name, value) => {
|
|
48452
|
+
if (name.toLowerCase().includes("token") || name.toLowerCase().includes("key") || name.toLowerCase().includes("secret")) {
|
|
48453
|
+
return `${name}=<redacted>`;
|
|
48454
|
+
}
|
|
48455
|
+
return match;
|
|
48456
|
+
});
|
|
48457
|
+
sanitized = sanitized.replace(/[a-zA-Z0-9_-]{20,}/g, (match) => {
|
|
48458
|
+
if (match.match(/^[A-Za-z0-9_-]+$/) && match.length > 30) {
|
|
48459
|
+
return "<redacted>";
|
|
48460
|
+
}
|
|
48461
|
+
return match;
|
|
48462
|
+
});
|
|
48463
|
+
return sanitized;
|
|
48464
|
+
}
|
|
48465
|
+
function categorizeLearning(title, content) {
|
|
48466
|
+
const text = (title + " " + content).toLowerCase();
|
|
48467
|
+
if (text.includes("workflow") || text.includes("ci") || text.includes("pipeline") || text.includes("deploy")) {
|
|
48468
|
+
return "workflow_pattern";
|
|
48469
|
+
}
|
|
48470
|
+
if (text.includes("debug") || text.includes("error") || text.includes("fix") || text.includes("fail")) {
|
|
48471
|
+
return "error_resolution";
|
|
48472
|
+
}
|
|
48473
|
+
if (text.includes("architecture") || text.includes("pattern") || text.includes("design")) {
|
|
48474
|
+
return "architectural_decision";
|
|
48475
|
+
}
|
|
48476
|
+
if (text.includes("tool") || text.includes("config") || text.includes("setup") || text.includes("install")) {
|
|
48477
|
+
return "tool_configuration";
|
|
48478
|
+
}
|
|
48479
|
+
if (text.includes("repo") || text.includes("project") || text.includes("codebase")) {
|
|
48480
|
+
return "repo_pattern";
|
|
48481
|
+
}
|
|
48482
|
+
return "repo_pattern";
|
|
48483
|
+
}
|
|
48302
48484
|
function createSyncCommand() {
|
|
48303
48485
|
const command = new Command("sync").description("Sync local configs with CISO-approved organization standard");
|
|
48304
48486
|
const statusCmd = command.command("status").description("Check current sync status (for extension integration)").option("--json", "Output as JSON").option("-p, --platform <platform>", "Filter by platform (claude, cursor, etc.)");
|
|
@@ -48403,7 +48585,7 @@ function createSyncCommand() {
|
|
|
48403
48585
|
}
|
|
48404
48586
|
console.log();
|
|
48405
48587
|
});
|
|
48406
|
-
command.argument("[orgName]", "Organization name (uses default if not specified)").option("-d, --directory <path>", "Local config directory (auto-detects project root)", process.cwd()).option("-p, --platform <platform>", "Filter by platform (claude, cursor, etc.)").option("--pull", "Download CISO-approved config to local directory").option("--repo <name>", "Personal repo to sync from (for users without org)").option("--check", "Check if local files match approved config (for hooks)").option("--regenerate", "Regenerate native configs from canonical .gal/ directory (offline)").option("--auto", "Non-interactive mode for hooks/scripts (no prompts, exit cleanly)").option("--output-json", "Output as JSON").option("--target <target>", "Sync specific target: configs (default), domains (domain allowlist)").action(async (orgName, options) => {
|
|
48588
|
+
command.argument("[orgName]", "Organization name (uses default if not specified)").option("-d, --directory <path>", "Local config directory (auto-detects project root)", process.cwd()).option("-p, --platform <platform>", "Filter by platform (claude, cursor, etc.)").option("--pull", "Download CISO-approved config to local directory").option("--push", "Push session learnings to organization (capture learning pipeline)").option("--repo <name>", "Personal repo to sync from (for users without org)").option("--check", "Check if local files match approved config (for hooks)").option("--regenerate", "Regenerate native configs from canonical .gal/ directory (offline)").option("--auto", "Non-interactive mode for hooks/scripts (no prompts, exit cleanly)").option("--output-json", "Output as JSON").option("--dry-run", "Preview what would be pushed without making API calls").option("--target <target>", "Sync specific target: configs (default), domains (domain allowlist)").action(async (orgName, options) => {
|
|
48407
48589
|
try {
|
|
48408
48590
|
const directory = findProjectRoot(options.directory);
|
|
48409
48591
|
if (options.regenerate) {
|
|
@@ -48607,6 +48789,27 @@ function createSyncCommand() {
|
|
|
48607
48789
|
const provider = new CoreServiceProvider({ apiUrl, authToken: config2.authToken });
|
|
48608
48790
|
const configRepo = provider.getConfigRepository();
|
|
48609
48791
|
const authRepo = provider.getAuthRepository();
|
|
48792
|
+
if (options.push) {
|
|
48793
|
+
const pushOrg = orgName || config2.defaultOrg;
|
|
48794
|
+
if (!pushOrg) {
|
|
48795
|
+
console.error(source_default.red("Error: Organization not specified"));
|
|
48796
|
+
console.error(source_default.dim("Run: gal sync <orgName> --push"));
|
|
48797
|
+
console.error(source_default.dim("Or: gal config set defaultOrg <orgName>"));
|
|
48798
|
+
process.exit(1);
|
|
48799
|
+
}
|
|
48800
|
+
const headers = {
|
|
48801
|
+
"Accept": "application/json",
|
|
48802
|
+
"Content-Type": "application/json"
|
|
48803
|
+
};
|
|
48804
|
+
if (config2.authToken) {
|
|
48805
|
+
headers["Authorization"] = `Bearer ${config2.authToken}`;
|
|
48806
|
+
}
|
|
48807
|
+
await pushLearnings(directory, pushOrg, apiUrl, headers, {
|
|
48808
|
+
dryRun: options.dryRun,
|
|
48809
|
+
platform: options.platform
|
|
48810
|
+
});
|
|
48811
|
+
process.exit(0);
|
|
48812
|
+
}
|
|
48610
48813
|
if (options.repo && options.pull) {
|
|
48611
48814
|
await pullPersonalRepoConfig(authRepo, options.repo, options.directory);
|
|
48612
48815
|
process.exit(0);
|
|
@@ -70559,7 +70762,7 @@ var init_index = __esm({
|
|
|
70559
70762
|
});
|
|
70560
70763
|
|
|
70561
70764
|
// src/bootstrap.ts
|
|
70562
|
-
var cliVersion10 = true ? "0.0.
|
|
70765
|
+
var cliVersion10 = true ? "0.0.373" : "0.0.0-dev";
|
|
70563
70766
|
var args = process.argv.slice(2);
|
|
70564
70767
|
var requestedGlobalHelp = args.length === 1 && (args[0] === "--help" || args[0] === "-h");
|
|
70565
70768
|
var requestedVersion = args.length === 1 && (args[0] === "--version" || args[0] === "-V");
|
package/package.json
CHANGED