oris-skills 2.2.3 → 3.0.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 +2 -3
- package/CHANGELOG.md +24 -6
- package/README.md +23 -59
- package/docs/architecture.md +15 -11
- package/docs/distribution.md +3 -3
- package/docs/maintainer-guide.md +4 -4
- package/docs/user-guide.md +12 -20
- package/package.json +3 -6
- package/references/conventions.md +20 -13
- package/references/doc-policy.md +5 -5
- package/references/research.md +45 -0
- package/references/settings.md +11 -5
- package/references/settings.schema.json +10 -0
- package/scripts/flow/oris-flow-layout.mjs +0 -16
- package/scripts/flow/oris-flow-scan.mjs +32 -317
- package/scripts/flow/oris-gitignore.mjs +1 -5
- package/scripts/install/install-user-skills.mjs +3 -3
- package/scripts/install/uninstall-user-skills.mjs +2 -29
- package/scripts/oris-skills.mjs +0 -47
- package/scripts/tests/run-all-tests.mjs +1 -9
- package/scripts/tests/test-cleanliness.mjs +54 -0
- package/scripts/tests/test-oris-flow-scan.mjs +21 -91
- package/scripts/tests/test-routing-lifecycle.mjs +22 -53
- package/scripts/tests/test-schemas.mjs +17 -31
- package/scripts/tests/test-skill-style.mjs +5 -2
- package/skills/oris-flow/SKILL.md +6 -8
- package/skills/oris-flow/references/architecture.md +1 -1
- package/skills/oris-flow/references/commit.md +9 -8
- package/skills/oris-flow/references/criteria.md +2 -2
- package/skills/oris-flow/references/discover.md +2 -2
- package/skills/oris-flow/references/fix.md +7 -3
- package/skills/oris-flow/references/help.md +1 -6
- package/skills/oris-flow/references/implement.md +5 -3
- package/skills/oris-flow/references/new.md +6 -6
- package/skills/oris-flow/references/plan.md +7 -2
- package/skills/oris-flow/references/setup.md +28 -35
- package/skills/oris-flow/references/verify.md +4 -5
- package/agents/oris-loop-debriefer.md +0 -30
- package/agents/oris-loop-doctor.md +0 -32
- package/agents/oris-loop-executor.md +0 -35
- package/agents/oris-loop-verifier.md +0 -35
- package/references/loop-adapter.schema.json +0 -93
- package/references/loop-contract.md +0 -126
- package/references/loop.schema.json +0 -156
- package/references/repo-map.md +0 -51
- package/references/repo-map.schema.json +0 -213
- package/scripts/flow/oris-flow-clean-runtime.mjs +0 -182
- package/scripts/install/generate-agent-adapters.mjs +0 -81
- package/scripts/loop/oris-loop-bootstrap.mjs +0 -383
- package/scripts/loop/oris-loop-bundle.mjs +0 -22
- package/scripts/loop/oris-loop-chat.mjs +0 -396
- package/scripts/loop/oris-loop-demo.mjs +0 -171
- package/scripts/loop/oris-loop-document.mjs +0 -196
- package/scripts/loop/oris-loop-dry-run.mjs +0 -114
- package/scripts/loop/oris-loop-fixtures.mjs +0 -149
- package/scripts/loop/oris-loop-list.mjs +0 -81
- package/scripts/loop/oris-loop-paths.mjs +0 -232
- package/scripts/loop/oris-loop-run.mjs +0 -301
- package/scripts/loop/oris-loop-stop.mjs +0 -358
- package/scripts/loop/oris-loop-templates.mjs +0 -80
- package/scripts/loop/oris-loop-verify.mjs +0 -205
- package/scripts/tests/test-agent-adapters.mjs +0 -70
- package/scripts/tests/test-oris-1-0-cleanliness.mjs +0 -58
- package/scripts/tests/test-oris-flow-clean-runtime.mjs +0 -75
- package/scripts/tests/test-oris-loop-bootstrap.mjs +0 -94
- package/scripts/tests/test-oris-loop-document.mjs +0 -148
- package/scripts/tests/test-oris-loop-list.mjs +0 -74
- package/scripts/tests/test-oris-loop-run.mjs +0 -71
- package/scripts/tests/test-oris-loop-smoke.mjs +0 -120
- package/scripts/tests/test-oris-loop-stop.mjs +0 -432
- package/skills/oris-flow/references/loop-craft.md +0 -59
- package/skills/oris-flow/references/loop-improve.md +0 -32
- package/skills/oris-flow/references/loop-run.md +0 -47
- package/skills/oris-flow/references/loop.md +0 -56
- package/skills/oris-flow/templates/debriefer.md +0 -19
- package/skills/oris-flow/templates/doctor.md +0 -22
- package/skills/oris-flow/templates/executor.md +0 -25
- package/skills/oris-flow/templates/orchestrator.md +0 -36
- package/skills/oris-flow/templates/verifier.md +0 -24
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import assert from "node:assert/strict";
|
|
4
|
+
import test from "node:test";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
8
|
+
const ignoredDirs = new Set([".git", "node_modules", ".cursor", ".claude", ".codex", "dist", ".cursor-plugin-install-backups", ".oris-flow"]);
|
|
9
|
+
|
|
10
|
+
function walk(dir, matches = []) {
|
|
11
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
12
|
+
if (ignoredDirs.has(entry.name)) continue;
|
|
13
|
+
const full = path.join(dir, entry.name);
|
|
14
|
+
if (entry.isDirectory()) walk(full, matches);
|
|
15
|
+
else matches.push(full);
|
|
16
|
+
}
|
|
17
|
+
return matches;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function repoFiles() {
|
|
21
|
+
return walk(repoRoot).map((filePath) => ({
|
|
22
|
+
filePath,
|
|
23
|
+
relative: path.relative(repoRoot, filePath).replace(/\\/g, "/"),
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
test("repository contains no PowerShell scripts", () => {
|
|
28
|
+
const psFiles = repoFiles().filter((entry) => entry.relative.endsWith(".ps1"));
|
|
29
|
+
assert.deepEqual(psFiles.map((entry) => entry.relative), []);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Machinery removed in 3.0: the loop runtime and the manifest/maps knowledge store.
|
|
33
|
+
// The agent-facing surface (skills, references, docs, README) must never point at it;
|
|
34
|
+
// CHANGELOG keeps the history, and the uninstaller/gitignore keep legacy-cleanup paths.
|
|
35
|
+
const removedMachinery = /oris-skills loop |scripts\/loop\/|\.oris-flow\/(?:loops|runtime|manifest\.json|maps|adapter\.json)|loop-contract|references\/repo-map|flow scan[^\n]*--(?:check|write|area)|flow clean-runtime/;
|
|
36
|
+
|
|
37
|
+
test("agent-facing text never references machinery removed in 3.0", () => {
|
|
38
|
+
const offenders = repoFiles()
|
|
39
|
+
.filter((entry) => /^(skills|references|docs)\/.*\.md$|^(README|AGENTS|CONTEXT)\.md$/.test(entry.relative))
|
|
40
|
+
.filter((entry) => removedMachinery.test(fs.readFileSync(entry.filePath, "utf8")))
|
|
41
|
+
.map((entry) => entry.relative);
|
|
42
|
+
assert.deepEqual(offenders, []);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("production scripts never invoke machinery removed in 3.0", () => {
|
|
46
|
+
const offenders = repoFiles()
|
|
47
|
+
// scripts/tests assert absences; the uninstaller/gitignore clean legacy paths — both must name them.
|
|
48
|
+
.filter((entry) => /^scripts\/(flow|install)\/.*\.mjs$|^scripts\/[^/]+\.mjs$/.test(entry.relative))
|
|
49
|
+
.filter((entry) => entry.relative !== "scripts/install/uninstall-user-skills.mjs")
|
|
50
|
+
.filter((entry) => entry.relative !== "scripts/flow/oris-gitignore.mjs")
|
|
51
|
+
.filter((entry) => removedMachinery.test(fs.readFileSync(entry.filePath, "utf8")))
|
|
52
|
+
.map((entry) => entry.relative);
|
|
53
|
+
assert.deepEqual(offenders, []);
|
|
54
|
+
});
|
|
@@ -1,18 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import os from "node:os";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import test from "node:test";
|
|
6
|
-
import
|
|
7
|
-
import { checkFreshness, scanRepository, writeScanResult } from "../flow/oris-flow-scan.mjs";
|
|
8
|
-
import { ORIS_FLOW_MANIFEST } from "../flow/oris-flow-layout.mjs";
|
|
6
|
+
import { scanRepository } from "../flow/oris-flow-scan.mjs";
|
|
9
7
|
|
|
10
8
|
function write(filePath, content) {
|
|
11
9
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
12
10
|
fs.writeFileSync(filePath, content, "utf8");
|
|
13
11
|
}
|
|
14
12
|
|
|
15
|
-
test("scanner
|
|
13
|
+
test("scanner reports only evidence-backed areas and writes nothing", () => {
|
|
16
14
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "oris-flow-scan-"));
|
|
17
15
|
try {
|
|
18
16
|
write(path.join(root, "README.md"), "# Demo\n");
|
|
@@ -20,17 +18,13 @@ test("scanner creates only evidence-backed map sections", () => {
|
|
|
20
18
|
write(path.join(root, "src", "index.js"), "export const value = 1;\n");
|
|
21
19
|
|
|
22
20
|
const scan = scanRepository(root);
|
|
23
|
-
assert.ok(scan.
|
|
24
|
-
assert.ok(scan.
|
|
25
|
-
assert.ok(scan.
|
|
26
|
-
assert.ok(scan.
|
|
27
|
-
assert.equal(scan.
|
|
28
|
-
assert.equal(scan.manifest["known" + "Gaps"], undefined);
|
|
21
|
+
assert.ok(scan.areas.docs);
|
|
22
|
+
assert.ok(scan.areas.code);
|
|
23
|
+
assert.ok(scan.stacks.some((stack) => stack.id === "node"));
|
|
24
|
+
assert.ok(scan.commands.some((command) => command.command === "npm run test"));
|
|
25
|
+
assert.equal(scan.areas.tests, undefined, "no test evidence → no tests area");
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
assert.equal(written.manifestPath, ORIS_FLOW_MANIFEST);
|
|
32
|
-
assert.ok(fs.existsSync(path.join(root, ORIS_FLOW_MANIFEST)));
|
|
33
|
-
assert.ok(fs.existsSync(path.join(root, ".oris-flow", "maps", "stack.md")));
|
|
27
|
+
assert.equal(fs.existsSync(path.join(root, ".oris-flow")), false, "scan never writes");
|
|
34
28
|
} finally {
|
|
35
29
|
fs.rmSync(root, { recursive: true, force: true });
|
|
36
30
|
}
|
|
@@ -41,8 +35,9 @@ test("scanner supports documentation-only repositories", () => {
|
|
|
41
35
|
try {
|
|
42
36
|
write(path.join(root, "docs", "guide.md"), "# Guide\n");
|
|
43
37
|
const scan = scanRepository(root);
|
|
44
|
-
assert.deepEqual(Object.keys(scan.
|
|
38
|
+
assert.deepEqual(Object.keys(scan.areas), ["docs"]);
|
|
45
39
|
assert.deepEqual(scan.stacks, []);
|
|
40
|
+
assert.deepEqual(scan.commands, []);
|
|
46
41
|
} finally {
|
|
47
42
|
fs.rmSync(root, { recursive: true, force: true });
|
|
48
43
|
}
|
|
@@ -61,96 +56,31 @@ test("scanner detects project type and derives inferred stack commands", () => {
|
|
|
61
56
|
const dotnetTest = scan.commands.find((command) => command.command.startsWith("dotnet test"));
|
|
62
57
|
assert.ok(dotnetTest, "expected an inferred dotnet test command");
|
|
63
58
|
assert.equal(dotnetTest.confidence, "inferred");
|
|
64
|
-
assert.equal(scan.manifest.setup.tasksRoot, ".oris-flow/tasks");
|
|
65
59
|
} finally {
|
|
66
60
|
fs.rmSync(root, { recursive: true, force: true });
|
|
67
61
|
}
|
|
68
62
|
});
|
|
69
63
|
|
|
70
|
-
test("
|
|
64
|
+
test("scanner flags truncation instead of silently under-reporting", () => {
|
|
71
65
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "oris-flow-scan-"));
|
|
72
66
|
try {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
first.manifest.sections.docs.confidence = "confirmed";
|
|
80
|
-
first.manifest.commands[0].confidence = "confirmed";
|
|
81
|
-
writeScanResult(first);
|
|
82
|
-
const enriched = "# Docs\n\nUser-confirmed enrichment that must survive refresh.\n";
|
|
83
|
-
write(path.join(root, ".oris-flow", "maps", "docs.md"), enriched);
|
|
84
|
-
|
|
85
|
-
const second = scanRepository(root);
|
|
86
|
-
assert.equal(second.manifest.setup.tasksRoot, "docs/tasks");
|
|
87
|
-
assert.deepEqual(second.manifest.setup.confirmedDecisions, ["tests live under scripts/tests"]);
|
|
88
|
-
assert.equal(second.manifest.sections.docs.confidence, "confirmed");
|
|
89
|
-
assert.equal(second.manifest.sections.docs.stale, false);
|
|
90
|
-
assert.equal(second.manifest.commands[0].confidence, "confirmed");
|
|
91
|
-
assert.ok(second.preservedSections.has("docs"));
|
|
92
|
-
|
|
93
|
-
writeScanResult(second);
|
|
94
|
-
assert.equal(fs.readFileSync(path.join(root, ".oris-flow", "maps", "docs.md"), "utf8"), enriched);
|
|
67
|
+
for (let index = 0; index < 12; index += 1) {
|
|
68
|
+
write(path.join(root, "src", `file-${index}.js`), "export {};\n");
|
|
69
|
+
}
|
|
70
|
+
const scan = scanRepository(root, { maxFiles: 5 });
|
|
71
|
+
assert.equal(scan.truncated, true);
|
|
72
|
+
assert.ok(scan.fileCount <= 5);
|
|
95
73
|
} finally {
|
|
96
74
|
fs.rmSync(root, { recursive: true, force: true });
|
|
97
75
|
}
|
|
98
76
|
});
|
|
99
77
|
|
|
100
|
-
test("
|
|
101
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), "oris-flow-scan-"));
|
|
102
|
-
try {
|
|
103
|
-
write(path.join(root, "README.md"), "# Demo\n");
|
|
104
|
-
const first = scanRepository(root);
|
|
105
|
-
first.manifest.sections.docs.confidence = "confirmed";
|
|
106
|
-
writeScanResult(first);
|
|
107
|
-
|
|
108
|
-
fs.rmSync(path.join(root, "README.md"));
|
|
109
|
-
const second = scanRepository(root);
|
|
110
|
-
assert.equal(second.manifest.sections.docs.confidence, "confirmed");
|
|
111
|
-
assert.equal(second.manifest.sections.docs.stale, true);
|
|
112
|
-
} finally {
|
|
113
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
test("scan anchors the map to HEAD; --check answers freshness from the SHA + changed files", () => {
|
|
78
|
+
test("empty repository classifies as empty", () => {
|
|
119
79
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), "oris-flow-scan-"));
|
|
120
|
-
const git = (...args) => childProcess.execFileSync("git", args, { cwd: root, encoding: "utf8" });
|
|
121
80
|
try {
|
|
122
|
-
write(path.join(root, "README.md"), "# Demo\n");
|
|
123
|
-
write(path.join(root, "package.json"), `${JSON.stringify({ scripts: { test: "node --test" } })}\n`);
|
|
124
|
-
git("init", "-q");
|
|
125
|
-
git("-c", "user.email=t@t", "-c", "user.name=t", "add", ".");
|
|
126
|
-
git("-c", "user.email=t@t", "-c", "user.name=t", "commit", "-q", "-m", "init");
|
|
127
|
-
|
|
128
|
-
const scan = scanRepository(root);
|
|
129
|
-
assert.equal(typeof scan.manifest.headSha, "string", "scan records the HEAD anchor");
|
|
130
|
-
writeScanResult(scan);
|
|
131
|
-
|
|
132
|
-
const fresh = checkFreshness(root);
|
|
133
|
-
assert.equal(fresh.fresh, true, "same SHA + clean tree → fresh, zero reads");
|
|
134
|
-
assert.deepEqual(fresh.changedFiles, []);
|
|
135
|
-
|
|
136
|
-
write(path.join(root, "src", "new.js"), "export const later = 1;\n");
|
|
137
|
-
const stale = checkFreshness(root);
|
|
138
|
-
assert.equal(stale.fresh, false, "working-tree change → stale");
|
|
139
|
-
assert.ok(stale.changedFiles.some((file) => file.includes("new.js")), "changed files name the delta");
|
|
140
|
-
} finally {
|
|
141
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
test("outside git the map still works — no anchor, never an error", () => {
|
|
146
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), "oris-flow-scan-"));
|
|
147
|
-
try {
|
|
148
|
-
write(path.join(root, "README.md"), "# Demo\n");
|
|
149
81
|
const scan = scanRepository(root);
|
|
150
|
-
assert.equal(scan.
|
|
151
|
-
|
|
152
|
-
const check = checkFreshness(root);
|
|
153
|
-
assert.equal(check.fresh, false, "no anchor → refresh advised, not an error");
|
|
82
|
+
assert.equal(scan.projectType.id, "empty");
|
|
83
|
+
assert.deepEqual(scan.areas, {});
|
|
154
84
|
} finally {
|
|
155
85
|
fs.rmSync(root, { recursive: true, force: true });
|
|
156
86
|
}
|
|
@@ -72,58 +72,27 @@ test("every skill points at conventions instead of repeating shared rules", () =
|
|
|
72
72
|
}
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
-
test("loop
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
assert.match(skill, /Cursor|Claude Code|Codex/);
|
|
86
|
-
for (const reference of ["craft", "run", "improve"]) {
|
|
87
|
-
assert.equal(exists(`skills/oris-flow/references/loop-${reference}.md`), true, reference);
|
|
88
|
-
}
|
|
89
|
-
for (const template of ["orchestrator", "executor", "verifier", "doctor", "debriefer"]) {
|
|
90
|
-
assert.equal(exists(`skills/oris-flow/templates/${template}.md`), true, template);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
test("loop contract stays subagent-first with per-role editable prompts", () => {
|
|
95
|
-
const contract = read("references/loop-contract.md");
|
|
96
|
-
assert.match(contract, /schemaVersion: 2/);
|
|
97
|
-
assert.match(contract, /approved: true/);
|
|
98
|
-
assert.match(contract, /prompts\/orchestrator\.md/);
|
|
99
|
-
assert.match(contract, /improve/);
|
|
100
|
-
assert.match(contract, /inherit/);
|
|
101
|
-
assert.match(contract, /NEVER success/i);
|
|
102
|
-
const run = read("skills/oris-flow/references/loop-run.md");
|
|
103
|
-
assert.match(run, /oris-skills loop chat --action start --loop <slug>/);
|
|
104
|
-
assert.match(run, /NEVER do executor\/verifier\/doctor work in the main chat/i);
|
|
105
|
-
assert.match(run, /set-state blocked/);
|
|
106
|
-
assert.match(run, /oris-skills loop run --loop <slug> --agent codex\|claude/);
|
|
107
|
-
const craft = read("skills/oris-flow/references/loop-craft.md");
|
|
108
|
-
assert.match(craft, /skills\/oris-flow\/templates\//);
|
|
109
|
-
assert.match(craft, /write the loop \| edit verifier \| edit executor \| edit doctor/);
|
|
110
|
-
assert.match(contract, /ORIS LOOP ACTIVE/, "the contract defines the executor gate literal");
|
|
111
|
-
for (const text of [contract, run, craft, read("skills/oris-flow/references/loop.md")]) {
|
|
112
|
-
assert.doesNotMatch(text, /node scripts\/(?:flow|loop)\//);
|
|
113
|
-
assert.doesNotMatch(text, /composer-2\.5/);
|
|
114
|
-
}
|
|
115
|
-
for (const text of [run, craft, read("skills/oris-flow/references/loop.md")]) {
|
|
116
|
-
assert.doesNotMatch(text, /ORIS LOOP ACTIVE/, "the gate literal lives in the contract and executor prompts only");
|
|
75
|
+
test("the loop runtime is fully removed", () => {
|
|
76
|
+
for (const legacy of [
|
|
77
|
+
"scripts/loop",
|
|
78
|
+
"agents",
|
|
79
|
+
"skills/oris-flow/templates",
|
|
80
|
+
"skills/oris-flow/references/loop.md",
|
|
81
|
+
"references/loop-contract.md",
|
|
82
|
+
"references/repo-map.md",
|
|
83
|
+
]) {
|
|
84
|
+
assert.equal(exists(legacy), false, `${legacy} must not exist`);
|
|
117
85
|
}
|
|
86
|
+
const skill = read("skills/oris-flow/SKILL.md");
|
|
87
|
+
assert.doesNotMatch(skill, /\bloop\b/i, "the router must not mention loops");
|
|
118
88
|
});
|
|
119
89
|
|
|
120
|
-
test("setup
|
|
90
|
+
test("setup keeps the installed CLI as the command surface and AGENTS.md as the store", () => {
|
|
121
91
|
const setup = read("skills/oris-flow/references/setup.md");
|
|
122
92
|
assert.match(setup, /oris-skills flow scan --repository-root <repo> --json/);
|
|
123
|
-
assert.match(setup, /oris-skills
|
|
124
|
-
assert.
|
|
125
|
-
|
|
126
|
-
assert.match(repoMap, /never `node scripts\/\.\.\.` paths/);
|
|
93
|
+
assert.match(setup, /oris-skills flow version-control --repository-root <repo> --mode commit\|gitignore/);
|
|
94
|
+
assert.doesNotMatch(setup, /node scripts\//, "always the installed CLI, never source paths");
|
|
95
|
+
assert.match(setup, /AGENTS\.md/);
|
|
127
96
|
});
|
|
128
97
|
|
|
129
98
|
test("no markdown references the pre-2.0 layout", () => {
|
|
@@ -165,18 +134,18 @@ test("uninstall and reinstall dry-runs show destructive plan before action", ()
|
|
|
165
134
|
}
|
|
166
135
|
});
|
|
167
136
|
|
|
168
|
-
test("CLI exposes install
|
|
137
|
+
test("CLI exposes install and flow helper commands, and no loop surface", () => {
|
|
169
138
|
const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), "oris-cli-help-"));
|
|
170
139
|
try {
|
|
171
140
|
const help = runCli(["help"], homeDir);
|
|
172
141
|
assert.equal(help.status, 0, help.stderr);
|
|
173
142
|
assert.match(help.stdout, /--agents cursor,claude,codex/);
|
|
174
|
-
assert.match(help.stdout, /oris-skills loop demo/);
|
|
175
|
-
assert.match(help.stdout, /oris-skills loop dry-run --loop <slug>/);
|
|
176
|
-
assert.match(help.stdout, /oris-skills loop verify --temp/);
|
|
177
|
-
assert.match(help.stdout, /oris-skills loop chat --action start --loop <slug>/);
|
|
178
|
-
assert.match(help.stdout, /oris-skills loop run --loop <slug> --agent codex\|claude/);
|
|
179
143
|
assert.match(help.stdout, /oris-skills flow scan/);
|
|
144
|
+
assert.match(help.stdout, /oris-skills flow version-control/);
|
|
145
|
+
assert.doesNotMatch(help.stdout, /loop/i);
|
|
146
|
+
|
|
147
|
+
const loop = runCli(["loop", "list"], homeDir);
|
|
148
|
+
assert.equal(loop.status, 1, "loop is an unknown command in 3.0");
|
|
180
149
|
} finally {
|
|
181
150
|
fs.rmSync(homeDir, { recursive: true, force: true });
|
|
182
151
|
}
|
|
@@ -12,45 +12,31 @@ function read(relativePath) {
|
|
|
12
12
|
return JSON.parse(fs.readFileSync(path.join(root, relativePath), "utf8"));
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
test("settings schema is the
|
|
15
|
+
test("settings schema is the v1 contract: preferences only, no secrets, no profiles", () => {
|
|
16
16
|
const schema = read("references/settings.schema.json");
|
|
17
17
|
assert.equal(schema.properties.version.const, 1);
|
|
18
18
|
assert.deepEqual(schema.required, ["version", "orisFlow"]);
|
|
19
|
-
|
|
20
|
-
assert.ok(schema.properties.orisFlow.properties.artifactLanguage);
|
|
21
|
-
assert.ok(!schema.properties.defaultProfiles, "test profiles were removed from settings");
|
|
22
|
-
assert.ok(!schema.properties.profiles, "test profiles were removed from settings");
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test("adapter schema v1 declares paths, commands, model aliases, preflight (no test profiles)", () => {
|
|
26
|
-
const schema = read("references/loop-adapter.schema.json");
|
|
27
|
-
assert.equal(schema.$id, "https://oris.dev/schemas/oris-loop-adapter-v1.json");
|
|
28
|
-
assert.equal(schema.properties.schemaVersion.const, 1);
|
|
19
|
+
const properties = schema.properties.orisFlow.properties;
|
|
29
20
|
assert.deepEqual(
|
|
30
|
-
|
|
31
|
-
["
|
|
21
|
+
Object.keys(properties).sort(),
|
|
22
|
+
["artifactLanguage", "responseStyle", "tasksRoot", "versionControl"],
|
|
32
23
|
);
|
|
33
|
-
assert.
|
|
34
|
-
assert.ok(schema.properties.
|
|
35
|
-
assert.ok(!schema.properties.
|
|
24
|
+
assert.deepEqual(properties.versionControl.enum, ["commit", "gitignore"]);
|
|
25
|
+
assert.ok(!schema.properties.defaultProfiles, "test profiles were removed from settings");
|
|
26
|
+
assert.ok(!schema.properties.profiles, "test profiles were removed from settings");
|
|
36
27
|
});
|
|
37
28
|
|
|
38
|
-
test("loop
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
assert.ok(schema.properties.models.properties[role], `models.${role}`);
|
|
29
|
+
test("no loop or repo-map schemas survive the 3.0 cleanup", () => {
|
|
30
|
+
for (const legacy of [
|
|
31
|
+
"references/loop.schema.json",
|
|
32
|
+
"references/loop-adapter.schema.json",
|
|
33
|
+
"references/repo-map.schema.json",
|
|
34
|
+
]) {
|
|
35
|
+
assert.equal(fs.existsSync(path.join(root, legacy)), false, `${legacy} must not exist`);
|
|
46
36
|
}
|
|
47
|
-
assert.deepEqual(schema.properties.improve.properties.mode.enum, ["propose", "auto"]);
|
|
48
|
-
assert.equal(schema.$defs.model.type, "string", "models are free strings (inherit | alias | model id), not a hardcoded enum");
|
|
49
37
|
});
|
|
50
38
|
|
|
51
|
-
test("
|
|
52
|
-
const
|
|
53
|
-
assert.
|
|
54
|
-
assert.deepEqual(schema.required, ["$schema", "schemaVersion", "repository", "generatedAt", "updatedAt", "setup", "sections"]);
|
|
55
|
-
assert.ok(schema.$defs.sections.additionalProperties);
|
|
39
|
+
test("devops schema stays the only other JSON contract", () => {
|
|
40
|
+
const schemas = fs.readdirSync(path.join(root, "references")).filter((name) => name.endsWith(".schema.json"));
|
|
41
|
+
assert.deepEqual(schemas.sort(), ["devops.schema.json", "settings.schema.json"]);
|
|
56
42
|
});
|
|
@@ -36,8 +36,11 @@ test("skill style: oris-flow", () => {
|
|
|
36
36
|
assert.match(front[1], /^name: oris-flow$/m, "front matter name matches the folder");
|
|
37
37
|
const description = front[1].match(/^description: (.+)$/m)?.[1];
|
|
38
38
|
assert.ok(description, "front matter has description");
|
|
39
|
-
assert.match(
|
|
40
|
-
|
|
39
|
+
assert.match(
|
|
40
|
+
front[1],
|
|
41
|
+
/^disable-model-invocation: true$/m,
|
|
42
|
+
"the entry point is user-invoked only (/oris-flow) — decision 2026-07-10, never auto-triggered",
|
|
43
|
+
);
|
|
41
44
|
assert.ok(text.includes("## Never"), "carries an explicit Never block");
|
|
42
45
|
assert.ok(text.includes("## Done when"), "ends with a Done when checklist");
|
|
43
46
|
const lines = text.split(/\r?\n/).length;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: oris-flow
|
|
3
|
-
description: The one Oris skill - read the intent, pick ONE route, apply it in the same chat.
|
|
3
|
+
description: The one Oris skill - read the intent, pick ONE route, apply it in the same chat. Routes setup, new project, discovery, acceptance criteria, technical plan, implement, fix, verify, spec change, architecture review, docs, commit, pull request, and help.
|
|
4
|
+
disable-model-invocation: true
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Oris Flow — the router
|
|
@@ -29,10 +30,9 @@ Match the desired outcome to ONE route, then apply its reference:
|
|
|
29
30
|
| fix | broken behavior, a regression, a failed QA check | `skills/oris-flow/references/fix.md` |
|
|
30
31
|
| verify | check acceptance criteria against the real product, once | `skills/oris-flow/references/verify.md` |
|
|
31
32
|
| change | the spec changed — update analysis, criteria, plan, and history together | `skills/oris-flow/references/change.md` |
|
|
32
|
-
| loop | repeat work until verified — fix until green, step through a plan | `skills/oris-flow/references/loop.md` |
|
|
33
33
|
| architecture | review architecture, find deepening/refactor opportunities | `skills/oris-flow/references/architecture.md` |
|
|
34
34
|
| docs | update task docs after a change | `skills/oris-flow/references/docs.md` |
|
|
35
|
-
| commit | commit changes with the Oris standard —
|
|
35
|
+
| commit | commit changes with the Oris standard — Conventional Commits, work item linked | `skills/oris-flow/references/commit.md` |
|
|
36
36
|
| pr | open or update a pull request, English title/description linked to the work item | `skills/oris-flow/references/pr.md` |
|
|
37
37
|
| help | how Oris works, install, update, troubleshooting | `skills/oris-flow/references/help.md` |
|
|
38
38
|
|
|
@@ -46,10 +46,9 @@ Match the desired outcome to ONE route, then apply its reference:
|
|
|
46
46
|
change owns the delta and the history.
|
|
47
47
|
4. BROKEN behavior → fix. NEW planned work → implement. The menu answer "Implement or
|
|
48
48
|
fix" gets exactly one follow-up question to split them.
|
|
49
|
-
5.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
7. COMMIT staged/working changes with the standard → commit. OPEN or update a pull request
|
|
49
|
+
5. EMPTY directory or from-scratch intent → new. Existing repo never touched by Oris
|
|
50
|
+
(no `AGENTS.md`, no `.oris-flow/`) → recommend setup first; respect the user's choice.
|
|
51
|
+
6. COMMIT staged/working changes with the standard → commit. OPEN or update a pull request
|
|
53
52
|
→ pr. Neither writes product code — they package and publish what implement/fix produced.
|
|
54
53
|
|
|
55
54
|
## Menu (low confidence)
|
|
@@ -66,7 +65,6 @@ fallback in `references/conventions.md` `## Questions`), options:
|
|
|
66
65
|
- Implement or fix
|
|
67
66
|
- Verify acceptance criteria
|
|
68
67
|
- Spec changed: update feature docs
|
|
69
|
-
- Loop: repeat until verified
|
|
70
68
|
- Architecture review
|
|
71
69
|
- Update docs
|
|
72
70
|
- Commit changes
|
|
@@ -22,7 +22,7 @@ routes to plan/implement.
|
|
|
22
22
|
|
|
23
23
|
## 1. Explore
|
|
24
24
|
|
|
25
|
-
1. READ first:
|
|
25
|
+
1. READ first: `AGENTS.md`, `CONTEXT.md` (domain names for good seams), `docs/adr/`
|
|
26
26
|
(decisions NOT to re-litigate).
|
|
27
27
|
2. WALK the codebase with parallel subagents. No rigid heuristics — note real friction:
|
|
28
28
|
- understanding one concept requires bouncing between many small modules;
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# Commit
|
|
2
2
|
|
|
3
|
-
Turn staged or working changes into one clean commit —
|
|
4
|
-
|
|
3
|
+
Turn staged or working changes into one clean commit — message in English, work item
|
|
4
|
+
linked. NEVER commit without showing the message and getting a yes.
|
|
5
5
|
|
|
6
6
|
RULES: `references/conventions.md`. DevOps config: `references/devops.md`.
|
|
7
7
|
|
|
8
8
|
## Before the commit
|
|
9
9
|
|
|
10
|
-
1.
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
1. READ the diff (`git status`, `git diff --staged`) to know what actually changed.
|
|
11
|
+
2. IF the diff proved a durable fact `AGENTS.md` lacks or contradicts (a new command, a
|
|
12
|
+
moved area) → correct `AGENTS.md` and stage it with the change (`references/conventions.md`
|
|
13
|
+
`## Project facts`).
|
|
13
14
|
3. RESOLVE the work item id from context — current branch, the active task doc, or the
|
|
14
15
|
DevOps provider's active items. Unsure → ASK once for the id; never invent one.
|
|
15
16
|
|
|
@@ -23,7 +24,7 @@ RULES: `references/conventions.md`. DevOps config: `references/devops.md`.
|
|
|
23
24
|
|
|
24
25
|
## Gate (never skipped)
|
|
25
26
|
|
|
26
|
-
SHOW the full message + the files it will commit
|
|
27
|
+
SHOW the full message + the files it will commit. ASK:
|
|
27
28
|
commit | edit message | cancel. ON commit → `git commit`; NEVER `--no-verify`.
|
|
28
29
|
|
|
29
30
|
## After
|
|
@@ -36,10 +37,10 @@ commit | edit message | cancel. ON commit → `git commit`; NEVER `--no-verify`.
|
|
|
36
37
|
- Commit before the message and file list are shown and confirmed.
|
|
37
38
|
- Write the message in any language but English.
|
|
38
39
|
- Invent a work item id, or bypass hooks with `--no-verify`.
|
|
39
|
-
-
|
|
40
|
+
- Touch `AGENTS.md` sections the diff did not contradict.
|
|
40
41
|
|
|
41
42
|
## Done when
|
|
42
43
|
|
|
43
|
-
- [ ]
|
|
44
|
+
- [ ] Diff read; `AGENTS.md` corrected only where the diff proved it wrong.
|
|
44
45
|
- [ ] English Conventional message with the work item linked, confirmed at the gate.
|
|
45
46
|
- [ ] Commit made without `--no-verify`; next step offered, not auto-run.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Acceptance Criteria
|
|
2
2
|
|
|
3
3
|
Generate verifiable criteria grounded in observable product behavior — UI, API response,
|
|
4
|
-
CLI output, or a visible system effect. They
|
|
5
|
-
|
|
4
|
+
CLI output, or a visible system effect. They are the exact checklist the verify route
|
|
5
|
+
executes — precision here pays twice.
|
|
6
6
|
|
|
7
7
|
RULES: `references/conventions.md`. Doc standards: `references/doc-policy.md`.
|
|
8
8
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Discovery
|
|
2
2
|
|
|
3
3
|
Interview the stakeholder until the desired business behavior is clear enough to
|
|
4
|
-
document. The cornerstone of the flow: everything downstream (criteria, plan,
|
|
4
|
+
document. The cornerstone of the flow: everything downstream (criteria, plan, implement)
|
|
5
5
|
builds on this.
|
|
6
6
|
|
|
7
7
|
RULES: `references/conventions.md`. Doc standards: `references/doc-policy.md`.
|
|
@@ -9,7 +9,7 @@ RULES: `references/conventions.md`. Doc standards: `references/doc-policy.md`.
|
|
|
9
9
|
## Before asking
|
|
10
10
|
|
|
11
11
|
1. EXPLORE first: task folder (`{tasksRoot}/{task}/`), existing task docs, project docs,
|
|
12
|
-
|
|
12
|
+
`AGENTS.md`, code evidence, DevOps context when available.
|
|
13
13
|
2. DEDUCE create vs update: existing `functional-analysis.md` → update; missing → create.
|
|
14
14
|
Ask only on contradictory signals.
|
|
15
15
|
3. Technical evidence = private input. TRANSLATE it to business language; never show file
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
The smallest safe change near the failing behavior. Reproduce first, root cause before
|
|
4
4
|
any edit, evidence before done.
|
|
5
5
|
|
|
6
|
-
RULES: `references/conventions.md`.
|
|
6
|
+
RULES: `references/conventions.md`. Research: `references/research.md`.
|
|
7
7
|
|
|
8
8
|
## Triage
|
|
9
9
|
|
|
@@ -19,8 +19,12 @@ RULES: `references/conventions.md`.
|
|
|
19
19
|
- REPRODUCE or trace the failure path in code before proposing anything.
|
|
20
20
|
- FIND root cause, not symptom. Check whether the documented behavior
|
|
21
21
|
(criteria/analysis) defines "correct".
|
|
22
|
-
-
|
|
23
|
-
|
|
22
|
+
- RESEARCH when the cause crosses the repo boundary (`references/research.md`): a
|
|
23
|
+
third-party error, a library behaving unlike its docs, a known-issue smell → search
|
|
24
|
+
for the documented behavior and standard workarounds at the repo's pinned versions,
|
|
25
|
+
instead of guessing.
|
|
26
|
+
- SUMMARIZE: root cause → proposed minimal fix → files → verification plan (+ sources
|
|
27
|
+
when research ran). CONFIRM explicitly.
|
|
24
28
|
|
|
25
29
|
## Fix
|
|
26
30
|
|
|
@@ -8,24 +8,19 @@ details on request.
|
|
|
8
8
|
| Need | Answer |
|
|
9
9
|
|------|--------|
|
|
10
10
|
| Start anything | Type `/oris-flow` — it routes or shows the menu. The only command to remember. |
|
|
11
|
-
| Try loops safely | `npx oris-skills loop demo` → follow the 3 printed steps (`dry-run` previews, `chat --action start` runs). |
|
|
12
11
|
| Install / update | `npx oris-skills@latest install` (detects Cursor, Claude Code, Codex, Copilot; or `--agents cursor,claude,codex,copilot`). Reload the agent afterwards. |
|
|
13
12
|
| Copilot / any other agent | Copilot Chat: enable `chat.promptFiles` once, then `/oris-flow`. Anything else that reads files: point it at `~/.oris/oris-flow.md`. |
|
|
14
13
|
| Uninstall / reinstall | `npx oris-skills uninstall` / `npx oris-skills reinstall` (preview with `--dry-run`). |
|
|
15
|
-
| Stop a loop | `npx oris-skills loop chat --action stop` |
|
|
16
|
-
| Loop status | `npx oris-skills loop chat --action status` / `npx oris-skills loop list` |
|
|
17
14
|
| Skill missing after install | Reload the agent; check `~/.oris/oris-skills/` exists; re-run install with `--force`. |
|
|
18
|
-
| Hook not firing | `npx oris-skills loop verify --temp` self-checks the runtime; `npx oris-skills loop bootstrap` re-arms repo hooks. |
|
|
19
15
|
| Change document language / chat verbosity | `/oris-flow` → Setup (settings live in `.oris-flow/settings.json`). |
|
|
20
16
|
| Ultra-short answers | `/oris-flow` → Setup → set `responseStyle: caveman` — chat shrinks to maximum token density; documents stay complete. |
|
|
21
|
-
| Edit a loop's behavior | Edit the files in `.oris-flow/loops/{slug}/prompts/` — the next pass obeys them. |
|
|
22
17
|
|
|
23
18
|
Deeper docs: `docs/user-guide.md`, `docs/architecture.md` in the bundle (`~/.oris/oris-skills/`).
|
|
24
19
|
|
|
25
20
|
## Never
|
|
26
21
|
|
|
27
22
|
- Tell users to paste secrets anywhere.
|
|
28
|
-
- Dump internals (
|
|
23
|
+
- Dump internals (settings JSON, install layout) unless the user asks for technical
|
|
29
24
|
troubleshooting.
|
|
30
25
|
|
|
31
26
|
## Done when
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Execute the confirmed plan — nothing more, nothing less.
|
|
4
4
|
|
|
5
|
-
RULES: `references/conventions.md`.
|
|
5
|
+
RULES: `references/conventions.md`. Research: `references/research.md`.
|
|
6
6
|
|
|
7
7
|
## Prepare
|
|
8
8
|
|
|
9
9
|
1. FIND `{tasksRoot}/{task}/implementation-plan.md`. IF missing → reconstruct a technical
|
|
10
10
|
recap from chat, task docs, and code; SHOW it; CONFIRM before any edit.
|
|
11
|
-
2. READ
|
|
12
|
-
the touched area.
|
|
11
|
+
2. READ `AGENTS.md`, project standards, local skills/rules, tests and build conventions
|
|
12
|
+
for the touched area.
|
|
13
13
|
3. MAP the code, tests, config, and translations the plan touches (parallel subagents
|
|
14
14
|
when available).
|
|
15
15
|
|
|
@@ -22,6 +22,8 @@ RULES: `references/conventions.md`.
|
|
|
22
22
|
## While coding
|
|
23
23
|
|
|
24
24
|
- FOLLOW existing project patterns; no new abstractions without need.
|
|
25
|
+
- RESEARCH mid-build only when an external API resists (`references/research.md`) —
|
|
26
|
+
verify its current docs at the repo's pinned version; never guess signatures.
|
|
25
27
|
- STAY inside the confirmed scope; park discoveries as notes, don't chase them.
|
|
26
28
|
- ADD or update focused tests for the changed behavior.
|
|
27
29
|
- CLEAN as you finish (`references/clean-code-checklist.md`): remove dead code, debug
|