wangchuan 3.0.0 → 3.1.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 +146 -174
- package/README.zh-CN.md +254 -0
- package/dist/bin/wangchuan.d.ts +1 -1
- package/dist/bin/wangchuan.js +2 -2
- package/dist/src/agents/codex.d.ts +9 -0
- package/dist/src/agents/codex.d.ts.map +1 -0
- package/dist/src/agents/codex.js +20 -0
- package/dist/src/agents/codex.js.map +1 -0
- package/dist/src/agents/index.d.ts.map +1 -1
- package/dist/src/agents/index.js +2 -0
- package/dist/src/agents/index.js.map +1 -1
- package/dist/src/commands/completions.js +2 -2
- package/dist/src/commands/completions.js.map +1 -1
- package/dist/src/commands/list.d.ts.map +1 -1
- package/dist/src/commands/list.js +1 -0
- package/dist/src/commands/list.js.map +1 -1
- package/dist/src/commands/template.js +1 -1
- package/dist/src/commands/template.js.map +1 -1
- package/dist/src/core/config.d.ts +9 -9
- package/dist/src/core/config.d.ts.map +1 -1
- package/dist/src/core/config.js +11 -11
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/crypto.d.ts +10 -10
- package/dist/src/core/crypto.d.ts.map +1 -1
- package/dist/src/core/crypto.js +19 -19
- package/dist/src/core/crypto.js.map +1 -1
- package/dist/src/core/git.d.ts +2 -2
- package/dist/src/core/git.js +9 -9
- package/dist/src/core/git.js.map +1 -1
- package/dist/src/core/json-field.d.ts +5 -5
- package/dist/src/core/json-field.d.ts.map +1 -1
- package/dist/src/core/json-field.js +5 -5
- package/dist/src/core/json-field.js.map +1 -1
- package/dist/src/core/sync.d.ts +9 -9
- package/dist/src/core/sync.js +47 -47
- package/dist/src/core/sync.js.map +1 -1
- package/dist/src/i18n.js +2 -2
- package/dist/src/i18n.js.map +1 -1
- package/dist/src/types.d.ts +24 -23
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +3 -3
- package/dist/src/types.js.map +1 -1
- package/dist/src/utils/linediff.d.ts +6 -5
- package/dist/src/utils/linediff.d.ts.map +1 -1
- package/dist/src/utils/linediff.js +10 -9
- package/dist/src/utils/linediff.js.map +1 -1
- package/dist/test/crypto.test.d.ts +1 -1
- package/dist/test/crypto.test.js +16 -16
- package/dist/test/crypto.test.js.map +1 -1
- package/dist/test/json-field.test.d.ts +1 -1
- package/dist/test/json-field.test.js +13 -13
- package/dist/test/json-field.test.js.map +1 -1
- package/dist/test/sync.test.d.ts +8 -8
- package/dist/test/sync.test.js +122 -113
- package/dist/test/sync.test.js.map +1 -1
- package/package.json +1 -1
package/dist/src/core/crypto.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* crypto.ts — AES-256-GCM
|
|
2
|
+
* crypto.ts — AES-256-GCM encryption/decryption module
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Key file format: 32 bytes of random data, stored as hex string (64 chars)
|
|
5
|
+
* Ciphertext format: IV(12B) | AuthTag(16B) | CipherText → Base64 written to .enc file
|
|
6
6
|
*/
|
|
7
7
|
import crypto from 'crypto';
|
|
8
8
|
import fs from 'fs';
|
|
@@ -10,20 +10,20 @@ import path from 'path';
|
|
|
10
10
|
import { logger } from '../utils/logger.js';
|
|
11
11
|
const ALGO = 'aes-256-gcm';
|
|
12
12
|
const KEY_BYTES = 32; // 256 bit
|
|
13
|
-
const IV_BYTES = 12; // 96 bit (GCM
|
|
13
|
+
const IV_BYTES = 12; // 96 bit (GCM recommended)
|
|
14
14
|
const TAG_BYTES = 16; // 128 bit auth tag
|
|
15
|
-
/**
|
|
15
|
+
/** Load and validate the master key from file */
|
|
16
16
|
function loadKey(keyPath) {
|
|
17
17
|
if (!fs.existsSync(keyPath)) {
|
|
18
|
-
throw new Error(
|
|
18
|
+
throw new Error(`Key file not found: ${keyPath}\nPlease run wangchuan init to generate a key.`);
|
|
19
19
|
}
|
|
20
20
|
const hex = fs.readFileSync(keyPath, 'utf-8').trim();
|
|
21
21
|
if (hex.length !== KEY_BYTES * 2) {
|
|
22
|
-
throw new Error(
|
|
22
|
+
throw new Error(`Invalid key file format, expected ${KEY_BYTES * 2} hex characters`);
|
|
23
23
|
}
|
|
24
24
|
return Buffer.from(hex, 'hex');
|
|
25
25
|
}
|
|
26
|
-
/**
|
|
26
|
+
/** Encrypt Buffer → Base64 string (format: IV + AuthTag + CipherText) */
|
|
27
27
|
function encryptBuffer(plaintext, key) {
|
|
28
28
|
const iv = crypto.randomBytes(IV_BYTES);
|
|
29
29
|
const cipher = crypto.createCipheriv(ALGO, key, iv);
|
|
@@ -31,7 +31,7 @@ function encryptBuffer(plaintext, key) {
|
|
|
31
31
|
const tag = cipher.getAuthTag();
|
|
32
32
|
return Buffer.concat([iv, tag, enc]).toString('base64');
|
|
33
33
|
}
|
|
34
|
-
/** Base64
|
|
34
|
+
/** Base64 ciphertext → original Buffer */
|
|
35
35
|
function decryptBuffer(b64, key) {
|
|
36
36
|
const data = Buffer.from(b64, 'base64');
|
|
37
37
|
const iv = data.subarray(0, IV_BYTES);
|
|
@@ -43,44 +43,44 @@ function decryptBuffer(b64, key) {
|
|
|
43
43
|
}
|
|
44
44
|
export const cryptoEngine = {
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
46
|
+
* Generate a new master key and write to file (mode 0o600).
|
|
47
|
+
* Returns the raw 32-byte Buffer for test assertions.
|
|
48
48
|
*/
|
|
49
49
|
generateKey(keyPath) {
|
|
50
50
|
fs.mkdirSync(path.dirname(keyPath), { recursive: true });
|
|
51
51
|
const key = crypto.randomBytes(KEY_BYTES);
|
|
52
52
|
fs.writeFileSync(keyPath, key.toString('hex'), { mode: 0o600, encoding: 'utf-8' });
|
|
53
|
-
logger.ok(
|
|
53
|
+
logger.ok(`Master key generated: ${keyPath}`);
|
|
54
54
|
return key;
|
|
55
55
|
},
|
|
56
|
-
/**
|
|
56
|
+
/** Check if key file exists */
|
|
57
57
|
hasKey(keyPath) {
|
|
58
58
|
return fs.existsSync(keyPath);
|
|
59
59
|
},
|
|
60
|
-
/**
|
|
60
|
+
/** Encrypt source file, write ciphertext to destPath (typically ending in .enc) */
|
|
61
61
|
encryptFile(srcPath, destPath, keyPath) {
|
|
62
62
|
const key = loadKey(keyPath);
|
|
63
63
|
const plaintext = fs.readFileSync(srcPath);
|
|
64
64
|
const encrypted = encryptBuffer(plaintext, key);
|
|
65
65
|
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
66
66
|
fs.writeFileSync(destPath, encrypted, 'utf-8');
|
|
67
|
-
logger.debug(
|
|
67
|
+
logger.debug(`Encrypted: ${srcPath} → ${destPath}`);
|
|
68
68
|
},
|
|
69
|
-
/**
|
|
69
|
+
/** Decrypt .enc file, write plaintext to destPath */
|
|
70
70
|
decryptFile(srcPath, destPath, keyPath) {
|
|
71
71
|
const key = loadKey(keyPath);
|
|
72
72
|
const encrypted = fs.readFileSync(srcPath, 'utf-8').trim();
|
|
73
73
|
const plaintext = decryptBuffer(encrypted, key);
|
|
74
74
|
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
75
75
|
fs.writeFileSync(destPath, plaintext);
|
|
76
|
-
logger.debug(
|
|
76
|
+
logger.debug(`Decrypted: ${srcPath} → ${destPath}`);
|
|
77
77
|
},
|
|
78
|
-
/**
|
|
78
|
+
/** Encrypt an arbitrary string, return Base64 ciphertext */
|
|
79
79
|
encryptString(plaintext, keyPath) {
|
|
80
80
|
const key = loadKey(keyPath);
|
|
81
81
|
return encryptBuffer(Buffer.from(plaintext, 'utf-8'), key);
|
|
82
82
|
},
|
|
83
|
-
/**
|
|
83
|
+
/** Decrypt Base64 ciphertext, return original string */
|
|
84
84
|
decryptString(b64, keyPath) {
|
|
85
85
|
const key = loadKey(keyPath);
|
|
86
86
|
return decryptBuffer(b64, key).toString('utf-8');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../../src/core/crypto.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,MAAU,IAAI,CAAC;AACxB,OAAO,IAAI,MAAQ,MAAM,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,MAAM,IAAI,GAAQ,aAAsB,CAAC;AACzC,MAAM,SAAS,GAAG,EAAE,CAAC,CAAE,UAAU;AACjC,MAAM,QAAQ,GAAI,EAAE,CAAC,CAAE,
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../../src/core/crypto.ts"],"names":[],"mappings":"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"}
|
package/dist/src/core/git.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* git.ts — simple-git
|
|
2
|
+
* git.ts — simple-git wrapper providing idempotent Git operations
|
|
3
3
|
*
|
|
4
|
-
* simple-git v3
|
|
4
|
+
* simple-git v3 requires named import { simpleGit } under ESM (not default import).
|
|
5
5
|
*/
|
|
6
6
|
import type { DefaultLogFields } from 'simple-git';
|
|
7
7
|
import type { CommitResult } from '../types.js';
|
package/dist/src/core/git.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* git.ts — simple-git
|
|
2
|
+
* git.ts — simple-git wrapper providing idempotent Git operations
|
|
3
3
|
*
|
|
4
|
-
* simple-git v3
|
|
4
|
+
* simple-git v3 requires named import { simpleGit } under ESM (not default import).
|
|
5
5
|
*/
|
|
6
6
|
import { simpleGit } from 'simple-git';
|
|
7
7
|
import fs from 'fs';
|
|
@@ -16,13 +16,13 @@ function createGit(repoPath) {
|
|
|
16
16
|
export const gitEngine = {
|
|
17
17
|
async cloneOrFetch(remoteUrl, localPath, branch = 'main') {
|
|
18
18
|
if (fs.existsSync(path.join(localPath, '.git'))) {
|
|
19
|
-
logger.debug(
|
|
19
|
+
logger.debug(`Repo exists, running fetch: ${localPath}`);
|
|
20
20
|
const git = createGit(localPath);
|
|
21
21
|
await git.fetch('origin', branch);
|
|
22
22
|
await git.reset(['--hard', `origin/${branch}`]);
|
|
23
23
|
}
|
|
24
24
|
else {
|
|
25
|
-
logger.debug(
|
|
25
|
+
logger.debug(`Cloning repo: ${remoteUrl} → ${localPath}`);
|
|
26
26
|
fs.mkdirSync(localPath, { recursive: true });
|
|
27
27
|
await simpleGit().clone(remoteUrl, localPath, ['--branch', branch, '--single-branch']);
|
|
28
28
|
}
|
|
@@ -38,14 +38,14 @@ export const gitEngine = {
|
|
|
38
38
|
await git.add('.');
|
|
39
39
|
const status = await git.status();
|
|
40
40
|
if (status.isClean()) {
|
|
41
|
-
logger.info('
|
|
41
|
+
logger.info('No new changes to commit');
|
|
42
42
|
return { committed: false, pushed: false };
|
|
43
43
|
}
|
|
44
|
-
logger.debug(
|
|
44
|
+
logger.debug(`Staged files: ${status.staged.join(', ')}`);
|
|
45
45
|
const commitResult = await git.commit(message);
|
|
46
46
|
logger.debug(`commit: ${commitResult.commit}`);
|
|
47
47
|
await git.push('origin', branch);
|
|
48
|
-
logger.debug(
|
|
48
|
+
logger.debug(`Pushed to origin/${branch}`);
|
|
49
49
|
return { committed: true, pushed: true, sha: commitResult.commit };
|
|
50
50
|
},
|
|
51
51
|
async status(localPath) {
|
|
@@ -62,9 +62,9 @@ export const gitEngine = {
|
|
|
62
62
|
return remotes.find(r => r.name === 'origin')?.refs?.fetch ?? null;
|
|
63
63
|
},
|
|
64
64
|
async rollback(localPath) {
|
|
65
|
-
logger.warn('
|
|
65
|
+
logger.warn('Rolling back the last commit...');
|
|
66
66
|
await createGit(localPath).reset(['--soft', 'HEAD~1']);
|
|
67
|
-
logger.warn('
|
|
67
|
+
logger.warn('Rollback complete, local changes preserved in staging area');
|
|
68
68
|
},
|
|
69
69
|
/**
|
|
70
70
|
* Fetch from remote and check if remote branch is ahead of local.
|
package/dist/src/core/git.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../../src/core/git.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,OAAO,EAAE,MAAQ,IAAI,CAAC;AACtB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAG5C,SAAS,SAAS,CAAC,QAAgB;IACjC,OAAO,SAAS,CAAC,QAAQ,EAAE;QACzB,sBAAsB,EAAE,CAAC;QACzB,OAAO,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;KAC3B,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB,KAAK,CAAC,YAAY,CAAC,SAAiB,EAAE,SAAiB,EAAE,MAAM,GAAG,MAAM;QACtE,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;YAChD,MAAM,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../../../src/core/git.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* json-field.ts — JSON
|
|
2
|
+
* json-field.ts — JSON field-level extraction and merge
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* pull
|
|
4
|
+
* Supports picking specified top-level fields from large JSON files for sync;
|
|
5
|
+
* merges fields back into target JSON on pull (without destroying other fields).
|
|
6
6
|
*/
|
|
7
7
|
type JsonObject = Record<string, unknown>;
|
|
8
|
-
/**
|
|
8
|
+
/** Extract specified top-level fields from an object */
|
|
9
9
|
export declare function extractFields(obj: JsonObject, fields: readonly string[]): JsonObject;
|
|
10
|
-
/**
|
|
10
|
+
/** Shallow merge extracted fields back into target object (only overwrites extracted fields, preserves the rest) */
|
|
11
11
|
export declare function mergeFields(target: JsonObject, partial: JsonObject): JsonObject;
|
|
12
12
|
export declare const jsonField: {
|
|
13
13
|
readonly extractFields: typeof extractFields;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-field.d.ts","sourceRoot":"","sources":["../../../src/core/json-field.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C,
|
|
1
|
+
{"version":3,"file":"json-field.d.ts","sourceRoot":"","sources":["../../../src/core/json-field.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C,wDAAwD;AACxD,wBAAgB,aAAa,CAAC,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAMpF;AAED,oHAAoH;AACpH,wBAAgB,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,GAAG,UAAU,CAE/E;AAED,eAAO,MAAM,SAAS;;;CAA0C,CAAC"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* json-field.ts — JSON
|
|
2
|
+
* json-field.ts — JSON field-level extraction and merge
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* pull
|
|
4
|
+
* Supports picking specified top-level fields from large JSON files for sync;
|
|
5
|
+
* merges fields back into target JSON on pull (without destroying other fields).
|
|
6
6
|
*/
|
|
7
|
-
/**
|
|
7
|
+
/** Extract specified top-level fields from an object */
|
|
8
8
|
export function extractFields(obj, fields) {
|
|
9
9
|
const result = {};
|
|
10
10
|
for (const f of fields) {
|
|
@@ -13,7 +13,7 @@ export function extractFields(obj, fields) {
|
|
|
13
13
|
}
|
|
14
14
|
return result;
|
|
15
15
|
}
|
|
16
|
-
/**
|
|
16
|
+
/** Shallow merge extracted fields back into target object (only overwrites extracted fields, preserves the rest) */
|
|
17
17
|
export function mergeFields(target, partial) {
|
|
18
18
|
return { ...target, ...partial };
|
|
19
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json-field.js","sourceRoot":"","sources":["../../../src/core/json-field.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,
|
|
1
|
+
{"version":3,"file":"json-field.js","sourceRoot":"","sources":["../../../src/core/json-field.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,wDAAwD;AACxD,MAAM,UAAU,aAAa,CAAC,GAAe,EAAE,MAAyB;IACtE,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,IAAI,GAAG;YAAE,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,oHAAoH;AACpH,MAAM,UAAU,WAAW,CAAC,MAAkB,EAAE,OAAmB;IACjE,OAAO,EAAE,GAAG,MAAM,EAAE,GAAG,OAAO,EAAE,CAAC;AACnC,CAAC;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,EAAE,aAAa,EAAE,WAAW,EAAW,CAAC"}
|
package/dist/src/core/sync.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* sync.ts —
|
|
2
|
+
* sync.ts — Core sync engine
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* stageToRepo
|
|
6
|
-
* restoreFromRepo
|
|
7
|
-
* diff
|
|
4
|
+
* Three directions:
|
|
5
|
+
* stageToRepo workspace → local repo directory (pre-push staging)
|
|
6
|
+
* restoreFromRepo local repo directory → workspace (post-pull restore)
|
|
7
|
+
* diff compare both sides, return diff summary
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
* shared —
|
|
11
|
-
* agents/* — per-agent
|
|
9
|
+
* Supports two-tier sync:
|
|
10
|
+
* shared — cross-agent sharing (skills, MCP templates, shared memory)
|
|
11
|
+
* agents/* — per-agent cross-environment sync
|
|
12
12
|
*
|
|
13
|
-
*
|
|
13
|
+
* All methods accept an optional agent filter parameter to operate on a specific agent's files only.
|
|
14
14
|
*/
|
|
15
15
|
import type { WangchuanConfig, FileEntry, StageResult, RestoreResult, DiffResult, AgentName, FilterOptions } from '../types.js';
|
|
16
16
|
export declare function expandHome(p: string): string;
|
package/dist/src/core/sync.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* sync.ts —
|
|
2
|
+
* sync.ts — Core sync engine
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* stageToRepo
|
|
6
|
-
* restoreFromRepo
|
|
7
|
-
* diff
|
|
4
|
+
* Three directions:
|
|
5
|
+
* stageToRepo workspace → local repo directory (pre-push staging)
|
|
6
|
+
* restoreFromRepo local repo directory → workspace (post-pull restore)
|
|
7
|
+
* diff compare both sides, return diff summary
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
* shared —
|
|
11
|
-
* agents/* — per-agent
|
|
9
|
+
* Supports two-tier sync:
|
|
10
|
+
* shared — cross-agent sharing (skills, MCP templates, shared memory)
|
|
11
|
+
* agents/* — per-agent cross-environment sync
|
|
12
12
|
*
|
|
13
|
-
*
|
|
13
|
+
* All methods accept an optional agent filter parameter to operate on a specific agent's files only.
|
|
14
14
|
*/
|
|
15
15
|
import fs from 'fs';
|
|
16
16
|
import path from 'path';
|
|
@@ -136,7 +136,7 @@ function walkDir(dirAbs) {
|
|
|
136
136
|
fs.readdirSync(dirAbs).forEach(f => walk(f));
|
|
137
137
|
return results;
|
|
138
138
|
}
|
|
139
|
-
/**
|
|
139
|
+
/** Deduplicate by repoRel, keeping the first occurrence */
|
|
140
140
|
function deduplicateEntries(entries) {
|
|
141
141
|
const seen = new Set();
|
|
142
142
|
return entries.filter(e => {
|
|
@@ -159,7 +159,7 @@ function logProgress(index, total, tag, filePath) {
|
|
|
159
159
|
logger.info(` ${counter} ${coloredTag} ${chalk.white(filePath)}`);
|
|
160
160
|
}
|
|
161
161
|
/**
|
|
162
|
-
*
|
|
162
|
+
* Build syncFiles + syncDirs + jsonFields entries for a given agent profile.
|
|
163
163
|
*/
|
|
164
164
|
function buildAgentEntries(name, profile, repoDirBase) {
|
|
165
165
|
const entries = [];
|
|
@@ -195,7 +195,7 @@ function buildAgentEntries(name, profile, repoDirBase) {
|
|
|
195
195
|
});
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
|
-
// jsonFields —
|
|
198
|
+
// jsonFields — field-level JSON extraction
|
|
199
199
|
for (const jf of (profile.jsonFields ?? [])) {
|
|
200
200
|
const suffix = jf.encrypt ? '.enc' : '';
|
|
201
201
|
entries.push({
|
|
@@ -213,7 +213,7 @@ function buildAgentEntries(name, profile, repoDirBase) {
|
|
|
213
213
|
return entries;
|
|
214
214
|
}
|
|
215
215
|
/**
|
|
216
|
-
*
|
|
216
|
+
* Build shared tier entries (skills, MCP templates, shared files).
|
|
217
217
|
*/
|
|
218
218
|
function buildSharedEntries(cfg, repoDirBase) {
|
|
219
219
|
const entries = [];
|
|
@@ -221,7 +221,7 @@ function buildSharedEntries(cfg, repoDirBase) {
|
|
|
221
221
|
if (!shared)
|
|
222
222
|
return entries;
|
|
223
223
|
const profiles = cfg.profiles.default;
|
|
224
|
-
// ── shared skills
|
|
224
|
+
// ── shared skills: multi-source aggregation ────────────────
|
|
225
225
|
for (const source of shared.skills.sources) {
|
|
226
226
|
const p = profiles[source.agent];
|
|
227
227
|
if (!p.enabled)
|
|
@@ -233,7 +233,7 @@ function buildSharedEntries(cfg, repoDirBase) {
|
|
|
233
233
|
if (!fs.existsSync(scanBase))
|
|
234
234
|
continue;
|
|
235
235
|
for (const relFile of walkDir(scanBase)) {
|
|
236
|
-
//
|
|
236
|
+
// Skip system files like .DS_Store
|
|
237
237
|
if (path.basename(relFile).startsWith('.'))
|
|
238
238
|
continue;
|
|
239
239
|
entries.push({
|
|
@@ -245,7 +245,7 @@ function buildSharedEntries(cfg, repoDirBase) {
|
|
|
245
245
|
});
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
|
-
// ── shared MCP
|
|
248
|
+
// ── shared MCP: extract mcpServers from each agent's JSON ──
|
|
249
249
|
for (const source of shared.mcp.sources) {
|
|
250
250
|
const p = profiles[source.agent];
|
|
251
251
|
if (!p.enabled)
|
|
@@ -308,30 +308,30 @@ function applyFilter(entries, filter) {
|
|
|
308
308
|
export function buildFileEntries(cfg, repoDirBase, agent, filter) {
|
|
309
309
|
const entries = [];
|
|
310
310
|
const profiles = cfg.profiles.default;
|
|
311
|
-
// per-agent
|
|
311
|
+
// per-agent entries
|
|
312
312
|
for (const name of AGENT_NAMES) {
|
|
313
313
|
const p = profiles[name];
|
|
314
314
|
if (!p.enabled || (agent && agent !== name))
|
|
315
315
|
continue;
|
|
316
316
|
entries.push(...buildAgentEntries(name, p, repoDirBase));
|
|
317
317
|
}
|
|
318
|
-
// shared
|
|
318
|
+
// shared entries (excluded when --agent filter is active, since shared belongs to no single agent)
|
|
319
319
|
if (!agent) {
|
|
320
320
|
entries.push(...buildSharedEntries(cfg, repoDirBase));
|
|
321
321
|
}
|
|
322
322
|
return applyFilter(deduplicateEntries(entries), filter);
|
|
323
323
|
}
|
|
324
324
|
/**
|
|
325
|
-
*
|
|
326
|
-
* push
|
|
325
|
+
* Distribute shared content (skills, MCP configs) to each agent's local directory.
|
|
326
|
+
* Called before push to ensure all agents have the latest shared resources.
|
|
327
327
|
*/
|
|
328
328
|
function distributeShared(cfg) {
|
|
329
329
|
const shared = cfg.shared;
|
|
330
330
|
if (!shared)
|
|
331
331
|
return;
|
|
332
332
|
const profiles = cfg.profiles.default;
|
|
333
|
-
// ──
|
|
334
|
-
//
|
|
333
|
+
// ── Distribute skills: collect from each source, only add skills the target is missing ──
|
|
334
|
+
// Collect each agent's current skill set
|
|
335
335
|
const agentSkills = new Map(); // agent → relPath → absPath
|
|
336
336
|
for (const source of shared.skills.sources) {
|
|
337
337
|
const p = profiles[source.agent];
|
|
@@ -348,7 +348,7 @@ function distributeShared(cfg) {
|
|
|
348
348
|
}
|
|
349
349
|
agentSkills.set(source.agent, skills);
|
|
350
350
|
}
|
|
351
|
-
//
|
|
351
|
+
// Merge all agents' skills (deduplicate, first occurrence wins)
|
|
352
352
|
const allSkills = new Map();
|
|
353
353
|
for (const skills of agentSkills.values()) {
|
|
354
354
|
for (const [rel, abs] of skills) {
|
|
@@ -356,7 +356,7 @@ function distributeShared(cfg) {
|
|
|
356
356
|
allSkills.set(rel, abs);
|
|
357
357
|
}
|
|
358
358
|
}
|
|
359
|
-
//
|
|
359
|
+
// Distribute: only copy skills that a given agent is missing (present in global set)
|
|
360
360
|
for (const source of shared.skills.sources) {
|
|
361
361
|
const p = profiles[source.agent];
|
|
362
362
|
if (!p.enabled)
|
|
@@ -365,14 +365,14 @@ function distributeShared(cfg) {
|
|
|
365
365
|
const skillsDir = path.join(expandHome(p.workspacePath), source.dir);
|
|
366
366
|
for (const [relFile, srcAbs] of allSkills) {
|
|
367
367
|
if (mySkills.has(relFile))
|
|
368
|
-
continue; //
|
|
368
|
+
continue; // already exists, do not overwrite
|
|
369
369
|
const dest = path.join(skillsDir, relFile);
|
|
370
370
|
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
371
371
|
fs.copyFileSync(srcAbs, dest);
|
|
372
372
|
logger.debug(` ${t('sync.distributeSkill', { file: relFile, agent: source.agent })}`);
|
|
373
373
|
}
|
|
374
374
|
}
|
|
375
|
-
// ──
|
|
375
|
+
// ── Distribute MCP configs: extract from each source, merge into other agents ──
|
|
376
376
|
const mergedMcp = {};
|
|
377
377
|
for (const source of shared.mcp.sources) {
|
|
378
378
|
const p = profiles[source.agent];
|
|
@@ -388,9 +388,9 @@ function distributeShared(cfg) {
|
|
|
388
388
|
Object.assign(mergedMcp, mcpField);
|
|
389
389
|
}
|
|
390
390
|
}
|
|
391
|
-
catch { /*
|
|
391
|
+
catch { /* ignore parse failures */ }
|
|
392
392
|
}
|
|
393
|
-
//
|
|
393
|
+
// Write back to each source agent's MCP config (create file if missing)
|
|
394
394
|
if (Object.keys(mergedMcp).length > 0) {
|
|
395
395
|
for (const source of shared.mcp.sources) {
|
|
396
396
|
const p = profiles[source.agent];
|
|
@@ -403,7 +403,7 @@ function distributeShared(cfg) {
|
|
|
403
403
|
json = JSON.parse(fs.readFileSync(srcPath, 'utf-8'));
|
|
404
404
|
}
|
|
405
405
|
const currentMcp = (json[source.field] ?? {});
|
|
406
|
-
//
|
|
406
|
+
// Only add MCP servers not present locally; do not overwrite existing configs
|
|
407
407
|
let changed = false;
|
|
408
408
|
for (const [key, val] of Object.entries(mergedMcp)) {
|
|
409
409
|
if (!(key in currentMcp)) {
|
|
@@ -418,13 +418,13 @@ function distributeShared(cfg) {
|
|
|
418
418
|
logger.debug(` ${t('sync.distributeMcp', { agent: source.agent })}`);
|
|
419
419
|
}
|
|
420
420
|
}
|
|
421
|
-
catch { /*
|
|
421
|
+
catch { /* ignore */ }
|
|
422
422
|
}
|
|
423
423
|
}
|
|
424
424
|
}
|
|
425
425
|
/**
|
|
426
|
-
*
|
|
427
|
-
*
|
|
426
|
+
* Prune stale files from repo — delete entries present in repo but absent from current entries.
|
|
427
|
+
* Only prunes files under agents/ and shared/ directories (does not touch .git etc.).
|
|
428
428
|
*/
|
|
429
429
|
function pruneRepoStaleFiles(repoPath, entries) {
|
|
430
430
|
const activeRepoRels = new Set(entries.map(e => e.repoRel));
|
|
@@ -442,7 +442,7 @@ function pruneRepoStaleFiles(repoPath, entries) {
|
|
|
442
442
|
fs.unlinkSync(abs);
|
|
443
443
|
deleted.push(repoRel);
|
|
444
444
|
logger.debug(` ${t('sync.pruneStale', { file: repoRel })}`);
|
|
445
|
-
//
|
|
445
|
+
// Clean up empty directories
|
|
446
446
|
let dir = path.dirname(abs);
|
|
447
447
|
while (dir !== scanRoot && dir.startsWith(scanRoot)) {
|
|
448
448
|
const remaining = fs.readdirSync(dir);
|
|
@@ -698,8 +698,8 @@ export const syncEngine = {
|
|
|
698
698
|
}
|
|
699
699
|
result.synced.push(entry.repoRel);
|
|
700
700
|
}
|
|
701
|
-
// ──
|
|
702
|
-
//
|
|
701
|
+
// ── Prune stale files from repo (full push only) ──────────────
|
|
702
|
+
// Only prune using entries actually written to repo, excluding skipped entries with missing local files
|
|
703
703
|
if (!agent) {
|
|
704
704
|
const syncedEntries = entries.filter(e => fs.existsSync(e.srcAbs));
|
|
705
705
|
const pruned = pruneRepoStaleFiles(repoPath, syncedEntries);
|
|
@@ -729,8 +729,8 @@ export const syncEngine = {
|
|
|
729
729
|
for (const entry of entries) {
|
|
730
730
|
const srcRepo = path.join(repoPath, entry.repoRel);
|
|
731
731
|
if (!fs.existsSync(srcRepo)) {
|
|
732
|
-
// repo
|
|
733
|
-
// jsonFields
|
|
732
|
+
// Not in repo but exists locally → mark as localOnly
|
|
733
|
+
// jsonFields entries: srcAbs is the full JSON (always exists), check if extracted fields are non-empty
|
|
734
734
|
if (entry.jsonExtract) {
|
|
735
735
|
try {
|
|
736
736
|
const fullJson = JSON.parse(fs.readFileSync(entry.srcAbs, 'utf-8'));
|
|
@@ -739,7 +739,7 @@ export const syncEngine = {
|
|
|
739
739
|
result.localOnly.push(entry.repoRel);
|
|
740
740
|
}
|
|
741
741
|
}
|
|
742
|
-
catch { /* JSON
|
|
742
|
+
catch { /* ignore JSON parse failures */ }
|
|
743
743
|
}
|
|
744
744
|
else if (fs.existsSync(entry.srcAbs)) {
|
|
745
745
|
result.localOnly.push(entry.repoRel);
|
|
@@ -748,7 +748,7 @@ export const syncEngine = {
|
|
|
748
748
|
result.skipped.push(entry.repoRel);
|
|
749
749
|
continue;
|
|
750
750
|
}
|
|
751
|
-
// ── JSON
|
|
751
|
+
// ── JSON field-level merge-back ────────────────────────────
|
|
752
752
|
if (entry.jsonExtract) {
|
|
753
753
|
let partialContent;
|
|
754
754
|
if (entry.encrypt) {
|
|
@@ -758,7 +758,7 @@ export const syncEngine = {
|
|
|
758
758
|
partialContent = fs.readFileSync(srcRepo, 'utf-8');
|
|
759
759
|
}
|
|
760
760
|
const partial = JSON.parse(partialContent);
|
|
761
|
-
//
|
|
761
|
+
// Read local full JSON, merge into it (without destroying other fields)
|
|
762
762
|
const targetPath = entry.jsonExtract.originalPath;
|
|
763
763
|
let fullJson = {};
|
|
764
764
|
if (fs.existsSync(targetPath)) {
|
|
@@ -767,7 +767,7 @@ export const syncEngine = {
|
|
|
767
767
|
const merged = jsonField.mergeFields(fullJson, partial);
|
|
768
768
|
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
769
769
|
fs.writeFileSync(targetPath, JSON.stringify(merged, null, 2), 'utf-8');
|
|
770
|
-
// shared MCP
|
|
770
|
+
// shared MCP entry: also distribute to all other agents
|
|
771
771
|
if (entry.agentName === 'shared' && cfg.shared) {
|
|
772
772
|
for (const source of cfg.shared.mcp.sources) {
|
|
773
773
|
const p = cfg.profiles.default[source.agent];
|
|
@@ -775,7 +775,7 @@ export const syncEngine = {
|
|
|
775
775
|
continue;
|
|
776
776
|
const otherPath = path.join(expandHome(p.workspacePath), source.src);
|
|
777
777
|
if (otherPath === targetPath)
|
|
778
|
-
continue; //
|
|
778
|
+
continue; // already handled
|
|
779
779
|
let otherJson = {};
|
|
780
780
|
if (fs.existsSync(otherPath)) {
|
|
781
781
|
try {
|
|
@@ -795,7 +795,7 @@ export const syncEngine = {
|
|
|
795
795
|
logProgress(restoreIdx, restoreTotal, entry.encrypt ? 'decrypted' : 'field', entry.repoRel);
|
|
796
796
|
continue;
|
|
797
797
|
}
|
|
798
|
-
// ── shared skills
|
|
798
|
+
// ── Distribute shared skills to all agents ─────────────────
|
|
799
799
|
if (entry.agentName === 'shared' && entry.repoRel.startsWith('shared/skills/')) {
|
|
800
800
|
const relInSkills = entry.repoRel.slice('shared/skills/'.length);
|
|
801
801
|
const shared = cfg.shared;
|
|
@@ -814,7 +814,7 @@ export const syncEngine = {
|
|
|
814
814
|
logProgress(restoreIdx, restoreTotal, 'copy', entry.repoRel);
|
|
815
815
|
continue;
|
|
816
816
|
}
|
|
817
|
-
// ──
|
|
817
|
+
// ── Conflict detection (whole-file sync) ──────────────────
|
|
818
818
|
if (fs.existsSync(entry.srcAbs)) {
|
|
819
819
|
let isDiff = false;
|
|
820
820
|
const localBuf = fs.readFileSync(entry.srcAbs);
|
|
@@ -906,7 +906,7 @@ export const syncEngine = {
|
|
|
906
906
|
}
|
|
907
907
|
}
|
|
908
908
|
}
|
|
909
|
-
// ──
|
|
909
|
+
// ── Write file ───────────────────────────────────────────
|
|
910
910
|
fs.mkdirSync(path.dirname(entry.srcAbs), { recursive: true });
|
|
911
911
|
if (entry.encrypt) {
|
|
912
912
|
cryptoEngine.decryptFile(srcRepo, entry.srcAbs, keyPath);
|
|
@@ -953,7 +953,7 @@ export const syncEngine = {
|
|
|
953
953
|
diff.missing.push(entry.repoRel);
|
|
954
954
|
continue;
|
|
955
955
|
}
|
|
956
|
-
// JSON
|
|
956
|
+
// For JSON field extraction, compare the extracted content
|
|
957
957
|
if (entry.jsonExtract) {
|
|
958
958
|
try {
|
|
959
959
|
const fullJson = JSON.parse(fs.readFileSync(entry.srcAbs, 'utf-8'));
|
|
@@ -974,7 +974,7 @@ export const syncEngine = {
|
|
|
974
974
|
}
|
|
975
975
|
continue;
|
|
976
976
|
}
|
|
977
|
-
//
|
|
977
|
+
// Whole-file comparison
|
|
978
978
|
const srcBuf = fs.readFileSync(entry.srcAbs);
|
|
979
979
|
const repoBuf = fs.readFileSync(path.join(repoPath, entry.repoRel));
|
|
980
980
|
if (entry.encrypt) {
|