@deftai/directive-core 0.68.1 → 0.69.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/doctor/constants.d.ts +9 -0
  2. package/dist/doctor/constants.js +11 -0
  3. package/dist/doctor/main.d.ts +47 -1
  4. package/dist/doctor/main.js +352 -2
  5. package/dist/doctor/payload-staleness.d.ts +8 -0
  6. package/dist/doctor/payload-staleness.js +11 -8
  7. package/dist/doctor/types.d.ts +48 -0
  8. package/dist/index.d.ts +2 -0
  9. package/dist/index.js +2 -0
  10. package/dist/init-deposit/gitignore.d.ts +18 -0
  11. package/dist/init-deposit/gitignore.js +94 -15
  12. package/dist/init-deposit/headless-manifest.d.ts +87 -0
  13. package/dist/init-deposit/headless-manifest.js +261 -0
  14. package/dist/init-deposit/index.d.ts +3 -0
  15. package/dist/init-deposit/index.js +3 -0
  16. package/dist/init-deposit/init-dispatch.d.ts +90 -0
  17. package/dist/init-deposit/init-dispatch.js +170 -0
  18. package/dist/init-deposit/refresh.d.ts +79 -2
  19. package/dist/init-deposit/refresh.js +177 -5
  20. package/dist/init-deposit/scaffold.d.ts +25 -0
  21. package/dist/init-deposit/scaffold.js +57 -0
  22. package/dist/init-deposit/untrack-core.d.ts +84 -0
  23. package/dist/init-deposit/untrack-core.js +150 -0
  24. package/dist/resolution/classify.d.ts +41 -0
  25. package/dist/resolution/classify.js +138 -0
  26. package/dist/resolution/engine-ladder.d.ts +98 -0
  27. package/dist/resolution/engine-ladder.js +185 -0
  28. package/dist/resolution/index.d.ts +19 -0
  29. package/dist/resolution/index.js +19 -0
  30. package/dist/resolution/integrity.d.ts +48 -0
  31. package/dist/resolution/integrity.js +80 -0
  32. package/dist/resolution/package-manager.d.ts +77 -0
  33. package/dist/resolution/package-manager.js +103 -0
  34. package/dist/resolution/pin.d.ts +73 -0
  35. package/dist/resolution/pin.js +169 -0
  36. package/dist/resolution/plan.d.ts +66 -0
  37. package/dist/resolution/plan.js +219 -0
  38. package/dist/resolution/skew-policy.d.ts +46 -0
  39. package/dist/resolution/skew-policy.js +120 -0
  40. package/dist/session/session-start.d.ts +2 -0
  41. package/dist/session/session-start.js +28 -1
  42. package/dist/user-config/index.d.ts +2 -0
  43. package/dist/user-config/index.js +2 -0
  44. package/dist/user-config/resolve-user-md.d.ts +76 -0
  45. package/dist/user-config/resolve-user-md.js +120 -0
  46. package/dist/xbrief-migrate/constants.d.ts +10 -0
  47. package/dist/xbrief-migrate/constants.js +19 -0
  48. package/dist/xbrief-migrate/detect.d.ts +34 -0
  49. package/dist/xbrief-migrate/detect.js +33 -0
  50. package/dist/xbrief-migrate/fs-helpers.d.ts +13 -0
  51. package/dist/xbrief-migrate/fs-helpers.js +46 -1
  52. package/dist/xbrief-migrate/index.d.ts +4 -2
  53. package/dist/xbrief-migrate/index.js +4 -2
  54. package/dist/xbrief-migrate/migrate-project.d.ts +23 -1
  55. package/dist/xbrief-migrate/migrate-project.js +97 -8
  56. package/dist/xbrief-migrate/signpost.d.ts +7 -1
  57. package/dist/xbrief-migrate/signpost.js +18 -3
  58. package/package.json +7 -3
@@ -3,9 +3,9 @@ import { dirname, join, relative, resolve } from "node:path";
3
3
  import { checkGitClean } from "../migrate-preflight/index.js";
4
4
  import { agentsRefreshPlan } from "../platform/agents-md.js";
5
5
  import { patchAgentsMdHeader, renderHeaderPatchSummary } from "./agents-header.js";
