kodingo-cli 1.0.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 +91 -0
- package/dist/adapters/agent-adapter/agent-connector.js +1 -0
- package/dist/adapters/ai/inference.js +55 -0
- package/dist/adapters/cloud-adapter/cloud-persistence.js +290 -0
- package/dist/adapters/db-adapter/memory-persistence.js +521 -0
- package/dist/adapters/git-adapter/git-listener.js +188 -0
- package/dist/cli.js +181 -0
- package/dist/commands/add-decision.js +1 -0
- package/dist/commands/affirm.js +41 -0
- package/dist/commands/canonicalize-symbol.js +95 -0
- package/dist/commands/capture.js +67 -0
- package/dist/commands/deny.js +67 -0
- package/dist/commands/doctor.js +168 -0
- package/dist/commands/explain-symbol.js +84 -0
- package/dist/commands/ignore.js +19 -0
- package/dist/commands/index.js +1 -0
- package/dist/commands/init.js +135 -0
- package/dist/commands/install-hook.js +61 -0
- package/dist/commands/query-memory.js +61 -0
- package/dist/commands/query-symbol.js +63 -0
- package/dist/commands/scan-git.js +59 -0
- package/dist/commands/uninstall-hook.js +51 -0
- package/dist/ports/cloud-event-port.js +207 -0
- package/dist/ports/db-event-port.js +195 -0
- package/dist/utils/persistence-config.js +69 -0
- package/dist/utils/repo-scope.js +48 -0
- package/package.json +37 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.explainSymbolCommand = explainSymbolCommand;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const persistence_config_1 = require("../utils/persistence-config");
|
|
10
|
+
async function explainSymbolCommand(symbol, options) {
|
|
11
|
+
const { initDb, queryMemoryBySymbol } = (0, persistence_config_1.getPersistence)();
|
|
12
|
+
await initDb();
|
|
13
|
+
const repoPath = resolveRepoScope(options);
|
|
14
|
+
const params = {
|
|
15
|
+
symbol,
|
|
16
|
+
limit: 1,
|
|
17
|
+
onlyCanonical: true,
|
|
18
|
+
};
|
|
19
|
+
if (repoPath)
|
|
20
|
+
params.repoPath = repoPath;
|
|
21
|
+
const results = await queryMemoryBySymbol(params);
|
|
22
|
+
// Fallback: if no affirmed record exists, surface best non-denied candidate.
|
|
23
|
+
let record = results[0];
|
|
24
|
+
if (!record) {
|
|
25
|
+
const fallbackParams = {
|
|
26
|
+
symbol,
|
|
27
|
+
limit: 1,
|
|
28
|
+
onlyCanonical: false,
|
|
29
|
+
includeDenied: false,
|
|
30
|
+
};
|
|
31
|
+
if (repoPath)
|
|
32
|
+
fallbackParams.repoPath = repoPath;
|
|
33
|
+
const fallback = await queryMemoryBySymbol(fallbackParams);
|
|
34
|
+
record = fallback[0];
|
|
35
|
+
}
|
|
36
|
+
if (!record) {
|
|
37
|
+
console.log(`No memory found for symbol: ${symbol}`);
|
|
38
|
+
console.log(`Tip: run "kodingo scan-git --repo <path>" to infer decisions from git history.`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const isCanonical = record.status === "affirmed";
|
|
42
|
+
const confidencePct = Math.round(record.confidence * 100);
|
|
43
|
+
console.log(`Symbol: ${symbol}`);
|
|
44
|
+
console.log(`${"─".repeat(60)}`);
|
|
45
|
+
if (record.title) {
|
|
46
|
+
console.log(`${record.title}`);
|
|
47
|
+
console.log("");
|
|
48
|
+
}
|
|
49
|
+
console.log(record.content);
|
|
50
|
+
console.log("");
|
|
51
|
+
const statusLabel = isCanonical
|
|
52
|
+
? `✓ affirmed`
|
|
53
|
+
: `~ ${record.status} (not yet affirmed)`;
|
|
54
|
+
console.log(`${statusLabel} · confidence ${confidencePct}% · ${record.type}`);
|
|
55
|
+
if (record.tags?.length) {
|
|
56
|
+
console.log(`tags: ${record.tags.join(", ")}`);
|
|
57
|
+
}
|
|
58
|
+
console.log(`id: ${record.id}`);
|
|
59
|
+
if (!isCanonical) {
|
|
60
|
+
console.log("");
|
|
61
|
+
console.log(`Note: No affirmed record exists for this symbol. Showing best available candidate.`);
|
|
62
|
+
console.log(`Run "kodingo affirm ${record.id}" to promote this to canonical truth.`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function resolveRepoScope(options) {
|
|
66
|
+
if (options.global === true)
|
|
67
|
+
return undefined;
|
|
68
|
+
if (options.repo && options.repo.trim()) {
|
|
69
|
+
return path_1.default.resolve(options.repo.trim());
|
|
70
|
+
}
|
|
71
|
+
return findRepoRoot(process.cwd());
|
|
72
|
+
}
|
|
73
|
+
function findRepoRoot(startPath) {
|
|
74
|
+
let current = startPath;
|
|
75
|
+
while (true) {
|
|
76
|
+
const gitPath = path_1.default.join(current, ".git");
|
|
77
|
+
if (fs_1.default.existsSync(gitPath))
|
|
78
|
+
return current;
|
|
79
|
+
const parent = path_1.default.dirname(current);
|
|
80
|
+
if (parent === current)
|
|
81
|
+
return startPath;
|
|
82
|
+
current = parent;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ignoreCommand = ignoreCommand;
|
|
4
|
+
const persistence_config_1 = require("../utils/persistence-config");
|
|
5
|
+
async function ignoreCommand(options) {
|
|
6
|
+
const { initDb, getMemoryById, updateMemoryLifecycle } = (0, persistence_config_1.getPersistence)();
|
|
7
|
+
await initDb();
|
|
8
|
+
const existing = await getMemoryById(options.id);
|
|
9
|
+
if (!existing) {
|
|
10
|
+
throw new Error(`Memory not found: ${options.id}`);
|
|
11
|
+
}
|
|
12
|
+
// Domain contract: ignore does not change confidence.
|
|
13
|
+
const updated = await updateMemoryLifecycle({
|
|
14
|
+
id: existing.id,
|
|
15
|
+
status: "ignored",
|
|
16
|
+
confidence: existing.confidence,
|
|
17
|
+
});
|
|
18
|
+
console.log(`Ignored memory: ${updated.id} (status=${updated.status}, confidence=${updated.confidence.toFixed(2)})`);
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* kodingo init
|
|
4
|
+
*
|
|
5
|
+
* Interactively configures ~/.kodingo/config.json.
|
|
6
|
+
* Supports both local (psql) and cloud (kodingo-api) modes.
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.registerInitCommand = registerInitCommand;
|
|
43
|
+
const readline = __importStar(require("node:readline"));
|
|
44
|
+
const persistence_config_1 = require("../utils/persistence-config");
|
|
45
|
+
// ── Prompt helper ─────────────────────────────────────────────────────────────
|
|
46
|
+
function prompt(rl, question) {
|
|
47
|
+
return new Promise((resolve) => rl.question(question, (ans) => resolve(ans.trim())));
|
|
48
|
+
}
|
|
49
|
+
// ── Health check ──────────────────────────────────────────────────────────────
|
|
50
|
+
async function verifyCloudConnection(apiUrl, token) {
|
|
51
|
+
const url = `${apiUrl.replace(/\/$/, "")}/health`;
|
|
52
|
+
const res = await fetch(url, { headers: { "X-Kodingo-Token": token } });
|
|
53
|
+
if (!res.ok)
|
|
54
|
+
throw new Error(`Health check failed (${res.status}) — check your API URL`);
|
|
55
|
+
}
|
|
56
|
+
// ── Command ───────────────────────────────────────────────────────────────────
|
|
57
|
+
function registerInitCommand(program) {
|
|
58
|
+
program
|
|
59
|
+
.command("init")
|
|
60
|
+
.description("Configure kodingo CLI (local psql or cloud API)")
|
|
61
|
+
.option("--local", "Switch to local mode (psql)")
|
|
62
|
+
.option("--cloud", "Switch to cloud mode (kodingo-api)")
|
|
63
|
+
.option("--api-url <url>", "Cloud API URL (skips prompt)")
|
|
64
|
+
.option("--token <token>", "Cloud API token (skips prompt)")
|
|
65
|
+
.action(async (opts) => {
|
|
66
|
+
const rl = readline.createInterface({
|
|
67
|
+
input: process.stdin,
|
|
68
|
+
output: process.stdout,
|
|
69
|
+
});
|
|
70
|
+
try {
|
|
71
|
+
const current = (0, persistence_config_1.readConfig)();
|
|
72
|
+
// ── Non-interactive flags ─────────────────────────────────────────────
|
|
73
|
+
if (opts.local) {
|
|
74
|
+
(0, persistence_config_1.writeConfig)({ mode: "local" });
|
|
75
|
+
console.log("✔ Switched to local mode — using local psql database.");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (opts.cloud && opts.apiUrl && opts.token) {
|
|
79
|
+
console.log("Verifying connection to kodingo-api...");
|
|
80
|
+
await verifyCloudConnection(opts.apiUrl, opts.token);
|
|
81
|
+
(0, persistence_config_1.writeConfig)({
|
|
82
|
+
mode: "cloud",
|
|
83
|
+
apiUrl: opts.apiUrl,
|
|
84
|
+
token: opts.token,
|
|
85
|
+
});
|
|
86
|
+
console.log(`✔ Cloud mode configured. Config saved to ${persistence_config_1.CONFIG_PATH}`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
// ── Interactive flow ──────────────────────────────────────────────────
|
|
90
|
+
console.log("\nKodingo CLI Setup\n");
|
|
91
|
+
console.log(`Current mode: ${current.mode}`);
|
|
92
|
+
if (current.apiUrl)
|
|
93
|
+
console.log(`Current API URL: ${current.apiUrl}`);
|
|
94
|
+
console.log();
|
|
95
|
+
const modeInput = await prompt(rl, "Choose mode — (l)ocal or (c)loud [default: local]: ");
|
|
96
|
+
const mode = modeInput.toLowerCase().startsWith("c")
|
|
97
|
+
? "cloud"
|
|
98
|
+
: "local";
|
|
99
|
+
if (mode === "local") {
|
|
100
|
+
(0, persistence_config_1.writeConfig)({ mode: "local" });
|
|
101
|
+
console.log("\n✔ Local mode configured. Using local psql database.");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// Cloud mode — collect API URL and token
|
|
105
|
+
const defaultUrl = current.apiUrl ?? "";
|
|
106
|
+
const apiUrlInput = await prompt(rl, `API URL${defaultUrl ? ` [${defaultUrl}]` : ""}: `);
|
|
107
|
+
const apiUrl = apiUrlInput || defaultUrl;
|
|
108
|
+
if (!apiUrl) {
|
|
109
|
+
console.error("✖ API URL is required for cloud mode.");
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
const tokenInput = await prompt(rl, "API Token (X-Kodingo-Token): ");
|
|
113
|
+
const token = tokenInput || current.token || "";
|
|
114
|
+
if (!token) {
|
|
115
|
+
console.error("✖ API token is required for cloud mode.");
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
console.log("\nVerifying connection to kodingo-api...");
|
|
119
|
+
await verifyCloudConnection(apiUrl, token);
|
|
120
|
+
const config = { mode: "cloud", apiUrl, token };
|
|
121
|
+
(0, persistence_config_1.writeConfig)(config);
|
|
122
|
+
console.log(`\n✔ Cloud mode configured successfully.`);
|
|
123
|
+
console.log(` API URL : ${apiUrl}`);
|
|
124
|
+
console.log(` Config : ${persistence_config_1.CONFIG_PATH}`);
|
|
125
|
+
console.log("\nYou're ready to use kodingo with the cloud API.");
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
console.error(`\n✖ Init failed: ${err.message}`);
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
rl.close();
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.installHookCommand = installHookCommand;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
async function installHookCommand(options) {
|
|
10
|
+
const repoRoot = resolveRepoRoot(options);
|
|
11
|
+
const hooksDir = path_1.default.join(repoRoot, ".git", "hooks");
|
|
12
|
+
if (!fs_1.default.existsSync(hooksDir)) {
|
|
13
|
+
throw new Error(`Hooks directory does not exist: ${hooksDir}. Is this a valid git repository?`);
|
|
14
|
+
}
|
|
15
|
+
const kodingoBin = process.argv[1];
|
|
16
|
+
const hookPath = path_1.default.join(hooksDir, "post-commit");
|
|
17
|
+
const hookBlock = [
|
|
18
|
+
"# <kodingo>",
|
|
19
|
+
"# Kodingo: auto-infer decision memory from git commits",
|
|
20
|
+
`"${kodingoBin}" scan-git --repo "$(git rev-parse --show-toplevel)" &`,
|
|
21
|
+
"# </kodingo>",
|
|
22
|
+
].join("\n");
|
|
23
|
+
if (fs_1.default.existsSync(hookPath)) {
|
|
24
|
+
const existing = fs_1.default.readFileSync(hookPath, "utf-8");
|
|
25
|
+
if (existing.includes("# <kodingo>")) {
|
|
26
|
+
console.log("Hook already installed");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// Append to existing hook
|
|
30
|
+
const updated = existing.endsWith("\n")
|
|
31
|
+
? existing + "\n" + hookBlock + "\n"
|
|
32
|
+
: existing + "\n\n" + hookBlock + "\n";
|
|
33
|
+
fs_1.default.writeFileSync(hookPath, updated, "utf-8");
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
// Create new hook file
|
|
37
|
+
const content = "#!/bin/sh\n\n" + hookBlock + "\n";
|
|
38
|
+
fs_1.default.writeFileSync(hookPath, content, "utf-8");
|
|
39
|
+
}
|
|
40
|
+
fs_1.default.chmodSync(hookPath, 0o755);
|
|
41
|
+
console.log(`Kodingo hook installed for repo: ${repoRoot}`);
|
|
42
|
+
}
|
|
43
|
+
function resolveRepoRoot(options) {
|
|
44
|
+
if (options.repo && options.repo.trim()) {
|
|
45
|
+
const resolved = path_1.default.resolve(options.repo.trim());
|
|
46
|
+
return findRepoRoot(resolved);
|
|
47
|
+
}
|
|
48
|
+
return findRepoRoot(process.cwd());
|
|
49
|
+
}
|
|
50
|
+
function findRepoRoot(startPath) {
|
|
51
|
+
let current = startPath;
|
|
52
|
+
while (true) {
|
|
53
|
+
const gitPath = path_1.default.join(current, ".git");
|
|
54
|
+
if (fs_1.default.existsSync(gitPath))
|
|
55
|
+
return current;
|
|
56
|
+
const parent = path_1.default.dirname(current);
|
|
57
|
+
if (parent === current)
|
|
58
|
+
return startPath;
|
|
59
|
+
current = parent;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.queryMemoryCommand = queryMemoryCommand;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const persistence_config_1 = require("../utils/persistence-config");
|
|
10
|
+
async function queryMemoryCommand(text, options) {
|
|
11
|
+
const { initDb, queryMemory } = (0, persistence_config_1.getPersistence)();
|
|
12
|
+
await initDb();
|
|
13
|
+
const limit = options.limit ?? 10;
|
|
14
|
+
const repoPath = resolveRepoScope(options);
|
|
15
|
+
const results = await queryMemory(text, limit, repoPath, {
|
|
16
|
+
dedupeBySymbol: options.dedupeBySymbol === true,
|
|
17
|
+
includeNonCanonical: options.includeNonCanonical === true,
|
|
18
|
+
});
|
|
19
|
+
if (results.length === 0) {
|
|
20
|
+
console.log("No results.");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
for (const r of results) {
|
|
24
|
+
const createdAt = r.createdAt instanceof Date
|
|
25
|
+
? r.createdAt.toISOString()
|
|
26
|
+
: String(r.createdAt);
|
|
27
|
+
console.log(`[${createdAt}] (${r.type}) ${r.title ?? "(untitled)"}`);
|
|
28
|
+
console.log(r.content);
|
|
29
|
+
if (r.tags?.length)
|
|
30
|
+
console.log(`tags: ${r.tags.join(", ")}`);
|
|
31
|
+
console.log(`status: ${r.status}`);
|
|
32
|
+
console.log(`confidence: ${r.confidence.toFixed(2)}`);
|
|
33
|
+
console.log(`repo: ${r.repo ?? "(unknown)"}`);
|
|
34
|
+
if (r.symbol)
|
|
35
|
+
console.log(`symbol: ${r.symbol}`);
|
|
36
|
+
if (r.externalId)
|
|
37
|
+
console.log(`externalId: ${r.externalId}`);
|
|
38
|
+
console.log(`id: ${r.id}`);
|
|
39
|
+
console.log("");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function resolveRepoScope(options) {
|
|
43
|
+
if (options.global === true)
|
|
44
|
+
return undefined;
|
|
45
|
+
if (options.repo && options.repo.trim()) {
|
|
46
|
+
return path_1.default.resolve(options.repo.trim());
|
|
47
|
+
}
|
|
48
|
+
return findRepoRoot(process.cwd());
|
|
49
|
+
}
|
|
50
|
+
function findRepoRoot(startPath) {
|
|
51
|
+
let current = startPath;
|
|
52
|
+
while (true) {
|
|
53
|
+
const gitPath = path_1.default.join(current, ".git");
|
|
54
|
+
if (fs_1.default.existsSync(gitPath))
|
|
55
|
+
return current;
|
|
56
|
+
const parent = path_1.default.dirname(current);
|
|
57
|
+
if (parent === current)
|
|
58
|
+
return startPath;
|
|
59
|
+
current = parent;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.querySymbolCommand = querySymbolCommand;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const persistence_config_1 = require("../utils/persistence-config");
|
|
10
|
+
async function querySymbolCommand(symbol, options) {
|
|
11
|
+
const { initDb, queryMemoryBySymbol } = (0, persistence_config_1.getPersistence)();
|
|
12
|
+
await initDb();
|
|
13
|
+
const limit = options.limit ?? 10;
|
|
14
|
+
const repoPath = resolveRepoScope(options);
|
|
15
|
+
const params = {
|
|
16
|
+
symbol,
|
|
17
|
+
limit,
|
|
18
|
+
includeDenied: options.includeDenied === true,
|
|
19
|
+
onlyCanonical: options.onlyCanonical === true,
|
|
20
|
+
};
|
|
21
|
+
if (repoPath)
|
|
22
|
+
params.repoPath = repoPath;
|
|
23
|
+
const results = await queryMemoryBySymbol(params);
|
|
24
|
+
if (results.length === 0) {
|
|
25
|
+
console.log("No results.");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
for (const r of results) {
|
|
29
|
+
console.log(`[${r.createdAt.toISOString()}] (${r.type}) ${r.title ?? "(untitled)"}`);
|
|
30
|
+
console.log(r.content);
|
|
31
|
+
if (r.tags?.length)
|
|
32
|
+
console.log(`tags: ${r.tags.join(", ")}`);
|
|
33
|
+
console.log(`status: ${r.status}`);
|
|
34
|
+
console.log(`confidence: ${r.confidence.toFixed(2)}`);
|
|
35
|
+
console.log(`repo: ${r.repo ?? "(unknown)"}`);
|
|
36
|
+
if (r.symbol)
|
|
37
|
+
console.log(`symbol: ${r.symbol}`);
|
|
38
|
+
if (r.externalId)
|
|
39
|
+
console.log(`externalId: ${r.externalId}`);
|
|
40
|
+
console.log(`id: ${r.id}`);
|
|
41
|
+
console.log("");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function resolveRepoScope(options) {
|
|
45
|
+
if (options.global === true)
|
|
46
|
+
return undefined;
|
|
47
|
+
if (options.repo && options.repo.trim()) {
|
|
48
|
+
return path_1.default.resolve(options.repo.trim());
|
|
49
|
+
}
|
|
50
|
+
return findRepoRoot(process.cwd());
|
|
51
|
+
}
|
|
52
|
+
function findRepoRoot(startPath) {
|
|
53
|
+
let current = startPath;
|
|
54
|
+
while (true) {
|
|
55
|
+
const gitPath = path_1.default.join(current, ".git");
|
|
56
|
+
if (fs_1.default.existsSync(gitPath))
|
|
57
|
+
return current;
|
|
58
|
+
const parent = path_1.default.dirname(current);
|
|
59
|
+
if (parent === current)
|
|
60
|
+
return startPath;
|
|
61
|
+
current = parent;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.scanGitCommand = scanGitCommand;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
|
10
|
+
const git_listener_1 = require("../adapters/git-adapter/git-listener");
|
|
11
|
+
const db_event_port_1 = require("../ports/db-event-port");
|
|
12
|
+
const cloud_event_port_1 = require("../ports/cloud-event-port");
|
|
13
|
+
const persistence_config_1 = require("../utils/persistence-config");
|
|
14
|
+
async function scanGitCommand(options) {
|
|
15
|
+
const { initDb, getMemoryByExternalId } = (0, persistence_config_1.getPersistence)();
|
|
16
|
+
const repoRoot = resolveRepoRoot(options);
|
|
17
|
+
await initDb();
|
|
18
|
+
const git = (0, simple_git_1.default)(repoRoot);
|
|
19
|
+
const log = await git.log({ maxCount: 1 });
|
|
20
|
+
const latestCommit = log.all?.[0];
|
|
21
|
+
if (!latestCommit?.hash) {
|
|
22
|
+
console.log(`Git scan complete (no commits) for repo: ${repoRoot}`);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const commitHash = String(latestCommit.hash);
|
|
26
|
+
const externalId = `git:${commitHash}`;
|
|
27
|
+
// Idempotency check via the active persistence adapter (local or cloud)
|
|
28
|
+
const existing = await getMemoryByExternalId(externalId, repoRoot);
|
|
29
|
+
if (existing) {
|
|
30
|
+
console.log(`Git scan skipped (already stored) for repo: ${repoRoot} commit: ${commitHash.slice(0, 8)}`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Use the appropriate event port based on configured persistence mode
|
|
34
|
+
const eventPort = (0, persistence_config_1.isCloudMode)()
|
|
35
|
+
? new cloud_event_port_1.CloudEventPort(repoRoot)
|
|
36
|
+
: new db_event_port_1.DbEventPort(repoRoot);
|
|
37
|
+
const adapter = new git_listener_1.GitListenerAdapter(repoRoot, eventPort, 5000);
|
|
38
|
+
await adapter.runOnceLatest();
|
|
39
|
+
console.log(`Git scan complete (latest commit) for repo: ${repoRoot} commit: ${commitHash.slice(0, 8)}`);
|
|
40
|
+
}
|
|
41
|
+
function resolveRepoRoot(options) {
|
|
42
|
+
if (options.repo && options.repo.trim()) {
|
|
43
|
+
const resolved = path_1.default.resolve(options.repo.trim());
|
|
44
|
+
return findRepoRoot(resolved);
|
|
45
|
+
}
|
|
46
|
+
return findRepoRoot(process.cwd());
|
|
47
|
+
}
|
|
48
|
+
function findRepoRoot(startPath) {
|
|
49
|
+
let current = startPath;
|
|
50
|
+
while (true) {
|
|
51
|
+
const gitPath = path_1.default.join(current, ".git");
|
|
52
|
+
if (fs_1.default.existsSync(gitPath))
|
|
53
|
+
return current;
|
|
54
|
+
const parent = path_1.default.dirname(current);
|
|
55
|
+
if (parent === current)
|
|
56
|
+
return startPath;
|
|
57
|
+
current = parent;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.uninstallHookCommand = uninstallHookCommand;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
async function uninstallHookCommand(options) {
|
|
10
|
+
const repoRoot = resolveRepoRoot(options);
|
|
11
|
+
const hookPath = path_1.default.join(repoRoot, ".git", "hooks", "post-commit");
|
|
12
|
+
if (!fs_1.default.existsSync(hookPath)) {
|
|
13
|
+
console.log("No hook found");
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const content = fs_1.default.readFileSync(hookPath, "utf-8");
|
|
17
|
+
if (!content.includes("# <kodingo>")) {
|
|
18
|
+
console.log("No hook found");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
// Remove the kodingo block and surrounding blank lines
|
|
22
|
+
const cleaned = content.replace(/\n*# <kodingo>\n[\s\S]*?# <\/kodingo>\n*/g, "\n");
|
|
23
|
+
const trimmed = cleaned.trim();
|
|
24
|
+
if (trimmed === "" || trimmed === "#!/bin/sh") {
|
|
25
|
+
fs_1.default.unlinkSync(hookPath);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
fs_1.default.writeFileSync(hookPath, trimmed + "\n", "utf-8");
|
|
29
|
+
fs_1.default.chmodSync(hookPath, 0o755);
|
|
30
|
+
}
|
|
31
|
+
console.log(`Kodingo hook uninstalled for repo: ${repoRoot}`);
|
|
32
|
+
}
|
|
33
|
+
function resolveRepoRoot(options) {
|
|
34
|
+
if (options.repo && options.repo.trim()) {
|
|
35
|
+
const resolved = path_1.default.resolve(options.repo.trim());
|
|
36
|
+
return findRepoRoot(resolved);
|
|
37
|
+
}
|
|
38
|
+
return findRepoRoot(process.cwd());
|
|
39
|
+
}
|
|
40
|
+
function findRepoRoot(startPath) {
|
|
41
|
+
let current = startPath;
|
|
42
|
+
while (true) {
|
|
43
|
+
const gitPath = path_1.default.join(current, ".git");
|
|
44
|
+
if (fs_1.default.existsSync(gitPath))
|
|
45
|
+
return current;
|
|
46
|
+
const parent = path_1.default.dirname(current);
|
|
47
|
+
if (parent === current)
|
|
48
|
+
return startPath;
|
|
49
|
+
current = parent;
|
|
50
|
+
}
|
|
51
|
+
}
|