@musashishao/agent-kit 1.2.2 → 1.4.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/.agent/mcp-gateway/README.md +121 -0
- package/.agent/mcp-gateway/dist/index.d.ts +11 -0
- package/.agent/mcp-gateway/dist/index.js +504 -0
- package/.agent/mcp-gateway/dist/sync/debouncer.d.ts +56 -0
- package/.agent/mcp-gateway/dist/sync/debouncer.js +112 -0
- package/.agent/mcp-gateway/dist/sync/incremental_syncer.d.ts +58 -0
- package/.agent/mcp-gateway/dist/sync/incremental_syncer.js +172 -0
- package/.agent/mcp-gateway/dist/sync/index.d.ts +6 -0
- package/.agent/mcp-gateway/dist/sync/index.js +6 -0
- package/.agent/mcp-gateway/dist/sync/timestamp_checker.d.ts +69 -0
- package/.agent/mcp-gateway/dist/sync/timestamp_checker.js +169 -0
- package/.agent/mcp-gateway/package.json +28 -0
- package/.agent/mcp-gateway/src/index.ts +608 -0
- package/.agent/mcp-gateway/src/sync/debouncer.ts +129 -0
- package/.agent/mcp-gateway/src/sync/incremental_syncer.ts +237 -0
- package/.agent/mcp-gateway/src/sync/index.ts +7 -0
- package/.agent/mcp-gateway/src/sync/timestamp_checker.ts +194 -0
- package/.agent/scripts/ak_cli.py +549 -0
- package/.agent/scripts/setup_host.py +557 -0
- package/.agent/scripts/verify_install.py +174 -0
- package/.agent/skills/app-builder/SKILL.md +51 -1
- package/.agent/skills/app-builder/scripts/generate_ai_infra.py +510 -0
- package/.agent/skills/documentation-templates/SKILL.md +9 -1
- package/.agent/skills/documentation-templates/agents-template.md +202 -0
- package/.agent/skills/graph-mapper/SKILL.md +211 -0
- package/.agent/skills/graph-mapper/scripts/generate_graph.py +705 -0
- package/.agent/skills/rag-engineering/SKILL.md +342 -0
- package/.agent/skills/rag-engineering/chunking-strategies.md +229 -0
- package/.agent/skills/rag-engineering/contextual-retrieval.md +261 -0
- package/.agent/skills/rag-engineering/hybrid-search.md +356 -0
- package/.agent/skills/rag-engineering/scripts/chunk_code.py +916 -0
- package/.agent/templates/mcp_configs/claude_desktop.json +14 -0
- package/.agent/templates/mcp_configs/cursor.json +13 -0
- package/.agent/templates/mcp_configs/vscode.json +13 -0
- package/.agent/workflows/create.md +70 -2
- package/bin/cli.js +91 -0
- package/docs/AI_DATA_INFRASTRUCTURE.md +288 -0
- package/docs/CHANGELOG_AI_INFRA.md +111 -0
- package/docs/PLAN-universal-intelligence.md +48 -0
- package/package.json +7 -2
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debouncer
|
|
3
|
+
*
|
|
4
|
+
* Prevents excessive syncing by enforcing a cooldown period
|
|
5
|
+
* between sync operations. Essential for performance when
|
|
6
|
+
* AI makes many rapid queries.
|
|
7
|
+
*/
|
|
8
|
+
export class Debouncer {
|
|
9
|
+
lastSync = 0;
|
|
10
|
+
cooldownMs;
|
|
11
|
+
pendingSync = null;
|
|
12
|
+
/**
|
|
13
|
+
* @param cooldownMs Minimum time between syncs (default: 30 seconds)
|
|
14
|
+
*/
|
|
15
|
+
constructor(cooldownMs = 30000) {
|
|
16
|
+
this.cooldownMs = cooldownMs;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Check if enough time has passed since last sync
|
|
20
|
+
*/
|
|
21
|
+
canSync() {
|
|
22
|
+
const now = Date.now();
|
|
23
|
+
return now - this.lastSync >= this.cooldownMs;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get remaining cooldown time in milliseconds
|
|
27
|
+
*/
|
|
28
|
+
getRemainingCooldown() {
|
|
29
|
+
const elapsed = Date.now() - this.lastSync;
|
|
30
|
+
const remaining = this.cooldownMs - elapsed;
|
|
31
|
+
return Math.max(0, remaining);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get remaining cooldown time in human-readable format
|
|
35
|
+
*/
|
|
36
|
+
getRemainingCooldownFormatted() {
|
|
37
|
+
const remaining = this.getRemainingCooldown();
|
|
38
|
+
if (remaining === 0)
|
|
39
|
+
return "Ready";
|
|
40
|
+
return `${Math.ceil(remaining / 1000)}s`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Mark that a sync has occurred
|
|
44
|
+
*/
|
|
45
|
+
markSynced() {
|
|
46
|
+
this.lastSync = Date.now();
|
|
47
|
+
this.pendingSync = null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Execute a sync operation with debouncing
|
|
51
|
+
*
|
|
52
|
+
* @param syncFn The sync function to execute
|
|
53
|
+
* @returns Result of sync or null if debounced
|
|
54
|
+
*/
|
|
55
|
+
async executeWithDebounce(syncFn) {
|
|
56
|
+
if (!this.canSync()) {
|
|
57
|
+
return {
|
|
58
|
+
executed: false,
|
|
59
|
+
reason: `Cooldown active. Next sync available in ${this.getRemainingCooldownFormatted()}`,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
// Prevent concurrent syncs
|
|
63
|
+
if (this.pendingSync) {
|
|
64
|
+
return {
|
|
65
|
+
executed: false,
|
|
66
|
+
reason: "Sync already in progress",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
this.pendingSync = Promise.resolve();
|
|
71
|
+
const result = await syncFn();
|
|
72
|
+
this.markSynced();
|
|
73
|
+
return { executed: true, result };
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
this.pendingSync = null;
|
|
77
|
+
return {
|
|
78
|
+
executed: false,
|
|
79
|
+
reason: `Sync failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Force reset the debouncer (for manual sync commands)
|
|
85
|
+
*/
|
|
86
|
+
reset() {
|
|
87
|
+
this.lastSync = 0;
|
|
88
|
+
this.pendingSync = null;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get debouncer status
|
|
92
|
+
*/
|
|
93
|
+
getStatus() {
|
|
94
|
+
return {
|
|
95
|
+
lastSync: this.lastSync > 0
|
|
96
|
+
? new Date(this.lastSync).toISOString()
|
|
97
|
+
: "Never",
|
|
98
|
+
canSync: this.canSync(),
|
|
99
|
+
cooldownRemaining: this.getRemainingCooldownFormatted(),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Singleton debouncer instance for global use
|
|
105
|
+
*/
|
|
106
|
+
let globalDebouncer = null;
|
|
107
|
+
export function getGlobalDebouncer(cooldownMs) {
|
|
108
|
+
if (!globalDebouncer) {
|
|
109
|
+
globalDebouncer = new Debouncer(cooldownMs);
|
|
110
|
+
}
|
|
111
|
+
return globalDebouncer;
|
|
112
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental Syncer
|
|
3
|
+
*
|
|
4
|
+
* Updates only the changed files in the AI infrastructure
|
|
5
|
+
* instead of regenerating everything from scratch.
|
|
6
|
+
*/
|
|
7
|
+
import { TimestampChecker } from "./timestamp_checker.js";
|
|
8
|
+
export interface SyncResult {
|
|
9
|
+
success: boolean;
|
|
10
|
+
target: "graph" | "rag" | "all";
|
|
11
|
+
filesUpdated: string[];
|
|
12
|
+
duration: number;
|
|
13
|
+
error?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare class IncrementalSyncer {
|
|
16
|
+
private projectRoot;
|
|
17
|
+
private kitPath;
|
|
18
|
+
private timestampChecker;
|
|
19
|
+
constructor(projectRoot: string, kitPath?: string);
|
|
20
|
+
/**
|
|
21
|
+
* Detect Agent Kit installation path
|
|
22
|
+
*/
|
|
23
|
+
private detectKitPath;
|
|
24
|
+
/**
|
|
25
|
+
* Run a Python script and return output
|
|
26
|
+
*/
|
|
27
|
+
private runPythonScript;
|
|
28
|
+
/**
|
|
29
|
+
* Sync dependency graph (full or incremental)
|
|
30
|
+
*/
|
|
31
|
+
syncGraph(changedFiles?: string[]): Promise<SyncResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Sync RAG chunks (full or incremental)
|
|
34
|
+
*/
|
|
35
|
+
syncRag(changedFiles?: string[]): Promise<SyncResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Sync all AI infrastructure
|
|
38
|
+
*/
|
|
39
|
+
syncAll(): Promise<{
|
|
40
|
+
graph: SyncResult;
|
|
41
|
+
rag: SyncResult;
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* Check if sync is needed and perform if necessary
|
|
45
|
+
*/
|
|
46
|
+
syncIfNeeded(): Promise<{
|
|
47
|
+
synced: boolean;
|
|
48
|
+
reason?: string;
|
|
49
|
+
results?: {
|
|
50
|
+
graph: SyncResult;
|
|
51
|
+
rag: SyncResult;
|
|
52
|
+
};
|
|
53
|
+
}>;
|
|
54
|
+
/**
|
|
55
|
+
* Get sync status
|
|
56
|
+
*/
|
|
57
|
+
getSyncStatus(): ReturnType<TimestampChecker["getSyncStatus"]>;
|
|
58
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Incremental Syncer
|
|
3
|
+
*
|
|
4
|
+
* Updates only the changed files in the AI infrastructure
|
|
5
|
+
* instead of regenerating everything from scratch.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
import { spawn } from "child_process";
|
|
10
|
+
import { TimestampChecker } from "./timestamp_checker.js";
|
|
11
|
+
export class IncrementalSyncer {
|
|
12
|
+
projectRoot;
|
|
13
|
+
kitPath;
|
|
14
|
+
timestampChecker;
|
|
15
|
+
constructor(projectRoot, kitPath) {
|
|
16
|
+
this.projectRoot = projectRoot;
|
|
17
|
+
this.kitPath = kitPath || this.detectKitPath();
|
|
18
|
+
this.timestampChecker = new TimestampChecker(projectRoot);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Detect Agent Kit installation path
|
|
22
|
+
*/
|
|
23
|
+
detectKitPath() {
|
|
24
|
+
// Check environment variable first
|
|
25
|
+
if (process.env.AGENT_KIT_PATH) {
|
|
26
|
+
return process.env.AGENT_KIT_PATH;
|
|
27
|
+
}
|
|
28
|
+
// Check relative to this file (when running from kit)
|
|
29
|
+
const relativePath = path.join(__dirname, "..", "..", "..");
|
|
30
|
+
if (fs.existsSync(path.join(relativePath, "skills"))) {
|
|
31
|
+
return relativePath;
|
|
32
|
+
}
|
|
33
|
+
// Check in project's .agent directory
|
|
34
|
+
const projectAgentPath = path.join(this.projectRoot, ".agent");
|
|
35
|
+
if (fs.existsSync(path.join(projectAgentPath, "skills"))) {
|
|
36
|
+
return projectAgentPath;
|
|
37
|
+
}
|
|
38
|
+
// Fallback to node_modules
|
|
39
|
+
const nodeModulesPath = path.join(this.projectRoot, "node_modules", "@musashishao", "agent-kit", ".agent");
|
|
40
|
+
return nodeModulesPath;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Run a Python script and return output
|
|
44
|
+
*/
|
|
45
|
+
async runPythonScript(scriptPath, args) {
|
|
46
|
+
return new Promise((resolve) => {
|
|
47
|
+
if (!fs.existsSync(scriptPath)) {
|
|
48
|
+
resolve({ success: false, output: `Script not found: ${scriptPath}` });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const proc = spawn("python3", [scriptPath, ...args], {
|
|
52
|
+
cwd: this.projectRoot,
|
|
53
|
+
env: { ...process.env, PYTHONUNBUFFERED: "1" },
|
|
54
|
+
});
|
|
55
|
+
let output = "";
|
|
56
|
+
let error = "";
|
|
57
|
+
proc.stdout.on("data", (data) => {
|
|
58
|
+
output += data.toString();
|
|
59
|
+
});
|
|
60
|
+
proc.stderr.on("data", (data) => {
|
|
61
|
+
error += data.toString();
|
|
62
|
+
});
|
|
63
|
+
proc.on("close", (code) => {
|
|
64
|
+
resolve({
|
|
65
|
+
success: code === 0,
|
|
66
|
+
output: code === 0 ? output : error || output,
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
proc.on("error", (err) => {
|
|
70
|
+
resolve({ success: false, output: `Failed to run script: ${err.message}` });
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Sync dependency graph (full or incremental)
|
|
76
|
+
*/
|
|
77
|
+
async syncGraph(changedFiles) {
|
|
78
|
+
const startTime = Date.now();
|
|
79
|
+
const scriptPath = path.join(this.kitPath, "skills", "graph-mapper", "scripts", "generate_graph.py");
|
|
80
|
+
// Determine source directory
|
|
81
|
+
let sourceDir = "src";
|
|
82
|
+
if (fs.existsSync(path.join(this.projectRoot, "app"))) {
|
|
83
|
+
sourceDir = "app";
|
|
84
|
+
}
|
|
85
|
+
const args = [
|
|
86
|
+
"--src",
|
|
87
|
+
path.join(this.projectRoot, sourceDir),
|
|
88
|
+
"--output",
|
|
89
|
+
path.join(this.projectRoot, ".agent", "graph.json"),
|
|
90
|
+
"--format",
|
|
91
|
+
"both",
|
|
92
|
+
];
|
|
93
|
+
const result = await this.runPythonScript(scriptPath, args);
|
|
94
|
+
if (result.success) {
|
|
95
|
+
// Update timestamps for synced files
|
|
96
|
+
if (changedFiles && changedFiles.length > 0) {
|
|
97
|
+
this.timestampChecker.updateFileTimestamps(changedFiles);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
this.timestampChecker.markAllSynced(sourceDir);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
success: result.success,
|
|
105
|
+
target: "graph",
|
|
106
|
+
filesUpdated: changedFiles || [],
|
|
107
|
+
duration: Date.now() - startTime,
|
|
108
|
+
error: result.success ? undefined : result.output,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Sync RAG chunks (full or incremental)
|
|
113
|
+
*/
|
|
114
|
+
async syncRag(changedFiles) {
|
|
115
|
+
const startTime = Date.now();
|
|
116
|
+
const scriptPath = path.join(this.kitPath, "skills", "rag-engineering", "scripts", "chunk_code.py");
|
|
117
|
+
// Determine source directory
|
|
118
|
+
let sourceDir = "src";
|
|
119
|
+
if (fs.existsSync(path.join(this.projectRoot, "app"))) {
|
|
120
|
+
sourceDir = "app";
|
|
121
|
+
}
|
|
122
|
+
const args = [
|
|
123
|
+
"--src",
|
|
124
|
+
path.join(this.projectRoot, sourceDir),
|
|
125
|
+
"--output",
|
|
126
|
+
path.join(this.projectRoot, ".agent", "rag", "chunks.json"),
|
|
127
|
+
];
|
|
128
|
+
const result = await this.runPythonScript(scriptPath, args);
|
|
129
|
+
return {
|
|
130
|
+
success: result.success,
|
|
131
|
+
target: "rag",
|
|
132
|
+
filesUpdated: changedFiles || [],
|
|
133
|
+
duration: Date.now() - startTime,
|
|
134
|
+
error: result.success ? undefined : result.output,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Sync all AI infrastructure
|
|
139
|
+
*/
|
|
140
|
+
async syncAll() {
|
|
141
|
+
const changedFiles = this.timestampChecker.getChangedFiles();
|
|
142
|
+
const [graphResult, ragResult] = await Promise.all([
|
|
143
|
+
this.syncGraph(changedFiles),
|
|
144
|
+
this.syncRag(changedFiles),
|
|
145
|
+
]);
|
|
146
|
+
return { graph: graphResult, rag: ragResult };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Check if sync is needed and perform if necessary
|
|
150
|
+
*/
|
|
151
|
+
async syncIfNeeded() {
|
|
152
|
+
const status = this.timestampChecker.getSyncStatus();
|
|
153
|
+
if (!status.needsSync) {
|
|
154
|
+
return {
|
|
155
|
+
synced: false,
|
|
156
|
+
reason: "No changes detected since last sync",
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
const results = await this.syncAll();
|
|
160
|
+
return {
|
|
161
|
+
synced: true,
|
|
162
|
+
reason: `${status.changedFiles} files changed`,
|
|
163
|
+
results,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Get sync status
|
|
168
|
+
*/
|
|
169
|
+
getSyncStatus() {
|
|
170
|
+
return this.timestampChecker.getSyncStatus();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timestamp Checker
|
|
3
|
+
*
|
|
4
|
+
* Tracks file modification times to detect when source code
|
|
5
|
+
* has changed since the last sync of AI infrastructure data.
|
|
6
|
+
*/
|
|
7
|
+
export interface TimestampCache {
|
|
8
|
+
lastSync: number;
|
|
9
|
+
files: Record<string, number>;
|
|
10
|
+
}
|
|
11
|
+
export declare class TimestampChecker {
|
|
12
|
+
private cachePath;
|
|
13
|
+
private cache;
|
|
14
|
+
private projectRoot;
|
|
15
|
+
constructor(projectRoot: string);
|
|
16
|
+
/**
|
|
17
|
+
* Load timestamp cache from disk
|
|
18
|
+
*/
|
|
19
|
+
private loadCache;
|
|
20
|
+
/**
|
|
21
|
+
* Save timestamp cache to disk
|
|
22
|
+
*/
|
|
23
|
+
saveCache(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Get modification time of a file
|
|
26
|
+
*/
|
|
27
|
+
private getFileModTime;
|
|
28
|
+
/**
|
|
29
|
+
* Check if a specific file has changed since last sync
|
|
30
|
+
*/
|
|
31
|
+
hasFileChanged(filePath: string): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Check if any source files have changed
|
|
34
|
+
*/
|
|
35
|
+
hasAnyChanges(sourceDir?: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Get list of all changed files since last sync
|
|
38
|
+
*/
|
|
39
|
+
getChangedFiles(sourceDir?: string): string[];
|
|
40
|
+
/**
|
|
41
|
+
* Get all source files in a directory
|
|
42
|
+
*/
|
|
43
|
+
private getAllSourceFiles;
|
|
44
|
+
/**
|
|
45
|
+
* Update timestamp for a file
|
|
46
|
+
*/
|
|
47
|
+
updateFileTimestamp(filePath: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Update timestamps for multiple files
|
|
50
|
+
*/
|
|
51
|
+
updateFileTimestamps(files: string[]): void;
|
|
52
|
+
/**
|
|
53
|
+
* Mark all current files as synced
|
|
54
|
+
*/
|
|
55
|
+
markAllSynced(sourceDir?: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Get last sync time
|
|
58
|
+
*/
|
|
59
|
+
getLastSyncTime(): number;
|
|
60
|
+
/**
|
|
61
|
+
* Get human-readable sync status
|
|
62
|
+
*/
|
|
63
|
+
getSyncStatus(sourceDir?: string): {
|
|
64
|
+
lastSync: string;
|
|
65
|
+
totalFiles: number;
|
|
66
|
+
changedFiles: number;
|
|
67
|
+
needsSync: boolean;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timestamp Checker
|
|
3
|
+
*
|
|
4
|
+
* Tracks file modification times to detect when source code
|
|
5
|
+
* has changed since the last sync of AI infrastructure data.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
export class TimestampChecker {
|
|
10
|
+
cachePath;
|
|
11
|
+
cache;
|
|
12
|
+
projectRoot;
|
|
13
|
+
constructor(projectRoot) {
|
|
14
|
+
this.projectRoot = projectRoot;
|
|
15
|
+
this.cachePath = path.join(projectRoot, ".agent", ".cache", "timestamps.json");
|
|
16
|
+
this.cache = this.loadCache();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Load timestamp cache from disk
|
|
20
|
+
*/
|
|
21
|
+
loadCache() {
|
|
22
|
+
try {
|
|
23
|
+
if (fs.existsSync(this.cachePath)) {
|
|
24
|
+
const data = fs.readFileSync(this.cachePath, "utf-8");
|
|
25
|
+
return JSON.parse(data);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
console.error("Failed to load timestamp cache:", error);
|
|
30
|
+
}
|
|
31
|
+
return { lastSync: 0, files: {} };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Save timestamp cache to disk
|
|
35
|
+
*/
|
|
36
|
+
saveCache() {
|
|
37
|
+
try {
|
|
38
|
+
const cacheDir = path.dirname(this.cachePath);
|
|
39
|
+
if (!fs.existsSync(cacheDir)) {
|
|
40
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
fs.writeFileSync(this.cachePath, JSON.stringify(this.cache, null, 2));
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
console.error("Failed to save timestamp cache:", error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Get modification time of a file
|
|
50
|
+
*/
|
|
51
|
+
getFileModTime(filePath) {
|
|
52
|
+
try {
|
|
53
|
+
const fullPath = path.resolve(this.projectRoot, filePath);
|
|
54
|
+
const stats = fs.statSync(fullPath);
|
|
55
|
+
return stats.mtimeMs;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if a specific file has changed since last sync
|
|
63
|
+
*/
|
|
64
|
+
hasFileChanged(filePath) {
|
|
65
|
+
const currentMtime = this.getFileModTime(filePath);
|
|
66
|
+
const cachedMtime = this.cache.files[filePath] || 0;
|
|
67
|
+
return currentMtime > cachedMtime;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check if any source files have changed
|
|
71
|
+
*/
|
|
72
|
+
hasAnyChanges(sourceDir = "src") {
|
|
73
|
+
const files = this.getAllSourceFiles(sourceDir);
|
|
74
|
+
for (const file of files) {
|
|
75
|
+
if (this.hasFileChanged(file)) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get list of all changed files since last sync
|
|
83
|
+
*/
|
|
84
|
+
getChangedFiles(sourceDir = "src") {
|
|
85
|
+
const files = this.getAllSourceFiles(sourceDir);
|
|
86
|
+
return files.filter((file) => this.hasFileChanged(file));
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Get all source files in a directory
|
|
90
|
+
*/
|
|
91
|
+
getAllSourceFiles(dir) {
|
|
92
|
+
const fullDir = path.resolve(this.projectRoot, dir);
|
|
93
|
+
if (!fs.existsSync(fullDir)) {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
const files = [];
|
|
97
|
+
const extensions = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".go"]);
|
|
98
|
+
const excludeDirs = new Set(["node_modules", "__pycache__", ".git", "dist", "build", ".next"]);
|
|
99
|
+
const walk = (currentDir) => {
|
|
100
|
+
try {
|
|
101
|
+
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
102
|
+
for (const entry of entries) {
|
|
103
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
104
|
+
const relativePath = path.relative(this.projectRoot, fullPath);
|
|
105
|
+
if (entry.isDirectory()) {
|
|
106
|
+
if (!excludeDirs.has(entry.name)) {
|
|
107
|
+
walk(fullPath);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else if (entry.isFile()) {
|
|
111
|
+
const ext = path.extname(entry.name);
|
|
112
|
+
if (extensions.has(ext)) {
|
|
113
|
+
files.push(relativePath);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
// Skip directories we can't read
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
walk(fullDir);
|
|
123
|
+
return files;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Update timestamp for a file
|
|
127
|
+
*/
|
|
128
|
+
updateFileTimestamp(filePath) {
|
|
129
|
+
this.cache.files[filePath] = this.getFileModTime(filePath);
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Update timestamps for multiple files
|
|
133
|
+
*/
|
|
134
|
+
updateFileTimestamps(files) {
|
|
135
|
+
for (const file of files) {
|
|
136
|
+
this.updateFileTimestamp(file);
|
|
137
|
+
}
|
|
138
|
+
this.cache.lastSync = Date.now();
|
|
139
|
+
this.saveCache();
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Mark all current files as synced
|
|
143
|
+
*/
|
|
144
|
+
markAllSynced(sourceDir = "src") {
|
|
145
|
+
const files = this.getAllSourceFiles(sourceDir);
|
|
146
|
+
this.updateFileTimestamps(files);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get last sync time
|
|
150
|
+
*/
|
|
151
|
+
getLastSyncTime() {
|
|
152
|
+
return this.cache.lastSync;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Get human-readable sync status
|
|
156
|
+
*/
|
|
157
|
+
getSyncStatus(sourceDir = "src") {
|
|
158
|
+
const allFiles = this.getAllSourceFiles(sourceDir);
|
|
159
|
+
const changedFiles = this.getChangedFiles(sourceDir);
|
|
160
|
+
return {
|
|
161
|
+
lastSync: this.cache.lastSync > 0
|
|
162
|
+
? new Date(this.cache.lastSync).toISOString()
|
|
163
|
+
: "Never",
|
|
164
|
+
totalFiles: allFiles.length,
|
|
165
|
+
changedFiles: changedFiles.length,
|
|
166
|
+
needsSync: changedFiles.length > 0,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@musashishao/agent-kit-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP Gateway Server for Agent Kit - Live Project Cortex",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"agent-kit-mcp": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx src/index.ts",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"watch": "tsc --watch"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
18
|
+
"zod": "^3.23.0"
|
|
19
|
+
},
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@types/node": "^20.0.0",
|
|
22
|
+
"tsx": "^4.0.0",
|
|
23
|
+
"typescript": "^5.0.0"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18.0.0"
|
|
27
|
+
}
|
|
28
|
+
}
|