@moskala/oneagent-core 0.2.4 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moskala/oneagent-core",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "type": "module",
5
5
  "description": "Core library for oneagent — one source of truth for AI agent rules",
6
6
  "license": "MIT",
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/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",
@@ -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 = [".cursorrules"];
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,7 +22,7 @@ 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, rules),
25
+ ...buildRulesSymlinks(root, targets),
26
26
  ...buildSkillSymlinks(root, targets),
27
27
  // .agents/skills skipped — handled by migrateAgentsSkillsDir
28
28
  ];
@@ -73,7 +73,7 @@ export async function generate(root: string, config: Config): Promise<void> {
73
73
  await migrateRuleAndSkillFiles(root);
74
74
 
75
75
  const mainSymlinks = buildMainSymlinks(root, targets);
76
- const rulesSymlinks = buildRulesSymlinks(root, targets, rules);
76
+ const rulesSymlinks = buildRulesSymlinks(root, targets);
77
77
  const skillSymlinks = await buildSkillSymlinks(root, targets);
78
78
  await createAllSymlinks([...mainSymlinks, ...rulesSymlinks, ...skillSymlinks, ...buildAgentsDirSymlinks(root)]);
79
79
 
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./types.ts";
2
+ export * from "./agents.ts";
2
3
  export * from "./config.ts";
3
4
  export * from "./detect.ts";
4
5
  export * from "./rules.ts";
package/src/status.ts CHANGED
@@ -44,7 +44,7 @@ export async function checkStatus(root: string, config: Config): Promise<StatusR
44
44
 
45
45
  const allEntries = [
46
46
  ...buildMainSymlinks(root, targets),
47
- ...buildRulesSymlinks(root, targets, rules),
47
+ ...buildRulesSymlinks(root, targets),
48
48
  ...buildSkillSymlinks(root, targets),
49
49
  ...buildAgentsDirSymlinks(root),
50
50
  ];
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, RuleFile, SymlinkCheck, SymlinkEntry } from "./types.ts";
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
- let symlinkPath: string;
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,64 +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: string,
62
- targets: AgentTarget[],
63
- rules: RuleFile[],
64
- ): SymlinkEntry[] {
65
- const entries: SymlinkEntry[] = [];
66
-
67
- for (const target of targets) {
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
- // Creates whole-directory symlinks: .claude/skills → .oneagent/skills, .cursor/skills → .oneagent/skills
102
53
  export function buildSkillSymlinks(root: string, targets: AgentTarget[]): SymlinkEntry[] {
103
54
  const targetAbs = path.join(root, ".oneagent/skills");
104
- const agentDirs: Partial<Record<AgentTarget, string>> = {
105
- claude: path.join(root, ".claude/skills"),
106
- cursor: path.join(root, ".cursor/skills"),
107
- windsurf: path.join(root, ".windsurf/skills"),
108
- copilot: path.join(root, ".github/skills"),
109
- };
110
-
111
- return (Object.entries(agentDirs) as [AgentTarget, string][])
112
- .filter(([target]) => targets.includes(target))
113
- .map(([, dir]) => ({
114
- symlinkPath: dir,
115
- target: relativeTarget(dir, targetAbs),
116
- label: path.relative(root, dir),
117
- }));
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
+ });
118
61
  }
119
62
 
120
63
  export function buildAgentsDirSymlinks(root: string): SymlinkEntry[] {