@deftai/directive-content 0.82.0 → 0.84.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/Taskfile.yml +7 -0
- package/commands.md +16 -2
- package/docs/product-signal.md +6 -2
- package/docs/task-cache.md +37 -0
- package/package.json +1 -1
- package/packs/skills/skills-pack-0.1.json +4 -4
- package/skills/deft-directive-build/SKILL.md +30 -8
- package/skills/deft-directive-pre-pr/SKILL.md +13 -3
- package/skills/deft-directive-product-signal/SKILL.md +3 -1
- package/skills/deft-directive-swarm/SKILL.md +20 -7
- package/tasks/engine-pm-run.cjs +269 -0
- package/tasks/engine-pm-run.test.cjs +201 -0
- package/tasks/engine.yml +2 -135
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const assert = require("node:assert/strict");
|
|
5
|
+
const fs = require("node:fs");
|
|
6
|
+
const os = require("node:os");
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
const { describe, it } = require("node:test");
|
|
9
|
+
const {
|
|
10
|
+
buildDispatchSteps,
|
|
11
|
+
executeAllowlisted,
|
|
12
|
+
hasCmd,
|
|
13
|
+
parsePnpmPin,
|
|
14
|
+
runPackageScript,
|
|
15
|
+
validateScriptName,
|
|
16
|
+
} = require("./engine-pm-run.cjs");
|
|
17
|
+
|
|
18
|
+
describe("parsePnpmPin", () => {
|
|
19
|
+
it("accepts stable, prerelease, and build-metadata pins", () => {
|
|
20
|
+
for (const pin of ["pnpm@11.8.0", "pnpm@1.0.0-alpha.1", "pnpm@1.0.0+build.1"]) {
|
|
21
|
+
const result = parsePnpmPin(pin);
|
|
22
|
+
assert.equal(result.ok, true);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("accepts missing/empty pins for unpinned fallback", () => {
|
|
27
|
+
assert.deepEqual(parsePnpmPin(undefined), { ok: true, semver: null, pin: null });
|
|
28
|
+
assert.deepEqual(parsePnpmPin(""), { ok: true, semver: null, pin: null });
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("rejects shell metacharacters and malformed pins before spawn", () => {
|
|
32
|
+
for (const pin of [
|
|
33
|
+
"pnpm@9.0.0; echo pwned",
|
|
34
|
+
"pnpm@9.0.0 & echo pwned",
|
|
35
|
+
"pnpm@^1.0.0",
|
|
36
|
+
"npm@1.0.0",
|
|
37
|
+
"pnpm@1.0.0 extra",
|
|
38
|
+
" pnpm@1.0.0",
|
|
39
|
+
"pnpm@1.0.0 ",
|
|
40
|
+
"pnpm@1.0.0\n",
|
|
41
|
+
]) {
|
|
42
|
+
const result = parsePnpmPin(pin);
|
|
43
|
+
assert.equal(result.ok, false, pin);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("validateScriptName", () => {
|
|
49
|
+
it("accepts only declared script keys with safe names", () => {
|
|
50
|
+
const scripts = { build: "tsc", "test:unit": "vitest" };
|
|
51
|
+
assert.equal(validateScriptName("build", scripts), true);
|
|
52
|
+
assert.equal(validateScriptName("test:unit", scripts), true);
|
|
53
|
+
assert.equal(validateScriptName("missing", scripts), false);
|
|
54
|
+
assert.equal(validateScriptName("build;rm", scripts), false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("buildDispatchSteps", () => {
|
|
59
|
+
it("preserves installed pnpm -> pinned corepack -> unpinned corepack order", () => {
|
|
60
|
+
const steps = buildDispatchSteps({
|
|
61
|
+
hasPnpm: true,
|
|
62
|
+
hasCorepack: true,
|
|
63
|
+
semver: "11.8.0",
|
|
64
|
+
script: "build",
|
|
65
|
+
});
|
|
66
|
+
assert.deepEqual(steps, [
|
|
67
|
+
{ cmd: "pnpm", args: ["run", "build"] },
|
|
68
|
+
{ cmd: "corepack", args: ["pnpm@11.8.0", "run", "build"] },
|
|
69
|
+
{ cmd: "corepack", args: ["pnpm", "run", "build"] },
|
|
70
|
+
]);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("runPackageScript security", () => {
|
|
75
|
+
function mkFixture(/** @type {Record<string, unknown>} */ pkgExtra) {
|
|
76
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "deft-engine-pm-run-"));
|
|
77
|
+
fs.writeFileSync(path.join(dir, "package.json"), JSON.stringify(pkgExtra, null, 2), "utf8");
|
|
78
|
+
return dir;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
it("rejects malicious pins before any execution call", () => {
|
|
82
|
+
const dir = mkFixture({
|
|
83
|
+
packageManager: "pnpm@9.0.0; echo pwned",
|
|
84
|
+
scripts: { build: "node -e \"\"" },
|
|
85
|
+
});
|
|
86
|
+
const sentinel = path.join(dir, "sentinel.txt");
|
|
87
|
+
let execCalls = 0;
|
|
88
|
+
const code = runPackageScript(dir, "build", {
|
|
89
|
+
execFileSync() {
|
|
90
|
+
execCalls += 1;
|
|
91
|
+
fs.writeFileSync(sentinel, "pwned", "utf8");
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
assert.equal(code, 3);
|
|
95
|
+
assert.equal(execCalls, 0);
|
|
96
|
+
assert.equal(fs.existsSync(sentinel), false);
|
|
97
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("records exact argv on execution without shell:true", () => {
|
|
101
|
+
const dir = mkFixture({
|
|
102
|
+
packageManager: "pnpm@11.8.0",
|
|
103
|
+
scripts: { build: "tsc" },
|
|
104
|
+
});
|
|
105
|
+
/** @type {Array<{ cmd: string, args: string[], shell?: boolean, stdio?: string }>} */
|
|
106
|
+
const calls = [];
|
|
107
|
+
const code = runPackageScript(dir, "build", {
|
|
108
|
+
execFileSync(cmd, args, opts) {
|
|
109
|
+
calls.push({ cmd, args: [...args], shell: opts?.shell, stdio: opts?.stdio });
|
|
110
|
+
if (opts?.stdio === "inherit") {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
assert.equal(code, 0);
|
|
116
|
+
const execCalls = calls.filter((c) => c.stdio === "inherit");
|
|
117
|
+
assert.equal(execCalls.length, 1);
|
|
118
|
+
assert.equal(execCalls[0].shell, false);
|
|
119
|
+
assert.equal(execCalls[0].stdio, "inherit");
|
|
120
|
+
if (process.platform === "win32") {
|
|
121
|
+
assert.equal(execCalls[0].cmd, "cmd.exe");
|
|
122
|
+
assert.deepEqual(execCalls[0].args.slice(0, 3), ["/d", "/s", "/c"]);
|
|
123
|
+
assert.match(execCalls[0].args[3], /pnpm.*run.*build/);
|
|
124
|
+
} else {
|
|
125
|
+
assert.deepEqual(execCalls[0], {
|
|
126
|
+
cmd: "pnpm",
|
|
127
|
+
args: ["run", "build"],
|
|
128
|
+
shell: false,
|
|
129
|
+
stdio: "inherit",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it("allows probe hasCmd to use shell:true separately from execution", () => {
|
|
136
|
+
/** @type {Array<{ shell?: boolean, stdio?: string }>} */
|
|
137
|
+
const calls = [];
|
|
138
|
+
let probeAttempts = 0;
|
|
139
|
+
const execFn = (/** @type {string} */ _cmd, /** @type {string[]} */ _args, /** @type {{ shell?: boolean, stdio?: string }} */ opts) => {
|
|
140
|
+
if (opts?.stdio === "ignore") {
|
|
141
|
+
probeAttempts += 1;
|
|
142
|
+
if (probeAttempts === 1 && !opts.shell) {
|
|
143
|
+
throw new Error("probe without shell failed");
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
calls.push({ shell: opts?.shell, stdio: opts?.stdio });
|
|
147
|
+
};
|
|
148
|
+
assert.equal(hasCmd(execFn, "pnpm"), true);
|
|
149
|
+
assert.ok(calls.some((c) => c.shell === true && c.stdio === "ignore"));
|
|
150
|
+
const execCalls = calls.filter((c) => c.stdio === "inherit");
|
|
151
|
+
assert.equal(execCalls.length, 0);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("rejects invalid package.json before probing PATH", () => {
|
|
155
|
+
const dir = mkFixture({ packageManager: "pnpm@11.8.0", scripts: { build: "tsc" } });
|
|
156
|
+
fs.writeFileSync(path.join(dir, "package.json"), "null", "utf8");
|
|
157
|
+
let execCalls = 0;
|
|
158
|
+
const code = runPackageScript(dir, "build", {
|
|
159
|
+
execFileSync() {
|
|
160
|
+
execCalls += 1;
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
assert.equal(code, 2);
|
|
164
|
+
assert.equal(execCalls, 0);
|
|
165
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("rejects unknown scripts before probing PATH", () => {
|
|
169
|
+
const dir = mkFixture({ packageManager: "pnpm@11.8.0", scripts: { build: "tsc" } });
|
|
170
|
+
let execCalls = 0;
|
|
171
|
+
const code = runPackageScript(dir, "lint", {
|
|
172
|
+
execFileSync() {
|
|
173
|
+
execCalls += 1;
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
assert.equal(code, 2);
|
|
177
|
+
assert.equal(execCalls, 0);
|
|
178
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it("uses cmd.exe on win32 without shell:true on the Node spawn", () => {
|
|
182
|
+
if (process.platform !== "win32") {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
/** @type {{ cmd?: string, args?: string[], shell?: boolean } | null} */
|
|
186
|
+
let recorded = null;
|
|
187
|
+
executeAllowlisted(
|
|
188
|
+
(cmd, args, opts) => {
|
|
189
|
+
recorded = { cmd, args: [...args], shell: opts?.shell };
|
|
190
|
+
},
|
|
191
|
+
"pnpm",
|
|
192
|
+
["run", "build"],
|
|
193
|
+
{ cwd: process.cwd() },
|
|
194
|
+
);
|
|
195
|
+
assert.ok(recorded);
|
|
196
|
+
assert.equal(recorded.cmd, "cmd.exe");
|
|
197
|
+
assert.equal(recorded.args[0], "/d");
|
|
198
|
+
assert.equal(recorded.shell, false);
|
|
199
|
+
assert.match(recorded.args[3], /^pnpm run build$|^"pnpm" "run" "build"$/);
|
|
200
|
+
});
|
|
201
|
+
});
|
package/tasks/engine.yml
CHANGED
|
@@ -20,71 +20,7 @@ tasks:
|
|
|
20
20
|
cmds:
|
|
21
21
|
- |
|
|
22
22
|
set -eu
|
|
23
|
-
node -
|
|
24
|
-
const {execFileSync}=require('child_process');
|
|
25
|
-
const fs=require('fs');
|
|
26
|
-
const root=process.argv[1];
|
|
27
|
-
const script=process.argv[2];
|
|
28
|
-
const pkgPath=root+'/package.json';
|
|
29
|
-
if(!fs.existsSync(pkgPath)){
|
|
30
|
-
console.error('deft: package.json missing at '+root);
|
|
31
|
-
process.exit(2);
|
|
32
|
-
}
|
|
33
|
-
const pkg=JSON.parse(fs.readFileSync(pkgPath,'utf8'));
|
|
34
|
-
// Cross-platform probe (#2415): do NOT use Unix `sh -c 'command -v'` —
|
|
35
|
-
// Windows native Task often has no `sh` on PATH, so Corepack.cmd was
|
|
36
|
-
// invisible after #2411. Prefer a direct spawn (POSIX / real binaries);
|
|
37
|
-
// fall back to shell:true so PATHEXT resolves .cmd/.exe on win32.
|
|
38
|
-
// windowsHide (#2563): CREATE_NO_WINDOW so Cursor Task shells do not
|
|
39
|
-
// flood visible cmd.exe/conhost windows on every probe/build.
|
|
40
|
-
const spawnOpts=(extra)=>({stdio:'ignore',windowsHide:true,...extra});
|
|
41
|
-
const hasCmd=(name)=>{
|
|
42
|
-
try{
|
|
43
|
-
execFileSync(name,['--version'],spawnOpts());
|
|
44
|
-
return true;
|
|
45
|
-
}catch{
|
|
46
|
-
try{
|
|
47
|
-
execFileSync(name,['--version'],spawnOpts({shell:true}));
|
|
48
|
-
return true;
|
|
49
|
-
}catch{
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
|
-
const run=(cmd,args)=>{
|
|
55
|
-
execFileSync(cmd,args,{cwd:root,stdio:'inherit',shell:true,windowsHide:true});
|
|
56
|
-
};
|
|
57
|
-
const trySteps=(steps)=>{
|
|
58
|
-
for(const [cmd,args] of steps){
|
|
59
|
-
try{
|
|
60
|
-
run(cmd,args);
|
|
61
|
-
process.exit(0);
|
|
62
|
-
}catch{
|
|
63
|
-
// fall through to Corepack / next resolver
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
const envPm=String(process.env.DEFT_PACKAGE_MANAGER||'').trim().toLowerCase();
|
|
68
|
-
if(envPm==='npm'){
|
|
69
|
-
run('npm',['run',script]);
|
|
70
|
-
process.exit(0);
|
|
71
|
-
}
|
|
72
|
-
const pin=String(pkg.packageManager||'').trim();
|
|
73
|
-
const match=pin.match(/^pnpm@(.+)$/);
|
|
74
|
-
const steps=[];
|
|
75
|
-
if(hasCmd('pnpm')) steps.push(['pnpm',['run',script]]);
|
|
76
|
-
if(hasCmd('corepack')&&match) steps.push(['corepack',['pnpm@'+match[1],'run',script]]);
|
|
77
|
-
if(hasCmd('corepack')) steps.push(['corepack',['pnpm','run',script]]);
|
|
78
|
-
trySteps(steps);
|
|
79
|
-
console.error('deft: neither pnpm nor corepack is available to run \"'+script+'\".');
|
|
80
|
-
if(pin){
|
|
81
|
-
console.error(' Enable Corepack for the pinned manager: corepack enable && corepack prepare '+pin+' --activate');
|
|
82
|
-
}else{
|
|
83
|
-
console.error(' Install pnpm or enable Corepack (see package.json#packageManager).');
|
|
84
|
-
}
|
|
85
|
-
console.error(' Or set DEFT_PACKAGE_MANAGER=npm for an explicit npm build path.');
|
|
86
|
-
process.exit(127);
|
|
87
|
-
" "{{.DEFT_ROOT}}" "{{.PM_SCRIPT}}"
|
|
23
|
+
node "{{.TASKFILE_DIR}}/engine-pm-run.cjs" "{{.DEFT_ROOT}}" "{{.PM_SCRIPT}}"
|
|
88
24
|
|
|
89
25
|
_ts-build:
|
|
90
26
|
internal: true
|
|
@@ -115,76 +51,7 @@ tasks:
|
|
|
115
51
|
if node "{{.TASKFILE_DIR}}/ts-build-fresh.cjs" "{{.DEFT_ROOT}}"; then
|
|
116
52
|
exit 0
|
|
117
53
|
fi
|
|
118
|
-
node -
|
|
119
|
-
const {execFileSync}=require('child_process');
|
|
120
|
-
const fs=require('fs');
|
|
121
|
-
const root=process.argv[1];
|
|
122
|
-
const script='build';
|
|
123
|
-
const pkgPath=root+'/package.json';
|
|
124
|
-
const pkg=JSON.parse(fs.readFileSync(pkgPath,'utf8'));
|
|
125
|
-
// Cross-platform probe (#2415): do NOT use Unix `sh -c 'command -v'` —
|
|
126
|
-
// Windows native Task often has no `sh` on PATH, so Corepack.cmd was
|
|
127
|
-
// invisible after #2411. Prefer a direct spawn (POSIX / real binaries);
|
|
128
|
-
// fall back to shell:true so PATHEXT resolves .cmd/.exe on win32.
|
|
129
|
-
// windowsHide (#2563): CREATE_NO_WINDOW so Cursor Task shells do not
|
|
130
|
-
// flood visible cmd.exe/conhost windows on every probe/build.
|
|
131
|
-
const spawnOpts=(extra)=>({stdio:'ignore',windowsHide:true,...extra});
|
|
132
|
-
const hasCmd=(name)=>{
|
|
133
|
-
try{
|
|
134
|
-
execFileSync(name,['--version'],spawnOpts());
|
|
135
|
-
return true;
|
|
136
|
-
}catch{
|
|
137
|
-
try{
|
|
138
|
-
execFileSync(name,['--version'],spawnOpts({shell:true}));
|
|
139
|
-
return true;
|
|
140
|
-
}catch{
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
const run=(cmd,args)=>{
|
|
146
|
-
execFileSync(cmd,args,{cwd:root,stdio:'inherit',shell:true,windowsHide:true});
|
|
147
|
-
};
|
|
148
|
-
const markWarm=()=>{
|
|
149
|
-
try{
|
|
150
|
-
const dist=root+'/packages/cli/dist';
|
|
151
|
-
fs.mkdirSync(dist,{recursive:true});
|
|
152
|
-
fs.writeFileSync(dist+'/.deft-ts-build-stamp',new Date().toISOString());
|
|
153
|
-
}catch{}
|
|
154
|
-
};
|
|
155
|
-
const trySteps=(steps)=>{
|
|
156
|
-
for(const [cmd,args] of steps){
|
|
157
|
-
try{
|
|
158
|
-
run(cmd,args);
|
|
159
|
-
markWarm();
|
|
160
|
-
process.exit(0);
|
|
161
|
-
}catch{
|
|
162
|
-
// fall through to Corepack / next resolver
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
const envPm=String(process.env.DEFT_PACKAGE_MANAGER||'').trim().toLowerCase();
|
|
167
|
-
if(envPm==='npm'){
|
|
168
|
-
run('npm',['run',script]);
|
|
169
|
-
markWarm();
|
|
170
|
-
process.exit(0);
|
|
171
|
-
}
|
|
172
|
-
const pin=String(pkg.packageManager||'').trim();
|
|
173
|
-
const match=pin.match(/^pnpm@(.+)$/);
|
|
174
|
-
const steps=[];
|
|
175
|
-
if(hasCmd('pnpm')) steps.push(['pnpm',['run',script]]);
|
|
176
|
-
if(hasCmd('corepack')&&match) steps.push(['corepack',['pnpm@'+match[1],'run',script]]);
|
|
177
|
-
if(hasCmd('corepack')) steps.push(['corepack',['pnpm','run',script]]);
|
|
178
|
-
trySteps(steps);
|
|
179
|
-
console.error('deft: neither pnpm nor corepack is available to run \"'+script+'\".');
|
|
180
|
-
if(pin){
|
|
181
|
-
console.error(' Enable Corepack for the pinned manager: corepack enable && corepack prepare '+pin+' --activate');
|
|
182
|
-
}else{
|
|
183
|
-
console.error(' Install pnpm or enable Corepack (see package.json#packageManager).');
|
|
184
|
-
}
|
|
185
|
-
console.error(' Or set DEFT_PACKAGE_MANAGER=npm for an explicit npm build path.');
|
|
186
|
-
process.exit(127);
|
|
187
|
-
" "{{.DEFT_ROOT}}"
|
|
54
|
+
node "{{.TASKFILE_DIR}}/engine-pm-run.cjs" "{{.DEFT_ROOT}}" build --mark-warm
|
|
188
55
|
fi
|
|
189
56
|
|
|
190
57
|
invoke:
|