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.
Files changed (79) hide show
  1. package/.cursor-plugin/plugin.json +2 -3
  2. package/CHANGELOG.md +24 -6
  3. package/README.md +23 -59
  4. package/docs/architecture.md +15 -11
  5. package/docs/distribution.md +3 -3
  6. package/docs/maintainer-guide.md +4 -4
  7. package/docs/user-guide.md +12 -20
  8. package/package.json +3 -6
  9. package/references/conventions.md +20 -13
  10. package/references/doc-policy.md +5 -5
  11. package/references/research.md +45 -0
  12. package/references/settings.md +11 -5
  13. package/references/settings.schema.json +10 -0
  14. package/scripts/flow/oris-flow-layout.mjs +0 -16
  15. package/scripts/flow/oris-flow-scan.mjs +32 -317
  16. package/scripts/flow/oris-gitignore.mjs +1 -5
  17. package/scripts/install/install-user-skills.mjs +3 -3
  18. package/scripts/install/uninstall-user-skills.mjs +2 -29
  19. package/scripts/oris-skills.mjs +0 -47
  20. package/scripts/tests/run-all-tests.mjs +1 -9
  21. package/scripts/tests/test-cleanliness.mjs +54 -0
  22. package/scripts/tests/test-oris-flow-scan.mjs +21 -91
  23. package/scripts/tests/test-routing-lifecycle.mjs +22 -53
  24. package/scripts/tests/test-schemas.mjs +17 -31
  25. package/scripts/tests/test-skill-style.mjs +5 -2
  26. package/skills/oris-flow/SKILL.md +6 -8
  27. package/skills/oris-flow/references/architecture.md +1 -1
  28. package/skills/oris-flow/references/commit.md +9 -8
  29. package/skills/oris-flow/references/criteria.md +2 -2
  30. package/skills/oris-flow/references/discover.md +2 -2
  31. package/skills/oris-flow/references/fix.md +7 -3
  32. package/skills/oris-flow/references/help.md +1 -6
  33. package/skills/oris-flow/references/implement.md +5 -3
  34. package/skills/oris-flow/references/new.md +6 -6
  35. package/skills/oris-flow/references/plan.md +7 -2
  36. package/skills/oris-flow/references/setup.md +28 -35
  37. package/skills/oris-flow/references/verify.md +4 -5
  38. package/agents/oris-loop-debriefer.md +0 -30
  39. package/agents/oris-loop-doctor.md +0 -32
  40. package/agents/oris-loop-executor.md +0 -35
  41. package/agents/oris-loop-verifier.md +0 -35
  42. package/references/loop-adapter.schema.json +0 -93
  43. package/references/loop-contract.md +0 -126
  44. package/references/loop.schema.json +0 -156
  45. package/references/repo-map.md +0 -51
  46. package/references/repo-map.schema.json +0 -213
  47. package/scripts/flow/oris-flow-clean-runtime.mjs +0 -182
  48. package/scripts/install/generate-agent-adapters.mjs +0 -81
  49. package/scripts/loop/oris-loop-bootstrap.mjs +0 -383
  50. package/scripts/loop/oris-loop-bundle.mjs +0 -22
  51. package/scripts/loop/oris-loop-chat.mjs +0 -396
  52. package/scripts/loop/oris-loop-demo.mjs +0 -171
  53. package/scripts/loop/oris-loop-document.mjs +0 -196
  54. package/scripts/loop/oris-loop-dry-run.mjs +0 -114
  55. package/scripts/loop/oris-loop-fixtures.mjs +0 -149
  56. package/scripts/loop/oris-loop-list.mjs +0 -81
  57. package/scripts/loop/oris-loop-paths.mjs +0 -232
  58. package/scripts/loop/oris-loop-run.mjs +0 -301
  59. package/scripts/loop/oris-loop-stop.mjs +0 -358
  60. package/scripts/loop/oris-loop-templates.mjs +0 -80
  61. package/scripts/loop/oris-loop-verify.mjs +0 -205
  62. package/scripts/tests/test-agent-adapters.mjs +0 -70
  63. package/scripts/tests/test-oris-1-0-cleanliness.mjs +0 -58
  64. package/scripts/tests/test-oris-flow-clean-runtime.mjs +0 -75
  65. package/scripts/tests/test-oris-loop-bootstrap.mjs +0 -94
  66. package/scripts/tests/test-oris-loop-document.mjs +0 -148
  67. package/scripts/tests/test-oris-loop-list.mjs +0 -74
  68. package/scripts/tests/test-oris-loop-run.mjs +0 -71
  69. package/scripts/tests/test-oris-loop-smoke.mjs +0 -120
  70. package/scripts/tests/test-oris-loop-stop.mjs +0 -432
  71. package/skills/oris-flow/references/loop-craft.md +0 -59
  72. package/skills/oris-flow/references/loop-improve.md +0 -32
  73. package/skills/oris-flow/references/loop-run.md +0 -47
  74. package/skills/oris-flow/references/loop.md +0 -56
  75. package/skills/oris-flow/templates/debriefer.md +0 -19
  76. package/skills/oris-flow/templates/doctor.md +0 -22
  77. package/skills/oris-flow/templates/executor.md +0 -25
  78. package/skills/oris-flow/templates/orchestrator.md +0 -36
  79. package/skills/oris-flow/templates/verifier.md +0 -24
