@dzhechkov/harness-core 0.8.31 → 0.8.33

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 (53) hide show
  1. package/.dz-manifest.json +97 -37
  2. package/README.md +30 -0
  3. package/dist/apply-leg.d.ts +13 -1
  4. package/dist/apply-leg.d.ts.map +1 -1
  5. package/dist/apply-leg.js +123 -10
  6. package/dist/apply-leg.js.map +1 -1
  7. package/dist/embed-socket-path.d.ts +65 -0
  8. package/dist/embed-socket-path.d.ts.map +1 -0
  9. package/dist/embed-socket-path.js +100 -0
  10. package/dist/embed-socket-path.js.map +1 -0
  11. package/dist/index.d.ts +9 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +7 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/operations.d.ts.map +1 -1
  16. package/dist/operations.js +66 -3
  17. package/dist/operations.js.map +1 -1
  18. package/dist/packed-install-smoke.d.ts +108 -0
  19. package/dist/packed-install-smoke.d.ts.map +1 -0
  20. package/dist/packed-install-smoke.js +172 -0
  21. package/dist/packed-install-smoke.js.map +1 -0
  22. package/dist/publish-sibling-drift.d.ts +67 -0
  23. package/dist/publish-sibling-drift.d.ts.map +1 -0
  24. package/dist/publish-sibling-drift.js +262 -0
  25. package/dist/publish-sibling-drift.js.map +1 -0
  26. package/dist/publish.d.ts +44 -0
  27. package/dist/publish.d.ts.map +1 -1
  28. package/dist/publish.js +243 -24
  29. package/dist/publish.js.map +1 -1
  30. package/dist/qe-bridge.d.ts +16 -0
  31. package/dist/qe-bridge.d.ts.map +1 -1
  32. package/dist/qe-bridge.js +1 -0
  33. package/dist/qe-bridge.js.map +1 -1
  34. package/dist/release.d.ts +19 -0
  35. package/dist/release.d.ts.map +1 -1
  36. package/dist/release.js +81 -2
  37. package/dist/release.js.map +1 -1
  38. package/dist/setup.d.ts +36 -0
  39. package/dist/setup.d.ts.map +1 -1
  40. package/dist/setup.js +96 -2
  41. package/dist/setup.js.map +1 -1
  42. package/package.json +23 -23
  43. package/sbom.json +186 -36
  44. package/src/apply-leg.ts +123 -10
  45. package/src/embed-socket-path.ts +113 -0
  46. package/src/index.ts +35 -2
  47. package/src/operations.ts +67 -3
  48. package/src/packed-install-smoke.ts +273 -0
  49. package/src/publish-sibling-drift.ts +293 -0
  50. package/src/publish.ts +280 -25
  51. package/src/qe-bridge.ts +14 -0
  52. package/src/release.ts +103 -2
  53. package/src/setup.ts +108 -2
