dev-recall 0.2.1__tar.gz → 0.2.3__tar.gz
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.
- {dev_recall-0.2.1 → dev_recall-0.2.3}/PKG-INFO +3 -2
- {dev_recall-0.2.1 → dev_recall-0.2.3}/README.md +2 -1
- {dev_recall-0.2.1 → dev_recall-0.2.3}/dev_recall.egg-info/PKG-INFO +3 -2
- {dev_recall-0.2.1 → dev_recall-0.2.3}/dev_recall.egg-info/SOURCES.txt +3 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/pyproject.toml +8 -1
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/cli.py +97 -2
- dev_recall-0.2.3/recall/vscode-extension/package.json +42 -0
- dev_recall-0.2.3/recall/vscode-extension/src/extension.ts +238 -0
- dev_recall-0.2.3/recall/vscode-extension/tsconfig.json +14 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/dev_recall.egg-info/dependency_links.txt +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/dev_recall.egg-info/entry_points.txt +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/dev_recall.egg-info/requires.txt +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/dev_recall.egg-info/top_level.txt +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/__init__.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/_hooks.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/collectors/__init__.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/collectors/ai_chat.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/collectors/containers.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/collectors/git.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/collectors/linux_process.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/collectors/linux_session.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/collectors/linux_window.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/collectors/shell.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/collectors/vscode.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/config.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/daemon.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/daemon_main.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/mcp_server.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/models.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/processor/__init__.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/processor/embedder.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/processor/enricher.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/processor/session.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/query/__init__.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/query/context.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/query/llm.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/query/retriever.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/query/timeparser.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/storage/__init__.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/storage/db.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/recall/storage/vectors.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/setup.cfg +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/tests/test_collectors.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/tests/test_enricher.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/tests/test_query.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/tests/test_session.py +0 -0
- {dev_recall-0.2.1 → dev_recall-0.2.3}/tests/test_storage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dev-recall
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Local-first developer memory layer
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: developer-tools,memory,productivity,cli
|
|
@@ -76,6 +76,7 @@ source .zshrc # or .bashrc
|
|
|
76
76
|
| `recall privacy ignore --cmd "pattern"` | Add a privacy filter |
|
|
77
77
|
| `recall daemon start/stop/status/logs` | Manage the background daemon |
|
|
78
78
|
| `recall mcp-serve` | Start MCP server (for Claude Code / Copilot) |
|
|
79
|
+
| `recall vscode install` | Build and install VS Code extension |
|
|
79
80
|
|
|
80
81
|
---
|
|
81
82
|
|
|
@@ -109,7 +110,7 @@ source ~/.config/dev-recall/hook.zsh
|
|
|
109
110
|
### VS Code activity
|
|
110
111
|
Install the extension:
|
|
111
112
|
```bash
|
|
112
|
-
|
|
113
|
+
recall vscode install
|
|
113
114
|
```
|
|
114
115
|
|
|
115
116
|
### AI chat sessions
|
|
@@ -47,6 +47,7 @@ source .zshrc # or .bashrc
|
|
|
47
47
|
| `recall privacy ignore --cmd "pattern"` | Add a privacy filter |
|
|
48
48
|
| `recall daemon start/stop/status/logs` | Manage the background daemon |
|
|
49
49
|
| `recall mcp-serve` | Start MCP server (for Claude Code / Copilot) |
|
|
50
|
+
| `recall vscode install` | Build and install VS Code extension |
|
|
50
51
|
|
|
51
52
|
---
|
|
52
53
|
|
|
@@ -80,7 +81,7 @@ source ~/.config/dev-recall/hook.zsh
|
|
|
80
81
|
### VS Code activity
|
|
81
82
|
Install the extension:
|
|
82
83
|
```bash
|
|
83
|
-
|
|
84
|
+
recall vscode install
|
|
84
85
|
```
|
|
85
86
|
|
|
86
87
|
### AI chat sessions
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dev-recall
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Local-first developer memory layer
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: developer-tools,memory,productivity,cli
|
|
@@ -76,6 +76,7 @@ source .zshrc # or .bashrc
|
|
|
76
76
|
| `recall privacy ignore --cmd "pattern"` | Add a privacy filter |
|
|
77
77
|
| `recall daemon start/stop/status/logs` | Manage the background daemon |
|
|
78
78
|
| `recall mcp-serve` | Start MCP server (for Claude Code / Copilot) |
|
|
79
|
+
| `recall vscode install` | Build and install VS Code extension |
|
|
79
80
|
|
|
80
81
|
---
|
|
81
82
|
|
|
@@ -109,7 +110,7 @@ source ~/.config/dev-recall/hook.zsh
|
|
|
109
110
|
### VS Code activity
|
|
110
111
|
Install the extension:
|
|
111
112
|
```bash
|
|
112
|
-
|
|
113
|
+
recall vscode install
|
|
113
114
|
```
|
|
114
115
|
|
|
115
116
|
### AI chat sessions
|
|
@@ -35,6 +35,9 @@ recall/query/timeparser.py
|
|
|
35
35
|
recall/storage/__init__.py
|
|
36
36
|
recall/storage/db.py
|
|
37
37
|
recall/storage/vectors.py
|
|
38
|
+
recall/vscode-extension/package.json
|
|
39
|
+
recall/vscode-extension/tsconfig.json
|
|
40
|
+
recall/vscode-extension/src/extension.ts
|
|
38
41
|
tests/test_collectors.py
|
|
39
42
|
tests/test_enricher.py
|
|
40
43
|
tests/test_query.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dev-recall"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.3"
|
|
8
8
|
description = "Local-first developer memory layer"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -42,6 +42,13 @@ linux = [
|
|
|
42
42
|
where = ["."]
|
|
43
43
|
include = ["recall*"]
|
|
44
44
|
|
|
45
|
+
[tool.setuptools.package-data]
|
|
46
|
+
recall = ["vscode-extension/**"]
|
|
47
|
+
|
|
48
|
+
# ensures non-.py files are included
|
|
49
|
+
[tool.setuptools]
|
|
50
|
+
include-package-data = true
|
|
51
|
+
|
|
45
52
|
[tool.ruff]
|
|
46
53
|
line-length = 100
|
|
47
54
|
target-version = "py310"
|
|
@@ -12,6 +12,7 @@ import time
|
|
|
12
12
|
from datetime import datetime, timezone
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
from typing import Optional
|
|
15
|
+
import importlib.resources as pkg_resources
|
|
15
16
|
|
|
16
17
|
import click
|
|
17
18
|
from rich.console import Console
|
|
@@ -66,7 +67,7 @@ _TYPE_STYLES = {
|
|
|
66
67
|
|
|
67
68
|
|
|
68
69
|
@click.group()
|
|
69
|
-
@click.version_option()
|
|
70
|
+
@click.version_option(package_name="dev-recall")
|
|
70
71
|
def cli():
|
|
71
72
|
"""Recall — local-first developer memory layer."""
|
|
72
73
|
pass
|
|
@@ -126,7 +127,12 @@ def init(yes: bool):
|
|
|
126
127
|
|
|
127
128
|
# Step 6: VS Code extension hint
|
|
128
129
|
_print_step(6, steps[5])
|
|
129
|
-
|
|
130
|
+
if yes or click.confirm(" → Would you like to build and install the VS Code extension now?", default=False):
|
|
131
|
+
# We can call the function directly or via a new command
|
|
132
|
+
ctx = click.get_current_context()
|
|
133
|
+
ctx.invoke(vscode_install)
|
|
134
|
+
else:
|
|
135
|
+
console.print(" → Skip. Run manually later: [cyan]recall vscode install[/cyan]")
|
|
130
136
|
|
|
131
137
|
console.print()
|
|
132
138
|
console.print("[bold green]Done. Recall is running.[/bold green]")
|
|
@@ -985,6 +991,95 @@ def mcp_serve():
|
|
|
985
991
|
run_mcp_server()
|
|
986
992
|
|
|
987
993
|
|
|
994
|
+
# ---------------------------------------------------------------------------
|
|
995
|
+
# vscode
|
|
996
|
+
# ---------------------------------------------------------------------------
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
@cli.group()
|
|
1000
|
+
def vscode():
|
|
1001
|
+
"""Manage the Recall VS Code extension."""
|
|
1002
|
+
pass
|
|
1003
|
+
|
|
1004
|
+
|
|
1005
|
+
@vscode.command("install")
|
|
1006
|
+
def vscode_install():
|
|
1007
|
+
"""Build and install the Recall VS Code extension."""
|
|
1008
|
+
import shutil
|
|
1009
|
+
|
|
1010
|
+
ext_dir = pkg_resources.files("recall") / "vscode-extension" # vsce produces name-version.vsix
|
|
1011
|
+
vsix_pattern = "recall-vscode-*.vsix"
|
|
1012
|
+
|
|
1013
|
+
# Check for npm and code
|
|
1014
|
+
if not shutil.which("npm"):
|
|
1015
|
+
console.print("[red]npm not found. Install Node.js to build the VS Code extension.[/red]")
|
|
1016
|
+
return
|
|
1017
|
+
if not shutil.which("code"):
|
|
1018
|
+
console.print("[red]'code' command not found. Make sure VS Code is in your PATH.[/red]")
|
|
1019
|
+
return
|
|
1020
|
+
|
|
1021
|
+
# Install dependencies and build
|
|
1022
|
+
console.print("[dim]Installing extension dependencies...[/dim]")
|
|
1023
|
+
result = subprocess.run(
|
|
1024
|
+
["npm", "install"],
|
|
1025
|
+
cwd=ext_dir,
|
|
1026
|
+
capture_output=True,
|
|
1027
|
+
text=True,
|
|
1028
|
+
)
|
|
1029
|
+
if result.returncode != 0:
|
|
1030
|
+
console.print(f"[red]npm install failed:[/red] {result.stderr}")
|
|
1031
|
+
return
|
|
1032
|
+
|
|
1033
|
+
console.print("[dim]Compiling TypeScript...[/dim]")
|
|
1034
|
+
result = subprocess.run(
|
|
1035
|
+
["npm", "run", "compile"],
|
|
1036
|
+
cwd=ext_dir,
|
|
1037
|
+
capture_output=True,
|
|
1038
|
+
text=True,
|
|
1039
|
+
)
|
|
1040
|
+
if result.returncode != 0:
|
|
1041
|
+
console.print(f"[red]TypeScript compilation failed:[/red] {result.stderr}")
|
|
1042
|
+
return
|
|
1043
|
+
|
|
1044
|
+
console.print("[dim]Packaging extension...[/dim]")
|
|
1045
|
+
# Remove old vsix files first to avoid ambiguity
|
|
1046
|
+
for old_vsix in ext_dir.glob("*.vsix"):
|
|
1047
|
+
old_vsix.unlink()
|
|
1048
|
+
|
|
1049
|
+
result = subprocess.run(
|
|
1050
|
+
["npx", "@vscode/vsce", "package"],
|
|
1051
|
+
cwd=ext_dir,
|
|
1052
|
+
capture_output=True,
|
|
1053
|
+
text=True,
|
|
1054
|
+
)
|
|
1055
|
+
if result.returncode != 0:
|
|
1056
|
+
console.print(f"[red]vsce package failed:[/red] {result.stderr}")
|
|
1057
|
+
return
|
|
1058
|
+
|
|
1059
|
+
# Find the generated .vsix file
|
|
1060
|
+
vsix_files = list(ext_dir.glob(vsix_pattern))
|
|
1061
|
+
if not vsix_files:
|
|
1062
|
+
console.print("[red]No .vsix file generated (looked for recall-vscode-*.vsix).[/red]")
|
|
1063
|
+
return
|
|
1064
|
+
|
|
1065
|
+
vsix_path = vsix_files[0]
|
|
1066
|
+
console.print(f" → [green]✓[/green] Generated {vsix_path.name}")
|
|
1067
|
+
|
|
1068
|
+
# Install the extension
|
|
1069
|
+
console.print(f"[dim]Installing extension via 'code --install-extension'...[/dim]")
|
|
1070
|
+
result = subprocess.run(
|
|
1071
|
+
["code", "--install-extension", str(vsix_path)],
|
|
1072
|
+
capture_output=True,
|
|
1073
|
+
text=True,
|
|
1074
|
+
)
|
|
1075
|
+
if result.returncode != 0:
|
|
1076
|
+
console.print(f"[red]Extension installation failed:[/red] {result.stderr}")
|
|
1077
|
+
return
|
|
1078
|
+
|
|
1079
|
+
console.print("[green]✓ VS Code extension installed successfully.[/green]")
|
|
1080
|
+
console.print(" → Restart VS Code (or run 'Developer: Reload Window') to activate.")
|
|
1081
|
+
|
|
1082
|
+
|
|
988
1083
|
# ---------------------------------------------------------------------------
|
|
989
1084
|
# Helpers
|
|
990
1085
|
# ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "recall-vscode",
|
|
3
|
+
"displayName": "Recall",
|
|
4
|
+
"description": "Developer memory layer — captures workspace activity for dev-recall",
|
|
5
|
+
"version": "0.1.0",
|
|
6
|
+
"publisher": "dev-recall",
|
|
7
|
+
"engines": {
|
|
8
|
+
"vscode": "^1.85.0"
|
|
9
|
+
},
|
|
10
|
+
"categories": ["Other"],
|
|
11
|
+
"activationEvents": ["onStartupFinished"],
|
|
12
|
+
"main": "./out/extension.js",
|
|
13
|
+
"contributes": {
|
|
14
|
+
"configuration": {
|
|
15
|
+
"title": "Recall",
|
|
16
|
+
"properties": {
|
|
17
|
+
"recall.port": {
|
|
18
|
+
"type": "number",
|
|
19
|
+
"default": 27182,
|
|
20
|
+
"description": "Port for the Recall daemon HTTP server"
|
|
21
|
+
},
|
|
22
|
+
"recall.enabled": {
|
|
23
|
+
"type": "boolean",
|
|
24
|
+
"default": true,
|
|
25
|
+
"description": "Enable/disable the Recall VS Code extension"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"vscode:prepublish": "npm run compile",
|
|
32
|
+
"compile": "tsc -p ./",
|
|
33
|
+
"watch": "tsc -watch -p ./",
|
|
34
|
+
"package": "npm run compile && vsce package"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/vscode": "^1.85.0",
|
|
38
|
+
"@types/node": "^20.0.0",
|
|
39
|
+
"typescript": "^5.0.0",
|
|
40
|
+
"@vscode/vsce": "^3.25.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import * as vscode from 'vscode';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Configuration
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
function getPort(): number {
|
|
10
|
+
// Try reading from config file first, then VS Code setting
|
|
11
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
12
|
+
// Cross-platform config path
|
|
13
|
+
let cfgFile: string;
|
|
14
|
+
if (process.platform === 'darwin') {
|
|
15
|
+
cfgFile = path.join(home, 'Library', 'Application Support', 'dev-recall', 'config.json');
|
|
16
|
+
} else if (process.platform === 'win32') {
|
|
17
|
+
cfgFile = path.join(process.env.APPDATA || '', 'dev-recall', 'config.json');
|
|
18
|
+
} else {
|
|
19
|
+
cfgFile = path.join(home, '.config', 'dev-recall', 'config.json');
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const raw = fs.readFileSync(cfgFile, 'utf-8');
|
|
23
|
+
const cfg = JSON.parse(raw);
|
|
24
|
+
if (cfg.daemon_port) { return cfg.daemon_port; }
|
|
25
|
+
} catch { /* ignore */ }
|
|
26
|
+
|
|
27
|
+
const vsCfg = vscode.workspace.getConfiguration('recall');
|
|
28
|
+
return vsCfg.get<number>('port', 27182);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isEnabled(): boolean {
|
|
32
|
+
return vscode.workspace.getConfiguration('recall').get<boolean>('enabled', true);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
// HTTP fire-and-forget
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
async function sendEvent(type: string, data: Record<string, unknown>): Promise<void> {
|
|
40
|
+
if (!isEnabled()) { return; }
|
|
41
|
+
const port = getPort();
|
|
42
|
+
const body = JSON.stringify({ type, ts: new Date().toISOString(), ...data });
|
|
43
|
+
try {
|
|
44
|
+
await fetch(`http://127.0.0.1:${port}/event`, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: { 'Content-Type': 'application/json' },
|
|
47
|
+
body,
|
|
48
|
+
signal: AbortSignal.timeout(500),
|
|
49
|
+
});
|
|
50
|
+
} catch {
|
|
51
|
+
// Daemon may not be running — silently ignore
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Active time tracking
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
let lastActivityAt: number = Date.now();
|
|
60
|
+
let activeTimeInterval: ReturnType<typeof setInterval> | undefined;
|
|
61
|
+
|
|
62
|
+
function recordActivity(): void {
|
|
63
|
+
lastActivityAt = Date.now();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Extension activation
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
export function activate(context: vscode.ExtensionContext): void {
|
|
71
|
+
// Report workspace open for each workspace folder
|
|
72
|
+
const folders = vscode.workspace.workspaceFolders;
|
|
73
|
+
if (folders) {
|
|
74
|
+
for (const folder of folders) {
|
|
75
|
+
sendEvent('workspace_open', { workspace: folder.uri.fsPath });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Workspace folders changed
|
|
80
|
+
context.subscriptions.push(
|
|
81
|
+
vscode.workspace.onDidChangeWorkspaceFolders(e => {
|
|
82
|
+
for (const added of e.added) {
|
|
83
|
+
sendEvent('workspace_open', { workspace: added.uri.fsPath });
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// File save
|
|
89
|
+
context.subscriptions.push(
|
|
90
|
+
vscode.workspace.onDidSaveTextDocument(doc => {
|
|
91
|
+
recordActivity();
|
|
92
|
+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(doc.uri);
|
|
93
|
+
sendEvent('file_save', {
|
|
94
|
+
file: doc.uri.fsPath,
|
|
95
|
+
language: doc.languageId,
|
|
96
|
+
workspace: workspaceFolder?.uri.fsPath ?? '',
|
|
97
|
+
});
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Track activity for active_time reporting
|
|
102
|
+
context.subscriptions.push(
|
|
103
|
+
vscode.window.onDidChangeActiveTextEditor(recordActivity),
|
|
104
|
+
vscode.workspace.onDidChangeTextDocument(recordActivity),
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
// File lifecycle events
|
|
108
|
+
context.subscriptions.push(
|
|
109
|
+
vscode.workspace.onDidCreateFiles(e => {
|
|
110
|
+
recordActivity();
|
|
111
|
+
for (const uri of e.files) {
|
|
112
|
+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
|
|
113
|
+
sendEvent('file_create', {
|
|
114
|
+
file: uri.fsPath,
|
|
115
|
+
filename: uri.fsPath.split('/').pop() ?? '',
|
|
116
|
+
workspace: workspaceFolder?.uri.fsPath ?? '',
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}),
|
|
120
|
+
vscode.workspace.onDidDeleteFiles(e => {
|
|
121
|
+
recordActivity();
|
|
122
|
+
for (const uri of e.files) {
|
|
123
|
+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
|
|
124
|
+
sendEvent('file_delete', {
|
|
125
|
+
file: uri.fsPath,
|
|
126
|
+
filename: uri.fsPath.split('/').pop() ?? '',
|
|
127
|
+
workspace: workspaceFolder?.uri.fsPath ?? '',
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}),
|
|
131
|
+
vscode.workspace.onDidRenameFiles(e => {
|
|
132
|
+
recordActivity();
|
|
133
|
+
for (const { oldUri, newUri } of e.files) {
|
|
134
|
+
const workspaceFolder = vscode.workspace.getWorkspaceFolder(newUri);
|
|
135
|
+
sendEvent('file_rename', {
|
|
136
|
+
old_file: oldUri.fsPath,
|
|
137
|
+
new_file: newUri.fsPath,
|
|
138
|
+
old_filename: oldUri.fsPath.split('/').pop() ?? '',
|
|
139
|
+
new_filename: newUri.fsPath.split('/').pop() ?? '',
|
|
140
|
+
workspace: workspaceFolder?.uri.fsPath ?? '',
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}),
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Debug session events
|
|
147
|
+
context.subscriptions.push(
|
|
148
|
+
vscode.debug.onDidStartDebugSession(session => {
|
|
149
|
+
const workspace = session.workspaceFolder?.uri.fsPath ?? '';
|
|
150
|
+
sendEvent('debug_session_start', {
|
|
151
|
+
name: session.name,
|
|
152
|
+
debug_type: session.type,
|
|
153
|
+
workspace,
|
|
154
|
+
});
|
|
155
|
+
}),
|
|
156
|
+
vscode.debug.onDidTerminateDebugSession(session => {
|
|
157
|
+
const workspace = session.workspaceFolder?.uri.fsPath ?? '';
|
|
158
|
+
sendEvent('debug_session_end', {
|
|
159
|
+
name: session.name,
|
|
160
|
+
debug_type: session.type,
|
|
161
|
+
workspace,
|
|
162
|
+
});
|
|
163
|
+
}),
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
// Test task events (tasks in the Test group)
|
|
167
|
+
context.subscriptions.push(
|
|
168
|
+
vscode.tasks.onDidStartTask(e => {
|
|
169
|
+
if (e.execution.task.group === vscode.TaskGroup.Test) {
|
|
170
|
+
const scope = e.execution.task.scope;
|
|
171
|
+
const workspace = (scope && typeof scope !== 'number')
|
|
172
|
+
? (scope as vscode.WorkspaceFolder).uri.fsPath : '';
|
|
173
|
+
sendEvent('test_run_start', {
|
|
174
|
+
name: e.execution.task.name,
|
|
175
|
+
workspace,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}),
|
|
179
|
+
vscode.tasks.onDidEndTaskProcess(e => {
|
|
180
|
+
if (e.execution.task.group === vscode.TaskGroup.Test) {
|
|
181
|
+
const scope = e.execution.task.scope;
|
|
182
|
+
const workspace = (scope && typeof scope !== 'number')
|
|
183
|
+
? (scope as vscode.WorkspaceFolder).uri.fsPath : '';
|
|
184
|
+
sendEvent('test_run_finish', {
|
|
185
|
+
name: e.execution.task.name,
|
|
186
|
+
exit_code: e.exitCode ?? 0,
|
|
187
|
+
workspace,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}),
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// Report active time every 5 minutes
|
|
194
|
+
activeTimeInterval = setInterval(() => {
|
|
195
|
+
const secondsSinceActivity = (Date.now() - lastActivityAt) / 1000;
|
|
196
|
+
// Only report if user was active in the last 5 minutes
|
|
197
|
+
if (secondsSinceActivity < 300) {
|
|
198
|
+
const workspace = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? '';
|
|
199
|
+
sendEvent('active_time', {
|
|
200
|
+
workspace,
|
|
201
|
+
seconds_active: 300 - secondsSinceActivity,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}, 5 * 60 * 1000);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
// Extension deactivation
|
|
209
|
+
// ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
export function deactivate(): void {
|
|
212
|
+
if (activeTimeInterval) {
|
|
213
|
+
clearInterval(activeTimeInterval);
|
|
214
|
+
}
|
|
215
|
+
const folders = vscode.workspace.workspaceFolders;
|
|
216
|
+
if (folders) {
|
|
217
|
+
for (const folder of folders) {
|
|
218
|
+
// Fire-and-forget — deactivation is synchronous, best effort
|
|
219
|
+
const port = getPort();
|
|
220
|
+
const body = JSON.stringify({
|
|
221
|
+
type: 'workspace_close',
|
|
222
|
+
ts: new Date().toISOString(),
|
|
223
|
+
workspace: folder.uri.fsPath,
|
|
224
|
+
});
|
|
225
|
+
try {
|
|
226
|
+
// Synchronous best-effort using Node.js http module
|
|
227
|
+
const http = require('http');
|
|
228
|
+
const req = http.request({
|
|
229
|
+
hostname: '127.0.0.1', port, path: '/event', method: 'POST',
|
|
230
|
+
headers: { 'Content-Type': 'application/json' },
|
|
231
|
+
timeout: 200,
|
|
232
|
+
});
|
|
233
|
+
req.write(body);
|
|
234
|
+
req.end();
|
|
235
|
+
} catch { /* ignore */ }
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "commonjs",
|
|
4
|
+
"target": "ES2020",
|
|
5
|
+
"outDir": "./out",
|
|
6
|
+
"lib": ["ES2020"],
|
|
7
|
+
"sourceMap": true,
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true
|
|
12
|
+
},
|
|
13
|
+
"exclude": ["node_modules", ".vscode-test"]
|
|
14
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|