@deftai/directive-core 0.73.0 → 0.73.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/dist/branch/evaluate.js +2 -1
- package/dist/deposit/copy-tree.js +31 -7
- package/dist/init-deposit/hygiene.d.ts +6 -0
- package/dist/init-deposit/hygiene.js +5 -1
- package/dist/init-deposit/refresh.d.ts +16 -2
- package/dist/init-deposit/refresh.js +144 -50
- package/dist/init-deposit/scaffold.js +5 -4
- package/dist/policy/disclosure.d.ts +1 -0
- package/dist/policy/disclosure.js +1 -0
- package/dist/policy/index.d.ts +1 -0
- package/dist/policy/index.js +1 -0
- package/dist/policy/policy-invocation.d.ts +5 -0
- package/dist/policy/policy-invocation.js +9 -0
- package/dist/policy/resolve.js +4 -2
- package/dist/policy/value-feedback.js +6 -3
- package/dist/swarm/constants.d.ts +1 -1
- package/dist/swarm/constants.js +2 -1
- package/dist/swarm/subagent-backend.js +2 -1
- package/dist/ts-check-lane/run-lane.d.ts +4 -0
- package/dist/ts-check-lane/run-lane.js +20 -6
- package/dist/value/feedback-file.js +3 -2
- package/dist/value/readback.js +3 -2
- package/package.json +3 -3
package/dist/branch/evaluate.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { recordBypassSignal, recordGateCatch } from "../events/attribution-ledger.js";
|
|
2
2
|
import { disclosureLine } from "../policy/disclosure.js";
|
|
3
|
+
import { policyColonInvocation } from "../policy/policy-invocation.js";
|
|
3
4
|
import { ENV_BYPASS, resolvePolicy } from "../policy/resolve.js";
|
|
4
5
|
import { currentBranch, GitNotFoundError } from "./git.js";
|
|
5
6
|
export const DEFAULT_BRANCHES = new Set(["master", "main"]);
|
|
@@ -32,7 +33,7 @@ function buildBlockMessage(branch, result) {
|
|
|
32
33
|
if (result.deprecationWarning !== null) {
|
|
33
34
|
parts.push(` Note: ${result.deprecationWarning}`);
|
|
34
35
|
}
|
|
35
|
-
parts.push("", " How to proceed:", " • Create a feature branch: git switch -c feat/<name>", " • Or opt out via the typed surface:", "
|
|
36
|
+
parts.push("", " How to proceed:", " • Create a feature branch: git switch -c feat/<name>", " • Or opt out via the typed surface:", ` ${policyColonInvocation("allow-direct-commits", " -- --confirm")}`, ` • Or set the emergency-escape env-var: ${ENV_BYPASS}=1`, "", " See README.md (Branch policy) and skills/deft-directive-setup/", " Phase 2 Step 9 (capability-cost disclosure).");
|
|
36
37
|
return parts.join("\n");
|
|
37
38
|
}
|
|
38
39
|
/**
|
|
@@ -7,12 +7,29 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Refs #1942 S1, #1477.
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { constants } from "node:fs";
|
|
11
|
+
import { lstat, mkdir, open, readdir, readFile, stat } from "node:fs/promises";
|
|
12
12
|
import { dirname, join } from "node:path";
|
|
13
|
-
import { pipeline } from "node:stream/promises";
|
|
14
13
|
const DEFAULT_FILE_MODE = 0o644;
|
|
15
14
|
const DEFAULT_DIR_MODE = 0o755;
|
|
15
|
+
async function assertDestinationIsNotSymlink(path) {
|
|
16
|
+
try {
|
|
17
|
+
const info = await lstat(path);
|
|
18
|
+
if (info.isSymbolicLink()) {
|
|
19
|
+
throw new Error(`copyTree: refusing to write through destination symlink ${path}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
if (err instanceof Error && err.message.startsWith("copyTree: refusing")) {
|
|
24
|
+
throw err;
|
|
25
|
+
}
|
|
26
|
+
if (err.code === "ENOENT") {
|
|
27
|
+
// Missing paths are created by the caller; only pre-existing symlinks matter.
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
16
33
|
async function copyFilePreserveMode(src, dst) {
|
|
17
34
|
let mode = DEFAULT_FILE_MODE;
|
|
18
35
|
try {
|
|
@@ -23,12 +40,19 @@ async function copyFilePreserveMode(src, dst) {
|
|
|
23
40
|
// Stat failure is non-fatal — fall back to 0o644 (mirrors Go copyFile).
|
|
24
41
|
}
|
|
25
42
|
await mkdir(dirname(dst), { recursive: true, mode: DEFAULT_DIR_MODE });
|
|
26
|
-
await
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
43
|
+
await assertDestinationIsNotSymlink(dst);
|
|
44
|
+
const handle = await open(dst, constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC | constants.O_NOFOLLOW, mode);
|
|
45
|
+
try {
|
|
46
|
+
await handle.writeFile(await readFile(src));
|
|
47
|
+
// Use the opened handle, not the path, so chmod cannot follow a swapped symlink.
|
|
48
|
+
await handle.chmod(mode);
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
await handle.close();
|
|
52
|
+
}
|
|
30
53
|
}
|
|
31
54
|
async function copyDirContents(src, dst) {
|
|
55
|
+
await assertDestinationIsNotSymlink(dst);
|
|
32
56
|
await mkdir(dst, { recursive: true, mode: DEFAULT_DIR_MODE });
|
|
33
57
|
const entries = await readdir(src, { withFileTypes: true });
|
|
34
58
|
for (const entry of entries) {
|
|
@@ -17,6 +17,12 @@ export declare function installerManagedMatchers(): InstallerManagedMatcher[];
|
|
|
17
17
|
export declare function installerManagedGuardEre(): string;
|
|
18
18
|
export declare function isInstallerManagedPath(path: string): boolean;
|
|
19
19
|
export interface FrameworkStagePathsOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Include the vendored `.deft/core` payload in the stage set. Defaults to
|
|
22
|
+
* `true` for init and real payload swaps. No-op updates set this to `false`
|
|
23
|
+
* so CRLF-only core noise cannot be staged while safe projections are repaired.
|
|
24
|
+
*/
|
|
25
|
+
readonly includeCore?: boolean;
|
|
20
26
|
/**
|
|
21
27
|
* Include the project-root ``Taskfile.yml`` in the stage set. Defaults to
|
|
22
28
|
* ``false``: unlike the other allowlisted paths, ``Taskfile.yml`` is a
|
|
@@ -82,7 +82,10 @@ export function frameworkStagePaths(projectDir, deftDir, options = {}) {
|
|
|
82
82
|
paths.push(normalized);
|
|
83
83
|
};
|
|
84
84
|
const relDeft = relative(projectDir, deftDir);
|
|
85
|
-
if (
|
|
85
|
+
if (options.includeCore !== false &&
|
|
86
|
+
relDeft &&
|
|
87
|
+
!relDeft.startsWith("..") &&
|
|
88
|
+
!relDeft.startsWith("/")) {
|
|
86
89
|
add(relDeft);
|
|
87
90
|
}
|
|
88
91
|
for (const matcher of installerManagedMatchers()) {
|
|
@@ -228,6 +231,7 @@ function actuallyStagedPaths(stagePaths, cachedNames) {
|
|
|
228
231
|
export function depositStagePaths(projectDir, options = {}) {
|
|
229
232
|
const deftDir = join(projectDir, CANONICAL_INSTALL_ROOT);
|
|
230
233
|
const stagePaths = frameworkStagePaths(projectDir, deftDir, {
|
|
234
|
+
includeCore: options.includeCore,
|
|
231
235
|
includeTaskfile: options.includeTaskfile ?? false,
|
|
232
236
|
});
|
|
233
237
|
const { staged } = stageFrameworkPaths(projectDir, stagePaths, options);
|
|
@@ -22,12 +22,15 @@ export interface RefreshDepositResult {
|
|
|
22
22
|
readonly contentVersion: string;
|
|
23
23
|
readonly engineVersion: string;
|
|
24
24
|
readonly previousDepositVersion: string | null;
|
|
25
|
+
readonly alreadyCurrent: boolean;
|
|
26
|
+
readonly strategy: RefreshDepositStrategy;
|
|
25
27
|
readonly agentsMdUpdated: boolean;
|
|
26
28
|
readonly versionSkewNotice: string | null;
|
|
27
29
|
readonly legacyLayout: boolean;
|
|
28
30
|
readonly taskfileWired: boolean;
|
|
29
31
|
readonly stagedPaths: string[];
|
|
30
32
|
}
|
|
33
|
+
export type RefreshDepositStrategy = "file-swap" | "no-op";
|
|
31
34
|
export interface RefreshDepositSeams {
|
|
32
35
|
resolveContentRoot?: () => Promise<string>;
|
|
33
36
|
copyContent?: (src: string, dst: string) => Promise<void>;
|
|
@@ -35,6 +38,7 @@ export interface RefreshDepositSeams {
|
|
|
35
38
|
readEngineVersion?: () => string;
|
|
36
39
|
nowIso?: () => string;
|
|
37
40
|
gitPorcelain?: (projectRoot: string) => string | null;
|
|
41
|
+
gitSemanticDiffNames?: GitSemanticDiffNames;
|
|
38
42
|
detectLegacy?: (projectDir: string) => LegacyLayoutDetection;
|
|
39
43
|
/**
|
|
40
44
|
* #2266: `git ls-files` probe threaded into the non-destructive `.gitignore`
|
|
@@ -101,9 +105,19 @@ export declare function buildDefaultLadderFacts(projectDir: string, facts: Resol
|
|
|
101
105
|
* manual npm/PATH steps (#2266 a3).
|
|
102
106
|
*/
|
|
103
107
|
export declare function selfHealEngine(projectDir: string, facts: ResolutionFacts, io: InitDepositIo, seams?: SelfHealSeams): EngineResolution;
|
|
108
|
+
export type GitSemanticDiffNames = (projectRoot: string, paths: readonly string[]) => readonly string[] | null;
|
|
109
|
+
export interface FrameworkRefreshSideEffectsOptions {
|
|
110
|
+
readonly readPorcelain?: (root: string) => string | null;
|
|
111
|
+
readonly readSemanticDiffNames?: GitSemanticDiffNames;
|
|
112
|
+
}
|
|
113
|
+
export interface RefreshSideEffects {
|
|
114
|
+
readonly files: string[];
|
|
115
|
+
readonly crlfOnlyCoreFiles: string[];
|
|
116
|
+
readonly payloadSwapped?: boolean;
|
|
117
|
+
}
|
|
104
118
|
/** Framework-managed uncommitted paths after refresh (#1671). */
|
|
105
|
-
export declare function frameworkRefreshSideEffects(projectDir: string,
|
|
106
|
-
export declare function printRefreshSideEffects(io: InitDepositIo,
|
|
119
|
+
export declare function frameworkRefreshSideEffects(projectDir: string, options?: FrameworkRefreshSideEffectsOptions): RefreshSideEffects;
|
|
120
|
+
export declare function printRefreshSideEffects(io: InitDepositIo, effects: RefreshSideEffects): void;
|
|
107
121
|
export declare function buildVersionSkewNotice(engineVersion: string, contentVersion: string, previousDepositVersion: string | null): string | null;
|
|
108
122
|
export declare function buildUpdateSummaryJson(result: RefreshDepositResult, options: RefreshDepositArgs, updateState?: UpdateState): Record<string, unknown>;
|
|
109
123
|
export declare function printUpdateComplete(result: RefreshDepositResult, io: InitDepositIo, updateState?: UpdateState): void;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Refs #1942, #1430, #1671.
|
|
9
9
|
*/
|
|
10
|
+
import { execFileSync } from "node:child_process";
|
|
10
11
|
import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
|
|
11
12
|
import { platform as osPlatform } from "node:os";
|
|
12
13
|
import { join, resolve } from "node:path";
|
|
@@ -218,11 +219,12 @@ function unquoteGitPath(path) {
|
|
|
218
219
|
}
|
|
219
220
|
return path;
|
|
220
221
|
}
|
|
221
|
-
function
|
|
222
|
-
const
|
|
222
|
+
function porcelainStatusEntries(porcelain) {
|
|
223
|
+
const entries = [];
|
|
223
224
|
for (const line of porcelain.split("\n")) {
|
|
224
225
|
if (line.length < 4)
|
|
225
226
|
continue;
|
|
227
|
+
const status = line.slice(0, 2);
|
|
226
228
|
let rest = line.slice(3);
|
|
227
229
|
const arrow = rest.indexOf(" -> ");
|
|
228
230
|
if (arrow >= 0)
|
|
@@ -230,43 +232,102 @@ function porcelainStatusPaths(porcelain) {
|
|
|
230
232
|
const trimmed = unquoteGitPath(rest.trim());
|
|
231
233
|
if (!trimmed)
|
|
232
234
|
continue;
|
|
233
|
-
|
|
235
|
+
entries.push({ status, path: trimmed.replace(/\\/g, "/") });
|
|
234
236
|
}
|
|
235
|
-
return
|
|
237
|
+
return entries;
|
|
236
238
|
}
|
|
237
|
-
function
|
|
239
|
+
function classifyChangedEntries(changed) {
|
|
238
240
|
const core = [];
|
|
239
241
|
const installerManaged = [];
|
|
240
|
-
for (const
|
|
242
|
+
for (const entry of changed) {
|
|
243
|
+
const { path } = entry;
|
|
241
244
|
if (!path)
|
|
242
245
|
continue;
|
|
243
246
|
if (path.startsWith(".deft/core/") || path === ".deft/core") {
|
|
244
|
-
core.push(
|
|
247
|
+
core.push(entry);
|
|
245
248
|
}
|
|
246
249
|
else if (isInstallerManagedPath(path)) {
|
|
247
|
-
installerManaged.push(
|
|
250
|
+
installerManaged.push(entry);
|
|
248
251
|
}
|
|
249
252
|
}
|
|
250
253
|
return { core, installerManaged };
|
|
251
254
|
}
|
|
255
|
+
function isCrlfNoiseCandidate(status) {
|
|
256
|
+
return status.includes("M") && /^[ M]+$/.test(status);
|
|
257
|
+
}
|
|
258
|
+
function normalizeGitNameList(output) {
|
|
259
|
+
return output
|
|
260
|
+
.split("\n")
|
|
261
|
+
.map((line) => line.trim().replace(/\\/g, "/"))
|
|
262
|
+
.filter(Boolean);
|
|
263
|
+
}
|
|
264
|
+
function gitSemanticDiffNames(projectRoot, paths) {
|
|
265
|
+
if (paths.length === 0)
|
|
266
|
+
return [];
|
|
267
|
+
try {
|
|
268
|
+
const args = ["diff", "--ignore-cr-at-eol", "--name-only", "--", ...paths];
|
|
269
|
+
const worktree = execFileSync("git", args, {
|
|
270
|
+
cwd: projectRoot,
|
|
271
|
+
encoding: "utf8",
|
|
272
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
273
|
+
});
|
|
274
|
+
const cached = execFileSync("git", ["diff", "--cached", "--ignore-cr-at-eol", "--name-only", "--", ...paths], {
|
|
275
|
+
cwd: projectRoot,
|
|
276
|
+
encoding: "utf8",
|
|
277
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
278
|
+
});
|
|
279
|
+
return [...new Set([...normalizeGitNameList(worktree), ...normalizeGitNameList(cached)])];
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
252
285
|
/** Framework-managed uncommitted paths after refresh (#1671). */
|
|
253
|
-
export function frameworkRefreshSideEffects(projectDir,
|
|
286
|
+
export function frameworkRefreshSideEffects(projectDir, options) {
|
|
287
|
+
const readPorcelain = options?.readPorcelain ?? gitPorcelain;
|
|
288
|
+
const readSemanticDiffNames = options?.readSemanticDiffNames ?? gitSemanticDiffNames;
|
|
254
289
|
const porcelain = readPorcelain(projectDir);
|
|
255
290
|
if (porcelain === null)
|
|
256
|
-
return [];
|
|
257
|
-
const changed =
|
|
258
|
-
const { core, installerManaged } =
|
|
259
|
-
const
|
|
260
|
-
|
|
291
|
+
return { files: [], crlfOnlyCoreFiles: [] };
|
|
292
|
+
const changed = porcelainStatusEntries(porcelain);
|
|
293
|
+
const { core, installerManaged } = classifyChangedEntries(changed);
|
|
294
|
+
const semanticCoreNames = core.length > 0
|
|
295
|
+
? readSemanticDiffNames(projectDir, core.map((entry) => entry.path))
|
|
296
|
+
: [];
|
|
297
|
+
const semanticCoreSet = semanticCoreNames === null ? null : new Set(semanticCoreNames);
|
|
298
|
+
const coreFiles = [];
|
|
299
|
+
const crlfOnlyCoreFiles = [];
|
|
300
|
+
for (const entry of core) {
|
|
301
|
+
if (semanticCoreSet !== null &&
|
|
302
|
+
isCrlfNoiseCandidate(entry.status) &&
|
|
303
|
+
!semanticCoreSet.has(entry.path)) {
|
|
304
|
+
crlfOnlyCoreFiles.push(entry.path);
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
coreFiles.push(entry.path);
|
|
308
|
+
}
|
|
309
|
+
const files = [...coreFiles, ...installerManaged.map((entry) => entry.path)].sort();
|
|
310
|
+
return { files, crlfOnlyCoreFiles: crlfOnlyCoreFiles.sort() };
|
|
261
311
|
}
|
|
262
|
-
export function printRefreshSideEffects(io,
|
|
263
|
-
if (
|
|
312
|
+
export function printRefreshSideEffects(io, effects) {
|
|
313
|
+
if (effects.crlfOnlyCoreFiles.length > 0) {
|
|
314
|
+
io.printf("\nWindows line-ending note (#2118): suppressed .deft/core CRLF/LF-only noise; " +
|
|
315
|
+
"ensure .gitattributes contains `.deft/core/** text eol=lf`.\n");
|
|
316
|
+
}
|
|
317
|
+
if (effects.files.length === 0)
|
|
318
|
+
return;
|
|
319
|
+
if (effects.payloadSwapped === false) {
|
|
320
|
+
io.printf("\nFramework-managed files still have semantic uncommitted changes:\n");
|
|
321
|
+
for (const file of effects.files) {
|
|
322
|
+
io.printf(` ${file}\n`);
|
|
323
|
+
}
|
|
264
324
|
return;
|
|
325
|
+
}
|
|
265
326
|
io.printf("\nAGENTS.md refresh side effects (#1671): the refresh and framework payload swap\n");
|
|
266
327
|
io.printf("left these framework files with uncommitted changes -- they belong in the\n");
|
|
267
328
|
io.printf("framework deposit commit (the installer stages them before printing the\n");
|
|
268
329
|
io.printf("`git add` list below, so there are no post-stage stragglers):\n");
|
|
269
|
-
for (const file of files) {
|
|
330
|
+
for (const file of effects.files) {
|
|
270
331
|
io.printf(` ${file}\n`);
|
|
271
332
|
}
|
|
272
333
|
}
|
|
@@ -307,7 +368,8 @@ export function buildUpdateSummaryJson(result, options, updateState) {
|
|
|
307
368
|
user_config_dir: "",
|
|
308
369
|
skills_created: false,
|
|
309
370
|
payload_layout: "vendored",
|
|
310
|
-
strategy:
|
|
371
|
+
strategy: result.strategy,
|
|
372
|
+
already_current: result.alreadyCurrent,
|
|
311
373
|
dirty_tree: false,
|
|
312
374
|
dirty_files: [],
|
|
313
375
|
staged_paths: result.stagedPaths,
|
|
@@ -319,9 +381,12 @@ export function buildUpdateSummaryJson(result, options, updateState) {
|
|
|
319
381
|
};
|
|
320
382
|
}
|
|
321
383
|
export function printUpdateComplete(result, io, updateState) {
|
|
322
|
-
io.printf(
|
|
384
|
+
io.printf(result.alreadyCurrent
|
|
385
|
+
? "\nOK Deft framework payload already current.\n\n"
|
|
386
|
+
: "\nOK Deft framework payload refreshed.\n\n");
|
|
323
387
|
io.printf(` Location : ${result.deftDir}\n`);
|
|
324
388
|
io.printf(` Content : v${normalizeVersion(result.contentVersion)}\n`);
|
|
389
|
+
io.printf(` Strategy : ${result.strategy}\n`);
|
|
325
390
|
if (updateState) {
|
|
326
391
|
io.printf(` State : ${updateState}\n`);
|
|
327
392
|
}
|
|
@@ -356,31 +421,41 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
356
421
|
const engineVersion = readEngine();
|
|
357
422
|
const contentVersion = readContentPackageVersion(contentRoot, readPackageVersion);
|
|
358
423
|
const versionSkewNotice = buildVersionSkewNotice(engineVersion, contentVersion, previousDepositVersion);
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
424
|
+
const alreadyCurrent = previousDepositVersion !== null &&
|
|
425
|
+
normalizeVersion(previousDepositVersion) === normalizeVersion(contentVersion);
|
|
426
|
+
const strategy = alreadyCurrent ? "no-op" : "file-swap";
|
|
427
|
+
if (alreadyCurrent) {
|
|
428
|
+
io.printf("[deft update] Framework payload already current; skipping payload copy.\n");
|
|
429
|
+
migrateLegacyInstallManifest(projectDir, join(deftDir, "VERSION"));
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
await copyContent(contentRoot, deftDir);
|
|
433
|
+
await prunePythonArtifactsFromDeposit(deftDir, projectDir, io);
|
|
434
|
+
// #2347: prune framework-source paths that the content package does not ship.
|
|
435
|
+
// The additive file-swap never removes them, causing the deposit-hygiene
|
|
436
|
+
// advisory to persist across every upgrade until manually cleaned.
|
|
437
|
+
await pruneStrayDepositPaths(deftDir, contentRoot, io);
|
|
438
|
+
const nowIso = seams.nowIso ?? (() => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"));
|
|
439
|
+
const manifestFields = {
|
|
440
|
+
ref: contentVersion.startsWith("v") ? contentVersion : `v${contentVersion}`,
|
|
441
|
+
sha: "content-package",
|
|
442
|
+
tag: contentVersion.startsWith("v") ? contentVersion : `v${contentVersion}`,
|
|
443
|
+
installRoot: CANONICAL_INSTALL_ROOT,
|
|
444
|
+
fetchedAt: nowIso(),
|
|
445
|
+
fetchedBy: "directive-update",
|
|
446
|
+
...(previousManagedBy ? { managedBy: previousManagedBy } : {}),
|
|
447
|
+
};
|
|
448
|
+
const writtenManifestPath = writeInstallManifest(projectDir, deftDir, manifestFields);
|
|
449
|
+
// #2064: retire a stale legacy .deft/VERSION now that the canonical
|
|
450
|
+
// .deft/core/VERSION has been rewritten (folded in from install-upgrade so no
|
|
451
|
+
// manifest behavior is lost by the redirect). Best-effort; never fatal.
|
|
452
|
+
migrateLegacyInstallManifest(projectDir, writtenManifestPath);
|
|
453
|
+
// #2055: regenerate the bare .deft-version derivative so it agrees with the
|
|
454
|
+
// freshly written manifest tag (otherwise doctor's manifest-agreement check
|
|
455
|
+
// fails and the operator must hand-edit the marker). No-op refreshes skip
|
|
456
|
+
// this projection because the manifest tag was not rewritten (#2118).
|
|
457
|
+
syncBareVersionMarker(projectDir, contentVersion);
|
|
458
|
+
}
|
|
384
459
|
const agentsMdUpdated = writeAgentsMd(projectDir, deftDir, io);
|
|
385
460
|
// #2148: the deft-core-guard CI workflow is only meaningful when the deposit
|
|
386
461
|
// is git-tracked (committed vendor layout). On an npm-managed (gitignored)
|
|
@@ -399,12 +474,27 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
399
474
|
if (args.nonInteractive) {
|
|
400
475
|
taskfileWired = ensureTaskfile(projectDir, io);
|
|
401
476
|
}
|
|
402
|
-
const { stagePaths, staged, stagedPaths } = depositStagePaths(projectDir, {
|
|
403
|
-
includeTaskfile: taskfileWired,
|
|
404
|
-
});
|
|
405
|
-
printCommitGuidance(io, stagePaths, staged);
|
|
406
477
|
const readPorcelain = seams.gitPorcelain ?? gitPorcelain;
|
|
407
|
-
|
|
478
|
+
const effects = frameworkRefreshSideEffects(projectDir, {
|
|
479
|
+
readPorcelain,
|
|
480
|
+
readSemanticDiffNames: seams.gitSemanticDiffNames ?? gitSemanticDiffNames,
|
|
481
|
+
});
|
|
482
|
+
let stagedPaths = [];
|
|
483
|
+
if (!alreadyCurrent || effects.files.length > 0) {
|
|
484
|
+
const stagedResult = depositStagePaths(projectDir, {
|
|
485
|
+
includeTaskfile: taskfileWired,
|
|
486
|
+
includeCore: !alreadyCurrent,
|
|
487
|
+
});
|
|
488
|
+
stagedPaths = stagedResult.stagedPaths;
|
|
489
|
+
if (!alreadyCurrent) {
|
|
490
|
+
printCommitGuidance(io, stagedResult.stagePaths, stagedResult.staged);
|
|
491
|
+
}
|
|
492
|
+
else if (stagedResult.stagedPaths.length > 0) {
|
|
493
|
+
io.printf("\nUpdated installer-managed projections for the current framework deposit:\n");
|
|
494
|
+
io.printf(` git add ${stagedResult.stagedPaths.join(" ")}\n`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
printRefreshSideEffects(io, { ...effects, payloadSwapped: !alreadyCurrent });
|
|
408
498
|
if (versionSkewNotice) {
|
|
409
499
|
io.printf(`${versionSkewNotice}\n`);
|
|
410
500
|
}
|
|
@@ -414,6 +504,8 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
414
504
|
contentVersion,
|
|
415
505
|
engineVersion,
|
|
416
506
|
previousDepositVersion,
|
|
507
|
+
alreadyCurrent,
|
|
508
|
+
strategy,
|
|
417
509
|
agentsMdUpdated,
|
|
418
510
|
versionSkewNotice,
|
|
419
511
|
legacyLayout: false,
|
|
@@ -515,7 +607,9 @@ export async function runRefreshDepositCli(options) {
|
|
|
515
607
|
}
|
|
516
608
|
try {
|
|
517
609
|
const result = await runRefreshDeposit(options, io, options.seams);
|
|
518
|
-
const state =
|
|
610
|
+
const state = result.alreadyCurrent
|
|
611
|
+
? "current"
|
|
612
|
+
: classification?.state;
|
|
519
613
|
if (options.jsonOut) {
|
|
520
614
|
options.writeOut(`${JSON.stringify(buildUpdateSummaryJson(result, options, state), null, 2)}\n`);
|
|
521
615
|
printUpdateComplete(result, { printf: options.writeErr }, state);
|
|
@@ -22,6 +22,7 @@ const FRAMEWORK_SELF_TEST_REL = ".deft/core/tests";
|
|
|
22
22
|
const VENDORED_TS_PACKAGES_REL = ".deft/core/packages";
|
|
23
23
|
const VENDORED_TS_TEST_RE = /\.(test|spec)\.(c|m)?[jt]sx?$/i;
|
|
24
24
|
const CORE_GITATTRIBUTES_LINES = [
|
|
25
|
+
`${CORE_GLOB} text eol=lf`,
|
|
25
26
|
`${CORE_GLOB} linguist-generated=true`,
|
|
26
27
|
`${CORE_GLOB} linguist-vendored=true`,
|
|
27
28
|
];
|
|
@@ -584,7 +585,7 @@ export function ensureGitattributes(projectDir, io) {
|
|
|
584
585
|
const present = new Set(existing.split("\n").map((line) => line.trim()));
|
|
585
586
|
const additions = CORE_GITATTRIBUTES_LINES.filter((line) => !present.has(line));
|
|
586
587
|
if (additions.length === 0) {
|
|
587
|
-
io.printf(`.gitattributes already marks ${CORE_GLOB} as generated/vendored — skipping.\n`);
|
|
588
|
+
io.printf(`.gitattributes already marks ${CORE_GLOB} as LF-pinned/generated/vendored — skipping.\n`);
|
|
588
589
|
return false;
|
|
589
590
|
}
|
|
590
591
|
let body = existing;
|
|
@@ -594,13 +595,13 @@ export function ensureGitattributes(projectDir, io) {
|
|
|
594
595
|
body += "\n";
|
|
595
596
|
body +=
|
|
596
597
|
"# Deft framework: the vendored payload is packaged framework code, not\n" +
|
|
597
|
-
"# consumer source.
|
|
598
|
-
"# diffs treat .deft/core/** as machine-managed (#1430).\n";
|
|
598
|
+
"# consumer source. Pin LF endings and mark it generated + vendored so\n" +
|
|
599
|
+
"# Git does not rewrite it and diffs treat .deft/core/** as machine-managed (#1430, #2118).\n";
|
|
599
600
|
for (const add of additions) {
|
|
600
601
|
body += `${add}\n`;
|
|
601
602
|
}
|
|
602
603
|
writeFileSync(path, body, "utf8");
|
|
603
|
-
io.printf(`.gitattributes updated with
|
|
604
|
+
io.printf(`.gitattributes updated with Deft core markers: ${additions.join(", ")}\n`);
|
|
604
605
|
return true;
|
|
605
606
|
}
|
|
606
607
|
function greptilePatternPresent(patterns, glob) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PolicyResult } from "./resolve.js";
|
|
2
|
+
export { policyColonInvocation, policySetInvocation } from "./policy-invocation.js";
|
|
2
3
|
/** One-liner disclosure phrasing for AGENTS.md / setup interview echo. */
|
|
3
4
|
export declare function disclosureLine(result: PolicyResult): string;
|
|
4
5
|
//# sourceMappingURL=disclosure.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ENV_BYPASS } from "./resolve.js";
|
|
2
|
+
export { policyColonInvocation, policySetInvocation } from "./policy-invocation.js";
|
|
2
3
|
/** One-liner disclosure phrasing for AGENTS.md / setup interview echo. */
|
|
3
4
|
export function disclosureLine(result) {
|
|
4
5
|
if (result.allowDirectCommits) {
|
package/dist/policy/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * from "./capacity.js";
|
|
|
4
4
|
export * from "./decisions.js";
|
|
5
5
|
export * from "./disclosure.js";
|
|
6
6
|
export * from "./plan-extensions.js";
|
|
7
|
+
export * from "./policy-invocation.js";
|
|
7
8
|
export * from "./resolve.js";
|
|
8
9
|
export * from "./value-feedback.js";
|
|
9
10
|
export * from "./wip.js";
|
package/dist/policy/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export * from "./capacity.js";
|
|
|
8
8
|
export * from "./decisions.js";
|
|
9
9
|
export * from "./disclosure.js";
|
|
10
10
|
export * from "./plan-extensions.js";
|
|
11
|
+
export * from "./policy-invocation.js";
|
|
11
12
|
export * from "./resolve.js";
|
|
12
13
|
export * from "./value-feedback.js";
|
|
13
14
|
export * from "./wip.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** User-facing colon-form policy verb for consumer `deft` disclosures (#2367). */
|
|
2
|
+
export declare function policyColonInvocation(subcommand: string, trailing?: string): string;
|
|
3
|
+
/** User-facing `deft policy set <cmd>` form for policy-set disclosures (#2367). */
|
|
4
|
+
export declare function policySetInvocation(subcommand: string, trailing?: string): string;
|
|
5
|
+
//# sourceMappingURL=policy-invocation.d.ts.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** User-facing colon-form policy verb for consumer `deft` disclosures (#2367). */
|
|
2
|
+
export function policyColonInvocation(subcommand, trailing = "") {
|
|
3
|
+
return `deft policy:${subcommand}${trailing}`;
|
|
4
|
+
}
|
|
5
|
+
/** User-facing `deft policy set <cmd>` form for policy-set disclosures (#2367). */
|
|
6
|
+
export function policySetInvocation(subcommand, trailing = "") {
|
|
7
|
+
return `deft policy set ${subcommand}${trailing}`;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=policy-invocation.js.map
|
package/dist/policy/resolve.js
CHANGED
|
@@ -3,6 +3,7 @@ import { join, resolve as pathResolve } from "node:path";
|
|
|
3
3
|
import { resolveProjectDefinitionPath } from "../layout/resolve.js";
|
|
4
4
|
import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
|
|
5
5
|
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
|
|
6
|
+
import { policyColonInvocation } from "./policy-invocation.js";
|
|
6
7
|
/** Filesystem-relative location of the project-definition xBRIEF (display/back-compat). */
|
|
7
8
|
export const PROJECT_DEFINITION_REL_PATH = "xbrief/PROJECT-DEFINITION.xbrief.json";
|
|
8
9
|
/** Environment variable emergency bypass for branch protection (#747). */
|
|
@@ -154,8 +155,9 @@ export function resolvePolicy(projectRoot) {
|
|
|
154
155
|
const warn = `DEPRECATED: PROJECT-DEFINITION uses the legacy narrative key ` +
|
|
155
156
|
`'${LEGACY_NARRATIVE_KEY}' (${pythonRepr(raw)}). Migrate to typed ` +
|
|
156
157
|
`plan.policy.allowDirectCommitsToMaster (#746). Run ` +
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
`\`${policyColonInvocation("enforce-branches")}\` or ` +
|
|
159
|
+
`\`${policyColonInvocation("allow-direct-commits", " -- --confirm")}\` ` +
|
|
160
|
+
`to set the typed flag explicitly.`;
|
|
159
161
|
return {
|
|
160
162
|
allowDirectCommits: allow,
|
|
161
163
|
source: "legacy-narrative",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
|
|
3
3
|
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
|
|
4
|
+
import { policyColonInvocation } from "./policy-invocation.js";
|
|
4
5
|
import { appendAuditLog, loadProjectDefinition, projectDefinitionPath } from "./resolve.js";
|
|
5
6
|
import { isTrustedOrgAutoEnable } from "./value-feedback-autoenable.js";
|
|
6
7
|
/** Canonical registered policy field name (matches other FIELD_* dotted paths). */
|
|
@@ -21,7 +22,9 @@ export const VALUE_FEEDBACK_CAPABILITY_COST_DISCLOSURE = "\u26a0 Capability-cost
|
|
|
21
22
|
" \u2022 A budgeted session one-liner may appear when concrete attributed value exists.\n" +
|
|
22
23
|
" \u2022 Upstream gap-escalation prompts stay OFF unless you explicitly enable " +
|
|
23
24
|
"`upstreamPrompt` (GitHub attention + token cost).\n" +
|
|
24
|
-
" \u2022 Inspect current state: `
|
|
25
|
+
" \u2022 Inspect current state: `" +
|
|
26
|
+
policyColonInvocation("show", " --field=valueFeedback") +
|
|
27
|
+
"`.\n" +
|
|
25
28
|
" \u2022 Reversible: set `enabled: false` under the typed policy block in PROJECT-DEFINITION.\n" +
|
|
26
29
|
" \u2022 Changes are recorded to meta/policy-changes.log for auditability.";
|
|
27
30
|
function defaultResolved(source, error = null) {
|
|
@@ -202,7 +205,7 @@ export function enableValueFeedback(projectRoot, options) {
|
|
|
202
205
|
return {
|
|
203
206
|
exitCode: 1,
|
|
204
207
|
stdout: `${VALUE_FEEDBACK_CAPABILITY_COST_DISCLOSURE}\n\n` +
|
|
205
|
-
|
|
208
|
+
`Re-run with --confirm to apply: ${policyColonInvocation("enable-value-feedback", " -- --confirm")}\n`,
|
|
206
209
|
changed: false,
|
|
207
210
|
};
|
|
208
211
|
}
|
|
@@ -265,7 +268,7 @@ export function enableValueFeedback(projectRoot, options) {
|
|
|
265
268
|
if (changedFlag) {
|
|
266
269
|
atomicWriteProjectDefinition(path, data);
|
|
267
270
|
}
|
|
268
|
-
const actor = options.actor ?? "
|
|
271
|
+
const actor = options.actor ?? policyColonInvocation("enable-value-feedback");
|
|
269
272
|
const note = options.note ?? "";
|
|
270
273
|
const parts = [
|
|
271
274
|
`actor=${actor}`,
|
|
@@ -7,7 +7,7 @@ export declare const EXIT_UNCLEAN = 1;
|
|
|
7
7
|
export declare const EXIT_EXTERNAL_ERROR = 2;
|
|
8
8
|
export declare const DEFAULT_BASE_BRANCH = "master";
|
|
9
9
|
export declare const LEAF_CODING_WORKER_ROLE = "leaf-implementation";
|
|
10
|
-
export declare const SUBAGENT_BACKEND_SET_CMD
|
|
10
|
+
export declare const SUBAGENT_BACKEND_SET_CMD: string;
|
|
11
11
|
export declare const GATE_ADVISE = "advise";
|
|
12
12
|
export declare const GATE_ENFORCE = "enforce";
|
|
13
13
|
export declare const READY = "ready";
|
package/dist/swarm/constants.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { policySetInvocation } from "../policy/policy-invocation.js";
|
|
1
2
|
export const EXIT_OK = 0;
|
|
2
3
|
export const EXIT_GATE_FAILED = 1;
|
|
3
4
|
export const EXIT_CONFIG_ERROR = 2;
|
|
@@ -7,7 +8,7 @@ export const EXIT_UNCLEAN = 1;
|
|
|
7
8
|
export const EXIT_EXTERNAL_ERROR = 2;
|
|
8
9
|
export const DEFAULT_BASE_BRANCH = "master";
|
|
9
10
|
export const LEAF_CODING_WORKER_ROLE = "leaf-implementation";
|
|
10
|
-
export const SUBAGENT_BACKEND_SET_CMD = "
|
|
11
|
+
export const SUBAGENT_BACKEND_SET_CMD = `${policySetInvocation("subagent-backend", " -- --set {backend_id}")}`;
|
|
11
12
|
export const GATE_ADVISE = "advise";
|
|
12
13
|
export const GATE_ENFORCE = "enforce";
|
|
13
14
|
export const READY = "ready";
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* @see {@link https://github.com/deftai/directive/issues/1860} Hard deletion tracking
|
|
13
13
|
*/
|
|
14
14
|
import { readPlanPolicy } from "../policy/plan-extensions.js";
|
|
15
|
+
import { policySetInvocation } from "../policy/policy-invocation.js";
|
|
15
16
|
import { loadProjectDefinition } from "../policy/resolve.js";
|
|
16
17
|
import { LEAF_CODING_WORKER_ROLE, SUBAGENT_BACKEND_SET_CMD } from "./constants.js";
|
|
17
18
|
const TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
@@ -132,7 +133,7 @@ export function enforceSubagentBackendPolicy(projectRoot, environ = process.env)
|
|
|
132
133
|
error: `${detail}\n` +
|
|
133
134
|
"Select a coding sub-agent backend before headless dispatch:\n" +
|
|
134
135
|
`${listing}\n` +
|
|
135
|
-
|
|
136
|
+
`Probe harness availability: ${policySetInvocation("subagent-backends")}\n` +
|
|
136
137
|
`Persist a choice: ${SUBAGENT_BACKEND_SET_CMD.replace("{backend_id}", "<id>")}`,
|
|
137
138
|
};
|
|
138
139
|
}
|
|
@@ -25,6 +25,8 @@ export declare const SKIP_NOTICE: string;
|
|
|
25
25
|
/** Result of a single lane command invocation. Mirrors a subset of SpawnSyncReturns. */
|
|
26
26
|
export interface RunnerResult {
|
|
27
27
|
readonly status: number | null;
|
|
28
|
+
readonly signal?: NodeJS.Signals | null;
|
|
29
|
+
readonly error?: Error;
|
|
28
30
|
}
|
|
29
31
|
export type LaneRunner = (argv: readonly string[], cwd: string) => RunnerResult;
|
|
30
32
|
export interface RunTsLaneOptions {
|
|
@@ -35,6 +37,8 @@ export interface RunTsLaneOptions {
|
|
|
35
37
|
/** Injected sink for human-facing notices (defaults to stdout). */
|
|
36
38
|
readonly out?: (message: string) => void;
|
|
37
39
|
}
|
|
40
|
+
/** Windows command shims (.cmd/.bat) need a shell; native executables do not. */
|
|
41
|
+
export declare function shouldUseShellForCommand(command: string, platform?: NodeJS.Platform): boolean;
|
|
38
42
|
export interface ResolvePnpmOptions {
|
|
39
43
|
readonly env?: NodeJS.ProcessEnv;
|
|
40
44
|
readonly platform?: NodeJS.Platform;
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { spawnSync } from "node:child_process";
|
|
20
20
|
import { existsSync } from "node:fs";
|
|
21
|
-
import {
|
|
21
|
+
import { posix, win32 } from "node:path";
|
|
22
22
|
/**
|
|
23
23
|
* Run order is deliberate: lint (cheapest, catches the biome class first),
|
|
24
24
|
* then build, then the test suite.
|
|
@@ -31,11 +31,20 @@ export const LANE_COMMANDS = [
|
|
|
31
31
|
export const SKIP_NOTICE = "[ts:check-lane] pnpm not found on PATH -- skipping the TypeScript lane " +
|
|
32
32
|
"(build/lint/test). The TS engine stays validated by the dedicated CI job. " +
|
|
33
33
|
"Install the Node toolchain (pnpm) to run the TS lane locally.";
|
|
34
|
-
/**
|
|
34
|
+
/** Windows command shims (.cmd/.bat) need a shell; native executables do not. */
|
|
35
|
+
export function shouldUseShellForCommand(command, platform = process.platform) {
|
|
36
|
+
return platform === "win32" && /\.(?:cmd|bat)$/i.test(command);
|
|
37
|
+
}
|
|
38
|
+
/** Default runner: an inherited-stdio pnpm invocation. */
|
|
35
39
|
function defaultRunner(argv, cwd) {
|
|
36
40
|
const [command, ...rest] = argv;
|
|
37
|
-
const
|
|
38
|
-
|
|
41
|
+
const commandPath = command ?? "";
|
|
42
|
+
const result = spawnSync(commandPath, rest, {
|
|
43
|
+
cwd,
|
|
44
|
+
stdio: "inherit",
|
|
45
|
+
shell: shouldUseShellForCommand(commandPath),
|
|
46
|
+
});
|
|
47
|
+
return { error: result.error, signal: result.signal, status: result.status };
|
|
39
48
|
}
|
|
40
49
|
/** Resolve the pnpm executable path, or null when it is not installed. */
|
|
41
50
|
export function resolvePnpm(options = {}) {
|
|
@@ -49,11 +58,12 @@ export function resolvePnpm(options = {}) {
|
|
|
49
58
|
const isWindows = platform === "win32";
|
|
50
59
|
const exts = isWindows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";") : [""];
|
|
51
60
|
const sep = isWindows ? ";" : ":";
|
|
61
|
+
const joinPath = isWindows ? win32.join : posix.join;
|
|
52
62
|
for (const dir of pathValue.split(sep)) {
|
|
53
63
|
if (dir === "")
|
|
54
64
|
continue;
|
|
55
65
|
for (const ext of exts) {
|
|
56
|
-
const candidate =
|
|
66
|
+
const candidate = joinPath(dir, `pnpm${ext}`);
|
|
57
67
|
if (exists(candidate)) {
|
|
58
68
|
return candidate;
|
|
59
69
|
}
|
|
@@ -85,7 +95,11 @@ export function runTsLane(projectRoot, options) {
|
|
|
85
95
|
// failure -- this mirrors the Python oracle, whose returncode is negative
|
|
86
96
|
// (non-zero) for a signal-killed process.
|
|
87
97
|
if (code === null) {
|
|
88
|
-
|
|
98
|
+
if (result.error) {
|
|
99
|
+
out(`[ts:check-lane] \`pnpm ${command.join(" ")}\` failed to start: ${result.error.message}`);
|
|
100
|
+
return 1;
|
|
101
|
+
}
|
|
102
|
+
out(`[ts:check-lane] \`pnpm ${command.join(" ")}\` was killed by ${result.signal ?? "a signal"} before exit -- treating as failure.`);
|
|
89
103
|
return 1;
|
|
90
104
|
}
|
|
91
105
|
if (code !== 0) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { isFrameworkRepoRoot } from "../check/orchestrator.js";
|
|
4
|
+
import { policyColonInvocation } from "../policy/policy-invocation.js";
|
|
4
5
|
import { isValueFeedbackPathAllowed, resolveValueFeedback } from "../policy/value-feedback.js";
|
|
5
6
|
import { GhRestError, restCreateIssue, restIssueListPaginated, } from "../scm/gh-rest.js";
|
|
6
7
|
import { resolveProjectRoot } from "../scope/project-context.js";
|
|
@@ -146,8 +147,8 @@ export function runFeedbackFile(options) {
|
|
|
146
147
|
body,
|
|
147
148
|
repo,
|
|
148
149
|
message: "[deft feedback] Blocked: plan.policy.valueFeedback upstreamPrompt is OFF. " +
|
|
149
|
-
|
|
150
|
-
|
|
150
|
+
`Enable with \`${policyColonInvocation("enable-value-feedback", " -- --confirm")}\` and set upstreamPrompt, ` +
|
|
151
|
+
`or inspect via \`${policyColonInvocation("show", " --field=valueFeedback")}\`.\n`,
|
|
151
152
|
};
|
|
152
153
|
}
|
|
153
154
|
const offline = isOffline();
|
package/dist/value/readback.js
CHANGED
|
@@ -3,6 +3,7 @@ import { join, resolve } from "node:path";
|
|
|
3
3
|
import { runningInsideDeftRepo } from "../doctor/paths.js";
|
|
4
4
|
import { ALL_ATTRIBUTION_EVENT_NAMES } from "../events/attribution-constants.js";
|
|
5
5
|
import { DEFAULT_EVENT_LOG, readEvents } from "../lifecycle/events.js";
|
|
6
|
+
import { policyColonInvocation } from "../policy/policy-invocation.js";
|
|
6
7
|
import { isValueFeedbackPathAllowed, resolveValueFeedback, } from "../policy/value-feedback.js";
|
|
7
8
|
import { resolveProjectRoot } from "../scope/project-context.js";
|
|
8
9
|
import { MAX_LINE_CHARS } from "../triage/welcome/constants.js";
|
|
@@ -364,7 +365,7 @@ export function formatValueShowReport(trend) {
|
|
|
364
365
|
if (trend.total === 0) {
|
|
365
366
|
return (`[value] No attributed signals in the last ${trend.windowLabel} ` +
|
|
366
367
|
"(ledger empty for this window).\n" +
|
|
367
|
-
|
|
368
|
+
`Inspect policy: \`${policyColonInvocation("show", " --field=valueFeedback")}\`.\n`);
|
|
368
369
|
}
|
|
369
370
|
const classParts = ["value", "bypass", "adoption", "friction"]
|
|
370
371
|
.filter((key) => trend.byClass[key] > 0)
|
|
@@ -409,7 +410,7 @@ export function runValueShow(options) {
|
|
|
409
410
|
gated: true,
|
|
410
411
|
empty: true,
|
|
411
412
|
text: "[value] Blocked: plan.policy.valueFeedback is OFF. " +
|
|
412
|
-
|
|
413
|
+
`Enable with \`${policyColonInvocation("enable-value-feedback", " -- --confirm")}\`.\n`,
|
|
413
414
|
trend: null,
|
|
414
415
|
};
|
|
415
416
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive-core",
|
|
3
|
-
"version": "0.73.
|
|
3
|
+
"version": "0.73.1",
|
|
4
4
|
"description": "TypeScript engine core for the Directive framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -281,8 +281,8 @@
|
|
|
281
281
|
"provenance": true
|
|
282
282
|
},
|
|
283
283
|
"dependencies": {
|
|
284
|
-
"@deftai/directive-content": "^0.73.
|
|
285
|
-
"@deftai/directive-types": "^0.73.
|
|
284
|
+
"@deftai/directive-content": "^0.73.1",
|
|
285
|
+
"@deftai/directive-types": "^0.73.1",
|
|
286
286
|
"archiver": "^8.0.0"
|
|
287
287
|
},
|
|
288
288
|
"scripts": {
|