package/src/setup.ts CHANGED
@@ -44,6 +44,77 @@ import {
44
44
  /** Memory backend type. */
45
45
  export type MemoryBackend = 'jsonl' | 'agentdb';
46
46
 
47
+ /** Where a resolved setup memory backend came from (feature `setup-backend-from-config`). */
48
+ export type MemoryBackendSource = 'flag' | 'config' | 'default' | 'default-unreadable' | 'disabled';
49
+
50
+ /** Result of {@link resolveSetupMemoryBackend}. */
51
+ export interface ResolvedSetupMemoryBackend {
52
+ readonly backend: MemoryBackend;
53
+ readonly source: MemoryBackendSource;
54
+ /** `true` only when an explicit `--memory jsonl` pulled an agentdb-configured project down to jsonl (FR-2). */
55
+ readonly downgraded: boolean;
56
+ }
57
+
58
+ /**
59
+ * FR-1/FR-2/FR-3 (feature `setup-backend-from-config`). Before this function, `runSetup` decided
60
+ * the backend as `opts.memory ?? 'jsonl'` — a repeat `dz setup --target claude-code` (no `--memory`)
61
+ * on an agentdb project silently reset it to jsonl and dropped `.dz/agentdb-writer.mjs` from
62
+ * `SessionStart` (AC-1, red-first). This is the ONE place that decides the backend for a run, so
63
+ * the config write, the printed source line, and the doctor cross-check can never disagree.
64
+ *
65
+ * - An explicit `--memory <x>` always wins (`source: 'flag'`) — including the one case that
66
+ * DOWNGRADES an agentdb-configured project to jsonl (FR-2): `downgraded` is set so the caller can
67
+ * warn and force the config write back in sync even without `--force`.
68
+ * - No flag, and `.dz/config.json` has a recognised `memory.backend` → that value, `source: 'config'`.
69
+ * - No flag, and no config (absent, unreadable, or an unrecognised backend value) → `jsonl`,
70
+ * `source: 'default'` — the literal ticket command on an empty project (no change, named in
71
+ * 01_requirements.md "Что НЕ чинится").
72
+ */
73
+ export function resolveSetupMemoryBackend(
74
+ projectRoot: string,
75
+ memoryOpt: MemoryBackend | undefined,
76
+ noMemory: boolean = false,
77
+ ): ResolvedSetupMemoryBackend {
78
+ // Lead edit after Codex review (findings 5/6): a config that EXISTS but cannot be read, or names an
79
+ // unknown backend, is not "no config" — its source is labeled so, and a later step never overwrites
80
+ // it silently. `--no-memory` disables memory entirely: no downgrade, no config rewrite.
81
+ let configuredBackend: MemoryBackend | undefined;
82
+ let configUnreadable = false;
83
+ const configPath = join(projectRoot, '.dz', 'config.json');
84
+ if (existsSync(configPath)) {
85
+ try {
86
+ const cfg = JSON.parse(readFileSync(configPath, 'utf-8')) as { memory?: { backend?: string } };
87
+ if (cfg.memory?.backend === 'agentdb') configuredBackend = 'agentdb';
88
+ else if (cfg.memory?.backend === 'jsonl') configuredBackend = 'jsonl';
89
+ else configUnreadable = true;
90
+ } catch {
91
+ configUnreadable = true;
92
+ }
93
+ }
94
+ if (noMemory) {
95
+ return { backend: memoryOpt ?? configuredBackend ?? 'jsonl', source: 'disabled', downgraded: false };
96
+ }
97
+ if (memoryOpt !== undefined) {
98
+ return { backend: memoryOpt, source: 'flag', downgraded: memoryOpt === 'jsonl' && configuredBackend === 'agentdb' };
99
+ }
100
+ if (configuredBackend !== undefined) {
101
+ return { backend: configuredBackend, source: 'config', downgraded: false };
102
+ }
103
+ return { backend: 'jsonl', source: configUnreadable ? 'default-unreadable' : 'default', downgraded: false };
104
+ }
105
+
106
+ /**
107
+ * FR-3: the human-readable "source" suffix, shared between `runSetup`'s own warning step and the
108
+ * CLI's printed `memory backend: …` line so the two texts can never drift apart.
109
+ */
110
+ export function memoryBackendSourceLabel(source: MemoryBackendSource): string {
111
+ if (source === 'flag') return 'from --memory';
112
+ if (source === 'config') return 'from .dz/config.json';
113
+ if (source === 'default-unreadable') return 'default — .dz/config.json unreadable or names no known backend';
114
+ if (source === 'disabled') return 'memory disabled (--no-memory)';
115
+ return 'default — no .dz/config.json';
116
+ }
117
+
47
118
  /** Setup options. */
48
119
  export interface SetupOptions {
49
120
  readonly projectRoot: string;
@@ -72,6 +143,12 @@ export interface SetupResult {
72
143
  readonly totalSteps: number;
73
144
  readonly completed: number;
74
145
  readonly skipped: number;
146
+ /** The memory backend this run actually used (feature `setup-backend-from-config`, FR-1). */
147
+ readonly memoryBackend: MemoryBackend;
148
+ /** Where {@link memoryBackend} came from — FR-3, also the `--json` field name. */
149
+ readonly memoryBackendSource: MemoryBackendSource;
150
+ /** `true` when an explicit `--memory jsonl` pulled an agentdb-configured project down (FR-2). */
151
+ readonly memoryBackendDowngraded: boolean;
75
152
  }
76
153
 
77
154
  /** A single setup step. */
@@ -809,7 +886,10 @@ function applyLegStepResult(opts: SetupOptions, backend: MemoryBackend): SetupSt
809
886
  export function runSetup(opts: SetupOptions): SetupResult {
810
887
  const steps: SetupStep[] = [];
811
888
  const dzDir = join(opts.projectRoot, '.dz');
812
- const backend: MemoryBackend = opts.memory ?? 'jsonl';
889
+ // FR-1/FR-2/FR-3 (feature `setup-backend-from-config`): read BEFORE this run writes anything, so
890
+ // the comparison is against the PRIOR config, never the one this same call is about to produce.
891
+ const resolvedMemory = resolveSetupMemoryBackend(opts.projectRoot, opts.memory, opts.noMemory === true);
892
+ const backend: MemoryBackend = resolvedMemory.backend;
813
893
 
814
894
  // Step 0: Install agentdb + better-sqlite3 locally so the session-hook writer can import them
815
895
  // and share a native store with the MCP server. Best-effort — the writer self-degrades to a
@@ -835,14 +915,37 @@ export function runSetup(opts: SetupOptions): SetupResult {
835
915
  steps.push({ name: 'Create .dz directory', status: 'skipped', detail: 'already exists' });
836
916
  }
837
917
 
838
- // Step 2: Write .dz/config.json
918
+ // Step 2: Write .dz/config.json. FR-2: a DOWNGRADE (explicit --memory jsonl over an
919
+ // agentdb-configured project) forces the write even without --force — "two truths after any
920
+ // setup coincide" means the config may not keep claiming agentdb once the caller has explicitly
921
+ // asked for jsonl.
839
922
  const configPath = join(dzDir, 'config.json');
840
923
  if (!existsSync(configPath) || opts.force) {
841
924
  writeFileSync(configPath, generateDzConfig(opts.target, opts.preset, backend));
842
925
  steps.push({ name: 'Write .dz/config.json', status: 'done', detail: `${backend} backend` });
926
+ } else if (resolvedMemory.downgraded) {
927
+ // Lead edit after Codex review (finding 3): a downgrade changes ONLY memory.backend — every other
928
+ // field the owner keeps in .dz/config.json survives; an unparsable file falls back to regeneration.
929
+ let rewritten = false;
930
+ try {
931
+ const cfg = JSON.parse(readFileSync(configPath, 'utf-8')) as Record<string, unknown>;
932
+ const memory = (cfg['memory'] !== null && typeof cfg['memory'] === 'object') ? (cfg['memory'] as Record<string, unknown>) : {};
933
+ cfg['memory'] = { ...memory, backend };
934
+ writeFileSync(configPath, JSON.stringify(cfg, null, 2) + '\n');
935
+ rewritten = true;
936
+ } catch { /* fall through to regeneration */ }
937
+ if (!rewritten) writeFileSync(configPath, generateDzConfig(opts.target, opts.preset, backend));
938
+ steps.push({ name: 'Write .dz/config.json', status: 'done', detail: `memory.backend → ${backend} (other fields kept)` });
843
939
  } else {
844
940
  steps.push({ name: 'Write .dz/config.json', status: 'skipped', detail: 'already exists (use --force)' });
845
941
  }
942
+ if (resolvedMemory.downgraded) {
943
+ steps.push({
944
+ name: 'Memory backend downgrade',
945
+ status: 'done',
946
+ detail: '⚠ memory backend downgraded agentdb → jsonl by --memory jsonl',
947
+ });
948
+ }
846
949
 
847
950
  // Step 3: Initialize session log
848
951
  const sessionsPath = join(dzDir, 'sessions.jsonl');
@@ -1219,5 +1322,8 @@ export function runSetup(opts: SetupOptions): SetupResult {
1219
1322
  totalSteps: steps.length,
1220
1323
  completed: steps.filter((s) => s.status === 'done').length,
1221
1324
  skipped: steps.filter((s) => s.status === 'skipped').length,
1325
+ memoryBackend: resolvedMemory.backend,
1326
+ memoryBackendSource: resolvedMemory.source,
1327
+ memoryBackendDowngraded: resolvedMemory.downgraded,
1222
1328
  };
1223
1329
  }