overarch-linux-arm64 0.8.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.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +214 -0
  3. package/dist/bin/overarch.d.ts +2 -0
  4. package/dist/bin/overarch.js +178 -0
  5. package/dist/src/agent/context.d.ts +14 -0
  6. package/dist/src/agent/context.js +200 -0
  7. package/dist/src/agent/context_compressor.d.ts +21 -0
  8. package/dist/src/agent/context_compressor.js +90 -0
  9. package/dist/src/agent/manifest.d.ts +22 -0
  10. package/dist/src/agent/manifest.js +131 -0
  11. package/dist/src/agent/planner.d.ts +38 -0
  12. package/dist/src/agent/planner.js +118 -0
  13. package/dist/src/agent/runtime.d.ts +59 -0
  14. package/dist/src/agent/runtime.js +400 -0
  15. package/dist/src/cli/approval.d.ts +13 -0
  16. package/dist/src/cli/approval.js +71 -0
  17. package/dist/src/cli/args.d.ts +15 -0
  18. package/dist/src/cli/args.js +47 -0
  19. package/dist/src/cli/commands/audit.d.ts +10 -0
  20. package/dist/src/cli/commands/audit.js +79 -0
  21. package/dist/src/cli/commands/companion.d.ts +1 -0
  22. package/dist/src/cli/commands/companion.js +143 -0
  23. package/dist/src/cli/commands/config.d.ts +1 -0
  24. package/dist/src/cli/commands/config.js +135 -0
  25. package/dist/src/cli/commands/doctor.d.ts +1 -0
  26. package/dist/src/cli/commands/doctor.js +217 -0
  27. package/dist/src/cli/commands/init.d.ts +1 -0
  28. package/dist/src/cli/commands/init.js +134 -0
  29. package/dist/src/cli/commands/memory.d.ts +2 -0
  30. package/dist/src/cli/commands/memory.js +98 -0
  31. package/dist/src/cli/commands/onboarding.d.ts +3 -0
  32. package/dist/src/cli/commands/onboarding.js +223 -0
  33. package/dist/src/cli/commands/sessions.d.ts +1 -0
  34. package/dist/src/cli/commands/sessions.js +22 -0
  35. package/dist/src/cli/commands/status.d.ts +1 -0
  36. package/dist/src/cli/commands/status.js +37 -0
  37. package/dist/src/cli/commands/update.d.ts +9 -0
  38. package/dist/src/cli/commands/update.js +105 -0
  39. package/dist/src/cli/repl.d.ts +3 -0
  40. package/dist/src/cli/repl.js +821 -0
  41. package/dist/src/cli/ui.d.ts +53 -0
  42. package/dist/src/cli/ui.js +215 -0
  43. package/dist/src/companion/petdex_adapter.d.ts +31 -0
  44. package/dist/src/companion/petdex_adapter.js +171 -0
  45. package/dist/src/companion/petdex_bridge.d.ts +65 -0
  46. package/dist/src/companion/petdex_bridge.js +356 -0
  47. package/dist/src/companion/types.d.ts +57 -0
  48. package/dist/src/companion/types.js +11 -0
  49. package/dist/src/config/index.d.ts +24 -0
  50. package/dist/src/config/index.js +237 -0
  51. package/dist/src/config/types.d.ts +58 -0
  52. package/dist/src/config/types.js +7 -0
  53. package/dist/src/core/diagnostics.d.ts +48 -0
  54. package/dist/src/core/diagnostics.js +115 -0
  55. package/dist/src/core/index.d.ts +23 -0
  56. package/dist/src/core/index.js +23 -0
  57. package/dist/src/core/mission.d.ts +55 -0
  58. package/dist/src/core/mission.js +173 -0
  59. package/dist/src/index.d.ts +1 -0
  60. package/dist/src/index.js +1 -0
  61. package/dist/src/learning/memory_manager.d.ts +26 -0
  62. package/dist/src/learning/memory_manager.js +211 -0
  63. package/dist/src/learning/types.d.ts +54 -0
  64. package/dist/src/learning/types.js +1 -0
  65. package/dist/src/mcp/registry.d.ts +17 -0
  66. package/dist/src/mcp/registry.js +97 -0
  67. package/dist/src/mcp/types.d.ts +13 -0
  68. package/dist/src/mcp/types.js +1 -0
  69. package/dist/src/providers/anthropic.d.ts +10 -0
  70. package/dist/src/providers/anthropic.js +95 -0
  71. package/dist/src/providers/base.d.ts +15 -0
  72. package/dist/src/providers/base.js +108 -0
  73. package/dist/src/providers/factory.d.ts +28 -0
  74. package/dist/src/providers/factory.js +142 -0
  75. package/dist/src/providers/gemini.d.ts +10 -0
  76. package/dist/src/providers/gemini.js +97 -0
  77. package/dist/src/providers/openai.d.ts +9 -0
  78. package/dist/src/providers/openai.js +85 -0
  79. package/dist/src/providers/openrouter.d.ts +5 -0
  80. package/dist/src/providers/openrouter.js +14 -0
  81. package/dist/src/providers/registry.d.ts +19 -0
  82. package/dist/src/providers/registry.js +209 -0
  83. package/dist/src/providers/types.d.ts +59 -0
  84. package/dist/src/providers/types.js +1 -0
  85. package/dist/src/providers/validator.d.ts +24 -0
  86. package/dist/src/providers/validator.js +161 -0
  87. package/dist/src/security/audit.d.ts +56 -0
  88. package/dist/src/security/audit.js +154 -0
  89. package/dist/src/security/guardrails.d.ts +9 -0
  90. package/dist/src/security/guardrails.js +32 -0
  91. package/dist/src/security/permissions.d.ts +12 -0
  92. package/dist/src/security/permissions.js +68 -0
  93. package/dist/src/security/policy.d.ts +220 -0
  94. package/dist/src/security/policy.js +720 -0
  95. package/dist/src/session/manager.d.ts +22 -0
  96. package/dist/src/session/manager.js +172 -0
  97. package/dist/src/session/types.d.ts +36 -0
  98. package/dist/src/session/types.js +1 -0
  99. package/dist/src/tools/checkpoint.d.ts +26 -0
  100. package/dist/src/tools/checkpoint.js +160 -0
  101. package/dist/src/tools/diagnostics.d.ts +2 -0
  102. package/dist/src/tools/diagnostics.js +125 -0
  103. package/dist/src/tools/diff.d.ts +14 -0
  104. package/dist/src/tools/diff.js +124 -0
  105. package/dist/src/tools/filesystem.d.ts +2 -0
  106. package/dist/src/tools/filesystem.js +224 -0
  107. package/dist/src/tools/git.d.ts +2 -0
  108. package/dist/src/tools/git.js +138 -0
  109. package/dist/src/tools/markdown.d.ts +33 -0
  110. package/dist/src/tools/markdown.js +190 -0
  111. package/dist/src/tools/project.d.ts +2 -0
  112. package/dist/src/tools/project.js +63 -0
  113. package/dist/src/tools/registry.d.ts +14 -0
  114. package/dist/src/tools/registry.js +230 -0
  115. package/dist/src/tools/search.d.ts +2 -0
  116. package/dist/src/tools/search.js +136 -0
  117. package/dist/src/tools/shell.d.ts +2 -0
  118. package/dist/src/tools/shell.js +74 -0
  119. package/dist/src/tools/symbols.d.ts +30 -0
  120. package/dist/src/tools/symbols.js +155 -0
  121. package/dist/src/tools/types.d.ts +28 -0
  122. package/dist/src/tools/types.js +1 -0
  123. package/dist/src/tools/web_search.d.ts +14 -0
  124. package/dist/src/tools/web_search.js +154 -0
  125. package/install.sh +29 -0
  126. package/overarch +15 -0
  127. package/package.json +28 -0
