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,78 @@
|
|
|
1
|
+
import { generateULID } from "../../cli/utils/time.js";
|
|
2
|
+
import { RoutingEngine } from "./routing-engine.js";
|
|
3
|
+
/**
|
|
4
|
+
* 🗺️ Planning Engine (The Strategist)
|
|
5
|
+
* Responsible for decomposing complex requests into a DAG of atomic tasks.
|
|
6
|
+
*/
|
|
7
|
+
export class PlanningEngine {
|
|
8
|
+
/**
|
|
9
|
+
* Creates a new Plan structure.
|
|
10
|
+
* In a real enterprise scenario, this would be fueled by an LLM prompt.
|
|
11
|
+
* For now, it provides the structural backbone.
|
|
12
|
+
*/
|
|
13
|
+
static createPlan(rawTasks) {
|
|
14
|
+
const planId = generateULID();
|
|
15
|
+
const tasks = rawTasks.map((t, index) => {
|
|
16
|
+
const id = t.id || `TASK_${String(index + 1).padStart(2, "0")}`;
|
|
17
|
+
return {
|
|
18
|
+
id,
|
|
19
|
+
agent: t.agent || RoutingEngine.resolveAgent(t.task),
|
|
20
|
+
task: t.task,
|
|
21
|
+
dependencies: t.dependencies || []
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
planId,
|
|
26
|
+
tasks
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Validates a plan's integrity (no circular dependencies, all agents valid).
|
|
31
|
+
*/
|
|
32
|
+
static validatePlan(plan) {
|
|
33
|
+
const errors = [];
|
|
34
|
+
const taskIds = new Set(plan.tasks.map(t => t.id));
|
|
35
|
+
for (const task of plan.tasks) {
|
|
36
|
+
// Check dependencies exist
|
|
37
|
+
for (const dep of task.dependencies) {
|
|
38
|
+
if (!taskIds.has(dep)) {
|
|
39
|
+
errors.push(`Task ${task.id} has non-existent dependency: ${dep}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Check circular dependencies (simple check for now)
|
|
43
|
+
if (task.dependencies.includes(task.id)) {
|
|
44
|
+
errors.push(`Task ${task.id} cannot depend on itself.`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Circular dependency detection using DFS
|
|
48
|
+
const visited = new Set();
|
|
49
|
+
const recStack = new Set();
|
|
50
|
+
const hasCycle = (taskId) => {
|
|
51
|
+
if (recStack.has(taskId))
|
|
52
|
+
return true;
|
|
53
|
+
if (visited.has(taskId))
|
|
54
|
+
return false;
|
|
55
|
+
visited.add(taskId);
|
|
56
|
+
recStack.add(taskId);
|
|
57
|
+
const task = plan.tasks.find(t => t.id === taskId);
|
|
58
|
+
if (task) {
|
|
59
|
+
for (const dep of task.dependencies) {
|
|
60
|
+
if (hasCycle(dep))
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
recStack.delete(taskId);
|
|
65
|
+
return false;
|
|
66
|
+
};
|
|
67
|
+
for (const task of plan.tasks) {
|
|
68
|
+
if (hasCycle(task.id)) {
|
|
69
|
+
errors.push("Circular dependency detected in plan.");
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
valid: errors.length === 0,
|
|
75
|
+
errors
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [SECURITY] Risk Engine (The Guardian)
|
|
3
|
+
* Calculates the danger level of a proposed task or operation.
|
|
4
|
+
*/
|
|
5
|
+
export class RiskEngine {
|
|
6
|
+
static HIGH_RISK_KEYWORDS = [
|
|
7
|
+
{ word: "delete", weight: 40 },
|
|
8
|
+
{ word: "drop", weight: 50 },
|
|
9
|
+
{ word: "truncate", weight: 50 },
|
|
10
|
+
{ word: "rm -rf", weight: 60 },
|
|
11
|
+
{ word: "purge", weight: 40 },
|
|
12
|
+
{ word: "format", weight: 50 },
|
|
13
|
+
{ word: "force", weight: 20 },
|
|
14
|
+
];
|
|
15
|
+
static SENSITIVE_PATHS = [
|
|
16
|
+
{ pattern: /\.env/, weight: 50 },
|
|
17
|
+
{ pattern: /config/, weight: 20 },
|
|
18
|
+
{ pattern: /database\//, weight: 30 },
|
|
19
|
+
{ pattern: /auth\//, weight: 30 },
|
|
20
|
+
{ pattern: /security/, weight: 30 },
|
|
21
|
+
{ pattern: /atabey/, weight: 40 }, // Framework protection
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* Assesses the risk of a natural language task or command string.
|
|
25
|
+
*/
|
|
26
|
+
static assessTaskRisk(task) {
|
|
27
|
+
const factors = [];
|
|
28
|
+
let totalScore = 0;
|
|
29
|
+
// 1. Keyword Analysis
|
|
30
|
+
for (const { word, weight } of this.HIGH_RISK_KEYWORDS) {
|
|
31
|
+
if (new RegExp(`\\b${word}\\b`, "i").test(task)) {
|
|
32
|
+
factors.push({
|
|
33
|
+
factor: `Keyword: ${word}`,
|
|
34
|
+
score: weight,
|
|
35
|
+
description: `Detected high-risk keyword '${word}' in task description.`
|
|
36
|
+
});
|
|
37
|
+
totalScore += weight;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// 2. Path Sensitivity (if paths are mentioned in the task)
|
|
41
|
+
for (const { pattern, weight } of this.SENSITIVE_PATHS) {
|
|
42
|
+
if (pattern.test(task)) {
|
|
43
|
+
factors.push({
|
|
44
|
+
factor: `Sensitive Path: ${pattern.source}`,
|
|
45
|
+
score: weight,
|
|
46
|
+
description: `Task involves access to sensitive path or pattern: ${pattern.source}`
|
|
47
|
+
});
|
|
48
|
+
totalScore += weight;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// 3. Complexity Risk (Length of task as a proxy)
|
|
52
|
+
if (task.length > 300) {
|
|
53
|
+
const score = 10;
|
|
54
|
+
factors.push({
|
|
55
|
+
factor: "High Complexity",
|
|
56
|
+
score,
|
|
57
|
+
description: "Task description is unusually long, increasing the chance of misunderstanding."
|
|
58
|
+
});
|
|
59
|
+
totalScore += score;
|
|
60
|
+
}
|
|
61
|
+
return this.finalizeAssessment(totalScore, factors);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Assesses risk based on a proposed file change.
|
|
65
|
+
*/
|
|
66
|
+
static assessChangeRisk(filePath, operation) {
|
|
67
|
+
const factors = [];
|
|
68
|
+
let totalScore = 0;
|
|
69
|
+
// 1. Operation Weight
|
|
70
|
+
const opWeights = { write: 30, replace: 5, patch: 10 };
|
|
71
|
+
totalScore += opWeights[operation];
|
|
72
|
+
factors.push({
|
|
73
|
+
factor: `Operation: ${operation}`,
|
|
74
|
+
score: opWeights[operation],
|
|
75
|
+
description: `A '${operation}' operation is inherently riskier than a surgical 'replace'.`
|
|
76
|
+
});
|
|
77
|
+
// 2. File Path Risk
|
|
78
|
+
for (const { pattern, weight } of this.SENSITIVE_PATHS) {
|
|
79
|
+
if (pattern.test(filePath)) {
|
|
80
|
+
factors.push({
|
|
81
|
+
factor: `Sensitive File: ${filePath}`,
|
|
82
|
+
score: weight,
|
|
83
|
+
description: "Modifying a sensitive file is high risk."
|
|
84
|
+
});
|
|
85
|
+
totalScore += weight;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return this.finalizeAssessment(totalScore, factors);
|
|
89
|
+
}
|
|
90
|
+
static finalizeAssessment(totalScore, factors) {
|
|
91
|
+
let severity = "LOW";
|
|
92
|
+
if (totalScore >= 80)
|
|
93
|
+
severity = "CRITICAL";
|
|
94
|
+
else if (totalScore >= 50)
|
|
95
|
+
severity = "HIGH";
|
|
96
|
+
else if (totalScore >= 20)
|
|
97
|
+
severity = "MEDIUM";
|
|
98
|
+
return {
|
|
99
|
+
totalScore: Math.min(totalScore, 100),
|
|
100
|
+
severity,
|
|
101
|
+
factors,
|
|
102
|
+
requiresApproval: totalScore >= 60
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { getFrameworkDir } from "../../cli/utils/memory.js";
|
|
4
|
+
import { ALL_AGENTS } from "../agents/definitions.js";
|
|
5
|
+
export class RoutingEngine {
|
|
6
|
+
/**
|
|
7
|
+
* Finds the best agent for a given task description based on capability scores and specialties.
|
|
8
|
+
*/
|
|
9
|
+
static resolveAgent(taskDescription) {
|
|
10
|
+
const textLower = taskDescription.toLowerCase();
|
|
11
|
+
const candidates = this.getCandidates();
|
|
12
|
+
let bestAgent = "@backend"; // Default fallback
|
|
13
|
+
let bestScore = 0;
|
|
14
|
+
for (const candidate of candidates) {
|
|
15
|
+
let candidateScore = 0;
|
|
16
|
+
for (const [specialty, weight] of Object.entries(candidate.specialties)) {
|
|
17
|
+
const specLower = specialty.toLowerCase();
|
|
18
|
+
if (textLower.includes(specLower)) {
|
|
19
|
+
candidateScore += weight;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (candidateScore > bestScore) {
|
|
23
|
+
bestScore = candidateScore;
|
|
24
|
+
bestAgent = candidate.agent;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Static fallback keywords if no specialty match found
|
|
28
|
+
if (bestScore === 0) {
|
|
29
|
+
if (textLower.includes("frontend") || textLower.includes("ui") || textLower.includes("page") || textLower.includes("css") || textLower.includes("html") || textLower.includes("react")) {
|
|
30
|
+
bestAgent = "@frontend";
|
|
31
|
+
}
|
|
32
|
+
else if (textLower.includes("security") || textLower.includes("audit") || textLower.includes("auth") || textLower.includes("token")) {
|
|
33
|
+
bestAgent = "@security";
|
|
34
|
+
}
|
|
35
|
+
else if (textLower.includes("database") || textLower.includes("migration") || textLower.includes("sql") || textLower.includes("schema") || textLower.includes("postgres")) {
|
|
36
|
+
bestAgent = "@database";
|
|
37
|
+
}
|
|
38
|
+
else if (textLower.includes("docker") || textLower.includes("ci") || textLower.includes("deploy") || textLower.includes("devops")) {
|
|
39
|
+
bestAgent = "@devops";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return bestAgent;
|
|
43
|
+
}
|
|
44
|
+
static getCandidates() {
|
|
45
|
+
const frameworkDir = getFrameworkDir();
|
|
46
|
+
const registryDir = path.join(frameworkDir, "registry");
|
|
47
|
+
let candidates = [];
|
|
48
|
+
// 1. Try Control Plane Registry first (active registered agents)
|
|
49
|
+
if (fs.existsSync(registryDir)) {
|
|
50
|
+
const registryFiles = fs.readdirSync(registryDir).filter(f => f.endsWith("_active.json"));
|
|
51
|
+
for (const file of registryFiles) {
|
|
52
|
+
try {
|
|
53
|
+
const data = JSON.parse(fs.readFileSync(path.join(registryDir, file), "utf8"));
|
|
54
|
+
if (data.agent && data.specialties) {
|
|
55
|
+
candidates.push({
|
|
56
|
+
agent: data.agent,
|
|
57
|
+
specialties: data.specialties
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch { /* ignore */ }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// 2. Fallback to built-in agent definitions
|
|
65
|
+
if (candidates.length === 0) {
|
|
66
|
+
candidates = ALL_AGENTS.map(ag => ({
|
|
67
|
+
agent: ag.name.startsWith("@") ? ag.name : `@${ag.name}`,
|
|
68
|
+
specialties: ag.specialties || {}
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
return candidates;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core Skill Definitions for Agent Atabey.
|
|
3
|
+
* Groups tools into logical capabilities for agents.
|
|
4
|
+
*/
|
|
5
|
+
export const CORE_SKILLS = {
|
|
6
|
+
FILE_SYSTEM: {
|
|
7
|
+
name: "File System Mastery",
|
|
8
|
+
tools: ["read_file", "write_file"],
|
|
9
|
+
description: "Enables reading and writing files in the workspace with token efficiency.",
|
|
10
|
+
mandates: [
|
|
11
|
+
"- **Token Efficiency:** When reading large files, always specify `startLine` and `endLine` to avoid loading the entire file content into context.",
|
|
12
|
+
"- **Surgical Changes:** Avoid overwriting entire files for small updates; prefer surgical edit tools."
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
EDITING: {
|
|
16
|
+
name: "Surgical Code Modification",
|
|
17
|
+
tools: ["replace_text", "patch_file"],
|
|
18
|
+
description: "Enables surgical, precise edits to source code files without overwriting the entire content.",
|
|
19
|
+
mandates: [
|
|
20
|
+
"- **Precise Selection:** Ensure `oldText` matches the target string exactly, including all whitespace and indentation.",
|
|
21
|
+
"- **Line-based Replacement:** Use `patch_file` for multi-line block updates, specifying exact 1-indexed start and end lines."
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
ORCHESTRATION: {
|
|
25
|
+
name: "Hermes Orchestration & Messaging",
|
|
26
|
+
tools: ["orchestrate_loop", "send_agent_message", "get_framework_status", "log_agent_action"],
|
|
27
|
+
description: "Governs inter-agent message passing, task delegation, and execution logs using the Hermes Message Broker.",
|
|
28
|
+
mandates: [
|
|
29
|
+
"- **Traceability:** Always include the active `traceId` in all messages and action logs.",
|
|
30
|
+
"- **Action Logs:** Log critical operations with `log_agent_action` to ensure transparency and accountability.",
|
|
31
|
+
"- **Message Loops:** Run `orchestrate_loop` to process queued messages and trigger state transitions."
|
|
32
|
+
]
|
|
33
|
+
},
|
|
34
|
+
GOVERNANCE: {
|
|
35
|
+
name: "Control Plane Governance & Locking",
|
|
36
|
+
tools: ["acquire_lock", "release_lock", "register_agent", "update_contract_hash"],
|
|
37
|
+
description: "Governs access control, resource locking, type contract validation, and agent registration.",
|
|
38
|
+
mandates: [
|
|
39
|
+
"- **Locking Protocol:** Always acquire a lock via `acquire_lock` on shared resources (like memory files) before editing, and release it immediately after writing.",
|
|
40
|
+
"- **Contract Enforcement:** Run `update_contract_hash` to re-sync backend types and check for breaking API changes."
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
QUALITY_ASSURANCE: {
|
|
44
|
+
name: "Quality Assurance & Testing",
|
|
45
|
+
tools: ["run_shell_command", "view_file"],
|
|
46
|
+
description: "Enforces testing coverage standards, code style compliance, and runs test suites.",
|
|
47
|
+
mandates: [
|
|
48
|
+
"- **Zero-Mock Policy:** Integration tests must use real test database connections or service-compatible backends; do not rely on fake mocks.",
|
|
49
|
+
"- **Coverage Standards:** Ensure new code meets the 80% test coverage threshold before transitioning to release phases."
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
DATABASE_MANAGEMENT: {
|
|
53
|
+
name: "Database Management & Migrations",
|
|
54
|
+
tools: ["view_file", "replace_text", "run_shell_command"],
|
|
55
|
+
description: "Handles database migrations, schema design, and query optimization.",
|
|
56
|
+
mandates: [
|
|
57
|
+
"- **No Direct DB Calls in Controllers:** Database operations must be isolated inside repository or service files; controllers must never perform raw DB calls.",
|
|
58
|
+
"- **No Raw SQL Strings:** Do not write raw SQL query strings; strictly use type-safe query builders like Kysely."
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
DEVOPS_INFRASTRUCTURE: {
|
|
62
|
+
name: "DevOps & Infrastructure",
|
|
63
|
+
tools: ["run_shell_command", "view_file"],
|
|
64
|
+
description: "Manages CI/CD pipelines, container configurations, env files, and deployments.",
|
|
65
|
+
mandates: [
|
|
66
|
+
"- **No Hardcoded Secrets:** Never embed API keys, secrets, or passwords inside configuration files or codebases.",
|
|
67
|
+
"- **Immutable Deploys:** Ensure build steps compile production bundles successfully without configuration side-effects."
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Atabey — Single Source of Truth for framework constants.
|
|
3
|
+
* Import from here instead of hardcoding paths, phases, or directory names.
|
|
4
|
+
*/
|
|
5
|
+
// ─── Framework identity ───────────────────────────────────────────────────────
|
|
6
|
+
export const FRAMEWORK = {
|
|
7
|
+
NAME: "Agent Atabey",
|
|
8
|
+
CORE_DIR: ".atabey",
|
|
9
|
+
UNIFIED_HUB_DIR: ".agents",
|
|
10
|
+
CONSTITUTION_FILE: "ATABEY.md",
|
|
11
|
+
DEFAULT_TRACE_ID: "T-000",
|
|
12
|
+
DEFAULT_PHASE: "PHASE_0",
|
|
13
|
+
DEFAULT_MANAGER_STATE: "ACTIVE",
|
|
14
|
+
DASHBOARD_PORT: 5858,
|
|
15
|
+
};
|
|
16
|
+
export const PROJECT_PHASES = [
|
|
17
|
+
"PHASE_0",
|
|
18
|
+
"PHASE_1",
|
|
19
|
+
"PHASE_2",
|
|
20
|
+
"PHASE_3",
|
|
21
|
+
"PHASE_4",
|
|
22
|
+
];
|
|
23
|
+
// ─── Adapter platform directories (native / legacy) ───────────────────────────
|
|
24
|
+
export const ADAPTER_DIRS = {
|
|
25
|
+
GEMINI: ".gemini",
|
|
26
|
+
CLAUDE: ".claude",
|
|
27
|
+
GROK: ".grok",
|
|
28
|
+
CURSOR: ".cursor",
|
|
29
|
+
CODEX: ".agents",
|
|
30
|
+
ANTIGRAVITY: ".agents",
|
|
31
|
+
LOCAL: ".atabey",
|
|
32
|
+
LEGACY_AGENT: ".agent",
|
|
33
|
+
};
|
|
34
|
+
/** Priority order for framework directory resolution (CLI + MCP). */
|
|
35
|
+
export const FRAMEWORK_DIR_CANDIDATES = [
|
|
36
|
+
FRAMEWORK.CORE_DIR,
|
|
37
|
+
ADAPTER_DIRS.CODEX,
|
|
38
|
+
ADAPTER_DIRS.CLAUDE,
|
|
39
|
+
ADAPTER_DIRS.GEMINI,
|
|
40
|
+
ADAPTER_DIRS.GROK,
|
|
41
|
+
ADAPTER_DIRS.CURSOR,
|
|
42
|
+
ADAPTER_DIRS.ANTIGRAVITY,
|
|
43
|
+
ADAPTER_DIRS.LOCAL,
|
|
44
|
+
];
|
|
45
|
+
/** Slug under `.agents/{slug}/` for each adapter in unified mode. */
|
|
46
|
+
export const UNIFIED_ADAPTER_SLUG = {
|
|
47
|
+
gemini: "gemini",
|
|
48
|
+
claude: "claude",
|
|
49
|
+
grok: "grok",
|
|
50
|
+
cursor: "cursor",
|
|
51
|
+
codex: "codex",
|
|
52
|
+
local: "local",
|
|
53
|
+
"antigravity-cli": "antigravity",
|
|
54
|
+
};
|
|
55
|
+
// ─── Native agent instruction paths (legacy tool compatibility) ───────────────
|
|
56
|
+
export const NATIVE_AGENT_PATHS = {
|
|
57
|
+
gemini: pathJoin(ADAPTER_DIRS.GEMINI, "agents"),
|
|
58
|
+
claude: pathJoin(ADAPTER_DIRS.CLAUDE, "agents"),
|
|
59
|
+
grok: pathJoin(ADAPTER_DIRS.GROK, "agents"),
|
|
60
|
+
cursor: pathJoin(ADAPTER_DIRS.CURSOR, "rules"),
|
|
61
|
+
codex: pathJoin(ADAPTER_DIRS.CODEX, "instructions"),
|
|
62
|
+
local: pathJoin(ADAPTER_DIRS.LOCAL, "agents"),
|
|
63
|
+
"antigravity-cli": pathJoin(ADAPTER_DIRS.ANTIGRAVITY, "agents"),
|
|
64
|
+
};
|
|
65
|
+
/** Legacy layout bases used by `check` and discovery (non-unified installs). */
|
|
66
|
+
export const LEGACY_AGENT_LAYOUT_BASES = [
|
|
67
|
+
NATIVE_AGENT_PATHS.gemini,
|
|
68
|
+
NATIVE_AGENT_PATHS.claude,
|
|
69
|
+
NATIVE_AGENT_PATHS.cursor,
|
|
70
|
+
NATIVE_AGENT_PATHS.grok,
|
|
71
|
+
NATIVE_AGENT_PATHS.codex,
|
|
72
|
+
NATIVE_AGENT_PATHS.local,
|
|
73
|
+
NATIVE_AGENT_PATHS["antigravity-cli"],
|
|
74
|
+
];
|
|
75
|
+
// ─── Framework internal subdirectories (under `.atabey/`) ──────────────────
|
|
76
|
+
export const FRAMEWORK_SUBDIRS = {
|
|
77
|
+
AGENTS: "agents",
|
|
78
|
+
SKILLS: "skills",
|
|
79
|
+
KNOWLEDGE: "knowledge",
|
|
80
|
+
PROMPTS: "prompts",
|
|
81
|
+
MEMORY: "memory",
|
|
82
|
+
ROUTER: "router",
|
|
83
|
+
REGISTRY: "registry",
|
|
84
|
+
OBSERVABILITY: "observability",
|
|
85
|
+
RULES: "rules",
|
|
86
|
+
MESSAGES: "messages",
|
|
87
|
+
LOGS: "logs",
|
|
88
|
+
MEMORY_GRAPH: "memory-graph",
|
|
89
|
+
DASHBOARD: "dashboard",
|
|
90
|
+
UI_DIST: "ui",
|
|
91
|
+
};
|
|
92
|
+
export const CORE_SCAFFOLD_SUBDIRS = [
|
|
93
|
+
FRAMEWORK_SUBDIRS.KNOWLEDGE,
|
|
94
|
+
FRAMEWORK_SUBDIRS.PROMPTS,
|
|
95
|
+
FRAMEWORK_SUBDIRS.MEMORY,
|
|
96
|
+
FRAMEWORK_SUBDIRS.ROUTER,
|
|
97
|
+
FRAMEWORK_SUBDIRS.REGISTRY,
|
|
98
|
+
FRAMEWORK_SUBDIRS.OBSERVABILITY,
|
|
99
|
+
FRAMEWORK_SUBDIRS.RULES,
|
|
100
|
+
];
|
|
101
|
+
export const RUNTIME_SUBDIRS = [
|
|
102
|
+
FRAMEWORK_SUBDIRS.MESSAGES,
|
|
103
|
+
FRAMEWORK_SUBDIRS.LOGS,
|
|
104
|
+
FRAMEWORK_SUBDIRS.MEMORY_GRAPH,
|
|
105
|
+
];
|
|
106
|
+
export const MEMORY_SUBDIRS = {
|
|
107
|
+
TASKS: "tasks",
|
|
108
|
+
HISTORY: "history",
|
|
109
|
+
};
|
|
110
|
+
export const MEMORY_FILES = {
|
|
111
|
+
STATE: "state.json",
|
|
112
|
+
STATUS: "status.json",
|
|
113
|
+
PROJECT_MEMORY: "PROJECT_MEMORY.md",
|
|
114
|
+
SHARED_FACTS: "shared-facts.json",
|
|
115
|
+
};
|
|
116
|
+
// ─── Monorepo default paths ───────────────────────────────────────────────────
|
|
117
|
+
export const DEFAULT_MONOREPO_PATHS = {
|
|
118
|
+
backend: "apps/backend",
|
|
119
|
+
frontend: "apps/web",
|
|
120
|
+
mobile: "apps/mobile",
|
|
121
|
+
docs: "docs",
|
|
122
|
+
tests: "tests",
|
|
123
|
+
};
|
|
124
|
+
/** Cursor rule globs per agent role (enterprise monorepo layout). */
|
|
125
|
+
export const CURSOR_AGENT_GLOBS = {
|
|
126
|
+
manager: "**/*",
|
|
127
|
+
security: "**/*",
|
|
128
|
+
architect: "**/*",
|
|
129
|
+
backend: `${DEFAULT_MONOREPO_PATHS.backend}/**/*`,
|
|
130
|
+
frontend: `${DEFAULT_MONOREPO_PATHS.frontend}/**/*`,
|
|
131
|
+
mobile: `${DEFAULT_MONOREPO_PATHS.mobile}/**/*`,
|
|
132
|
+
native: "apps/native/**/*",
|
|
133
|
+
database: `${DEFAULT_MONOREPO_PATHS.backend}/src/database/**/*`,
|
|
134
|
+
devops: "{.github,docker,infra,scripts,*.yml,*.yaml,Dockerfile*}",
|
|
135
|
+
quality: "**/*",
|
|
136
|
+
analyst: "{docs,specs,contracts}/**/*",
|
|
137
|
+
explorer: "**/*",
|
|
138
|
+
git: "**/*",
|
|
139
|
+
};
|
|
140
|
+
// ─── MCP & environment ──────────────────────────────────────────────────────
|
|
141
|
+
export const MCP = {
|
|
142
|
+
SERVER_NAME: "atabey",
|
|
143
|
+
ROOT_CONFIG_FILE: "mcp.json",
|
|
144
|
+
PROJECT_ROOT_ENV: "ATABEY_PROJECT_ROOT",
|
|
145
|
+
TEST_DIR_ENV: "ATABEY_TEST_DIR",
|
|
146
|
+
SERVER_DIST_PATH: "framework-mcp/dist/index.js",
|
|
147
|
+
};
|
|
148
|
+
export const ROOT_CONFIG_FILES = {
|
|
149
|
+
MCP: MCP.ROOT_CONFIG_FILE,
|
|
150
|
+
DOT_MCP: ".mcp.json",
|
|
151
|
+
ENV_EXAMPLE: ".env.example",
|
|
152
|
+
VSCODE_MCP: ".vscode/mcp.json",
|
|
153
|
+
};
|
|
154
|
+
// ─── Shim template placeholders ───────────────────────────────────────────────
|
|
155
|
+
export const TEMPLATE_PLACEHOLDERS = {
|
|
156
|
+
FRAMEWORK_DIR: "{{FRAMEWORK_DIR}}",
|
|
157
|
+
ADAPTER: "{{ADAPTER}}",
|
|
158
|
+
BACKEND_DIR: "{{BACKEND_DIR}}",
|
|
159
|
+
FRONTEND_DIR: "{{FRONTEND_DIR}}",
|
|
160
|
+
DOCS_DIR: "{{DOCS_DIR}}",
|
|
161
|
+
TESTS_DIR: "{{TESTS_DIR}}",
|
|
162
|
+
};
|
|
163
|
+
// ─── File extensions ─────────────────────────────────────────────────────────
|
|
164
|
+
export const AGENT_FILE_EXT = {
|
|
165
|
+
MARKDOWN: ".md",
|
|
166
|
+
CURSOR_RULE: ".mdc",
|
|
167
|
+
};
|
|
168
|
+
// ─── Path helpers ─────────────────────────────────────────────────────────────
|
|
169
|
+
export function pathJoin(...segments) {
|
|
170
|
+
return segments.filter(Boolean).join("/");
|
|
171
|
+
}
|
|
172
|
+
export function corePath(...segments) {
|
|
173
|
+
return pathJoin(FRAMEWORK.CORE_DIR, ...segments);
|
|
174
|
+
}
|
|
175
|
+
export function unifiedHubPath(...segments) {
|
|
176
|
+
return pathJoin(FRAMEWORK.UNIFIED_HUB_DIR, ...segments);
|
|
177
|
+
}
|
|
178
|
+
export function unifiedAdapterPath(slug, ...segments) {
|
|
179
|
+
return unifiedHubPath(slug, ...segments);
|
|
180
|
+
}
|
|
181
|
+
export function knowledgePath(filename) {
|
|
182
|
+
return corePath(FRAMEWORK_SUBDIRS.KNOWLEDGE, filename);
|
|
183
|
+
}
|
|
184
|
+
/** Backward-compatible aliases */
|
|
185
|
+
export const CORE_FRAMEWORK_DIR = FRAMEWORK.CORE_DIR;
|
|
186
|
+
export const UNIFIED_HUB_DIR = FRAMEWORK.UNIFIED_HUB_DIR;
|
|
187
|
+
export const SKILLS_HUB_PATH = pathJoin(UNIFIED_HUB_DIR, FRAMEWORK_SUBDIRS.SKILLS);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enterprise Base Error class for the Agent Atabey Framework.
|
|
3
|
+
*/
|
|
4
|
+
export class AtabeyBaseError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
timestamp;
|
|
7
|
+
details;
|
|
8
|
+
solution;
|
|
9
|
+
constructor(message, code = "ATABEY_INTERNAL_ERROR", details, solution) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = this.constructor.name;
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.timestamp = new Date();
|
|
14
|
+
this.details = details;
|
|
15
|
+
this.solution = solution;
|
|
16
|
+
// Ensure proper prototype chain and capture stack trace
|
|
17
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
18
|
+
if (Error.captureStackTrace) {
|
|
19
|
+
Error.captureStackTrace(this, this.constructor);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Converts the error into a structured JSON log format.
|
|
24
|
+
*/
|
|
25
|
+
toJSON() {
|
|
26
|
+
return {
|
|
27
|
+
name: this.name,
|
|
28
|
+
message: this.message,
|
|
29
|
+
code: this.code,
|
|
30
|
+
timestamp: this.timestamp.toISOString(),
|
|
31
|
+
details: this.details,
|
|
32
|
+
solution: this.solution,
|
|
33
|
+
stack: this.stack,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Thrown when configuration loading or validation fails.
|
|
39
|
+
*/
|
|
40
|
+
export class ConfigurationError extends AtabeyBaseError {
|
|
41
|
+
constructor(message, details, solution) {
|
|
42
|
+
super(message, "CONFIGURATION_ERROR", details, solution);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Thrown when data schema or payload validation fails.
|
|
47
|
+
*/
|
|
48
|
+
export class ValidationError extends AtabeyBaseError {
|
|
49
|
+
constructor(message, details, solution) {
|
|
50
|
+
super(message, "VALIDATION_ERROR", details, solution);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Thrown when adapter initialization or execution fails.
|
|
55
|
+
*/
|
|
56
|
+
export class AdapterError extends AtabeyBaseError {
|
|
57
|
+
constructor(message, adapterId, details, solution) {
|
|
58
|
+
super(`Adapter '${adapterId}' failure: ${message}`, "ADAPTER_ERROR", details, solution);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Thrown when orchestration or agent communication fails.
|
|
63
|
+
*/
|
|
64
|
+
export class OrchestrationError extends AtabeyBaseError {
|
|
65
|
+
constructor(message, details, solution) {
|
|
66
|
+
super(message, "ORCHESTRATION_ERROR", details, solution);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { execFileSync } from "child_process";
|
|
4
|
+
import { logger } from "./logger.js";
|
|
5
|
+
export function ensureDir(dirPath, dryRun = false) {
|
|
6
|
+
if (!fs.existsSync(dirPath)) {
|
|
7
|
+
if (dryRun) {
|
|
8
|
+
logger.info(`[DRY RUN] Would create directory: ${dirPath}`);
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Writes content to a file atomically by using a temporary file.
|
|
17
|
+
* This prevents data corruption during unexpected system failures.
|
|
18
|
+
*/
|
|
19
|
+
export function writeTextFile(filePath, content, dryRun = false) {
|
|
20
|
+
if (dryRun) {
|
|
21
|
+
logger.info(`[DRY RUN] Would write file: ${filePath}`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const dir = path.dirname(filePath);
|
|
25
|
+
ensureDir(dir);
|
|
26
|
+
const tempPath = `${filePath}.${Math.random().toString(36).slice(2, 9)}.tmp`;
|
|
27
|
+
const finalContent = content.endsWith("\n") ? content : `${content}\n`;
|
|
28
|
+
try {
|
|
29
|
+
fs.writeFileSync(tempPath, finalContent, "utf8");
|
|
30
|
+
fs.renameSync(tempPath, filePath);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
if (fs.existsSync(tempPath))
|
|
34
|
+
fs.unlinkSync(tempPath);
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function appendFile(filePath, content, dryRun = false) {
|
|
39
|
+
if (dryRun) {
|
|
40
|
+
logger.info(`[DRY RUN] Would append to file: ${filePath}`);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
ensureDir(path.dirname(filePath));
|
|
44
|
+
fs.appendFileSync(filePath, content, "utf8");
|
|
45
|
+
}
|
|
46
|
+
export function writeJsonFile(filePath, value, dryRun = false) {
|
|
47
|
+
writeTextFile(filePath, JSON.stringify(value, null, 2), dryRun);
|
|
48
|
+
}
|
|
49
|
+
export function runCommandQuiet(command, args, cwd) {
|
|
50
|
+
execFileSync(command, args, { cwd, stdio: "ignore" });
|
|
51
|
+
}
|