@schneiderjoseph/devia 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 — 2026-09-09
4
+
5
+ The standard is unchanged: `VERSION` stays at 0.1.0, no adopter needs `devia sync`.
6
+
7
+ ### devia is for every agent
8
+
9
+ 0.2.0 shipped `--global` serving Claude Code alone and reported the other agents as SKIP. Two of
10
+ those reasons were wrong: they came from an absence never verified. `~/.cursor/rules/` holds
11
+ user-level `.mdc` rules, and `~/.codex/skills/` uses the same `SKILL.md` convention as Claude
12
+ Code. The decision is recorded as G6: no agent is privileged.
13
+
14
+ `devia skills install --global` now writes, each in the format the agent actually reads:
15
+
16
+ | Agent | Path | File |
17
+ |---|---|---|
18
+ | Claude Code | `~/.claude/skills/devia/SKILL.md` | skill pack |
19
+ | Codex | `~/.codex/skills/devia/SKILL.md` | skill pack |
20
+ | Cursor | `~/.cursor/rules/devia.mdc` | rules adapter |
21
+ | Gemini | `~/.gemini/GEMINI.md` | universal contract, only when absent or empty |
22
+ | Copilot, Windsurf | — | `SKIP`: user-level configuration is editor settings, not a file devia can place (`12_DEBT.md` D8) |
23
+
24
+ `CLAUDE_CONFIG_DIR` and `CODEX_HOME` are honoured when set. A directory the agent owns is
25
+ written to freely; a file the **user** owns is written only when absent or empty, and otherwise
26
+ skipped with the reason rather than replaced. `--force` overrides both and names every path.
27
+
3
28
  ## 0.2.0 — 2026-09-09
4
29
 
5
30
  The standard is unchanged: `VERSION` stays at 0.1.0 and no adopter needs `devia sync`. This
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@schneiderjoseph/devia",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "One standard, one memory: engineering and design rules plus living project memory for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -38,57 +38,84 @@ export function installAdapters(root, { force = false, agents = Object.keys(ADAP
38
38
  return { written, kept };
39
39
  }
40
40
 
41
+ const SKILL_PACK = path.join("skills", "devia", "SKILL.md");
42
+ const adapter = (file) => path.join("templates", "agents", file);
43
+
41
44
  /**
42
- * Where an agent keeps skills for every project, not one.
45
+ * Where each agent keeps a contract that applies to every project, not one.
43
46
  *
44
- * Only agents whose user-level location devia can actually determine are listed. The rest are
45
- * reported as SKIP with the reason: guessing a path in someone's home directory and writing to
46
- * it is exactly the kind of confident wrong answer this tool exists to prevent.
47
+ * devia is for every agent, so an agent is listed here as soon as its user-level location is
48
+ * known — and reported as SKIP with the reason when it is not. Each entry carries the file the
49
+ * agent actually reads: a skill pack where the agent loads skills, its own rules format
50
+ * otherwise. Guessing a path inside someone's home directory is the confident wrong answer this
51
+ * tool exists to prevent, so absence of evidence is reported, never rounded up.
47
52
  */
48
- function globalSkillTargets() {
53
+ function globalTargets() {
49
54
  const home = os.homedir();
50
- const claudeDir = process.env.CLAUDE_CONFIG_DIR
51
- ? path.resolve(process.env.CLAUDE_CONFIG_DIR)
52
- : path.join(home, ".claude");
55
+ const configDir = (envVar, fallback) =>
56
+ process.env[envVar] ? path.resolve(process.env[envVar]) : path.join(home, fallback);
57
+
53
58
  return {
54
59
  claude: {
55
- target: path.join(claudeDir, "skills", "devia", "SKILL.md"),
60
+ target: path.join(configDir("CLAUDE_CONFIG_DIR", ".claude"), "skills", "devia", "SKILL.md"),
61
+ source: SKILL_PACK,
62
+ },
63
+ codex: {
64
+ target: path.join(configDir("CODEX_HOME", ".codex"), "skills", "devia", "SKILL.md"),
65
+ source: SKILL_PACK,
56
66
  },
57
67
  cursor: {
58
- reason: "no user-level skill directory — use the per-project .cursor/rules/devia.mdc",
68
+ target: path.join(home, ".cursor", "rules", "devia.mdc"),
69
+ source: adapter("cursor.mdc"),
70
+ },
71
+ gemini: {
72
+ // One file the user owns, not a directory devia can add to: written only when it is
73
+ // absent or empty, so a global instruction file is never silently replaced.
74
+ target: path.join(home, ".gemini", "GEMINI.md"),
75
+ source: adapter("AGENTS.md"),
76
+ onlyWhenEmpty: true,
59
77
  },
60
78
  copilot: {
61
- reason: "instructions are per-repository .github/copilot-instructions.md",
79
+ reason: "user-level instructions live in the editor's settings, not a file devia can place",
62
80
  },
63
81
  windsurf: {
64
- reason: "rules are per-repository — .windsurfrules",
82
+ reason: "no user-level rules file — .windsurfrules is per repository",
65
83
  },
66
84
  };
67
85
  }
68
86
 
69
87
  /**
70
- * Install the skill once for every project. This is the only path that writes outside `--root`,
71
- * it happens only behind `--global`, and it prints every path it touches (04_PERMISSIONS.md).
88
+ * Install the contract once for every project. This is the only path that writes outside
89
+ * `--root`: it happens behind `--global`, and it prints every path it touches
90
+ * (`04_PERMISSIONS.md`).
72
91
  */
73
92
  export function installGlobalSkill({ force = false } = {}) {
74
- const skill = read(path.join(packageRoot, "skills", "devia", "SKILL.md"));
75
93
  const written = [];
76
94
  const kept = [];
77
95
  const skipped = [];
78
- for (const [key, entry] of Object.entries(globalSkillTargets())) {
96
+
97
+ for (const [key, entry] of Object.entries(globalTargets())) {
79
98
  if (!entry.target) {
80
99
  skipped.push([key, entry.reason]);
81
100
  continue;
82
101
  }
83
- if (skill === null) {
84
- skipped.push([key, "skill pack missing from the installed package"]);
102
+ const content = read(path.join(packageRoot, entry.source));
103
+ if (content === null) {
104
+ skipped.push([key, `${entry.source} missing from the installed package`]);
85
105
  continue;
86
106
  }
87
107
  if (exists(entry.target) && !force) {
88
- kept.push([key, entry.target]);
89
- continue;
108
+ const current = read(entry.target) || "";
109
+ if (entry.onlyWhenEmpty && current.trim()) {
110
+ skipped.push([key, `${path.basename(entry.target)} already has content — add the contract yourself`]);
111
+ continue;
112
+ }
113
+ if (current.trim()) {
114
+ kept.push([key, entry.target]);
115
+ continue;
116
+ }
90
117
  }
91
- writeFile(entry.target, skill);
118
+ writeFile(entry.target, content);
92
119
  written.push([key, entry.target]);
93
120
  }
94
121
  return { written, kept, skipped };