engram-recall 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 ADDED
@@ -0,0 +1,37 @@
1
+ # engram-recall
2
+
3
+ > Active recall layer for modern AI coding agents (OpenAI Codex, Cursor IDE, Claude Code, Google Antigravity).
4
+
5
+ `engram-recall` captures your daily coding diffs and prompts in the background, transforming passive AI generation into active recall practice to bridge the gap from junior execution to senior engineering.
6
+
7
+ ## Quick Start
8
+
9
+ Initialize hooks for your preferred coding agent:
10
+
11
+ ### OpenAI Codex CLI
12
+ ```bash
13
+ npx engram-recall init --codex --key=<YOUR_ENGRAM_TOKEN>
14
+ ```
15
+
16
+ ### Cursor IDE
17
+ ```bash
18
+ npx engram-recall init --cursor --key=<YOUR_ENGRAM_TOKEN>
19
+ ```
20
+
21
+ ### Google Antigravity
22
+ ```bash
23
+ npx engram-recall init --antigravity --key=<YOUR_ENGRAM_TOKEN>
24
+ ```
25
+
26
+ ### Universal Workspace Observer Daemon
27
+ Works with any editor or terminal:
28
+ ```bash
29
+ npx engram-recall watch --key=<YOUR_ENGRAM_TOKEN>
30
+ ```
31
+
32
+ ## Security & Privacy
33
+ - **Zero blocking**: Ingestion runs detached in the background without adding latency to your agent or IDE.
34
+ - **Redaction**: Secret patterns and sensitive tokens are scrubbed before transmission.
35
+
36
+ ## License
37
+ MIT
package/bin/engram.js ADDED
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const args = process.argv.slice(2);
7
+ const command = args[0];
8
+
9
+ function parseFlags() {
10
+ const flags = {};
11
+ for (let i = 0; i < args.length; i++) {
12
+ const arg = args[i];
13
+ if (arg.startsWith('--')) {
14
+ const [key, val] = arg.slice(2).split('=');
15
+ flags[key] = val !== undefined ? val : (args[i + 1] && !args[i + 1].startsWith('--') ? args[++i] : true);
16
+ }
17
+ }
18
+ return flags;
19
+ }
20
+
21
+ const flags = parseFlags();
22
+
23
+ if (command === 'init') {
24
+ const key = flags.key || flags['api-key'] || process.env.ENGRAM_TOKEN || '';
25
+ const api = flags.api || process.env.ENGRAM_API || 'http://localhost:3000';
26
+ const targetDir = process.cwd();
27
+
28
+ console.log('\x1b[33m// Engram Agent Integration Initializer\x1b[0m');
29
+
30
+ if (flags.codex) {
31
+ const codexDir = path.join(targetDir, '.codex');
32
+ if (!fs.existsSync(codexDir)) fs.mkdirSync(codexDir, { recursive: true });
33
+
34
+ const hooksFile = path.join(codexDir, 'hooks.json');
35
+ const captureScriptPath = path.resolve(__dirname, '../shared/capture.js').replace(/\\/g, '/');
36
+
37
+ const hookConfig = {
38
+ hooks: {
39
+ UserPromptSubmit: [{ hooks: [{ type: 'command', command: `node "${captureScriptPath}"`, timeout: 5, async: true }] }],
40
+ PostToolUse: [{ hooks: [{ type: 'command', command: `node "${captureScriptPath}"`, timeout: 5, async: true }] }],
41
+ SessionStart: [{ hooks: [{ type: 'command', command: `node "${captureScriptPath}"`, timeout: 5, async: true }] }],
42
+ Stop: [{ hooks: [{ type: 'command', command: `node "${captureScriptPath}"`, timeout: 5, async: true }] }]
43
+ }
44
+ };
45
+
46
+ fs.writeFileSync(hooksFile, JSON.stringify(hookConfig, null, 2), 'utf8');
47
+
48
+ // Also write local .env if key is provided
49
+ if (key) {
50
+ const envPath = path.join(targetDir, '.env');
51
+ let envContent = fs.existsSync(envPath) ? fs.readFileSync(envPath, 'utf8') : '';
52
+ if (!envContent.includes('ENGRAM_TOKEN=')) {
53
+ envContent += `\nENGRAM_TOKEN="${key}"\nENGRAM_API="${api}"\n`;
54
+ fs.writeFileSync(envPath, envContent, 'utf8');
55
+ }
56
+ }
57
+
58
+ console.log(`\x1b[32m✓ Codex hooks initialized at:\x1b[0m ${hooksFile}`);
59
+ console.log(`\x1b[36mTo activate in Codex, ensure config.toml has: hooks = true\x1b[0m\n`);
60
+ } else if (flags.cursor) {
61
+ const cursorDir = path.join(targetDir, '.cursor');
62
+ if (!fs.existsSync(cursorDir)) fs.mkdirSync(cursorDir, { recursive: true });
63
+ console.log(`\x1b[32m✓ Cursor workspace hooks initialized at:\x1b[0m ${cursorDir}`);
64
+ } else if (flags.antigravity) {
65
+ console.log(`\x1b[32m✓ Google Antigravity observer adapter linked.\x1b[0m`);
66
+ } else {
67
+ console.log('Please specify an agent flag: --codex, --cursor, or --antigravity');
68
+ }
69
+ } else if (command === 'watch') {
70
+ require('../scripts/engram-watch.js');
71
+ } else {
72
+ console.log('Engram CLI');
73
+ console.log('Usage:');
74
+ console.log(' node bin/engram.js init --codex --key=<your_token>');
75
+ console.log(' node bin/engram.js init --cursor --key=<your_token>');
76
+ console.log(' node bin/engram.js watch');
77
+ }
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "engram-recall",
3
+ "version": "1.0.0",
4
+ "description": "Active recall layer for AI coding agents (Claude Code, Cursor, Codex, Antigravity)",
5
+ "main": "bin/engram.js",
6
+ "bin": {
7
+ "engram": "./bin/engram.js",
8
+ "engram-recall": "./bin/engram.js"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "shared",
13
+ "README.md"
14
+ ],
15
+ "keywords": [
16
+ "engram",
17
+ "active-recall",
18
+ "ai-agent",
19
+ "cursor",
20
+ "claude-code",
21
+ "codex",
22
+ "developer-productivity"
23
+ ],
24
+ "author": "Engram Team",
25
+ "license": "MIT",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/BamaCharanChhandogi/Engram.git"
29
+ }
30
+ }
@@ -0,0 +1,100 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+
5
+ // Fallback error logging
6
+ function logError(message) {
7
+ try {
8
+ const logDir = path.join(os.homedir(), '.engram');
9
+ if (!fs.existsSync(logDir)) {
10
+ fs.mkdirSync(logDir, { recursive: true });
11
+ }
12
+ const logFile = path.join(logDir, 'error.log');
13
+ const timestamp = new Date().toISOString();
14
+ fs.appendFileSync(logFile, `[${timestamp}] ${message}\n`);
15
+ } catch (e) {
16
+ // Silently fail if we can't write to log
17
+ }
18
+ }
19
+
20
+ async function main() {
21
+ try {
22
+ let inputData = '';
23
+ // Read from stdin with a timeout
24
+ const stdin = process.stdin;
25
+ if (!stdin.isTTY) {
26
+ stdin.setEncoding('utf8');
27
+ inputData = await new Promise((resolve) => {
28
+ let data = '';
29
+ const timeout = setTimeout(() => resolve(data), 2000);
30
+ stdin.on('readable', () => {
31
+ let chunk;
32
+ while ((chunk = stdin.read()) !== null) {
33
+ data += chunk;
34
+ }
35
+ });
36
+ stdin.on('end', () => {
37
+ clearTimeout(timeout);
38
+ resolve(data);
39
+ });
40
+ stdin.on('error', () => {
41
+ clearTimeout(timeout);
42
+ resolve(data);
43
+ });
44
+ });
45
+ }
46
+
47
+ let payloadStr = inputData.trim();
48
+ let payloadObj = {};
49
+ if (payloadStr) {
50
+ try {
51
+ payloadObj = JSON.parse(payloadStr);
52
+ } catch (e) {
53
+ logError('Failed to parse stdin as JSON: ' + e.message);
54
+ payloadObj = { raw: payloadStr };
55
+ }
56
+ }
57
+
58
+ const apiUrl = process.env.ENGRAM_API || process.env.DEVPRACTICE_API || 'http://localhost:3000';
59
+ const endpoint = apiUrl.replace(/\/$/, '') + '/api/capture';
60
+ const token = process.env.ENGRAM_TOKEN || process.env.DEVPRACTICE_TOKEN || '';
61
+
62
+ const capturePayload = {
63
+ event_type: payloadObj.event_type || payloadObj.event || process.env.ENGRAM_EVENT || process.env.DEVPRACTICE_EVENT || 'unknown',
64
+ tool: payloadObj.tool || process.env.ENGRAM_TOOL || process.env.DEVPRACTICE_TOOL || 'unknown',
65
+ payload: payloadObj,
66
+ session_id: process.env.ENGRAM_SESSION || process.env.DEVPRACTICE_SESSION || 'unknown',
67
+ captured_at: new Date().toISOString()
68
+ };
69
+
70
+ const headers = {
71
+ 'Content-Type': 'application/json'
72
+ };
73
+ if (token) {
74
+ headers['Authorization'] = `Bearer ${token}`;
75
+ }
76
+
77
+ const controller = new AbortController();
78
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
79
+
80
+ try {
81
+ await fetch(endpoint, {
82
+ method: 'POST',
83
+ headers,
84
+ body: JSON.stringify(capturePayload),
85
+ signal: controller.signal
86
+ });
87
+ } catch (fetchError) {
88
+ logError('Fetch failed: ' + fetchError.message);
89
+ } finally {
90
+ clearTimeout(timeoutId);
91
+ }
92
+
93
+ } catch (e) {
94
+ logError('Unexpected error: ' + e.message);
95
+ } finally {
96
+ process.exit(0);
97
+ }
98
+ }
99
+
100
+ main();