kodingo-cli 1.0.31 → 1.0.33

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.
@@ -73,6 +73,16 @@ async function contextCommand(filePath, options) {
73
73
  }
74
74
  const data = await res.json();
75
75
  const memories = data.memories ?? [];
76
+ // Send metadata signal for each memory — agent queried it, that is weak evidence of relevance
77
+ for (const m of memories) {
78
+ if (m.id) {
79
+ fetch(`${apiUrl}/memory/${m.id}/evidence`, {
80
+ method: "PATCH",
81
+ headers: { "Content-Type": "application/json", "X-Kodingo-Token": token },
82
+ body: JSON.stringify({ weightClass: "metadata" }),
83
+ }).catch(() => { });
84
+ }
85
+ }
76
86
  if (!options.prompt) {
77
87
  console.log(JSON.stringify({ file: relFile, repo: repoName, count: memories.length, memories }, null, 2));
78
88
  return;
@@ -104,6 +104,37 @@ async function scanGitCommand(options) {
104
104
  const adapter = new git_listener_1.GitListenerAdapter(repoRoot, eventPort, 5000);
105
105
  await adapter.runOnceLatest();
106
106
  console.log(`Git scan complete (latest commit) for repo: ${repoRoot} commit: ${commitHash.slice(0, 8)}`);
107
+ // Send code_change signals for functions touched in this commit that already have memory
108
+ try {
109
+ const { token, apiUrl } = getCredentials(repoRoot);
110
+ const diffOutput = await git.diff([`${commitHash}^`, commitHash, '--name-only']).catch(() => '');
111
+ const commitChangedFiles = diffOutput.split('\n').filter(Boolean).map((f) => path_1.default.join(repoRoot, f)).filter((f) => /\.(ts|tsx|js|jsx|py|go|rs|php|java|kt|rb)$/.test(f));
112
+ if (token && commitChangedFiles.length > 0) {
113
+ const repoName = path_1.default.basename(repoRoot);
114
+ for (const filePath of commitChangedFiles) {
115
+ if (!fs_1.default.existsSync(filePath))
116
+ continue;
117
+ const source = fs_1.default.readFileSync(filePath, "utf-8");
118
+ const nodes = await (0, ast_parser_1.parseFile)(filePath, source);
119
+ for (const node of nodes) {
120
+ // Check if memory exists for this symbol
121
+ const res = await fetch(`${apiUrl}/memory?symbol=${encodeURIComponent(node.name)}&repo=${encodeURIComponent(repoName)}&limit=1`, { headers: { "X-Kodingo-Token": token } });
122
+ if (!res.ok)
123
+ continue;
124
+ const data = await res.json();
125
+ const existing = data.data?.[0];
126
+ if (existing?.id) {
127
+ await fetch(`${apiUrl}/memory/${existing.id}/evidence`, {
128
+ method: "PATCH",
129
+ headers: { "Content-Type": "application/json", "X-Kodingo-Token": token },
130
+ body: JSON.stringify({ weightClass: "code_change" }),
131
+ }).catch(() => { });
132
+ }
133
+ }
134
+ }
135
+ }
136
+ }
137
+ catch { }
107
138
  // Write flag file so VS Code extension knows to surface proposed memories
108
139
  writePendingReviewFlag(repoRoot, commitHash);
109
140
  }
@@ -113,8 +113,9 @@ async function extractSymbols(filePath) {
113
113
  return [];
114
114
  }
115
115
  }
