sandstream-kit 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -92
- package/dist/adapters/expo-eas.js +1 -1
- package/dist/adapters/expo-eas.js.map +1 -1
- package/dist/audit-logging-service.js +1 -1
- package/dist/audit-logging-service.js.map +1 -1
- package/dist/author-verification.js +1 -1
- package/dist/author-verification.js.map +1 -1
- package/dist/check-web-search.js +37 -6
- package/dist/check-web-search.js.map +1 -1
- package/dist/cli-shared.d.ts +4 -0
- package/dist/cli-shared.js +11 -0
- package/dist/cli-shared.js.map +1 -0
- package/dist/cli.js +175 -1189
- package/dist/cli.js.map +1 -1
- package/dist/commands/audit.d.ts +1 -0
- package/dist/commands/audit.js +99 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/auth.d.ts +1 -0
- package/dist/commands/auth.js +121 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/env.d.ts +1 -0
- package/dist/commands/env.js +149 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/hooks.d.ts +1 -0
- package/dist/commands/hooks.js +146 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/mcp.d.ts +1 -0
- package/dist/commands/mcp.js +120 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/memory.d.ts +1 -0
- package/dist/commands/memory.js +534 -0
- package/dist/commands/memory.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +4 -0
- package/dist/config.js.map +1 -1
- package/dist/mcp-server.js +59 -6
- package/dist/mcp-server.js.map +1 -1
- package/dist/memory/amazonq.d.ts +5 -0
- package/dist/memory/amazonq.js +161 -0
- package/dist/memory/amazonq.js.map +1 -0
- package/dist/memory/cline.d.ts +5 -0
- package/dist/memory/cline.js +117 -0
- package/dist/memory/cline.js.map +1 -0
- package/dist/memory/codex.d.ts +5 -0
- package/dist/memory/codex.js +150 -0
- package/dist/memory/codex.js.map +1 -0
- package/dist/memory/continue.d.ts +5 -0
- package/dist/memory/continue.js +116 -0
- package/dist/memory/continue.js.map +1 -0
- package/dist/memory/cursor.d.ts +4 -0
- package/dist/memory/cursor.js +116 -0
- package/dist/memory/cursor.js.map +1 -0
- package/dist/memory/db.d.ts +12 -1
- package/dist/memory/db.js +43 -1
- package/dist/memory/db.js.map +1 -1
- package/dist/memory/gemini.d.ts +5 -0
- package/dist/memory/gemini.js +176 -0
- package/dist/memory/gemini.js.map +1 -0
- package/dist/memory/hook.d.ts +9 -0
- package/dist/memory/hook.js +34 -1
- package/dist/memory/hook.js.map +1 -1
- package/dist/memory/install.js +1 -0
- package/dist/memory/install.js.map +1 -1
- package/dist/memory/merge.d.ts +18 -0
- package/dist/memory/merge.js +109 -0
- package/dist/memory/merge.js.map +1 -0
- package/dist/memory/parser.d.ts +9 -0
- package/dist/memory/parser.js +40 -3
- package/dist/memory/parser.js.map +1 -1
- package/dist/memory/suggest.d.ts +21 -0
- package/dist/memory/suggest.js +36 -0
- package/dist/memory/suggest.js.map +1 -0
- package/dist/onepassword.js +1 -1
- package/dist/onepassword.js.map +1 -1
- package/dist/open.js +8 -2
- package/dist/open.js.map +1 -1
- package/dist/run.js +1 -1
- package/dist/run.js.map +1 -1
- package/dist/service-auth.d.ts +31 -0
- package/dist/service-auth.js +47 -0
- package/dist/service-auth.js.map +1 -0
- package/dist/stack-detector.js +53 -76
- package/dist/stack-detector.js.map +1 -1
- package/dist/status.d.ts +9 -0
- package/dist/status.js +119 -0
- package/dist/status.js.map +1 -0
- package/package.json +9 -4
- package/dist/memory/backup 2.d.ts +0 -6
- package/dist/memory/backup 2.js +0 -80
- package/dist/memory/backup 2.js.map +0 -1
- package/dist/memory/db 2.d.ts +0 -40
- package/dist/memory/db 2.js +0 -233
- package/dist/memory/db 2.js.map +0 -1
- package/dist/memory/hook 2.d.ts +0 -6
- package/dist/memory/hook 2.js +0 -51
- package/dist/memory/hook 2.js.map +0 -1
- package/dist/memory/hook.test 2.d.ts +0 -1
- package/dist/memory/hook.test 2.js +0 -35
- package/dist/memory/hook.test 2.js.map +0 -1
- package/dist/memory/install 2.d.ts +0 -8
- package/dist/memory/install 2.js +0 -72
- package/dist/memory/install 2.js.map +0 -1
- package/dist/memory/install.test 2.d.ts +0 -1
- package/dist/memory/install.test 2.js +0 -59
- package/dist/memory/install.test 2.js.map +0 -1
- package/dist/memory/pal 2.d.ts +0 -47
- package/dist/memory/pal 2.js +0 -154
- package/dist/memory/pal 2.js.map +0 -1
- package/dist/memory/project 2.d.ts +0 -1
- package/dist/memory/project 2.js +0 -24
- package/dist/memory/project 2.js.map +0 -1
- package/dist/memory/scan 2.d.ts +0 -15
- package/dist/memory/scan 2.js +0 -94
- package/dist/memory/scan 2.js.map +0 -1
- package/dist/memory/scan.test 2.d.ts +0 -1
- package/dist/memory/scan.test 2.js +0 -55
- package/dist/memory/scan.test 2.js.map +0 -1
- package/dist/memory/shared 2.d.ts +0 -33
- package/dist/memory/shared 2.js +0 -120
- package/dist/memory/shared 2.js.map +0 -1
- package/dist/memory/threads 2.d.ts +0 -37
- package/dist/memory/threads 2.js +0 -50
- package/dist/memory/threads 2.js.map +0 -1
- package/dist/memory/threads.test 2.d.ts +0 -1
- package/dist/memory/threads.test 2.js +0 -66
- package/dist/memory/threads.test 2.js.map +0 -1
- package/dist/memory/types 2.d.ts +0 -52
- package/dist/memory/types 2.js +0 -5
- package/dist/memory/types 2.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { readFileSync,
|
|
2
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { writeFile, access, mkdir } from "node:fs/promises";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { resolve, dirname, join
|
|
6
|
-
import { loadConfig
|
|
5
|
+
import { resolve, dirname, join } from "node:path";
|
|
6
|
+
import { loadConfig } from "./config.js";
|
|
7
7
|
import { checkTools } from "./check-tools.js";
|
|
8
8
|
import { checkServices } from "./check-services.js";
|
|
9
9
|
import { checkSecrets } from "./check-secrets.js";
|
|
@@ -20,17 +20,16 @@ import { scanPlaintextSecrets } from "./scan-plaintext.js";
|
|
|
20
20
|
import { planMigration, writeSecretToBackend, commentOutInFile, } from "./secrets-migrate.js";
|
|
21
21
|
import { analyzeRepo, renderClaudeMd, renderRulesMd } from "./analyze.js";
|
|
22
22
|
import { cmdSecretsRotate, pickBackendOpts } from "./secrets-rotate-cli.js";
|
|
23
|
+
import { setSecretValue } from "./secrets-set.js";
|
|
23
24
|
import { detectTools as detectPurgeTools, previewMatches, purgeHistory, } from "./secrets-purge-history.js";
|
|
24
25
|
import { propagate, parseTargets, ALL_TARGETS, } from "./secrets-propagate.js";
|
|
25
26
|
import { initAllowlist, checkAllowlist, addToAllowlist, checkSecretPolicy, } from "./security-policy.js";
|
|
26
27
|
import { clearBumblebeeCache } from "./bumblebee.js";
|
|
27
|
-
import { KNOWN_ENVS, readActiveEnv, writeActiveEnv, } from "./env-switch.js";
|
|
28
28
|
import { scanStagedFiles } from "./scan-staged.js";
|
|
29
29
|
import { scanBuildArtifacts } from "./scan-build.js";
|
|
30
30
|
import { scanTranscripts } from "./scan-transcripts.js";
|
|
31
31
|
import { sampleCosts } from "./cost-monitor.js";
|
|
32
|
-
import {
|
|
33
|
-
import { requireElevation, consumeElevation, grantElevation, clearElevation, readElevation, verifyTotp, elevationTtlMinutes, enrollTotp, resolveTotpSecret, } from "./elevation.js";
|
|
32
|
+
import { requireElevation, consumeElevation } from "./elevation.js";
|
|
34
33
|
import { checkGitignore, patchGitignore, findCommittedSensitive, } from "./check-gitignore.js";
|
|
35
34
|
import { auditPull, reportSeverity } from "./post-pull-audit.js";
|
|
36
35
|
import { checkOneCliStatus, registerSecretInOneCli, generatePlaceholder, resolveOneCliConfig, } from "./secrets-onecli.js";
|
|
@@ -41,23 +40,28 @@ import { checkForUpdate, printUpdateNotice } from "./update-check.js";
|
|
|
41
40
|
import { checkSkills } from "./check-skills.js";
|
|
42
41
|
import { checkLockFiles } from "./check-lock.js";
|
|
43
42
|
import { collectEscalations, formatEscalationMessage } from "./escalate.js";
|
|
44
|
-
import { printToolsTable, printServicesTable, printSecretsTable, printSkillsTable, printWebSearchStatus, printSecurityTable, printLockTable, printSummary,
|
|
43
|
+
import { printToolsTable, printServicesTable, printSecretsTable, printSkillsTable, printWebSearchStatus, printSecurityTable, printLockTable, printSummary, runStep, stepHeader, } from "./output.js";
|
|
45
44
|
import { checkRevocationStatus } from "./revocation.js";
|
|
46
45
|
import { getBudgetStatus, formatBudgetStatus } from "./budget.js";
|
|
47
|
-
import { readAuditLog } from "./audit.js";
|
|
48
46
|
import { formatGovernanceStatus, mergeGovernanceConfigAsync } from "./governance.js";
|
|
49
47
|
import { withGovernance } from "./governance-middleware.js";
|
|
50
|
-
import {
|
|
48
|
+
import { SKIPPED_COMMITS_LOG } from "./hooks.js";
|
|
51
49
|
import { writeAgentConfig, detectAgentTargets } from "./agent-config.js";
|
|
52
50
|
import { checkHooks, isGitRepository } from "./check-hooks.js";
|
|
53
51
|
import { readkitMeta, readSkillsLock, readCliLock, updateSkillsLock, updateCliLock, } from "./lock.js";
|
|
54
52
|
import { provisionService, listAvailableServices, getServiceInfo } from "./provision.js";
|
|
55
53
|
import { cmdFix } from "./fix.js";
|
|
56
54
|
import { promptConfirm } from "./utils/prompt.js";
|
|
57
|
-
import { startMcpServer } from "./mcp-server.js";
|
|
58
55
|
import { c } from "./utils/colors.js";
|
|
56
|
+
import { gatherStatus } from "./status.js";
|
|
57
|
+
import { KIT_FILE, resolveConfigPath } from "./cli-shared.js";
|
|
58
|
+
import { cmdEnv } from "./commands/env.js";
|
|
59
|
+
import { cmdAuth } from "./commands/auth.js";
|
|
60
|
+
import { cmdAudit } from "./commands/audit.js";
|
|
61
|
+
import { cmdMcp } from "./commands/mcp.js";
|
|
62
|
+
import { cmdHooks } from "./commands/hooks.js";
|
|
63
|
+
import { resolveAllAuth } from "./service-auth.js";
|
|
59
64
|
import { runDoctor } from "./doctor.js";
|
|
60
|
-
import { inspectEnv } from "./env-inspect.js";
|
|
61
65
|
import { detectStack } from "./stack-detector.js";
|
|
62
66
|
import { generateToml } from "./toml-generator.js";
|
|
63
67
|
import { createPlugin } from "./create-plugin.js";
|
|
@@ -68,22 +72,9 @@ import { listServices, openService } from "./open.js";
|
|
|
68
72
|
import { gatherProjectContext } from "./context.js";
|
|
69
73
|
import { runTriage, listTriageTools } from "./triage.js";
|
|
70
74
|
import { parsePkgSpec, installPkg } from "./pkg.js";
|
|
71
|
-
import {
|
|
72
|
-
import { indexClaudeTranscripts, getClaudeProjectsDir } from "./memory/parser.js";
|
|
73
|
-
import { getCurrentProjectRoot } from "./memory/project.js";
|
|
74
|
-
import { scanDbForSecrets } from "./memory/scan.js";
|
|
75
|
-
import { backupEncrypted, restoreEncrypted } from "./memory/backup.js";
|
|
76
|
-
import { shareEntry, listAreas, queryArea, getSharedPath, } from "./memory/shared.js";
|
|
77
|
-
import { userPromptSubmitReminder, runSessionEndIndex } from "./memory/hook.js";
|
|
78
|
-
import { installMemoryHooks, uninstallMemoryHooks, getClaudeSettingsPath, } from "./memory/install.js";
|
|
79
|
-
import { palAdd, palList, palDone, palSnooze, palAutoVerify, importLegacyLedger, } from "./memory/pal.js";
|
|
80
|
-
import { saveThread, listThreads, removeThread, latestSessionId, resolveThread, } from "./memory/threads.js";
|
|
81
|
-
const KIT_FILE = ".kit.toml";
|
|
75
|
+
import { cmdMemory } from "./commands/memory.js";
|
|
82
76
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
83
77
|
const KIT_VERSION = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8")).version;
|
|
84
|
-
function resolveConfigPath() {
|
|
85
|
-
return resolve(process.cwd(), KIT_FILE);
|
|
86
|
-
}
|
|
87
78
|
async function cmdCheck() {
|
|
88
79
|
const jsonMode = hasFlag(process.argv, "--json");
|
|
89
80
|
const enforceTests = hasFlag(process.argv, "--enforce-tests");
|
|
@@ -469,6 +460,23 @@ async function cmdLogin() {
|
|
|
469
460
|
servicesConfig = { [serviceFilter]: servicesConfig[serviceFilter] };
|
|
470
461
|
console.log(`${c.dim}Filtering to service "${serviceFilter}"${retryCount ? ` (retries=${retryCount})` : ""}${c.reset}`);
|
|
471
462
|
}
|
|
463
|
+
// `--plan`: read-only. Show the resolved auth strategy per service (vault /
|
|
464
|
+
// interactive / capture, + passkey warnings) without logging in to anything.
|
|
465
|
+
if (hasFlag(args, "--plan")) {
|
|
466
|
+
const plan = resolveAllAuth(servicesConfig);
|
|
467
|
+
if (hasFlag(args, "--json")) {
|
|
468
|
+
console.log(JSON.stringify(plan, null, 2));
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
console.log(`${c.bold}auth plan${c.reset} ${c.dim}${plan.length} service(s)${c.reset}`);
|
|
472
|
+
for (const p of plan) {
|
|
473
|
+
const tag = p.passkey
|
|
474
|
+
? `${c.yellow}${p.strategy} ⚿${c.reset}`
|
|
475
|
+
: `${c.cyan}${p.strategy}${c.reset}`;
|
|
476
|
+
console.log(` ${tag} ${p.name} ${c.dim}${p.instruction}${c.reset}`);
|
|
477
|
+
}
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
472
480
|
console.log(`${c.bold}${c.cyan}Authenticating services...${c.reset}`);
|
|
473
481
|
return await withGovernance(config, {
|
|
474
482
|
operation: "services.login",
|
|
@@ -546,6 +554,9 @@ async function cmdSecrets() {
|
|
|
546
554
|
if (process.argv[3] === "propagate") {
|
|
547
555
|
return cmdSecretsPropagateStandalone();
|
|
548
556
|
}
|
|
557
|
+
if (process.argv[3] === "set") {
|
|
558
|
+
return cmdSecretsSet();
|
|
559
|
+
}
|
|
549
560
|
if (process.argv[3] === "revoke-old") {
|
|
550
561
|
return cmdSecretsRevokeOld();
|
|
551
562
|
}
|
|
@@ -1023,612 +1034,6 @@ async function offerFirstInstallPrescan() {
|
|
|
1023
1034
|
}
|
|
1024
1035
|
console.log();
|
|
1025
1036
|
}
|
|
1026
|
-
/** Built-in pre-commit hook: scans staged files for known credential patterns. */
|
|
1027
|
-
const BUILTIN_HOOKS = {
|
|
1028
|
-
"secret-scan": {
|
|
1029
|
-
hookName: "pre-commit",
|
|
1030
|
-
commands: ["kit security scan-staged"],
|
|
1031
|
-
description: "Block commits that stage known credential patterns (Stripe, AWS, JWT, etc).",
|
|
1032
|
-
},
|
|
1033
|
-
"post-pull-audit": {
|
|
1034
|
-
hookName: "post-merge",
|
|
1035
|
-
commands: ["kit security verify-pull"],
|
|
1036
|
-
description: "After git pull/merge, audit new deps, gitignore drops, and introduced secrets.",
|
|
1037
|
-
},
|
|
1038
|
-
};
|
|
1039
|
-
async function cmdHooks() {
|
|
1040
|
-
const subcommand = process.argv[3];
|
|
1041
|
-
// Built-in hooks bypass the .kit.toml config path so they're available
|
|
1042
|
-
// on any repo, including ones that haven't run `kit init` yet.
|
|
1043
|
-
if (subcommand === "add") {
|
|
1044
|
-
return cmdHooksAdd();
|
|
1045
|
-
}
|
|
1046
|
-
const config = await loadConfig(resolveConfigPath());
|
|
1047
|
-
if (!config.hooks || Object.keys(config.hooks).length === 0) {
|
|
1048
|
-
console.log(`${c.dim}No hooks configured in ${KIT_FILE}${c.reset}`);
|
|
1049
|
-
return true;
|
|
1050
|
-
}
|
|
1051
|
-
if (!isGitRepository()) {
|
|
1052
|
-
console.log(`${c.red}Not a git repository${c.reset}`);
|
|
1053
|
-
return false;
|
|
1054
|
-
}
|
|
1055
|
-
if (subcommand === "install" || !subcommand) {
|
|
1056
|
-
console.log(`${c.bold}${c.cyan}Installing git hooks...${c.reset}\n`);
|
|
1057
|
-
const results = await installHooks(config.hooks);
|
|
1058
|
-
let allOk = true;
|
|
1059
|
-
for (const r of results) {
|
|
1060
|
-
const icon = r.action === "failed"
|
|
1061
|
-
? `${c.red}✗${c.reset}`
|
|
1062
|
-
: `${c.green}✓${c.reset}`;
|
|
1063
|
-
const label = r.action === "installed"
|
|
1064
|
-
? `${c.green}installed${c.reset}`
|
|
1065
|
-
: r.action === "updated"
|
|
1066
|
-
? `${c.green}updated${c.reset}`
|
|
1067
|
-
: r.action === "skipped"
|
|
1068
|
-
? `${c.dim}skipped${c.reset}`
|
|
1069
|
-
: `${c.red}failed${c.reset}`;
|
|
1070
|
-
console.log(` ${icon} ${r.hookName} ${label} ${c.dim}${r.detail}${c.reset}`);
|
|
1071
|
-
if (r.action === "failed")
|
|
1072
|
-
allOk = false;
|
|
1073
|
-
}
|
|
1074
|
-
console.log();
|
|
1075
|
-
return allOk;
|
|
1076
|
-
}
|
|
1077
|
-
else if (subcommand === "check") {
|
|
1078
|
-
console.log(`${c.bold}${c.cyan}Git Hooks${c.reset}\n`);
|
|
1079
|
-
const results = await checkHooks(config.hooks);
|
|
1080
|
-
let allOk = true;
|
|
1081
|
-
for (const r of results) {
|
|
1082
|
-
const icon = !r.installed
|
|
1083
|
-
? `${c.red}✗${c.reset}`
|
|
1084
|
-
: !r.upToDate
|
|
1085
|
-
? `${c.yellow}!${c.reset}`
|
|
1086
|
-
: `${c.green}✓${c.reset}`;
|
|
1087
|
-
const status = !r.installed
|
|
1088
|
-
? `${c.red}not installed${c.reset}`
|
|
1089
|
-
: !r.upToDate
|
|
1090
|
-
? `${c.yellow}outdated${c.reset}`
|
|
1091
|
-
: `${c.green}up-to-date${c.reset}`;
|
|
1092
|
-
console.log(` ${icon} ${r.hookName} ${status} ${c.dim}${r.detail}${c.reset}`);
|
|
1093
|
-
if (!r.installed || !r.upToDate)
|
|
1094
|
-
allOk = false;
|
|
1095
|
-
}
|
|
1096
|
-
if (!allOk) {
|
|
1097
|
-
console.log(`\n${c.dim}Run ${c.reset}${c.bold}kit hooks install${c.reset}${c.dim} to install/update hooks${c.reset}`);
|
|
1098
|
-
}
|
|
1099
|
-
console.log();
|
|
1100
|
-
return allOk;
|
|
1101
|
-
}
|
|
1102
|
-
else {
|
|
1103
|
-
console.error(`Unknown hooks subcommand: ${subcommand}`);
|
|
1104
|
-
console.error(`Usage: kit hooks [install|check|add <name>]`);
|
|
1105
|
-
return false;
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
async function cmdHooksAdd() {
|
|
1109
|
-
const name = process.argv[4];
|
|
1110
|
-
if (!name) {
|
|
1111
|
-
console.error(`${c.red}Usage: kit hooks add <name>${c.reset}\n${c.dim}Available built-in hooks:${c.reset}`);
|
|
1112
|
-
for (const [k, v] of Object.entries(BUILTIN_HOOKS)) {
|
|
1113
|
-
console.error(` ${c.bold}${k}${c.reset} (git ${v.hookName}) — ${v.description}`);
|
|
1114
|
-
}
|
|
1115
|
-
return false;
|
|
1116
|
-
}
|
|
1117
|
-
const builtin = BUILTIN_HOOKS[name];
|
|
1118
|
-
if (!builtin) {
|
|
1119
|
-
console.error(`${c.red}No built-in hook named "${name}"${c.reset}`);
|
|
1120
|
-
console.error(`${c.dim}Available: ${Object.keys(BUILTIN_HOOKS).join(", ")}${c.reset}`);
|
|
1121
|
-
return false;
|
|
1122
|
-
}
|
|
1123
|
-
if (!isGitRepository()) {
|
|
1124
|
-
console.error(`${c.red}Not a git repository${c.reset}`);
|
|
1125
|
-
return false;
|
|
1126
|
-
}
|
|
1127
|
-
console.log(`${c.bold}${c.cyan}kit hooks add ${name}${c.reset} ${c.dim}(git ${builtin.hookName})${c.reset}`);
|
|
1128
|
-
console.log(`${c.dim}${"─".repeat(50)}${c.reset}\n`);
|
|
1129
|
-
// Check whether the target hook is already present and contains a kit-
|
|
1130
|
-
// managed step. If so, just confirm without re-writing.
|
|
1131
|
-
const { hooksDir } = await ensureHooksDir();
|
|
1132
|
-
const hookPath = `${hooksDir}/${builtin.hookName}`;
|
|
1133
|
-
const { existsSync, readFileSync } = await import("node:fs");
|
|
1134
|
-
if (existsSync(hookPath)) {
|
|
1135
|
-
const existing = readFileSync(hookPath, "utf-8");
|
|
1136
|
-
if (existing.includes(builtin.commands[0])) {
|
|
1137
|
-
console.log(` ${c.green}✓${c.reset} ${builtin.hookName} already has ${c.bold}${builtin.commands[0]}${c.reset}\n`);
|
|
1138
|
-
return true;
|
|
1139
|
-
}
|
|
1140
|
-
console.log(`${c.yellow}⚠ ${builtin.hookName} already exists at ${hookPath}.${c.reset}`);
|
|
1141
|
-
console.log(`${c.dim}kit will overwrite it with a managed version that calls back into kit.${c.reset}\n`);
|
|
1142
|
-
if (!isNonInteractive()) {
|
|
1143
|
-
const ok = await promptConfirm(`Overwrite? [Y/n] (auto-yes in 8s): `, 8000);
|
|
1144
|
-
if (!ok) {
|
|
1145
|
-
console.log(`${c.dim}Aborted.${c.reset}`);
|
|
1146
|
-
return false;
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
}
|
|
1150
|
-
const results = await installHooks({ [builtin.hookName]: builtin.commands });
|
|
1151
|
-
const r = results[0];
|
|
1152
|
-
if (r.action === "failed") {
|
|
1153
|
-
console.error(`${c.red}✗ install failed: ${r.detail}${c.reset}`);
|
|
1154
|
-
return false;
|
|
1155
|
-
}
|
|
1156
|
-
console.log(` ${c.green}✓${c.reset} ${builtin.hookName} ${c.green}${r.action}${c.reset} ${c.dim}${r.detail}${c.reset}`);
|
|
1157
|
-
console.log(`\n${c.dim}Test by staging a file with a fake credential (e.g. ${c.bold}sk_${"test"}_${"A".repeat(20)}${c.reset}${c.dim}) and running ${c.bold}git commit${c.reset}${c.dim} — the commit should be blocked.${c.reset}\n`);
|
|
1158
|
-
return true;
|
|
1159
|
-
}
|
|
1160
|
-
async function ensureHooksDir() {
|
|
1161
|
-
const { resolve } = await import("node:path");
|
|
1162
|
-
return { hooksDir: resolve(process.cwd(), ".git", "hooks") };
|
|
1163
|
-
}
|
|
1164
|
-
/**
|
|
1165
|
-
* `kit mcp <list|status|auth|set-token|clear>` — declarative
|
|
1166
|
-
* MCP-server registry. The block in .kit.toml [mcp.<name>] declares
|
|
1167
|
-
* which MCPs the project intends to use; this command surfaces auth-state
|
|
1168
|
-
* and stages tokens for the kit-plugins to consume.
|
|
1169
|
-
*
|
|
1170
|
-
* Browser-OAuth flow itself is delegated — `kit mcp auth <name>`
|
|
1171
|
-
* prints the vendor's authorization URL and asks the operator to paste
|
|
1172
|
-
* back the callback URL (same pattern as the Sentry MCP auth flow this
|
|
1173
|
-
* session walked through). `kit mcp set-token <name>` is the
|
|
1174
|
-
* headless alternative for CI: read access-token from an env var.
|
|
1175
|
-
*/
|
|
1176
|
-
async function cmdMcp() {
|
|
1177
|
-
const sub = process.argv[3];
|
|
1178
|
-
// An MCP client launches `kit mcp` and speaks the stdio transport — no
|
|
1179
|
-
// sub-command and a non-TTY stdin. Start the server in that case. Interactive
|
|
1180
|
-
// use (TTY) or any explicit sub falls through to the orchestrator below.
|
|
1181
|
-
if (!sub && !process.stdin.isTTY) {
|
|
1182
|
-
await startMcpServer();
|
|
1183
|
-
return true;
|
|
1184
|
-
}
|
|
1185
|
-
const config = await loadConfig(resolveConfigPath()).catch(() => null);
|
|
1186
|
-
const mcpConfig = config?.mcp;
|
|
1187
|
-
const { statusAll, statusForMcp, clearMcpToken, storeStaticToken, } = await import("./mcp-orchestrator.js");
|
|
1188
|
-
if (!sub || sub === "list" || sub === "status") {
|
|
1189
|
-
console.log(`${c.bold}${c.cyan}kit mcp${c.reset}`);
|
|
1190
|
-
console.log(`${c.dim}${"─".repeat(50)}${c.reset}\n`);
|
|
1191
|
-
const entries = await statusAll(mcpConfig);
|
|
1192
|
-
if (entries.length === 0) {
|
|
1193
|
-
console.log(`${c.dim}No [mcp.*] blocks declared in .kit.toml${c.reset}`);
|
|
1194
|
-
console.log(`${c.dim}Add e.g. [mcp.sentry] scopes = ["org:read", "project:write"]${c.reset}`);
|
|
1195
|
-
return true;
|
|
1196
|
-
}
|
|
1197
|
-
for (const e of entries) {
|
|
1198
|
-
const color = e.status === "ok"
|
|
1199
|
-
? c.green
|
|
1200
|
-
: e.status === "missing" || e.status === "expired"
|
|
1201
|
-
? c.yellow
|
|
1202
|
-
: c.red;
|
|
1203
|
-
const marker = e.status === "ok" ? "✓" : e.status === "missing" ? "?" : "✗";
|
|
1204
|
-
const scopeStr = e.declared?.scopes ? ` ${c.dim}[${e.declared.scopes.join(", ")}]${c.reset}` : "";
|
|
1205
|
-
console.log(` ${color}${marker}${c.reset} ${e.name.padEnd(14)} ${color}${e.status}${c.reset}${scopeStr}`);
|
|
1206
|
-
if (e.detail)
|
|
1207
|
-
console.log(` ${c.dim}${e.detail}${c.reset}`);
|
|
1208
|
-
}
|
|
1209
|
-
console.log();
|
|
1210
|
-
return entries.every((e) => e.status === "ok" || e.status === "missing");
|
|
1211
|
-
}
|
|
1212
|
-
if (sub === "auth") {
|
|
1213
|
-
const name = process.argv[4];
|
|
1214
|
-
if (!name) {
|
|
1215
|
-
console.error(`${c.red}Usage: kit mcp auth <name>${c.reset}`);
|
|
1216
|
-
return false;
|
|
1217
|
-
}
|
|
1218
|
-
const declared = mcpConfig?.[name];
|
|
1219
|
-
if (!declared) {
|
|
1220
|
-
console.error(`${c.red}No [mcp.${name}] block in .kit.toml${c.reset}`);
|
|
1221
|
-
return false;
|
|
1222
|
-
}
|
|
1223
|
-
// For now we surface vendor-specific guidance; full OAuth flow is
|
|
1224
|
-
// delegated to the operator (paste callback URL). When the vendor's
|
|
1225
|
-
// MCP server publishes a stable /authorize endpoint we can fully
|
|
1226
|
-
// automate.
|
|
1227
|
-
const authUrl = declared.url ?? `https://mcp.${name}.dev`;
|
|
1228
|
-
console.log(`${c.bold}Authorize kit for MCP server "${name}"${c.reset}`);
|
|
1229
|
-
console.log(`${c.dim}Vendor URL: ${authUrl}${c.reset}`);
|
|
1230
|
-
console.log(`${c.dim}Required scopes: ${(declared.scopes ?? ["(none declared)"]).join(", ")}${c.reset}\n`);
|
|
1231
|
-
console.log(`${c.yellow}OAuth-flow not yet automated for "${name}". Two options:${c.reset}`);
|
|
1232
|
-
console.log(` 1. Set env var: ${c.bold}kit mcp set-token ${name} --from-env <VAR>${c.reset}`);
|
|
1233
|
-
console.log(` 2. Paste token: ${c.bold}kit mcp set-token ${name} --paste${c.reset}`);
|
|
1234
|
-
return true;
|
|
1235
|
-
}
|
|
1236
|
-
if (sub === "set-token") {
|
|
1237
|
-
const name = process.argv[4];
|
|
1238
|
-
if (!name) {
|
|
1239
|
-
console.error(`${c.red}Usage: kit mcp set-token <name> [--from-env VAR | --paste]${c.reset}`);
|
|
1240
|
-
return false;
|
|
1241
|
-
}
|
|
1242
|
-
const args = process.argv.slice(5);
|
|
1243
|
-
const fromEnvIdx = args.indexOf("--from-env");
|
|
1244
|
-
let accessToken;
|
|
1245
|
-
if (fromEnvIdx >= 0 && args[fromEnvIdx + 1]) {
|
|
1246
|
-
const envVar = args[fromEnvIdx + 1];
|
|
1247
|
-
accessToken = process.env[envVar];
|
|
1248
|
-
if (!accessToken) {
|
|
1249
|
-
console.error(`${c.red}Env var ${envVar} is empty${c.reset}`);
|
|
1250
|
-
return false;
|
|
1251
|
-
}
|
|
1252
|
-
}
|
|
1253
|
-
else if (hasFlag(args, "--paste")) {
|
|
1254
|
-
const readline = await import("node:readline/promises");
|
|
1255
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1256
|
-
try {
|
|
1257
|
-
accessToken = (await rl.question(`Paste access token for "${name}": `)).trim();
|
|
1258
|
-
}
|
|
1259
|
-
finally {
|
|
1260
|
-
rl.close();
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
else {
|
|
1264
|
-
console.error(`${c.red}Missing --from-env <VAR> or --paste${c.reset}`);
|
|
1265
|
-
return false;
|
|
1266
|
-
}
|
|
1267
|
-
const declared = mcpConfig?.[name];
|
|
1268
|
-
await storeStaticToken(name, accessToken, {
|
|
1269
|
-
scopes: declared?.scopes,
|
|
1270
|
-
});
|
|
1271
|
-
console.log(`${c.green}✓${c.reset} Token stored for ${name} in ~/.kit/mcp-tokens.json (chmod 0o600)`);
|
|
1272
|
-
const status = await statusForMcp(name, declared ?? null);
|
|
1273
|
-
console.log(`${c.dim}Status: ${status.status}${c.reset}`);
|
|
1274
|
-
return true;
|
|
1275
|
-
}
|
|
1276
|
-
if (sub === "clear") {
|
|
1277
|
-
const name = process.argv[4];
|
|
1278
|
-
if (!name) {
|
|
1279
|
-
console.error(`${c.red}Usage: kit mcp clear <name>${c.reset}`);
|
|
1280
|
-
return false;
|
|
1281
|
-
}
|
|
1282
|
-
await clearMcpToken(name);
|
|
1283
|
-
console.log(`${c.green}✓${c.reset} Cleared token for ${name}`);
|
|
1284
|
-
return true;
|
|
1285
|
-
}
|
|
1286
|
-
console.error(`${c.red}Usage: kit mcp [list | status | auth <name> | set-token <name> | clear <name>]${c.reset}`);
|
|
1287
|
-
return false;
|
|
1288
|
-
}
|
|
1289
|
-
async function cmdAuth() {
|
|
1290
|
-
const sub = process.argv[3];
|
|
1291
|
-
if (sub === "elevate")
|
|
1292
|
-
return cmdAuthElevate();
|
|
1293
|
-
if (sub === "status")
|
|
1294
|
-
return cmdAuthStatus();
|
|
1295
|
-
if (sub === "revoke")
|
|
1296
|
-
return cmdAuthRevoke();
|
|
1297
|
-
if (sub === "setup-totp")
|
|
1298
|
-
return cmdAuthSetupTotp();
|
|
1299
|
-
console.error(`${c.red}Usage: kit auth [elevate | status | revoke | setup-totp]${c.reset}`);
|
|
1300
|
-
return false;
|
|
1301
|
-
}
|
|
1302
|
-
async function cmdAuthSetupTotp() {
|
|
1303
|
-
// kit auth setup-totp [--issuer <name>] [--account <user@host>] [--overwrite]
|
|
1304
|
-
const args = process.argv.slice(4);
|
|
1305
|
-
const issuerIdx = args.indexOf("--issuer");
|
|
1306
|
-
const accountIdx = args.indexOf("--account");
|
|
1307
|
-
const overwrite = hasFlag(args, "--overwrite");
|
|
1308
|
-
const issuer = issuerIdx >= 0 ? args[issuerIdx + 1] : "kit";
|
|
1309
|
-
const defaultAccount = `${process.env.USER ?? "user"}@${process.env.HOSTNAME ?? "host"}`;
|
|
1310
|
-
const accountName = accountIdx >= 0 ? args[accountIdx + 1] : defaultAccount;
|
|
1311
|
-
console.log(`${c.bold}${c.cyan}kit auth setup-totp${c.reset}`);
|
|
1312
|
-
console.log(`${c.dim}${"─".repeat(50)}${c.reset}\n`);
|
|
1313
|
-
let result;
|
|
1314
|
-
try {
|
|
1315
|
-
result = await enrollTotp({ accountName, issuer, overwrite });
|
|
1316
|
-
}
|
|
1317
|
-
catch (err) {
|
|
1318
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
1319
|
-
console.error(`${c.red}✗ ${msg}${c.reset}`);
|
|
1320
|
-
console.error(`${c.dim}If you intentionally want to replace the existing secret, re-run with ${c.bold}--overwrite${c.reset}${c.dim} (your old authenticator entry will stop working).${c.reset}\n`);
|
|
1321
|
-
return false;
|
|
1322
|
-
}
|
|
1323
|
-
console.log(` ${c.green}✓${c.reset} secret written to ${result.filePath} ${c.dim}(chmod 600)${c.reset}\n`);
|
|
1324
|
-
console.log(`${c.bold}Provisioning URI:${c.reset}`);
|
|
1325
|
-
console.log(` ${c.dim}${result.uri}${c.reset}\n`);
|
|
1326
|
-
console.log(`${c.bold}Or enter the secret manually in your authenticator app:${c.reset}`);
|
|
1327
|
-
console.log(` ${c.bold}Secret:${c.reset} ${result.secret}`);
|
|
1328
|
-
console.log(` ${c.bold}Account:${c.reset} ${accountName}`);
|
|
1329
|
-
console.log(` ${c.bold}Issuer:${c.reset} ${issuer}\n`);
|
|
1330
|
-
console.log(`${c.bold}Verify enrollment — the next 6-digit code should be:${c.reset}`);
|
|
1331
|
-
console.log(` ${c.bold}${result.currentCode}${c.reset} ${c.dim}(or the one shown in your authenticator right now)${c.reset}\n`);
|
|
1332
|
-
console.log(`${c.dim}Future ${c.bold}kit auth elevate${c.reset}${c.dim} runs will prompt for the TOTP code automatically.${c.reset}`);
|
|
1333
|
-
console.log(`${c.dim}Override with ${c.bold}KIT_TOTP_SECRET=...${c.reset}${c.dim} in CI / scripted contexts.${c.reset}\n`);
|
|
1334
|
-
return true;
|
|
1335
|
-
}
|
|
1336
|
-
async function cmdAuthElevate() {
|
|
1337
|
-
const args = process.argv.slice(4);
|
|
1338
|
-
const scope = flagValue(args, "--scope") ?? "all";
|
|
1339
|
-
const ttlIdx = args.indexOf("--ttl-minutes");
|
|
1340
|
-
if (ttlIdx >= 0 && args[ttlIdx + 1]) {
|
|
1341
|
-
const n = parseInt(args[ttlIdx + 1], 10);
|
|
1342
|
-
if (Number.isFinite(n) && n > 0 && n <= 240) {
|
|
1343
|
-
process.env.KIT_ELEVATION_TTL_MINUTES = String(n);
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
console.log(`${c.bold}${c.cyan}kit auth elevate${c.reset} ${c.dim}(scope=${scope})${c.reset}`);
|
|
1347
|
-
console.log(`${c.dim}${"─".repeat(50)}${c.reset}\n`);
|
|
1348
|
-
if (isNonInteractive()) {
|
|
1349
|
-
console.error(`${c.red}✗ Elevation requires an interactive TTY. Cannot run from agent / CI.${c.reset}`);
|
|
1350
|
-
console.error(`${c.dim}For CI deploy jobs that legitimately need a destructive op, set ${c.bold}KIT_ELEVATED=1${c.reset}${c.dim} (and audit-log the use).${c.reset}\n`);
|
|
1351
|
-
return false;
|
|
1352
|
-
}
|
|
1353
|
-
// Resolve from env first, then ~/.kit/totp-secret (created by
|
|
1354
|
-
// `kit auth setup-totp`).
|
|
1355
|
-
const totpSecret = await resolveTotpSecret();
|
|
1356
|
-
let method = "yes-prompt";
|
|
1357
|
-
if (totpSecret) {
|
|
1358
|
-
method = "totp";
|
|
1359
|
-
const readline = await import("node:readline/promises");
|
|
1360
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1361
|
-
try {
|
|
1362
|
-
const code = (await rl.question(`Enter 6-digit TOTP from your authenticator: `)).trim();
|
|
1363
|
-
if (!verifyTotp(code, totpSecret)) {
|
|
1364
|
-
console.error(`${c.red}✗ Invalid TOTP code.${c.reset}`);
|
|
1365
|
-
return false;
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
finally {
|
|
1369
|
-
rl.close();
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
else {
|
|
1373
|
-
const ok = await promptConfirm(`Confirm elevation [type YES, default no in 15s]: `, 15_000, false);
|
|
1374
|
-
if (!ok) {
|
|
1375
|
-
console.log(`${c.dim}Aborted.${c.reset}`);
|
|
1376
|
-
return false;
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
const state = await grantElevation(scope, method);
|
|
1380
|
-
console.log(` ${c.green}✓${c.reset} elevated ${c.dim}scope=${state.scope} method=${state.method} expires=${state.expiresAt}${c.reset}`);
|
|
1381
|
-
console.log(`\n${c.dim}Destructive secret ops (rotate, migrate, onecli register, propagate) are unlocked for ${elevationTtlMinutes()}m. Run ${c.bold}kit auth revoke${c.reset}${c.dim} to drop early.${c.reset}\n`);
|
|
1382
|
-
return true;
|
|
1383
|
-
}
|
|
1384
|
-
async function cmdAuthStatus() {
|
|
1385
|
-
const state = await readElevation(process.cwd());
|
|
1386
|
-
if (!state) {
|
|
1387
|
-
console.log(`${c.dim}Not elevated.${c.reset}`);
|
|
1388
|
-
return true;
|
|
1389
|
-
}
|
|
1390
|
-
const expires = Date.parse(state.expiresAt);
|
|
1391
|
-
const valid = Number.isFinite(expires) && expires > Date.now();
|
|
1392
|
-
const status = valid
|
|
1393
|
-
? `${c.green}active${c.reset} ${c.dim}(expires ${state.expiresAt})${c.reset}`
|
|
1394
|
-
: `${c.red}expired${c.reset} ${c.dim}(at ${state.expiresAt})${c.reset}`;
|
|
1395
|
-
console.log(`${status} scope=${state.scope} method=${state.method} granter=${state.granter}`);
|
|
1396
|
-
return true;
|
|
1397
|
-
}
|
|
1398
|
-
async function cmdAuthRevoke() {
|
|
1399
|
-
await clearElevation(process.cwd());
|
|
1400
|
-
console.log(`${c.green}✓${c.reset} elevation marker cleared.`);
|
|
1401
|
-
return true;
|
|
1402
|
-
}
|
|
1403
|
-
async function cmdAuditSecrets() {
|
|
1404
|
-
const args = process.argv.slice(4); // after "audit secrets"
|
|
1405
|
-
const sinceIdx = args.indexOf("--since-days");
|
|
1406
|
-
const sinceDays = sinceIdx >= 0 && args[sinceIdx + 1] ? parseInt(args[sinceIdx + 1], 10) : 30;
|
|
1407
|
-
const keyFilter = flagValue(args, "--key");
|
|
1408
|
-
const jsonMode = hasFlag(args, "--json");
|
|
1409
|
-
const events = await readSecretAuditEvents(process.cwd(), sinceDays);
|
|
1410
|
-
const { reports, unattributed } = groupBySecret(events);
|
|
1411
|
-
const filteredReports = keyFilter
|
|
1412
|
-
? reports.filter((r) => r.key === keyFilter)
|
|
1413
|
-
: reports;
|
|
1414
|
-
const summary = summarize(filteredReports, sinceDays);
|
|
1415
|
-
if (jsonMode) {
|
|
1416
|
-
console.log(JSON.stringify({ summary, reports: filteredReports, unattributed }, null, 2));
|
|
1417
|
-
return true;
|
|
1418
|
-
}
|
|
1419
|
-
console.log(`${c.bold}${c.cyan}kit audit secrets${c.reset} ${c.dim}(last ${sinceDays}d)${c.reset}`);
|
|
1420
|
-
console.log(`${c.dim}${"─".repeat(50)}${c.reset}\n`);
|
|
1421
|
-
if (events.length === 0) {
|
|
1422
|
-
console.log(`${c.dim}No secret-related events in audit log (.kit-audit.jsonl).${c.reset}\n`);
|
|
1423
|
-
return true;
|
|
1424
|
-
}
|
|
1425
|
-
console.log(`${c.bold}${summary.totalEvents}${c.reset} event(s) across ${c.bold}${summary.keyCount}${c.reset} key(s)`);
|
|
1426
|
-
if (summary.topKey) {
|
|
1427
|
-
console.log(`${c.dim}Most touched: ${c.bold}${summary.topKey.key}${c.reset}${c.dim} (${summary.topKey.count} events)${c.reset}`);
|
|
1428
|
-
}
|
|
1429
|
-
console.log();
|
|
1430
|
-
for (const r of filteredReports.slice(0, 20)) {
|
|
1431
|
-
console.log(`${c.bold}${r.key}${c.reset} ${c.dim}(${r.events.length} events)${c.reset}`);
|
|
1432
|
-
for (const e of r.events.slice(-10)) {
|
|
1433
|
-
const icon = e.success ? `${c.green}✓${c.reset}` : `${c.red}✗${c.reset}`;
|
|
1434
|
-
const ts = e.timestamp.slice(0, 19);
|
|
1435
|
-
const agent = e.agent ? `[${e.agent}]` : "";
|
|
1436
|
-
const detail = e.detail ? ` ${c.dim}${e.detail}${c.reset}` : "";
|
|
1437
|
-
console.log(` ${icon} ${ts} ${e.operation} ${c.dim}${agent}${c.reset}${detail}`);
|
|
1438
|
-
}
|
|
1439
|
-
if (r.events.length > 10) {
|
|
1440
|
-
console.log(` ${c.dim}… ${r.events.length - 10} earlier events truncated${c.reset}`);
|
|
1441
|
-
}
|
|
1442
|
-
console.log();
|
|
1443
|
-
}
|
|
1444
|
-
if (!keyFilter && unattributed.length > 0) {
|
|
1445
|
-
console.log(`${c.dim}+ ${unattributed.length} event(s) couldn't be tied to a specific key. Use ${c.bold}--json${c.reset}${c.dim} for the full set.${c.reset}\n`);
|
|
1446
|
-
}
|
|
1447
|
-
return true;
|
|
1448
|
-
}
|
|
1449
|
-
async function cmdAudit() {
|
|
1450
|
-
const args = process.argv.slice(3);
|
|
1451
|
-
// Sub-sub: kit audit secrets [--since-days N] [--key NAME] [--json]
|
|
1452
|
-
if (args[0] === "secrets") {
|
|
1453
|
-
return cmdAuditSecrets();
|
|
1454
|
-
}
|
|
1455
|
-
// Parse --limit N
|
|
1456
|
-
let limit = 20;
|
|
1457
|
-
const limitIdx = args.indexOf("--limit");
|
|
1458
|
-
if (limitIdx !== -1 && args[limitIdx + 1]) {
|
|
1459
|
-
const parsed = parseInt(args[limitIdx + 1], 10);
|
|
1460
|
-
if (!isNaN(parsed) && parsed > 0)
|
|
1461
|
-
limit = parsed;
|
|
1462
|
-
}
|
|
1463
|
-
// Parse --operation <name>
|
|
1464
|
-
let operationFilter;
|
|
1465
|
-
const opIdx = args.indexOf("--operation");
|
|
1466
|
-
if (opIdx !== -1 && args[opIdx + 1]) {
|
|
1467
|
-
operationFilter = args[opIdx + 1];
|
|
1468
|
-
}
|
|
1469
|
-
// Determine log file path (use config if available, else default)
|
|
1470
|
-
let logFile = ".kit-audit.jsonl";
|
|
1471
|
-
try {
|
|
1472
|
-
const config = await loadConfig(resolveConfigPath());
|
|
1473
|
-
const govFile = config.governance?.audit?.log_file;
|
|
1474
|
-
if (govFile)
|
|
1475
|
-
logFile = govFile;
|
|
1476
|
-
}
|
|
1477
|
-
catch {
|
|
1478
|
-
// No .kit.toml — use default log file
|
|
1479
|
-
}
|
|
1480
|
-
let events = await readAuditLog(logFile, limit * 5); // read extra to allow filtering
|
|
1481
|
-
if (operationFilter) {
|
|
1482
|
-
events = events.filter((e) => e.operation.includes(operationFilter));
|
|
1483
|
-
}
|
|
1484
|
-
// Apply limit after filter
|
|
1485
|
-
events = events.slice(-limit);
|
|
1486
|
-
printAuditTable(events);
|
|
1487
|
-
if (events.length > 0) {
|
|
1488
|
-
console.log();
|
|
1489
|
-
console.log(`${c.dim}Showing ${events.length} entries from ${logFile}${c.reset}`);
|
|
1490
|
-
}
|
|
1491
|
-
return true;
|
|
1492
|
-
}
|
|
1493
|
-
async function cmdEnvList() {
|
|
1494
|
-
let config;
|
|
1495
|
-
try {
|
|
1496
|
-
// Load base config without env merge so we can inspect [env.*] sections
|
|
1497
|
-
config = await loadConfig(resolveConfigPath(), "dev");
|
|
1498
|
-
}
|
|
1499
|
-
catch {
|
|
1500
|
-
console.log(`${c.dim}No .kit.toml found — no environments configured.${c.reset}`);
|
|
1501
|
-
return true;
|
|
1502
|
-
}
|
|
1503
|
-
const activeEnv = resolveActiveEnvironment();
|
|
1504
|
-
const envSections = config.env ?? {};
|
|
1505
|
-
const envNames = ["dev", ...Object.keys(envSections).filter((k) => k !== "dev")];
|
|
1506
|
-
console.log(`${c.bold}${c.cyan}Environments${c.reset} ${c.dim}(active: ${activeEnv})${c.reset}\n`);
|
|
1507
|
-
for (const name of envNames) {
|
|
1508
|
-
const isActive = name === activeEnv;
|
|
1509
|
-
const marker = isActive ? `${c.green}●${c.reset} ` : " ";
|
|
1510
|
-
const override = envSections[name];
|
|
1511
|
-
if (!override) {
|
|
1512
|
-
console.log(` ${marker}${c.bold}${name}${c.reset} ${c.dim}base config (default)${c.reset}`);
|
|
1513
|
-
continue;
|
|
1514
|
-
}
|
|
1515
|
-
// Summarise which keys are overridden
|
|
1516
|
-
const overrideKeys = [];
|
|
1517
|
-
if (override.tools)
|
|
1518
|
-
overrideKeys.push(`tools(${Object.keys(override.tools).join(",")})`);
|
|
1519
|
-
if (override.secrets)
|
|
1520
|
-
overrideKeys.push(`secrets.store=${override.secrets.store ?? "?"}`);
|
|
1521
|
-
if (override.services)
|
|
1522
|
-
overrideKeys.push(`services(${Object.keys(override.services).join(",")})`);
|
|
1523
|
-
if (override.governance)
|
|
1524
|
-
overrideKeys.push("governance");
|
|
1525
|
-
if (override.skills)
|
|
1526
|
-
overrideKeys.push("skills");
|
|
1527
|
-
const summary = overrideKeys.length > 0 ? overrideKeys.join(", ") : "no overrides";
|
|
1528
|
-
console.log(` ${marker}${c.bold}${name}${c.reset} ${c.dim}${summary}${c.reset}`);
|
|
1529
|
-
}
|
|
1530
|
-
if (Object.keys(envSections).length === 0) {
|
|
1531
|
-
console.log(`${c.dim}\nNo [env.*] sections defined in .kit.toml.${c.reset}`);
|
|
1532
|
-
console.log(`${c.dim}Add [env.staging.*] or [env.production.*] to configure per-environment overrides.${c.reset}`);
|
|
1533
|
-
}
|
|
1534
|
-
console.log();
|
|
1535
|
-
return true;
|
|
1536
|
-
}
|
|
1537
|
-
async function cmdEnvSwitch() {
|
|
1538
|
-
// argv layout: [node, cli.js, "env", "switch", "<target>"]
|
|
1539
|
-
const target = process.argv[4];
|
|
1540
|
-
if (!target || !KNOWN_ENVS.includes(target)) {
|
|
1541
|
-
console.error(`${c.red}Usage: kit env switch <${KNOWN_ENVS.join("|")}>${c.reset}`);
|
|
1542
|
-
return false;
|
|
1543
|
-
}
|
|
1544
|
-
const env = target;
|
|
1545
|
-
console.log(`${c.bold}${c.cyan}kit env switch ${env}${c.reset}`);
|
|
1546
|
-
console.log(`${c.dim}${"─".repeat(50)}${c.reset}\n`);
|
|
1547
|
-
if (env === "prod" && !isNonInteractive()) {
|
|
1548
|
-
console.log(`${c.red}⚠ Switching to ${c.bold}prod${c.reset}${c.red} unlocks production credentials in this shell.${c.reset}`);
|
|
1549
|
-
console.log(`${c.dim}Anything that reads ${c.bold}.env.local${c.reset}${c.dim} after this point can call live services.${c.reset}\n`);
|
|
1550
|
-
const ok = await promptConfirm(`Continue? [y/N] (auto-no in 10s): `, 10_000, false);
|
|
1551
|
-
if (!ok) {
|
|
1552
|
-
console.log(`${c.dim}Aborted — active env unchanged.${c.reset}`);
|
|
1553
|
-
return false;
|
|
1554
|
-
}
|
|
1555
|
-
}
|
|
1556
|
-
const state = await writeActiveEnv(env, process.cwd());
|
|
1557
|
-
console.log(` ${c.green}✓${c.reset} active env is now ${c.bold}${state.env}${c.reset} ${c.dim}(by ${state.switchedBy} at ${state.switchedAt})${c.reset}`);
|
|
1558
|
-
if (env === "prod") {
|
|
1559
|
-
console.log(`\n${c.dim}Re-run ${c.bold}kit secrets${c.reset}${c.dim} to materialize prod values into .env.local. ${c.bold}kit env switch dev${c.reset}${c.dim} when done.${c.reset}\n`);
|
|
1560
|
-
}
|
|
1561
|
-
else {
|
|
1562
|
-
console.log(`\n${c.dim}Prod-scoped keys are now refused unless you switch back or set ${c.bold}KIT_PROD_OK=1${c.reset}${c.dim}.${c.reset}\n`);
|
|
1563
|
-
}
|
|
1564
|
-
return true;
|
|
1565
|
-
}
|
|
1566
|
-
async function cmdEnvCurrent() {
|
|
1567
|
-
const state = await readActiveEnv(process.cwd());
|
|
1568
|
-
if (!state) {
|
|
1569
|
-
console.log(`${c.dim}No active env set (defaulting to ${c.bold}dev${c.reset}${c.dim}). Run ${c.bold}kit env switch <env>${c.reset}${c.dim} to set one.${c.reset}`);
|
|
1570
|
-
console.log(`dev`);
|
|
1571
|
-
return true;
|
|
1572
|
-
}
|
|
1573
|
-
const color = state.env === "prod" ? c.red : state.env === "staging" ? c.yellow : c.green;
|
|
1574
|
-
console.log(`${color}${state.env}${c.reset} ${c.dim}(set ${state.switchedAt} by ${state.switchedBy})${c.reset}`);
|
|
1575
|
-
return true;
|
|
1576
|
-
}
|
|
1577
|
-
async function cmdEnv() {
|
|
1578
|
-
const args = process.argv.slice(2); // includes "env"
|
|
1579
|
-
// Route subcommand: kit env list / switch / current / validate
|
|
1580
|
-
if (args[1] === "list")
|
|
1581
|
-
return cmdEnvList();
|
|
1582
|
-
if (args[1] === "switch")
|
|
1583
|
-
return cmdEnvSwitch();
|
|
1584
|
-
if (args[1] === "current")
|
|
1585
|
-
return cmdEnvCurrent();
|
|
1586
|
-
const showValues = hasFlag(args, "--show-values");
|
|
1587
|
-
const missingOnly = hasFlag(args, "--missing");
|
|
1588
|
-
const jsonMode = hasFlag(args, "--json");
|
|
1589
|
-
let config = {};
|
|
1590
|
-
try {
|
|
1591
|
-
config = await loadConfig(resolveConfigPath());
|
|
1592
|
-
}
|
|
1593
|
-
catch {
|
|
1594
|
-
// Works without .kit.toml — shows .env.local contents only
|
|
1595
|
-
}
|
|
1596
|
-
const result = await inspectEnv(config, { showValues, missingOnly, cwd: process.cwd() });
|
|
1597
|
-
if (jsonMode) {
|
|
1598
|
-
console.log(JSON.stringify(result, null, 2));
|
|
1599
|
-
return result.ok;
|
|
1600
|
-
}
|
|
1601
|
-
console.log(`${c.bold}${c.cyan}Environment${c.reset}`);
|
|
1602
|
-
console.log(`${c.dim}${"─".repeat(50)}${c.reset}\n`);
|
|
1603
|
-
if (!result.envLocalExists) {
|
|
1604
|
-
console.log(`${c.yellow}⚠${c.reset} .env.local not found — run ${c.bold}kit secrets${c.reset} to generate it\n`);
|
|
1605
|
-
}
|
|
1606
|
-
if (result.keys.length === 0) {
|
|
1607
|
-
if (missingOnly) {
|
|
1608
|
-
console.log(`${c.green}✓${c.reset} All keys are set\n`);
|
|
1609
|
-
}
|
|
1610
|
-
else {
|
|
1611
|
-
console.log(`${c.dim}No keys configured or found${c.reset}\n`);
|
|
1612
|
-
}
|
|
1613
|
-
return result.ok;
|
|
1614
|
-
}
|
|
1615
|
-
for (const key of result.keys) {
|
|
1616
|
-
const icon = key.set ? `${c.green}✓${c.reset}` : `${c.red}✗${c.reset}`;
|
|
1617
|
-
const valueStr = key.set
|
|
1618
|
-
? showValues
|
|
1619
|
-
? `${c.dim}${key.value}${c.reset}`
|
|
1620
|
-
: `${c.dim}${key.redacted}${c.reset}`
|
|
1621
|
-
: `${c.red}not set${c.reset}`;
|
|
1622
|
-
const src = `${c.dim}[${key.source}]${c.reset}`;
|
|
1623
|
-
console.log(` ${icon} ${key.name} ${valueStr} ${src}`);
|
|
1624
|
-
}
|
|
1625
|
-
console.log();
|
|
1626
|
-
if (!result.ok && !missingOnly) {
|
|
1627
|
-
console.log(`${c.dim}Run ${c.reset}${c.bold}kit env --missing${c.reset}${c.dim} to see only unset keys${c.reset}`);
|
|
1628
|
-
console.log(`${c.dim}Run ${c.reset}${c.bold}kit secrets${c.reset}${c.dim} to generate .env.local${c.reset}\n`);
|
|
1629
|
-
}
|
|
1630
|
-
return result.ok;
|
|
1631
|
-
}
|
|
1632
1037
|
async function cmdDoctor() {
|
|
1633
1038
|
console.log(`${c.bold}${c.cyan}kit doctor${c.reset}`);
|
|
1634
1039
|
console.log(`${c.dim}${"─".repeat(50)}${c.reset}\n`);
|
|
@@ -1932,6 +1337,52 @@ async function cmdSecretsRevokeOld() {
|
|
|
1932
1337
|
console.log(`\n${c.dim}Verify with ${c.bold}kit secrets onecli status${c.reset}${c.dim} or the Supabase Dashboard.${c.reset}\n`);
|
|
1933
1338
|
return true;
|
|
1934
1339
|
}
|
|
1340
|
+
async function cmdSecretsSet() {
|
|
1341
|
+
// kit secrets set <KEY> [--value <v> | --stdin] [--store <backend>]
|
|
1342
|
+
// Capture-to-vault: write a user-provided value to the configured vault. This
|
|
1343
|
+
// is the execution behind a service's `auth = "capture"` strategy. The value
|
|
1344
|
+
// is read via --stdin (safer — not in argv/ps) or --value; kit reuses the
|
|
1345
|
+
// existing setSecretValue path, so it never echoes or logs the secret.
|
|
1346
|
+
const args = process.argv.slice(3);
|
|
1347
|
+
const keyName = process.argv[4];
|
|
1348
|
+
if (!keyName || keyName.startsWith("--")) {
|
|
1349
|
+
console.error(`${c.red}Usage: kit secrets set <KEY> [--value <v> | --stdin] [--store <backend>]${c.reset}`);
|
|
1350
|
+
return false;
|
|
1351
|
+
}
|
|
1352
|
+
const config = await loadConfig(resolveConfigPath());
|
|
1353
|
+
// Read value: --value <v> (visible in argv/ps) or --stdin (safer).
|
|
1354
|
+
let value;
|
|
1355
|
+
const valueIdx = args.indexOf("--value");
|
|
1356
|
+
if (valueIdx >= 0)
|
|
1357
|
+
value = args[valueIdx + 1];
|
|
1358
|
+
if (!value && hasFlag(args, "--stdin")) {
|
|
1359
|
+
value = await new Promise((resolve) => {
|
|
1360
|
+
let buf = "";
|
|
1361
|
+
process.stdin.setEncoding("utf-8");
|
|
1362
|
+
process.stdin.on("data", (chunk) => {
|
|
1363
|
+
buf += chunk;
|
|
1364
|
+
});
|
|
1365
|
+
process.stdin.on("end", () => resolve(buf.trim()));
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
if (!value) {
|
|
1369
|
+
console.error(`${c.red}Provide the value via --stdin (safer) or --value <v> (visible in argv/ps).${c.reset}`);
|
|
1370
|
+
return false;
|
|
1371
|
+
}
|
|
1372
|
+
const storeIdx = args.indexOf("--store");
|
|
1373
|
+
const storeOverride = storeIdx >= 0 ? args[storeIdx + 1] : undefined;
|
|
1374
|
+
const backendOpts = pickBackendOpts(config.secrets ?? {}, keyName, { envFallback: true });
|
|
1375
|
+
const result = await setSecretValue(config.secrets, keyName, value, {
|
|
1376
|
+
...backendOpts,
|
|
1377
|
+
store: storeOverride,
|
|
1378
|
+
});
|
|
1379
|
+
if (!result.ok) {
|
|
1380
|
+
console.error(`${c.red}✗ ${result.detail}${c.reset}`);
|
|
1381
|
+
return false;
|
|
1382
|
+
}
|
|
1383
|
+
console.log(`${c.green}✓${c.reset} captured ${c.bold}${keyName}${c.reset} to the vault ${c.dim}(${result.detail})${c.reset}`);
|
|
1384
|
+
return true;
|
|
1385
|
+
}
|
|
1935
1386
|
async function cmdSecretsPropagateStandalone() {
|
|
1936
1387
|
// kit secrets propagate <KEY> --value <v> | --stdin --to <targets> [opts]
|
|
1937
1388
|
const args = process.argv.slice(4);
|
|
@@ -2225,7 +1676,7 @@ async function cmdSecretsVaultMigrate() {
|
|
|
2225
1676
|
console.log("");
|
|
2226
1677
|
console.log("Migration is gated by elevation (one-shot — consumed on use):");
|
|
2227
1678
|
console.log(" kit auth elevate --scope vault-migrate");
|
|
2228
|
-
return !fromArg || !toArg
|
|
1679
|
+
return !(!fromArg || !toArg);
|
|
2229
1680
|
}
|
|
2230
1681
|
console.log(`${c.bold}${c.cyan}kit secrets vault-migrate${c.reset}`);
|
|
2231
1682
|
console.log(`${c.dim}${"─".repeat(50)}${c.reset}\n`);
|
|
@@ -2352,8 +1803,9 @@ function emitGitlabJunit(checks, allOk) {
|
|
|
2352
1803
|
}
|
|
2353
1804
|
lines.push(` </testsuite>`, `</testsuites>`);
|
|
2354
1805
|
const xml = lines.join("\n");
|
|
2355
|
-
// Write to file for GitLab artifact collection
|
|
2356
|
-
|
|
1806
|
+
// Write to file for GitLab artifact collection — synchronous so the report is
|
|
1807
|
+
// guaranteed flushed before this (sync) function returns.
|
|
1808
|
+
writeFileSync("kit-report.xml", xml, "utf8");
|
|
2357
1809
|
if (!allOk || warnings.length > 0) {
|
|
2358
1810
|
console.log(`CI report written to kit-report.xml (${failures.length} failures, ${warnings.length} warnings)`);
|
|
2359
1811
|
}
|
|
@@ -3218,7 +2670,7 @@ async function cmdOpen() {
|
|
|
3218
2670
|
const args = process.argv.slice(2);
|
|
3219
2671
|
const serviceName = args[1];
|
|
3220
2672
|
// Load env from .env.local for dashboard URL resolution
|
|
3221
|
-
|
|
2673
|
+
const env = {};
|
|
3222
2674
|
try {
|
|
3223
2675
|
const envPath = resolve(process.cwd(), ".env.local");
|
|
3224
2676
|
const { readFileSync } = await import("node:fs");
|
|
@@ -3329,6 +2781,21 @@ async function cmdContext() {
|
|
|
3329
2781
|
return false;
|
|
3330
2782
|
}
|
|
3331
2783
|
}
|
|
2784
|
+
async function cmdStatus() {
|
|
2785
|
+
const items = await gatherStatus();
|
|
2786
|
+
if (hasFlag(process.argv, "--json")) {
|
|
2787
|
+
console.log(JSON.stringify(items, null, 2));
|
|
2788
|
+
return true;
|
|
2789
|
+
}
|
|
2790
|
+
const done = items.filter((i) => i.ok).length;
|
|
2791
|
+
console.log(`${c.bold}kit status${c.reset} ${c.dim}${done}/${items.length} set up${c.reset}`);
|
|
2792
|
+
for (const item of items) {
|
|
2793
|
+
const mark = item.ok ? `${c.green}✓${c.reset}` : `${c.yellow}○${c.reset}`;
|
|
2794
|
+
const hint = !item.ok && item.hint ? ` ${c.dim}→ ${item.hint}${c.reset}` : "";
|
|
2795
|
+
console.log(` ${mark} ${item.label} ${c.dim}${item.detail}${c.reset}${hint}`);
|
|
2796
|
+
}
|
|
2797
|
+
return true;
|
|
2798
|
+
}
|
|
3332
2799
|
async function cmdWhoami() {
|
|
3333
2800
|
const jsonMode = hasFlag(process.argv, "--json");
|
|
3334
2801
|
let config = {};
|
|
@@ -3400,12 +2867,15 @@ function cmdVersion() {
|
|
|
3400
2867
|
return true;
|
|
3401
2868
|
}
|
|
3402
2869
|
const COMMAND_HELP = {
|
|
2870
|
+
status: "Adoption checklist — what's set up across kit + the next step for each gap",
|
|
3403
2871
|
check: "Check status of all tools, services, secrets, and lock files",
|
|
3404
2872
|
memory: "Local conversation memory — index transcripts + show stats",
|
|
3405
2873
|
"memory index": "Index ~/.claude transcripts into the SQLite memory store",
|
|
3406
2874
|
"memory search": "Full-text search memory (current project; --global for all)",
|
|
3407
2875
|
"memory stats": "Show what the local memory store contains",
|
|
3408
|
-
"memory
|
|
2876
|
+
"memory suggest": "Emit a BYO-LLM review prompt (recent activity + open items) — pipe to your own model",
|
|
2877
|
+
"memory merge": "Merge another machine's memory.db into this one (dedup by uuid)",
|
|
2878
|
+
"memory install": "Wire UserPromptSubmit + SessionEnd + SessionStart (recovery) hooks into ~/.claude/settings.json",
|
|
3409
2879
|
"memory scan": "Scan the memory store for stored secrets (exit 1 if any found)",
|
|
3410
2880
|
"memory backup": "Encrypted backup of the memory store (AES-256-GCM; KIT_MEMORY_PASSPHRASE)",
|
|
3411
2881
|
"memory restore": "Restore an encrypted memory backup (e.g. on a new machine)",
|
|
@@ -3420,9 +2890,11 @@ const COMMAND_HELP = {
|
|
|
3420
2890
|
upgrade: "Update lock files from .kit.toml",
|
|
3421
2891
|
install: "Install missing tools via mise",
|
|
3422
2892
|
login: "Guided login to all configured services",
|
|
2893
|
+
"login --plan": "Show the resolved auth strategy per service (vault/interactive/capture + passkey) without logging in",
|
|
3423
2894
|
secrets: "Generate .env.local from template + secret store",
|
|
3424
2895
|
"secrets sync": "Push resolved secrets to GitHub Actions / .env.ci / stdout",
|
|
3425
2896
|
"secrets migrate": "Migrate plaintext secrets in .env* → configured vault",
|
|
2897
|
+
"secrets set": "Capture a value to the vault: kit secrets set <KEY> --stdin (safer) | --value <v>",
|
|
3426
2898
|
"secrets rotate": "Rotate a key: write new value to vault (explicit / random)",
|
|
3427
2899
|
"secrets onecli": "Register a key with OneCLI gateway so agent never sees the real value",
|
|
3428
2900
|
"secrets purge-history": "Destructive: rewrite git history to remove a leaked value (--force-history)",
|
|
@@ -3887,430 +3359,6 @@ async function showSkippedCommitBanner() {
|
|
|
3887
3359
|
* `kit memory` — local conversation memory (SQLite + FTS5). Deterministic and
|
|
3888
3360
|
* zero-LLM: it stores raw transcripts and searches them; it never calls a model.
|
|
3889
3361
|
*/
|
|
3890
|
-
async function cmdMemory() {
|
|
3891
|
-
const subcommand = process.argv[3];
|
|
3892
|
-
const jsonMode = hasFlag(process.argv, "--json");
|
|
3893
|
-
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
3894
|
-
console.log("kit memory — local conversation memory (SQLite + FTS5)");
|
|
3895
|
-
console.log("\nUsage:");
|
|
3896
|
-
console.log(" kit memory index Index ~/.claude transcripts into the memory store");
|
|
3897
|
-
console.log(" kit memory search <query> Search memory (current project; --global for all)");
|
|
3898
|
-
console.log(" kit memory stats Show what the memory store contains");
|
|
3899
|
-
console.log(" kit memory install Wire the 2 hooks into ~/.claude/settings.json");
|
|
3900
|
-
console.log(" kit memory uninstall Remove the hooks");
|
|
3901
|
-
console.log(" kit memory pal [list|add|done|snooze|verify|import] Pending action ledger");
|
|
3902
|
-
console.log(" kit memory save <name> Bookmark the current session as a named copilot");
|
|
3903
|
-
console.log(" kit memory threads List saved copilots (--global for all)");
|
|
3904
|
-
console.log(" kit memory resume <name|n> Print the resume command for a saved copilot");
|
|
3905
|
-
console.log(" kit memory forget <name> Remove a saved copilot");
|
|
3906
|
-
console.log(" kit memory scan Scan the store for stored secrets");
|
|
3907
|
-
console.log(" kit memory backup <file> Encrypted backup (set KIT_MEMORY_PASSPHRASE)");
|
|
3908
|
-
console.log(" kit memory restore <file> Restore an encrypted backup (new machine)");
|
|
3909
|
-
console.log(" kit memory share … Promote a curated entry to shared (team) memory");
|
|
3910
|
-
console.log(" kit memory areas List shared responsibility areas");
|
|
3911
|
-
console.log(" kit memory area <name> Show shared entries for one area");
|
|
3912
|
-
return true;
|
|
3913
|
-
}
|
|
3914
|
-
if (subcommand === "index") {
|
|
3915
|
-
const db = openMemoryDb();
|
|
3916
|
-
const t0 = Date.now();
|
|
3917
|
-
const res = indexClaudeTranscripts(db);
|
|
3918
|
-
const ms = Date.now() - t0;
|
|
3919
|
-
db.close();
|
|
3920
|
-
if (jsonMode) {
|
|
3921
|
-
console.log(JSON.stringify({ ...res, ms }));
|
|
3922
|
-
return true;
|
|
3923
|
-
}
|
|
3924
|
-
console.log(`${c.green}✓${c.reset} indexed ${c.bold}${res.messages}${c.reset} messages + ${res.toolUses} tool-uses from ${res.files} sessions ${c.dim}(${ms}ms)${c.reset}`);
|
|
3925
|
-
console.log(`${c.dim}source: ${getClaudeProjectsDir()}${c.reset}`);
|
|
3926
|
-
return true;
|
|
3927
|
-
}
|
|
3928
|
-
if (subcommand === "stats") {
|
|
3929
|
-
const db = openMemoryDb();
|
|
3930
|
-
const s = getStats(db);
|
|
3931
|
-
db.close();
|
|
3932
|
-
if (jsonMode) {
|
|
3933
|
-
console.log(JSON.stringify(s));
|
|
3934
|
-
return true;
|
|
3935
|
-
}
|
|
3936
|
-
console.log(`${c.bold}kit memory${c.reset} ${c.dim}${s.dbPath}${c.reset}`);
|
|
3937
|
-
console.log(` sessions ${s.sessions}`);
|
|
3938
|
-
console.log(` messages ${s.messages}`);
|
|
3939
|
-
console.log(` tool-uses ${s.toolUses}`);
|
|
3940
|
-
console.log(` pending ${s.pendingOpen} ${c.dim}(open action items)${c.reset}`);
|
|
3941
|
-
console.log(` size ${Math.round(s.sizeBytes / 1024)} KB`);
|
|
3942
|
-
return true;
|
|
3943
|
-
}
|
|
3944
|
-
if (subcommand === "search") {
|
|
3945
|
-
const terms = process.argv.slice(4).filter((a) => !a.startsWith("--"));
|
|
3946
|
-
const query = terms.join(" ").trim();
|
|
3947
|
-
if (!query) {
|
|
3948
|
-
console.error(`${c.red}usage: kit memory search <query> [--global] [--project=<path>] [--limit=N]${c.reset}`);
|
|
3949
|
-
return false;
|
|
3950
|
-
}
|
|
3951
|
-
const limit = Number(flagValue(process.argv, "--limit") ?? "20") || 20;
|
|
3952
|
-
const projectPath = hasFlag(process.argv, "--global")
|
|
3953
|
-
? undefined
|
|
3954
|
-
: (flagValue(process.argv, "--project") ?? getCurrentProjectRoot());
|
|
3955
|
-
const db = openMemoryDb();
|
|
3956
|
-
const hits = searchMessages(db, query, { limit, projectPath });
|
|
3957
|
-
db.close();
|
|
3958
|
-
if (jsonMode) {
|
|
3959
|
-
console.log(JSON.stringify(hits));
|
|
3960
|
-
return true;
|
|
3961
|
-
}
|
|
3962
|
-
const scope = projectPath ? `${c.dim}in ${projectPath}${c.reset}` : `${c.dim}(global)${c.reset}`;
|
|
3963
|
-
if (!hits.length) {
|
|
3964
|
-
console.log(`${c.dim}no matches for "${query}" ${projectPath ?? "(global)"}${c.reset}`);
|
|
3965
|
-
return true;
|
|
3966
|
-
}
|
|
3967
|
-
console.log(`${c.bold}${hits.length}${c.reset} match(es) ${scope}`);
|
|
3968
|
-
for (const h of hits) {
|
|
3969
|
-
const snippet = (h.content ?? "").replace(/\s+/g, " ").slice(0, 120);
|
|
3970
|
-
console.log(` ${c.dim}${h.timestamp ?? "?"}${c.reset} ${c.bold}${h.role ?? h.uuid ?? ""}${c.reset} ${snippet}`);
|
|
3971
|
-
}
|
|
3972
|
-
return true;
|
|
3973
|
-
}
|
|
3974
|
-
if (subcommand === "hook") {
|
|
3975
|
-
// Internal: invoked by Claude Code hooks. Fail-open — never block.
|
|
3976
|
-
const event = process.argv[4];
|
|
3977
|
-
if (event === "user-prompt-submit") {
|
|
3978
|
-
const text = userPromptSubmitReminder();
|
|
3979
|
-
if (text)
|
|
3980
|
-
console.log(text);
|
|
3981
|
-
return true;
|
|
3982
|
-
}
|
|
3983
|
-
if (event === "session-end") {
|
|
3984
|
-
runSessionEndIndex();
|
|
3985
|
-
return true;
|
|
3986
|
-
}
|
|
3987
|
-
console.error(`${c.red}Unknown hook event: ${event ?? "(none)"}${c.reset}`);
|
|
3988
|
-
return false;
|
|
3989
|
-
}
|
|
3990
|
-
if (subcommand === "install") {
|
|
3991
|
-
const { added, alreadyPresent } = installMemoryHooks();
|
|
3992
|
-
for (const e of added)
|
|
3993
|
-
console.log(`${c.green}✓${c.reset} installed ${e} hook`);
|
|
3994
|
-
for (const e of alreadyPresent)
|
|
3995
|
-
console.log(`${c.dim}• ${e} hook already present${c.reset}`);
|
|
3996
|
-
console.log(`${c.dim}settings: ${getClaudeSettingsPath()}${c.reset}`);
|
|
3997
|
-
return true;
|
|
3998
|
-
}
|
|
3999
|
-
if (subcommand === "uninstall") {
|
|
4000
|
-
const { removed } = uninstallMemoryHooks();
|
|
4001
|
-
if (removed.length) {
|
|
4002
|
-
for (const e of removed)
|
|
4003
|
-
console.log(`${c.green}✓${c.reset} removed ${e} hook`);
|
|
4004
|
-
}
|
|
4005
|
-
else {
|
|
4006
|
-
console.log(`${c.dim}no kit memory hooks were installed${c.reset}`);
|
|
4007
|
-
}
|
|
4008
|
-
return true;
|
|
4009
|
-
}
|
|
4010
|
-
if (subcommand === "share") {
|
|
4011
|
-
const area = flagValue(process.argv, "--area");
|
|
4012
|
-
const title = flagValue(process.argv, "--title");
|
|
4013
|
-
const kind = (flagValue(process.argv, "--kind") ?? "note");
|
|
4014
|
-
const body = flagValue(process.argv, "--body") ?? "";
|
|
4015
|
-
const ref = flagValue(process.argv, "--ref");
|
|
4016
|
-
if (!area || !title) {
|
|
4017
|
-
console.error(`${c.red}usage: kit memory share --area <a> --title <t> [--kind decision|convention|how-built|status|security|note] [--body <b>] [--ref <r>]${c.reset}`);
|
|
4018
|
-
return false;
|
|
4019
|
-
}
|
|
4020
|
-
const root = getCurrentProjectRoot();
|
|
4021
|
-
try {
|
|
4022
|
-
const e = shareEntry(root, { area, kind, title, body, refs: ref ? [ref] : [] }, new Date().toISOString());
|
|
4023
|
-
console.log(`${c.green}✓${c.reset} shared ${c.bold}${e.id}${c.reset} to area ${c.bold}${area}${c.reset} ${c.dim}(${getSharedPath(root)})${c.reset}`);
|
|
4024
|
-
console.log(`${c.dim}commit .kit/shared/memory.jsonl + open a PR — shared memory is reviewed like code${c.reset}`);
|
|
4025
|
-
}
|
|
4026
|
-
catch (err) {
|
|
4027
|
-
console.error(`${c.red}${err.message}${c.reset}`);
|
|
4028
|
-
return false;
|
|
4029
|
-
}
|
|
4030
|
-
return true;
|
|
4031
|
-
}
|
|
4032
|
-
if (subcommand === "areas") {
|
|
4033
|
-
const areas = listAreas(getCurrentProjectRoot());
|
|
4034
|
-
if (jsonMode) {
|
|
4035
|
-
console.log(JSON.stringify(areas));
|
|
4036
|
-
return true;
|
|
4037
|
-
}
|
|
4038
|
-
if (!areas.length) {
|
|
4039
|
-
console.log(`${c.dim}no shared areas yet — add one with kit memory share${c.reset}`);
|
|
4040
|
-
return true;
|
|
4041
|
-
}
|
|
4042
|
-
console.log(`${c.bold}${areas.length}${c.reset} responsibility area(s):`);
|
|
4043
|
-
for (const a of areas) {
|
|
4044
|
-
console.log(` ${c.bold}${a.area}${c.reset} ${c.dim}· ${a.count} entr${a.count === 1 ? "y" : "ies"}${c.reset}`);
|
|
4045
|
-
}
|
|
4046
|
-
return true;
|
|
4047
|
-
}
|
|
4048
|
-
if (subcommand === "area") {
|
|
4049
|
-
const name = process.argv[4];
|
|
4050
|
-
if (!name) {
|
|
4051
|
-
console.error(`${c.red}usage: kit memory area <name>${c.reset}`);
|
|
4052
|
-
return false;
|
|
4053
|
-
}
|
|
4054
|
-
const entries = queryArea(getCurrentProjectRoot(), name);
|
|
4055
|
-
if (jsonMode) {
|
|
4056
|
-
console.log(JSON.stringify(entries));
|
|
4057
|
-
return true;
|
|
4058
|
-
}
|
|
4059
|
-
if (!entries.length) {
|
|
4060
|
-
console.log(`${c.dim}no shared memory for area '${name}'${c.reset}`);
|
|
4061
|
-
return true;
|
|
4062
|
-
}
|
|
4063
|
-
console.log(`${c.bold}${name}${c.reset} ${c.dim}· ${entries.length} entr${entries.length === 1 ? "y" : "ies"}${c.reset}`);
|
|
4064
|
-
for (const e of entries) {
|
|
4065
|
-
const prov = `${e.author}${e.source_ref ? ` @${e.source_ref}` : ""}`;
|
|
4066
|
-
console.log(` ${c.bold}[${e.kind}]${c.reset} ${e.title} ${c.dim}— ${prov}${c.reset}`);
|
|
4067
|
-
if (e.body)
|
|
4068
|
-
console.log(` ${e.body}`);
|
|
4069
|
-
if (e.refs.length)
|
|
4070
|
-
console.log(` ${c.dim}refs: ${e.refs.join(", ")}${c.reset}`);
|
|
4071
|
-
}
|
|
4072
|
-
return true;
|
|
4073
|
-
}
|
|
4074
|
-
if (subcommand === "scan") {
|
|
4075
|
-
const db = openMemoryDb();
|
|
4076
|
-
const findings = scanDbForSecrets(db);
|
|
4077
|
-
db.close();
|
|
4078
|
-
if (jsonMode) {
|
|
4079
|
-
console.log(JSON.stringify(findings));
|
|
4080
|
-
return !findings.some((f) => f.confidence === "high");
|
|
4081
|
-
}
|
|
4082
|
-
if (!findings.length) {
|
|
4083
|
-
console.log(`${c.green}✓${c.reset} no stored secrets found in the memory store`);
|
|
4084
|
-
return true;
|
|
4085
|
-
}
|
|
4086
|
-
const high = findings.filter((f) => f.confidence === "high");
|
|
4087
|
-
const heuristic = findings.filter((f) => f.confidence === "heuristic");
|
|
4088
|
-
const times = (n) => (n > 1 ? ` ×${n}` : "");
|
|
4089
|
-
if (high.length) {
|
|
4090
|
-
console.log(`${c.red}⚠ ${high.length} high-confidence secret(s):${c.reset}`);
|
|
4091
|
-
for (const f of high) {
|
|
4092
|
-
const proj = f.projects.length ? `${c.bold}[${f.projects.join(", ")}]${c.reset}${c.dim} · ` : "";
|
|
4093
|
-
console.log(` ${c.bold}${f.label}${c.reset} ${c.dim}${f.preview}${times(f.count)} · ${proj}${f.sample}${c.reset}`);
|
|
4094
|
-
}
|
|
4095
|
-
}
|
|
4096
|
-
else {
|
|
4097
|
-
console.log(`${c.green}✓${c.reset} no high-confidence secrets`);
|
|
4098
|
-
}
|
|
4099
|
-
if (heuristic.length) {
|
|
4100
|
-
const showAll = hasFlag(process.argv, "--all");
|
|
4101
|
-
if (showAll) {
|
|
4102
|
-
console.log(`${c.dim}${heuristic.length} heuristic match(es) (KEY=value patterns — usually env vars / paths):${c.reset}`);
|
|
4103
|
-
for (const f of heuristic) {
|
|
4104
|
-
console.log(` ${c.dim}${f.label} ${f.preview}${times(f.count)} · ${f.sample}${c.reset}`);
|
|
4105
|
-
}
|
|
4106
|
-
}
|
|
4107
|
-
else {
|
|
4108
|
-
console.log(`${c.dim}+ ${heuristic.length} heuristic match(es) (likely env vars / paths) — run with --all to see${c.reset}`);
|
|
4109
|
-
}
|
|
4110
|
-
}
|
|
4111
|
-
return high.length === 0; // exit non-zero only on high-confidence findings
|
|
4112
|
-
}
|
|
4113
|
-
if (subcommand === "backup") {
|
|
4114
|
-
const out = process.argv[4];
|
|
4115
|
-
const pass = process.env.KIT_MEMORY_PASSPHRASE ?? flagValue(process.argv, "--passphrase");
|
|
4116
|
-
if (!out) {
|
|
4117
|
-
console.error(`${c.red}usage: kit memory backup <file> (set KIT_MEMORY_PASSPHRASE)${c.reset}`);
|
|
4118
|
-
return false;
|
|
4119
|
-
}
|
|
4120
|
-
if (!pass) {
|
|
4121
|
-
console.error(`${c.red}set KIT_MEMORY_PASSPHRASE (or --passphrase) — the key is never stored${c.reset}`);
|
|
4122
|
-
return false;
|
|
4123
|
-
}
|
|
4124
|
-
try {
|
|
4125
|
-
backupEncrypted(pass, getMemoryDbPath(), out);
|
|
4126
|
-
}
|
|
4127
|
-
catch (err) {
|
|
4128
|
-
console.error(`${c.red}${err.message}${c.reset}`);
|
|
4129
|
-
return false;
|
|
4130
|
-
}
|
|
4131
|
-
console.log(`${c.green}✓${c.reset} encrypted backup → ${out} ${c.dim}(AES-256-GCM · scrypt)${c.reset}`);
|
|
4132
|
-
return true;
|
|
4133
|
-
}
|
|
4134
|
-
if (subcommand === "restore") {
|
|
4135
|
-
const inFile = process.argv[4];
|
|
4136
|
-
const pass = process.env.KIT_MEMORY_PASSPHRASE ?? flagValue(process.argv, "--passphrase");
|
|
4137
|
-
if (!inFile) {
|
|
4138
|
-
console.error(`${c.red}usage: kit memory restore <file> [--to <path>] [--force]${c.reset}`);
|
|
4139
|
-
return false;
|
|
4140
|
-
}
|
|
4141
|
-
if (!pass) {
|
|
4142
|
-
console.error(`${c.red}set KIT_MEMORY_PASSPHRASE (or --passphrase)${c.reset}`);
|
|
4143
|
-
return false;
|
|
4144
|
-
}
|
|
4145
|
-
const dest = flagValue(process.argv, "--to") ?? getMemoryDbPath();
|
|
4146
|
-
if (existsSync(dest) && !hasFlag(process.argv, "--force")) {
|
|
4147
|
-
console.error(`${c.red}${dest} exists — pass --force to overwrite${c.reset}`);
|
|
4148
|
-
return false;
|
|
4149
|
-
}
|
|
4150
|
-
try {
|
|
4151
|
-
restoreEncrypted(pass, inFile, dest);
|
|
4152
|
-
}
|
|
4153
|
-
catch {
|
|
4154
|
-
console.error(`${c.red}restore failed — wrong passphrase or corrupt backup${c.reset}`);
|
|
4155
|
-
return false;
|
|
4156
|
-
}
|
|
4157
|
-
console.log(`${c.green}✓${c.reset} restored → ${dest}`);
|
|
4158
|
-
return true;
|
|
4159
|
-
}
|
|
4160
|
-
if (subcommand === "save") {
|
|
4161
|
-
const name = process.argv.slice(4).filter((a) => !a.startsWith("--")).join(" ").trim();
|
|
4162
|
-
if (!name) {
|
|
4163
|
-
console.error(`${c.red}usage: kit memory save <name> [--session=<id>]${c.reset}`);
|
|
4164
|
-
return false;
|
|
4165
|
-
}
|
|
4166
|
-
const root = getCurrentProjectRoot();
|
|
4167
|
-
const db = openMemoryDb();
|
|
4168
|
-
const sessionId = flagValue(process.argv, "--session") ?? latestSessionId(db, { projectPath: root });
|
|
4169
|
-
if (!sessionId) {
|
|
4170
|
-
db.close();
|
|
4171
|
-
console.error(`${c.red}no session found for ${root} — index first or pass --session=<id>${c.reset}`);
|
|
4172
|
-
return false;
|
|
4173
|
-
}
|
|
4174
|
-
saveThread(db, { name, sessionId, projectPath: root });
|
|
4175
|
-
db.close();
|
|
4176
|
-
console.log(`${c.green}✓${c.reset} saved copilot ${c.bold}${name}${c.reset} ${c.dim}→ ${sessionId}${c.reset}`);
|
|
4177
|
-
return true;
|
|
4178
|
-
}
|
|
4179
|
-
if (subcommand === "threads") {
|
|
4180
|
-
const projectPath = hasFlag(process.argv, "--global") ? undefined : getCurrentProjectRoot();
|
|
4181
|
-
const db = openMemoryDb();
|
|
4182
|
-
const list = listThreads(db, { projectPath });
|
|
4183
|
-
db.close();
|
|
4184
|
-
if (jsonMode) {
|
|
4185
|
-
console.log(JSON.stringify(list));
|
|
4186
|
-
return true;
|
|
4187
|
-
}
|
|
4188
|
-
if (!list.length) {
|
|
4189
|
-
console.log(`${c.dim}no saved copilots${projectPath ? ` in ${projectPath}` : ""}${c.reset}`);
|
|
4190
|
-
return true;
|
|
4191
|
-
}
|
|
4192
|
-
const scope = projectPath ? `${c.dim}in ${projectPath}${c.reset}` : `${c.dim}(global)${c.reset}`;
|
|
4193
|
-
console.log(`${c.bold}${list.length}${c.reset} saved copilot(s) ${scope}:`);
|
|
4194
|
-
list.forEach((t, i) => {
|
|
4195
|
-
console.log(` ${c.bold}${i + 1}${c.reset}. ${t.name} ${c.dim}${t.session_id}${c.reset}`);
|
|
4196
|
-
});
|
|
4197
|
-
console.log(`${c.dim}resume with: kit memory resume <name|number>${c.reset}`);
|
|
4198
|
-
return true;
|
|
4199
|
-
}
|
|
4200
|
-
if (subcommand === "resume") {
|
|
4201
|
-
const ref = process.argv[4];
|
|
4202
|
-
if (!ref) {
|
|
4203
|
-
console.error(`${c.red}usage: kit memory resume <name|number>${c.reset}`);
|
|
4204
|
-
return false;
|
|
4205
|
-
}
|
|
4206
|
-
const projectPath = hasFlag(process.argv, "--global") ? undefined : getCurrentProjectRoot();
|
|
4207
|
-
const db = openMemoryDb();
|
|
4208
|
-
const t = resolveThread(db, ref, { projectPath });
|
|
4209
|
-
db.close();
|
|
4210
|
-
if (!t) {
|
|
4211
|
-
console.error(`${c.red}no saved copilot '${ref}'${c.reset}`);
|
|
4212
|
-
return false;
|
|
4213
|
-
}
|
|
4214
|
-
console.log(`${c.bold}${t.name}${c.reset} — run:`);
|
|
4215
|
-
console.log(` claude --resume ${t.session_id}`);
|
|
4216
|
-
return true;
|
|
4217
|
-
}
|
|
4218
|
-
if (subcommand === "forget") {
|
|
4219
|
-
const name = process.argv.slice(4).filter((a) => !a.startsWith("--")).join(" ").trim();
|
|
4220
|
-
if (!name) {
|
|
4221
|
-
console.error(`${c.red}usage: kit memory forget <name>${c.reset}`);
|
|
4222
|
-
return false;
|
|
4223
|
-
}
|
|
4224
|
-
const db = openMemoryDb();
|
|
4225
|
-
const ok = removeThread(db, name);
|
|
4226
|
-
db.close();
|
|
4227
|
-
console.log(ok ? `${c.green}✓${c.reset} forgot ${name}` : `${c.dim}no copilot '${name}'${c.reset}`);
|
|
4228
|
-
return true;
|
|
4229
|
-
}
|
|
4230
|
-
if (subcommand === "pal") {
|
|
4231
|
-
const action = process.argv[4] && !process.argv[4].startsWith("--") ? process.argv[4] : "list";
|
|
4232
|
-
const db = openMemoryDb();
|
|
4233
|
-
try {
|
|
4234
|
-
if (action === "list") {
|
|
4235
|
-
const scope = hasFlag(process.argv, "--global")
|
|
4236
|
-
? undefined
|
|
4237
|
-
: basename(getCurrentProjectRoot());
|
|
4238
|
-
const items = palList(db, { scope });
|
|
4239
|
-
if (jsonMode) {
|
|
4240
|
-
console.log(JSON.stringify(items));
|
|
4241
|
-
return true;
|
|
4242
|
-
}
|
|
4243
|
-
if (!items.length) {
|
|
4244
|
-
console.log(`${c.dim}no open action items${c.reset}`);
|
|
4245
|
-
return true;
|
|
4246
|
-
}
|
|
4247
|
-
console.log(`${c.bold}${items.length}${c.reset} open action item(s):`);
|
|
4248
|
-
for (const p of items) {
|
|
4249
|
-
const tag = p.kind === "auto" ? ` ${c.dim}· auto${c.reset}` : "";
|
|
4250
|
-
const scope = p.scope ? ` ${c.dim}[${p.scope}]${c.reset}` : "";
|
|
4251
|
-
console.log(` ${c.bold}${p.id}${c.reset} ${p.title}${scope}${tag}`);
|
|
4252
|
-
}
|
|
4253
|
-
return true;
|
|
4254
|
-
}
|
|
4255
|
-
if (action === "add") {
|
|
4256
|
-
const title = process.argv.slice(5).filter((a) => !a.startsWith("--")).join(" ").trim();
|
|
4257
|
-
if (!title) {
|
|
4258
|
-
console.error(`${c.red}usage: kit memory pal add <title> [--verify=<cmd>] [--scope=<s>]${c.reset}`);
|
|
4259
|
-
return false;
|
|
4260
|
-
}
|
|
4261
|
-
const id = palAdd(db, {
|
|
4262
|
-
title,
|
|
4263
|
-
verifyCmd: flagValue(process.argv, "--verify"),
|
|
4264
|
-
scope: flagValue(process.argv, "--scope") ?? basename(getCurrentProjectRoot()),
|
|
4265
|
-
});
|
|
4266
|
-
console.log(`${c.green}✓${c.reset} added ${c.bold}${id}${c.reset}`);
|
|
4267
|
-
return true;
|
|
4268
|
-
}
|
|
4269
|
-
if (action === "done") {
|
|
4270
|
-
const id = process.argv[5];
|
|
4271
|
-
if (!id) {
|
|
4272
|
-
console.error(`${c.red}usage: kit memory pal done <id>${c.reset}`);
|
|
4273
|
-
return false;
|
|
4274
|
-
}
|
|
4275
|
-
console.log(palDone(db, id)
|
|
4276
|
-
? `${c.green}✓${c.reset} closed ${id}`
|
|
4277
|
-
: `${c.dim}${id} not found or already closed${c.reset}`);
|
|
4278
|
-
return true;
|
|
4279
|
-
}
|
|
4280
|
-
if (action === "snooze") {
|
|
4281
|
-
const id = process.argv[5];
|
|
4282
|
-
const days = Number(process.argv[6] ?? "7") || 7;
|
|
4283
|
-
if (!id) {
|
|
4284
|
-
console.error(`${c.red}usage: kit memory pal snooze <id> [days]${c.reset}`);
|
|
4285
|
-
return false;
|
|
4286
|
-
}
|
|
4287
|
-
console.log(palSnooze(db, id, days)
|
|
4288
|
-
? `${c.green}✓${c.reset} snoozed ${id} for ${days}d`
|
|
4289
|
-
: `${c.dim}${id} not found${c.reset}`);
|
|
4290
|
-
return true;
|
|
4291
|
-
}
|
|
4292
|
-
if (action === "verify") {
|
|
4293
|
-
const r = palAutoVerify(db);
|
|
4294
|
-
console.log(`${c.dim}checked ${r.checked} · closed ${r.closed.length} · reopened ${r.reopened.length}${c.reset}`);
|
|
4295
|
-
return true;
|
|
4296
|
-
}
|
|
4297
|
-
if (action === "import") {
|
|
4298
|
-
const r = importLegacyLedger(db);
|
|
4299
|
-
console.log(`${c.green}✓${c.reset} imported ${r.imported} item(s) from the legacy ledger`);
|
|
4300
|
-
return true;
|
|
4301
|
-
}
|
|
4302
|
-
console.error(`${c.red}Unknown pal action: ${action}${c.reset}`);
|
|
4303
|
-
console.error("Use: kit memory pal [list|add|done|snooze|verify|import]");
|
|
4304
|
-
return false;
|
|
4305
|
-
}
|
|
4306
|
-
finally {
|
|
4307
|
-
db.close();
|
|
4308
|
-
}
|
|
4309
|
-
}
|
|
4310
|
-
console.error(`${c.red}Unknown memory subcommand: ${subcommand}${c.reset}`);
|
|
4311
|
-
console.error("Use: kit memory index | search <query> | stats | install | uninstall | pal");
|
|
4312
|
-
return false;
|
|
4313
|
-
}
|
|
4314
3362
|
async function main() {
|
|
4315
3363
|
const args = process.argv.slice(2);
|
|
4316
3364
|
const command = args[0];
|
|
@@ -4360,135 +3408,71 @@ async function main() {
|
|
|
4360
3408
|
process.exitCode = 0;
|
|
4361
3409
|
return;
|
|
4362
3410
|
}
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
process.stdout.write(script);
|
|
4379
|
-
ok = true;
|
|
4380
|
-
break;
|
|
3411
|
+
// version/help/completions need bespoke handling; everything else is a flat
|
|
3412
|
+
// command->fn dispatch (was a ~40-case switch — the main complexity driver).
|
|
3413
|
+
if (command === "version") {
|
|
3414
|
+
ok = cmdVersion();
|
|
3415
|
+
}
|
|
3416
|
+
else if (command === "help") {
|
|
3417
|
+
ok = cmdHelp(args[1]);
|
|
3418
|
+
}
|
|
3419
|
+
else if (command === "completions") {
|
|
3420
|
+
const shell = args[1];
|
|
3421
|
+
const script = generateCompletions(shell);
|
|
3422
|
+
if (!script) {
|
|
3423
|
+
console.error(`Unknown shell: ${shell}. Use: bash, zsh, fish`);
|
|
3424
|
+
process.exitCode = 1;
|
|
3425
|
+
return;
|
|
4381
3426
|
}
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4421
|
-
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
case
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
case "auth":
|
|
4432
|
-
ok = await cmdAuth();
|
|
4433
|
-
break;
|
|
4434
|
-
case "mcp":
|
|
4435
|
-
ok = await cmdMcp();
|
|
4436
|
-
break;
|
|
4437
|
-
case "env":
|
|
4438
|
-
ok = await cmdEnv();
|
|
4439
|
-
break;
|
|
4440
|
-
case "doctor":
|
|
4441
|
-
ok = await cmdDoctor();
|
|
4442
|
-
break;
|
|
4443
|
-
case "analyze":
|
|
4444
|
-
ok = await cmdAnalyze();
|
|
4445
|
-
break;
|
|
4446
|
-
case "security":
|
|
4447
|
-
ok = await cmdSecurity();
|
|
4448
|
-
break;
|
|
4449
|
-
case "create-plugin":
|
|
4450
|
-
ok = await cmdCreatePlugin();
|
|
4451
|
-
break;
|
|
4452
|
-
case "plugin":
|
|
4453
|
-
ok = await cmdPlugin();
|
|
4454
|
-
break;
|
|
4455
|
-
case "ci":
|
|
4456
|
-
ok = await cmdCi();
|
|
4457
|
-
break;
|
|
4458
|
-
case "clone":
|
|
4459
|
-
ok = await cmdClone();
|
|
4460
|
-
break;
|
|
4461
|
-
case "run":
|
|
4462
|
-
ok = await cmdRun();
|
|
4463
|
-
break;
|
|
4464
|
-
case "open":
|
|
4465
|
-
ok = await cmdOpen();
|
|
4466
|
-
break;
|
|
4467
|
-
case "context":
|
|
4468
|
-
ok = await cmdContext();
|
|
4469
|
-
break;
|
|
4470
|
-
case "triage":
|
|
4471
|
-
ok = await cmdTriage();
|
|
4472
|
-
break;
|
|
4473
|
-
case "baseline":
|
|
4474
|
-
ok = await cmdBaseline();
|
|
4475
|
-
break;
|
|
4476
|
-
case "design":
|
|
4477
|
-
ok = await cmdDesign();
|
|
4478
|
-
break;
|
|
4479
|
-
case "review":
|
|
4480
|
-
ok = await cmdReview();
|
|
4481
|
-
break;
|
|
4482
|
-
case "pkg":
|
|
4483
|
-
ok = await cmdPkg();
|
|
4484
|
-
break;
|
|
4485
|
-
case "team":
|
|
4486
|
-
ok = await cmdTeam();
|
|
4487
|
-
break;
|
|
4488
|
-
case "memory":
|
|
4489
|
-
ok = await cmdMemory();
|
|
4490
|
-
break;
|
|
4491
|
-
default: {
|
|
3427
|
+
process.stdout.write(script);
|
|
3428
|
+
ok = true;
|
|
3429
|
+
}
|
|
3430
|
+
else {
|
|
3431
|
+
const COMMANDS = {
|
|
3432
|
+
status: cmdStatus,
|
|
3433
|
+
whoami: cmdWhoami,
|
|
3434
|
+
check: cmdCheck,
|
|
3435
|
+
init: cmdInit,
|
|
3436
|
+
upgrade: cmdUpgrade,
|
|
3437
|
+
install: cmdInstall,
|
|
3438
|
+
login: cmdLogin,
|
|
3439
|
+
secrets: cmdSecrets,
|
|
3440
|
+
setup: cmdSetup,
|
|
3441
|
+
skills: cmdSkills,
|
|
3442
|
+
fix: cmdFix,
|
|
3443
|
+
escalate: cmdEscalate,
|
|
3444
|
+
governance: cmdGovernance,
|
|
3445
|
+
"agent-config": cmdAgentConfig,
|
|
3446
|
+
hooks: cmdHooks,
|
|
3447
|
+
add: cmdAdd,
|
|
3448
|
+
audit: cmdAudit,
|
|
3449
|
+
auth: cmdAuth,
|
|
3450
|
+
mcp: cmdMcp,
|
|
3451
|
+
env: cmdEnv,
|
|
3452
|
+
doctor: cmdDoctor,
|
|
3453
|
+
analyze: cmdAnalyze,
|
|
3454
|
+
security: cmdSecurity,
|
|
3455
|
+
"create-plugin": cmdCreatePlugin,
|
|
3456
|
+
plugin: cmdPlugin,
|
|
3457
|
+
ci: cmdCi,
|
|
3458
|
+
clone: cmdClone,
|
|
3459
|
+
run: cmdRun,
|
|
3460
|
+
open: cmdOpen,
|
|
3461
|
+
context: cmdContext,
|
|
3462
|
+
triage: cmdTriage,
|
|
3463
|
+
baseline: cmdBaseline,
|
|
3464
|
+
design: cmdDesign,
|
|
3465
|
+
review: cmdReview,
|
|
3466
|
+
pkg: cmdPkg,
|
|
3467
|
+
team: cmdTeam,
|
|
3468
|
+
memory: cmdMemory,
|
|
3469
|
+
};
|
|
3470
|
+
// no command → `check` (the prior `case undefined` behaviour).
|
|
3471
|
+
const handler = COMMANDS[command ?? "check"];
|
|
3472
|
+
if (handler) {
|
|
3473
|
+
ok = await handler();
|
|
3474
|
+
}
|
|
3475
|
+
else {
|
|
4492
3476
|
console.error(`Unknown command: ${command}`);
|
|
4493
3477
|
const { didYouMean } = await import("./utils/didYouMean.js");
|
|
4494
3478
|
const knownCommands = [
|
|
@@ -4526,5 +3510,7 @@ async function main() {
|
|
|
4526
3510
|
}
|
|
4527
3511
|
}
|
|
4528
3512
|
}
|
|
4529
|
-
main()
|
|
3513
|
+
// Entrypoint — fire-and-forget by design; main() handles its own errors and sets
|
|
3514
|
+
// process.exitCode. `void` marks the intentional non-await for no-floating-promises.
|
|
3515
|
+
void main();
|
|
4530
3516
|
//# sourceMappingURL=cli.js.map
|