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.
- package/.cursor-plugin/plugin.json +1 -1
- package/CHANGELOG.md +4 -0
- package/docs/architecture.md +1 -1
- package/docs/distribution.md +1 -1
- package/package.json +1 -1
- package/scripts/install/generate-agent-adapters.mjs +5 -15
- package/scripts/install/install-user-skills.mjs +0 -6
- package/scripts/install/uninstall-user-skills.mjs +12 -0
- package/scripts/tests/test-agent-adapters.mjs +52 -1
|
@@ -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.
|
|
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/`.
|
package/docs/architecture.md
CHANGED
|
@@ -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
|
|
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
|
```
|
package/docs/distribution.md
CHANGED
|
@@ -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.
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
+
});
|