opencode-autognosis 2.0.5 → 2.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.
@@ -8,6 +8,7 @@ import * as crypto from "node:crypto";
8
8
  import { getDb } from "./database.js";
9
9
  import { CHUNK_DIR, ensureChunkDir, calculateHash, calculateComplexity, parseFileAST, generateSummaryChunk, generateApiChunk, generateInvariantChunk, extractDependencies, extractSymbolsFromAST, extractSymbols } from "./chunk-cards.js";
10
10
  import { Logger } from "./services/logger.js";
11
+ import { tui } from "./services/tui.js";
11
12
  const execAsync = promisify(exec);
12
13
  const PROJECT_ROOT = process.cwd();
13
14
  const OPENCODE_DIR = path.join(PROJECT_ROOT, ".opencode");
@@ -606,7 +607,7 @@ async function getAllSourceFiles() {
606
607
  await scanDirectory(PROJECT_ROOT);
607
608
  return sourceFiles;
608
609
  }
609
- async function indexFile(filePath) {
610
+ export async function indexFile(filePath) {
610
611
  try {
611
612
  const content = await fs.readFile(filePath, 'utf-8');
612
613
  await ensureChunkDir();
@@ -711,8 +712,11 @@ async function runBackgroundIndexing(taskId, indexingState) {
711
712
  processed++;
712
713
  // Update progress periodically
713
714
  if (processed % 5 === 0 || processed === total) {
714
- task.progress = Math.round((processed / total) * 100);
715
+ const progress = Math.round((processed / total) * 100);
716
+ task.progress = progress;
715
717
  await fs.writeFile(taskPath, JSON.stringify(task, null, 2));
718
+ // Stream to TUI
719
+ await tui.showProgress("Codebase Indexing", progress, `Processing: ${file}`);
716
720
  }
717
721
  }
718
722
  // Complete task
@@ -720,6 +724,7 @@ async function runBackgroundIndexing(taskId, indexingState) {
720
724
  task.completed_at = new Date().toISOString();
721
725
  task.progress = 100;
722
726
  await fs.writeFile(taskPath, JSON.stringify(task, null, 2));
727
+ await tui.showSuccess("Indexing Complete", `Processed ${total} files.`);
723
728
  }
724
729
  catch (error) {
725
730
  // Update task with error
@@ -0,0 +1,8 @@
1
+ export declare const DEFAULT_MLX_MODEL = "sentence-transformers/all-MiniLM-L6-v2";
2
+ export declare class MLXService {
3
+ private isAvailable;
4
+ checkAvailability(): Promise<boolean>;
5
+ setup(): Promise<string>;
6
+ getEmbedding(text: string, model?: string): Promise<number[]>;
7
+ }
8
+ export declare const mlxService: MLXService;
@@ -0,0 +1,53 @@
1
+ import { exec } from "node:child_process";
2
+ import { promisify } from "node:util";
3
+ import * as fs from "node:fs";
4
+ import * as path from "node:path";
5
+ import { Logger } from "./logger.js";
6
+ const execAsync = promisify(exec);
7
+ export const DEFAULT_MLX_MODEL = "sentence-transformers/all-MiniLM-L6-v2";
8
+ export class MLXService {
9
+ isAvailable = null;
10
+ async checkAvailability() {
11
+ if (this.isAvailable !== null)
12
+ return this.isAvailable;
13
+ try {
14
+ await execAsync('python3 -c "import mlx.core; import sentence_transformers"');
15
+ this.isAvailable = true;
16
+ }
17
+ catch {
18
+ this.isAvailable = false;
19
+ }
20
+ return this.isAvailable;
21
+ }
22
+ async setup() {
23
+ try {
24
+ Logger.log("MLX", "Setting up MLX dependencies...");
25
+ await execAsync("pip3 install mlx sentence-transformers huggingface_hub");
26
+ this.isAvailable = true;
27
+ return "MLX and sentence-transformers installed successfully.";
28
+ }
29
+ catch (error) {
30
+ throw new Error(`MLX setup failed: ${error.message}`);
31
+ }
32
+ }
33
+ async getEmbedding(text, model = DEFAULT_MLX_MODEL) {
34
+ if (!text || !text.trim())
35
+ return [];
36
+ // Escape text for python string
37
+ const escapedText = text.replace(/"/g, '\\"').replace(/\n/g, ' ');
38
+ // MLX optimized sentence-transformers execution
39
+ const pyScript = "\nimport mlx.core as mx\nfrom sentence_transformers import SentenceTransformer\nimport json\nimport sys\n\ntry:\n model = SentenceTransformer(\"${model}\")\n # Move to GPU if available (MLX default)\n embeddings = model.encode([\"${escapedText}\"])\n print(json.dumps(embeddings[0].tolist()))\nexcept Exception as e:\n print(json.dumps({\"error\": str(e)}))\n sys.exit(1)\n";
40
+ try {
41
+ const { stdout } = await execAsync(`python3 -c '${pyScript}'`);
42
+ const result = JSON.parse(stdout);
43
+ if (result.error)
44
+ throw new Error(result.error);
45
+ return result;
46
+ }
47
+ catch (error) {
48
+ Logger.log("MLX", "Embedding failed", error);
49
+ return [];
50
+ }
51
+ }
52
+ }
53
+ export const mlxService = new MLXService();
@@ -0,0 +1,12 @@
1
+ export interface PolicyViolation {
2
+ file: string;
3
+ line: number;
4
+ message: string;
5
+ severity: "error" | "warning";
6
+ }
7
+ export declare class PolicyEngine {
8
+ private rules;
9
+ checkContent(file: string, content: string): PolicyViolation[];
10
+ checkDiff(diff: string): PolicyViolation[];
11
+ }
12
+ export declare const policyEngine: PolicyEngine;
@@ -0,0 +1,59 @@
1
+ import * as fsSync from "node:fs";
2
+ import * as path from "node:path";
3
+ import { Logger } from "./logger.js";
4
+ export class PolicyEngine {
5
+ rules = [
6
+ {
7
+ name: "No Debug Logs",
8
+ pattern: /console\.(log|debug|info)\(/,
9
+ message: "Direct console logging is forbidden in production code.",
10
+ severity: "error"
11
+ },
12
+ {
13
+ name: "No TODO Debt",
14
+ pattern: /\/\/\s*TODO/,
15
+ message: "New TODOs must be linked to a ticket ID.",
16
+ severity: "warning"
17
+ },
18
+ {
19
+ name: "Forbidden Eval",
20
+ pattern: /eval\(/,
21
+ message: "Use of 'eval' is strictly forbidden for security reasons.",
22
+ severity: "error"
23
+ }
24
+ ];
25
+ checkContent(file, content) {
26
+ const violations = [];
27
+ const lines = content.split('\n');
28
+ for (const rule of this.rules) {
29
+ lines.forEach((line, index) => {
30
+ if (rule.pattern.test(line)) {
31
+ violations.push({
32
+ file,
33
+ line: index + 1,
34
+ message: rule.message,
35
+ severity: rule.severity
36
+ });
37
+ }
38
+ });
39
+ }
40
+ return violations;
41
+ }
42
+ checkDiff(diff) {
43
+ // Check only added lines in diffs
44
+ const addedLines = diff.split('\n').filter(l => l.startsWith('+') && !l.startsWith('+++'));
45
+ const violations = [];
46
+ for (const rule of this.rules) {
47
+ if (rule.pattern.test(addedLines.join('\n'))) {
48
+ violations.push({
49
+ file: "diff",
50
+ line: 0,
51
+ message: `[Policy: ${rule.name}] ${rule.message}`,
52
+ severity: rule.severity
53
+ });
54
+ }
55
+ }
56
+ return violations;
57
+ }
58
+ }
59
+ export const policyEngine = new PolicyEngine();
@@ -0,0 +1,8 @@
1
+ export declare class TUIService {
2
+ private client;
3
+ setClient(client: any): void;
4
+ showProgress(title: string, progress: number, message: string): Promise<void>;
5
+ showSuccess(title: string, message: string): Promise<void>;
6
+ showError(title: string, message: string): Promise<void>;
7
+ }
8
+ export declare const tui: TUIService;
@@ -0,0 +1,43 @@
1
+ export class TUIService {
2
+ client;
3
+ setClient(client) {
4
+ this.client = client;
5
+ }
6
+ async showProgress(title, progress, message) {
7
+ if (!this.client)
8
+ return;
9
+ try {
10
+ await this.client.tui.showToast({
11
+ body: {
12
+ title: `[${progress}%] ${title}`,
13
+ message,
14
+ variant: "info"
15
+ }
16
+ });
17
+ }
18
+ catch (e) {
19
+ // Ignore if TUI not available
20
+ }
21
+ }
22
+ async showSuccess(title, message) {
23
+ if (!this.client)
24
+ return;
25
+ try {
26
+ await this.client.tui.showToast({
27
+ body: { title, message, variant: "success" }
28
+ });
29
+ }
30
+ catch (e) { }
31
+ }
32
+ async showError(title, message) {
33
+ if (!this.client)
34
+ return;
35
+ try {
36
+ await this.client.tui.showToast({
37
+ body: { title, message, variant: "error" }
38
+ });
39
+ }
40
+ catch (e) { }
41
+ }
42
+ }
43
+ export const tui = new TUIService();
@@ -0,0 +1,8 @@
1
+ export declare class CodeWatcher {
2
+ private watcher;
3
+ start(): void;
4
+ stop(): void;
5
+ private handleFileChange;
6
+ private handleFileDelete;
7
+ }
8
+ export declare const codeWatcher: CodeWatcher;
@@ -0,0 +1,50 @@
1
+ import chokidar, { FSWatcher } from "chokidar";
2
+ import * as path from "node:path";
3
+ import { indexFile } from "../performance-optimization.js";
4
+ import { Logger } from "./logger.js";
5
+ const PROJECT_ROOT = process.cwd();
6
+ export class CodeWatcher {
7
+ watcher = null;
8
+ start() {
9
+ if (this.watcher)
10
+ return;
11
+ Logger.log("Watcher", "Starting live codebase watcher...");
12
+ this.watcher = chokidar.watch(PROJECT_ROOT, {
13
+ ignored: [
14
+ "**/node_modules/**",
15
+ "**/dist/**",
16
+ "**/build/**",
17
+ "**/.opencode/**"
18
+ ],
19
+ persistent: true,
20
+ ignoreInitial: true
21
+ });
22
+ this.watcher
23
+ .on("add", (filePath) => this.handleFileChange("added", filePath))
24
+ .on("change", (filePath) => this.handleFileChange("changed", filePath))
25
+ .on("unlink", (filePath) => this.handleFileDelete(filePath));
26
+ }
27
+ stop() {
28
+ if (this.watcher) {
29
+ this.watcher.close();
30
+ this.watcher = null;
31
+ }
32
+ }
33
+ async handleFileChange(event, filePath) {
34
+ const ext = path.extname(filePath);
35
+ const supportedExts = [".ts", ".js", ".tsx", ".jsx", ".cpp", ".c", ".h", ".hpp", ".swift", ".py", ".go", ".rs"];
36
+ if (supportedExts.includes(ext)) {
37
+ Logger.log("Watcher", `File ${event}: ${filePath}`);
38
+ try {
39
+ await indexFile(filePath);
40
+ }
41
+ catch (e) {
42
+ Logger.log("Watcher", `Failed to index ${filePath}`, e);
43
+ }
44
+ }
45
+ }
46
+ handleFileDelete(filePath) {
47
+ Logger.log("Watcher", `File deleted: ${filePath}`);
48
+ }
49
+ }
50
+ export const codeWatcher = new CodeWatcher();
@@ -7,6 +7,7 @@ import { promisify } from "node:util";
7
7
  import * as crypto from "node:crypto";
8
8
  import { Logger } from "./services/logger.js";
9
9
  import { getDb } from "./database.js";
10
+ import { tui } from "./services/tui.js";
10
11
  const execAsync = promisify(exec);
11
12
  const PROJECT_ROOT = process.cwd();
12
13
  const OPENCODE_DIR = path.join(PROJECT_ROOT, ".opencode");
@@ -305,10 +306,12 @@ export function systemTools() {
305
306
  // Spawn background worker
306
307
  (async () => {
307
308
  getDb().updateJob(jobId, { status: "running", progress: 10 });
309
+ await tui.showProgress("Patch Validation", 10, "Creating temporary worktree...");
308
310
  const tempWorktree = path.join(PROJECT_ROOT, ".opencode", "temp-" + jobId);
309
311
  try {
310
312
  await runCmd(`git worktree add -d "${tempWorktree}"`);
311
313
  getDb().updateJob(jobId, { progress: 30 });
314
+ await tui.showProgress("Patch Validation", 30, "Applying diff...");
312
315
  const content = await fs.readFile(patch_path, "utf-8");
313
316
  const parts = content.split('\n\n');
314
317
  const diffOnly = parts.length > 1 ? parts.slice(1).join('\n\n') : content;
@@ -318,6 +321,7 @@ export function systemTools() {
318
321
  if (applyError)
319
322
  throw new Error(`Apply failed: ${applyError.message}`);
320
323
  getDb().updateJob(jobId, { progress: 60 });
324
+ await tui.showProgress("Patch Validation", 60, "Running build verification...");
321
325
  let buildStatus = "SKIPPED";
322
326
  if (fsSync.existsSync(path.join(tempWorktree, "package.json"))) {
323
327
  const { error: buildError } = await runCmd("npm run build", tempWorktree);
@@ -332,9 +336,11 @@ export function systemTools() {
332
336
  progress: 100,
333
337
  result: JSON.stringify({ apply: "OK", build: buildStatus })
334
338
  });
339
+ await tui.showSuccess("Validation Complete", `Apply: OK, Build: ${buildStatus}`);
335
340
  }
336
341
  catch (error) {
337
342
  getDb().updateJob(jobId, { status: "failed", error: error.message });
343
+ await tui.showError("Validation Failed", error.message);
338
344
  }
339
345
  finally {
340
346
  try {