@@ -1,156 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://oris.dev/schemas/oris-loop-v2.json",
4
- "title": "Oris Loop document front matter (v2)",
5
- "type": "object",
6
- "additionalProperties": true,
7
- "required": [
8
- "schemaVersion",
9
- "kind",
10
- "name",
11
- "approved",
12
- "phases",
13
- "limits",
14
- "permissions",
15
- "verification"
16
- ],
17
- "properties": {
18
- "schemaVersion": {
19
- "const": 2
20
- },
21
- "kind": {
22
- "const": "oris-loop"
23
- },
24
- "name": {
25
- "type": "string",
26
- "pattern": "^[a-z0-9]+(?:[._-][a-z0-9]+)*$",
27
- "minLength": 2,
28
- "maxLength": 80
29
- },
30
- "approved": {
31
- "type": "boolean",
32
- "description": "true only after the user approved goal, scope, prompts, and limits"
33
- },
34
- "models": {
35
- "type": "object",
36
- "additionalProperties": true,
37
- "description": "Per-role model: \"inherit\" (session model), an adapter alias, or a platform model id",
38
- "properties": {
39
- "default": { "$ref": "#/$defs/model" },
40
- "executor": { "$ref": "#/$defs/model" },
41
- "verifier": { "$ref": "#/$defs/model" },
42
- "doctor": { "$ref": "#/$defs/model" },
43
- "debriefer": { "$ref": "#/$defs/model" }
44
- }
45
- },
46
- "improve": {
47
- "type": "object",
48
- "additionalProperties": true,
49
- "properties": {
50
- "mode": {
51
- "enum": ["propose", "auto"],
52
- "description": "auto lets the debriefer rewrite prompts/ between passes; propose requires user approval for every change"
53
- }
54
- }
55
- },
56
- "phases": {
57
- "type": "array",
58
- "minItems": 1,
59
- "maxItems": 12,
60
- "items": {
61
- "type": "string",
62
- "pattern": "^[a-z][a-z0-9-]*$"
63
- }
64
- },
65
- "roles": {
66
- "type": "array",
67
- "description": "Active roles per pass. Default [executor, verifier]; opt in doctor and/or debriefer. Executor and verifier are always required.",
68
- "minItems": 2,
69
- "uniqueItems": true,
70
- "items": {
71
- "enum": ["executor", "verifier", "doctor", "debriefer"]
72
- },
73
- "allOf": [
74
- { "contains": { "const": "executor" } },
75
- { "contains": { "const": "verifier" } }
76
- ]
77
- },
78
- "limits": {
79
- "type": "object",
80
- "required": [
81
- "maxIterations",
82
- "maxNoProgress"
83
- ],
84
- "additionalProperties": true,
85
- "properties": {
86
- "maxIterations": {
87
- "type": "integer",
88
- "minimum": 1,
89
- "maximum": 500
90
- },
91
- "maxNoProgress": {
92
- "type": "integer",
93
- "minimum": 1,
94
- "maximum": 50
95
- },
96
- "maxMinutes": {
97
- "type": "integer",
98
- "minimum": 1,
99
- "maximum": 1440
100
- }
101
- }
102
- },
103
- "permissions": {
104
- "type": "object",
105
- "required": [
106
- "allowedPaths",
107
- "forbiddenActions"
108
- ],
109
- "additionalProperties": true,
110
- "properties": {
111
- "allowedPaths": {
112
- "type": "array",
113
- "items": {
114
- "type": "string",
115
- "minLength": 1
116
- }
117
- },
118
- "forbiddenActions": {
119
- "type": "array",
120
- "minItems": 1,
121
- "items": {
122
- "type": "string",
123
- "minLength": 1
124
- }
125
- }
126
- }
127
- },
128
- "verification": {
129
- "type": "object",
130
- "required": [
131
- "commands",
132
- "evidenceRequired"
133
- ],
134
- "additionalProperties": true,
135
- "properties": {
136
- "commands": {
137
- "type": "array",
138
- "items": {
139
- "type": "string",
140
- "minLength": 1
141
- }
142
- },
143
- "evidenceRequired": {
144
- "type": "boolean"
145
- }
146
- }
147
- }
148
- },
149
- "$defs": {
150
- "model": {
151
- "type": "string",
152
- "minLength": 1,
153
- "maxLength": 120
154
- }
155
- }
156
- }
@@ -1,51 +0,0 @@
1
- # Oris Repository Map
2
-
3
- Setup map = the default project context for every Oris skill and loop. It exists so agents stop re-scanning and users stop repeating project facts.
4
-
5
- ## Files
6
-
7
- | File | Scope | Purpose |
8
- |------|-------|---------|
9
- | `.oris-flow/manifest.json` | repo | compact index: section paths, freshness, confidence |
10
- | `.oris-flow/maps/**` | repo | short markdown maps: docs, stack, commands, code areas, tests |
11
- | `.oris-flow/adapter.json` | repo | loop adapter: paths, commands, model aliases |
12
- | `.oris-flow/settings.json` | repo | preferences: `responseStyle` + `artifactLanguage` (non-secret) |
13
- | `{tasksRoot}/{task}/…` | task | business artifacts only — `setup.tasksRoot` in the manifest, default `.oris-flow/tasks` (see `references/doc-policy.md`) |
14
-
15
- Manifest and maps are never task authority and NEVER hold secrets.
16
-
17
- ## Manifest shape (v1)
18
-
19
- ```json
20
- {
21
- "$schema": "https://oris.dev/schemas/oris-flow-map-v1.json",
22
- "schemaVersion": 1,
23
- "repository": "",
24
- "generatedAt": "",
25
- "updatedAt": "",
26
- "setup": { "versionControl": { "orisFlowMap": "commit" } },
27
- "sections": {}
28
- }
29
- ```
30
-
31
- - Small: only sections that exist for the detected stack; no placeholders.
32
- - Each section: file path, one-line summary, `confidence`, `lastScannedAt`, stale flag.
33
- - Confidence: `confirmed` (user/authoritative) | `detected` (strong scan evidence) | `inferred` (unverified) | `stale` (no longer matches).
34
- - `setup.versionControl.orisFlowMap`: `commit` | `gitignore` — applied via `npx oris-skills flow version-control --mode <choice>`.
35
- - Schema: `references/repo-map.schema.json`.
36
-
37
- ## Map sections
38
-
39
- `.oris-flow/maps/`: `README.md`, `docs.md`, `stack.md`, `commands.md`, `code.md`, `tests.md`.
40
- Each section: purpose, authoritative paths, confirmed decisions, key commands/conventions, confidence + stale notes, pointers instead of copied content.
41
-
42
- ## Consumption rules
43
-
44
- 1. MANIFEST first, then only the relevant map sections — before any broad scan.
45
- 2. VERIFY stale or high-risk facts against the repository before consequential actions.
46
- 3. Loops: the orchestrator passes relevant setup-map facts into role prompts at craft time; users never repeat project facts per loop.
47
- 4. SUGGEST `/oris-flow` → Setup refresh only when a stale gap materially affects the task.
48
-
49
- ## Command surface
50
-
51
- In target repositories always use the installed CLI (`npx oris-skills flow scan|version-control|clean-runtime`, `npx oris-skills loop …`), never `node scripts/...` paths — those exist only in the OrisSkills source checkout.
@@ -1,213 +0,0 @@
1
- {
2
- "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://oris.dev/schemas/oris-flow-map-v1.json",
4
- "title": "Oris Flow Repository Map",
5
- "type": "object",
6
- "additionalProperties": false,
7
- "required": [
8
- "$schema",
9
- "schemaVersion",
10
- "repository",
11
- "generatedAt",
12
- "updatedAt",
13
- "setup",
14
- "sections"
15
- ],
16
- "properties": {
17
- "$schema": {
18
- "const": "https://oris.dev/schemas/oris-flow-map-v1.json"
19
- },
20
- "schemaVersion": {
21
- "const": 1
22
- },
23
- "repository": {
24
- "type": "string",
25
- "minLength": 1
26
- },
27
- "generatedAt": {
28
- "type": "string",
29
- "format": "date-time"
30
- },
31
- "updatedAt": {
32
- "type": "string",
33
- "format": "date-time"
34
- },
35
- "headSha": {
36
- "type": ["string", "null"],
37
- "description": "Commit SHA the last scan was anchored to; null outside git. Freshness = headSha vs current HEAD."
38
- },
39
- "mapVersion": {
40
- "type": "string",
41
- "minLength": 1
42
- },
43
- "projectType": {
44
- "$ref": "#/$defs/projectType"
45
- },
46
- "truncated": {
47
- "type": "boolean"
48
- },
49
- "setup": {
50
- "$ref": "#/$defs/setup"
51
- },
52
- "sections": {
53
- "$ref": "#/$defs/sections"
54
- },
55
- "stacks": {
56
- "type": "array",
57
- "items": {
58
- "$ref": "#/$defs/stack"
59
- }
60
- },
61
- "commands": {
62
- "type": "array",
63
- "items": {
64
- "$ref": "#/$defs/detectedCommand"
65
- }
66
- }
67
- },
68
- "$defs": {
69
- "confidence": {
70
- "type": "string",
71
- "enum": ["confirmed", "detected", "inferred", "stale"]
72
- },
73
- "sectionPointer": {
74
- "type": "object",
75
- "additionalProperties": false,
76
- "required": ["path", "confidence"],
77
- "properties": {
78
- "path": {
79
- "type": "string",
80
- "pattern": "^\\.oris-flow/"
81
- },
82
- "confidence": {
83
- "$ref": "#/$defs/confidence"
84
- },
85
- "lastScannedAt": {
86
- "type": "string",
87
- "format": "date-time"
88
- },
89
- "stale": {
90
- "type": "boolean"
91
- },
92
- "summary": {
93
- "type": "string"
94
- }
95
- }
96
- },
97
- "sections": {
98
- "type": "object",
99
- "additionalProperties": {
100
- "$ref": "#/$defs/sectionPointer"
101
- },
102
- "propertyNames": {
103
- "pattern": "^[a-z][a-zA-Z0-9-]*$"
104
- }
105
- },
106
- "setup": {
107
- "type": "object",
108
- "additionalProperties": false,
109
- "required": ["mode", "scanDepth"],
110
- "properties": {
111
- "mode": {
112
- "type": "string",
113
- "enum": ["create", "refresh", "review-stale", "update-area", "summary"]
114
- },
115
- "scanDepth": {
116
- "type": "string",
117
- "enum": ["fast", "balanced", "manual-summary"]
118
- },
119
- "tasksRoot": {
120
- "type": "string",
121
- "minLength": 1
122
- },
123
- "confirmedDecisions": {
124
- "type": "array",
125
- "items": {
126
- "type": "string"
127
- }
128
- },
129
- "tradeOff": {
130
- "type": "string"
131
- },
132
- "versionControl": {
133
- "type": "object",
134
- "additionalProperties": false,
135
- "properties": {
136
- "orisFlowMap": {
137
- "type": "string",
138
- "enum": ["commit", "gitignore"]
139
- }
140
- }
141
- }
142
- }
143
- },
144
- "stack": {
145
- "type": "object",
146
- "additionalProperties": false,
147
- "required": ["id", "label", "evidence"],
148
- "properties": {
149
- "id": {
150
- "type": "string",
151
- "pattern": "^[a-z][a-z0-9-]*$"
152
- },
153
- "label": {
154
- "type": "string",
155
- "minLength": 1
156
- },
157
- "evidence": {
158
- "type": "array",
159
- "items": {
160
- "type": "string"
161
- }
162
- }
163
- }
164
- },
165
- "detectedCommand": {
166
- "type": "object",
167
- "additionalProperties": false,
168
- "required": ["name", "cwd", "command", "evidence"],
169
- "properties": {
170
- "name": {
171
- "type": "string",
172
- "pattern": "^[a-z][a-z0-9-]*$"
173
- },
174
- "cwd": {
175
- "type": "string"
176
- },
177
- "command": {
178
- "type": "string"
179
- },
180
- "evidence": {
181
- "type": "string"
182
- },
183
- "confidence": {
184
- "$ref": "#/$defs/confidence"
185
- }
186
- }
187
- },
188
- "projectType": {
189
- "type": "object",
190
- "additionalProperties": false,
191
- "required": ["id", "label", "confidence"],
192
- "properties": {
193
- "id": {
194
- "type": "string",
195
- "enum": ["empty", "monorepo", "webapp", "api", "library", "cli", "unknown"]
196
- },
197
- "label": {
198
- "type": "string",
199
- "minLength": 1
200
- },
201
- "confidence": {
202
- "$ref": "#/$defs/confidence"
203
- },
204
- "evidence": {
205
- "type": "array",
206
- "items": {
207
- "type": "string"
208
- }
209
- }
210
- }
211
- }
212
- }
213
- }
@@ -1,182 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import {
5
- ORIS_FLOW_ADAPTER,
6
- ORIS_FLOW_LOOPS_ROOT,
7
- ORIS_FLOW_MANIFEST,
8
- ORIS_FLOW_MAPS_ROOT,
9
- ORIS_FLOW_RUNTIME,
10
- } from "./oris-flow-layout.mjs";
11
-
12
- const DEFAULT_RUNTIME_RELATIVE = ORIS_FLOW_RUNTIME;
13
-
14
- function readJson(filePath) {
15
- if (!fs.existsSync(filePath)) return null;
16
- return JSON.parse(fs.readFileSync(filePath, "utf8"));
17
- }
18
-
19
- function assertSafeRelativePath(relativePath, label) {
20
- const normalized = String(relativePath ?? "").replace(/\\/g, "/");
21
- if (!normalized || normalized.startsWith("/") || /^[A-Za-z]:\//.test(normalized)) {
22
- throw new Error(`${label} must be a repository-relative path.`);
23
- }
24
- if (normalized.split("/").includes("..")) {
25
- throw new Error(`${label} must not contain '..'.`);
26
- }
27
- return normalized;
28
- }
29
-
30
- function assertUnderRepository(repositoryRoot, targetPath) {
31
- const root = path.resolve(repositoryRoot);
32
- const resolved = path.resolve(targetPath);
33
- const relative = path.relative(root, resolved);
34
- if (relative.startsWith("..") || path.isAbsolute(relative)) {
35
- throw new Error(`Refusing to clean path outside repository: ${targetPath}`);
36
- }
37
- return resolved;
38
- }
39
-
40
- export function resolveCleanTargets(repositoryRoot) {
41
- const root = path.resolve(repositoryRoot);
42
- const adapter = readJson(path.join(root, ORIS_FLOW_ADAPTER));
43
- const runtimeRelative = assertSafeRelativePath(
44
- adapter?.paths?.runtime ?? DEFAULT_RUNTIME_RELATIVE,
45
- "paths.runtime",
46
- );
47
-
48
- if (runtimeRelative.replace(/\\/g, "/") === ORIS_FLOW_RUNTIME) {
49
- return [assertUnderRepository(root, path.join(root, ...ORIS_FLOW_RUNTIME.split("/")))];
50
- }
51
-
52
- return [assertUnderRepository(root, path.join(root, ...runtimeRelative.split("/")))];
53
- }
54
-
55
- function listEntries(directoryPath) {
56
- if (!fs.existsSync(directoryPath)) return [];
57
- const entries = [];
58
- const stack = [directoryPath];
59
- while (stack.length > 0) {
60
- const current = stack.pop();
61
- for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
62
- const fullPath = path.join(current, entry.name);
63
- entries.push(fullPath);
64
- if (entry.isDirectory()) stack.push(fullPath);
65
- }
66
- }
67
- return entries.sort();
68
- }
69
-
70
- function removeEntry(entryPath) {
71
- if (!fs.existsSync(entryPath)) return;
72
- const stat = fs.lstatSync(entryPath);
73
- if (stat.isDirectory()) {
74
- fs.rmSync(entryPath, { recursive: true, force: true });
75
- return;
76
- }
77
- fs.rmSync(entryPath, { force: true });
78
- }
79
-
80
- export function cleanFlowRuntime(repositoryRoot, options = {}) {
81
- const root = path.resolve(repositoryRoot);
82
- const dryRun = Boolean(options.dryRun);
83
- const targets = resolveCleanTargets(root);
84
- const removed = [];
85
- const preserved = [
86
- ORIS_FLOW_MANIFEST,
87
- `${ORIS_FLOW_MAPS_ROOT}/**`,
88
- ORIS_FLOW_ADAPTER,
89
- `${ORIS_FLOW_LOOPS_ROOT}/**`,
90
- ".cursor/hooks.json",
91
- ".cursor/hooks/oris-loop-stop.mjs",
92
- ];
93
-
94
- for (const target of targets) {
95
- if (!fs.existsSync(target)) continue;
96
- const stat = fs.lstatSync(target);
97
- if (stat.isDirectory()) {
98
- for (const entry of listEntries(target)) {
99
- const relative = path.relative(root, entry).split(path.sep).join("/");
100
- if (dryRun) {
101
- removed.push(relative);
102
- } else {
103
- removeEntry(entry);
104
- removed.push(relative);
105
- }
106
- }
107
- if (dryRun) {
108
- removed.push(path.relative(root, target).split(path.sep).join("/"));
109
- } else {
110
- removeEntry(target);
111
- removed.push(path.relative(root, target).split(path.sep).join("/"));
112
- }
113
- continue;
114
- }
115
- const relative = path.relative(root, target).split(path.sep).join("/");
116
- if (dryRun) {
117
- removed.push(relative);
118
- } else {
119
- removeEntry(target);
120
- removed.push(relative);
121
- }
122
- }
123
-
124
- return {
125
- repositoryRoot: root,
126
- dryRun,
127
- targets: targets.map((target) => path.relative(root, target).split(path.sep).join("/")),
128
- removed,
129
- preserved,
130
- cleaned: removed.length > 0,
131
- };
132
- }
133
-
134
- function parseArgs(argv) {
135
- const options = {
136
- repositoryRoot: process.cwd(),
137
- dryRun: false,
138
- json: false,
139
- };
140
- for (let i = 0; i < argv.length; i += 1) {
141
- const raw = argv[i];
142
- if (raw === "--dry-run") options.dryRun = true;
143
- else if (raw === "--json") options.json = true;
144
- else if (raw.startsWith("--repository-root=")) options.repositoryRoot = raw.slice("--repository-root=".length);
145
- else if (raw === "--repository-root" && argv[i + 1]) {
146
- options.repositoryRoot = argv[i + 1];
147
- i += 1;
148
- } else if (!raw.startsWith("-")) {
149
- options.repositoryRoot = raw;
150
- }
151
- }
152
- return options;
153
- }
154
-
155
- export function printCleanReport(result) {
156
- console.log(`Oris Flow runtime clean${result.dryRun ? " (dry run)" : ""}`);
157
- console.log(`Repository: ${result.repositoryRoot}`);
158
- console.log("");
159
- console.log("Targets:");
160
- for (const target of result.targets) console.log(`- ${target}`);
161
- console.log("");
162
- if (result.removed.length === 0) {
163
- console.log("No ephemeral runtime files found.");
164
- return;
165
- }
166
- console.log(`${result.dryRun ? "Would remove" : "Removed"} ${result.removed.length} path(s):`);
167
- for (const entry of result.removed) console.log(`- ${entry}`);
168
- console.log("");
169
- console.log("Preserved by design:");
170
- for (const entry of result.preserved) console.log(`- ${entry}`);
171
- }
172
-
173
- if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url))) {
174
- const options = parseArgs(process.argv.slice(2));
175
- const result = cleanFlowRuntime(options.repositoryRoot, { dryRun: options.dryRun });
176
- if (options.json) {
177
- console.log(JSON.stringify(result, null, 2));
178
- } else {
179
- printCleanReport(result);
180
- }
181
- process.exit(0);
182
- }
@@ -1,81 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from "node:fs";
4
- import path from "node:path";
5
- import process from "node:process";
6
- import { fileURLToPath } from "node:url";
7
-
8
- const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..");
9
- const sourceRoot = path.join(repoRoot, "agents");
10
-
11
- function readAgent(filePath) {
12
- const text = fs.readFileSync(filePath, "utf8");
13
- const match = text.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/);
14
- if (!match) throw new Error(`Agent file is missing front matter: ${filePath}`);
15
- const frontMatter = Object.fromEntries(
16
- match[1]
17
- .split(/\r?\n/)
18
- .map((line) => line.trim())
19
- .filter(Boolean)
20
- .map((line) => {
21
- const index = line.indexOf(":");
22
- if (index < 0) throw new Error(`Unsupported front matter line in ${filePath}: ${line}`);
23
- return [line.slice(0, index).trim(), line.slice(index + 1).trim()];
24
- }),
25
- );
26
- return { frontMatter, body: match[2].trim() };
27
- }
28
-
29
- function writeFile(filePath, content) {
30
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
31
- fs.writeFileSync(filePath, `${content.trimEnd()}\n`, "utf8");
32
- }
33
-
34
- function renderMarkdownAgent(agent) {
35
- return `---
36
- name: ${agent.frontMatter.name}
37
- description: ${agent.frontMatter.description}
38
- readonly: ${agent.frontMatter.readonly ?? "false"}
39
- ---
40
-
41
- ${agent.body}
42
- `;
43
- }
44
-
45
- function renderCodexAgent(agent) {
46
- const name = JSON.stringify(agent.frontMatter.name);
47
- const description = JSON.stringify(agent.frontMatter.description);
48
- const instructions = JSON.stringify(agent.body);
49
- return `name = ${name}
50
- description = ${description}
51
- developer_instructions = ${instructions}
52
- `;
53
- }
54
-
55
- function readAgents() {
56
- return fs.readdirSync(sourceRoot)
57
- .filter((name) => name.endsWith(".md"))
58
- .map((name) => ({ name, ...readAgent(path.join(sourceRoot, name)) }));
59
- }
60
-
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
- */
66
- export function generateAgentAdapters(root = repoRoot) {
67
- const agents = readAgents();
68
- for (const agent of agents) {
69
- writeFile(path.join(root, ".cursor", "agents", agent.name), renderMarkdownAgent(agent));
70
- writeFile(path.join(root, ".claude", "agents", agent.name), renderMarkdownAgent(agent));
71
- writeFile(path.join(root, ".codex", "agents", agent.name.replace(/\.md$/, ".toml")), renderCodexAgent(agent));
72
- }
73
- return agents.map((agent) => agent.frontMatter.name);
74
- }
75
-
76
- if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(fileURLToPath(import.meta.url))) {
77
- const rootArg = process.argv.find((arg) => arg.startsWith("--root="));
78
- const root = rootArg ? path.resolve(rootArg.slice("--root=".length)) : repoRoot;
79
- const generated = generateAgentAdapters(root);
80
- console.log(JSON.stringify({ generated }, null, 2));
81
- }