@deftai/directive-content 0.87.0 → 0.88.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/.agents/skills/deft-directive-swarm/SKILL.md +1 -996
- package/QUICK-START.md +4 -0
- package/Taskfile.yml +1 -0
- package/UPGRADING.md +24 -2
- package/coding/coding.md +5 -1
- package/commands.md +1 -1
- package/contracts/runtime-authority.md +34 -8
- package/docs/getting-started.md +4 -0
- package/docs/no-deft-directive.md +87 -0
- package/docs/openclaw-agent-host.md +34 -1
- package/docs/product-signal.md +2 -0
- package/docs/writing-ste100.md +53 -0
- package/glossary.md +37 -39
- package/package.json +2 -1
- package/packs/rules/rules-pack-0.1.json +25 -1
- package/packs/skills/skills-pack-0.1.json +24 -24
- package/packs/strategies/strategies-pack-0.1.json +4 -4
- package/skills/deft-directive-setup/SKILL.md +35 -8
- package/skills/deft-directive-swarm/SKILL.md +81 -978
- package/skills/deft-directive-swarm/references/core-ops.md +144 -0
- package/skills/deft-directive-swarm/references/core-phase-0.md +200 -0
- package/skills/deft-directive-swarm/references/core-phase-1-2.md +73 -0
- package/skills/deft-directive-swarm/references/core-phase-3.md +145 -0
- package/skills/deft-directive-swarm/references/core-phase-4.md +71 -0
- package/skills/deft-directive-swarm/references/core-phase-5-6.md +317 -0
- package/skills/deft-directive-swarm/references/host-cursor.md +25 -0
- package/skills/deft-directive-swarm/references/host-generic.md +27 -0
- package/skills/deft-directive-swarm/references/host-grok-build.md +37 -0
- package/skills/deft-directive-swarm/references/host-openclaw.md +70 -0
- package/skills/deft-directive-swarm/references/host-warp.md +37 -0
- package/skills/deft-directive-write-skill/SKILL.md +17 -0
- package/strategies/artifact-guards.md +24 -14
- package/strategies/discuss.md +40 -1
- package/strategies/interview.md +103 -30
- package/strategies/probe.md +27 -1
- package/tasks/engine-invoke.cjs +69 -13
- package/tasks/engine-invoke.test.cjs +188 -0
- package/tasks/verify.yml +7 -0
- package/templates/agent-prompt-preamble.md +6 -0
- package/vbrief/vbrief.md +4 -2
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const assert = require("node:assert/strict");
|
|
5
|
+
const { describe, it } = require("node:test");
|
|
6
|
+
const {
|
|
7
|
+
shellSplit,
|
|
8
|
+
quoteWin32Arg,
|
|
9
|
+
buildSpawnPlan,
|
|
10
|
+
WIN32_CMD_METACHAR_RE,
|
|
11
|
+
} = require("./engine-invoke.cjs");
|
|
12
|
+
|
|
13
|
+
const WIN32 = { platform: "win32", nodePath: "/node" };
|
|
14
|
+
const POSIX = { platform: "linux", nodePath: "/node" };
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Split a `cmd.exe`-quoted command line into top-level tokens, honouring
|
|
18
|
+
* double-quote grouping and the `""` escaped-quote convention. Used to assert
|
|
19
|
+
* that a metacharacter-bearing arg survives as exactly one argv token and is
|
|
20
|
+
* never seen by cmd.exe as a command separator.
|
|
21
|
+
* @param {string} line
|
|
22
|
+
*/
|
|
23
|
+
function splitCmdTokens(line) {
|
|
24
|
+
const tokens = [];
|
|
25
|
+
let cur = "";
|
|
26
|
+
let inQuote = false;
|
|
27
|
+
let started = false;
|
|
28
|
+
for (let i = 0; i < line.length; i++) {
|
|
29
|
+
const c = line[i];
|
|
30
|
+
if (inQuote) {
|
|
31
|
+
if (c === '"') {
|
|
32
|
+
if (line[i + 1] === '"') {
|
|
33
|
+
cur += '"';
|
|
34
|
+
i++;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
inQuote = false;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
cur += c;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (c === '"') {
|
|
44
|
+
inQuote = true;
|
|
45
|
+
started = true;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (c === " ") {
|
|
49
|
+
if (started) {
|
|
50
|
+
tokens.push(cur);
|
|
51
|
+
cur = "";
|
|
52
|
+
started = false;
|
|
53
|
+
}
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
started = true;
|
|
57
|
+
cur += c;
|
|
58
|
+
}
|
|
59
|
+
if (started) {
|
|
60
|
+
tokens.push(cur);
|
|
61
|
+
}
|
|
62
|
+
return tokens;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
describe("shellSplit", () => {
|
|
66
|
+
it("keeps quoted free-text (apostrophes, spaces, metachars) as one token", () => {
|
|
67
|
+
assert.deepEqual(shellSplit(`release --summary "It's a & test"`), [
|
|
68
|
+
"release",
|
|
69
|
+
"--summary",
|
|
70
|
+
"It's a & test",
|
|
71
|
+
]);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("quoteWin32Arg", () => {
|
|
76
|
+
it("passes safe tokens through unquoted", () => {
|
|
77
|
+
assert.equal(quoteWin32Arg("release"), "release");
|
|
78
|
+
assert.equal(quoteWin32Arg("--summary=fixed"), "--summary=fixed");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("double-quotes whitespace, quotes, and cmd.exe metacharacters", () => {
|
|
82
|
+
assert.equal(quoteWin32Arg("a b"), '"a b"');
|
|
83
|
+
assert.equal(quoteWin32Arg("a&b"), '"a&b"');
|
|
84
|
+
assert.equal(quoteWin32Arg("a|b"), '"a|b"');
|
|
85
|
+
assert.equal(quoteWin32Arg("a>b"), '"a>b"');
|
|
86
|
+
assert.equal(quoteWin32Arg("a<b"), '"a<b"');
|
|
87
|
+
assert.equal(quoteWin32Arg("(a)"), '"(a)"');
|
|
88
|
+
assert.equal(quoteWin32Arg("%PATH%"), '"%PATH%"');
|
|
89
|
+
assert.equal(quoteWin32Arg("a^b"), '"a^b"');
|
|
90
|
+
assert.equal(quoteWin32Arg("a!b"), '"a!b"');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("escapes embedded double quotes by doubling", () => {
|
|
94
|
+
assert.equal(quoteWin32Arg('a"b'), '"a""b"');
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("regex flags every cmd.exe separator", () => {
|
|
98
|
+
for (const meta of [" ", '"', "&", "|", "<", ">", "^", "(", ")", "%", "!"]) {
|
|
99
|
+
assert.ok(WIN32_CMD_METACHAR_RE.test(`x${meta}y`), meta);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe("buildSpawnPlan — win32 global (subprocess-scm-01 / #2911)", () => {
|
|
105
|
+
it("never uses shell:true and routes through cmd.exe /d /s /c", () => {
|
|
106
|
+
const plan = buildSpawnPlan("global", "deft", ["release"], WIN32);
|
|
107
|
+
assert.equal(plan.shell, false);
|
|
108
|
+
assert.equal(plan.command, "cmd.exe");
|
|
109
|
+
assert.deepEqual(plan.args.slice(0, 3), ["/d", "/s", "/c"]);
|
|
110
|
+
assert.equal(plan.args[3], "deft release");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("keeps injection-shaped free-text args as a single quoted token", () => {
|
|
114
|
+
const injections = [
|
|
115
|
+
"& calc.exe",
|
|
116
|
+
"&calc",
|
|
117
|
+
"| whoami",
|
|
118
|
+
"&& shutdown /s",
|
|
119
|
+
"; rm -rf /",
|
|
120
|
+
"$(reboot)",
|
|
121
|
+
"`reboot`",
|
|
122
|
+
"> C:\\pwn.txt",
|
|
123
|
+
"< C:\\secret",
|
|
124
|
+
"(malicious)",
|
|
125
|
+
"%USERPROFILE%",
|
|
126
|
+
"^escaped",
|
|
127
|
+
"!DELAYED!",
|
|
128
|
+
];
|
|
129
|
+
for (const evil of injections) {
|
|
130
|
+
const plan = buildSpawnPlan("global", "deft", ["release", "--summary", evil], WIN32);
|
|
131
|
+
assert.equal(plan.shell, false, evil);
|
|
132
|
+
const commandLine = plan.args[3];
|
|
133
|
+
const tokens = splitCmdTokens(commandLine);
|
|
134
|
+
// deft + release + --summary + evil == 4 top-level tokens, evil intact.
|
|
135
|
+
assert.deepEqual(tokens, ["deft", "release", "--summary", evil], `injection ${evil}`);
|
|
136
|
+
// Any cmd.exe metacharacter must be neutralised inside a quoted span so it
|
|
137
|
+
// can never act as a bare command separator (POSIX-only chars like the
|
|
138
|
+
// backtick are literal to cmd.exe and need no quoting).
|
|
139
|
+
if (WIN32_CMD_METACHAR_RE.test(evil)) {
|
|
140
|
+
assert.ok(commandLine.includes(`"${evil.replace(/"/g, '""')}"`), `quoted ${evil}`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it("routes end-to-end from a quoted DEFT_ENGINE_CMD string", () => {
|
|
146
|
+
const argv = shellSplit('release --summary "pwn & calc | whoami"');
|
|
147
|
+
const plan = buildSpawnPlan("global", "directive", argv, WIN32);
|
|
148
|
+
assert.equal(plan.shell, false);
|
|
149
|
+
assert.deepEqual(splitCmdTokens(plan.args[3]), [
|
|
150
|
+
"directive",
|
|
151
|
+
"release",
|
|
152
|
+
"--summary",
|
|
153
|
+
"pwn & calc | whoami",
|
|
154
|
+
]);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("leaves safe args unquoted for readability", () => {
|
|
158
|
+
const plan = buildSpawnPlan("global", "deft", ["session:start", "--json"], WIN32);
|
|
159
|
+
assert.equal(plan.args[3], "deft session:start --json");
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe("buildSpawnPlan — other paths keep shell:false", () => {
|
|
164
|
+
it("win32 vendored spawns node directly (no cmd.exe, no shell)", () => {
|
|
165
|
+
const plan = buildSpawnPlan("vendored", "/bin.js", ["release", "a&b"], WIN32);
|
|
166
|
+
assert.equal(plan.shell, false);
|
|
167
|
+
assert.equal(plan.command, "/node");
|
|
168
|
+
assert.deepEqual(plan.args, ["/bin.js", "release", "a&b"]);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("posix global spawns the shim directly with shell:false", () => {
|
|
172
|
+
const plan = buildSpawnPlan("global", "deft", ["release", "a&b"], POSIX);
|
|
173
|
+
assert.equal(plan.shell, false);
|
|
174
|
+
assert.equal(plan.command, "deft");
|
|
175
|
+
assert.deepEqual(plan.args, ["release", "a&b"]);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("posix vendored spawns node with shell:false", () => {
|
|
179
|
+
const plan = buildSpawnPlan("vendored", "/bin.js", ["release"], POSIX);
|
|
180
|
+
assert.equal(plan.shell, false);
|
|
181
|
+
assert.equal(plan.command, "/node");
|
|
182
|
+
assert.deepEqual(plan.args, ["/bin.js", "release"]);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("returns null for unknown modes (caller exits 2)", () => {
|
|
186
|
+
assert.equal(buildSpawnPlan("bogus", "deft", ["release"], WIN32), null);
|
|
187
|
+
});
|
|
188
|
+
});
|
package/tasks/verify.yml
CHANGED
|
@@ -59,6 +59,13 @@ tasks:
|
|
|
59
59
|
vars:
|
|
60
60
|
ENGINE_CMD: 'verify-content-manifest --project-root "{{.DEFT_ROOT}}"'
|
|
61
61
|
|
|
62
|
+
license-sync:
|
|
63
|
+
desc: "Drift guard for root LICENSE ↔ content/LICENSE.md and published package.json license fields (#2902). Three-state exit (0 clean / 1 drift / 2 config). Framework-source only."
|
|
64
|
+
# Framework-source-only gate: reads THIS repo's LICENSE + package manifests.
|
|
65
|
+
# Plain node script (no engine build required).
|
|
66
|
+
cmds:
|
|
67
|
+
- node "{{.DEFT_ROOT}}/scripts/verify-license-sync.mjs" --project-root "{{.DEFT_ROOT}}"
|
|
68
|
+
|
|
62
69
|
skill-external-fetch-gate:
|
|
63
70
|
desc: "Verify shipped skills do not pair external fetch/follow-through with execute/install without Security context mitigation (#1936 / #1532)."
|
|
64
71
|
deps:
|
|
@@ -529,6 +529,12 @@ Every worker MUST send a final status message before exiting its tool loop, rega
|
|
|
529
529
|
|
|
530
530
|
⊗ Emit `DONE` from a `drive-to: merge-ready` worker while merge-ready is false — a false-terminal `DONE` pulls the cohort monitor into inline Greptile fixes and violates Gap D (#2843 monitor-as-implementer recurrence).
|
|
531
531
|
|
|
532
|
+
! **Thin DONE is not success (#2943):** A terminal message that lacks PR URL / merge evidence (no `PR #N`, no PR URL, no merge confirmation) is a **thin DONE** / failed-leaf signal for the parent monitor — re-dispatch or take over after ground truth. Prefer structured completion fields when the host supplies them (`prUrl`, `mergeStatus`, `emptyDiff`). Workers MUST NOT exit with mid-edit prose and call it `DONE` when the envelope required a PR or merge-ready outcome.
|
|
533
|
+
|
|
534
|
+
! **Parent tool-first after leaf completion (#2943):** When a parent / monitor receives a leaf completion event (`subagent_announce`, parent-push, or host completion notify), its **first response** MUST be a **tool-first** ground-truth batch (`gh` / `git` / worktree or file status) **or** a host **yield** (`sessions_yield` on OpenClaw, or equivalent). ⊗ Multi-sentence progress-only first response with zero tools / yield — the OpenClaw text-repetition hang class (#2943).
|
|
535
|
+
|
|
536
|
+
⊗ Treat thin DONE (no PR URL / merge evidence) as success (#2943).
|
|
537
|
+
|
|
532
538
|
Per-step acks during the run are noise. ONE start message, ONE final message; intermediate messages only on `BLOCKED` / `FAILED`. The final message lets the dispatcher distinguish a clean exit from a silent timeout when the lifecycle event arrives.
|
|
533
539
|
|
|
534
540
|
## 12. Session ritual + `task verify:cache-fresh` gates before `start_agent` (#1348 / #1127)
|
package/vbrief/vbrief.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# vBRIEF Usage in Deft
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **Public name (#2907):** The sole current work-state name is **xBRIEF** under `xbrief/` (`.xbrief.json`). **vBRIEF** / `vbrief/` / `.vbrief.json` in this document are **legacy / schema-lineage** names (this path still hosts core schemas). Prefer xbrief in product docs and new guidance. Authoritative rename/history: [UPGRADING.md — xBRIEF rename](../UPGRADING.md#xbrief-rename-2034--2110--2907). Migrate on-disk layouts with `deft migrate:xbrief`.
|
|
4
|
+
|
|
5
|
+
Canonical **schema and convention** reference for durable work-state files within Deft-managed projects (historical filename: vBRIEF).
|
|
4
6
|
|
|
5
7
|
Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
|
|
6
8
|
|
|
7
|
-
**⚠️ See also**: [
|
|
9
|
+
**⚠️ See also**: [glossary.md](../glossary.md) | [UPGRADING.md — xBRIEF rename](../UPGRADING.md#xbrief-rename-2034--2110--2907) | [context/working-memory.md](../context/working-memory.md) | [resilience/continue-here.md](../resilience/continue-here.md)
|
|
8
10
|
|
|
9
11
|
---
|
|
10
12
|
|