116
- async function fetchExistingSymbols(token, repo) {
116
+ async function fetchExistingSymbols(token, repo, apiUrl) {
117
117
  const known = new Set();
118
+ const idMap = new Map(); // symbol::repo -> memory id
118
119
  let offset = 0;
119
120
  const limit = 200;
120
121
  try {
@@ -137,7 +138,17 @@ async function fetchExistingSymbols(token, repo) {
137
138
  }
138
139
  }
139
140
  catch { }
140
- return known;
141
+ return { known, idMap };
142
+ }
143
+ async function sendSignal(token, apiUrl, memoryId, weightClass) {
144
+ try {
145
+ await fetch(`${apiUrl}/memory/${memoryId}/evidence`, {
146
+ method: "PATCH",
147
+ headers: { "Content-Type": "application/json", "X-Kodingo-Token": token },
148
+ body: JSON.stringify({ weightClass }),
149
+ });
150
+ }
151
+ catch { }
141
152
  }
142
153
  async function inferAndCapture(token, symbol, repo) {
143
154
  try {
@@ -206,7 +217,7 @@ async function scanRepoCommand(options = {}) {
206
217
  console.log(` Checking which ones need memory...\n`);
207
218
  }
208
219
  // Fetch all existing memory symbols in one bulk call
209
- const existingSymbols = await fetchExistingSymbols(token, repoName);
220
+ const { known: existingSymbols, idMap: existingIds } = await fetchExistingSymbols(token, repoName, apiUrl);
210
221
  // Shuffle so repeated runs cover different parts of the codebase
211
222
  const shuffled = [...allSymbols].sort(() => Math.random() - 0.5);
212
223
  // Filter locally — no per-symbol API calls needed
@@ -221,8 +232,15 @@ async function scanRepoCommand(options = {}) {
221
232
  seen.add(key);
222
233
  const symRepoKey = `${sym.name}::${repoName}`;
223
234
  const hasMemory = existingSymbols.has(symRepoKey) || existingSymbols.has(sym.name);
224
- if (!hasMemory)
235
+ if (!hasMemory) {
225
236
  toCapture.push(sym);
237
+ }
238
+ else {
239
+ // Send structural signal — this function was seen again during a repo scan
240
+ const memId = existingIds.get(symRepoKey);
241
+ if (memId)
242
+ sendSignal(token, apiUrl, memId, "structural");
243
+ }
226
244
  }
227
245
  if (toCapture.length === 0) {
228
246
  if (!options.silent)
@@ -72,6 +72,16 @@ async function sessionBriefCommand(options) {
72
72
  }
73
73
  const data = await res.json();
74
74
  const memories = (data.memories ?? []).sort((a, b) => b.confidence - a.confidence);
75
+ // Send metadata signal for each memory surfaced in session brief
76
+ for (const m of memories) {
77
+ if (m.id) {
78
+ fetch(`${apiUrl}/memory/${m.id}/evidence`, {
79
+ method: "PATCH",
80
+ headers: { "Content-Type": "application/json", "X-Kodingo-Token": token },
81
+ body: JSON.stringify({ weightClass: "metadata" }),
82
+ }).catch(() => { });
83
+ }
84
+ }
75
85
  if (!options.prompt) {
76
86
  console.log(JSON.stringify({
77
87
  repo: repoName,
@@ -60,13 +60,22 @@ class CloudEventPort {
60
60
  return;
61
61
  }
62
62
  }
63
- // No existing proposed/ignored record — insert new
63
+ // No existing proposed/ignored record — validate invariants via core then insert
64
+ try {
65
+ (0, kodingo_core_1.enforceDecisionInvariants)(decision, _context);
66
+ }
67
+ catch (e) {
68
+ console.warn(`[core] Decision invariant failed: ${e.message} — skipping`);
69
+ return;
70
+ }
71
+ // Apply lifecycle — produces events and new memory state
72
+ const outcome = (0, kodingo_core_1.applyDecisionLifecycle)(decision, _context);
64
73
  const input = {
65
74
  type: "decision",
66
75
  content,
67
76
  repoPath: this.repoPath,
68
77
  status: "proposed",
69
- confidence: 0.3,
78
+ confidence: decision.confidence ?? 0.3,
70
79
  externalId,
71
80
  tags: ["signal:git", "kind:decision"],
72
81
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kodingo-cli",
3
- "version": "1.0.31",
3
+ "version": "1.0.33",
4
4
  "description": "Kodingo CLI",
5
5
  "license": "MIT",
6
6
  "private": false,