@@ -0,0 +1,74 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { PermissionTier } from '../config/types.js';
3
+ import { policyEngine } from '../security/policy.js';
4
+ export function createShellTools() {
5
+ return [
6
+ {
7
+ definition: {
8
+ name: 'executeCommand',
9
+ description: 'Execute a shell command',
10
+ tier: PermissionTier.StateMutating,
11
+ parameters: [
12
+ { name: 'command', type: 'string', description: 'Command to run', required: true },
13
+ { name: 'cwd', type: 'string', description: 'Working directory', required: false }
14
+ ]
15
+ },
16
+ handler: async (params) => {
17
+ const cmd = String(params.command);
18
+ const cwd = params.cwd ? String(params.cwd) : process.cwd();
19
+ const isWindows = process.platform === 'win32';
20
+ return new Promise((resolve) => {
21
+ const child = spawn(cmd, [], {
22
+ shell: isWindows ? 'powershell.exe' : true,
23
+ cwd,
24
+ windowsVerbatimArguments: true,
25
+ });
26
+ if (child.pid) {
27
+ policyEngine.registerProcess(child.pid);
28
+ child.on('close', () => {
29
+ if (child.pid)
30
+ policyEngine.unregisterProcess(child.pid);
31
+ });
32
+ }
33
+ let stdout = '';
34
+ let stderr = '';
35
+ child.stdout?.on('data', (data) => {
36
+ if (stdout.length < 10240) {
37
+ stdout += data.toString();
38
+ }
39
+ });
40
+ child.stderr?.on('data', (data) => {
41
+ if (stderr.length < 5120) {
42
+ stderr += data.toString();
43
+ }
44
+ });
45
+ let timer = setTimeout(() => {
46
+ child.kill();
47
+ resolve({
48
+ success: false,
49
+ output: stdout,
50
+ error: 'Command timed out after 60s.\n' + stderr
51
+ });
52
+ }, 60000);
53
+ child.on('close', (code) => {
54
+ clearTimeout(timer);
55
+ resolve({
56
+ success: code === 0,
57
+ output: stdout.slice(0, 10240),
58
+ error: stderr.slice(0, 5120),
59
+ metadata: { code }
60
+ });
61
+ });
62
+ child.on('error', (err) => {
63
+ clearTimeout(timer);
64
+ resolve({
65
+ success: false,
66
+ output: stdout,
67
+ error: err.message
68
+ });
69
+ });
70
+ });
71
+ }
72
+ }
73
+ ];
74
+ }
@@ -0,0 +1,30 @@
1
+ export interface CodeSymbol {
2
+ name: string;
3
+ kind: 'function' | 'class' | 'interface' | 'type' | 'variable' | 'export';
4
+ line: number;
5
+ file: string;
6
+ }
7
+ export interface SymbolReference {
8
+ file: string;
9
+ line: number;
10
+ context: string;
11
+ }
12
+ export interface ChangeImpact {
13
+ targetFile: string;
14
+ symbolsChanged: string[];
15
+ referencingFiles: string[];
16
+ affectedTests: string[];
17
+ }
18
+ /**
19
+ * Lightweight regex-based AST extractor for common languages (TS, JS, Python, Go)
20
+ */
21
+ export declare function extractSymbols(content: string, filePath: string): CodeSymbol[];
22
+ /**
23
+ * Finds all references to a symbol across files in a directory
24
+ */
25
+ export declare function findSymbolReferences(cwd: string, symbolName: string, excludes?: string[]): Promise<SymbolReference[]>;
26
+ /**
27
+ * Computes change-impact analysis: given a file, discovers its symbols,
28
+ * referencing files, and matching tests.
29
+ */
30
+ export declare function analyzeChangeImpact(cwd: string, targetFile: string): Promise<ChangeImpact>;
@@ -0,0 +1,155 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ /**
4
+ * Lightweight regex-based AST extractor for common languages (TS, JS, Python, Go)
5
+ */
6
+ export function extractSymbols(content, filePath) {
7
+ const symbols = [];
8
+ const lines = content.split('\n');
9
+ for (let i = 0; i < lines.length; i++) {
10
+ const line = lines[i].trim();
11
+ const lineNum = i + 1;
12
+ // TypeScript / JavaScript
13
+ const fnMatch = line.match(/^(?:export\s+)?(?:async\s+)?function\s+([a-zA-Z0-9_$]+)/);
14
+ if (fnMatch) {
15
+ symbols.push({ name: fnMatch[1], kind: 'function', line: lineNum, file: filePath });
16
+ continue;
17
+ }
18
+ const classMatch = line.match(/^(?:export\s+)?(?:abstract\s+)?class\s+([a-zA-Z0-9_$]+)/);
19
+ if (classMatch) {
20
+ symbols.push({ name: classMatch[1], kind: 'class', line: lineNum, file: filePath });
21
+ continue;
22
+ }
23
+ const interfaceMatch = line.match(/^(?:export\s+)?interface\s+([a-zA-Z0-9_$]+)/);
24
+ if (interfaceMatch) {
25
+ symbols.push({ name: interfaceMatch[1], kind: 'interface', line: lineNum, file: filePath });
26
+ continue;
27
+ }
28
+ const typeMatch = line.match(/^(?:export\s+)?type\s+([a-zA-Z0-9_$]+)\s*=/);
29
+ if (typeMatch) {
30
+ symbols.push({ name: typeMatch[1], kind: 'type', line: lineNum, file: filePath });
31
+ continue;
32
+ }
33
+ const constFnMatch = line.match(/^(?:export\s+)?const\s+([a-zA-Z0-9_$]+)\s*=\s*(?:async\s*)?\(/);
34
+ if (constFnMatch) {
35
+ symbols.push({ name: constFnMatch[1], kind: 'function', line: lineNum, file: filePath });
36
+ continue;
37
+ }
38
+ // Python
39
+ const pyDef = line.match(/^def\s+([a-zA-Z0-9_]+)\s*\(/);
40
+ if (pyDef) {
41
+ symbols.push({ name: pyDef[1], kind: 'function', line: lineNum, file: filePath });
42
+ continue;
43
+ }
44
+ const pyClass = line.match(/^class\s+([a-zA-Z0-9_]+)/);
45
+ if (pyClass) {
46
+ symbols.push({ name: pyClass[1], kind: 'class', line: lineNum, file: filePath });
47
+ continue;
48
+ }
49
+ // Go
50
+ const goFn = line.match(/^func\s+(?:\([a-zA-Z0-9_*\s]+\)\s+)?([a-zA-Z0-9_]+)\s*\(/);
51
+ if (goFn) {
52
+ symbols.push({ name: goFn[1], kind: 'function', line: lineNum, file: filePath });
53
+ continue;
54
+ }
55
+ }
56
+ return symbols;
57
+ }
58
+ /**
59
+ * Finds all references to a symbol across files in a directory
60
+ */
61
+ export async function findSymbolReferences(cwd, symbolName, excludes = ['node_modules', '.git', 'dist', 'build']) {
62
+ const references = [];
63
+ const regex = new RegExp(`\\b${symbolName}\\b`);
64
+ async function scan(dir) {
65
+ let entries;
66
+ try {
67
+ entries = await fs.readdir(dir, { withFileTypes: true });
68
+ }
69
+ catch {
70
+ return;
71
+ }
72
+ for (const entry of entries) {
73
+ const fullPath = path.join(dir, entry.name);
74
+ const relPath = path.relative(cwd, fullPath);
75
+ if (entry.isDirectory()) {
76
+ if (excludes.some(ex => relPath.split(path.sep).includes(ex)))
77
+ continue;
78
+ await scan(fullPath);
79
+ }
80
+ else if (entry.isFile()) {
81
+ const ext = path.extname(entry.name).toLowerCase();
82
+ if (!['.ts', '.js', '.jsx', '.tsx', '.py', '.go', '.rs'].includes(ext))
83
+ continue;
84
+ try {
85
+ const content = await fs.readFile(fullPath, 'utf8');
86
+ const lines = content.split('\n');
87
+ for (let i = 0; i < lines.length; i++) {
88
+ if (regex.test(lines[i])) {
89
+ references.push({
90
+ file: relPath.replace(/\\/g, '/'),
91
+ line: i + 1,
92
+ context: lines[i].trim()
93
+ });
94
+ }
95
+ }
96
+ }
97
+ catch { }
98
+ }
99
+ }
100
+ }
101
+ await scan(cwd);
102
+ return references;
103
+ }
104
+ /**
105
+ * Computes change-impact analysis: given a file, discovers its symbols,
106
+ * referencing files, and matching tests.
107
+ */
108
+ export async function analyzeChangeImpact(cwd, targetFile) {
109
+ const absPath = path.isAbsolute(targetFile) ? targetFile : path.join(cwd, targetFile);
110
+ const relPath = path.relative(cwd, absPath).replace(/\\/g, '/');
111
+ let content = '';
112
+ try {
113
+ content = await fs.readFile(absPath, 'utf8');
114
+ }
115
+ catch {
116
+ return { targetFile: relPath, symbolsChanged: [], referencingFiles: [], affectedTests: [] };
117
+ }
118
+ const symbols = extractSymbols(content, relPath);
119
+ const referencingFilesSet = new Set();
120
+ const affectedTestsSet = new Set();
121
+ // Check direct test mapping
122
+ const baseName = path.basename(relPath, path.extname(relPath));
123
+ const testPatterns = [
124
+ `tests/${baseName}.test.ts`,
125
+ `tests/${baseName}.test.js`,
126
+ `test/${baseName}.test.ts`,
127
+ `tests/test_${baseName}.py`,
128
+ `${baseName}_test.go`
129
+ ];
130
+ for (const tp of testPatterns) {
131
+ try {
132
+ await fs.access(path.join(cwd, tp));
133
+ affectedTestsSet.add(tp);
134
+ }
135
+ catch { }
136
+ }
137
+ // Scan top 5 symbols for callers
138
+ for (const sym of symbols.slice(0, 5)) {
139
+ const refs = await findSymbolReferences(cwd, sym.name);
140
+ for (const ref of refs) {
141
+ if (ref.file !== relPath) {
142
+ referencingFilesSet.add(ref.file);
143
+ if (ref.file.toLowerCase().includes('test') || ref.file.toLowerCase().includes('spec')) {
144
+ affectedTestsSet.add(ref.file);
145
+ }
146
+ }
147
+ }
148
+ }
149
+ return {
150
+ targetFile: relPath,
151
+ symbolsChanged: symbols.map(s => `${s.kind} ${s.name}`),
152
+ referencingFiles: Array.from(referencingFilesSet),
153
+ affectedTests: Array.from(affectedTestsSet)
154
+ };
155
+ }
@@ -0,0 +1,28 @@
1
+ import { PermissionTier } from '../config/types.js';
2
+ export interface ToolParameter {
3
+ name: string;
4
+ type: 'string' | 'number' | 'boolean' | 'array' | 'object';
5
+ description: string;
6
+ required: boolean;
7
+ enum?: string[];
8
+ items?: {
9
+ type: string;
10
+ };
11
+ }
12
+ export interface ToolDefinition {
13
+ name: string;
14
+ description: string;
15
+ parameters: ToolParameter[];
16
+ tier: PermissionTier;
17
+ }
18
+ export interface ToolResult {
19
+ success: boolean;
20
+ output: string;
21
+ error?: string;
22
+ metadata?: Record<string, unknown>;
23
+ }
24
+ export type ToolHandler = (params: Record<string, unknown>) => Promise<ToolResult>;
25
+ export interface Tool {
26
+ definition: ToolDefinition;
27
+ handler: ToolHandler;
28
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ export interface WebSearchResult {
2
+ title: string;
3
+ snippet: string;
4
+ url: string;
5
+ }
6
+ export interface WebSearchResponse {
7
+ query: string;
8
+ results: WebSearchResult[];
9
+ totalResults: number;
10
+ }
11
+ /**
12
+ * Executes a web search using DuckDuckGo HTML endpoint without external dependencies.
13
+ */
14
+ export declare function searchWeb(query: string, maxResults?: number): Promise<WebSearchResponse>;
@@ -0,0 +1,154 @@
1
+ import * as https from 'node:https';
2
+ import * as http from 'node:http';
3
+ import { URL } from 'node:url';
4
+ /**
5
+ * Clean HTML tags and decode HTML entities from text.
6
+ */
7
+ function cleanHtml(raw) {
8
+ return raw
9
+ .replace(/<[^>]+>/g, '')
10
+ .replace(/&amp;/g, '&')
11
+ .replace(/&lt;/g, '<')
12
+ .replace(/&gt;/g, '>')
13
+ .replace(/&quot;/g, '"')
14
+ .replace(/&#39;/g, "'")
15
+ .replace(/&nbsp;/g, ' ')
16
+ .replace(/\s+/g, ' ')
17
+ .trim();
18
+ }
19
+ /**
20
+ * Standard-library HTTP GET helper with timeout.
21
+ */
22
+ function fetchText(urlStr, timeoutMs = 8000) {
23
+ return new Promise((resolve, reject) => {
24
+ const parsedUrl = new URL(urlStr);
25
+ const client = parsedUrl.protocol === 'https:' ? https : http;
26
+ const req = client.get(urlStr, {
27
+ headers: {
28
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
29
+ Accept: 'text/html,application/xhtml+xml,application/json,*/*',
30
+ 'Accept-Language': 'en-US,en;q=0.9',
31
+ },
32
+ }, (res) => {
33
+ // Follow redirect (301, 302)
34
+ if (res.statusCode &&
35
+ res.statusCode >= 300 &&
36
+ res.statusCode < 400 &&
37
+ res.headers.location) {
38
+ const redirectUrl = new URL(res.headers.location, urlStr).toString();
39
+ return fetchText(redirectUrl, timeoutMs).then(resolve).catch(reject);
40
+ }
41
+ if (res.statusCode && (res.statusCode < 200 || res.statusCode >= 300)) {
42
+ return reject(new Error(`HTTP request failed with status ${res.statusCode}`));
43
+ }
44
+ let data = '';
45
+ res.on('data', (chunk) => (data += chunk));
46
+ res.on('end', () => resolve(data));
47
+ });
48
+ req.on('error', reject);
49
+ req.setTimeout(timeoutMs, () => {
50
+ req.destroy();
51
+ reject(new Error(`Search request timed out after ${timeoutMs}ms`));
52
+ });
53
+ });
54
+ }
55
+ /**
56
+ * Executes a web search using DuckDuckGo HTML endpoint without external dependencies.
57
+ */
58
+ export async function searchWeb(query, maxResults = 5) {
59
+ const cleanQuery = query.trim();
60
+ if (!cleanQuery) {
61
+ return { query: '', results: [], totalResults: 0 };
62
+ }
63
+ const encoded = encodeURIComponent(cleanQuery);
64
+ const searchUrl = `https://html.duckduckgo.com/html/?q=${encoded}`;
65
+ try {
66
+ const html = await fetchText(searchUrl, 10000);
67
+ const results = [];
68
+ // Parse DuckDuckGo HTML results
69
+ // Matches: <a class="result__snippet" ...>...</a> and result links
70
+ const resultBlocks = html.split(/class="result\s+results_links/i).slice(1);
71
+ for (const block of resultBlocks) {
72
+ if (results.length >= maxResults)
73
+ break;
74
+ // Title & URL
75
+ const linkMatch = block.match(/<a[^>]+class="result__url"[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/i) ||
76
+ block.match(/<a[^>]+class="result__snippet"[^>]+href="([^"]+)"[^>]*>/i);
77
+ const titleMatch = block.match(/<a[^>]+class="result__a"[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/i);
78
+ const snippetMatch = block.match(/<a[^>]+class="result__snippet"[^>]*>([\s\S]*?)<\/a>/i);
79
+ let url = '';
80
+ if (titleMatch && titleMatch[1]) {
81
+ url = titleMatch[1];
82
+ }
83
+ else if (linkMatch && linkMatch[1]) {
84
+ url = linkMatch[1];
85
+ }
86
+ // Unwrap DuckDuckGo redirect uddg= if present
87
+ if (url.includes('uddg=')) {
88
+ try {
89
+ const match = url.match(/uddg=([^&]+)/);
90
+ if (match)
91
+ url = decodeURIComponent(match[1]);
92
+ }
93
+ catch { }
94
+ }
95
+ const title = titleMatch ? cleanHtml(titleMatch[2]) : cleanQuery;
96
+ const snippet = snippetMatch ? cleanHtml(snippetMatch[1]) : '';
97
+ if (url && (title || snippet)) {
98
+ results.push({
99
+ title: title || cleanQuery,
100
+ snippet: snippet || 'No snippet available',
101
+ url,
102
+ });
103
+ }
104
+ }
105
+ // Fallback if HTML layout changes: DuckDuckGo instant API
106
+ if (results.length === 0) {
107
+ try {
108
+ const apiUrl = `https://api.duckduckgo.com/?q=${encoded}&format=json&no_html=1&skip_disambig=1`;
109
+ const jsonText = await fetchText(apiUrl, 5000);
110
+ const data = JSON.parse(jsonText);
111
+ if (data.AbstractText) {
112
+ results.push({
113
+ title: data.Heading || cleanQuery,
114
+ snippet: data.AbstractText,
115
+ url: data.AbstractURL || 'https://duckduckgo.com',
116
+ });
117
+ }
118
+ if (Array.isArray(data.RelatedTopics)) {
119
+ for (const topic of data.RelatedTopics) {
120
+ if (results.length >= maxResults)
121
+ break;
122
+ if (topic.Text && topic.FirstURL) {
123
+ results.push({
124
+ title: topic.Text.split(' - ')[0] || cleanQuery,
125
+ snippet: topic.Text,
126
+ url: topic.FirstURL,
127
+ });
128
+ }
129
+ }
130
+ }
131
+ }
132
+ catch { }
133
+ }
134
+ return {
135
+ query: cleanQuery,
136
+ results,
137
+ totalResults: results.length,
138
+ };
139
+ }
140
+ catch (err) {
141
+ // Return empty results with error message rather than crashing
142
+ return {
143
+ query: cleanQuery,
144
+ results: [
145
+ {
146
+ title: `Search error for: ${cleanQuery}`,
147
+ snippet: `Unable to retrieve live search results: ${err.message}`,
148
+ url: `https://duckduckgo.com/?q=${encoded}`,
149
+ },
150
+ ],
151
+ totalResults: 0,
152
+ };
153
+ }
154
+ }
package/install.sh ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env sh
2
+
3
+ set -e
4
+
5
+ INSTALL_DIR="${OVERARCH_INSTALL_DIR:-$HOME/.local/bin}"
6
+
7
+ mkdir -p "$INSTALL_DIR"
8
+
9
+ SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
10
+
11
+ rm -rf "$HOME/.overarch-dist"
12
+ cp -R "$SCRIPT_DIR/dist" "$HOME/.overarch-dist"
13
+
14
+ cp "$SCRIPT_DIR/overarch" "$INSTALL_DIR/overarch"
15
+
16
+ chmod +x "$INSTALL_DIR/overarch"
17
+
18
+ echo "✓ Overarch CLI v0.8.0 installed to:"
19
+ echo " $INSTALL_DIR/overarch"
20
+
21
+ case ":$PATH:" in
22
+ *":$INSTALL_DIR:"*)
23
+ ;;
24
+ *)
25
+ echo
26
+ echo "Add this directory to your PATH:"
27
+ echo " export PATH=\"\$PATH:$INSTALL_DIR\""
28
+ ;;
29
+ esac
package/overarch ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env sh
2
+ # Overarch CLI v0.8.0
3
+ # Linux (ARM64 / aarch64)
4
+
5
+ set -e
6
+
7
+ SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
8
+
9
+ if ! command -v node >/dev/null 2>&1; then
10
+ echo "Error: Node.js >= 20 is required to run Overarch."
11
+ echo "Install Node.js from https://nodejs.org or your package manager."
12
+ exit 1
13
+ fi
14
+
15
+ exec node "$SCRIPT_DIR/dist/bin/overarch.js" "$@"
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "overarch-linux-arm64",
3
+ "version": "0.8.0",
4
+ "description": "Overarch CLI platform package for Linux (ARM64 / aarch64)",
5
+ "os": [
6
+ "linux"
7
+ ],
8
+ "cpu": [
9
+ "arm64"
10
+ ],
11
+ "bin": "./overarch",
12
+ "files": [
13
+ "overarch",
14
+ "install.sh",
15
+ "dist",
16
+ "README.md",
17
+ "LICENSE"
18
+ ],
19
+ "license": "MIT",
20
+ "author": "Yatharth Roy (The Thursday AI Company of New Delhi)",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/yritb/overarch.git"
24
+ },
25
+ "engines": {
26
+ "node": ">=20.0.0"
27
+ }
28
+ }