atabey 0.0.7 → 0.0.8
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 +1 -1
- package/dist/src/cli/adapters/core.js +12 -19
- package/dist/src/cli/adapters/core.js.map +1 -1
- package/dist/src/shared/constants.d.ts +1 -0
- package/dist/src/shared/constants.js +1 -0
- package/dist/src/shared/constants.js.map +1 -1
- package/dist/tests/adapter.test.js +3 -2
- package/dist/tests/adapter.test.js.map +1 -1
- package/framework-mcp/dist/constants.js +64 -0
- package/framework-mcp/dist/framework-mcp/src/constants.js +64 -0
- package/framework-mcp/dist/framework-mcp/src/index.js +144 -0
- package/framework-mcp/dist/framework-mcp/src/resources/index.js +58 -0
- package/framework-mcp/dist/framework-mcp/src/tools/control_plane/locking.js +82 -0
- package/framework-mcp/dist/framework-mcp/src/tools/control_plane/registry.js +35 -0
- package/framework-mcp/dist/framework-mcp/src/tools/definitions.js +322 -0
- package/framework-mcp/dist/framework-mcp/src/tools/file_system/batch_surgical_edit.js +64 -0
- package/framework-mcp/dist/framework-mcp/src/tools/file_system/patch_file.js +34 -0
- package/framework-mcp/dist/framework-mcp/src/tools/file_system/read_file.js +51 -0
- package/framework-mcp/dist/framework-mcp/src/tools/file_system/replace_text.js +50 -0
- package/framework-mcp/dist/framework-mcp/src/tools/file_system/write_file.js +43 -0
- package/framework-mcp/dist/framework-mcp/src/tools/framework/audit_deps.js +41 -0
- package/framework-mcp/dist/framework-mcp/src/tools/framework/get_status.js +5 -0
- package/framework-mcp/dist/framework-mcp/src/tools/framework/orchestrate.js +5 -0
- package/framework-mcp/dist/framework-mcp/src/tools/framework/run_tests.js +27 -0
- package/framework-mcp/dist/framework-mcp/src/tools/framework/submit_plan.js +13 -0
- package/framework-mcp/dist/framework-mcp/src/tools/framework/update_contract_hash.js +5 -0
- package/framework-mcp/dist/framework-mcp/src/tools/framework/update_memory.js +8 -0
- package/framework-mcp/dist/framework-mcp/src/tools/index.js +62 -0
- package/framework-mcp/dist/framework-mcp/src/tools/memory/get_insights.js +34 -0
- package/framework-mcp/dist/framework-mcp/src/tools/memory/read_memory.js +28 -0
- package/framework-mcp/dist/framework-mcp/src/tools/messaging/log_action.js +22 -0
- package/framework-mcp/dist/framework-mcp/src/tools/messaging/send_message.js +94 -0
- package/framework-mcp/dist/framework-mcp/src/tools/observability/check_ports.js +26 -0
- package/framework-mcp/dist/framework-mcp/src/tools/observability/get_health.js +19 -0
- package/framework-mcp/dist/framework-mcp/src/tools/quality/check_lint.js +30 -0
- package/framework-mcp/dist/framework-mcp/src/tools/search/get_gaps.js +48 -0
- package/framework-mcp/dist/framework-mcp/src/tools/search/get_map.js +43 -0
- package/framework-mcp/dist/framework-mcp/src/tools/search/grep_search.js +75 -0
- package/framework-mcp/dist/framework-mcp/src/tools/search/list_dir.js +28 -0
- package/framework-mcp/dist/framework-mcp/src/tools/shell/run_command.js +56 -0
- package/framework-mcp/dist/framework-mcp/src/tools/types.js +1 -0
- package/framework-mcp/dist/framework-mcp/src/utils/cli.js +59 -0
- package/framework-mcp/dist/framework-mcp/src/utils/compliance.js +231 -0
- package/framework-mcp/dist/framework-mcp/src/utils/fs.js +44 -0
- package/framework-mcp/dist/framework-mcp/src/utils/metrics.js +56 -0
- package/framework-mcp/dist/framework-mcp/src/utils/permissions.js +71 -0
- package/framework-mcp/dist/framework-mcp/src/utils/security.js +60 -0
- package/framework-mcp/dist/index.js +144 -0
- package/framework-mcp/dist/resources/index.js +58 -0
- package/framework-mcp/dist/src/cli/adapters/core.js +71 -0
- package/framework-mcp/dist/src/cli/adapters/index.js +5 -0
- package/framework-mcp/dist/src/cli/adapters/paths.js +101 -0
- package/framework-mcp/dist/src/cli/adapters/scaffold.js +71 -0
- package/framework-mcp/dist/src/cli/adapters/utils.js +75 -0
- package/framework-mcp/dist/src/cli/commands/approve.js +63 -0
- package/framework-mcp/dist/src/cli/commands/check.js +181 -0
- package/framework-mcp/dist/src/cli/commands/compliance.js +50 -0
- package/framework-mcp/dist/src/cli/commands/contract.js +50 -0
- package/framework-mcp/dist/src/cli/commands/dashboard.js +123 -0
- package/framework-mcp/dist/src/cli/commands/explorer.js +42 -0
- package/framework-mcp/dist/src/cli/commands/git.js +40 -0
- package/framework-mcp/dist/src/cli/commands/init/create-agent.js +58 -0
- package/framework-mcp/dist/src/cli/commands/init/scaffold-core.js +112 -0
- package/framework-mcp/dist/src/cli/commands/init/scaffold-docs.js +34 -0
- package/framework-mcp/dist/src/cli/commands/init/scaffold-ops.js +80 -0
- package/framework-mcp/dist/src/cli/commands/init/scaffold-standards.js +67 -0
- package/framework-mcp/dist/src/cli/commands/init.js +167 -0
- package/framework-mcp/dist/src/cli/commands/knowledge.js +42 -0
- package/framework-mcp/dist/src/cli/commands/lint.js +22 -0
- package/framework-mcp/dist/src/cli/commands/log.js +10 -0
- package/framework-mcp/dist/src/cli/commands/memory.js +4 -0
- package/framework-mcp/dist/src/cli/commands/orchestrate.js +159 -0
- package/framework-mcp/dist/src/cli/commands/plan.js +117 -0
- package/framework-mcp/dist/src/cli/commands/script.js +19 -0
- package/framework-mcp/dist/src/cli/commands/security.js +36 -0
- package/framework-mcp/dist/src/cli/commands/status.js +97 -0
- package/framework-mcp/dist/src/cli/commands/trace.js +109 -0
- package/framework-mcp/dist/src/cli/index.js +338 -0
- package/framework-mcp/dist/src/cli/shims.js +66 -0
- package/framework-mcp/dist/src/cli/utils/claude.js +56 -0
- package/framework-mcp/dist/src/cli/utils/compliance.js +173 -0
- package/framework-mcp/dist/src/cli/utils/config-schema.js +42 -0
- package/framework-mcp/dist/src/cli/utils/fs.js +137 -0
- package/framework-mcp/dist/src/cli/utils/i18n.js +30 -0
- package/framework-mcp/dist/src/cli/utils/memory.js +276 -0
- package/framework-mcp/dist/src/cli/utils/pkg.js +282 -0
- package/framework-mcp/dist/src/cli/utils/schemas.js +19 -0
- package/framework-mcp/dist/src/cli/utils/string.js +49 -0
- package/framework-mcp/dist/src/cli/utils/time.js +27 -0
- package/framework-mcp/dist/src/cli/utils/ui.js +58 -0
- package/framework-mcp/dist/src/contracts/index.js +1 -0
- package/framework-mcp/dist/src/contracts/tasks.js +20 -0
- package/framework-mcp/dist/src/dashboard/vite.config.js +15 -0
- package/framework-mcp/dist/src/modules/adapters/definitions.js +140 -0
- package/framework-mcp/dist/src/modules/adapters/registry.js +18 -0
- package/framework-mcp/dist/src/modules/adapters/shared.js +104 -0
- package/framework-mcp/dist/src/modules/adapters/types.js +1 -0
- package/framework-mcp/dist/src/modules/agents/definitions.js +457 -0
- package/framework-mcp/dist/src/modules/agents/registry/analyst.js +39 -0
- package/framework-mcp/dist/src/modules/agents/registry/architect.js +42 -0
- package/framework-mcp/dist/src/modules/agents/registry/backend.js +49 -0
- package/framework-mcp/dist/src/modules/agents/registry/database.js +45 -0
- package/framework-mcp/dist/src/modules/agents/registry/devops.js +45 -0
- package/framework-mcp/dist/src/modules/agents/registry/explorer.js +36 -0
- package/framework-mcp/dist/src/modules/agents/registry/frontend.js +51 -0
- package/framework-mcp/dist/src/modules/agents/registry/git.js +36 -0
- package/framework-mcp/dist/src/modules/agents/registry/manager.js +53 -0
- package/framework-mcp/dist/src/modules/agents/registry/mobile.js +39 -0
- package/framework-mcp/dist/src/modules/agents/registry/native.js +39 -0
- package/framework-mcp/dist/src/modules/agents/registry/quality.js +41 -0
- package/framework-mcp/dist/src/modules/agents/registry/security.js +43 -0
- package/framework-mcp/dist/src/modules/agents/types.js +1 -0
- package/framework-mcp/dist/src/modules/engines/evaluation-engine.js +102 -0
- package/framework-mcp/dist/src/modules/engines/health-engine.js +49 -0
- package/framework-mcp/dist/src/modules/engines/planning-engine.js +78 -0
- package/framework-mcp/dist/src/modules/engines/risk-engine.js +105 -0
- package/framework-mcp/dist/src/modules/engines/routing-engine.js +73 -0
- package/framework-mcp/dist/src/modules/engines/types.js +1 -0
- package/framework-mcp/dist/src/modules/skills/definitions.js +70 -0
- package/framework-mcp/dist/src/shared/constants.js +187 -0
- package/framework-mcp/dist/src/shared/errors.js +68 -0
- package/framework-mcp/dist/src/shared/fs.js +51 -0
- package/framework-mcp/dist/src/shared/logger.js +116 -0
- package/framework-mcp/dist/src/shared/storage.js +207 -0
- package/framework-mcp/dist/src/shared/types.js +12 -0
- package/framework-mcp/dist/tools/control_plane/locking.js +82 -0
- package/framework-mcp/dist/tools/control_plane/registry.js +35 -0
- package/framework-mcp/dist/tools/definitions.js +322 -0
- package/framework-mcp/dist/tools/file_system/batch_surgical_edit.js +64 -0
- package/framework-mcp/dist/tools/file_system/patch_file.js +34 -0
- package/framework-mcp/dist/tools/file_system/read_file.js +51 -0
- package/framework-mcp/dist/tools/file_system/replace_text.js +50 -0
- package/framework-mcp/dist/tools/file_system/write_file.js +43 -0
- package/framework-mcp/dist/tools/framework/audit_deps.js +41 -0
- package/framework-mcp/dist/tools/framework/get_status.js +5 -0
- package/framework-mcp/dist/tools/framework/orchestrate.js +5 -0
- package/framework-mcp/dist/tools/framework/run_tests.js +27 -0
- package/framework-mcp/dist/tools/framework/submit_plan.js +13 -0
- package/framework-mcp/dist/tools/framework/update_contract_hash.js +5 -0
- package/framework-mcp/dist/tools/framework/update_memory.js +8 -0
- package/framework-mcp/dist/tools/index.js +62 -0
- package/framework-mcp/dist/tools/memory/get_insights.js +34 -0
- package/framework-mcp/dist/tools/memory/read_memory.js +28 -0
- package/framework-mcp/dist/tools/messaging/log_action.js +22 -0
- package/framework-mcp/dist/tools/messaging/send_message.js +94 -0
- package/framework-mcp/dist/tools/observability/check_ports.js +26 -0
- package/framework-mcp/dist/tools/observability/get_health.js +19 -0
- package/framework-mcp/dist/tools/quality/check_lint.js +30 -0
- package/framework-mcp/dist/tools/search/get_gaps.js +48 -0
- package/framework-mcp/dist/tools/search/get_map.js +43 -0
- package/framework-mcp/dist/tools/search/grep_search.js +75 -0
- package/framework-mcp/dist/tools/search/list_dir.js +28 -0
- package/framework-mcp/dist/tools/shell/run_command.js +56 -0
- package/framework-mcp/dist/tools/types.js +1 -0
- package/framework-mcp/dist/utils/cli.js +59 -0
- package/framework-mcp/dist/utils/compliance.js +231 -0
- package/framework-mcp/dist/utils/fs.js +44 -0
- package/framework-mcp/dist/utils/metrics.js +56 -0
- package/framework-mcp/dist/utils/permissions.js +71 -0
- package/framework-mcp/dist/utils/security.js +60 -0
- package/framework-mcp/package.json +35 -0
- package/mcp.json +1 -1
- package/package.json +6 -3
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import ts from "typescript";
|
|
4
|
+
/**
|
|
5
|
+
* Enterprise Compliance Scanner (AST-Based)
|
|
6
|
+
* Scans the project source code for governance and discipline violations.
|
|
7
|
+
*/
|
|
8
|
+
export function scanProjectCompliance(targetDir = "src") {
|
|
9
|
+
const issues = [];
|
|
10
|
+
if (!fs.existsSync(targetDir))
|
|
11
|
+
return [];
|
|
12
|
+
const files = getAllFiles(targetDir);
|
|
13
|
+
for (const file of files) {
|
|
14
|
+
// Absolute skip for critical framework and utility files to avoid self-flagging
|
|
15
|
+
if (file.includes("compliance") ||
|
|
16
|
+
file.includes("definitions") ||
|
|
17
|
+
file.includes("agents/registry") ||
|
|
18
|
+
file.includes("scaffold-ops.ts") ||
|
|
19
|
+
file.includes("logger") ||
|
|
20
|
+
file.includes("errors") ||
|
|
21
|
+
file.includes("shared/fs") ||
|
|
22
|
+
file.includes("commands/git") ||
|
|
23
|
+
file.includes("commands/lint") ||
|
|
24
|
+
file.includes("commands/script") ||
|
|
25
|
+
file.includes("commands/check") ||
|
|
26
|
+
file.includes("cli/index.ts") ||
|
|
27
|
+
file.includes("adapters/shared"))
|
|
28
|
+
continue;
|
|
29
|
+
const content = fs.readFileSync(file, "utf8");
|
|
30
|
+
// --- 1. AST-Based Analysis (For Language Rules) ---
|
|
31
|
+
const sourceFile = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true);
|
|
32
|
+
const visit = (node) => {
|
|
33
|
+
// Rule: No console.log allowed (excluding UI and warn/error helpers)
|
|
34
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
35
|
+
const expression = node.expression;
|
|
36
|
+
const name = node.name.text;
|
|
37
|
+
if (ts.isIdentifier(expression) && expression.text === "console" && name === "log") {
|
|
38
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
39
|
+
issues.push({ file, line: line + 1, rule: "No console.log allowed (Use logger instead)" });
|
|
40
|
+
}
|
|
41
|
+
// Rule: innerHTML / outerHTML check
|
|
42
|
+
if (name === "innerHTML" || name === "outerHTML") {
|
|
43
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
44
|
+
issues.push({ file, line: line + 1, rule: `Unsafe assignment/usage of '${name}' detected (Avoid XSS)` });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Rule: No 'any' type usage
|
|
48
|
+
if (ts.isTypeReferenceNode(node)) {
|
|
49
|
+
if (ts.isIdentifier(node.typeName) && node.typeName.text === "any") {
|
|
50
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
51
|
+
issues.push({ file, line: line + 1, rule: "Usage of 'any' type is forbidden" });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (node.kind === ts.SyntaxKind.AnyKeyword) {
|
|
55
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
56
|
+
issues.push({ file, line: line + 1, rule: "Usage of 'any' keyword is forbidden" });
|
|
57
|
+
}
|
|
58
|
+
// Rule: Zero UI Library & child_process Policy
|
|
59
|
+
if (ts.isImportDeclaration(node)) {
|
|
60
|
+
const moduleSpecifier = node.moduleSpecifier;
|
|
61
|
+
if (ts.isStringLiteral(moduleSpecifier)) {
|
|
62
|
+
const forbiddenLibs = ["@chakra-ui", "mui", "@shadcn", "antd", "bootstrap"];
|
|
63
|
+
const lib = forbiddenLibs.find(l => moduleSpecifier.text.includes(l));
|
|
64
|
+
if (lib) {
|
|
65
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
66
|
+
issues.push({ file, line: line + 1, rule: `Forbidden UI library '${lib}' usage detected` });
|
|
67
|
+
}
|
|
68
|
+
if (moduleSpecifier.text === "child_process") {
|
|
69
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
70
|
+
issues.push({ file, line: line + 1, rule: "Direct child_process module usage is forbidden. Command execution must be managed through secure framework APIs." });
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Rule: child_process dynamic usage & Raw SQL templates
|
|
75
|
+
if (ts.isCallExpression(node)) {
|
|
76
|
+
const expression = node.expression;
|
|
77
|
+
if (ts.isIdentifier(expression) && expression.text === "require") {
|
|
78
|
+
const args = node.arguments;
|
|
79
|
+
if (args.length > 0 && ts.isStringLiteral(args[0]) && args[0].text === "child_process") {
|
|
80
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
81
|
+
issues.push({ file, line: line + 1, rule: "Direct child_process module usage is forbidden. Command execution must be managed through secure framework APIs." });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (node.kind === ts.SyntaxKind.ImportKeyword) {
|
|
86
|
+
const parent = node.parent;
|
|
87
|
+
if (parent && ts.isCallExpression(parent)) {
|
|
88
|
+
const args = parent.arguments;
|
|
89
|
+
if (args.length > 0 && ts.isStringLiteral(args[0]) && args[0].text === "child_process") {
|
|
90
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(parent.getStart());
|
|
91
|
+
issues.push({ file, line: line + 1, rule: "Direct child_process module usage is forbidden. Command execution must be managed through secure framework APIs." });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (ts.isTaggedTemplateExpression(node)) {
|
|
96
|
+
const tag = node.tag;
|
|
97
|
+
if (ts.isIdentifier(tag) && tag.text === "sql") {
|
|
98
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
99
|
+
issues.push({ file, line: line + 1, rule: "Raw SQL tagged template detected. Use Kysely query builder instead." });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Rule: Raw fs mutation check (Use atomic utilities)
|
|
103
|
+
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
|
|
104
|
+
const prop = node.expression;
|
|
105
|
+
if (ts.isIdentifier(prop.expression) && prop.expression.text === "fs") {
|
|
106
|
+
if (["writeFileSync", "appendFileSync"].includes(prop.name.text)) {
|
|
107
|
+
// Skip if it's explicitly allowed (e.g. lock files)
|
|
108
|
+
const args = node.arguments;
|
|
109
|
+
let isLockFile = false;
|
|
110
|
+
if (args.length > 0) {
|
|
111
|
+
const firstArgText = args[0].getText(sourceFile).toLowerCase();
|
|
112
|
+
if (firstArgText.includes("lock")) {
|
|
113
|
+
isLockFile = true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
if (!isLockFile) {
|
|
117
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
118
|
+
issues.push({ file, line: line + 1, rule: "Use atomic utilities (writeTextFile) instead of raw fs mutations" });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
ts.forEachChild(node, visit);
|
|
124
|
+
};
|
|
125
|
+
visit(sourceFile);
|
|
126
|
+
// --- 2. Pattern-Based Analysis (For Secrets & PII & Debt) ---
|
|
127
|
+
const lines = content.split("\n");
|
|
128
|
+
const piiKeywords = [
|
|
129
|
+
{ regex: /API_KEY\s*[:=]\s*['"][^'"]+['"]/i, msg: "Hardcoded API Key" },
|
|
130
|
+
{ regex: /SECRET\s*[:=]\s*['"][^'"]+['"]/i, msg: "Hardcoded Secret" },
|
|
131
|
+
{ regex: /PASSWORD\s*[:=]\s*['"][^'"]+['"]/i, msg: "Hardcoded Password" },
|
|
132
|
+
{ regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/, msg: "PII Detected: Email Address" },
|
|
133
|
+
{ regex: /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/, msg: "PII Detected: Credit Card Pattern" }
|
|
134
|
+
];
|
|
135
|
+
lines.forEach((line, index) => {
|
|
136
|
+
const lineNum = index + 1;
|
|
137
|
+
const trimmedLine = line.trim();
|
|
138
|
+
if (trimmedLine.startsWith("//") || trimmedLine.startsWith("*"))
|
|
139
|
+
return;
|
|
140
|
+
// PII & Secret Guard
|
|
141
|
+
for (const { regex, msg } of piiKeywords) {
|
|
142
|
+
if (regex.test(line)) {
|
|
143
|
+
// Allow emails in specific files
|
|
144
|
+
if (msg.includes("Email") && (file.endsWith("README.md") || file.endsWith("package.json") || file.includes("CONTRIBUTING"))) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
issues.push({ file, line: lineNum, rule: `Corporate Compliance Breach: ${msg}` });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Technical Debt (TODO/FIXME)
|
|
151
|
+
if (/\b(TODO|FIXME)\b/i.test(line)) {
|
|
152
|
+
issues.push({ file, line: lineNum, rule: "Unresolved Technical Debt (TODO/FIXME) found" });
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return issues;
|
|
157
|
+
}
|
|
158
|
+
function getAllFiles(dirPath, arrayOfFiles = []) {
|
|
159
|
+
const files = fs.readdirSync(dirPath);
|
|
160
|
+
files.forEach((file) => {
|
|
161
|
+
const fullPath = path.join(dirPath, file);
|
|
162
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
163
|
+
arrayOfFiles = getAllFiles(fullPath, arrayOfFiles);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
const ext = path.extname(fullPath);
|
|
167
|
+
if ([".ts", ".tsx", ".js", ".jsx"].includes(ext)) {
|
|
168
|
+
arrayOfFiles.push(fullPath);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
return arrayOfFiles;
|
|
173
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Standard Color Palette Schema
|
|
4
|
+
*/
|
|
5
|
+
export const ColorPaletteSchema = z.object({
|
|
6
|
+
primary: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/),
|
|
7
|
+
secondary: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/),
|
|
8
|
+
accent: z.string().regex(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/),
|
|
9
|
+
});
|
|
10
|
+
/**
|
|
11
|
+
* Agent Atabey config.json Schema
|
|
12
|
+
*/
|
|
13
|
+
export const ConfigSchema = z.object({
|
|
14
|
+
name: z.string().default("Agent Atabey"),
|
|
15
|
+
version: z.string(),
|
|
16
|
+
frameworkDir: z.string().optional(),
|
|
17
|
+
theme: z.object({
|
|
18
|
+
palette: z.string(),
|
|
19
|
+
colors: ColorPaletteSchema
|
|
20
|
+
}).optional(),
|
|
21
|
+
paths: z.object({
|
|
22
|
+
backend: z.string(),
|
|
23
|
+
frontend: z.string(),
|
|
24
|
+
docs: z.string(),
|
|
25
|
+
tests: z.string(),
|
|
26
|
+
}).default({
|
|
27
|
+
backend: "apps/backend",
|
|
28
|
+
frontend: "apps/web",
|
|
29
|
+
docs: "docs",
|
|
30
|
+
tests: "tests"
|
|
31
|
+
})
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* MCP mcp.json Schema
|
|
35
|
+
*/
|
|
36
|
+
export const McpConfigSchema = z.object({
|
|
37
|
+
mcpServers: z.record(z.object({
|
|
38
|
+
command: z.string(),
|
|
39
|
+
args: z.array(z.string()),
|
|
40
|
+
env: z.record(z.string()).optional()
|
|
41
|
+
}))
|
|
42
|
+
});
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import crypto from "crypto";
|
|
4
|
+
import { remapFrameworkContent } from "../adapters/index.js"; // Import from the new adapters.js
|
|
5
|
+
import { ensureDir, writeTextFile, writeJsonFile, appendFile } from "../../shared/fs.js";
|
|
6
|
+
export { ensureDir, writeTextFile, writeJsonFile, appendFile };
|
|
7
|
+
export function updateGitIgnore(targetPath, frameworkDir = ".atabey", dryRun = false) {
|
|
8
|
+
const IGNORE_LINES = [
|
|
9
|
+
"# Agent Atabey",
|
|
10
|
+
`${frameworkDir}/logs/*.json`,
|
|
11
|
+
`${frameworkDir}/*.lock`,
|
|
12
|
+
`${frameworkDir}/memory/`,
|
|
13
|
+
".env",
|
|
14
|
+
".DS_Store",
|
|
15
|
+
];
|
|
16
|
+
let content = "";
|
|
17
|
+
if (fs.existsSync(targetPath)) {
|
|
18
|
+
content = fs.readFileSync(targetPath, "utf8");
|
|
19
|
+
}
|
|
20
|
+
const lines = content.split("\n").map((l) => l.trim());
|
|
21
|
+
let added = false;
|
|
22
|
+
for (const line of IGNORE_LINES) {
|
|
23
|
+
if (!lines.includes(line)) {
|
|
24
|
+
content += (content.endsWith("\n") || content === "" ? "" : "\n") + line + "\n";
|
|
25
|
+
added = true;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (added) {
|
|
29
|
+
if (dryRun) {
|
|
30
|
+
console.warn(`[DRY RUN] Would update .gitignore at ${targetPath}`);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
writeTextFile(targetPath, content);
|
|
34
|
+
console.warn("[OK] .gitignore updated.");
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function copyDir(src, dest, skipSet = new Set(), nonDestructive = false, frameworkDir = ".gemini", targetScope = "", sanitizeJson, adapterId = "gemini", dryRun = false) {
|
|
39
|
+
const DEFAULT_SKIP = new Set(["node_modules", ".git", ".DS_Store", "package-lock.json"]);
|
|
40
|
+
const actualSkip = new Set([...DEFAULT_SKIP, ...skipSet]);
|
|
41
|
+
if (!fs.existsSync(dest) && !dryRun) {
|
|
42
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
fs.readdirSync(src, { withFileTypes: true }).forEach((entry) => {
|
|
45
|
+
if (actualSkip.has(entry.name))
|
|
46
|
+
return;
|
|
47
|
+
const srcPath = path.join(src, entry.name);
|
|
48
|
+
// Physical folder remapping during copy.
|
|
49
|
+
// In unified mode (frameworkDir === ".atabey") folder names are always standard —
|
|
50
|
+
// agents/ stays agents/, knowledge/ stays knowledge/.
|
|
51
|
+
// Only remap when using a legacy non-unified adapter directory.
|
|
52
|
+
let effectiveEntryName = entry.name;
|
|
53
|
+
if (entry.isDirectory() && frameworkDir !== ".atabey") {
|
|
54
|
+
if (entry.name === "agents") {
|
|
55
|
+
if (adapterId === "antigravity-cli")
|
|
56
|
+
effectiveEntryName = "skills";
|
|
57
|
+
else if (adapterId === "grok")
|
|
58
|
+
effectiveEntryName = "plugins";
|
|
59
|
+
}
|
|
60
|
+
else if (entry.name === "knowledge") {
|
|
61
|
+
if (adapterId === "antigravity-cli")
|
|
62
|
+
effectiveEntryName = "rules";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const destPath = path.join(dest, effectiveEntryName);
|
|
66
|
+
if (entry.isDirectory()) {
|
|
67
|
+
copyDir(srcPath, destPath, skipSet, nonDestructive, frameworkDir, targetScope, sanitizeJson, adapterId, dryRun);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
if (nonDestructive && fs.existsSync(destPath)) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const ext = path.extname(entry.name);
|
|
74
|
+
const textExtensions = [".md", ".json", ".js", ".ts", ".txt", ""];
|
|
75
|
+
if (textExtensions.includes(ext)) {
|
|
76
|
+
let content = fs.readFileSync(srcPath, "utf8");
|
|
77
|
+
content = remapFrameworkContent(content, frameworkDir, adapterId); // Use the new remap function
|
|
78
|
+
if (ext === ".json") {
|
|
79
|
+
try {
|
|
80
|
+
const json = JSON.parse(content);
|
|
81
|
+
writeJsonFile(destPath, sanitizeJson(json, targetScope), dryRun);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
const fallback = content.replace(/workspace:[^"'\s]*/g, "*");
|
|
85
|
+
writeTextFile(destPath, fallback, dryRun);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
content = content.replace(/workspace:[^"'\s]*/g, "*");
|
|
90
|
+
content = remapFrameworkContent(content, frameworkDir, adapterId); // Apply remap once to the final content
|
|
91
|
+
writeTextFile(destPath, content, dryRun);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
if (dryRun) {
|
|
96
|
+
console.warn(`[DRY RUN] Would copy binary file: ${destPath}`);
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
fs.copyFileSync(srcPath, destPath);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
export function collectFiles(dir, extensions) {
|
|
106
|
+
let results = [];
|
|
107
|
+
if (!fs.existsSync(dir))
|
|
108
|
+
return results;
|
|
109
|
+
const list = fs.readdirSync(dir);
|
|
110
|
+
list.forEach((file) => {
|
|
111
|
+
file = path.join(dir, file);
|
|
112
|
+
const stat = fs.statSync(file);
|
|
113
|
+
if (stat && stat.isDirectory()) {
|
|
114
|
+
if (!file.includes("node_modules") && !file.includes(".git")) {
|
|
115
|
+
results = results.concat(collectFiles(file, extensions));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
else if (extensions.includes(path.extname(file))) {
|
|
119
|
+
results.push(file);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
return results;
|
|
123
|
+
}
|
|
124
|
+
export function computeTypesHash(projectRoot, sharedDir) {
|
|
125
|
+
const walk = (d) => fs.readdirSync(d, { withFileTypes: true }).flatMap((e) => {
|
|
126
|
+
const fullPath = path.join(d, e.name);
|
|
127
|
+
return e.isDirectory() ? walk(fullPath) : (e.name.endsWith(".ts") ? [fullPath] : []);
|
|
128
|
+
});
|
|
129
|
+
const hash = crypto.createHash("sha256");
|
|
130
|
+
for (const filePath of walk(sharedDir).sort()) {
|
|
131
|
+
hash.update(path.relative(projectRoot, filePath));
|
|
132
|
+
hash.update("\0");
|
|
133
|
+
hash.update(fs.readFileSync(filePath));
|
|
134
|
+
hash.update("\0");
|
|
135
|
+
}
|
|
136
|
+
return hash.digest("hex");
|
|
137
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const TRANSLATIONS = {
|
|
2
|
+
tr: {
|
|
3
|
+
welcome: "Agent Atabey İnteraktif Kuruluma Hoş Geldiniz!",
|
|
4
|
+
select_backend: "Kurumsal Backend Dilini Seçin",
|
|
5
|
+
select_frontend: "Kurumsal Frontend Altyapısını Seçin",
|
|
6
|
+
select_language: "Framework İletişim Dilini Seçin",
|
|
7
|
+
select_dirs: "Dahil edilecek framework dizinlerini seçin (örn: 1,2,3) veya HEPSİ için Enter:",
|
|
8
|
+
select_agents: "Dahil edilecek çekirdek ajanları seçin (örn: 1,2) veya HEPSİ için Enter:",
|
|
9
|
+
select_palette: "Renk paletini seçin (1-3) veya Enter (Modern Blue):",
|
|
10
|
+
init_success: "Agent Atabey başarıyla kuruldu!",
|
|
11
|
+
constitution_title: "Atabey Anayasası — Disiplin ve Nizam",
|
|
12
|
+
status_title: "Durum Çizelgesi",
|
|
13
|
+
agent_ready: "HAZIR",
|
|
14
|
+
next_steps: "Sonraki Adımlar:",
|
|
15
|
+
},
|
|
16
|
+
en: {
|
|
17
|
+
welcome: "Welcome to Agent Atabey Interactive Setup!",
|
|
18
|
+
select_backend: "Select Enterprise Backend Language",
|
|
19
|
+
select_frontend: "Select Enterprise Frontend Framework/Build Tool",
|
|
20
|
+
select_language: "Select Framework Communication Language",
|
|
21
|
+
select_dirs: "Enter directory numbers to include (e.g. 1,2,3) or Enter for ALL:",
|
|
22
|
+
select_agents: "Enter agent numbers to include (e.g. 1,2) or Enter for ALL:",
|
|
23
|
+
select_palette: "Select palette (1-3) or Enter (Modern Blue):",
|
|
24
|
+
init_success: "Agent Atabey initialized successfully!",
|
|
25
|
+
constitution_title: "Atabey Constitution — Discipline and Order",
|
|
26
|
+
status_title: "Status Board",
|
|
27
|
+
agent_ready: "READY",
|
|
28
|
+
next_steps: "Next Steps:",
|
|
29
|
+
}
|
|
30
|
+
};
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import os from "os";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { logger } from "../../shared/logger.js";
|
|
5
|
+
import { ensureDir, writeTextFile, writeJsonFile } from "../../shared/fs.js";
|
|
6
|
+
import { Storage } from "../../shared/storage.js";
|
|
7
|
+
import { FRAMEWORK, FRAMEWORK_DIR_CANDIDATES, MCP, } from "../../shared/constants.js";
|
|
8
|
+
export { generateULID } from "./time.js";
|
|
9
|
+
const CWD = process.cwd();
|
|
10
|
+
const HOME = os.homedir();
|
|
11
|
+
function findFrameworkDir(basePath) {
|
|
12
|
+
try {
|
|
13
|
+
const pkgPath = path.join(basePath, "package.json");
|
|
14
|
+
if (fs.existsSync(pkgPath)) {
|
|
15
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
16
|
+
if (pkg.atabey && typeof pkg.atabey.frameworkDir === "string") {
|
|
17
|
+
const customDir = path.join(basePath, pkg.atabey.frameworkDir);
|
|
18
|
+
if (fs.existsSync(customDir))
|
|
19
|
+
return customDir;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
logger.debug("Failed to read package.json in findFrameworkDir", err);
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
export function isFrameworkDevelopmentRepo() {
|
|
29
|
+
try {
|
|
30
|
+
const pkgPath = path.join(CWD, "package.json");
|
|
31
|
+
if (fs.existsSync(pkgPath)) {
|
|
32
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
33
|
+
if (pkg.name === "atabey") {
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
logger.debug("Failed to read package.json in isFrameworkDevelopmentRepo", err);
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
export function getLocalFrameworkDir() {
|
|
44
|
+
const localDir = findFrameworkDir(CWD);
|
|
45
|
+
return localDir || path.join(CWD, FRAMEWORK.CORE_DIR);
|
|
46
|
+
}
|
|
47
|
+
export function getConfigDir() {
|
|
48
|
+
const localDir = findFrameworkDir(CWD);
|
|
49
|
+
if (localDir)
|
|
50
|
+
return localDir;
|
|
51
|
+
// Check for standard local directories if package.json doesn't specify
|
|
52
|
+
const localCandidates = [...FRAMEWORK_DIR_CANDIDATES, ".agent"];
|
|
53
|
+
for (const cand of localCandidates) {
|
|
54
|
+
const p = path.join(CWD, cand);
|
|
55
|
+
if (fs.existsSync(p))
|
|
56
|
+
return p;
|
|
57
|
+
}
|
|
58
|
+
// In dev repo, don't fall back to global dir. Point to local default.
|
|
59
|
+
if (isFrameworkDevelopmentRepo()) {
|
|
60
|
+
return path.join(CWD, FRAMEWORK.CORE_DIR);
|
|
61
|
+
}
|
|
62
|
+
return path.join(HOME, FRAMEWORK.CORE_DIR);
|
|
63
|
+
}
|
|
64
|
+
export function getFrameworkDir() {
|
|
65
|
+
const testDir = process.env[MCP.TEST_DIR_ENV];
|
|
66
|
+
if (testDir)
|
|
67
|
+
return testDir;
|
|
68
|
+
return getConfigDir();
|
|
69
|
+
}
|
|
70
|
+
export function getDocumentStorePath() {
|
|
71
|
+
const frameworkDir = getFrameworkDir();
|
|
72
|
+
return path.join(frameworkDir, "memory");
|
|
73
|
+
}
|
|
74
|
+
export function initDocumentStore(frameworkDir) {
|
|
75
|
+
const storePath = frameworkDir ? path.join(frameworkDir, "memory") : getDocumentStorePath();
|
|
76
|
+
ensureDir(storePath);
|
|
77
|
+
ensureDir(path.join(storePath, "tasks"));
|
|
78
|
+
ensureDir(path.join(storePath, "history"));
|
|
79
|
+
const stateFile = path.join(storePath, "state.json");
|
|
80
|
+
if (!fs.existsSync(stateFile)) {
|
|
81
|
+
writeJsonFile(stateFile, { phase: "PHASE_0", traceId: "T-000", managerState: "ACTIVE" });
|
|
82
|
+
}
|
|
83
|
+
const statusFile = path.join(storePath, "status.json");
|
|
84
|
+
if (!fs.existsSync(statusFile)) {
|
|
85
|
+
writeJsonFile(statusFile, {});
|
|
86
|
+
}
|
|
87
|
+
// Initialize SQLite
|
|
88
|
+
Storage.setMetadata("phase", "PHASE_0");
|
|
89
|
+
Storage.setMetadata("traceId", "T-000");
|
|
90
|
+
Storage.setMetadata("managerState", "ACTIVE");
|
|
91
|
+
// Ensure the initial Markdown view is created
|
|
92
|
+
syncMarkdownMemory(frameworkDir);
|
|
93
|
+
}
|
|
94
|
+
export function readState() {
|
|
95
|
+
const phase = Storage.getMetadata("phase") || "PHASE_0";
|
|
96
|
+
const traceId = Storage.getMetadata("traceId") || "T-000";
|
|
97
|
+
const managerState = Storage.getMetadata("managerState") || "ACTIVE";
|
|
98
|
+
return { phase, traceId, managerState };
|
|
99
|
+
}
|
|
100
|
+
export function readStatus() {
|
|
101
|
+
const agents = Storage.getAllAgents();
|
|
102
|
+
const status = {};
|
|
103
|
+
agents.forEach((a) => {
|
|
104
|
+
const name = a.name.startsWith("@") ? a.name : `@${a.name}`;
|
|
105
|
+
status[name] = { state: a.state, task: a.task, lastUpdated: a.last_updated };
|
|
106
|
+
});
|
|
107
|
+
return status;
|
|
108
|
+
}
|
|
109
|
+
export function listTasks() {
|
|
110
|
+
return Storage.getTasks();
|
|
111
|
+
}
|
|
112
|
+
export function getMemoryPath() {
|
|
113
|
+
return path.join(getDocumentStorePath(), "state.json");
|
|
114
|
+
}
|
|
115
|
+
export function readActiveTraceId(memoryContent) {
|
|
116
|
+
try {
|
|
117
|
+
const state = JSON.parse(memoryContent);
|
|
118
|
+
return state.traceId || null;
|
|
119
|
+
}
|
|
120
|
+
catch (_e) { /* ignore */
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export function updateProjectMemory(section, content) {
|
|
125
|
+
if (section === "HISTORY") {
|
|
126
|
+
updateDocumentStore("history", { content }, new Date().toISOString().replace(/[:.]/g, "-"));
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const state = readState() || {};
|
|
130
|
+
state[section] = content;
|
|
131
|
+
updateDocumentStore("state", state);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export function initializeMemory(memoryPathOrBase, targetBaseOrDryRun) {
|
|
135
|
+
const targetBase = typeof targetBaseOrDryRun === "string" ? targetBaseOrDryRun : memoryPathOrBase;
|
|
136
|
+
initDocumentStore(targetBase);
|
|
137
|
+
logger.info("[OK] Document store initialized.");
|
|
138
|
+
}
|
|
139
|
+
export function getConfiguredPaths() {
|
|
140
|
+
const defaultPaths = { backend: "apps/backend", frontend: "apps/web", mobile: "apps/mobile", docs: "docs", tests: "tests" };
|
|
141
|
+
try {
|
|
142
|
+
const frameworkDir = getConfigDir();
|
|
143
|
+
const configPath = path.join(frameworkDir, "config.json");
|
|
144
|
+
if (fs.existsSync(configPath)) {
|
|
145
|
+
const rawConfig = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
146
|
+
return rawConfig.paths || defaultPaths;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
logger.debug("Critical config read failure", err);
|
|
151
|
+
}
|
|
152
|
+
return defaultPaths;
|
|
153
|
+
}
|
|
154
|
+
export function updateDocumentStore(type, data, id, frameworkDir) {
|
|
155
|
+
const storePath = frameworkDir ? path.join(frameworkDir, "memory") : getDocumentStorePath();
|
|
156
|
+
ensureDir(storePath);
|
|
157
|
+
switch (type) {
|
|
158
|
+
case "state":
|
|
159
|
+
writeJsonFile(path.join(storePath, "state.json"), data);
|
|
160
|
+
break;
|
|
161
|
+
case "status": {
|
|
162
|
+
const status = data;
|
|
163
|
+
for (const [agent, info] of Object.entries(status)) {
|
|
164
|
+
Storage.updateAgentStatus(agent, info.state, info.task, info.lastUpdated);
|
|
165
|
+
}
|
|
166
|
+
break;
|
|
167
|
+
}
|
|
168
|
+
case "task": {
|
|
169
|
+
Storage.saveTask(data);
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
case "history": {
|
|
173
|
+
const db = Storage.getDB();
|
|
174
|
+
db.prepare(`
|
|
175
|
+
INSERT INTO logs (agent, action, summary)
|
|
176
|
+
VALUES (?, ?, ?)
|
|
177
|
+
`).run("@manager", "HISTORY_UPDATE", data.content);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Auto-sync Markdown view after any change
|
|
182
|
+
syncMarkdownMemory(frameworkDir);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Single Source of Truth Bridge: JSON -> Markdown
|
|
186
|
+
* Regenerates PROJECT_MEMORY.md from structured state.
|
|
187
|
+
*/
|
|
188
|
+
export function syncMarkdownMemory(fDir) {
|
|
189
|
+
const frameworkDir = fDir || getFrameworkDir();
|
|
190
|
+
const storePath = path.join(frameworkDir, "memory");
|
|
191
|
+
const mdPath = path.join(storePath, "PROJECT_MEMORY.md");
|
|
192
|
+
try {
|
|
193
|
+
const state = readState();
|
|
194
|
+
const status = readStatus();
|
|
195
|
+
const tasks = Storage.getTasks();
|
|
196
|
+
if (!state)
|
|
197
|
+
return;
|
|
198
|
+
const lines = [
|
|
199
|
+
"# [MEMORY] Agent Atabey — Project Memory",
|
|
200
|
+
"",
|
|
201
|
+
"## 📍 Current State",
|
|
202
|
+
`- **Phase:** ${state.phase || "PHASE_0"}`,
|
|
203
|
+
`- **Trace ID:** ${state.traceId || "N/A"}`,
|
|
204
|
+
`- **@manager state:** ${state.managerState || "ACTIVE"}`,
|
|
205
|
+
];
|
|
206
|
+
// Add custom sections from state
|
|
207
|
+
const internalFields = ["phase", "traceId", "managerState"];
|
|
208
|
+
Object.entries(state).forEach(([key, value]) => {
|
|
209
|
+
if (!internalFields.includes(key) && value) {
|
|
210
|
+
lines.push("", `## 📌 ${key}`, String(value));
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
lines.push("", "## 📋 Active Tasks", "| Trace ID | Task | Agent | Priority | Status |", "| :--- | :--- | :--- | :--- | :--- |");
|
|
214
|
+
tasks.forEach(t => {
|
|
215
|
+
if (t.status !== "COMPLETED") {
|
|
216
|
+
lines.push(`| ${t.traceId} | ${t.description} | ${t.agent} | ${t.priority} | ${t.status} |`);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
lines.push("", "## [AI] Agent Statuses");
|
|
220
|
+
lines.push("| Agent | State | Active Task | Last Updated |");
|
|
221
|
+
lines.push("| :--- | :--- | :--- | :--- |");
|
|
222
|
+
for (const [agent, info] of Object.entries(status)) {
|
|
223
|
+
const data = info;
|
|
224
|
+
const displayAgent = agent.startsWith("@") ? agent : `@${agent}`;
|
|
225
|
+
lines.push(`| ${displayAgent} | ${data.state} | ${data.task} | ${data.lastUpdated || "-"} |`);
|
|
226
|
+
}
|
|
227
|
+
lines.push("", "## 📜 HISTORY");
|
|
228
|
+
// Add last 5 history items
|
|
229
|
+
const historyDir = path.join(storePath, "history");
|
|
230
|
+
if (fs.existsSync(historyDir)) {
|
|
231
|
+
const histFiles = fs.readdirSync(historyDir)
|
|
232
|
+
.filter(f => f.endsWith(".json"))
|
|
233
|
+
.sort()
|
|
234
|
+
.reverse()
|
|
235
|
+
.slice(0, 10);
|
|
236
|
+
histFiles.forEach(f => {
|
|
237
|
+
const hist = JSON.parse(fs.readFileSync(path.join(historyDir, f), "utf8"));
|
|
238
|
+
lines.push(`### ${f.replace(".json", "")}`);
|
|
239
|
+
lines.push(hist.content);
|
|
240
|
+
lines.push("");
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
writeTextFile(mdPath, lines.join("\n"));
|
|
244
|
+
logger.debug("Markdown memory synchronized.");
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
logger.debug("Markdown memory sync failed", err);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
export function acquireMemoryLock(lockPath) {
|
|
251
|
+
try {
|
|
252
|
+
fs.writeFileSync(lockPath, String(Date.now()), { flag: "wx" });
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
catch (err) {
|
|
256
|
+
const error = err;
|
|
257
|
+
if (error.code === "EEXIST") {
|
|
258
|
+
try {
|
|
259
|
+
const stat = fs.statSync(lockPath);
|
|
260
|
+
if (Date.now() - stat.mtimeMs > 10000) {
|
|
261
|
+
fs.unlinkSync(lockPath);
|
|
262
|
+
fs.writeFileSync(lockPath, String(Date.now()), { flag: "wx" });
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
catch (_e) { /* ignore */
|
|
267
|
+
// Ignore
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
export function releaseMemoryLock(lockPath) {
|
|
274
|
+
if (fs.existsSync(lockPath))
|
|
275
|
+
fs.unlinkSync(lockPath);
|
|
276
|
+
}
|