oris-skills 2.2.3 → 3.0.2
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 +28 -60
- 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/clean-code-checklist.md +2 -2
- package/references/conventions.md +36 -16
- 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 +24 -54
- package/scripts/tests/test-schemas.mjs +17 -31
- package/scripts/tests/test-skill-style.mjs +5 -2
- package/skills/oris-flow/SKILL.md +21 -11
- package/skills/oris-flow/agents/openai.yaml +1 -1
- package/skills/oris-flow/references/architecture.md +6 -3
- package/skills/oris-flow/references/change.md +3 -0
- package/skills/oris-flow/references/commit.md +9 -8
- package/skills/oris-flow/references/criteria.md +9 -5
- package/skills/oris-flow/references/discover.md +2 -2
- package/skills/oris-flow/references/docs.md +5 -1
- package/skills/oris-flow/references/fix.md +10 -5
- package/skills/oris-flow/references/handoff.md +67 -0
- package/skills/oris-flow/references/help.md +3 -6
- package/skills/oris-flow/references/implement.md +7 -4
- package/skills/oris-flow/references/new.md +6 -6
- package/skills/oris-flow/references/plan.md +7 -2
- package/skills/oris-flow/references/pr.md +2 -2
- package/skills/oris-flow/references/setup.md +33 -39
- package/skills/oris-flow/references/triage.md +69 -0
- package/skills/oris-flow/references/verify.md +5 -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
|
}
|
|
@@ -59,7 +59,8 @@ test("shared conventions centralize question, language, and gate rules", () => {
|
|
|
59
59
|
assert.match(conventions, /`Continua l'intervista`/);
|
|
60
60
|
assert.match(conventions, /`Approva documento`/);
|
|
61
61
|
assert.match(conventions, /`Annulla`/);
|
|
62
|
-
assert.match(conventions, /oris-flow
|
|
62
|
+
assert.match(conventions, /skills\/oris-flow\/references\/criteria\.md`.*writes first/i, "the criteria write-first exception must live where skills read it");
|
|
63
|
+
assert.doesNotMatch(conventions, /oris-flow-(criteria|implement|fix)/, "destinations are named by file path, never by prose nickname");
|
|
63
64
|
assert.equal(exists("references/questions.md"), false, "questions.md was merged into conventions.md");
|
|
64
65
|
});
|
|
65
66
|
|
|
@@ -72,58 +73,27 @@ test("every skill points at conventions instead of repeating shared rules", () =
|
|
|
72
73
|
}
|
|
73
74
|
});
|
|
74
75
|
|
|
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");
|
|
76
|
+
test("the loop runtime is fully removed", () => {
|
|
77
|
+
for (const legacy of [
|
|
78
|
+
"scripts/loop",
|
|
79
|
+
"agents",
|
|
80
|
+
"skills/oris-flow/templates",
|
|
81
|
+
"skills/oris-flow/references/loop.md",
|
|
82
|
+
"references/loop-contract.md",
|
|
83
|
+
"references/repo-map.md",
|
|
84
|
+
]) {
|
|
85
|
+
assert.equal(exists(legacy), false, `${legacy} must not exist`);
|
|
117
86
|
}
|
|
87
|
+
const skill = read("skills/oris-flow/SKILL.md");
|
|
88
|
+
assert.doesNotMatch(skill, /\bloop\b/i, "the router must not mention loops");
|
|
118
89
|
});
|
|
119
90
|
|
|
120
|
-
test("setup
|
|
91
|
+
test("setup keeps the installed CLI as the command surface and AGENTS.md as the store", () => {
|
|
121
92
|
const setup = read("skills/oris-flow/references/setup.md");
|
|
122
93
|
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/);
|
|
94
|
+
assert.match(setup, /oris-skills flow version-control --repository-root <repo> --mode commit\|gitignore/);
|
|
95
|
+
assert.doesNotMatch(setup, /node scripts\//, "always the installed CLI, never source paths");
|
|
96
|
+
assert.match(setup, /AGENTS\.md/);
|
|
127
97
|
});
|
|
128
98
|
|
|
129
99
|
test("no markdown references the pre-2.0 layout", () => {
|
|
@@ -165,18 +135,18 @@ test("uninstall and reinstall dry-runs show destructive plan before action", ()
|
|
|
165
135
|
}
|
|
166
136
|
});
|
|
167
137
|
|
|
168
|
-
test("CLI exposes install
|
|
138
|
+
test("CLI exposes install and flow helper commands, and no loop surface", () => {
|
|
169
139
|
const homeDir = fs.mkdtempSync(path.join(os.tmpdir(), "oris-cli-help-"));
|
|
170
140
|
try {
|
|
171
141
|
const help = runCli(["help"], homeDir);
|
|
172
142
|
assert.equal(help.status, 0, help.stderr);
|
|
173
143
|
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
144
|
assert.match(help.stdout, /oris-skills flow scan/);
|
|
145
|
+
assert.match(help.stdout, /oris-skills flow version-control/);
|
|
146
|
+
assert.doesNotMatch(help.stdout, /loop/i);
|
|
147
|
+
|
|
148
|
+
const loop = runCli(["loop", "list"], homeDir);
|
|
149
|
+
assert.equal(loop.status, 1, "loop is an unknown command in 3.0");
|
|
180
150
|
} finally {
|
|
181
151
|
fs.rmSync(homeDir, { recursive: true, force: true });
|
|
182
152
|
}
|
|
@@ -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, triage, discover, criteria, plan, implement, fix, verify, change, architecture, docs, commit, pr, handoff, and help.
|
|
4
|
+
disable-model-invocation: true
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Oris Flow — the router
|
|
@@ -22,6 +23,7 @@ Match the desired outcome to ONE route, then apply its reference:
|
|
|
22
23
|
|-------|----------------------|-------|
|
|
23
24
|
| setup | prepare / map / refresh an existing repo for Oris and agents | `skills/oris-flow/references/setup.md` |
|
|
24
25
|
| new | bootstrap a project from scratch, greenfield, empty directory | `skills/oris-flow/references/new.md` |
|
|
26
|
+
| triage | capture an idea, bug report, or request as a work item with an agent-ready brief | `skills/oris-flow/references/triage.md` |
|
|
25
27
|
| discover | understand or define product behavior, functional analysis | `skills/oris-flow/references/discover.md` |
|
|
26
28
|
| criteria | acceptance criteria, QA checks, testable scenarios | `skills/oris-flow/references/criteria.md` |
|
|
27
29
|
| plan | a technical implementation plan before coding | `skills/oris-flow/references/plan.md` |
|
|
@@ -29,11 +31,11 @@ Match the desired outcome to ONE route, then apply its reference:
|
|
|
29
31
|
| fix | broken behavior, a regression, a failed QA check | `skills/oris-flow/references/fix.md` |
|
|
30
32
|
| verify | check acceptance criteria against the real product, once | `skills/oris-flow/references/verify.md` |
|
|
31
33
|
| 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
34
|
| architecture | review architecture, find deepening/refactor opportunities | `skills/oris-flow/references/architecture.md` |
|
|
34
35
|
| docs | update task docs after a change | `skills/oris-flow/references/docs.md` |
|
|
35
|
-
| commit | commit changes with the Oris standard —
|
|
36
|
+
| commit | commit changes with the Oris standard — Conventional Commits, work item linked | `skills/oris-flow/references/commit.md` |
|
|
36
37
|
| pr | open or update a pull request, English title/description linked to the work item | `skills/oris-flow/references/pr.md` |
|
|
38
|
+
| handoff | package session state — decisions, evidence, next move — to resume in a fresh session | `skills/oris-flow/references/handoff.md` |
|
|
37
39
|
| help | how Oris works, install, update, troubleshooting | `skills/oris-flow/references/help.md` |
|
|
38
40
|
|
|
39
41
|
## Precedence (the calls that look ambiguous)
|
|
@@ -43,14 +45,18 @@ Match the desired outcome to ONE route, then apply its reference:
|
|
|
43
45
|
2. RETHINK beats build: analyze, question, or reshape existing work →
|
|
44
46
|
route to the interview owner (discover / plan / change), NEVER straight to implement.
|
|
45
47
|
3. CHANGED spec on a feature that already has task docs or code → change, not discover —
|
|
46
|
-
change owns the delta and the history.
|
|
48
|
+
change owns the delta and the history. Spec unchanged but code/reality moved on and the
|
|
49
|
+
task docs lag behind → docs, not change.
|
|
47
50
|
4. BROKEN behavior → fix. NEW planned work → implement. The menu answer "Implement or
|
|
48
51
|
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
|
|
52
|
+
5. EMPTY directory or from-scratch intent → new. Existing repo never touched by Oris
|
|
53
|
+
(no `AGENTS.md`, no `.oris-flow/`) → recommend setup first; respect the user's choice.
|
|
54
|
+
6. COMMIT staged/working changes with the standard → commit. OPEN or update a pull request
|
|
53
55
|
→ pr. Neither writes product code — they package and publish what implement/fix produced.
|
|
56
|
+
7. CAPTURE work for later (an idea, a report, backlog intake) → triage — it creates the
|
|
57
|
+
work item and stops. DOING the work now → discover / fix / implement.
|
|
58
|
+
8. CONTINUE elsewhere — session ending, context full, "pass this to another agent" →
|
|
59
|
+
handoff.
|
|
54
60
|
|
|
55
61
|
## Menu (low confidence)
|
|
56
62
|
|
|
@@ -60,22 +66,26 @@ fallback in `references/conventions.md` `## Questions`), options:
|
|
|
60
66
|
|
|
61
67
|
- Setup repository
|
|
62
68
|
- New project from scratch
|
|
69
|
+
- Capture an idea or bug as a work item (triage)
|
|
63
70
|
- Understand / define behavior (discovery)
|
|
64
71
|
- Acceptance criteria
|
|
65
72
|
- Technical plan
|
|
66
73
|
- Implement or fix
|
|
67
74
|
- Verify acceptance criteria
|
|
68
75
|
- Spec changed: update feature docs
|
|
69
|
-
- Loop: repeat until verified
|
|
70
76
|
- Architecture review
|
|
71
77
|
- Update docs
|
|
72
78
|
- Commit changes
|
|
73
79
|
- Open a pull request
|
|
80
|
+
- Hand off this session (continue elsewhere)
|
|
74
81
|
- Help
|
|
75
82
|
- Explain the options / Spiega le opzioni
|
|
76
83
|
|
|
77
|
-
IF "Explain the options" →
|
|
78
|
-
|
|
84
|
+
IF "Explain the options" → ONE message that contains BOTH: one explanation line per
|
|
85
|
+
option, THEN the full lettered list again below the explanations. Re-asking without the
|
|
86
|
+
explanations visible in that same message is the failure this rule exists to prevent.
|
|
87
|
+
Every other label maps to its route in the table above; "Implement or fix" is the one
|
|
88
|
+
label that splits, with exactly one follow-up question (Precedence 4).
|
|
79
89
|
|
|
80
90
|
## Never
|
|
81
91
|
|
|
@@ -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;
|
|
@@ -37,10 +37,13 @@ WRITE a self-contained HTML file to the OS temp dir (`architecture-review-<times
|
|
|
37
37
|
OPEN it (`start` | `open` | `xdg-open`), TELL the absolute path.
|
|
38
38
|
|
|
39
39
|
FORMAT: Tailwind CDN + Mermaid CDN. Per candidate, one card:
|
|
40
|
-
- title (names the deepening) + strength badge
|
|
40
|
+
- title (names the deepening) + strength badge — `Strong` (friction observed in the code,
|
|
41
|
+
fix shape clear) | `Worth exploring` (friction observed, fix shape uncertain) |
|
|
42
|
+
`Speculative` (pattern suggests it, not yet observed);
|
|
41
43
|
- files (monospaced), Problem (one sentence), Solution (one sentence);
|
|
42
44
|
- Before/After diagram side by side — Mermaid for graph-shaped, hand-built divs/SVG for
|
|
43
|
-
mass diagrams;
|
|
45
|
+
mass diagrams; the diagram must explain itself without a caption — if it needs one,
|
|
46
|
+
simplify the diagram;
|
|
44
47
|
- wins as bullets ≤6 words, glossary terms only — never "cleaner code";
|
|
45
48
|
- ADR conflict → amber callout ("contradicts ADR-0007 — worth reopening because…").
|
|
46
49
|
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
Specs changed while the feature exists (documented, planned, or half-built). Understand
|
|
4
4
|
the delta, update EVERYTHING together, keep the history traceable.
|
|
5
5
|
|
|
6
|
+
BOUNDARY: the business spec MOVED → this route. Spec unchanged but code/reality moved on
|
|
7
|
+
and the docs lag behind → `skills/oris-flow/references/docs.md`.
|
|
8
|
+
|
|
6
9
|
RULES: `references/conventions.md`. Doc standards: `references/doc-policy.md`.
|
|
7
10
|
|
|
8
11
|
## Delta interview
|
|
@@ -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
|
|
|
@@ -12,9 +12,13 @@ RULES: `references/conventions.md`. Doc standards: `references/doc-policy.md`.
|
|
|
12
12
|
preferred, never required — say once when missing) > current UI > code and tests.
|
|
13
13
|
2. DEDUCE create vs update: existing `acceptance-criteria.md` → update, PRESERVE IDs.
|
|
14
14
|
3. IDs: `AC-001`, `AC-002`, … stable across revisions.
|
|
15
|
-
4. Each scenario declares ALL of:
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
4. Each scenario declares ALL of:
|
|
16
|
+
- role + starting state;
|
|
17
|
+
- deterministic data;
|
|
18
|
+
- numbered user steps;
|
|
19
|
+
- observable outcome under the step that causes it;
|
|
20
|
+
- negative coverage;
|
|
21
|
+
- regressions to keep.
|
|
18
22
|
5. CLASSIFY each: `Verifiable` | `Needs setup/data` | `Blocked by Open QA`.
|
|
19
23
|
6. DISCARD criteria not checkable through observable product behavior. Unbuilt features:
|
|
20
24
|
only confirmed intended behavior.
|
|
@@ -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
|