kodingo-cli 1.0.25 → 1.0.26

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/cli.js CHANGED
@@ -21,6 +21,7 @@ const sync_1 = require("./commands/sync");
21
21
  const context_1 = require("./commands/context");
22
22
  const guardrails_1 = require("./commands/guardrails");
23
23
  const session_brief_1 = require("./commands/session-brief");
24
+ const check_rules_1 = require("./commands/check-rules");
24
25
  const aging_1 = require("./commands/aging");
25
26
  const program = new commander_1.Command();
26
27
  program.name("kodingo").description("Kodingo CLI").version("1.0.4");
@@ -247,4 +248,8 @@ program
247
248
  .option("--prompt", "Output as prompt-ready string instead of JSON")
248
249
  .option("--limit <n>", "Max memories to include", "30")
249
250
  .action((opts) => (0, session_brief_1.sessionBriefCommand)({ prompt: opts.prompt, limit: parseInt(opts.limit) }));
251
+ program
252
+ .command("check-rules")
253
+ .description("Check staged diff against project guardrails — called by pre-commit hook")
254
+ .action(() => (0, check_rules_1.checkRulesCommand)());
250
255
  program.parse(process.argv);
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkRulesCommand = checkRulesCommand;
4
+ const child_process_1 = require("child_process");
5
+ const persistence_config_1 = require("../utils/persistence-config");
6
+ const API_BASE_DEFAULT = "https://kodingo-api.onrender.com";
7
+ function normalizePath(p) {
8
+ return p.replace(/\\/g, "/").toLowerCase();
9
+ }
10
+ function getCredentials() {
11
+ const config = (0, persistence_config_1.readConfig)();
12
+ const cwd = normalizePath(process.cwd());
13
+ const ws = (0, persistence_config_1.readWorkspaceConfig)(cwd);
14
+ const token = ws?.token ?? config.token ?? "";
15
+ const apiUrl = ws?.apiUrl ?? config.apiUrl ?? API_BASE_DEFAULT;
16
+ return { token, apiUrl };
17
+ }
18
+ function getStagedDiff() {
19
+ try {
20
+ return (0, child_process_1.execSync)("git diff --cached", { encoding: "utf-8", timeout: 10000 });
21
+ }
22
+ catch {
23
+ return "";
24
+ }
25
+ }
26
+ function getProjectId(token, apiUrl) {
27
+ return fetch(`${apiUrl}/org/projects/by-token`, {
28
+ headers: { "X-Kodingo-Token": token },
29
+ })
30
+ .then(r => r.ok ? r.json() : null)
31
+ .then((d) => d?.project?.id ?? null)
32
+ .catch(() => null);
33
+ }
34
+ async function checkRulesCommand() {
35
+ const { token, apiUrl } = getCredentials();
36
+ if (!token)
37
+ return; // No token — silent pass, never block
38
+ const diff = getStagedDiff();
39
+ if (!diff.trim())
40
+ return; // Empty diff — nothing to check
41
+ try {
42
+ const projectId = await getProjectId(token, apiUrl);
43
+ if (!projectId)
44
+ return; // Can't resolve project — silent pass
45
+ const res = await fetch(`${apiUrl}/org/projects/${projectId}/rules/check`, {
46
+ method: "POST",
47
+ headers: { "Content-Type": "application/json", "X-Kodingo-Token": token },
48
+ body: JSON.stringify({ diff }),
49
+ });
50
+ if (!res.ok)
51
+ return; // API error — silent pass, never block on infrastructure failure
52
+ const data = await res.json();
53
+ const violations = data.violations ?? [];
54
+ if (!violations.length)
55
+ return; // All clear
56
+ // Hard block
57
+ console.error("\n╔════════════════════════════════════════════════════════════╗");
58
+ console.error("║ KORTEX GUARDRAIL VIOLATION — COMMIT BLOCKED ║");
59
+ console.error("╚════════════════════════════════════════════════════════════╝\n");
60
+ console.error(`${violations.length} rule${violations.length !== 1 ? "s" : ""} violated:\n`);
61
+ for (const v of violations) {
62
+ console.error(` ✕ ${v.title}`);
63
+ console.error(` ${v.description}`);
64
+ if (v.explanation)
65
+ console.error(` → ${v.explanation}`);
66
+ console.error("");
67
+ }
68
+ console.error("Fix the violations above or contact your engineering lead to update the rules.");
69
+ console.error("To bypass in an emergency: git commit --no-verify\n");
70
+ process.exit(1);
71
+ }
72
+ catch {
73
+ return; // Any unexpected error — silent pass
74
+ }
75
+ }
@@ -20,6 +20,28 @@ async function installHookCommand(options) {
20
20
  `"${kodingoBin}" scan-git --repo "$(git rev-parse --show-toplevel)" &`,
21
21
  "# </kodingo>",
22
22
  ].join("\n");
23
+ // Pre-commit hook — runs synchronously to enforce guardrails
24
+ const preCommitPath = path_1.default.join(hooksDir, "pre-commit");
25
+ const preCommitBlock = [
26
+ "# <kodingo-guardrails>",
27
+ "# Kortex: enforce project guardrails before commit",
28
+ `"${kodingoBin}" check-rules`,
29
+ "# </kodingo-guardrails>",
30
+ ].join("\n");
31
+ if (fs_1.default.existsSync(preCommitPath)) {
32
+ const existing = fs_1.default.readFileSync(preCommitPath, "utf-8");
33
+ if (!existing.includes("# <kodingo-guardrails>")) {
34
+ const updated = existing.endsWith("\n")
35
+ ? existing + "\n" + preCommitBlock + "\n"
36
+ : existing + "\n\n" + preCommitBlock + "\n";
37
+ fs_1.default.writeFileSync(preCommitPath, updated, "utf-8");
38
+ }
39
+ }
40
+ else {
41
+ const content = "#!/bin/sh\n\n" + preCommitBlock + "\n";
42
+ fs_1.default.writeFileSync(preCommitPath, content, "utf-8");
43
+ }
44
+ fs_1.default.chmodSync(preCommitPath, 0o755);
23
45
  if (fs_1.default.existsSync(hookPath)) {
24
46
  const existing = fs_1.default.readFileSync(hookPath, "utf-8");
25
47
  if (existing.includes("# <kodingo>")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodingo-cli",
3
- "version": "1.0.25",
3
+ "version": "1.0.26",
4
4
  "description": "Kodingo CLI",
5
5
  "license": "MIT",
6
6
  "private": false,