@damador/agentsync 1.0.1
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/LICENSE +21 -0
- package/README.md +121 -0
- package/dist/gitSync.d.ts +8 -0
- package/dist/gitSync.js +101 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +201 -0
- package/dist/provider.d.ts +21 -0
- package/dist/provider.js +2 -0
- package/dist/providers/aider.d.ts +9 -0
- package/dist/providers/aider.js +98 -0
- package/dist/providers/claudeCode.d.ts +8 -0
- package/dist/providers/claudeCode.js +90 -0
- package/dist/providers/codex.d.ts +8 -0
- package/dist/providers/codex.js +83 -0
- package/dist/providers/copilot.d.ts +8 -0
- package/dist/providers/copilot.js +110 -0
- package/dist/providers/cursor.d.ts +9 -0
- package/dist/providers/cursor.js +97 -0
- package/dist/providers/gemini.d.ts +8 -0
- package/dist/providers/gemini.js +82 -0
- package/dist/vault.d.ts +17 -0
- package/dist/vault.js +85 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Krzysztof Radzikowski
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# AgentSync 🔄
|
|
2
|
+
|
|
3
|
+
**AgentSync** is a lightweight, cross-machine synchronization tool designed to persistently migrate and synchronize AI agent session data—including conversation logs, history, and workspace context—across different development machines.
|
|
4
|
+
|
|
5
|
+
Keep your AI pairing assistants (like Claude Code, Cursor, Aider, Copilot, Gemini, and Codex) perfectly in sync between your workstation, laptop, and remote environments.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **Multi-Agent Support**: Out-of-the-box support for:
|
|
12
|
+
- 🤖 **Claude Code**
|
|
13
|
+
- 🚀 **Cursor**
|
|
14
|
+
- 💻 **Aider**
|
|
15
|
+
- 👥 **GitHub Copilot**
|
|
16
|
+
- ♊ **Gemini**
|
|
17
|
+
- ⌨️ **Codex CLI**
|
|
18
|
+
- **Git-Backed Vault**: Securely tracks and synchronizes your agent data using a private Git repository as the central storage ("vault").
|
|
19
|
+
- **Seamless Local Persistence**: Exports from local application directories and imports them back smoothly.
|
|
20
|
+
- **Developer-Friendly CLI**: Intuitive commands to keep your workflow synchronized with minimal friction.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
You can run AgentSync directly via `npx` without installing it:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx @damador/agentsync init <your-private-vault-git-repo-url>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Alternatively, you can install it globally to use the `agentsync` command anywhere:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install -g @damador/agentsync
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Usage
|
|
41
|
+
|
|
42
|
+
AgentSync commands can be run directly using `npx` (or `agentsync` if installed globally):
|
|
43
|
+
|
|
44
|
+
### 1. Initialize the Vault
|
|
45
|
+
Connect your AgentSync to a private remote Git repository which will act as your cross-machine vault:
|
|
46
|
+
```bash
|
|
47
|
+
npx @damador/agentsync init <your-private-vault-git-repo-url>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### 2. Configure Active Agents
|
|
51
|
+
By default, the first time you run a sync command, an **interactive setup wizard** will detect the agents installed on your machine and ask you to select which ones to sync.
|
|
52
|
+
|
|
53
|
+
You can also trigger the wizard or configure this manually at any time:
|
|
54
|
+
```bash
|
|
55
|
+
# Run the interactive setup wizard
|
|
56
|
+
npx @damador/agentsync config
|
|
57
|
+
|
|
58
|
+
# Or manually set specific agents
|
|
59
|
+
npx @damador/agentsync config --agents claude,cursor
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
*You'll see a prompt like this:*
|
|
63
|
+
```
|
|
64
|
+
Welcome to AgentSync! Let's configure which agents to synchronize.
|
|
65
|
+
? Select the agents you want to sync »
|
|
66
|
+
Instructions:
|
|
67
|
+
↑/↓: Highlight option
|
|
68
|
+
←/→/[space]: Toggle selection
|
|
69
|
+
a: Toggle all
|
|
70
|
+
enter/return: Complete answer
|
|
71
|
+
( ) claude - Not detected locally
|
|
72
|
+
(*) cursor - Detected locally
|
|
73
|
+
( ) aider - Not detected locally
|
|
74
|
+
(*) copilot - Detected locally
|
|
75
|
+
( ) gemini - Not detected locally
|
|
76
|
+
( ) codex - Not detected locally
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 3. Export & Push Agent Sessions
|
|
80
|
+
To save your current local sessions and push them to your remote vault:
|
|
81
|
+
```bash
|
|
82
|
+
npx @damador/agentsync push
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 4. Pull & Import Agent Sessions
|
|
86
|
+
On another machine, pull the latest data from the remote vault and import it into your local agent directories:
|
|
87
|
+
```bash
|
|
88
|
+
npx @damador/agentsync pull
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### 5. Check Vault Status
|
|
92
|
+
Check the status of your local synchronization vault:
|
|
93
|
+
```bash
|
|
94
|
+
npx @damador/agentsync status
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Testing
|
|
100
|
+
|
|
101
|
+
AgentSync includes a comprehensive test suite powered by **Vitest**. The tests heavily mock filesystem operations (`fs-extra`) and git interactions (`simple-git`) to ensure they run quickly and safely without altering your real local files or Git repositories.
|
|
102
|
+
|
|
103
|
+
To run the test suite:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
npm test
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## How It Works
|
|
112
|
+
|
|
113
|
+
1. **Vault Management**: AgentSync manages a local vault directory that holds exported configuration, database files, and JSONL log files from supported agents.
|
|
114
|
+
2. **Push/Pull Engine**: Uses a Git-backed engine to track file changes and handle version synchronization (commit, pull, push) securely and privately.
|
|
115
|
+
3. **Provider System**: Each agent provider defines how to locate, export, and import its session files safely.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## License
|
|
120
|
+
|
|
121
|
+
MIT License.
|
package/dist/gitSync.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.GitSyncEngine = void 0;
|
|
40
|
+
const simple_git_1 = __importDefault(require("simple-git"));
|
|
41
|
+
const fs = __importStar(require("fs-extra"));
|
|
42
|
+
class GitSyncEngine {
|
|
43
|
+
git;
|
|
44
|
+
repoDir;
|
|
45
|
+
constructor(repoDir) {
|
|
46
|
+
this.repoDir = repoDir;
|
|
47
|
+
this.git = (0, simple_git_1.default)(this.repoDir);
|
|
48
|
+
}
|
|
49
|
+
async init(remoteUrl) {
|
|
50
|
+
await fs.ensureDir(this.repoDir);
|
|
51
|
+
const isRepo = await this.git.checkIsRepo();
|
|
52
|
+
if (!isRepo) {
|
|
53
|
+
await this.git.init();
|
|
54
|
+
await this.git.addRemote('origin', remoteUrl);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async pull() {
|
|
58
|
+
// Stash any local changes before pulling to avoid merge conflicts
|
|
59
|
+
const status = await this.git.status();
|
|
60
|
+
let stashed = false;
|
|
61
|
+
if (status.files.length > 0) {
|
|
62
|
+
await this.git.stash();
|
|
63
|
+
stashed = true;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
await this.git.pull('origin', 'main', { '--rebase': 'true' });
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
// If pull fails (e.g., no remote branch yet), just ignore
|
|
70
|
+
console.log('Notice: Could not pull from remote (might be empty or unreachable).');
|
|
71
|
+
}
|
|
72
|
+
if (stashed) {
|
|
73
|
+
try {
|
|
74
|
+
await this.git.stash(['pop']);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.warn('Warning: Could not pop stash after pull. You may need to resolve conflicts manually.');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async push(message = 'Sync agent data') {
|
|
82
|
+
await this.git.add('./*');
|
|
83
|
+
const status = await this.git.status();
|
|
84
|
+
// Ensure the branch is renamed to main (in case it defaulted to master)
|
|
85
|
+
try {
|
|
86
|
+
await this.git.branch(['-M', 'main']);
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
// Ignore if it fails (e.g., if there are no commits yet)
|
|
90
|
+
}
|
|
91
|
+
if (status.staged.length > 0 || status.created.length > 0 || status.modified.length > 0 || status.deleted.length > 0) {
|
|
92
|
+
await this.git.commit(message);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
console.log('No new changes to commit.');
|
|
96
|
+
}
|
|
97
|
+
// Always attempt to push (there might be unpushed commits even if working tree is clean)
|
|
98
|
+
await this.git.push('origin', 'main', { '--set-upstream': null });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.GitSyncEngine = GitSyncEngine;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
9
|
+
const vault_1 = require("./vault");
|
|
10
|
+
const gitSync_1 = require("./gitSync");
|
|
11
|
+
const claudeCode_1 = require("./providers/claudeCode");
|
|
12
|
+
const cursor_1 = require("./providers/cursor");
|
|
13
|
+
const aider_1 = require("./providers/aider");
|
|
14
|
+
const copilot_1 = require("./providers/copilot");
|
|
15
|
+
const gemini_1 = require("./providers/gemini");
|
|
16
|
+
const codex_1 = require("./providers/codex");
|
|
17
|
+
const program = new commander_1.Command();
|
|
18
|
+
const vaultManager = new vault_1.VaultManager();
|
|
19
|
+
const providers = [
|
|
20
|
+
new claudeCode_1.ClaudeCodeProvider(),
|
|
21
|
+
new cursor_1.CursorProvider(),
|
|
22
|
+
new aider_1.AiderProvider(),
|
|
23
|
+
new copilot_1.CopilotProvider(),
|
|
24
|
+
new gemini_1.GeminiProvider(),
|
|
25
|
+
new codex_1.CodexProvider()
|
|
26
|
+
];
|
|
27
|
+
async function runSetupWizard() {
|
|
28
|
+
console.log('\nWelcome to AgentSync! Let\'s configure which agents to synchronize.');
|
|
29
|
+
// Detect installed agents
|
|
30
|
+
const choices = [];
|
|
31
|
+
for (const provider of providers) {
|
|
32
|
+
const installed = await provider.isInstalled();
|
|
33
|
+
choices.push({
|
|
34
|
+
title: provider.name,
|
|
35
|
+
value: provider.name,
|
|
36
|
+
selected: installed,
|
|
37
|
+
description: installed ? 'Detected locally' : 'Not detected locally'
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
const response = await (0, prompts_1.default)({
|
|
41
|
+
type: 'multiselect',
|
|
42
|
+
name: 'agents',
|
|
43
|
+
message: 'Select the agents you want to sync',
|
|
44
|
+
choices,
|
|
45
|
+
min: 1
|
|
46
|
+
});
|
|
47
|
+
if (response.agents) {
|
|
48
|
+
const config = await vaultManager.getConfig();
|
|
49
|
+
config.agents = response.agents;
|
|
50
|
+
await vaultManager.saveConfig(config);
|
|
51
|
+
console.log(`\nConfiguration saved. Active agents: ${config.agents.join(', ')}\n`);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
console.log('\nSetup cancelled. No agents configured.\n');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
program
|
|
59
|
+
.name('agentsync')
|
|
60
|
+
.description('Synchronize AI agent sessions across machines')
|
|
61
|
+
.version('1.0.0');
|
|
62
|
+
program
|
|
63
|
+
.command('init')
|
|
64
|
+
.description('Initialize the AgentSync vault and connect to a remote Git repository')
|
|
65
|
+
.argument('<repo>', 'Git repository URL')
|
|
66
|
+
.action(async (repo) => {
|
|
67
|
+
try {
|
|
68
|
+
console.log('Initializing AgentSync vault...');
|
|
69
|
+
await vaultManager.initVault();
|
|
70
|
+
const gitSync = new gitSync_1.GitSyncEngine(vaultManager.getVaultDir());
|
|
71
|
+
await gitSync.init(repo);
|
|
72
|
+
console.log('Successfully initialized AgentSync vault connected to', repo);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
console.error('Failed to initialize:', error);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
program
|
|
79
|
+
.command('config')
|
|
80
|
+
.description('Manage AgentSync configuration')
|
|
81
|
+
.option('--agents <agents>', 'Comma-separated list of agents to sync (e.g., claude,cursor)')
|
|
82
|
+
.action(async (options) => {
|
|
83
|
+
try {
|
|
84
|
+
const config = await vaultManager.getConfig();
|
|
85
|
+
if (options.agents) {
|
|
86
|
+
const agents = options.agents.split(',').map((a) => a.trim().toLowerCase());
|
|
87
|
+
config.agents = agents;
|
|
88
|
+
await vaultManager.saveConfig(config);
|
|
89
|
+
console.log(`Config updated. Active agents: ${agents.join(', ')}`);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// Run wizard if no options are passed
|
|
93
|
+
await runSetupWizard();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
console.error('Failed to update config:', e);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
program
|
|
101
|
+
.command('push')
|
|
102
|
+
.description('Export local agent data and push to the remote vault')
|
|
103
|
+
.action(async () => {
|
|
104
|
+
try {
|
|
105
|
+
if (await vaultManager.isFirstRun()) {
|
|
106
|
+
await runSetupWizard();
|
|
107
|
+
}
|
|
108
|
+
console.log('Preparing to push data...');
|
|
109
|
+
await vaultManager.initVault();
|
|
110
|
+
const vaultDir = vaultManager.getVaultDir();
|
|
111
|
+
let activeProviders = providers;
|
|
112
|
+
const config = await vaultManager.getConfig();
|
|
113
|
+
if (config.agents && config.agents.length > 0) {
|
|
114
|
+
const configuredAgents = config.agents;
|
|
115
|
+
activeProviders = providers.filter(p => configuredAgents.includes(p.name.toLowerCase()));
|
|
116
|
+
if (activeProviders.length === 0) {
|
|
117
|
+
console.log('No matching agents found. Check your config.');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Export from all selected providers
|
|
122
|
+
for (const provider of activeProviders) {
|
|
123
|
+
try {
|
|
124
|
+
await provider.exportToVault(vaultDir);
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
console.error(`Error exporting ${provider.name}:`, e);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const gitSync = new gitSync_1.GitSyncEngine(vaultDir);
|
|
131
|
+
await gitSync.push('Auto-sync agent data from ' + new Date().toISOString());
|
|
132
|
+
console.log('Push complete.');
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
console.error('Failed to push:', error);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
program
|
|
139
|
+
.command('pull')
|
|
140
|
+
.description('Pull data from the remote vault and import into local agents')
|
|
141
|
+
.action(async () => {
|
|
142
|
+
try {
|
|
143
|
+
if (await vaultManager.isFirstRun()) {
|
|
144
|
+
await runSetupWizard();
|
|
145
|
+
}
|
|
146
|
+
console.log('Preparing to pull data...');
|
|
147
|
+
await vaultManager.initVault();
|
|
148
|
+
const vaultDir = vaultManager.getVaultDir();
|
|
149
|
+
const gitSync = new gitSync_1.GitSyncEngine(vaultDir);
|
|
150
|
+
await gitSync.pull();
|
|
151
|
+
let activeProviders = providers;
|
|
152
|
+
const config = await vaultManager.getConfig();
|
|
153
|
+
if (config.agents && config.agents.length > 0) {
|
|
154
|
+
const configuredAgents = config.agents;
|
|
155
|
+
activeProviders = providers.filter(p => configuredAgents.includes(p.name.toLowerCase()));
|
|
156
|
+
if (activeProviders.length === 0) {
|
|
157
|
+
console.log('No matching agents found. Check your config.');
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Import into all selected providers
|
|
162
|
+
for (const provider of activeProviders) {
|
|
163
|
+
try {
|
|
164
|
+
await provider.importFromVault(vaultDir);
|
|
165
|
+
}
|
|
166
|
+
catch (e) {
|
|
167
|
+
console.error(`Error importing ${provider.name}:`, e);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
console.log('Pull complete.');
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
console.error('Failed to pull:', error);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
program
|
|
177
|
+
.command('status')
|
|
178
|
+
.description('Check the status of the local vault')
|
|
179
|
+
.action(async () => {
|
|
180
|
+
try {
|
|
181
|
+
const vaultDir = vaultManager.getVaultDir();
|
|
182
|
+
console.log(`Vault directory: ${vaultDir}`);
|
|
183
|
+
const gitSync = new gitSync_1.GitSyncEngine(vaultDir);
|
|
184
|
+
// Let simpleGit handle it (dirty hack but works for logging)
|
|
185
|
+
const git = require('simple-git')(vaultDir);
|
|
186
|
+
const isRepo = await git.checkIsRepo();
|
|
187
|
+
if (!isRepo) {
|
|
188
|
+
console.log('Vault is not a git repository. Run `agentsync init <repo>` first.');
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
const status = await git.status();
|
|
192
|
+
console.log(`Current branch: ${status.current}`);
|
|
193
|
+
console.log(`Tracking: ${status.tracking}`);
|
|
194
|
+
console.log(`Modified files: ${status.modified.length}`);
|
|
195
|
+
console.log(`Untracked files: ${status.not_added.length}`);
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
console.error('Failed to check status:', error);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
program.parse();
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface SyncProvider {
|
|
2
|
+
/**
|
|
3
|
+
* The name of the AI agent (e.g., 'claude', 'cursor', 'aider')
|
|
4
|
+
*/
|
|
5
|
+
name: string;
|
|
6
|
+
/**
|
|
7
|
+
* Extracts data from the agent's local directories and copies it into the vault.
|
|
8
|
+
* @param vaultDir The base directory of the vault.
|
|
9
|
+
*/
|
|
10
|
+
exportToVault(vaultDir: string): Promise<void>;
|
|
11
|
+
/**
|
|
12
|
+
* Restores data from the vault back into the agent's local directories.
|
|
13
|
+
* @param vaultDir The base directory of the vault.
|
|
14
|
+
*/
|
|
15
|
+
importFromVault(vaultDir: string): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Checks whether the agent is installed locally on this machine.
|
|
18
|
+
* Used to auto-detect agents during first run setup.
|
|
19
|
+
*/
|
|
20
|
+
isInstalled(): Promise<boolean>;
|
|
21
|
+
}
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SyncProvider } from '../provider';
|
|
2
|
+
export declare class AiderProvider implements SyncProvider {
|
|
3
|
+
name: string;
|
|
4
|
+
private getProjectHash;
|
|
5
|
+
private getAiderFiles;
|
|
6
|
+
isInstalled(): Promise<boolean>;
|
|
7
|
+
exportToVault(vaultDir: string): Promise<void>;
|
|
8
|
+
importFromVault(vaultDir: string): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.AiderProvider = void 0;
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const fs = __importStar(require("fs-extra"));
|
|
39
|
+
const crypto = __importStar(require("crypto"));
|
|
40
|
+
class AiderProvider {
|
|
41
|
+
name = 'aider';
|
|
42
|
+
getProjectHash(dir) {
|
|
43
|
+
return crypto.createHash('md5').update(dir).digest('hex');
|
|
44
|
+
}
|
|
45
|
+
getAiderFiles() {
|
|
46
|
+
const cwd = process.cwd();
|
|
47
|
+
return [
|
|
48
|
+
path.join(cwd, '.aider.chat.history.md'),
|
|
49
|
+
path.join(cwd, '.aider.input.history'),
|
|
50
|
+
path.join(cwd, '.aider.tags.cache.v3')
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
async isInstalled() {
|
|
54
|
+
const files = this.getAiderFiles();
|
|
55
|
+
for (const f of files) {
|
|
56
|
+
if (await fs.pathExists(f))
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
async exportToVault(vaultDir) {
|
|
62
|
+
const cwd = process.cwd();
|
|
63
|
+
const hash = this.getProjectHash(cwd);
|
|
64
|
+
const vaultAiderDir = path.join(vaultDir, this.name, hash);
|
|
65
|
+
const aiderFiles = this.getAiderFiles();
|
|
66
|
+
let exported = false;
|
|
67
|
+
for (const file of aiderFiles) {
|
|
68
|
+
if (await fs.pathExists(file)) {
|
|
69
|
+
if (!exported) {
|
|
70
|
+
await fs.ensureDir(vaultAiderDir);
|
|
71
|
+
exported = true;
|
|
72
|
+
}
|
|
73
|
+
const fileName = path.basename(file);
|
|
74
|
+
const vaultFile = path.join(vaultAiderDir, fileName);
|
|
75
|
+
await fs.copy(file, vaultFile, { overwrite: true });
|
|
76
|
+
console.log(`Exported Aider file: ${fileName}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async importFromVault(vaultDir) {
|
|
81
|
+
const cwd = process.cwd();
|
|
82
|
+
const hash = this.getProjectHash(cwd);
|
|
83
|
+
const vaultAiderDir = path.join(vaultDir, this.name, hash);
|
|
84
|
+
if (await fs.pathExists(vaultAiderDir)) {
|
|
85
|
+
const files = await fs.readdir(vaultAiderDir);
|
|
86
|
+
for (const file of files) {
|
|
87
|
+
const vaultFile = path.join(vaultAiderDir, file);
|
|
88
|
+
const localFile = path.join(cwd, file);
|
|
89
|
+
await fs.copy(vaultFile, localFile, { overwrite: true });
|
|
90
|
+
console.log(`Imported Aider file: ${file}`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
console.log(`No Aider data found in vault for current project.`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
exports.AiderProvider = AiderProvider;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { SyncProvider } from '../provider';
|
|
2
|
+
export declare class ClaudeCodeProvider implements SyncProvider {
|
|
3
|
+
name: string;
|
|
4
|
+
private getLocalClaudeDir;
|
|
5
|
+
isInstalled(): Promise<boolean>;
|
|
6
|
+
exportToVault(vaultDir: string): Promise<void>;
|
|
7
|
+
importFromVault(vaultDir: string): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ClaudeCodeProvider = void 0;
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const fs = __importStar(require("fs-extra"));
|
|
40
|
+
class ClaudeCodeProvider {
|
|
41
|
+
name = 'claude';
|
|
42
|
+
getLocalClaudeDir() {
|
|
43
|
+
const homeDir = os.homedir();
|
|
44
|
+
// Claude Code uses ~/.claude or %USERPROFILE%\.claude
|
|
45
|
+
return path.join(homeDir, '.claude');
|
|
46
|
+
}
|
|
47
|
+
async isInstalled() {
|
|
48
|
+
return fs.pathExists(this.getLocalClaudeDir());
|
|
49
|
+
}
|
|
50
|
+
async exportToVault(vaultDir) {
|
|
51
|
+
const localDir = this.getLocalClaudeDir();
|
|
52
|
+
const vaultClaudeDir = path.join(vaultDir, this.name);
|
|
53
|
+
if (await fs.pathExists(localDir)) {
|
|
54
|
+
console.log(`Exporting ${this.name} data...`);
|
|
55
|
+
await fs.ensureDir(vaultClaudeDir);
|
|
56
|
+
// Copy the entire .claude directory to the vault, excluding potentially huge caches if needed
|
|
57
|
+
// but for now, copy everything.
|
|
58
|
+
await fs.copy(localDir, vaultClaudeDir, {
|
|
59
|
+
overwrite: true,
|
|
60
|
+
errorOnExist: false,
|
|
61
|
+
filter: (src) => {
|
|
62
|
+
// You might want to exclude things here, like active sockets or massive log files if any exist
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
console.log(`Successfully exported ${this.name} data.`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(`${this.name} local data not found at ${localDir}, skipping export.`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async importFromVault(vaultDir) {
|
|
73
|
+
const localDir = this.getLocalClaudeDir();
|
|
74
|
+
const vaultClaudeDir = path.join(vaultDir, this.name);
|
|
75
|
+
if (await fs.pathExists(vaultClaudeDir)) {
|
|
76
|
+
console.log(`Importing ${this.name} data...`);
|
|
77
|
+
await fs.ensureDir(localDir);
|
|
78
|
+
// Copy from vault back to local
|
|
79
|
+
await fs.copy(vaultClaudeDir, localDir, {
|
|
80
|
+
overwrite: true,
|
|
81
|
+
errorOnExist: false
|
|
82
|
+
});
|
|
83
|
+
console.log(`Successfully imported ${this.name} data.`);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.log(`${this.name} data not found in vault, skipping import.`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
exports.ClaudeCodeProvider = ClaudeCodeProvider;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { SyncProvider } from '../provider';
|
|
2
|
+
export declare class CodexProvider implements SyncProvider {
|
|
3
|
+
name: string;
|
|
4
|
+
private getLocalCodexDir;
|
|
5
|
+
isInstalled(): Promise<boolean>;
|
|
6
|
+
exportToVault(vaultDir: string): Promise<void>;
|
|
7
|
+
importFromVault(vaultDir: string): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CodexProvider = void 0;
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const fs = __importStar(require("fs-extra"));
|
|
40
|
+
class CodexProvider {
|
|
41
|
+
name = 'codex';
|
|
42
|
+
getLocalCodexDir() {
|
|
43
|
+
const homeDir = os.homedir();
|
|
44
|
+
// Codex CLI uses ~/.codex
|
|
45
|
+
return path.join(homeDir, '.codex');
|
|
46
|
+
}
|
|
47
|
+
async isInstalled() {
|
|
48
|
+
return fs.pathExists(this.getLocalCodexDir());
|
|
49
|
+
}
|
|
50
|
+
async exportToVault(vaultDir) {
|
|
51
|
+
const localDir = this.getLocalCodexDir();
|
|
52
|
+
const vaultCodexDir = path.join(vaultDir, this.name);
|
|
53
|
+
if (await fs.pathExists(localDir)) {
|
|
54
|
+
console.log(`Exporting ${this.name} data...`);
|
|
55
|
+
await fs.ensureDir(vaultCodexDir);
|
|
56
|
+
await fs.copy(localDir, vaultCodexDir, {
|
|
57
|
+
overwrite: true,
|
|
58
|
+
errorOnExist: false
|
|
59
|
+
});
|
|
60
|
+
console.log(`Successfully exported ${this.name} data.`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
console.log(`${this.name} local data not found at ${localDir}, skipping export.`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async importFromVault(vaultDir) {
|
|
67
|
+
const localDir = this.getLocalCodexDir();
|
|
68
|
+
const vaultCodexDir = path.join(vaultDir, this.name);
|
|
69
|
+
if (await fs.pathExists(vaultCodexDir)) {
|
|
70
|
+
console.log(`Importing ${this.name} data...`);
|
|
71
|
+
await fs.ensureDir(localDir);
|
|
72
|
+
await fs.copy(vaultCodexDir, localDir, {
|
|
73
|
+
overwrite: true,
|
|
74
|
+
errorOnExist: false
|
|
75
|
+
});
|
|
76
|
+
console.log(`Successfully imported ${this.name} data.`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log(`${this.name} data not found in vault, skipping import.`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.CodexProvider = CodexProvider;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { SyncProvider } from '../provider';
|
|
2
|
+
export declare class CopilotProvider implements SyncProvider {
|
|
3
|
+
name: string;
|
|
4
|
+
private getWorkspaceStorageDir;
|
|
5
|
+
isInstalled(): Promise<boolean>;
|
|
6
|
+
exportToVault(vaultDir: string): Promise<void>;
|
|
7
|
+
importFromVault(vaultDir: string): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CopilotProvider = void 0;
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const fs = __importStar(require("fs-extra"));
|
|
40
|
+
class CopilotProvider {
|
|
41
|
+
name = 'copilot';
|
|
42
|
+
getWorkspaceStorageDir() {
|
|
43
|
+
const platform = os.platform();
|
|
44
|
+
const homeDir = os.homedir();
|
|
45
|
+
if (platform === 'win32') {
|
|
46
|
+
const appData = process.env.APPDATA || path.join(homeDir, 'AppData', 'Roaming');
|
|
47
|
+
return path.join(appData, 'Code', 'User', 'workspaceStorage');
|
|
48
|
+
}
|
|
49
|
+
else if (platform === 'darwin') {
|
|
50
|
+
return path.join(homeDir, 'Library', 'Application Support', 'Code', 'User', 'workspaceStorage');
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
return path.join(homeDir, '.config', 'Code', 'User', 'workspaceStorage');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async isInstalled() {
|
|
57
|
+
return fs.pathExists(this.getWorkspaceStorageDir());
|
|
58
|
+
}
|
|
59
|
+
async exportToVault(vaultDir) {
|
|
60
|
+
const storageDir = this.getWorkspaceStorageDir();
|
|
61
|
+
const vaultCopilotDir = path.join(vaultDir, this.name);
|
|
62
|
+
if (await fs.pathExists(storageDir)) {
|
|
63
|
+
const workspaces = await fs.readdir(storageDir);
|
|
64
|
+
let foundCopilotData = false;
|
|
65
|
+
for (const ws of workspaces) {
|
|
66
|
+
const wsDir = path.join(storageDir, ws);
|
|
67
|
+
const stat = await fs.stat(wsDir);
|
|
68
|
+
if (stat.isDirectory()) {
|
|
69
|
+
// Copilot chat is typically stored in GitHub.copilot-chat or similar within the workspace hash
|
|
70
|
+
const copilotChatDir = path.join(wsDir, 'GitHub.copilot-chat');
|
|
71
|
+
if (await fs.pathExists(copilotChatDir)) {
|
|
72
|
+
if (!foundCopilotData) {
|
|
73
|
+
await fs.ensureDir(vaultCopilotDir);
|
|
74
|
+
foundCopilotData = true;
|
|
75
|
+
}
|
|
76
|
+
const vaultWsDir = path.join(vaultCopilotDir, ws, 'GitHub.copilot-chat');
|
|
77
|
+
await fs.copy(copilotChatDir, vaultWsDir, { overwrite: true });
|
|
78
|
+
console.log(`Exported Copilot data for workspace ${ws}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (!foundCopilotData) {
|
|
83
|
+
console.log(`No Copilot data found in workspaceStorage.`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
console.log(`VS Code workspaceStorage not found at ${storageDir}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async importFromVault(vaultDir) {
|
|
91
|
+
const storageDir = this.getWorkspaceStorageDir();
|
|
92
|
+
const vaultCopilotDir = path.join(vaultDir, this.name);
|
|
93
|
+
if (await fs.pathExists(vaultCopilotDir)) {
|
|
94
|
+
const workspaces = await fs.readdir(vaultCopilotDir);
|
|
95
|
+
for (const ws of workspaces) {
|
|
96
|
+
const vaultWsCopilotDir = path.join(vaultCopilotDir, ws, 'GitHub.copilot-chat');
|
|
97
|
+
const localWsCopilotDir = path.join(storageDir, ws, 'GitHub.copilot-chat');
|
|
98
|
+
if (await fs.pathExists(vaultWsCopilotDir)) {
|
|
99
|
+
await fs.ensureDir(path.join(storageDir, ws));
|
|
100
|
+
await fs.copy(vaultWsCopilotDir, localWsCopilotDir, { overwrite: true });
|
|
101
|
+
console.log(`Imported Copilot data for workspace ${ws}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
console.log(`${this.name} data not found in vault, skipping import.`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
exports.CopilotProvider = CopilotProvider;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SyncProvider } from '../provider';
|
|
2
|
+
export declare class CursorProvider implements SyncProvider {
|
|
3
|
+
name: string;
|
|
4
|
+
private getLocalCursorUserDir;
|
|
5
|
+
private getCursorPaths;
|
|
6
|
+
isInstalled(): Promise<boolean>;
|
|
7
|
+
exportToVault(vaultDir: string): Promise<void>;
|
|
8
|
+
importFromVault(vaultDir: string): Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CursorProvider = void 0;
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const fs = __importStar(require("fs-extra"));
|
|
40
|
+
class CursorProvider {
|
|
41
|
+
name = 'cursor';
|
|
42
|
+
getLocalCursorUserDir() {
|
|
43
|
+
const platform = os.platform();
|
|
44
|
+
const homeDir = os.homedir();
|
|
45
|
+
if (platform === 'win32') {
|
|
46
|
+
const appData = process.env.APPDATA || path.join(homeDir, 'AppData', 'Roaming');
|
|
47
|
+
return path.join(appData, 'Cursor', 'User');
|
|
48
|
+
}
|
|
49
|
+
else if (platform === 'darwin') {
|
|
50
|
+
return path.join(homeDir, 'Library', 'Application Support', 'Cursor', 'User');
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
return path.join(homeDir, '.config', 'Cursor', 'User');
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
getCursorPaths() {
|
|
57
|
+
const baseDir = this.getLocalCursorUserDir();
|
|
58
|
+
return [
|
|
59
|
+
path.join(baseDir, 'globalStorage', 'state.vscdb'), // The SQLite database
|
|
60
|
+
path.join(baseDir, 'workspaceStorage'), // Workspace specific settings
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
async isInstalled() {
|
|
64
|
+
return fs.pathExists(this.getLocalCursorUserDir());
|
|
65
|
+
}
|
|
66
|
+
async exportToVault(vaultDir) {
|
|
67
|
+
const vaultCursorDir = path.join(vaultDir, this.name);
|
|
68
|
+
await fs.ensureDir(vaultCursorDir);
|
|
69
|
+
const pathsToSync = this.getCursorPaths();
|
|
70
|
+
for (const p of pathsToSync) {
|
|
71
|
+
if (await fs.pathExists(p)) {
|
|
72
|
+
const relativePath = path.relative(this.getLocalCursorUserDir(), p);
|
|
73
|
+
const vaultPath = path.join(vaultCursorDir, relativePath);
|
|
74
|
+
console.log(`Exporting ${this.name} data: ${relativePath}`);
|
|
75
|
+
await fs.copy(p, vaultPath, { overwrite: true });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async importFromVault(vaultDir) {
|
|
80
|
+
const vaultCursorDir = path.join(vaultDir, this.name);
|
|
81
|
+
if (await fs.pathExists(vaultCursorDir)) {
|
|
82
|
+
const pathsToSync = this.getCursorPaths();
|
|
83
|
+
for (const p of pathsToSync) {
|
|
84
|
+
const relativePath = path.relative(this.getLocalCursorUserDir(), p);
|
|
85
|
+
const vaultPath = path.join(vaultCursorDir, relativePath);
|
|
86
|
+
if (await fs.pathExists(vaultPath)) {
|
|
87
|
+
console.log(`Importing ${this.name} data: ${relativePath}`);
|
|
88
|
+
await fs.copy(vaultPath, p, { overwrite: true });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
console.log(`${this.name} data not found in vault, skipping import.`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
exports.CursorProvider = CursorProvider;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { SyncProvider } from '../provider';
|
|
2
|
+
export declare class GeminiProvider implements SyncProvider {
|
|
3
|
+
name: string;
|
|
4
|
+
private getLocalGeminiDir;
|
|
5
|
+
isInstalled(): Promise<boolean>;
|
|
6
|
+
exportToVault(vaultDir: string): Promise<void>;
|
|
7
|
+
importFromVault(vaultDir: string): Promise<void>;
|
|
8
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.GeminiProvider = void 0;
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const fs = __importStar(require("fs-extra"));
|
|
40
|
+
class GeminiProvider {
|
|
41
|
+
name = 'gemini';
|
|
42
|
+
getLocalGeminiDir() {
|
|
43
|
+
const homeDir = os.homedir();
|
|
44
|
+
return path.join(homeDir, '.gemini', 'tmp');
|
|
45
|
+
}
|
|
46
|
+
async isInstalled() {
|
|
47
|
+
return fs.pathExists(this.getLocalGeminiDir());
|
|
48
|
+
}
|
|
49
|
+
async exportToVault(vaultDir) {
|
|
50
|
+
const localDir = this.getLocalGeminiDir();
|
|
51
|
+
const vaultGeminiDir = path.join(vaultDir, this.name);
|
|
52
|
+
if (await fs.pathExists(localDir)) {
|
|
53
|
+
console.log(`Exporting ${this.name} data...`);
|
|
54
|
+
await fs.ensureDir(vaultGeminiDir);
|
|
55
|
+
await fs.copy(localDir, vaultGeminiDir, {
|
|
56
|
+
overwrite: true,
|
|
57
|
+
errorOnExist: false
|
|
58
|
+
});
|
|
59
|
+
console.log(`Successfully exported ${this.name} data.`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log(`${this.name} local data not found at ${localDir}, skipping export.`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async importFromVault(vaultDir) {
|
|
66
|
+
const localDir = this.getLocalGeminiDir();
|
|
67
|
+
const vaultGeminiDir = path.join(vaultDir, this.name);
|
|
68
|
+
if (await fs.pathExists(vaultGeminiDir)) {
|
|
69
|
+
console.log(`Importing ${this.name} data...`);
|
|
70
|
+
await fs.ensureDir(localDir);
|
|
71
|
+
await fs.copy(vaultGeminiDir, localDir, {
|
|
72
|
+
overwrite: true,
|
|
73
|
+
errorOnExist: false
|
|
74
|
+
});
|
|
75
|
+
console.log(`Successfully imported ${this.name} data.`);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
console.log(`${this.name} data not found in vault, skipping import.`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.GeminiProvider = GeminiProvider;
|
package/dist/vault.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface AgentSyncConfig {
|
|
2
|
+
agents?: string[];
|
|
3
|
+
}
|
|
4
|
+
export declare class VaultManager {
|
|
5
|
+
private baseDir;
|
|
6
|
+
private vaultDir;
|
|
7
|
+
private configPath;
|
|
8
|
+
constructor();
|
|
9
|
+
getVaultDir(): string;
|
|
10
|
+
initVault(): Promise<void>;
|
|
11
|
+
getConfig(): Promise<AgentSyncConfig>;
|
|
12
|
+
saveConfig(config: AgentSyncConfig): Promise<void>;
|
|
13
|
+
isFirstRun(): Promise<boolean>;
|
|
14
|
+
cleanVault(): Promise<void>;
|
|
15
|
+
getAgentVaultDir(agentName: string): string;
|
|
16
|
+
ensureAgentVaultDir(agentName: string): Promise<void>;
|
|
17
|
+
}
|
package/dist/vault.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.VaultManager = void 0;
|
|
37
|
+
const os = __importStar(require("os"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const fs = __importStar(require("fs-extra"));
|
|
40
|
+
class VaultManager {
|
|
41
|
+
baseDir;
|
|
42
|
+
vaultDir;
|
|
43
|
+
configPath;
|
|
44
|
+
constructor() {
|
|
45
|
+
this.baseDir = path.join(os.homedir(), '.agentsync');
|
|
46
|
+
this.vaultDir = path.join(this.baseDir, 'vault');
|
|
47
|
+
this.configPath = path.join(this.baseDir, 'config.json');
|
|
48
|
+
}
|
|
49
|
+
getVaultDir() {
|
|
50
|
+
return this.vaultDir;
|
|
51
|
+
}
|
|
52
|
+
async initVault() {
|
|
53
|
+
await fs.ensureDir(this.vaultDir);
|
|
54
|
+
}
|
|
55
|
+
async getConfig() {
|
|
56
|
+
if (await fs.pathExists(this.configPath)) {
|
|
57
|
+
return fs.readJson(this.configPath);
|
|
58
|
+
}
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
async saveConfig(config) {
|
|
62
|
+
await fs.ensureDir(this.baseDir);
|
|
63
|
+
await fs.writeJson(this.configPath, config, { spaces: 2 });
|
|
64
|
+
}
|
|
65
|
+
async isFirstRun() {
|
|
66
|
+
const config = await this.getConfig();
|
|
67
|
+
return config.agents === undefined;
|
|
68
|
+
}
|
|
69
|
+
async cleanVault() {
|
|
70
|
+
// Keep the .git folder if it exists, clear everything else
|
|
71
|
+
const files = await fs.readdir(this.vaultDir);
|
|
72
|
+
for (const file of files) {
|
|
73
|
+
if (file !== '.git') {
|
|
74
|
+
await fs.remove(path.join(this.vaultDir, file));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
getAgentVaultDir(agentName) {
|
|
79
|
+
return path.join(this.vaultDir, agentName);
|
|
80
|
+
}
|
|
81
|
+
async ensureAgentVaultDir(agentName) {
|
|
82
|
+
await fs.ensureDir(this.getAgentVaultDir(agentName));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
exports.VaultManager = VaultManager;
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@damador/agentsync",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Synchronization tool for AI Agent conversations.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"agentsync": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"start": "ts-node src/index.ts",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"test:coverage": "vitest run --coverage",
|
|
17
|
+
"prepare": "npm run build"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/damadorPL/agentsync.git"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"agentsync",
|
|
25
|
+
"cli",
|
|
26
|
+
"sync",
|
|
27
|
+
"ai",
|
|
28
|
+
"agent"
|
|
29
|
+
],
|
|
30
|
+
"author": "Krzysztof Radzikowski",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"type": "commonjs",
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"commander": "^14.0.3",
|
|
35
|
+
"fs-extra": "^11.3.5",
|
|
36
|
+
"prompts": "^2.4.2",
|
|
37
|
+
"simple-git": "^3.36.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/fs-extra": "^11.0.4",
|
|
41
|
+
"@types/node": "^25.8.0",
|
|
42
|
+
"@types/prompts": "^2.4.9",
|
|
43
|
+
"@vitest/coverage-v8": "^4.1.6",
|
|
44
|
+
"ts-node": "^10.9.2",
|
|
45
|
+
"typescript": "^6.0.3",
|
|
46
|
+
"vitest": "^4.1.6"
|
|
47
|
+
}
|
|
48
|
+
}
|