@orkestrel/scaffold 0.0.107 → 0.0.108
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/dist/agents/skills/orkestrel-dispatch/scripts/sweep.js +124 -4
- package/dist/bin/main.js +1 -1
- package/dist/bin/main.js.map +1 -1
- package/dist/host/AGENTS.md +5 -1
- package/dist/host/agents/orchestration.md +60 -45
- package/dist/host/agents/platforms/posix.md +15 -0
- package/dist/host/agents/platforms/windows.md +37 -0
- package/dist/host/agents/skills/orkestrel-debrief/references/retention.md +10 -10
- package/dist/host/agents/skills/orkestrel-dispatch/SKILL.md +6 -5
- package/dist/host/agents/skills/orkestrel-dispatch/references/launch.md +12 -8
- package/dist/host/agents/skills/orkestrel-dispatch/scripts/sweep.ts +144 -4
- package/dist/host/agents/skills/orkestrel-parallel/SKILL.md +67 -10
- package/dist/host/agents/skills/orkestrel-parallel/agents/openai.yaml +2 -2
- package/dist/host/agents/skills/orkestrel-parallel/references/claude.md +32 -0
- package/dist/host/agents/skills/orkestrel-parallel/references/codex.md +28 -0
- package/dist/host/agents/skills/orkestrel-parallel/references/cursor.md +29 -0
- package/dist/host/agents/skills/orkestrel-parallel/references/t3.md +31 -0
- package/dist/host/agents/skills/orkestrel-publish/references/wave.md +2 -1
- package/dist/host/agents/transports/codex.md +1 -1
- package/dist/host/claude/AGENTS.md +6 -9
- package/dist/host/claude/rules/campaign.md +55 -0
- package/dist/host/claude/rules/documentation.md +2 -0
- package/dist/host/claude/rules/portability.md +1 -2
- package/dist/host/claude/rules/typescript.md +1 -1
- package/dist/host/claude/rules/writing.md +5 -2
- package/dist/host/claude/skills/orkestrel-parallel/SKILL.md +1 -1
- package/dist/host/codex/config.toml +3 -3
- package/dist/host/configs/policy.ts +106 -1
- package/dist/host/cursor/rules/orchestration.mdc +1 -0
- package/dist/host/dotfiles/oxlintrc.json +4 -2
- package/dist/host/guides/scaffold.md +56 -1
- package/dist/host/manifest.json +64 -27
- package/dist/host/tests/config.test.ts +560 -5
- package/dist/host/tests/setupPolicy.ts +496 -6
- package/dist/src/core/index.cjs +26 -2
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +1 -1
- package/dist/src/core/index.d.ts +1 -1
- package/dist/src/core/index.js +26 -2
- package/dist/src/core/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/host/agents/skills/orkestrel-parallel/references/harnesses.md +0 -19
|
@@ -2,14 +2,21 @@
|
|
|
2
2
|
// node .agents/skills/orkestrel-dispatch/scripts/sweep.ts --report
|
|
3
3
|
// node .agents/skills/orkestrel-dispatch/scripts/sweep.ts --tmp [--older-than MINUTES]
|
|
4
4
|
// node .agents/skills/orkestrel-dispatch/scripts/sweep.ts --unit UNIT [--delete]
|
|
5
|
-
// --report lists leftovers under tmp/ and .orkestrel/ by age and deletes nothing.
|
|
5
|
+
// --report lists leftovers under tmp/ and .orkestrel/ by age and deletes nothing. It also checks
|
|
6
|
+
// every .md file under .orkestrel/: a backticked repository-relative path that git ignores prints
|
|
7
|
+
// `<file>:<line> cites ignored path <path>` (a path git tracks is never reported, nor is a glob or
|
|
8
|
+
// placeholder whose literal prefix git tracks), and an absolute machine path on a line prints
|
|
9
|
+
// `<file>:<line> names a machine path`. Both lines are report-only.
|
|
6
10
|
// --tmp deletes launch files older than the threshold (default 60 minutes), removes emptied
|
|
7
11
|
// directories, and refuses while any launch file changed in the last 2 minutes.
|
|
8
12
|
// --unit lists one accepted unit's files under the launch directories (the brief, report, claims,
|
|
9
13
|
// journal, errors, status, pid, and last-message files whose name is UNIT followed by `-` or `.`), and
|
|
10
14
|
// --delete removes them, refusing while one changed in the last 2 minutes.
|
|
15
|
+
// --tmp and --unit --delete never delete a file git tracks: each prints `kept tracked <path>` and the
|
|
16
|
+
// refusal ignores a tracked file; --unit alone marks a tracked file in its listing.
|
|
11
17
|
// Exit 0 on success, 2 when a deletion is refused, 64 on usage.
|
|
12
|
-
import {
|
|
18
|
+
import { spawnSync } from 'node:child_process';
|
|
19
|
+
import { existsSync, readFileSync, readdirSync, rmSync } from 'node:fs';
|
|
13
20
|
import { basename, join } from 'node:path';
|
|
14
21
|
import { listFiles, readMissingFlags, readOption } from "./helpers.js";
|
|
15
22
|
const LAUNCH_DIRECTORIES = [
|
|
@@ -24,6 +31,13 @@ const LAUNCH_DIRECTORIES = [
|
|
|
24
31
|
const RECENT_MS = 2 * 60_000;
|
|
25
32
|
// A launch directory holds nothing a walk skips, so every file beneath it counts.
|
|
26
33
|
const NOTHING_SKIPPED = new Set();
|
|
34
|
+
// Git's output is bounded by the repository's file count, so a generous buffer is the only limit.
|
|
35
|
+
const GIT_BUFFER = 64 * 1024 * 1024;
|
|
36
|
+
// An absolute machine path: a drive-letter path, or one that opens `/Users/`, `/home/`, or `~/`. The
|
|
37
|
+
// lookbehind keeps `https://`, `src/home/`, and `./home/` from matching.
|
|
38
|
+
const MACHINE_PATH = /(?<![\w.-])(?:[A-Za-z]:[\\/]|\/(?:Users|home)\/|~\/)/u;
|
|
39
|
+
const BACKTICKED = /`([^`\n]+)`/gu;
|
|
40
|
+
const PLACEHOLDER = /[*?<{]/u;
|
|
27
41
|
function formatDay(modified) {
|
|
28
42
|
return new Date(modified).toISOString().slice(0, 10);
|
|
29
43
|
}
|
|
@@ -67,6 +81,99 @@ function readThreshold(argv) {
|
|
|
67
81
|
const value = Number(readOption(argv, '--older-than'));
|
|
68
82
|
return Number.isFinite(value) && value > 0 ? value : undefined;
|
|
69
83
|
}
|
|
84
|
+
// Lists the files git tracks beneath the scope (the whole repository when empty), as forward-slash
|
|
85
|
+
// paths from the root. Any git failure reads as nothing tracked, so the run keeps its old behavior.
|
|
86
|
+
function listTracked(scope) {
|
|
87
|
+
const args = ['ls-files', '-z', ...(scope.length === 0 ? [] : ['--', ...scope])];
|
|
88
|
+
const run = spawnSync('git', args, { encoding: 'utf8', windowsHide: true, maxBuffer: GIT_BUFFER });
|
|
89
|
+
if (run.status !== 0)
|
|
90
|
+
return [];
|
|
91
|
+
return run.stdout.split('\0').filter((path) => path !== '');
|
|
92
|
+
}
|
|
93
|
+
// Names the probes git ignores, from one batched `git check-ignore`: status 0 and 1 both answer,
|
|
94
|
+
// and any other status reads as nothing ignored.
|
|
95
|
+
function readIgnored(probes) {
|
|
96
|
+
if (probes.length === 0)
|
|
97
|
+
return new Set();
|
|
98
|
+
const run = spawnSync('git', ['check-ignore', '--stdin', '-z'], {
|
|
99
|
+
encoding: 'utf8',
|
|
100
|
+
windowsHide: true,
|
|
101
|
+
maxBuffer: GIT_BUFFER,
|
|
102
|
+
input: probes.join('\0'),
|
|
103
|
+
});
|
|
104
|
+
if (run.status !== 0 && run.status !== 1)
|
|
105
|
+
return new Set();
|
|
106
|
+
return new Set(run.stdout.split('\0').filter((path) => path !== ''));
|
|
107
|
+
}
|
|
108
|
+
// Reads a backticked token as a repository-relative citation. A token counts when it has no
|
|
109
|
+
// whitespace, contains `/`, and is no URL; the path stops at its first glob or placeholder character
|
|
110
|
+
// and drops a trailing `/`. A machine path, a path outside the repository, and a backslash path are
|
|
111
|
+
// no citation.
|
|
112
|
+
function readCitation(token) {
|
|
113
|
+
if (/\s/u.test(token) || !token.includes('/') || token.includes('://'))
|
|
114
|
+
return undefined;
|
|
115
|
+
const stop = token.search(PLACEHOLDER);
|
|
116
|
+
const cut = (stop === -1 ? token : token.slice(0, stop)).replace(/^\.\//u, '');
|
|
117
|
+
const path = cut.replace(/\/+$/u, '');
|
|
118
|
+
if (path === '' || /^(?:\/|~|\.\.(?:\/|$)|-|[A-Za-z]:)/u.test(path) || path.includes('\\')) {
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
return { path, directory: cut.endsWith('/'), prefix: stop === -1 ? undefined : cut };
|
|
122
|
+
}
|
|
123
|
+
function listFindings(file) {
|
|
124
|
+
const lines = readFileSync(file, 'utf8').split(/\r\n|\n/u);
|
|
125
|
+
return lines.flatMap((text, index) => {
|
|
126
|
+
const line = index + 1;
|
|
127
|
+
const machine = MACHINE_PATH.test(text) ? [{ file, line, citation: undefined }] : [];
|
|
128
|
+
const cited = [...text.matchAll(BACKTICKED)].flatMap((match) => {
|
|
129
|
+
const citation = readCitation(match[1] ?? '');
|
|
130
|
+
return citation === undefined ? [] : [{ file, line, citation }];
|
|
131
|
+
});
|
|
132
|
+
return [...machine, ...cited];
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function probeOf(citation) {
|
|
136
|
+
return citation.directory ? `${citation.path}/` : citation.path;
|
|
137
|
+
}
|
|
138
|
+
// A citation is covered when git tracks the path, a file beneath it, or a file its glob prefix opens.
|
|
139
|
+
function isCovered(citation, tracked) {
|
|
140
|
+
return tracked.some((path) => path === citation.path ||
|
|
141
|
+
path.startsWith(`${citation.path}/`) ||
|
|
142
|
+
(citation.prefix !== undefined && path.startsWith(citation.prefix)));
|
|
143
|
+
}
|
|
144
|
+
function reportCitations() {
|
|
145
|
+
if (!existsSync('.orkestrel'))
|
|
146
|
+
return [];
|
|
147
|
+
const findings = listFiles('.orkestrel', NOTHING_SKIPPED)
|
|
148
|
+
.map((file) => file.path)
|
|
149
|
+
.filter((path) => /\.md$/iu.test(path))
|
|
150
|
+
.sort()
|
|
151
|
+
.flatMap((path) => listFindings(path));
|
|
152
|
+
const probes = new Set();
|
|
153
|
+
for (const finding of findings) {
|
|
154
|
+
if (finding.citation !== undefined)
|
|
155
|
+
probes.add(probeOf(finding.citation));
|
|
156
|
+
}
|
|
157
|
+
const ignored = readIgnored([...probes]);
|
|
158
|
+
const tracked = ignored.size === 0 ? [] : listTracked([]);
|
|
159
|
+
const seen = new Set();
|
|
160
|
+
const lines = [];
|
|
161
|
+
for (const { file, line, citation } of findings) {
|
|
162
|
+
const text = citation === undefined
|
|
163
|
+
? 'names a machine path'
|
|
164
|
+
: ignored.has(probeOf(citation)) && !isCovered(citation, tracked)
|
|
165
|
+
? `cites ignored path ${citation.path}`
|
|
166
|
+
: undefined;
|
|
167
|
+
if (text === undefined)
|
|
168
|
+
continue;
|
|
169
|
+
const key = `${file}:${line} ${text}`;
|
|
170
|
+
if (seen.has(key))
|
|
171
|
+
continue;
|
|
172
|
+
seen.add(key);
|
|
173
|
+
lines.push(`sweep: ${key}`);
|
|
174
|
+
}
|
|
175
|
+
return lines;
|
|
176
|
+
}
|
|
70
177
|
function runReport() {
|
|
71
178
|
for (const directory of [...LAUNCH_DIRECTORIES, ...listCampaignDirectories()]) {
|
|
72
179
|
const line = reportDirectory(directory);
|
|
@@ -76,13 +183,16 @@ function runReport() {
|
|
|
76
183
|
const files = reportCampaignFiles();
|
|
77
184
|
if (files !== undefined)
|
|
78
185
|
console.log(files);
|
|
186
|
+
for (const line of reportCitations())
|
|
187
|
+
console.log(line);
|
|
79
188
|
return 0;
|
|
80
189
|
}
|
|
81
190
|
function runSweep(thresholdMinutes) {
|
|
82
191
|
const now = Date.now();
|
|
83
192
|
const present = LAUNCH_DIRECTORIES.filter((directory) => existsSync(directory));
|
|
193
|
+
const tracked = new Set(listTracked(LAUNCH_DIRECTORIES));
|
|
84
194
|
const files = present.flatMap((directory) => listFiles(directory, NOTHING_SKIPPED));
|
|
85
|
-
if (files.some((file) => now - file.modified < RECENT_MS)) {
|
|
195
|
+
if (files.some((file) => !tracked.has(file.path) && now - file.modified < RECENT_MS)) {
|
|
86
196
|
console.error('sweep: a launch file changed within the last 2 minutes; refusing to delete');
|
|
87
197
|
return 2;
|
|
88
198
|
}
|
|
@@ -90,6 +200,10 @@ function runSweep(thresholdMinutes) {
|
|
|
90
200
|
for (const file of files) {
|
|
91
201
|
if (file.modified > cutoff)
|
|
92
202
|
continue;
|
|
203
|
+
if (tracked.has(file.path)) {
|
|
204
|
+
console.log(`sweep: kept tracked ${file.path}`);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
93
207
|
rmSync(file.path, { force: true });
|
|
94
208
|
console.log(`sweep: deleted ${file.path}`);
|
|
95
209
|
}
|
|
@@ -111,11 +225,17 @@ function runUnit(unit, remove) {
|
|
|
111
225
|
return 64;
|
|
112
226
|
}
|
|
113
227
|
const files = listUnitFiles(unit);
|
|
114
|
-
|
|
228
|
+
const tracked = new Set(listTracked(LAUNCH_DIRECTORIES));
|
|
229
|
+
if (remove &&
|
|
230
|
+
files.some((file) => !tracked.has(file.path) && Date.now() - file.modified < RECENT_MS)) {
|
|
115
231
|
console.error(`sweep: a file of ${unit} changed within the last 2 minutes; refusing to delete`);
|
|
116
232
|
return 2;
|
|
117
233
|
}
|
|
118
234
|
for (const file of files) {
|
|
235
|
+
if (tracked.has(file.path)) {
|
|
236
|
+
console.log(remove ? `sweep: kept tracked ${file.path}` : `sweep: found ${file.path} (tracked)`);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
119
239
|
if (remove)
|
|
120
240
|
rmSync(file.path, { force: true });
|
|
121
241
|
console.log(`sweep: ${remove ? 'deleted' : 'found'} ${file.path}`);
|
package/dist/bin/main.js
CHANGED
|
@@ -10,7 +10,7 @@ import { normalize } from "node:path";
|
|
|
10
10
|
import { parseArgs } from "node:util";
|
|
11
11
|
var package_default = {
|
|
12
12
|
name: "@orkestrel/scaffold",
|
|
13
|
-
version: "0.0.
|
|
13
|
+
version: "0.0.108",
|
|
14
14
|
description: "Scaffold workspaces with commands: new, audit, repair, catalog, and overwrite.",
|
|
15
15
|
keywords: [
|
|
16
16
|
"audit",
|