oris-skills 2.2.0 → 2.2.1

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "oris-skills",
3
3
  "description": "Oris Skills: one entry point (/oris-flow), guided discovery-to-implementation flow, and self-tuning verified loops. Cross-model: Cursor, Claude Code, Codex.",
4
- "version": "2.2.0",
4
+ "version": "2.2.1",
5
5
  "author": {
6
6
  "name": "Davide Baldassarre"
7
7
  },
package/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.2.1 - 2026-07-07
4
+
5
+ - **Loop roles are invisible to users**: the installer no longer writes `oris-loop-{executor,verifier,doctor,debriefer}` into `~/.cursor/agents/`, `~/.claude/agents/`, or `~/.codex/agents/` — they showed up as manually invocable subagents, but the loop orchestrator never spawns them by name (it passes each loop's `prompts/<role>.md` to a generic subagent). `agents/` stays in the repo as the canonical role definitions for development. Uninstall (and therefore reinstall/upgrade) now also removes adapters left behind by earlier versions.
6
+
3
7
  ## 2.2.0 - 2026-07-07
4
8
 
5
9
  - **One skill**: users see only `/oris-flow`. Every former skill (setup, new, discover, criteria, plan, implement, fix, verify, change, architecture, merge, docs, help, loop) is now a route reference inside `skills/oris-flow/references/`; the router reads the intent, picks ONE route, and applies its reference in the same chat. Loop prompt templates moved to `skills/oris-flow/templates/`.
@@ -7,7 +7,7 @@ Read this before explaining or changing the project.
7
7
  ```text
8
8
  skills/oris-flow/ the ONE user-facing skill: SKILL.md routes; references/ hold every route (setup…loop); templates/ hold the loop role prompts
9
9
  references/ shared contracts (conventions, loop-contract, settings, repo-map, doc-policy, clean-code-checklist) + JSON schemas
10
- agents/ canonical subagent definitions; adapters generated per platform
10
+ agents/ canonical loop-role definitions (internal); never installed into user agent homes — roles reach subagents via each loop's prompts/<role>.md
11
11
  scripts/ Node CLI (oris-skills.mjs), loop runtime, installer, tests
12
12
  docs/ this file, user-guide, maintainer-guide, distribution
13
13
  ```
@@ -26,7 +26,7 @@ Project assets live only in each repository's `.oris-flow/` (adapter, loops, run
26
26
 
27
27
  ```bash
28
28
  npx oris-skills uninstall --dry-run # preview
29
- npx oris-skills uninstall # removes Oris-owned paths incl. legacy ~/.cursor locations
29
+ npx oris-skills uninstall # removes Oris-owned paths incl. legacy ~/.cursor locations and pre-2.2.1 loop agent adapters (~/.cursor|.claude|.codex/agents/oris-*)
30
30
  npx oris-skills reinstall
31
31
  ```
32
32
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oris-skills",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "Oris Skills: one entry point (/oris-flow), guided discovery-to-implementation flow, and self-tuning verified loops for Cursor, Claude Code, and Codex.",
5
5
  "license": "MIT",
6
6
  "author": "Davide Baldassarre",
@@ -58,7 +58,11 @@ function readAgents() {
58
58
  .map((name) => ({ name, ...readAgent(path.join(sourceRoot, name)) }));
59
59
  }
60
60
 
61
- /** Write adapters into repo-local .cursor/.claude/.codex dirs under root. */
61
+ /**
62
+ * Write adapters into repo-local .cursor/.claude/.codex dirs under root.
63
+ * Development-only: the installer never copies these into user agent homes —
64
+ * loop roles are internal and reach subagents via per-loop prompts/<role>.md files.
65
+ */
62
66
  export function generateAgentAdapters(root = repoRoot) {
63
67
  const agents = readAgents();
64
68
  for (const agent of agents) {
@@ -69,20 +73,6 @@ export function generateAgentAdapters(root = repoRoot) {
69
73
  return agents.map((agent) => agent.frontMatter.name);
70
74
  }
71
75
 
72
- /**
73
- * Write adapters into explicit agent homes, e.g. { cursor: "~/.cursor", codex: "~/.codex" }.
74
- * Used by the installer so the generated adapters actually reach the user's agents.
75
- */
76
- export function writeAgentAdapters(targets) {
77
- const agents = readAgents();
78
- for (const agent of agents) {
79
- if (targets.cursor) writeFile(path.join(targets.cursor, "agents", agent.name), renderMarkdownAgent(agent));
80
- if (targets.claude) writeFile(path.join(targets.claude, "agents", agent.name), renderMarkdownAgent(agent));
81
- if (targets.codex) writeFile(path.join(targets.codex, "agents", agent.name.replace(/\.md$/, ".toml")), renderCodexAgent(agent));
82
- }
83
- return agents.map((agent) => agent.frontMatter.name);
84
- }
85
-
86
76
  if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url))) {
87
77
  const rootArg = process.argv.find((arg) => arg.startsWith("--root="));
88
78
  const root = rootArg ? path.resolve(rootArg.slice("--root=".length)) : repoRoot;
@@ -22,7 +22,6 @@ import os from "node:os";
22
22
  import path from "node:path";
23
23
  import process from "node:process";
24
24
  import { fileURLToPath } from "node:url";
25
- import { writeAgentAdapters } from "./generate-agent-adapters.mjs";
26
25
 
27
26
  const markerName = ".oris-skills-managed.json";
28
27
  const installManifestName = ".oris-skills-install.json";
@@ -273,7 +272,6 @@ function main() {
273
272
  if (agent === "codex") log(`Would write Codex prompts: ${path.join(options.agentRoots.codex, "prompts")}`);
274
273
  else if (agent === "copilot") log(`Would write Copilot prompts: ${path.join(options.agentRoots.copilot, "prompts")}`);
275
274
  else log(`Would write ${agent} skills: ${path.join(options.agentRoots[agent], "skills")}`);
276
- if (agent !== "copilot") log(`Would write ${agent} loop agents: ${path.join(options.agentRoots[agent], "agents")}`);
277
275
  }
278
276
  log(`Would write universal pointer: ${path.join(path.dirname(options.bundleRoot), "oris-flow.md")}`);
279
277
  log(`Would create settings if missing: ${options.settingsPath}`);
@@ -305,10 +303,6 @@ function main() {
305
303
  }
306
304
  installUniversalPointer(skills, options.bundleRoot);
307
305
 
308
- const adapterTargets = Object.fromEntries(agents.filter((agent) => agent !== "copilot").map((agent) => [agent, options.agentRoots[agent]]));
309
- const loopAgents = writeAgentAdapters(adapterTargets);
310
- log(`Installed ${loopAgents.length} loop agent adapters (${agents.join(", ")})`);
311
-
312
306
  if (!fs.existsSync(options.settingsPath)) writeJson(options.settingsPath, defaultSettings());
313
307
  writeJson(path.join(options.bundleRoot, installManifestName), {
314
308
  package: manifest.name,
@@ -92,6 +92,15 @@ function managedCodexPrompts(codexRoot) {
92
92
  .map((entry) => path.join(promptsRoot, entry.name));
93
93
  }
94
94
 
95
+ /** Loop agent adapters that pre-2.2.1 installers wrote into <agent home>/agents/. */
96
+ function managedAgentAdapters(agentRoot, extension) {
97
+ const agentsRoot = path.join(agentRoot, "agents");
98
+ if (!fs.existsSync(agentsRoot)) return [];
99
+ return fs.readdirSync(agentsRoot, { withFileTypes: true })
100
+ .filter((entry) => entry.isFile() && entry.name.toLowerCase().startsWith("oris-") && entry.name.endsWith(extension))
101
+ .map((entry) => path.join(agentsRoot, entry.name));
102
+ }
103
+
95
104
  function managedCopilotPrompts(copilotRoot) {
96
105
  const promptsRoot = path.join(copilotRoot, "prompts");
97
106
  if (!fs.existsSync(promptsRoot)) return [];
@@ -119,6 +128,9 @@ function plannedRemovals(options) {
119
128
  ...managedSkillDirs(path.join(options.claudeRoot, "skills")),
120
129
  ...managedCodexPrompts(options.codexRoot),
121
130
  ...managedCopilotPrompts(options.copilotRoot),
131
+ ...managedAgentAdapters(options.cursorRoot, ".md"),
132
+ ...managedAgentAdapters(options.claudeRoot, ".md"),
133
+ ...managedAgentAdapters(options.codexRoot, ".toml"),
122
134
  path.join(path.dirname(options.bundleRoot), "oris-flow.md"),
123
135
  ...legacyOrisPaths(options),
124
136
  ]);
@@ -1,11 +1,25 @@
1
+ import childProcess from "node:child_process";
1
2
  import fs from "node:fs";
2
3
  import os from "node:os";
3
4
  import path from "node:path";
4
5
  import assert from "node:assert/strict";
6
+ import process from "node:process";
5
7
  import test from "node:test";
8
+ import { fileURLToPath } from "node:url";
6
9
  import { generateAgentAdapters } from "../install/generate-agent-adapters.mjs";
7
10
 
8
- test("generates Cursor, Claude, and Codex agent adapters", () => {
11
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
12
+
13
+ function runCli(args, homeDir) {
14
+ return childProcess.spawnSync(process.execPath, ["scripts/oris-skills.mjs", ...args], {
15
+ cwd: root,
16
+ encoding: "utf8",
17
+ env: { ...process.env, HOME: homeDir, USERPROFILE: homeDir },
18
+ shell: false,
19
+ });
20
+ }
21
+
22
+ test("generates repo-local Cursor, Claude, and Codex agent adapters", () => {
9
23
  const temp = fs.mkdtempSync(path.join(os.tmpdir(), "oris-agent-adapters-"));
10
24
  try {
11
25
  const generated = generateAgentAdapters(temp);
@@ -17,3 +31,40 @@ test("generates Cursor, Claude, and Codex agent adapters", () => {
17
31
  fs.rmSync(temp, { recursive: true, force: true });
18
32
  }
19
33
  });
34
+
35
+ test("install never plans loop agent adapters in user agent homes", () => {
36
+ const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), "oris-agent-install-"));
37
+ try {
38
+ fs.mkdirSync(path.join(homeDir, ".claude"), { recursive: true });
39
+ fs.mkdirSync(path.join(homeDir, ".cursor"), { recursive: true });
40
+ const result = runCli(["install", "--dry-run"], homeDir);
41
+ assert.equal(result.status, 0, result.stderr);
42
+ assert.doesNotMatch(result.stdout, /loop agents/i);
43
+ assert.doesNotMatch(result.stdout, /agent adapters/i);
44
+ } finally {
45
+ fs.rmSync(homeDir, { recursive: true, force: true });
46
+ }
47
+ });
48
+
49
+ test("uninstall removes legacy loop agent adapters but keeps user agents", () => {
50
+ const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), "oris-agent-uninstall-"));
51
+ try {
52
+ const claudeAgents = path.join(homeDir, ".claude", "agents");
53
+ const cursorAgents = path.join(homeDir, ".cursor", "agents");
54
+ const codexAgents = path.join(homeDir, ".codex", "agents");
55
+ for (const dir of [claudeAgents, cursorAgents, codexAgents]) fs.mkdirSync(dir, { recursive: true });
56
+ fs.writeFileSync(path.join(claudeAgents, "oris-loop-executor.md"), "legacy adapter\n", "utf8");
57
+ fs.writeFileSync(path.join(claudeAgents, "my-agent.md"), "user agent\n", "utf8");
58
+ fs.writeFileSync(path.join(cursorAgents, "oris-loop-verifier.md"), "legacy adapter\n", "utf8");
59
+ fs.writeFileSync(path.join(codexAgents, "oris-loop-doctor.toml"), "legacy adapter\n", "utf8");
60
+
61
+ const result = runCli(["uninstall"], homeDir);
62
+ assert.equal(result.status, 0, result.stderr);
63
+ assert.equal(fs.existsSync(path.join(claudeAgents, "oris-loop-executor.md")), false);
64
+ assert.equal(fs.existsSync(path.join(cursorAgents, "oris-loop-verifier.md")), false);
65
+ assert.equal(fs.existsSync(path.join(codexAgents, "oris-loop-doctor.toml")), false);
66
+ assert.equal(fs.existsSync(path.join(claudeAgents, "my-agent.md")), true, "non-Oris agents must survive");
67
+ } finally {
68
+ fs.rmSync(homeDir, { recursive: true, force: true });
69
+ }
70
+ });