@moskala/oneagent-core 0.2.3 → 0.2.5
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/package.json +1 -1
- package/src/agents.ts +71 -0
- package/src/apply-template.ts +7 -3
- package/src/config.ts +5 -3
- package/src/copilot.ts +2 -2
- package/src/detect.ts +3 -2
- package/src/generate.ts +8 -7
- package/src/index.ts +1 -0
- package/src/opencode.ts +3 -2
- package/src/rules.ts +1 -1
- package/src/skills.ts +1 -1
- package/src/status.ts +5 -4
- package/src/symlinks.ts +23 -75
package/package.json
CHANGED
package/src/agents.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { AgentTarget } from "./types.ts";
|
|
2
|
+
|
|
3
|
+
export interface AgentDefinition {
|
|
4
|
+
target: AgentTarget;
|
|
5
|
+
displayName: string;
|
|
6
|
+
hint: string;
|
|
7
|
+
/** Paths (relative to root) checked to detect agent presence. Any match = present. */
|
|
8
|
+
detectIndicators: string[];
|
|
9
|
+
/** Symlink path for main instructions file (relative to root). */
|
|
10
|
+
mainFile: string;
|
|
11
|
+
/** Whole-dir symlink for rules (relative to root). Omit if not applicable. */
|
|
12
|
+
rulesDir?: string;
|
|
13
|
+
/** Whole-dir symlink for skills (relative to root). Omit if not applicable. */
|
|
14
|
+
skillsDir?: string;
|
|
15
|
+
/** Legacy files to remove during init (superseded by current format). */
|
|
16
|
+
deprecatedFiles?: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const AGENT_DEFINITIONS: AgentDefinition[] = [
|
|
20
|
+
{
|
|
21
|
+
target: "claude",
|
|
22
|
+
displayName: "Claude Code",
|
|
23
|
+
hint: "CLAUDE.md + .claude/rules/",
|
|
24
|
+
detectIndicators: ["CLAUDE.md", ".claude"],
|
|
25
|
+
mainFile: "CLAUDE.md",
|
|
26
|
+
rulesDir: ".claude/rules",
|
|
27
|
+
skillsDir: ".claude/skills",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
target: "cursor",
|
|
31
|
+
displayName: "Cursor",
|
|
32
|
+
hint: "AGENTS.md + .cursor/rules/",
|
|
33
|
+
detectIndicators: [".cursor", ".cursorrules"],
|
|
34
|
+
mainFile: "AGENTS.md",
|
|
35
|
+
rulesDir: ".cursor/rules",
|
|
36
|
+
skillsDir: ".cursor/skills",
|
|
37
|
+
deprecatedFiles: [".cursorrules"],
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
target: "windsurf",
|
|
41
|
+
displayName: "Windsurf",
|
|
42
|
+
hint: "AGENTS.md + .windsurf/rules/",
|
|
43
|
+
detectIndicators: [".windsurf", ".windsurfrules"],
|
|
44
|
+
mainFile: "AGENTS.md",
|
|
45
|
+
rulesDir: ".windsurf/rules",
|
|
46
|
+
skillsDir: ".windsurf/skills",
|
|
47
|
+
deprecatedFiles: [".windsurfrules"],
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
target: "opencode",
|
|
51
|
+
displayName: "OpenCode",
|
|
52
|
+
hint: "AGENTS.md + opencode.json",
|
|
53
|
+
detectIndicators: ["opencode.json", ".opencode"],
|
|
54
|
+
mainFile: "AGENTS.md",
|
|
55
|
+
// rules: handled via opencode.json config, not symlinks
|
|
56
|
+
// skills: handled via .agents/skills dir symlink
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
target: "copilot",
|
|
60
|
+
displayName: "GitHub Copilot",
|
|
61
|
+
hint: ".github/instructions/*.instructions.md",
|
|
62
|
+
detectIndicators: [".github/copilot-instructions.md", ".github"],
|
|
63
|
+
mainFile: ".github/copilot-instructions.md",
|
|
64
|
+
skillsDir: ".github/skills",
|
|
65
|
+
// rules: generated as .instructions.md files, not symlinks
|
|
66
|
+
},
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
export function getAgentDef(target: AgentTarget): AgentDefinition {
|
|
70
|
+
return AGENT_DEFINITIONS.find((d) => d.target === target)!;
|
|
71
|
+
}
|
package/src/apply-template.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fs from "fs/promises";
|
|
3
|
+
import { execFile } from "child_process";
|
|
4
|
+
import { promisify } from "util";
|
|
5
|
+
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
3
7
|
|
|
4
8
|
export interface TemplateDefinition {
|
|
5
9
|
name: string;
|
|
@@ -17,10 +21,10 @@ export async function applyTemplateFiles(root: string, template: TemplateDefinit
|
|
|
17
21
|
await fs.mkdir(path.join(oneagentDir, "rules"), { recursive: true });
|
|
18
22
|
await fs.mkdir(path.join(oneagentDir, "skills"), { recursive: true });
|
|
19
23
|
|
|
20
|
-
await
|
|
24
|
+
await fs.writeFile(path.join(oneagentDir, "instructions.md"), template.instructions);
|
|
21
25
|
|
|
22
26
|
for (const rule of template.rules) {
|
|
23
|
-
await
|
|
27
|
+
await fs.writeFile(path.join(oneagentDir, "rules", `${rule.name}.md`), rule.content);
|
|
24
28
|
}
|
|
25
29
|
}
|
|
26
30
|
|
|
@@ -33,7 +37,7 @@ export async function installTemplateSkills(
|
|
|
33
37
|
): Promise<void> {
|
|
34
38
|
for (const identifier of template.skills) {
|
|
35
39
|
try {
|
|
36
|
-
await
|
|
40
|
+
await execFileAsync("npx", ["skills", "add", identifier, "--agent", "universal", "--yes"], { cwd: root });
|
|
37
41
|
onSkillInstalled?.(identifier);
|
|
38
42
|
} catch (err) {
|
|
39
43
|
const message = err instanceof Error ? err.message : String(err);
|
package/src/config.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { parse, stringify } from "yaml";
|
|
2
2
|
import path from "path";
|
|
3
|
+
import fs from "fs/promises";
|
|
3
4
|
import type { AgentTarget, Config } from "./types.ts";
|
|
4
5
|
|
|
5
6
|
const CONFIG_REL = ".oneagent/config.yml";
|
|
@@ -15,15 +16,16 @@ export function makeTargets(...enabled: AgentTarget[]): Record<AgentTarget, bool
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export async function configExists(root: string): Promise<boolean> {
|
|
18
|
-
return
|
|
19
|
+
return fs.access(path.join(root, CONFIG_REL)).then(() => true, () => false);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
export async function readConfig(root: string): Promise<Config> {
|
|
22
|
-
const content = await
|
|
23
|
+
const content = await fs.readFile(path.join(root, CONFIG_REL), "utf-8");
|
|
23
24
|
return parse(content) as Config;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
export async function writeConfig(root: string, config: Config): Promise<void> {
|
|
27
28
|
const filePath = path.join(root, CONFIG_REL);
|
|
28
|
-
await
|
|
29
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
30
|
+
await fs.writeFile(filePath, stringify(config));
|
|
29
31
|
}
|
package/src/copilot.ts
CHANGED
|
@@ -13,7 +13,7 @@ export function copilotFilePath(root: string, ruleName: string): string {
|
|
|
13
13
|
export async function generateCopilotRule(root: string, rule: RuleFile): Promise<void> {
|
|
14
14
|
const filePath = copilotFilePath(root, rule.name);
|
|
15
15
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
16
|
-
await
|
|
16
|
+
await fs.writeFile(filePath, buildCopilotContent(rule));
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export async function generateCopilotRules(root: string, rules: RuleFile[]): Promise<void> {
|
|
@@ -34,7 +34,7 @@ export function copilotPromptFilePath(root: string, skillName: string): string {
|
|
|
34
34
|
export async function generateCopilotSkill(root: string, skill: SkillFile): Promise<void> {
|
|
35
35
|
const filePath = copilotPromptFilePath(root, skill.name);
|
|
36
36
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
37
|
-
await
|
|
37
|
+
await fs.writeFile(filePath, buildCopilotPromptContent(skill));
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export async function generateCopilotSkills(root: string, skills: SkillFile[]): Promise<void> {
|
package/src/detect.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fs from "fs/promises";
|
|
3
3
|
import type { DetectedFile } from "./types.ts";
|
|
4
|
+
import { AGENT_DEFINITIONS } from "./agents.ts";
|
|
4
5
|
|
|
5
6
|
export const AGENT_FILES = [
|
|
6
7
|
"CLAUDE.md",
|
|
@@ -22,7 +23,7 @@ export async function readDetectedFile(root: string, rel: string): Promise<Detec
|
|
|
22
23
|
if (resolved.startsWith(path.join(root, ".one"))) return null;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
const content = await
|
|
26
|
+
const content = await fs.readFile(absolutePath, "utf-8");
|
|
26
27
|
return {
|
|
27
28
|
relativePath: rel,
|
|
28
29
|
absolutePath,
|
|
@@ -46,7 +47,7 @@ export function filesHaveSameContent(files: DetectedFile[]): boolean {
|
|
|
46
47
|
return files.every((f) => f.content === first);
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
const DEPRECATED_FILES = [
|
|
50
|
+
const DEPRECATED_FILES = AGENT_DEFINITIONS.flatMap((d) => d.deprecatedFiles ?? []);
|
|
50
51
|
|
|
51
52
|
export async function removeDeprecatedFiles(root: string): Promise<void> {
|
|
52
53
|
for (const rel of DEPRECATED_FILES) {
|
package/src/generate.ts
CHANGED
|
@@ -22,8 +22,8 @@ export async function detectGenerateCollisions(root: string, config: Config): Pr
|
|
|
22
22
|
const mainEntries = buildMainSymlinks(root, targets);
|
|
23
23
|
// 2. Rule/skill symlink paths
|
|
24
24
|
const ruleSkillEntries = [
|
|
25
|
-
...buildRulesSymlinks(root, targets
|
|
26
|
-
...buildSkillSymlinks(root, targets
|
|
25
|
+
...buildRulesSymlinks(root, targets),
|
|
26
|
+
...buildSkillSymlinks(root, targets),
|
|
27
27
|
// .agents/skills skipped — handled by migrateAgentsSkillsDir
|
|
28
28
|
];
|
|
29
29
|
|
|
@@ -41,7 +41,7 @@ export async function detectGenerateCollisions(root: string, config: Config): Pr
|
|
|
41
41
|
...rules.map(async (rule): Promise<DetectedFile | null> => {
|
|
42
42
|
const filePath = copilotFilePath(root, rule.name);
|
|
43
43
|
try {
|
|
44
|
-
const content = await
|
|
44
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
45
45
|
if (content === buildCopilotContent(rule)) return null;
|
|
46
46
|
const stat = await fs.lstat(filePath);
|
|
47
47
|
return { relativePath: path.relative(root, filePath), absolutePath: filePath, sizeBytes: stat.size, modifiedAt: stat.mtime, content };
|
|
@@ -50,7 +50,7 @@ export async function detectGenerateCollisions(root: string, config: Config): Pr
|
|
|
50
50
|
...skills.map(async (skill): Promise<DetectedFile | null> => {
|
|
51
51
|
const filePath = copilotPromptFilePath(root, skill.name);
|
|
52
52
|
try {
|
|
53
|
-
const content = await
|
|
53
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
54
54
|
if (content === buildCopilotPromptContent(skill)) return null;
|
|
55
55
|
const stat = await fs.lstat(filePath);
|
|
56
56
|
return { relativePath: path.relative(root, filePath), absolutePath: filePath, sizeBytes: stat.size, modifiedAt: stat.mtime, content };
|
|
@@ -70,10 +70,11 @@ export async function generate(root: string, config: Config): Promise<void> {
|
|
|
70
70
|
const [rules, skills] = await Promise.all([readRules(root), readSkills(root)]);
|
|
71
71
|
const targets = activeTargets(config);
|
|
72
72
|
|
|
73
|
-
const mainSymlinks = buildMainSymlinks(root, targets);
|
|
74
|
-
const rulesSymlinks = buildRulesSymlinks(root, targets, rules);
|
|
75
|
-
const skillSymlinks = buildSkillSymlinks(root, targets, skills);
|
|
76
73
|
await migrateRuleAndSkillFiles(root);
|
|
74
|
+
|
|
75
|
+
const mainSymlinks = buildMainSymlinks(root, targets);
|
|
76
|
+
const rulesSymlinks = buildRulesSymlinks(root, targets);
|
|
77
|
+
const skillSymlinks = await buildSkillSymlinks(root, targets);
|
|
77
78
|
await createAllSymlinks([...mainSymlinks, ...rulesSymlinks, ...skillSymlinks, ...buildAgentsDirSymlinks(root)]);
|
|
78
79
|
|
|
79
80
|
if (targets.includes("copilot")) {
|
package/src/index.ts
CHANGED
package/src/opencode.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
+
import fs from "fs/promises";
|
|
2
3
|
import type { RuleFile } from "./types.ts";
|
|
3
4
|
|
|
4
5
|
export async function readOpencode(root: string): Promise<Record<string, unknown> | null> {
|
|
5
6
|
try {
|
|
6
|
-
const content = await
|
|
7
|
+
const content = await fs.readFile(path.join(root, "opencode.json"), "utf-8");
|
|
7
8
|
return JSON.parse(content) as Record<string, unknown>;
|
|
8
9
|
} catch {
|
|
9
10
|
return null;
|
|
@@ -20,5 +21,5 @@ export function buildOpencodeConfig(existing: Record<string, unknown> | null): o
|
|
|
20
21
|
export async function writeOpencode(root: string, _rules: RuleFile[]): Promise<void> {
|
|
21
22
|
const existing = await readOpencode(root);
|
|
22
23
|
const config = buildOpencodeConfig(existing);
|
|
23
|
-
await
|
|
24
|
+
await fs.writeFile(path.join(root, "opencode.json"), JSON.stringify(config, null, 2) + "\n");
|
|
24
25
|
}
|
package/src/rules.ts
CHANGED
|
@@ -16,7 +16,7 @@ export function parseFrontmatter(raw: string): { applyTo: string; content: strin
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export async function readRuleFile(filePath: string): Promise<RuleFile> {
|
|
19
|
-
const raw = await
|
|
19
|
+
const raw = await fs.readFile(filePath, "utf-8");
|
|
20
20
|
const { applyTo, content } = parseFrontmatter(raw);
|
|
21
21
|
return { name: path.basename(filePath, ".md"), path: filePath, applyTo, content };
|
|
22
22
|
}
|
package/src/skills.ts
CHANGED
|
@@ -23,7 +23,7 @@ export function parseSkillFrontmatter(raw: string): { description: string; mode:
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export async function readSkillFile(filePath: string): Promise<SkillFile> {
|
|
26
|
-
const raw = await
|
|
26
|
+
const raw = await fs.readFile(filePath, "utf-8");
|
|
27
27
|
const { description, mode, content } = parseSkillFrontmatter(raw);
|
|
28
28
|
return { name: path.basename(filePath, ".md"), path: filePath, description, mode, content };
|
|
29
29
|
}
|
package/src/status.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fs from "fs/promises";
|
|
1
2
|
import type { Config, GeneratedFileCheck, OpenCodeCheck, RuleFile, StatusResult } from "./types.ts";
|
|
2
3
|
import { activeTargets } from "./config.ts";
|
|
3
4
|
import { readRules } from "./rules.ts";
|
|
@@ -10,7 +11,7 @@ export async function checkGeneratedFile(root: string, rule: RuleFile): Promise<
|
|
|
10
11
|
const filePath = copilotFilePath(root, rule.name);
|
|
11
12
|
const expected = buildCopilotContent(rule);
|
|
12
13
|
try {
|
|
13
|
-
const content = await
|
|
14
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
14
15
|
return { path: filePath, exists: true, upToDate: content === expected };
|
|
15
16
|
} catch {
|
|
16
17
|
return { path: filePath, exists: false, upToDate: false };
|
|
@@ -30,7 +31,7 @@ export async function checkCopilotPrompt(root: string, skill: import("./types.ts
|
|
|
30
31
|
const filePath = copilotPromptFilePath(root, skill.name);
|
|
31
32
|
const expected = buildCopilotPromptContent(skill);
|
|
32
33
|
try {
|
|
33
|
-
const content = await
|
|
34
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
34
35
|
return { path: filePath, exists: true, upToDate: content === expected };
|
|
35
36
|
} catch {
|
|
36
37
|
return { path: filePath, exists: false, upToDate: false };
|
|
@@ -43,8 +44,8 @@ export async function checkStatus(root: string, config: Config): Promise<StatusR
|
|
|
43
44
|
|
|
44
45
|
const allEntries = [
|
|
45
46
|
...buildMainSymlinks(root, targets),
|
|
46
|
-
...buildRulesSymlinks(root, targets
|
|
47
|
-
...buildSkillSymlinks(root, targets
|
|
47
|
+
...buildRulesSymlinks(root, targets),
|
|
48
|
+
...buildSkillSymlinks(root, targets),
|
|
48
49
|
...buildAgentsDirSymlinks(root),
|
|
49
50
|
];
|
|
50
51
|
|
package/src/symlinks.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from "path";
|
|
2
2
|
import fs from "fs/promises";
|
|
3
|
-
import type { AgentTarget,
|
|
3
|
+
import type { AgentTarget, SymlinkCheck, SymlinkEntry } from "./types.ts";
|
|
4
|
+
import { AGENT_DEFINITIONS } from "./agents.ts";
|
|
4
5
|
|
|
5
6
|
export async function ensureDir(dirPath: string): Promise<void> {
|
|
6
7
|
await fs.mkdir(dirPath, { recursive: true });
|
|
@@ -25,26 +26,8 @@ export function buildMainSymlinks(root: string, targets: AgentTarget[]): Symlink
|
|
|
25
26
|
const seen = new Map<string, SymlinkEntry>();
|
|
26
27
|
|
|
27
28
|
for (const target of targets) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
switch (target) {
|
|
31
|
-
case "claude":
|
|
32
|
-
symlinkPath = path.join(root, "CLAUDE.md");
|
|
33
|
-
break;
|
|
34
|
-
case "cursor":
|
|
35
|
-
symlinkPath = path.join(root, "AGENTS.md");
|
|
36
|
-
break;
|
|
37
|
-
case "windsurf":
|
|
38
|
-
symlinkPath = path.join(root, ".windsurfrules");
|
|
39
|
-
break;
|
|
40
|
-
case "opencode":
|
|
41
|
-
symlinkPath = path.join(root, "AGENTS.md");
|
|
42
|
-
break;
|
|
43
|
-
case "copilot":
|
|
44
|
-
symlinkPath = path.join(root, ".github/copilot-instructions.md");
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
|
|
29
|
+
const def = AGENT_DEFINITIONS.find((d) => d.target === target)!;
|
|
30
|
+
const symlinkPath = path.join(root, def.mainFile);
|
|
48
31
|
if (!seen.has(symlinkPath)) {
|
|
49
32
|
seen.set(symlinkPath, {
|
|
50
33
|
symlinkPath,
|
|
@@ -57,59 +40,24 @@ export function buildMainSymlinks(root: string, targets: AgentTarget[]): Symlink
|
|
|
57
40
|
return Array.from(seen.values());
|
|
58
41
|
}
|
|
59
42
|
|
|
60
|
-
export function buildRulesSymlinks(
|
|
61
|
-
root
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
let rulesDir: string | null = null;
|
|
69
|
-
|
|
70
|
-
switch (target) {
|
|
71
|
-
case "claude":
|
|
72
|
-
rulesDir = path.join(root, ".claude/rules");
|
|
73
|
-
break;
|
|
74
|
-
case "cursor":
|
|
75
|
-
rulesDir = path.join(root, ".cursor/rules");
|
|
76
|
-
break;
|
|
77
|
-
case "windsurf":
|
|
78
|
-
rulesDir = path.join(root, ".windsurf/rules");
|
|
79
|
-
break;
|
|
80
|
-
case "opencode":
|
|
81
|
-
case "copilot":
|
|
82
|
-
rulesDir = null;
|
|
83
|
-
break;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (!rulesDir) continue;
|
|
87
|
-
|
|
88
|
-
for (const rule of rules) {
|
|
89
|
-
const symlinkPath = path.join(rulesDir, `${rule.name}.md`);
|
|
90
|
-
entries.push({
|
|
91
|
-
symlinkPath,
|
|
92
|
-
target: relativeTarget(symlinkPath, rule.path),
|
|
93
|
-
label: path.relative(root, symlinkPath),
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return entries;
|
|
43
|
+
export function buildRulesSymlinks(root: string, targets: AgentTarget[]): SymlinkEntry[] {
|
|
44
|
+
const targetAbs = path.join(root, ".oneagent/rules");
|
|
45
|
+
return AGENT_DEFINITIONS
|
|
46
|
+
.filter((d) => targets.includes(d.target) && d.rulesDir)
|
|
47
|
+
.map((d) => {
|
|
48
|
+
const symlinkPath = path.join(root, d.rulesDir!);
|
|
49
|
+
return { symlinkPath, target: relativeTarget(symlinkPath, targetAbs), label: d.rulesDir! };
|
|
50
|
+
});
|
|
99
51
|
}
|
|
100
52
|
|
|
101
|
-
export function buildSkillSymlinks(root: string, targets: AgentTarget[]
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
target: relativeTarget(symlinkPath, skill.path),
|
|
110
|
-
label: path.relative(root, symlinkPath),
|
|
111
|
-
};
|
|
112
|
-
});
|
|
53
|
+
export function buildSkillSymlinks(root: string, targets: AgentTarget[]): SymlinkEntry[] {
|
|
54
|
+
const targetAbs = path.join(root, ".oneagent/skills");
|
|
55
|
+
return AGENT_DEFINITIONS
|
|
56
|
+
.filter((d) => targets.includes(d.target) && d.skillsDir)
|
|
57
|
+
.map((d) => {
|
|
58
|
+
const symlinkPath = path.join(root, d.skillsDir!);
|
|
59
|
+
return { symlinkPath, target: relativeTarget(symlinkPath, targetAbs), label: d.skillsDir! };
|
|
60
|
+
});
|
|
113
61
|
}
|
|
114
62
|
|
|
115
63
|
export function buildAgentsDirSymlinks(root: string): SymlinkEntry[] {
|
|
@@ -142,15 +90,15 @@ async function migrateFilesFromDir(srcDir: string, destDir: string, root: string
|
|
|
142
90
|
if (destExists) {
|
|
143
91
|
// dest exists — compare content before deleting source
|
|
144
92
|
const [srcContent, destContent] = await Promise.all([
|
|
145
|
-
|
|
146
|
-
|
|
93
|
+
fs.readFile(srcFile, "utf-8"),
|
|
94
|
+
fs.readFile(destFile, "utf-8"),
|
|
147
95
|
]);
|
|
148
96
|
if (srcContent !== destContent) {
|
|
149
97
|
// Different content — backup source before deleting
|
|
150
98
|
const backupDir = path.join(root, ".oneagent/backup");
|
|
151
99
|
await fs.mkdir(backupDir, { recursive: true });
|
|
152
100
|
const safeName = path.relative(root, srcFile).replace(/\//g, "_");
|
|
153
|
-
await
|
|
101
|
+
await fs.writeFile(path.join(backupDir, safeName), srcContent);
|
|
154
102
|
}
|
|
155
103
|
await fs.unlink(srcFile); // safe to delete — dest has the content (or backup was created)
|
|
156
104
|
} else {
|