@deftai/directive-runtime 0.129.0 → 0.129.1
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/UPGRADING.md +2 -2
- package/dist/doctor-diagnose.d.ts +8 -3
- package/dist/doctor-diagnose.js +23 -5
- package/dist/hook-dispatch.js +2 -1
- 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 +80 -21
- 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/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-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 +8 -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 +1 -0
- package/vendor/directive-core/dist/coverage-applicability/evaluate.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 +111 -0
- package/vendor/directive-core/dist/install-health/wiring.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
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
|
@@ -16,6 +16,11 @@ export { DEFT_DOCTOR_HOOK_DIAGNOSTIC };
|
|
|
16
16
|
export const GIT_PRECONDITION_RECOVERY_GUIDANCE = "git init, then directive install --repair";
|
|
17
17
|
/** Readiness detail when wiring-ready is withheld solely because there is no .git. */
|
|
18
18
|
export const GIT_PRECONDITION_READINESS_DETAIL = "incomplete install — wiring-ready withheld until git init (hooksPath not activatable)";
|
|
19
|
+
/**
|
|
20
|
+
* #5798 — wiring-only residual (pin/install/readiness already ok).
|
|
21
|
+
* Names Taskfile/probe surface; owned wiring damage → repair, never npm i -g hammer.
|
|
22
|
+
*/
|
|
23
|
+
export const WIRING_RECOVERY_GUIDANCE = "inspect Taskfile.yml / Directive wiring targets (or probe); for owned wiring damage: directive install --repair";
|
|
19
24
|
/** Patterns that must never appear as doctor remedies (AC12.5–12.7 / AC9.11). */
|
|
20
25
|
export const FORBIDDEN_DOCTOR_REMEDY_PATTERNS = [
|
|
21
26
|
/\balign-pin\b/i,
|
|
@@ -208,13 +213,26 @@ function chooseRemedy(sync, dataStatus, projectRoot) {
|
|
|
208
213
|
if (isNoGitReadinessGap(sync, projectRoot)) {
|
|
209
214
|
return { remedy: GIT_PRECONDITION_RECOVERY_GUIDANCE, schemaRemedy: null };
|
|
210
215
|
}
|
|
211
|
-
const
|
|
212
|
-
//
|
|
213
|
-
|
|
214
|
-
|
|
216
|
+
const p = sync.predicates;
|
|
217
|
+
// Install-class faults only — do NOT fold wiring-only into AC9.7 via !inSync (#5798).
|
|
218
|
+
const pinInstallBroken = sync.failClosed || !p.pin.ok || !p.installed.ok || !p.lockfile.ok || !p.generation.ok;
|
|
219
|
+
// Missing readiness (with .git) stays install/repair guidance (acceptance / F-010 peer).
|
|
220
|
+
const readinessBroken = !p.readiness.ok;
|
|
221
|
+
const wiringBroken = !p.wiring.ok;
|
|
222
|
+
const schemaBehind = dataStatus === "migration_needed" || dataStatus === "diagnosis_failed";
|
|
223
|
+
// AC12.7: AC9.7 global install + schema upgrade must not co-prescribe for install-class red.
|
|
224
|
+
if (pinInstallBroken || readinessBroken) {
|
|
215
225
|
return { remedy: INSTALLATION_RECOVERY_GUIDANCE, schemaRemedy: null };
|
|
216
226
|
}
|
|
217
|
-
|
|
227
|
+
// Wiring-only (good pin/install/readiness): failure-shaped map — Taskfile/probe, not npm i -g.
|
|
228
|
+
if (wiringBroken) {
|
|
229
|
+
return {
|
|
230
|
+
remedy: WIRING_RECOVERY_GUIDANCE,
|
|
231
|
+
// Distinct surfaces may list both (#5798 multi-surface); not the AC9.7+upgrade dual ban.
|
|
232
|
+
schemaRemedy: schemaBehind ? "directive upgrade" : null,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (schemaBehind) {
|
|
218
236
|
return { remedy: null, schemaRemedy: "directive upgrade" };
|
|
219
237
|
}
|
|
220
238
|
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/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_COMMAND, GROK_PRETOOLUSE_WRAPPER_REL_PATH, grokPretooluseWrapperScriptSource, isGrokWrapperCommand, } 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";
|
|
@@ -428,6 +429,7 @@ function rewriteCommandsInValue(value) {
|
|
|
428
429
|
}
|
|
429
430
|
function isManagedHookCommand(command) {
|
|
430
431
|
return (command.includes(HOOK_BIN_COMMAND) ||
|
|
432
|
+
isGrokWrapperCommand(command) ||
|
|
431
433
|
/(^|[\s"`'])deft-hook([\s"`']|$)/.test(command) ||
|
|
432
434
|
command.includes("deft hook:dispatch"));
|
|
433
435
|
}
|
|
@@ -436,26 +438,49 @@ function object(value) {
|
|
|
436
438
|
? value
|
|
437
439
|
: null;
|
|
438
440
|
}
|
|
439
|
-
function
|
|
441
|
+
function isManagedNestedHookCommand(command, host) {
|
|
442
|
+
if (host === "grok" && isGrokWrapperCommand(command))
|
|
443
|
+
return true;
|
|
444
|
+
return isManagedHookCommand(command) && command.includes(`--host ${host}`);
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Drop managed commands inside a nested group; keep custom hooks and group fields.
|
|
448
|
+
* Returns null when the group is empty after filtering (or was fully managed).
|
|
449
|
+
* Non-group entries pass through unchanged.
|
|
450
|
+
*/
|
|
451
|
+
function filterManagedCommandsInNestedGroup(value, host, options = {}) {
|
|
440
452
|
const group = object(value);
|
|
441
453
|
if (group === null || !Array.isArray(group.hooks))
|
|
442
|
-
return
|
|
443
|
-
|
|
454
|
+
return value;
|
|
455
|
+
const keptHooks = group.hooks.filter((h) => {
|
|
444
456
|
const hook = object(h);
|
|
445
|
-
|
|
457
|
+
if (typeof hook?.command !== "string")
|
|
458
|
+
return true;
|
|
459
|
+
if (options.hostAgnostic)
|
|
460
|
+
return !isManagedHookCommand(hook.command);
|
|
461
|
+
return !isManagedNestedHookCommand(hook.command, host);
|
|
446
462
|
});
|
|
463
|
+
if (keptHooks.length === 0)
|
|
464
|
+
return null;
|
|
465
|
+
return { ...group, hooks: keptHooks };
|
|
447
466
|
}
|
|
448
467
|
function cursorManagedEntry(value) {
|
|
449
468
|
const entry = object(value);
|
|
450
469
|
return typeof entry?.command === "string" && isManagedHookCommand(entry.command);
|
|
451
470
|
}
|
|
471
|
+
function nestedToolCommand(host, event) {
|
|
472
|
+
// Grok PreToolUse deposits the outer-deny wrapper (#5807 / #5656 / #5715).
|
|
473
|
+
if (host === "grok" && event === "tool.before")
|
|
474
|
+
return GROK_PRETOOLUSE_WRAPPER_COMMAND;
|
|
475
|
+
return hookCommand(host, event);
|
|
476
|
+
}
|
|
452
477
|
function nestedGroup(host, event, matcher) {
|
|
453
478
|
return {
|
|
454
479
|
...(event === "tool.before" && matcher !== undefined ? { matcher } : {}),
|
|
455
480
|
hooks: [
|
|
456
481
|
{
|
|
457
482
|
type: "command",
|
|
458
|
-
command:
|
|
483
|
+
command: nestedToolCommand(host, event),
|
|
459
484
|
timeout: event === "tool.before" ? 30 : 5,
|
|
460
485
|
},
|
|
461
486
|
],
|
|
@@ -478,17 +503,9 @@ function stripNestedManaged(existing, host) {
|
|
|
478
503
|
if (!(key in hooks))
|
|
479
504
|
continue;
|
|
480
505
|
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
|
-
})
|
|
506
|
+
? hooks[key]
|
|
507
|
+
.map((e) => filterManagedCommandsInNestedGroup(e, host))
|
|
508
|
+
.filter((e) => e !== null)
|
|
492
509
|
: [];
|
|
493
510
|
if (filtered.length > 0)
|
|
494
511
|
nextHooks[key] = filtered;
|
|
@@ -525,13 +542,21 @@ function stripCursorManaged(existing) {
|
|
|
525
542
|
}
|
|
526
543
|
return { ...existing, version: 1, hooks: nextHooks };
|
|
527
544
|
}
|
|
545
|
+
function keepCustomNestedGroups(entries, host) {
|
|
546
|
+
return entries
|
|
547
|
+
.map((e) => filterManagedCommandsInNestedGroup(e, host, {
|
|
548
|
+
// Merge clears any managed identity (incl. Grok wrapper) before redeposit.
|
|
549
|
+
hostAgnostic: true,
|
|
550
|
+
}))
|
|
551
|
+
.filter((e) => e !== null);
|
|
552
|
+
}
|
|
528
553
|
function mergeNestedHost(existing, host, options) {
|
|
529
554
|
const hooks = object(existing.hooks) ?? {};
|
|
530
555
|
const session = Array.isArray(hooks.SessionStart)
|
|
531
|
-
? hooks.SessionStart
|
|
556
|
+
? keepCustomNestedGroups(hooks.SessionStart, host)
|
|
532
557
|
: [];
|
|
533
558
|
const preTool = Array.isArray(hooks.PreToolUse)
|
|
534
|
-
? hooks.PreToolUse
|
|
559
|
+
? keepCustomNestedGroups(hooks.PreToolUse, host)
|
|
535
560
|
: [];
|
|
536
561
|
const nextHooks = {
|
|
537
562
|
...hooks,
|
|
@@ -543,10 +568,10 @@ function mergeNestedHost(existing, host, options) {
|
|
|
543
568
|
};
|
|
544
569
|
if (options.compact) {
|
|
545
570
|
const preCompact = Array.isArray(hooks.PreCompact)
|
|
546
|
-
? hooks.PreCompact
|
|
571
|
+
? keepCustomNestedGroups(hooks.PreCompact, host)
|
|
547
572
|
: [];
|
|
548
573
|
const postCompact = Array.isArray(hooks.PostCompact)
|
|
549
|
-
? hooks.PostCompact
|
|
574
|
+
? keepCustomNestedGroups(hooks.PostCompact, host)
|
|
550
575
|
: [];
|
|
551
576
|
nextHooks.PreCompact = [...preCompact, nestedGroup(host, "session.compact")];
|
|
552
577
|
nextHooks.PostCompact = [...postCompact, nestedGroup(host, "session.compact")];
|
|
@@ -665,6 +690,38 @@ export function ensureHostHooks(projectRoot, hostHooksPolicy = loadHostHooksPoli
|
|
|
665
690
|
else
|
|
666
691
|
modified.push(spec.rel);
|
|
667
692
|
}
|
|
693
|
+
// #5807: rewrite/redeposit Grok outer-deny wrapper so leftover deft-hook deny
|
|
694
|
+
// bodies cannot sit beside a healthy PreToolUse registration.
|
|
695
|
+
const grokEnabled = isHostHookDepositEnabled("grok", hostHooksPolicy);
|
|
696
|
+
const wrapperRel = GROK_PRETOOLUSE_WRAPPER_REL_PATH;
|
|
697
|
+
if (grokEnabled) {
|
|
698
|
+
const wrapperBody = grokPretooluseWrapperScriptSource();
|
|
699
|
+
const wrapperPrior = readText(join(projectRoot, wrapperRel));
|
|
700
|
+
if (wrapperPrior !== wrapperBody) {
|
|
701
|
+
writeContained(projectRoot, wrapperRel, wrapperBody);
|
|
702
|
+
if (wrapperPrior === null)
|
|
703
|
+
created.push(wrapperRel);
|
|
704
|
+
else
|
|
705
|
+
modified.push(wrapperRel);
|
|
706
|
+
}
|
|
707
|
+
if (existsSync(join(projectRoot, wrapperRel))) {
|
|
708
|
+
try {
|
|
709
|
+
containedChmod({ root: projectRoot, target: wrapperRel, mode: 0o755 });
|
|
710
|
+
}
|
|
711
|
+
catch {
|
|
712
|
+
/* best-effort on platforms without chmod */
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
else if (existsSync(join(projectRoot, wrapperRel))) {
|
|
717
|
+
try {
|
|
718
|
+
containedRemove({ root: projectRoot, target: wrapperRel });
|
|
719
|
+
modified.push(wrapperRel);
|
|
720
|
+
}
|
|
721
|
+
catch {
|
|
722
|
+
/* best-effort strip */
|
|
723
|
+
}
|
|
724
|
+
}
|
|
668
725
|
return { created, modified };
|
|
669
726
|
}
|
|
670
727
|
const GITHOOK_HELPER = `#!/usr/bin/env sh
|
|
@@ -771,7 +828,9 @@ export function ensureGitHooks(projectRoot, options = {}) {
|
|
|
771
828
|
{ rel: ".githooks/pre-push", body: PRE_PUSH },
|
|
772
829
|
];
|
|
773
830
|
for (const file of files) {
|
|
774
|
-
const
|
|
831
|
+
const override = options.testOnlyNextBodyByRel?.[file.rel];
|
|
832
|
+
const rawNext = override ?? file.body;
|
|
833
|
+
const next = rawNext.endsWith("\n") ? rawNext : `${rawNext}\n`;
|
|
775
834
|
const prior = readText(join(projectRoot, file.rel));
|
|
776
835
|
if (prior !== null && prior !== next && !isOurGitHookBody(file.rel, prior)) {
|
|
777
836
|
conflicts.push(`${file.rel}: existing hook is not Directive-managed; refusing to overwrite (chain or remove manually)`);
|
package/dist/setup.js
CHANGED
|
@@ -9,14 +9,20 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
|
9
9
|
import { dirname, join } from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
import { ContainedWriteError, containedRemove, containedWrite, } from "../vendor/directive-core/dist/fs/contained-write.js";
|
|
12
|
+
import { defaultTrackedNames, isTrackedDeletePath, stageFrameworkPaths, } from "../vendor/directive-core/dist/init-deposit/hygiene.js";
|
|
13
|
+
import { seedMinimalProjectDefinition } from "../vendor/directive-core/dist/init-deposit/init-deposit.js";
|
|
12
14
|
import { buildOwnershipRecord, fingerprintContent, ownershipRecordsEqual, readEvidenceGenerationNumber, readOwnershipRecord, writeOwnershipRecord, } from "../vendor/directive-core/dist/install-health/index.js";
|
|
13
15
|
import { assertInventoryCompleteness } from "./installer-output-inventory.js";
|
|
14
16
|
import { CURRENT_DATA_SCHEMA_VERSION, DATA_SCHEMA_MARKER_REL } from "./migrate.js";
|
|
15
17
|
import { isConvertStrategyFrom, runStrategy } from "./removal-steps/index.js";
|
|
16
18
|
import { ensureAgentsManagedSection, ensureGitHooks, ensureHostHooks, ensureOpenClawIntegrations, ensureSkillPointers, ensureSlashCommands, ensureTaskfileInclude, listSkillPointerPaths, listVerifiedManagedSlashCommandPaths, RUNTIME_PREFIX, } from "./setup-wiring.js";
|
|
17
19
|
const SEED_PLACEHOLDER_REL = "xbrief/.directive-seed-placeholder";
|
|
20
|
+
const PD_REL = "xbrief/PROJECT-DEFINITION.xbrief.json";
|
|
18
21
|
const GEN_REL = ".deft/GENERATION.json";
|
|
19
22
|
const WIRING_READY_DIR_REL = ".deft/directive";
|
|
23
|
+
const XBRIEF_LIFECYCLE_DIRS = ["proposed", "pending", "active", "completed", "cancelled"];
|
|
24
|
+
const XBRIEF_LIFECYCLE_GITKEEP = "# Keeps this xbrief/ lifecycle directory present in version control so the\n" +
|
|
25
|
+
"# deft-directive-setup pre-cutover guard does not fire on a fresh install (#4666).\n";
|
|
20
26
|
function readOwnRuntimeVersion() {
|
|
21
27
|
try {
|
|
22
28
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
@@ -144,6 +150,59 @@ function createSeedPlaceholder(projectRoot) {
|
|
|
144
150
|
writeContained(projectRoot, SEED_PLACEHOLDER_REL, "# Directive initial seed placeholder (install only; repair must not create this).\n");
|
|
145
151
|
return SEED_PLACEHOLDER_REL;
|
|
146
152
|
}
|
|
153
|
+
/** Install-only: five xbrief lifecycle dirs + .gitkeeps (#4666 / AC8.4). */
|
|
154
|
+
function ensureXbriefLifecycleGitkeeps(projectRoot) {
|
|
155
|
+
const created = [];
|
|
156
|
+
for (const sub of XBRIEF_LIFECYCLE_DIRS) {
|
|
157
|
+
const rel = relPosix("xbrief", sub, ".gitkeep");
|
|
158
|
+
if (existsSync(join(projectRoot, rel)))
|
|
159
|
+
continue;
|
|
160
|
+
writeContained(projectRoot, rel, XBRIEF_LIFECYCLE_GITKEEP);
|
|
161
|
+
created.push(rel);
|
|
162
|
+
}
|
|
163
|
+
return created;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Stage this-run setup writes after install/upgrade (#4666).
|
|
167
|
+
* Always includes PD + lifecycle gitkeeps when present (fix the 0.126 hole).
|
|
168
|
+
* Does not `git add` every existing managed matcher path — that would stage
|
|
169
|
+
* unrelated dirty peers (package.json / custom .githooks/*). Keeps exact
|
|
170
|
+
* this-run paths (no skill/command parent compaction) so sibling untracked
|
|
171
|
+
* skills stay unstaged. This-run removals may be missing on disk so tracked
|
|
172
|
+
* deletions can reach `git add`; untracked missing paths are skipped so one
|
|
173
|
+
* unmatched pathspec cannot fail the whole staging batch (#5883 P1 / #3394).
|
|
174
|
+
*/
|
|
175
|
+
function stageSetupManagedPaths(projectRoot, thisRunPaths) {
|
|
176
|
+
const present = [];
|
|
177
|
+
const missingAllow = [];
|
|
178
|
+
const seen = new Set();
|
|
179
|
+
const add = (rel, opts = {}) => {
|
|
180
|
+
const normalized = rel.replace(/\\/g, "/");
|
|
181
|
+
if (!normalized || seen.has(normalized))
|
|
182
|
+
return;
|
|
183
|
+
const onDisk = existsSync(join(projectRoot, normalized));
|
|
184
|
+
if (!onDisk && !opts.allowMissing)
|
|
185
|
+
return;
|
|
186
|
+
seen.add(normalized);
|
|
187
|
+
if (onDisk)
|
|
188
|
+
present.push(normalized);
|
|
189
|
+
else
|
|
190
|
+
missingAllow.push(normalized);
|
|
191
|
+
};
|
|
192
|
+
for (const rel of thisRunPaths) {
|
|
193
|
+
add(rel, { allowMissing: true });
|
|
194
|
+
}
|
|
195
|
+
add(PD_REL);
|
|
196
|
+
for (const sub of XBRIEF_LIFECYCLE_DIRS) {
|
|
197
|
+
add(relPosix("xbrief", sub, ".gitkeep"));
|
|
198
|
+
}
|
|
199
|
+
const tracked = new Set(defaultTrackedNames(projectRoot, missingAllow));
|
|
200
|
+
const stagePaths = [
|
|
201
|
+
...present,
|
|
202
|
+
...missingAllow.filter((path) => isTrackedDeletePath(path, tracked)),
|
|
203
|
+
];
|
|
204
|
+
stageFrameworkPaths(projectRoot, stagePaths);
|
|
205
|
+
}
|
|
147
206
|
/** Shared host settings / Taskfile: only Directive entries are owned (structured-entry). */
|
|
148
207
|
const STRUCTURED_ENTRY_RELS = [
|
|
149
208
|
{ path: ".claude/settings.json", entryKey: "hooks" },
|
|
@@ -207,7 +266,9 @@ function buildOwnershipArtifacts(projectRoot) {
|
|
|
207
266
|
});
|
|
208
267
|
}
|
|
209
268
|
const seedAbs = join(projectRoot, SEED_PLACEHOLDER_REL);
|
|
210
|
-
|
|
269
|
+
const pdAbs = join(projectRoot, PD_REL);
|
|
270
|
+
const hasLifecycle = XBRIEF_LIFECYCLE_DIRS.some((sub) => existsSync(join(projectRoot, "xbrief", sub, ".gitkeep")));
|
|
271
|
+
if (existsSync(seedAbs) || existsSync(pdAbs) || hasLifecycle) {
|
|
211
272
|
artifacts.push({
|
|
212
273
|
path: "xbrief",
|
|
213
274
|
kind: "retained",
|
|
@@ -312,10 +373,15 @@ export function runSetup(options) {
|
|
|
312
373
|
// Seeds only on first-time install (AC8.4); repair/upgrade never create them.
|
|
313
374
|
// AC6.2: leave data-schema current for empty/seeded install. Repair may write a
|
|
314
375
|
// missing marker (not a migration); upgrade still migrates when data is behind.
|
|
376
|
+
// #4666: greenfield retain — PD + lifecycle dirs (not placeholder-only); no schemas.
|
|
315
377
|
if (operation === "install" && conflicts.length === 0) {
|
|
316
378
|
const seed = createSeedPlaceholder(root);
|
|
317
379
|
if (seed !== null)
|
|
318
380
|
created.push(seed);
|
|
381
|
+
created.push(...ensureXbriefLifecycleGitkeeps(root));
|
|
382
|
+
if (seedMinimalProjectDefinition(root, { printf: () => undefined })) {
|
|
383
|
+
created.push(PD_REL);
|
|
384
|
+
}
|
|
319
385
|
}
|
|
320
386
|
if ((operation === "install" || operation === "repair") &&
|
|
321
387
|
conflicts.length === 0 &&
|
|
@@ -338,6 +404,12 @@ export function runSetup(options) {
|
|
|
338
404
|
created.push(GEN_REL);
|
|
339
405
|
else if (!unchanged || legacyConversion || convertViaRemovals)
|
|
340
406
|
modified.push(GEN_REL);
|
|
407
|
+
// #4666: stage this-run managed peers including PD + lifecycle after install/upgrade.
|
|
408
|
+
// Still no auto-commit. Repair does not stage (seeds are install-only; upgrade stages
|
|
409
|
+
// existing PD/lifecycle with refreshed wiring).
|
|
410
|
+
if (conflicts.length === 0 && (operation === "install" || operation === "upgrade")) {
|
|
411
|
+
stageSetupManagedPaths(root, [...created, ...modified, ...removed]);
|
|
412
|
+
}
|
|
341
413
|
if (conflicts.length > 0) {
|
|
342
414
|
// Never publish readiness after conflict; invalidate any existing marker (AC8.6).
|
|
343
415
|
invalidateReadinessMarkers(root, removed);
|
package/package.json
CHANGED