orchestrix-skills 0.7.0 → 0.8.0
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/bin/install.js +42 -4
- package/package.json +1 -1
- package/skills/orchestrate/SKILL.md +64 -9
package/bin/install.js
CHANGED
|
@@ -2,13 +2,18 @@
|
|
|
2
2
|
// orchestrix-skills installer — zero dependencies.
|
|
3
3
|
// Free path: copy skills into the runtime's skills dir + scaffold knowledge/.
|
|
4
4
|
// No MCP, no license. Premium (hosted orchestrator / KB / 建造中心) is a separate opt-in.
|
|
5
|
-
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
|
|
9
9
|
const PKG = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
10
10
|
const MANAGED_START = "<!-- orchestrix:start -->";
|
|
11
11
|
const MANAGED_END = "<!-- orchestrix:end -->";
|
|
12
|
+
// Written into the skills dir after every successful install. Two consumers:
|
|
13
|
+
// this installer (prunes skills it placed that the package no longer ships) and
|
|
14
|
+
// hosts that auto-upgrade projects (compare `version` against the npm dist-tag
|
|
15
|
+
// to decide whether to reinstall — no second version constant to maintain).
|
|
16
|
+
const STAMP = ".orchestrix-skills.json";
|
|
12
17
|
|
|
13
18
|
// Where each runtime auto-loads skills from (relative to the target project).
|
|
14
19
|
function adapter(name) {
|
|
@@ -54,9 +59,22 @@ function transformSkill(source, runtime) {
|
|
|
54
59
|
);
|
|
55
60
|
}
|
|
56
61
|
|
|
62
|
+
function packageVersion() {
|
|
63
|
+
return JSON.parse(readFileSync(join(PKG, "package.json"), "utf8")).version;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readStamp(target) {
|
|
67
|
+
try {
|
|
68
|
+
return JSON.parse(readFileSync(join(target, STAMP), "utf8"));
|
|
69
|
+
} catch {
|
|
70
|
+
return null; // absent, or written by a version that predates stamping
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
57
74
|
function installSkills(dir, runtimeName, runtime) {
|
|
58
75
|
const target = join(dir, runtime.skillsDir);
|
|
59
76
|
mkdirSync(target, { recursive: true });
|
|
77
|
+
const previous = readStamp(target);
|
|
60
78
|
const entries = readdirSync(join(PKG, "skills"), { withFileTypes: true });
|
|
61
79
|
for (const entry of entries) {
|
|
62
80
|
const source = join(PKG, "skills", entry.name);
|
|
@@ -74,7 +92,26 @@ function installSkills(dir, runtimeName, runtime) {
|
|
|
74
92
|
}
|
|
75
93
|
}
|
|
76
94
|
}
|
|
77
|
-
|
|
95
|
+
|
|
96
|
+
const names = entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
97
|
+
// Retire skills a PREVIOUS install of this package placed that it no longer
|
|
98
|
+
// ships. Only names recorded in our own stamp are candidates, so a skill the
|
|
99
|
+
// project or its host installed alongside ours is never touched.
|
|
100
|
+
let pruned = 0;
|
|
101
|
+
for (const name of previous?.skills ?? []) {
|
|
102
|
+
if (names.includes(name)) continue;
|
|
103
|
+
const stale = join(target, name);
|
|
104
|
+
if (!isDirectory(stale)) continue;
|
|
105
|
+
rmSync(stale, { recursive: true, force: true });
|
|
106
|
+
pruned += 1;
|
|
107
|
+
}
|
|
108
|
+
// Stamp LAST: a crash mid-copy leaves the older stamp in place, so the next
|
|
109
|
+
// run still sees a mismatch and reinstalls rather than declaring itself current.
|
|
110
|
+
writeFileSync(
|
|
111
|
+
join(target, STAMP),
|
|
112
|
+
`${JSON.stringify({ version: packageVersion(), ide: runtimeName, skills: names }, null, 2)}\n`,
|
|
113
|
+
);
|
|
114
|
+
return { count: names.length, pruned };
|
|
78
115
|
}
|
|
79
116
|
|
|
80
117
|
function installRuntimeGuidance(dir, runtimeName) {
|
|
@@ -142,8 +179,9 @@ function install() {
|
|
|
142
179
|
}
|
|
143
180
|
|
|
144
181
|
// 1. Skills (capabilities) — always refreshed.
|
|
145
|
-
const count = installSkills(dir, ide, runtime);
|
|
146
|
-
console.log(`✓ ${count} skills → ${runtime.skillsDir}
|
|
182
|
+
const { count, pruned } = installSkills(dir, ide, runtime);
|
|
183
|
+
console.log(`✓ ${count} skills → ${runtime.skillsDir}/ (v${packageVersion()})`);
|
|
184
|
+
if (pruned > 0) console.log(`✓ ${pruned} retired skill(s) removed`);
|
|
147
185
|
installRuntimeGuidance(dir, ide);
|
|
148
186
|
|
|
149
187
|
// 2. Knowledge (the brain) — scaffold only if absent; never clobber the user's brain.
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ description: Use when a goal must be delivered end-to-end by composing skills, w
|
|
|
4
4
|
license: MIT
|
|
5
5
|
allowed-tools: [Read, Write, Edit, Bash, Grep, Glob, Task]
|
|
6
6
|
metadata:
|
|
7
|
-
version:
|
|
7
|
+
version: 6
|
|
8
8
|
requires:
|
|
9
9
|
capabilities: [filesystem.read, filesystem.write, shell.execute, "agent.spawn?"]
|
|
10
10
|
contract:
|
|
@@ -36,9 +36,10 @@ no step above intent.
|
|
|
36
36
|
1. **Bind intent.** Read the human's goal and constraints. This is the only
|
|
37
37
|
place intent enters. Then run the first-run preflight (below) before any
|
|
38
38
|
wiring.
|
|
39
|
-
2. **Select.** Read the skill registry
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
2. **Select.** Read the skill registry (the `description:` line of every
|
|
40
|
+
SKILL.md in the runtime's skills directory). Pick skills by that
|
|
41
|
+
when-to-use description. Load a skill's full `contract` only when it is a
|
|
42
|
+
candidate — never load every contract at once.
|
|
42
43
|
3. **Wire (emergent, not hardcoded).** Build the path by matching one skill's
|
|
43
44
|
`outputs` to the next skill's `inputs`. Skills do not know each other; only
|
|
44
45
|
you do. Do not assume a fixed pipeline — wire what this intent needs.
|
|
@@ -68,8 +69,12 @@ no step above intent.
|
|
|
68
69
|
6. **Accept (gate).** Apply the rule below. Then continue — do not pause to ask
|
|
69
70
|
"should I keep going?" mid-run.
|
|
70
71
|
7. **Repeat** 3–6 until the intent is fulfilled.
|
|
71
|
-
8. **Final acceptance.**
|
|
72
|
-
|
|
72
|
+
8. **Final acceptance.** FIRST re-read the intent from the `run_start` ledger
|
|
73
|
+
line and check the assembled result against IT — every step passing its own
|
|
74
|
+
verify does not prove the composition serves the intent (steps can each be
|
|
75
|
+
right while the whole drifts). Then present the batched deferred accepts and
|
|
76
|
+
a final review to the human, once. Apply corrections (see Metabolism), then
|
|
77
|
+
deliver.
|
|
73
78
|
|
|
74
79
|
## Namespace resolution (`core-config.yaml`)
|
|
75
80
|
|
|
@@ -114,6 +119,27 @@ resolved paths):
|
|
|
114
119
|
|
|
115
120
|
Both checks are per-run and idempotent: a populated brain makes them no-ops.
|
|
116
121
|
|
|
122
|
+
## Hard wiring rules (what emergence cannot reach)
|
|
123
|
+
|
|
124
|
+
Output→input matching wires most of the graph. Two skills it structurally
|
|
125
|
+
CANNOT reach — wire these by rule, not by match:
|
|
126
|
+
|
|
127
|
+
1. **`smoke-test` is the acceptance floor for runnable apps.** Nothing in the
|
|
128
|
+
graph outputs its `flows` or `run_instructions`, so no output→input match
|
|
129
|
+
will ever select it. If the deliverable is a runnable app or service and
|
|
130
|
+
this run changed it, wire `smoke-test` before final acceptance and derive
|
|
131
|
+
its inputs yourself: `flows` from the story's acceptance criteria (or from
|
|
132
|
+
the intent, when there is no story), `run_instructions` from `registry/app`
|
|
133
|
+
(or the project's own manifest). `run-tests` proves functions; `smoke-test`
|
|
134
|
+
proves the product — green unit tests are not this evidence. A `failed` or
|
|
135
|
+
`untested` verdict is a real result: carry it into final acceptance
|
|
136
|
+
verbatim, never round it up to passed.
|
|
137
|
+
2. **`design-system` comes before `design-ui`.** `design-ui` READS
|
|
138
|
+
`taste/design-system` — it never produces it. If UI work is wired and the
|
|
139
|
+
resolved `taste/design-system` namespace is empty, wire `design-system`
|
|
140
|
+
first; otherwise `design-ui` dresses a project that has a brand in generic
|
|
141
|
+
defaults.
|
|
142
|
+
|
|
117
143
|
## Accept gate
|
|
118
144
|
|
|
119
145
|
| Skill's `accept.timing` | Skill's `authority` | Action |
|
|
@@ -141,8 +167,9 @@ aimed at a symptom re-rolls the dice.
|
|
|
141
167
|
STOP the run — do not burn a 4th attempt. Write a `gate` event to the ledger
|
|
142
168
|
(`{"e":"gate","kind":"rework_exhausted","question":"step <n> (<skill>) failed 3
|
|
143
169
|
attempts: <one-line why>"}`), summarize the three failures for the human, and
|
|
144
|
-
|
|
145
|
-
human
|
|
170
|
+
stop for their decision. A step that cannot pass its own verify after three
|
|
171
|
+
tries needs a human (wrong approach, wrong spec, or wrong verify), not more
|
|
172
|
+
tokens.
|
|
146
173
|
|
|
147
174
|
## Metabolism — governed writeback
|
|
148
175
|
|
|
@@ -181,13 +208,18 @@ platform renders it as live progress). It is append-only JSONL: one JSON event
|
|
|
181
208
|
per line, appended with `Bash` (`echo '<json>' >> .orchestrate/ledger.jsonl`).
|
|
182
209
|
Never rewrite or delete lines. Timestamps: `date -u +%FT%TZ`.
|
|
183
210
|
|
|
211
|
+
**Quoting hazard:** the single-quoted `echo` breaks on `'` inside the JSON —
|
|
212
|
+
and a mangled line corrupts the run's only durable memory. Keep every free-text
|
|
213
|
+
field (`intent`, `question`, `title`) to one line with no single quotes:
|
|
214
|
+
rephrase (`don't` → `do not`) before writing, never fight the shell escaping.
|
|
215
|
+
|
|
184
216
|
Events and when to write them:
|
|
185
217
|
|
|
186
218
|
| Event | When | Shape |
|
|
187
219
|
| ----- | ---- | ----- |
|
|
188
220
|
| `run_start` | right after binding intent | `{"e":"run_start","run":"r-<yyyymmdd>-<slug>","intent":"...","ts":"..."}` |
|
|
189
221
|
| `plan` | after wiring the graph, and EVERY time the graph changes | `{"e":"plan","run":"...","steps":[{"n":1,"skill":"research","title":"..."}, …]}` — full current plan; latest `plan` line wins; steps may be added, never removed |
|
|
190
|
-
| `step` | immediately BEFORE each dispatch, and again after its verify | `{"e":"step","run":"...","n":3,"skill":"implement","status":"dispatched\|done\|failed","attempt":1,"evidence":"<file or one-line result>","ts":"..."}` — rework = same `n`, next `attempt` |
|
|
222
|
+
| `step` | immediately BEFORE each dispatch, and again after its verify | `{"e":"step","run":"...","n":3,"skill":"implement","status":"dispatched\|done\|failed\|skipped","attempt":1,"evidence":"<file or one-line result>","ts":"..."}` — rework = same `n`, next `attempt`; a step a replan made obsolete gets `skipped` with the reason in `evidence` (plan lines are never removed, so this is how an obsolete step closes) |
|
|
191
223
|
| `gate` | when stopping at a human gate | `{"e":"gate","run":"...","kind":"inline_accept","question":"...","ts":"..."}` |
|
|
192
224
|
| `run_end` | at delivery or abandonment | `{"e":"run_end","run":"...","result":"delivered\|paused\|abandoned","ts":"..."}` |
|
|
193
225
|
|
|
@@ -196,6 +228,24 @@ step is required and should be the step's verify log path
|
|
|
196
228
|
(`.orchestrate/verify/step-<n>-attempt-<k>.log`); a `done` with no evidence is
|
|
197
229
|
a false claim.
|
|
198
230
|
|
|
231
|
+
## Resume — cold re-entry (deterministic, not from memory)
|
|
232
|
+
|
|
233
|
+
Whenever you enter with an existing ledger — after compaction, an interrupted
|
|
234
|
+
session, or a wake-up — do NOT continue from what you remember. Replay:
|
|
235
|
+
|
|
236
|
+
1. Read `.orchestrate/ledger.jsonl`. The active run is the last `run_start`
|
|
237
|
+
with no matching `run_end`. Its `intent` line — not your recollection — is
|
|
238
|
+
what you are delivering. No active run → this is a fresh start.
|
|
239
|
+
2. Rebuild state from events alone: the latest `plan` wins; `done` and
|
|
240
|
+
`skipped` steps are closed; prior `attempt` values count toward each step's
|
|
241
|
+
cap of 3.
|
|
242
|
+
3. **A dangling `dispatched`** (no `done`/`failed`/`skipped` after it) means
|
|
243
|
+
that attempt was cut off mid-flight. Trust it in NEITHER direction: run that
|
|
244
|
+
step's verify command now. Pass → append its `done` with the evidence.
|
|
245
|
+
Fail → re-dispatch as the next attempt.
|
|
246
|
+
4. Continue the loop from the first open step. If the run was stopped at a
|
|
247
|
+
`gate`, re-ask that gate's question — never assume it was answered.
|
|
248
|
+
|
|
199
249
|
## Context discipline (stay lean)
|
|
200
250
|
|
|
201
251
|
- **Files, not paste.** Move artifacts between steps as files. Never paste a
|
|
@@ -211,6 +261,8 @@ a false claim.
|
|
|
211
261
|
- Hardcoding a fixed skill order instead of wiring outputs→inputs
|
|
212
262
|
- Pasting a step's full output into your context instead of handing a file
|
|
213
263
|
- Re-dispatching a step the ledger already marks done
|
|
264
|
+
- Resuming from memory instead of replaying the ledger — or trusting a
|
|
265
|
+
dangling `dispatched` in either direction without running its verify
|
|
214
266
|
- Dispatching a step without first writing its `dispatched` ledger line
|
|
215
267
|
- Ending a run without a `run_end` ledger line
|
|
216
268
|
- Marking a step done on the subagent's say-so, without your own verify command
|
|
@@ -219,4 +271,7 @@ a false claim.
|
|
|
219
271
|
- Appending to `taste/*` without reading it first (duplicate/contradiction risk)
|
|
220
272
|
- Dispatching a design/build skill in an existing codebase while `registry/*`
|
|
221
273
|
is empty (first-run preflight skipped)
|
|
274
|
+
- Delivering a runnable app this run changed with no `smoke-test` verdicts
|
|
275
|
+
(unit tests are not that evidence)
|
|
276
|
+
- Dispatching `design-ui` while the resolved `taste/design-system` is empty
|
|
222
277
|
- Marking the run complete without every step's `verify` evidence
|