kodingo-cli 1.0.24 → 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 +26 -0
- package/dist/commands/check-rules.js +75 -0
- package/dist/commands/context.js +105 -0
- package/dist/commands/guardrails.js +66 -0
- package/dist/commands/install-hook.js +22 -0
- package/dist/commands/session-brief.js +114 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,10 @@ const init_1 = require("./commands/init");
|
|
|
18
18
|
const install_hook_1 = require("./commands/install-hook");
|
|
19
19
|
const update_1 = require("./commands/update");
|
|
20
20
|
const sync_1 = require("./commands/sync");
|
|
21
|
+
const context_1 = require("./commands/context");
|
|
22
|
+
const guardrails_1 = require("./commands/guardrails");
|
|
23
|
+
const session_brief_1 = require("./commands/session-brief");
|
|
24
|
+
const check_rules_1 = require("./commands/check-rules");
|
|
21
25
|
const aging_1 = require("./commands/aging");
|
|
22
26
|
const program = new commander_1.Command();
|
|
23
27
|
program.name("kodingo").description("Kodingo CLI").version("1.0.4");
|
|
@@ -226,4 +230,26 @@ program
|
|
|
226
230
|
payload.repo = String(options.repo);
|
|
227
231
|
await (0, scan_repo_1.scanRepoCommand)(payload);
|
|
228
232
|
});
|
|
233
|
+
program
|
|
234
|
+
.command("context")
|
|
235
|
+
.description("Get affirmed memories for a file — designed for AI agents")
|
|
236
|
+
.requiredOption("-f, --file <path>", "File path to get context for")
|
|
237
|
+
.option("--prompt", "Output as prompt-ready string instead of JSON")
|
|
238
|
+
.option("--limit <n>", "Max memories to return", "20")
|
|
239
|
+
.action((opts) => (0, context_1.contextCommand)(opts.file, { prompt: opts.prompt, limit: parseInt(opts.limit) }));
|
|
240
|
+
program
|
|
241
|
+
.command("guardrails")
|
|
242
|
+
.description("Get project guardrails — rules and rejected patterns for AI agents")
|
|
243
|
+
.option("--prompt", "Output as prompt-ready string instead of JSON")
|
|
244
|
+
.action((opts) => (0, guardrails_1.guardrailsCommand)({ prompt: opts.prompt }));
|
|
245
|
+
program
|
|
246
|
+
.command("session-brief")
|
|
247
|
+
.description("Get a compact project intelligence brief — call at the start of an AI coding session")
|
|
248
|
+
.option("--prompt", "Output as prompt-ready string instead of JSON")
|
|
249
|
+
.option("--limit <n>", "Max memories to include", "30")
|
|
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)());
|
|
229
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
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.contextCommand = contextCommand;
|
|
37
|
+
/**
|
|
38
|
+
* kodingo context --file <path>
|
|
39
|
+
*
|
|
40
|
+
* Returns all affirmed memories relevant to a file.
|
|
41
|
+
* Designed for AI agents — call before touching a file.
|
|
42
|
+
* Output: JSON by default, prompt-ready string with --prompt flag.
|
|
43
|
+
*/
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const persistence_config_1 = require("../utils/persistence-config");
|
|
46
|
+
const API_BASE_DEFAULT = "https://kodingo-api.onrender.com";
|
|
47
|
+
function normalizePath(p) {
|
|
48
|
+
return p.replace(/\\\\/g, "/").toLowerCase();
|
|
49
|
+
}
|
|
50
|
+
function getCredentials() {
|
|
51
|
+
const config = (0, persistence_config_1.readConfig)();
|
|
52
|
+
const cwd = normalizePath(process.cwd());
|
|
53
|
+
const ws = (0, persistence_config_1.readWorkspaceConfig)(cwd);
|
|
54
|
+
const token = ws?.token ?? config.token ?? "";
|
|
55
|
+
const apiUrl = ws?.apiUrl ?? config.apiUrl ?? API_BASE_DEFAULT;
|
|
56
|
+
return { token, apiUrl };
|
|
57
|
+
}
|
|
58
|
+
async function contextCommand(filePath, options) {
|
|
59
|
+
const { token, apiUrl } = getCredentials();
|
|
60
|
+
if (!token) {
|
|
61
|
+
console.error("No token found. Run `kodingo init` first.");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const limit = options.limit ?? 20;
|
|
65
|
+
const repoName = path.basename(process.cwd());
|
|
66
|
+
const relFile = path.relative(process.cwd(), path.resolve(filePath));
|
|
67
|
+
try {
|
|
68
|
+
// Fetch memories for this file (by repo path)
|
|
69
|
+
const res = await fetch(`${apiUrl}/memory?repo=${encodeURIComponent(relFile)}&status=affirmed&limit=${limit}`, { headers: { "X-Kodingo-Token": token } });
|
|
70
|
+
if (!res.ok) {
|
|
71
|
+
console.error(`API error: ${res.status}`);
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
const data = await res.json();
|
|
75
|
+
const memories = data.memories ?? [];
|
|
76
|
+
if (!options.prompt) {
|
|
77
|
+
console.log(JSON.stringify({ file: relFile, repo: repoName, count: memories.length, memories }, null, 2));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Prompt-ready format
|
|
81
|
+
if (!memories.length) {
|
|
82
|
+
console.log(`No affirmed memories found for ${relFile}.`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const lines = [
|
|
86
|
+
`# Kortex context for ${relFile}`,
|
|
87
|
+
`# ${memories.length} affirmed decision${memories.length !== 1 ? "s" : ""} your team has made about this file:`,
|
|
88
|
+
"",
|
|
89
|
+
];
|
|
90
|
+
for (const m of memories) {
|
|
91
|
+
lines.push(`## ${m.title ?? "Decision"}`);
|
|
92
|
+
lines.push(m.content);
|
|
93
|
+
if (m.symbol)
|
|
94
|
+
lines.push(`Symbol: ${m.symbol}`);
|
|
95
|
+
lines.push(`Confidence: ${(m.confidence * 100).toFixed(0)}%`);
|
|
96
|
+
lines.push("");
|
|
97
|
+
}
|
|
98
|
+
lines.push("# End of Kortex context. Respect these decisions in your changes.");
|
|
99
|
+
console.log(lines.join("\n"));
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
console.error("Failed to fetch context:", err.message);
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.guardrailsCommand = guardrailsCommand;
|
|
4
|
+
const persistence_config_1 = require("../utils/persistence-config");
|
|
5
|
+
const API_BASE_DEFAULT = "https://kodingo-api.onrender.com";
|
|
6
|
+
function normalizePath(p) {
|
|
7
|
+
return p.replace(/\\\\/g, "/").toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
function getCredentials() {
|
|
10
|
+
const config = (0, persistence_config_1.readConfig)();
|
|
11
|
+
const cwd = normalizePath(process.cwd());
|
|
12
|
+
const ws = (0, persistence_config_1.readWorkspaceConfig)(cwd);
|
|
13
|
+
const token = ws?.token ?? config.token ?? "";
|
|
14
|
+
const apiUrl = ws?.apiUrl ?? config.apiUrl ?? API_BASE_DEFAULT;
|
|
15
|
+
return { token, apiUrl };
|
|
16
|
+
}
|
|
17
|
+
async function guardrailsCommand(options) {
|
|
18
|
+
const { token, apiUrl } = getCredentials();
|
|
19
|
+
if (!token) {
|
|
20
|
+
console.error("No token found. Run `kodingo init` first.");
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
// Fetch denied memories (things explicitly ruled out)
|
|
25
|
+
const [deniedRes, affiredRes] = await Promise.all([
|
|
26
|
+
fetch(`${apiUrl}/memory?status=denied&limit=50`, { headers: { "X-Kodingo-Token": token } }),
|
|
27
|
+
fetch(`${apiUrl}/memory?status=affirmed&limit=50`, { headers: { "X-Kodingo-Token": token } }),
|
|
28
|
+
]);
|
|
29
|
+
const denied = deniedRes.ok ? (await deniedRes.json()).memories ?? [] : [];
|
|
30
|
+
const affirmed = affiredRes.ok ? (await affiredRes.json()).memories ?? [] : [];
|
|
31
|
+
// High confidence affirmed = guardrails (confidence >= 0.7)
|
|
32
|
+
const rules = affirmed.filter((m) => m.confidence >= 0.7);
|
|
33
|
+
if (!options.prompt) {
|
|
34
|
+
console.log(JSON.stringify({ rules: rules.length, denied: denied.length, rules_list: rules, denied_list: denied }, null, 2));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const lines = ["# Kortex guardrails — decisions your team has enforced", ""];
|
|
38
|
+
if (rules.length) {
|
|
39
|
+
lines.push("## Rules to follow:");
|
|
40
|
+
for (const m of rules) {
|
|
41
|
+
lines.push(`- ${m.title ?? m.content.slice(0, 100)}`);
|
|
42
|
+
if (m.symbol)
|
|
43
|
+
lines.push(` (applies to: ${m.symbol})`);
|
|
44
|
+
}
|
|
45
|
+
lines.push("");
|
|
46
|
+
}
|
|
47
|
+
if (denied.length) {
|
|
48
|
+
lines.push("## Patterns explicitly rejected by your team (do NOT use):");
|
|
49
|
+
for (const m of denied) {
|
|
50
|
+
lines.push(`- ${m.title ?? m.content.slice(0, 100)}`);
|
|
51
|
+
if (m.symbol)
|
|
52
|
+
lines.push(` (rejected for: ${m.symbol})`);
|
|
53
|
+
}
|
|
54
|
+
lines.push("");
|
|
55
|
+
}
|
|
56
|
+
if (!rules.length && !denied.length) {
|
|
57
|
+
lines.push("No guardrails defined yet. Affirm and deny memories to build them.");
|
|
58
|
+
}
|
|
59
|
+
lines.push("# End of Kortex guardrails.");
|
|
60
|
+
console.log(lines.join("\n"));
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
console.error("Failed to fetch guardrails:", err.message);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -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>")) {
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.sessionBriefCommand = sessionBriefCommand;
|
|
37
|
+
/**
|
|
38
|
+
* kodingo session-brief
|
|
39
|
+
*
|
|
40
|
+
* Returns a compact summary of the most important decisions across the project.
|
|
41
|
+
* Designed for AI agents — call once at the start of a new coding session.
|
|
42
|
+
* Output: JSON by default, prompt-ready string with --prompt flag.
|
|
43
|
+
*/
|
|
44
|
+
const persistence_config_1 = require("../utils/persistence-config");
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const API_BASE_DEFAULT = "https://kodingo-api.onrender.com";
|
|
47
|
+
function normalizePath(p) {
|
|
48
|
+
return p.replace(/\\\\/g, "/").toLowerCase();
|
|
49
|
+
}
|
|
50
|
+
function getCredentials() {
|
|
51
|
+
const config = (0, persistence_config_1.readConfig)();
|
|
52
|
+
const cwd = normalizePath(process.cwd());
|
|
53
|
+
const ws = (0, persistence_config_1.readWorkspaceConfig)(cwd);
|
|
54
|
+
const token = ws?.token ?? config.token ?? "";
|
|
55
|
+
const apiUrl = ws?.apiUrl ?? config.apiUrl ?? API_BASE_DEFAULT;
|
|
56
|
+
return { token, apiUrl };
|
|
57
|
+
}
|
|
58
|
+
async function sessionBriefCommand(options) {
|
|
59
|
+
const { token, apiUrl } = getCredentials();
|
|
60
|
+
if (!token) {
|
|
61
|
+
console.error("No token found. Run `kodingo init` first.");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
const limit = options.limit ?? 30;
|
|
65
|
+
const repoName = path.basename(process.cwd());
|
|
66
|
+
try {
|
|
67
|
+
// Top affirmed memories sorted by confidence
|
|
68
|
+
const res = await fetch(`${apiUrl}/memory?status=affirmed&limit=${limit}`, { headers: { "X-Kodingo-Token": token } });
|
|
69
|
+
if (!res.ok) {
|
|
70
|
+
console.error(`API error: ${res.status}`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const data = await res.json();
|
|
74
|
+
const memories = (data.memories ?? []).sort((a, b) => b.confidence - a.confidence);
|
|
75
|
+
if (!options.prompt) {
|
|
76
|
+
console.log(JSON.stringify({
|
|
77
|
+
repo: repoName,
|
|
78
|
+
total_affirmed: data.total,
|
|
79
|
+
showing: memories.length,
|
|
80
|
+
memories,
|
|
81
|
+
}, null, 2));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const lines = [
|
|
85
|
+
`# Kortex session brief for ${repoName}`,
|
|
86
|
+
`# ${data.total} total affirmed decisions. Showing top ${memories.length} by confidence.`,
|
|
87
|
+
`# Read these before writing any code.`,
|
|
88
|
+
"",
|
|
89
|
+
];
|
|
90
|
+
// Group by type
|
|
91
|
+
const byType = {};
|
|
92
|
+
for (const m of memories) {
|
|
93
|
+
const t = m.type ?? "context";
|
|
94
|
+
if (!byType[t])
|
|
95
|
+
byType[t] = [];
|
|
96
|
+
byType[t].push(m);
|
|
97
|
+
}
|
|
98
|
+
for (const [type, items] of Object.entries(byType)) {
|
|
99
|
+
lines.push(`## ${type.charAt(0).toUpperCase() + type.slice(1)} decisions`);
|
|
100
|
+
for (const m of items) {
|
|
101
|
+
const conf = (m.confidence * 100).toFixed(0);
|
|
102
|
+
const symbol = m.symbol ? ` [${m.symbol}]` : "";
|
|
103
|
+
lines.push(`- ${m.title ?? m.content.slice(0, 100)}${symbol} (${conf}% confidence)`);
|
|
104
|
+
}
|
|
105
|
+
lines.push("");
|
|
106
|
+
}
|
|
107
|
+
lines.push("# End of Kortex session brief. These decisions represent your team\'s collective intelligence.");
|
|
108
|
+
console.log(lines.join("\n"));
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
console.error("Failed to fetch session brief:", err.message);
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
}
|