@deftai/directive-runtime 0.129.0 → 0.129.2
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/.githooks/_directive-run.sh +9 -12
- package/.githooks/pre-push +1 -5
- package/UPGRADING.md +2 -2
- package/dist/doctor-diagnose.d.ts +8 -3
- package/dist/doctor-diagnose.js +29 -8
- package/dist/hook-dispatch.js +2 -1
- package/dist/pre-pr-run.js +9 -2
- package/dist/self-complete.d.ts +4 -0
- package/dist/self-complete.js +18 -0
- package/dist/setup-wiring.d.ts +5 -0
- package/dist/setup-wiring.js +123 -41
- package/dist/setup.js +73 -1
- package/package.json +1 -1
- package/packs/skills/skills-pack-0.1.json +2 -2
- package/skills/deft-directive-refinement/SKILL.md +2 -2
- package/skills/deft-directive-setup/SKILL.md +2 -2
- package/skills/deft-directive-swarm/references/core-phase-0.md +1 -1
- package/tasks/docs.yml +1 -1
- package/tasks/engine-invoke.cjs +37 -2
- package/tasks/engine-invoke.test.cjs +14 -0
- package/tasks/engine.yml +5 -0
- package/vendor/directive-content/.githooks/_directive-run.sh +9 -12
- package/vendor/directive-content/.githooks/pre-push +1 -5
- package/vendor/directive-content/UPGRADING.md +2 -2
- package/vendor/directive-content/packs/skills/skills-pack-0.1.json +2 -2
- package/vendor/directive-content/skills/deft-directive-refinement/SKILL.md +2 -2
- package/vendor/directive-content/skills/deft-directive-setup/SKILL.md +2 -2
- package/vendor/directive-content/skills/deft-directive-swarm/references/core-phase-0.md +1 -1
- package/vendor/directive-content/tasks/docs.yml +1 -1
- package/vendor/directive-content/tasks/engine-invoke.cjs +37 -2
- package/vendor/directive-content/tasks/engine-invoke.test.cjs +14 -0
- package/vendor/directive-content/tasks/engine.yml +5 -0
- package/vendor/directive-core/dist/.tsbuildinfo +1 -1
- package/vendor/directive-core/dist/class-checks/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/class-checks/evaluate.js +25 -0
- package/vendor/directive-core/dist/class-checks/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/coverage-applicability/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/coverage-applicability/evaluate.js +135 -6
- package/vendor/directive-core/dist/coverage-applicability/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/coverage-hotspots/evaluate.d.ts +2 -0
- package/vendor/directive-core/dist/coverage-hotspots/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/coverage-hotspots/evaluate.js +19 -6
- package/vendor/directive-core/dist/coverage-hotspots/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/fs/contained-write.d.ts +12 -0
- package/vendor/directive-core/dist/fs/contained-write.d.ts.map +1 -1
- package/vendor/directive-core/dist/fs/contained-write.js +35 -1
- package/vendor/directive-core/dist/fs/contained-write.js.map +1 -1
- package/vendor/directive-core/dist/init-deposit/agent-hooks.d.ts +1 -0
- package/vendor/directive-core/dist/init-deposit/agent-hooks.d.ts.map +1 -1
- package/vendor/directive-core/dist/init-deposit/agent-hooks.js +75 -72
- package/vendor/directive-core/dist/init-deposit/agent-hooks.js.map +1 -1
- package/vendor/directive-core/dist/init-deposit/host-hook-outer.d.ts +26 -0
- package/vendor/directive-core/dist/init-deposit/host-hook-outer.d.ts.map +1 -0
- package/vendor/directive-core/dist/init-deposit/host-hook-outer.js +158 -0
- package/vendor/directive-core/dist/init-deposit/host-hook-outer.js.map +1 -0
- package/vendor/directive-core/dist/init-deposit/hygiene.d.ts.map +1 -1
- package/vendor/directive-core/dist/init-deposit/hygiene.js +29 -1
- package/vendor/directive-core/dist/init-deposit/hygiene.js.map +1 -1
- package/vendor/directive-core/dist/install-health/wiring.d.ts.map +1 -1
- package/vendor/directive-core/dist/install-health/wiring.js +141 -1
- package/vendor/directive-core/dist/install-health/wiring.js.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/controller.d.ts +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/controller.d.ts.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/controller.js +43 -15
- package/vendor/directive-core/dist/pre-pr-controller/controller.js.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/coverage-report.d.ts +22 -0
- package/vendor/directive-core/dist/pre-pr-controller/coverage-report.d.ts.map +1 -0
- package/vendor/directive-core/dist/pre-pr-controller/coverage-report.js +48 -0
- package/vendor/directive-core/dist/pre-pr-controller/coverage-report.js.map +1 -0
- package/vendor/directive-core/dist/pre-pr-controller/types.d.ts +7 -0
- package/vendor/directive-core/dist/pre-pr-controller/types.d.ts.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/types.js.map +1 -1
- package/vendor/directive-core/dist/xbrief-migrate/migrate-project.d.ts +5 -4
- package/vendor/directive-core/dist/xbrief-migrate/migrate-project.d.ts.map +1 -1
- package/vendor/directive-core/dist/xbrief-migrate/migrate-project.js +65 -46
- package/vendor/directive-core/dist/xbrief-migrate/migrate-project.js.map +1 -1
- package/vendor/directive-core/dist/xbrief-migrate/transforms.d.ts.map +1 -1
- package/vendor/directive-core/dist/xbrief-migrate/transforms.js +13 -6
- package/vendor/directive-core/dist/xbrief-migrate/transforms.js.map +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env sh
|
|
2
|
-
# Package-relative Directive git-hook helper (AC9.9).
|
|
2
|
+
# Package-relative Directive git-hook helper (AC9.9 / AC9.12 / #5994).
|
|
3
3
|
# Sourced by pre-commit/pre-push; defines run_directive.
|
|
4
|
+
# F1 order: disable → missing pin (project PM) → pin bin.js.
|
|
4
5
|
if [ -z "${REPO_ROOT:-}" ]; then
|
|
5
6
|
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"
|
|
6
7
|
fi
|
|
@@ -8,23 +9,19 @@ if [ -z "$REPO_ROOT" ]; then
|
|
|
8
9
|
echo "directive hooks: unable to resolve repo root." >&2
|
|
9
10
|
exit 1
|
|
10
11
|
fi
|
|
11
|
-
RUNTIME_BIN="$REPO_ROOT/node_modules/@deftai/directive-runtime/dist/bin.js"
|
|
12
|
-
if [ ! -f "$RUNTIME_BIN" ]; then
|
|
13
|
-
echo "directive hooks: missing $RUNTIME_BIN" >&2
|
|
14
|
-
echo "Recovery: npm i -g @deftai/directive, then directive install --repair" >&2
|
|
15
|
-
exit 1
|
|
16
|
-
fi
|
|
17
12
|
run_directive() {
|
|
18
|
-
# #3039 / #5543
|
|
19
|
-
# not a symlink, and not tracked by git (tracked flag must not disable clones).
|
|
13
|
+
# #3039 / #5543 / #5994 F1: disable BEFORE requiring pin bin.js; quiet skip.
|
|
20
14
|
_kill="$REPO_ROOT/.deft-directive-disable"
|
|
21
15
|
if [ -f "$_kill" ] && [ ! -L "$_kill" ]; then
|
|
22
16
|
if ! git -C "$REPO_ROOT" ls-files --error-unmatch -- .deft-directive-disable >/dev/null 2>&1; then
|
|
23
|
-
echo "Directive is DISABLED for this project via root .deft-directive-disable (test/local kill-switch)." >&2
|
|
24
|
-
echo "Git hooks skipping Directive CLI spawn (#5543 / #3039). Deposit may remain." >&2
|
|
25
|
-
echo "Recovery: rm .deft-directive-disable && start a new agent session." >&2
|
|
26
17
|
return 0
|
|
27
18
|
fi
|
|
28
19
|
fi
|
|
20
|
+
RUNTIME_BIN="$REPO_ROOT/node_modules/@deftai/directive-runtime/dist/bin.js"
|
|
21
|
+
if [ ! -f "$RUNTIME_BIN" ]; then
|
|
22
|
+
echo "directive hooks: missing $RUNTIME_BIN" >&2
|
|
23
|
+
echo "Directive hooks: missing node_modules/@deftai/directive-runtime. Run npm install or pnpm install (or yarn) in this project." >&2
|
|
24
|
+
exit 1
|
|
25
|
+
fi
|
|
29
26
|
node "$RUNTIME_BIN" "$@"
|
|
30
27
|
}
|
package/.githooks/pre-push
CHANGED
|
@@ -5,8 +5,4 @@ REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || exit 1
|
|
|
5
5
|
. "$REPO_ROOT/.githooks/_directive-run.sh"
|
|
6
6
|
run_directive verify:branch --project-root "$REPO_ROOT" || exit $?
|
|
7
7
|
# Enrolled bound pre-PR arm: sole-stdin (do not redirect shared run_directive to /dev/null)
|
|
8
|
-
|
|
9
|
-
_pre_pr_help="$(run_directive pre-pr:run --help </dev/null 2>&1)" || exit $?
|
|
10
|
-
case "$_pre_pr_help" in
|
|
11
|
-
*--push-admit*) run_directive pre-pr:run --push-admit --project-root "$REPO_ROOT" || exit $? ;;
|
|
12
|
-
esac
|
|
8
|
+
run_directive pre-pr:run --push-admit --project-root "$REPO_ROOT" || exit $?
|
package/UPGRADING.md
CHANGED
|
@@ -79,7 +79,7 @@ npm install -g --allow-scripts=<pkg,...> <package>
|
|
|
79
79
|
|
|
80
80
|
### Framework monorepo contributors
|
|
81
81
|
|
|
82
|
-
Install source of truth for this repo is **pnpm** (`packageManager` field; CI uses `pnpm install --frozen-lockfile`). Install-script builds are gated by `pnpm-workspace.yaml` **`allowBuilds`** (currently `esbuild: true`). Do **not** treat `npm approve-scripts` under the pnpm virtual store as monorepo SoT.
|
|
82
|
+
Install source of truth for this repo is **pnpm** (`packageManager` field; CI uses `pnpm install --frozen-lockfile`). Install-script builds are gated by `pnpm-workspace.yaml` **`allowBuilds`** (currently `esbuild: true` where needed). **`allowBuilds` is PM-owned**: Directive install/upgrade uses the package manager for `package.json` / lockfile pin ops and does **not** hand-edit `allowBuilds`. If pnpm fails with `ERR_PNPM_IGNORED_BUILDS`, recover with `pnpm approve-builds` or set a real `true`/`false` under `allowBuilds` — do **not** commit pnpm's interactive placeholder text. Pin/add paths already pass `--ignore-scripts` so ordinary consumer upgrades should not need a runtime `allowBuilds` entry. Do **not** treat `npm approve-scripts` under the pnpm virtual store as monorepo SoT.
|
|
83
83
|
|
|
84
84
|
### Git / remote package sources and publish
|
|
85
85
|
|
|
@@ -456,7 +456,7 @@ import schema from "@deftai/directive-types/schemas/xbrief-core-0.8.schema.json"
|
|
|
456
456
|
|
|
457
457
|
**Supported public API:** `@deftai/directive-types` and its published schema subpaths only. **`@deftai/directive-core` is published for npm dependency resolution but is not a supported library surface** — use the `deft` / `directive` CLI for behavior.
|
|
458
458
|
|
|
459
|
-
**Schema source of truth:** `
|
|
459
|
+
**Schema source of truth (OVPR):** the pinned runtime ships schemas under `node_modules/@deftai/directive-runtime/vbrief/schemas/` (current write schema: `xbrief-core-0.8.schema.json`). The types package publishes the same artifacts as `@deftai/directive-types/schemas/…`. Framework source authors at `content/vbrief/schemas/`. Install does **not** deposit project-root `xbrief/schemas/`. Refresh via `directive upgrade` (pin) or `@deftai/directive-types` (npm).
|
|
460
460
|
|
|
461
461
|
### `deft migrate` vs pre-v0.20 document-model migration
|
|
462
462
|
|
|
@@ -8,6 +8,11 @@ export { DEFT_DOCTOR_HOOK_DIAGNOSTIC };
|
|
|
8
8
|
export declare const GIT_PRECONDITION_RECOVERY_GUIDANCE = "git init, then directive install --repair";
|
|
9
9
|
/** Readiness detail when wiring-ready is withheld solely because there is no .git. */
|
|
10
10
|
export declare const GIT_PRECONDITION_READINESS_DETAIL = "incomplete install \u2014 wiring-ready withheld until git init (hooksPath not activatable)";
|
|
11
|
+
/**
|
|
12
|
+
* #5798 — wiring-only residual (pin/install/readiness already ok).
|
|
13
|
+
* Names Taskfile/probe surface; owned wiring damage → repair, never npm i -g hammer.
|
|
14
|
+
*/
|
|
15
|
+
export declare const WIRING_RECOVERY_GUIDANCE = "inspect Taskfile.yml / Directive wiring targets (or probe); for owned wiring damage: directive install --repair";
|
|
11
16
|
/** Patterns that must never appear as doctor remedies (AC12.5–12.7 / AC9.11). */
|
|
12
17
|
export declare const FORBIDDEN_DOCTOR_REMEDY_PATTERNS: readonly RegExp[];
|
|
13
18
|
export interface DoctorSurface {
|
|
@@ -34,11 +39,11 @@ export interface DoctorDiagnosis {
|
|
|
34
39
|
readonly failClosed: boolean;
|
|
35
40
|
readonly dataStatus: DataStatusValue;
|
|
36
41
|
/**
|
|
37
|
-
* Install
|
|
38
|
-
* Null when healthy or schema-only.
|
|
42
|
+
* Install-class (AC9.7), wiring-only (#5798 WIRING_RECOVERY_GUIDANCE), or
|
|
43
|
+
* F-010 git-precondition recovery. Null when healthy or schema-only.
|
|
39
44
|
*/
|
|
40
45
|
readonly remedy: string | null;
|
|
41
|
-
/** Schema/data migration remedy; never
|
|
46
|
+
/** Schema/data migration remedy; never the AC9.7 global-install hammer. */
|
|
42
47
|
readonly schemaRemedy: "directive upgrade" | null;
|
|
43
48
|
/**
|
|
44
49
|
* F-010: set when pin/lock/installed/GENERATION/wiring are ok but readiness is
|
package/dist/doctor-diagnose.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
|
+
import { isHostHookOuterCommand } from "../vendor/directive-core/dist/init-deposit/host-hook-outer.js";
|
|
9
10
|
import { checkFullInSync, DEFT_DOCTOR_HOOK_DIAGNOSTIC, INSTALLATION_RECOVERY_GUIDANCE, RUNTIME_PACKAGE_PATH_PREFIX, } from "../vendor/directive-core/dist/install-health/index.js";
|
|
10
11
|
import { readProjectDataStatus } from "./data-status.js";
|
|
11
12
|
export { DEFT_DOCTOR_HOOK_DIAGNOSTIC };
|
|
@@ -16,6 +17,11 @@ export { DEFT_DOCTOR_HOOK_DIAGNOSTIC };
|
|
|
16
17
|
export const GIT_PRECONDITION_RECOVERY_GUIDANCE = "git init, then directive install --repair";
|
|
17
18
|
/** Readiness detail when wiring-ready is withheld solely because there is no .git. */
|
|
18
19
|
export const GIT_PRECONDITION_READINESS_DETAIL = "incomplete install — wiring-ready withheld until git init (hooksPath not activatable)";
|
|
20
|
+
/**
|
|
21
|
+
* #5798 — wiring-only residual (pin/install/readiness already ok).
|
|
22
|
+
* Names Taskfile/probe surface; owned wiring damage → repair, never npm i -g hammer.
|
|
23
|
+
*/
|
|
24
|
+
export const WIRING_RECOVERY_GUIDANCE = "inspect Taskfile.yml / Directive wiring targets (or probe); for owned wiring damage: directive install --repair";
|
|
19
25
|
/** Patterns that must never appear as doctor remedies (AC12.5–12.7 / AC9.11). */
|
|
20
26
|
export const FORBIDDEN_DOCTOR_REMEDY_PATTERNS = [
|
|
21
27
|
/\balign-pin\b/i,
|
|
@@ -43,7 +49,7 @@ function schemaSurface(dataStatus) {
|
|
|
43
49
|
return { ok: false, detail: "project data schema diagnosis failed" };
|
|
44
50
|
}
|
|
45
51
|
/**
|
|
46
|
-
* Cheap read-only check that committed hook/skill wiring uses package-relative form.
|
|
52
|
+
* Cheap read-only check that committed hook/skill wiring uses host `.mjs` outer or package-relative form.
|
|
47
53
|
* Does not spawn hooks; structural only (AC12.3 hook command form / skill pointers).
|
|
48
54
|
*/
|
|
49
55
|
function inspectHookAndSkillForm(projectRoot) {
|
|
@@ -65,11 +71,13 @@ function inspectHookAndSkillForm(projectRoot) {
|
|
|
65
71
|
sawHook = true;
|
|
66
72
|
try {
|
|
67
73
|
const text = readFileSync(abs, "utf8");
|
|
74
|
+
// #5994 / AC9.1: v261027 host `.mjs` outer is the registered form; pin hook-bin is spawned inside.
|
|
75
|
+
const hasOuter = isHostHookOuterCommand(text);
|
|
68
76
|
const hasPkg = text.includes(prefix) && text.includes("hook-bin");
|
|
69
77
|
const hasLegacy = text.includes(".deft/core/") || /(^|[\s"`'])deft-hook([\s"`']|$)/.test(text);
|
|
70
|
-
if (hasLegacy || !hasPkg) {
|
|
78
|
+
if (hasLegacy || !(hasOuter || hasPkg)) {
|
|
71
79
|
hookOk = false;
|
|
72
|
-
hookDetail = `${rel} does not use package-relative hook-bin form`;
|
|
80
|
+
hookDetail = `${rel} does not use host .mjs outer or package-relative hook-bin form`;
|
|
73
81
|
break;
|
|
74
82
|
}
|
|
75
83
|
}
|
|
@@ -208,13 +216,26 @@ function chooseRemedy(sync, dataStatus, projectRoot) {
|
|
|
208
216
|
if (isNoGitReadinessGap(sync, projectRoot)) {
|
|
209
217
|
return { remedy: GIT_PRECONDITION_RECOVERY_GUIDANCE, schemaRemedy: null };
|
|
210
218
|
}
|
|
211
|
-
const
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
219
|
+
const p = sync.predicates;
|
|
220
|
+
// Install-class faults only — do NOT fold wiring-only into AC9.7 via !inSync (#5798).
|
|
221
|
+
const pinInstallBroken = sync.failClosed || !p.pin.ok || !p.installed.ok || !p.lockfile.ok || !p.generation.ok;
|
|
222
|
+
// Missing readiness (with .git) stays install/repair guidance (acceptance / F-010 peer).
|
|
223
|
+
const readinessBroken = !p.readiness.ok;
|
|
224
|
+
const wiringBroken = !p.wiring.ok;
|
|
225
|
+
const schemaBehind = dataStatus === "migration_needed" || dataStatus === "diagnosis_failed";
|
|
226
|
+
// AC12.7: AC9.7 global install + schema upgrade must not co-prescribe for install-class red.
|
|
227
|
+
if (pinInstallBroken || readinessBroken) {
|
|
215
228
|
return { remedy: INSTALLATION_RECOVERY_GUIDANCE, schemaRemedy: null };
|
|
216
229
|
}
|
|
217
|
-
|
|
230
|
+
// Wiring-only (good pin/install/readiness): failure-shaped map — Taskfile/probe, not npm i -g.
|
|
231
|
+
if (wiringBroken) {
|
|
232
|
+
return {
|
|
233
|
+
remedy: WIRING_RECOVERY_GUIDANCE,
|
|
234
|
+
// Distinct surfaces may list both (#5798 multi-surface); not the AC9.7+upgrade dual ban.
|
|
235
|
+
schemaRemedy: schemaBehind ? "directive upgrade" : null,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
if (schemaBehind) {
|
|
218
239
|
return { remedy: null, schemaRemedy: "directive upgrade" };
|
|
219
240
|
}
|
|
220
241
|
return { remedy: null, schemaRemedy: null };
|
package/dist/hook-dispatch.js
CHANGED
|
@@ -239,7 +239,8 @@ export function run(argv, seams = {}) {
|
|
|
239
239
|
}
|
|
240
240
|
// Wave 2d / AC8.6: registered hooks self-complete when readiness is missing/stale.
|
|
241
241
|
// Suppressed when DEFT_DOCTOR_HOOK_DIAGNOSTIC is set (AC9.10).
|
|
242
|
-
|
|
242
|
+
// #5885: thread seams.writeErr so preserved tracked hooks disclose on stderr.
|
|
243
|
+
maybeSelfCompleteSetup(projectRoot, { fromHook: true, writeErr });
|
|
243
244
|
const environ = mergeHookDispatchEnviron(payload);
|
|
244
245
|
let decision = decideHook({
|
|
245
246
|
host: args.host,
|
package/dist/pre-pr-run.js
CHANGED
|
@@ -422,13 +422,15 @@ export function run(argv) {
|
|
|
422
422
|
return 2;
|
|
423
423
|
}
|
|
424
424
|
const spec = phaseSpec(phase);
|
|
425
|
+
// Same root rule as push-admit: one report path for observe and satisfy.
|
|
426
|
+
const projectRoot = args.projectRoot?.trim() || process.cwd();
|
|
425
427
|
const d = observeCommandPhase(store, args.runId, {
|
|
426
428
|
phaseId: phase,
|
|
427
429
|
command: args.command.length > 0 ? args.command : (spec.command ?? ""),
|
|
428
430
|
exitCode: args.exitCode,
|
|
429
431
|
inputHash: args.inputHash ?? rec?.inputHash ?? "",
|
|
430
432
|
skipReason: args.skipReason,
|
|
431
|
-
});
|
|
433
|
+
}, { projectRoot });
|
|
432
434
|
emit(args.json, d, d.message, !d.ok);
|
|
433
435
|
return d.ok ? 0 : 1;
|
|
434
436
|
}
|
|
@@ -508,7 +510,12 @@ export function run(argv) {
|
|
|
508
510
|
process.stderr.write("pre_pr_run: --complete requires --run-id\n");
|
|
509
511
|
return 2;
|
|
510
512
|
}
|
|
511
|
-
|
|
513
|
+
// #5804 / #5994: projectRoot is required so completeRun can stamp
|
|
514
|
+
// pushBinding.reviewedDeletedPaths from base..head.
|
|
515
|
+
const projectRoot = args.projectRoot?.trim() || process.cwd();
|
|
516
|
+
const d = completeRun(store, controllerPublisher(), args.runId, undefined, {
|
|
517
|
+
projectRoot,
|
|
518
|
+
});
|
|
512
519
|
emit(args.json, d, d.message, !d.ok);
|
|
513
520
|
return d.ok ? 0 : 1;
|
|
514
521
|
}
|
package/dist/self-complete.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ import { type SetupResult } from "./setup.js";
|
|
|
5
5
|
* Doctor sets this before spawning committed hook probes.
|
|
6
6
|
*/
|
|
7
7
|
export declare const DEFT_DOCTOR_HOOK_DIAGNOSTIC = "DEFT_DOCTOR_HOOK_DIAGNOSTIC";
|
|
8
|
+
/** Test-only: clear the process once-guard for preserved disclosure. */
|
|
9
|
+
export declare function resetSelfCompletePreservedDisclosureForTests(): void;
|
|
8
10
|
export type SelfCompleteSkipReason = "fast-path" | "doctor-diagnostic" | "ineligible" | "not-configured" | "fail-closed" | "no-version";
|
|
9
11
|
export interface SelfCompleteResult {
|
|
10
12
|
readonly ran: boolean;
|
|
@@ -24,5 +26,7 @@ export declare function maybeSelfCompleteSetup(projectRoot: string, options?: {
|
|
|
24
26
|
readonly environ?: NodeJS.ProcessEnv;
|
|
25
27
|
/** Hooks are always eligible unless doctor-diagnostic. */
|
|
26
28
|
readonly fromHook?: boolean;
|
|
29
|
+
/** Stderr sink for preserved tracked-hook disclosure (#5885); never stdout. */
|
|
30
|
+
readonly writeErr?: (text: string) => void;
|
|
27
31
|
}): SelfCompleteResult;
|
|
28
32
|
//# sourceMappingURL=self-complete.d.ts.map
|
package/dist/self-complete.js
CHANGED
|
@@ -23,6 +23,22 @@ import { runSetup } from "./setup.js";
|
|
|
23
23
|
* Doctor sets this before spawning committed hook probes.
|
|
24
24
|
*/
|
|
25
25
|
export const DEFT_DOCTOR_HOOK_DIAGNOSTIC = DOCTOR_DIAGNOSTIC_ENV;
|
|
26
|
+
/** Process once-guard for preserved-hook stderr lines (#5885). */
|
|
27
|
+
const emittedPreservedLines = new Set();
|
|
28
|
+
/** Test-only: clear the process once-guard for preserved disclosure. */
|
|
29
|
+
export function resetSelfCompletePreservedDisclosureForTests() {
|
|
30
|
+
emittedPreservedLines.clear();
|
|
31
|
+
}
|
|
32
|
+
function disclosePreservedHooks(preserved, writeErr) {
|
|
33
|
+
if (preserved === undefined || preserved.length === 0)
|
|
34
|
+
return;
|
|
35
|
+
for (const line of preserved) {
|
|
36
|
+
if (emittedPreservedLines.has(line))
|
|
37
|
+
continue;
|
|
38
|
+
emittedPreservedLines.add(line);
|
|
39
|
+
writeErr(`${line}\n`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
26
42
|
/** Installation-management and help heads that never trigger self-complete. */
|
|
27
43
|
const INELIGIBLE_HEADS = new Set([
|
|
28
44
|
"install",
|
|
@@ -76,6 +92,7 @@ function readOwnRuntimeVersion() {
|
|
|
76
92
|
*/
|
|
77
93
|
export function maybeSelfCompleteSetup(projectRoot, options = {}) {
|
|
78
94
|
const environ = options.environ ?? process.env;
|
|
95
|
+
const writeErr = options.writeErr ?? ((text) => process.stderr.write(text));
|
|
79
96
|
if (isDoctorDiagnosticEnv(environ)) {
|
|
80
97
|
return { ran: false, skipReason: "doctor-diagnostic", setup: null };
|
|
81
98
|
}
|
|
@@ -113,6 +130,7 @@ export function maybeSelfCompleteSetup(projectRoot, options = {}) {
|
|
|
113
130
|
from: version,
|
|
114
131
|
root: projectRoot,
|
|
115
132
|
});
|
|
133
|
+
disclosePreservedHooks(setup.preserved, writeErr);
|
|
116
134
|
return { ran: true, skipReason: null, setup };
|
|
117
135
|
}
|
|
118
136
|
//# sourceMappingURL=self-complete.js.map
|
package/dist/setup-wiring.d.ts
CHANGED
|
@@ -94,6 +94,11 @@ export declare function formatTrackedHookPreserved(rel: string): string;
|
|
|
94
94
|
export interface EnsureGitHooksOptions {
|
|
95
95
|
/** Explicit pre-write operator force: replace tracked divergent managed hooks (#5837). */
|
|
96
96
|
readonly forceTrackedReplace?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Test-only: override the next body for named relative hook paths so fixtures
|
|
99
|
+
* can reach refuse-to-downgrade under forceTrackedReplace (#5885 / #5804).
|
|
100
|
+
*/
|
|
101
|
+
readonly testOnlyNextBodyByRel?: Readonly<Record<string, string>>;
|
|
97
102
|
}
|
|
98
103
|
export declare function ensureGitHooks(projectRoot: string, options?: EnsureGitHooksOptions): {
|
|
99
104
|
created: string[];
|
package/dist/setup-wiring.js
CHANGED
|
@@ -8,6 +8,7 @@ import { dirname, join } from "node:path";
|
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import { containedChmod, containedRemove, containedWrite, } from "../vendor/directive-core/dist/fs/contained-write.js";
|
|
10
10
|
import { DIRECT_WRITE_HOOK_MATCHER, KILL_HOOK_MATCHER, MCP_HOOK_MATCHER, SHELL_HOOK_MATCHER, SPAWN_HOOK_MATCHER, } from "../vendor/directive-core/dist/hooks/tools.js";
|
|
11
|
+
import { GROK_PRETOOLUSE_WRAPPER_REL_PATH, HOST_HOOK_MISSING_PIN_PROJECT_PM_GUIDANCE, hostHookOuterCommand, hostHookOuterRelPath, hostHookOuterSha8, hostHookOuterSource, isGrokWrapperCommand, isHostHookOuterCommand, } from "../vendor/directive-core/dist/init-deposit/agent-hooks.js";
|
|
11
12
|
import { INSTALLATION_RECOVERY_GUIDANCE, RUNTIME_PACKAGE_PATH_PREFIX, } from "../vendor/directive-core/dist/install-health/constants.js";
|
|
12
13
|
import { MANAGED_SECTION_CLOSE, MANAGED_SECTION_OPEN_PREFIX, } from "../vendor/directive-core/dist/install-health/index.js";
|
|
13
14
|
import { applyAgentsRefresh } from "../vendor/directive-core/dist/platform/agents-md.js";
|
|
@@ -375,25 +376,45 @@ export function ensureOpenClawIntegrations(projectRoot) {
|
|
|
375
376
|
}
|
|
376
377
|
}
|
|
377
378
|
function hookCommand(host, event) {
|
|
378
|
-
|
|
379
|
+
const sha8 = hostHookOuterSha8();
|
|
380
|
+
return `${hostHookOuterCommand(host, sha8)} --host ${host} --event ${event}`;
|
|
379
381
|
}
|
|
380
382
|
/** Rewrite bare `deft-hook` / legacy `deft hook:dispatch` to package-relative (AC9.2). */
|
|
381
383
|
export function rewriteBareDeftHookCommand(command) {
|
|
382
384
|
const trimmed = command.trim();
|
|
383
|
-
if (trimmed
|
|
385
|
+
if (isHostHookOuterCommand(trimmed))
|
|
384
386
|
return command;
|
|
387
|
+
const hostMatch = trimmed.match(/--host\s+(\w+)/);
|
|
388
|
+
const eventMatch = trimmed.match(/--event\s+([\w.]+)/);
|
|
389
|
+
const host = hostMatch?.[1];
|
|
390
|
+
const event = eventMatch?.[1];
|
|
391
|
+
const sha8 = hostHookOuterSha8();
|
|
392
|
+
const toOuter = (h, e) => `${hostHookOuterCommand(h, sha8)} --host ${h} --event ${e}`;
|
|
393
|
+
// Legacy Grok .sh wrapper → outer
|
|
394
|
+
if (isGrokWrapperCommand(trimmed) && host && event) {
|
|
395
|
+
return toOuter(host, event);
|
|
396
|
+
}
|
|
397
|
+
if (isGrokWrapperCommand(trimmed)) {
|
|
398
|
+
return toOuter("grok", "tool.before");
|
|
399
|
+
}
|
|
400
|
+
// Direct hook-bin with host/event → outer
|
|
401
|
+
if (trimmed.includes(HOOK_BIN_COMMAND) && host && event) {
|
|
402
|
+
return toOuter(host, event);
|
|
403
|
+
}
|
|
385
404
|
const bare = /^(?:deft-hook|deft\s+hook:dispatch)(\s+.*)?$/;
|
|
386
405
|
const m = trimmed.match(bare);
|
|
387
406
|
if (m) {
|
|
388
407
|
const rest = (m[1] ?? "").trim();
|
|
408
|
+
if (host && event)
|
|
409
|
+
return toOuter(host, event);
|
|
410
|
+
// bare without host/event: leave as package-relative interim only if no host
|
|
389
411
|
return rest.length > 0 ? `${HOOK_BIN_COMMAND} ${rest}` : HOOK_BIN_COMMAND;
|
|
390
412
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
return trimmed.replace(/\bdeft-hook\b/g, HOOK_BIN_COMMAND);
|
|
413
|
+
if (/\bdeft-hook\b/.test(trimmed) && !isHostHookOuterCommand(trimmed) && host && event) {
|
|
414
|
+
return toOuter(host, event);
|
|
394
415
|
}
|
|
395
|
-
if (/\bdeft\s+hook:dispatch\b/.test(trimmed) &&
|
|
396
|
-
return
|
|
416
|
+
if (/\bdeft\s+hook:dispatch\b/.test(trimmed) && host && event) {
|
|
417
|
+
return toOuter(host, event);
|
|
397
418
|
}
|
|
398
419
|
return command;
|
|
399
420
|
}
|
|
@@ -427,7 +448,9 @@ function rewriteCommandsInValue(value) {
|
|
|
427
448
|
return { value, changed: false };
|
|
428
449
|
}
|
|
429
450
|
function isManagedHookCommand(command) {
|
|
430
|
-
return (command
|
|
451
|
+
return (isHostHookOuterCommand(command) ||
|
|
452
|
+
command.includes(HOOK_BIN_COMMAND) ||
|
|
453
|
+
isGrokWrapperCommand(command) ||
|
|
431
454
|
/(^|[\s"`'])deft-hook([\s"`']|$)/.test(command) ||
|
|
432
455
|
command.includes("deft hook:dispatch"));
|
|
433
456
|
}
|
|
@@ -436,26 +459,47 @@ function object(value) {
|
|
|
436
459
|
? value
|
|
437
460
|
: null;
|
|
438
461
|
}
|
|
439
|
-
function
|
|
462
|
+
function isManagedNestedHookCommand(command, host) {
|
|
463
|
+
if (host === "grok" && isGrokWrapperCommand(command))
|
|
464
|
+
return true;
|
|
465
|
+
return isManagedHookCommand(command) && command.includes(`--host ${host}`);
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Drop managed commands inside a nested group; keep custom hooks and group fields.
|
|
469
|
+
* Returns null when the group is empty after filtering (or was fully managed).
|
|
470
|
+
* Non-group entries pass through unchanged.
|
|
471
|
+
*/
|
|
472
|
+
function filterManagedCommandsInNestedGroup(value, host, options = {}) {
|
|
440
473
|
const group = object(value);
|
|
441
474
|
if (group === null || !Array.isArray(group.hooks))
|
|
442
|
-
return
|
|
443
|
-
|
|
475
|
+
return value;
|
|
476
|
+
const keptHooks = group.hooks.filter((h) => {
|
|
444
477
|
const hook = object(h);
|
|
445
|
-
|
|
478
|
+
if (typeof hook?.command !== "string")
|
|
479
|
+
return true;
|
|
480
|
+
if (options.hostAgnostic)
|
|
481
|
+
return !isManagedHookCommand(hook.command);
|
|
482
|
+
return !isManagedNestedHookCommand(hook.command, host);
|
|
446
483
|
});
|
|
484
|
+
if (keptHooks.length === 0)
|
|
485
|
+
return null;
|
|
486
|
+
return { ...group, hooks: keptHooks };
|
|
447
487
|
}
|
|
448
488
|
function cursorManagedEntry(value) {
|
|
449
489
|
const entry = object(value);
|
|
450
490
|
return typeof entry?.command === "string" && isManagedHookCommand(entry.command);
|
|
451
491
|
}
|
|
492
|
+
function nestedToolCommand(host, event) {
|
|
493
|
+
// #5994 / AC9.1: every host/event uses the content-hashed `.mjs` outer.
|
|
494
|
+
return hookCommand(host, event);
|
|
495
|
+
}
|
|
452
496
|
function nestedGroup(host, event, matcher) {
|
|
453
497
|
return {
|
|
454
498
|
...(event === "tool.before" && matcher !== undefined ? { matcher } : {}),
|
|
455
499
|
hooks: [
|
|
456
500
|
{
|
|
457
501
|
type: "command",
|
|
458
|
-
command:
|
|
502
|
+
command: nestedToolCommand(host, event),
|
|
459
503
|
timeout: event === "tool.before" ? 30 : 5,
|
|
460
504
|
},
|
|
461
505
|
],
|
|
@@ -478,17 +522,9 @@ function stripNestedManaged(existing, host) {
|
|
|
478
522
|
if (!(key in hooks))
|
|
479
523
|
continue;
|
|
480
524
|
const filtered = Array.isArray(hooks[key])
|
|
481
|
-
? hooks[key]
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
return true;
|
|
485
|
-
return !group.hooks.some((h) => {
|
|
486
|
-
const hook = object(h);
|
|
487
|
-
return (typeof hook?.command === "string" &&
|
|
488
|
-
isManagedHookCommand(hook.command) &&
|
|
489
|
-
hook.command.includes(`--host ${host}`));
|
|
490
|
-
});
|
|
491
|
-
})
|
|
525
|
+
? hooks[key]
|
|
526
|
+
.map((e) => filterManagedCommandsInNestedGroup(e, host))
|
|
527
|
+
.filter((e) => e !== null)
|
|
492
528
|
: [];
|
|
493
529
|
if (filtered.length > 0)
|
|
494
530
|
nextHooks[key] = filtered;
|
|
@@ -525,13 +561,21 @@ function stripCursorManaged(existing) {
|
|
|
525
561
|
}
|
|
526
562
|
return { ...existing, version: 1, hooks: nextHooks };
|
|
527
563
|
}
|
|
564
|
+
function keepCustomNestedGroups(entries, host) {
|
|
565
|
+
return entries
|
|
566
|
+
.map((e) => filterManagedCommandsInNestedGroup(e, host, {
|
|
567
|
+
// Merge clears any managed identity (incl. Grok wrapper) before redeposit.
|
|
568
|
+
hostAgnostic: true,
|
|
569
|
+
}))
|
|
570
|
+
.filter((e) => e !== null);
|
|
571
|
+
}
|
|
528
572
|
function mergeNestedHost(existing, host, options) {
|
|
529
573
|
const hooks = object(existing.hooks) ?? {};
|
|
530
574
|
const session = Array.isArray(hooks.SessionStart)
|
|
531
|
-
? hooks.SessionStart
|
|
575
|
+
? keepCustomNestedGroups(hooks.SessionStart, host)
|
|
532
576
|
: [];
|
|
533
577
|
const preTool = Array.isArray(hooks.PreToolUse)
|
|
534
|
-
? hooks.PreToolUse
|
|
578
|
+
? keepCustomNestedGroups(hooks.PreToolUse, host)
|
|
535
579
|
: [];
|
|
536
580
|
const nextHooks = {
|
|
537
581
|
...hooks,
|
|
@@ -543,10 +587,10 @@ function mergeNestedHost(existing, host, options) {
|
|
|
543
587
|
};
|
|
544
588
|
if (options.compact) {
|
|
545
589
|
const preCompact = Array.isArray(hooks.PreCompact)
|
|
546
|
-
? hooks.PreCompact
|
|
590
|
+
? keepCustomNestedGroups(hooks.PreCompact, host)
|
|
547
591
|
: [];
|
|
548
592
|
const postCompact = Array.isArray(hooks.PostCompact)
|
|
549
|
-
? hooks.PostCompact
|
|
593
|
+
? keepCustomNestedGroups(hooks.PostCompact, host)
|
|
550
594
|
: [];
|
|
551
595
|
nextHooks.PreCompact = [...preCompact, nestedGroup(host, "session.compact")];
|
|
552
596
|
nextHooks.PostCompact = [...postCompact, nestedGroup(host, "session.compact")];
|
|
@@ -665,11 +709,51 @@ export function ensureHostHooks(projectRoot, hostHooksPolicy = loadHostHooksPoli
|
|
|
665
709
|
else
|
|
666
710
|
modified.push(spec.rel);
|
|
667
711
|
}
|
|
712
|
+
// #5994: deposit content-hashed `.mjs` outers; strip legacy Grok `.sh`.
|
|
713
|
+
const outerSource = hostHookOuterSource();
|
|
714
|
+
const outerSha8 = hostHookOuterSha8(outerSource);
|
|
715
|
+
for (const host of ["claude", "grok", "cursor", "codex"]) {
|
|
716
|
+
const rel = hostHookOuterRelPath(host, outerSha8);
|
|
717
|
+
const enabled = isHostHookDepositEnabled(host, hostHooksPolicy);
|
|
718
|
+
if (enabled) {
|
|
719
|
+
const prior = readText(join(projectRoot, rel));
|
|
720
|
+
if (prior !== outerSource) {
|
|
721
|
+
writeContained(projectRoot, rel, outerSource);
|
|
722
|
+
if (prior === null)
|
|
723
|
+
created.push(rel);
|
|
724
|
+
else
|
|
725
|
+
modified.push(rel);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
else if (existsSync(join(projectRoot, rel))) {
|
|
729
|
+
try {
|
|
730
|
+
containedRemove({ root: projectRoot, target: rel });
|
|
731
|
+
modified.push(rel);
|
|
732
|
+
}
|
|
733
|
+
catch {
|
|
734
|
+
/* best-effort strip */
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
const wrapperRel = GROK_PRETOOLUSE_WRAPPER_REL_PATH;
|
|
739
|
+
if (existsSync(join(projectRoot, wrapperRel))) {
|
|
740
|
+
try {
|
|
741
|
+
containedRemove({ root: projectRoot, target: wrapperRel });
|
|
742
|
+
modified.push(wrapperRel);
|
|
743
|
+
}
|
|
744
|
+
catch {
|
|
745
|
+
/* best-effort strip */
|
|
746
|
+
}
|
|
747
|
+
}
|
|
668
748
|
return { created, modified };
|
|
669
749
|
}
|
|
750
|
+
// Taskfile F1 disable-first + project-PM pin miss live in tasks/engine.yml +
|
|
751
|
+
// tasks/engine-invoke.cjs (#5994) — disable-active runs BEFORE pin-bin / is-buildable-source;
|
|
752
|
+
// pin-bin also quiet-exits when disable is active. This GITHOOK_HELPER is the .githooks twin.
|
|
670
753
|
const GITHOOK_HELPER = `#!/usr/bin/env sh
|
|
671
|
-
# Package-relative Directive git-hook helper (AC9.9).
|
|
754
|
+
# Package-relative Directive git-hook helper (AC9.9 / AC9.12 / #5994).
|
|
672
755
|
# Sourced by pre-commit/pre-push; defines run_directive.
|
|
756
|
+
# F1 order: disable → missing pin (project PM) → pin bin.js.
|
|
673
757
|
if [ -z "\${REPO_ROOT:-}" ]; then
|
|
674
758
|
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"
|
|
675
759
|
fi
|
|
@@ -677,24 +761,20 @@ if [ -z "$REPO_ROOT" ]; then
|
|
|
677
761
|
echo "directive hooks: unable to resolve repo root." >&2
|
|
678
762
|
exit 1
|
|
679
763
|
fi
|
|
680
|
-
RUNTIME_BIN="$REPO_ROOT/${RUNTIME_BIN_REL}"
|
|
681
|
-
if [ ! -f "$RUNTIME_BIN" ]; then
|
|
682
|
-
echo "directive hooks: missing $RUNTIME_BIN" >&2
|
|
683
|
-
echo "Recovery: ${INSTALLATION_RECOVERY_GUIDANCE}" >&2
|
|
684
|
-
exit 1
|
|
685
|
-
fi
|
|
686
764
|
run_directive() {
|
|
687
|
-
# #3039 / #5543
|
|
688
|
-
# not a symlink, and not tracked by git (tracked flag must not disable clones).
|
|
765
|
+
# #3039 / #5543 / #5994 F1: disable BEFORE requiring pin bin.js; quiet skip.
|
|
689
766
|
_kill="$REPO_ROOT/.deft-directive-disable"
|
|
690
767
|
if [ -f "$_kill" ] && [ ! -L "$_kill" ]; then
|
|
691
768
|
if ! git -C "$REPO_ROOT" ls-files --error-unmatch -- .deft-directive-disable >/dev/null 2>&1; then
|
|
692
|
-
echo "Directive is DISABLED for this project via root .deft-directive-disable (test/local kill-switch)." >&2
|
|
693
|
-
echo "Git hooks skipping Directive CLI spawn (#5543 / #3039). Deposit may remain." >&2
|
|
694
|
-
echo "Recovery: rm .deft-directive-disable && start a new agent session." >&2
|
|
695
769
|
return 0
|
|
696
770
|
fi
|
|
697
771
|
fi
|
|
772
|
+
RUNTIME_BIN="$REPO_ROOT/${RUNTIME_BIN_REL}"
|
|
773
|
+
if [ ! -f "$RUNTIME_BIN" ]; then
|
|
774
|
+
echo "directive hooks: missing $RUNTIME_BIN" >&2
|
|
775
|
+
echo "${HOST_HOOK_MISSING_PIN_PROJECT_PM_GUIDANCE}" >&2
|
|
776
|
+
exit 1
|
|
777
|
+
fi
|
|
698
778
|
node "$RUNTIME_BIN" "$@"
|
|
699
779
|
}
|
|
700
780
|
`;
|
|
@@ -771,7 +851,9 @@ export function ensureGitHooks(projectRoot, options = {}) {
|
|
|
771
851
|
{ rel: ".githooks/pre-push", body: PRE_PUSH },
|
|
772
852
|
];
|
|
773
853
|
for (const file of files) {
|
|
774
|
-
const
|
|
854
|
+
const override = options.testOnlyNextBodyByRel?.[file.rel];
|
|
855
|
+
const rawNext = override ?? file.body;
|
|
856
|
+
const next = rawNext.endsWith("\n") ? rawNext : `${rawNext}\n`;
|
|
775
857
|
const prior = readText(join(projectRoot, file.rel));
|
|
776
858
|
if (prior !== null && prior !== next && !isOurGitHookBody(file.rel, prior)) {
|
|
777
859
|
conflicts.push(`${file.rel}: existing hook is not Directive-managed; refusing to overwrite (chain or remove manually)`);
|