6
- import { LEGACY_ARTIFACT_DIR, LEGACY_ARTIFACT_SUFFIX, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, } from "./constants.js";
7
- import { detectLegacyVbriefLayout } from "./detect.js";
8
- import { isDirectory } from "./fs-helpers.js";
6
+ import { LEGACY_ARTIFACT_DIR, LEGACY_ARTIFACT_SUFFIX, MIGRATED_ARTIFACT_DIR, MIGRATED_ARTIFACT_SUFFIX, VBRIEF_DEPRECATION_MARKER_BODY, VBRIEF_DEPRECATION_MARKER_FILENAME, } from "./constants.js";
7
+ import { detectLegacyVbriefLayout, detectXbriefConvergence } from "./detect.js";
8
+ import { hasVbriefDeprecationMarker, isDirectory, isEffectivelyEmptyDir } from "./fs-helpers.js";
9
9
  import { renderXbriefMigrationLine, xbriefMigrationGuidance } from "./signpost.js";
10
10
  import { rewriteEmbeddedTokens, transformArtifactV06ToV08Transactional } from "./transforms.js";
11
11
  function collectFiles(root, acc = []) {
@@ -52,7 +52,7 @@ function backupLegacyTree(projectRoot, legacyDir) {
52
52
  cpSync(legacyDir, join(backupRoot, LEGACY_ARTIFACT_DIR), { recursive: true });
53
53
  return backupRoot;
54
54
  }
55
- function migrateLegacyTree(projectRoot, legacyDir) {
55
+ function migrateLegacyTree(projectRoot, legacyDir, options) {
56
56
  const migratedDir = join(projectRoot, MIGRATED_ARTIFACT_DIR);
57
57
  if (existsSync(migratedDir)) {
58
58
  throw new Error(`refusing to migrate: '${MIGRATED_ARTIFACT_DIR}/' already exists alongside '${LEGACY_ARTIFACT_DIR}/'`);
@@ -71,7 +71,15 @@ function migrateLegacyTree(projectRoot, legacyDir) {
71
71
  writeMigratedFile(srcPath, destPath);
72
72
  }
73
73
  renameOrReplace(stagedDir, migratedDir);
74
- rmSync(legacyDir, { recursive: true, force: true });
74
+ // Converge to a single unambiguous root: the fully-migrated legacy tree is
75
+ // either removed (default) or retained for read-compat behind an explicit
76
+ // deprecation marker so it never looks like an active source of truth (#2270).
77
+ if (options.keepLegacy) {
78
+ writeVbriefDeprecationMarker(legacyDir);
79
+ }
80
+ else {
81
+ rmSync(legacyDir, { recursive: true, force: true });
82
+ }
75
83
  return { backupDir, files: files.length };
76
84
  }
77
85
  catch (err) {
@@ -79,6 +87,36 @@ function migrateLegacyTree(projectRoot, legacyDir) {
79
87
  throw err;
80
88
  }
81
89
  }
90
+ /** Idempotently write the legacy-root deprecation marker (#2270). */
91
+ function writeVbriefDeprecationMarker(legacyDir) {
92
+ mkdirSync(legacyDir, { recursive: true });
93
+ if (hasVbriefDeprecationMarker(legacyDir)) {
94
+ return;
95
+ }
96
+ writeFileSync(join(legacyDir, VBRIEF_DEPRECATION_MARKER_FILENAME), VBRIEF_DEPRECATION_MARKER_BODY, "utf8");
97
+ }
98
+ /**
99
+ * Converge a leftover legacy `vbrief/` root to an unambiguous state (#2270).
100
+ * An effectively-empty tree is removed by default (single canonical root); a
101
+ * tree with real content — or an empty tree when `retain` is requested — is
102
+ * kept for read-compat behind an explicit deprecation marker. Idempotent: a
103
+ * missing dir or an already-marked dir is a no-op.
104
+ */
105
+ export function convergeLegacyVbriefRoot(projectRoot, options) {
106
+ const legacyDir = join(projectRoot, LEGACY_ARTIFACT_DIR);
107
+ if (!isDirectory(legacyDir)) {
108
+ return "removed";
109
+ }
110
+ if (hasVbriefDeprecationMarker(legacyDir)) {
111
+ return "marker";
112
+ }
113
+ if (isEffectivelyEmptyDir(legacyDir) && !options.retain) {
114
+ rmSync(legacyDir, { recursive: true, force: true });
115
+ return "removed";
116
+ }
117
+ writeVbriefDeprecationMarker(legacyDir);
118
+ return "marker";
119
+ }
82
120
  function renameOrReplace(src, dest) {
83
121
  if (existsSync(dest)) {
84
122
  rmSync(dest, { recursive: true, force: true });
@@ -107,9 +145,58 @@ function runAgentsRefresh(projectRoot, frameworkRoot, io) {
107
145
  io.writeOut(`AGENTS.md updated (state=${state}).\n`);
108
146
  return 0;
109
147
  }
110
- /** Core orchestrator for the consumer xbrief rename (#2110). */
148
+ /** Core orchestrator for the consumer xbrief rename (#2110) + convergence (#2270). */
111
149
  export function runXbriefMigration(args, _io) {
112
150
  const projectRoot = resolve(args.projectRoot);
151
+ const legacyDir = join(projectRoot, LEGACY_ARTIFACT_DIR);
152
+ const keepLegacy = args.keepLegacy ?? false;
153
+ const convergence = detectXbriefConvergence(projectRoot);
154
+ switch (convergence.state) {
155
+ // Already converged: legacy root is retained behind an explicit marker.
156
+ // Rerun is a pure no-op (no re-removal, no duplicate marker) — idempotent.
157
+ case "xbrief-marker":
158
+ return {
159
+ kind: "converged",
160
+ action: "marker",
161
+ already: true,
162
+ message: `Legacy '${LEGACY_ARTIFACT_DIR}/' already carries a deprecation marker — layout already converged.`,
163
+ };
164
+ // Ambiguous dual-empty root: a fully-migrated empty legacy tree. Converge to
165
+ // a single canonical root by removing it (or marking it when read-compat
166
+ // retention is requested); never leave two indistinguishable empty roots.
167
+ // Read-compat retention is only honored when a canonical xbrief/ actually
168
+ // has content — marking an empty legacy root with no canonical replacement
169
+ // would strand the project behind a marker that claims a migration that
170
+ // never happened, so that case always removes the stray empty root.
171
+ case "empty-vbrief": {
172
+ const retain = keepLegacy && convergence.xbriefHasContent;
173
+ const action = convergeLegacyVbriefRoot(projectRoot, { retain });
174
+ let message;
175
+ if (action === "removed") {
176
+ message = convergence.xbriefHasContent
177
+ ? `Converged layout: removed empty legacy '${LEGACY_ARTIFACT_DIR}/' — single '${MIGRATED_ARTIFACT_DIR}/' root.`
178
+ : `Converged layout: removed empty legacy '${LEGACY_ARTIFACT_DIR}/' — no canonical '${MIGRATED_ARTIFACT_DIR}/' root to migrate to yet.`;
179
+ }
180
+ else {
181
+ message = `Converged layout: wrote deprecation marker to legacy '${LEGACY_ARTIFACT_DIR}/' (retained for read-compat).`;
182
+ }
183
+ return { kind: "converged", action, already: false, message };
184
+ }
185
+ // Legacy content coexisting with a populated canonical xbrief/. We never
186
+ // destructively merge; converge non-destructively by marking the legacy
187
+ // tree deprecated so it no longer looks like an active source of truth.
188
+ case "dual-populated": {
189
+ convergeLegacyVbriefRoot(projectRoot, { retain: true });
190
+ return {
191
+ kind: "converged",
192
+ action: "marker",
193
+ already: false,
194
+ message: `Converged layout: '${MIGRATED_ARTIFACT_DIR}/' is canonical; wrote deprecation marker to legacy '${LEGACY_ARTIFACT_DIR}/' (retained for read-compat).`,
195
+ };
196
+ }
197
+ default:
198
+ break;
199
+ }
113
200
  const detection = detectLegacyVbriefLayout(projectRoot);
114
201
  if (!detection.legacyLayout) {
115
202
  return {
@@ -117,7 +204,6 @@ export function runXbriefMigration(args, _io) {
117
204
  message: "Project is already on the xbrief layout — nothing to migrate.",
118
205
  };
119
206
  }
120
- const legacyDir = join(projectRoot, LEGACY_ARTIFACT_DIR);
121
207
  if (!isDirectory(legacyDir)) {
122
208
  return {
123
209
  kind: "config",
@@ -134,7 +220,7 @@ export function runXbriefMigration(args, _io) {
134
220
  }
135
221
  }
136
222
  try {
137
- const { backupDir, files } = migrateLegacyTree(projectRoot, legacyDir);
223
+ const { backupDir, files } = migrateLegacyTree(projectRoot, legacyDir, { keepLegacy });
138
224
  return { kind: "migrated", backupDir, files };
139
225
  }
140
226
  catch (err) {
@@ -165,6 +251,9 @@ export function emitXbriefMigration(outcome, io, options = {}) {
165
251
  io.writeOut(`Migrated ${outcome.files} file(s) from ${LEGACY_ARTIFACT_DIR}/ to ${MIGRATED_ARTIFACT_DIR}/.\n` +
166
252
  `Backup written to ${outcome.backupDir}.\n`);
167
253
  return 0;
254
+ case "converged":
255
+ io.writeOut(`${outcome.message}\n`);
256
+ return 0;
168
257
  default: {
169
258
  const _exhaustive = outcome;
170
259
  return _exhaustive;
@@ -1,5 +1,11 @@
1
1
  /** Operator guidance for the TS-native xbrief rename (#2034 / #2110). */
2
2
  export declare function xbriefMigrationGuidance(): string;
3
- /** One-line doctor / ritual signpost mirroring `renderPrecutoverLine` (#2110). */
3
+ /**
4
+ * One-line doctor / ritual signpost mirroring `renderPrecutoverLine` (#2110),
5
+ * reporting an unambiguous convergence state (#2270). A migrated project reads
6
+ * as `xbrief active` plus either `vbrief legacy marker` or `vbrief removed`; a
7
+ * stray empty legacy root is called out as a pending convergence rather than a
8
+ * generic "run migrate" dead end — never a dual-empty-root ambiguity.
9
+ */
4
10
  export declare function renderXbriefMigrationLine(projectRoot: string): string;
5
11
  //# sourceMappingURL=signpost.d.ts.map
@@ -1,13 +1,28 @@
1
- import { detectLegacyVbriefLayout } from "./detect.js";
1
+ import { detectLegacyVbriefLayout, detectXbriefConvergence } from "./detect.js";
2
2
  /** Operator guidance for the TS-native xbrief rename (#2034 / #2110). */
3
3
  export function xbriefMigrationGuidance() {
4
4
  return "Run `deft migrate:xbrief` (or `task migrate:xbrief`) to convert vbrief/ to xbrief/ safely.";
5
5
  }
6
- /** One-line doctor / ritual signpost mirroring `renderPrecutoverLine` (#2110). */
6
+ /**
7
+ * One-line doctor / ritual signpost mirroring `renderPrecutoverLine` (#2110),
8
+ * reporting an unambiguous convergence state (#2270). A migrated project reads
9
+ * as `xbrief active` plus either `vbrief legacy marker` or `vbrief removed`; a
10
+ * stray empty legacy root is called out as a pending convergence rather than a
11
+ * generic "run migrate" dead end — never a dual-empty-root ambiguity.
12
+ */
7
13
  export function renderXbriefMigrationLine(projectRoot) {
14
+ const convergence = detectXbriefConvergence(projectRoot);
15
+ // Converged: legacy vbrief/ retained for read-compat behind an explicit marker.
16
+ if (convergence.state === "xbrief-marker") {
17
+ return "xBrief migration: converged -- xbrief active, vbrief legacy marker (read-compat).";
18
+ }
19
+ // Ambiguous dual-empty root: canonical xbrief/ (or none) plus a stray empty vbrief/.
20
+ if (convergence.state === "empty-vbrief") {
21
+ return `xBrief migration: converge pending -- xbrief active, empty legacy vbrief/ present. ${xbriefMigrationGuidance()}`;
22
+ }
8
23
  const { legacyLayout, reasons } = detectLegacyVbriefLayout(projectRoot);
9
24
  if (!legacyLayout) {
10
- return "xBrief migration: none -- project is on the xbrief layout.";
25
+ return "xBrief migration: none -- xbrief active, vbrief removed.";
11
26
  }
12
27
  const maxReasons = 3;
13
28
  const shown = reasons.slice(0, maxReasons);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deftai/directive-core",
3
- "version": "0.68.1",
3
+ "version": "0.69.0",
4
4
  "description": "TypeScript engine core for the Directive framework.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -90,6 +90,10 @@
90
90
  "types": "./dist/release-e2e/index.d.ts",
91
91
  "default": "./dist/release-e2e/index.js"
92
92
  },
93
+ "./resolution": {
94
+ "types": "./dist/resolution/index.d.ts",
95
+ "default": "./dist/resolution/index.js"
96
+ },
93
97
  "./pr-merge-readiness": {
94
98
  "types": "./dist/pr-merge-readiness/index.d.ts",
95
99
  "default": "./dist/pr-merge-readiness/index.js"
@@ -245,8 +249,8 @@
245
249
  "provenance": true
246
250
  },
247
251
  "dependencies": {
248
- "@deftai/directive-content": "^0.68.1",
249
- "@deftai/directive-types": "^0.68.1",
252
+ "@deftai/directive-content": "^0.69.0",
253
+ "@deftai/directive-types": "^0.69.0",
250
254
  "archiver": "^8.0.0"
251
255
  },
252
256
  "scripts": {