@ornexus/neocortex 4.60.73 → 4.60.74

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 (241) hide show
  1. package/dist/release-source.json +1 -1
  2. package/dist/sbom.cdx.json +1216 -1210
  3. package/install.ps1 +1 -1
  4. package/install.sh +1 -1
  5. package/package.json +2 -2
  6. package/packages/client/dist/adapters/adapter-registry.js +1 -108
  7. package/packages/client/dist/adapters/antigravity-adapter.js +2 -77
  8. package/packages/client/dist/adapters/claude-code-adapter.js +3 -79
  9. package/packages/client/dist/adapters/codex-adapter.js +2 -80
  10. package/packages/client/dist/adapters/cursor-adapter.js +4 -115
  11. package/packages/client/dist/adapters/gemini-adapter.js +2 -71
  12. package/packages/client/dist/adapters/grok-adapter.js +2 -42
  13. package/packages/client/dist/adapters/index.js +1 -22
  14. package/packages/client/dist/adapters/platform-detector.js +1 -212
  15. package/packages/client/dist/adapters/target-adapter.js +0 -12
  16. package/packages/client/dist/adapters/vscode-adapter.js +2 -72
  17. package/packages/client/dist/agent/refresh-stubs.js +2 -417
  18. package/packages/client/dist/agent/update-agent-yaml.js +1 -102
  19. package/packages/client/dist/agent/update-description.js +1 -251
  20. package/packages/client/dist/cache/crypto-utils.js +1 -78
  21. package/packages/client/dist/cache/encrypted-cache.js +1 -108
  22. package/packages/client/dist/cache/in-memory-asset-cache.js +1 -73
  23. package/packages/client/dist/cache/index.js +1 -13
  24. package/packages/client/dist/cache/protected-pi-boundary.js +1 -44
  25. package/packages/client/dist/checkpoint/checkpoint-client-reader.js +2 -127
  26. package/packages/client/dist/checkpoint/index.js +1 -11
  27. package/packages/client/dist/checkpoint/shared-checkpoint-types.js +1 -32
  28. package/packages/client/dist/cli.js +5 -358
  29. package/packages/client/dist/commands/activate.js +8 -394
  30. package/packages/client/dist/commands/architecture-answers.js +7 -274
  31. package/packages/client/dist/commands/architecture-memory-terminal.js +4 -253
  32. package/packages/client/dist/commands/architecture-non-ui-continuation.js +1 -100
  33. package/packages/client/dist/commands/architecture-root-binding.js +1 -51
  34. package/packages/client/dist/commands/cache-status.js +2 -112
  35. package/packages/client/dist/commands/config.js +1 -88
  36. package/packages/client/dist/commands/doctor.js +2 -635
  37. package/packages/client/dist/commands/invoke.js +143 -7200
  38. package/packages/client/dist/commands/legacy-epic-terminal.js +1 -71
  39. package/packages/client/dist/commands/legacy-story-branch.js +1 -96
  40. package/packages/client/dist/commands/legacy-story-pr.js +1 -276
  41. package/packages/client/dist/commands/mutation-qa-checkpoint.js +5 -285
  42. package/packages/client/dist/commands/mutation-qa-gate.js +1 -54
  43. package/packages/client/dist/commands/mutation-qa-git.js +1 -41
  44. package/packages/client/dist/commands/mutation-qa-policy.js +1 -102
  45. package/packages/client/dist/commands/mutation-qa-run.js +3 -219
  46. package/packages/client/dist/commands/mutation-qa-verdict.js +1 -149
  47. package/packages/client/dist/commands/mutation-qa.js +1 -220
  48. package/packages/client/dist/commands/native-agent.js +2 -136
  49. package/packages/client/dist/commands/planning-diagnosis.js +1 -43
  50. package/packages/client/dist/commands/planning-generation.js +1 -53
  51. package/packages/client/dist/commands/planning-memory.js +1 -40
  52. package/packages/client/dist/commands/planning-research.js +1 -42
  53. package/packages/client/dist/commands/private-session.js +1 -75
  54. package/packages/client/dist/commands/property-qa.js +1 -139
  55. package/packages/client/dist/commands/qa-control.js +1 -16
  56. package/packages/client/dist/commands/qa-scan-browser-launch.js +1 -55
  57. package/packages/client/dist/commands/qa-scan-browser-route.js +1 -28
  58. package/packages/client/dist/commands/qa-scan-capture.js +5 -663
  59. package/packages/client/dist/commands/qa-scan-discovery.js +3 -376
  60. package/packages/client/dist/commands/qa-scan-review.js +5 -826
  61. package/packages/client/dist/commands/qa-synthetic-backend-capture.js +1 -266
  62. package/packages/client/dist/commands/qa-synthetic-backend-review.js +1 -297
  63. package/packages/client/dist/commands/qa-synthetic-backend.js +2 -340
  64. package/packages/client/dist/commands/qa-synthetic-broker-port.js +1 -85
  65. package/packages/client/dist/commands/qa-synthetic-browser.js +1 -39
  66. package/packages/client/dist/commands/refactor-characterization.js +21 -671
  67. package/packages/client/dist/commands/refactor-commit.js +13 -399
  68. package/packages/client/dist/commands/refactor-control.js +3 -142
  69. package/packages/client/dist/commands/refactor-cpp-assertions.js +3 -93
  70. package/packages/client/dist/commands/refactor-review.js +5 -283
  71. package/packages/client/dist/commands/refactor-root.js +7 -588
  72. package/packages/client/dist/commands/refresh-memory.js +3 -156
  73. package/packages/client/dist/commands/shared-architecture-non-ui.js +1 -24
  74. package/packages/client/dist/commands/story-checkpoint.js +1 -171
  75. package/packages/client/dist/config/profile-directory.js +1 -13
  76. package/packages/client/dist/config/resolver-selection.js +1 -278
  77. package/packages/client/dist/config/secure-config.js +12 -281
  78. package/packages/client/dist/config.js +2 -223
  79. package/packages/client/dist/constants.js +1 -25
  80. package/packages/client/dist/context/context-collector.js +2 -222
  81. package/packages/client/dist/context/context-sanitizer.js +1 -145
  82. package/packages/client/dist/continuity/autonomous-governance.js +1 -285
  83. package/packages/client/dist/continuity/continuity-client-state-store.js +1 -1587
  84. package/packages/client/dist/continuity/invoke-hooks.js +1 -129
  85. package/packages/client/dist/continuity/migrations/001-initial-schema.js +2 -12
  86. package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +2 -12
  87. package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +2 -12
  88. package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +2 -12
  89. package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +2 -12
  90. package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +2 -10
  91. package/packages/client/dist/continuity/native-agent-store.js +1 -69
  92. package/packages/client/dist/continuity/native-agent-types.js +1 -90
  93. package/packages/client/dist/continuity/outbox.js +1 -117
  94. package/packages/client/dist/continuity/runner-bootstrap-policy.js +1 -172
  95. package/packages/client/dist/continuity/sqlite-store.js +81 -2612
  96. package/packages/client/dist/effects/invoke-effect-gateways.js +1 -62
  97. package/packages/client/dist/errors/error-messages.js +2 -228
  98. package/packages/client/dist/errors/frontmatter-diagnostics.js +1 -125
  99. package/packages/client/dist/evidence/collector.js +1 -347
  100. package/packages/client/dist/evidence/index.js +1 -1
  101. package/packages/client/dist/evidence/types.js +0 -2
  102. package/packages/client/dist/fs/atomic-state-replace.js +1 -152
  103. package/packages/client/dist/generated/command-registry.generated.js +1 -4076
  104. package/packages/client/dist/generated/public-projections.generated.js +1 -149
  105. package/packages/client/dist/git/direct-yolo-c01-retry.js +4 -138
  106. package/packages/client/dist/git/direct-yolo-checkpoint.js +4 -1234
  107. package/packages/client/dist/git/epic-operational-state.js +2 -672
  108. package/packages/client/dist/git/git-lifecycle-step-ids.js +1 -14
  109. package/packages/client/dist/git/git-lifecycle-store.d.ts +23 -0
  110. package/packages/client/dist/git/git-lifecycle-store.js +10 -1981
  111. package/packages/client/dist/git/legacy-story-branch-checkpoint.js +9 -259
  112. package/packages/client/dist/git/legacy-story-branch-setup.js +4 -348
  113. package/packages/client/dist/git/legacy-story-pr.js +70 -3538
  114. package/packages/client/dist/git/secure-filesystem-boundary.js +5 -182
  115. package/packages/client/dist/graph-retrieval/pre-command-hook.js +1 -284
  116. package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +1 -237
  117. package/packages/client/dist/i18n/first-run.js +2 -60
  118. package/packages/client/dist/index.js +1 -47
  119. package/packages/client/dist/invocation/invoke-input.js +1 -24
  120. package/packages/client/dist/license/index.js +1 -5
  121. package/packages/client/dist/license/license-client.js +1 -304
  122. package/packages/client/dist/machine/fingerprint.js +2 -163
  123. package/packages/client/dist/machine/index.js +1 -5
  124. package/packages/client/dist/memory/project-memory-path-scope.js +1 -62
  125. package/packages/client/dist/memory/project-memory-writer.js +40 -1007
  126. package/packages/client/dist/memory/shared-project-memory-types.js +2 -253
  127. package/packages/client/dist/migrations/epic-lifecycle-backfill.js +3 -1060
  128. package/packages/client/dist/migrations/project-identity-capability.js +1 -50
  129. package/packages/client/dist/migrations/project-identity.js +7 -493
  130. package/packages/client/dist/migrations/state-json.js +4 -393
  131. package/packages/client/dist/policy/architecture-policy.js +2 -184
  132. package/packages/client/dist/policy/index.js +1 -7
  133. package/packages/client/dist/policy/shared-policy-types.js +0 -38
  134. package/packages/client/dist/policy/subagent-approval-policy.js +1 -55
  135. package/packages/client/dist/private-execution/clean-baseline.js +2 -279
  136. package/packages/client/dist/private-execution/codex-executor.js +4 -574
  137. package/packages/client/dist/private-execution/codex-host-runtime.js +1 -137
  138. package/packages/client/dist/private-execution/codex-lifecycle-guard.js +1 -97
  139. package/packages/client/dist/private-execution/codex-scoped-permissions.js +1 -128
  140. package/packages/client/dist/private-execution/delivery.js +13 -410
  141. package/packages/client/dist/private-execution/diagnosis-root-review.js +2 -294
  142. package/packages/client/dist/private-execution/frozen-handoff.js +6 -19
  143. package/packages/client/dist/private-execution/implementation-validation.js +2 -106
  144. package/packages/client/dist/private-execution/isolated-admission.js +23 -1432
  145. package/packages/client/dist/private-execution/isolated-session.js +5 -725
  146. package/packages/client/dist/private-execution/isolated-workspace.js +1 -122
  147. package/packages/client/dist/private-execution/pact-consumer-runtime.js +3 -409
  148. package/packages/client/dist/private-execution/pact-test-source.js +1 -258
  149. package/packages/client/dist/private-execution/pact-toolchain.js +3 -290
  150. package/packages/client/dist/private-execution/pact-write-set.js +1 -172
  151. package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +5 -406
  152. package/packages/client/dist/private-execution/planning-generation-checkpoint.js +5 -462
  153. package/packages/client/dist/private-execution/planning-memory-checkpoint.js +3 -139
  154. package/packages/client/dist/private-execution/planning-research-checkpoint.js +4 -325
  155. package/packages/client/dist/private-execution/private-report-store.js +1 -42
  156. package/packages/client/dist/private-execution/property-test-candidate.js +1 -103
  157. package/packages/client/dist/private-execution/property-test-choices.js +3 -147
  158. package/packages/client/dist/private-execution/property-test-plan.js +7 -271
  159. package/packages/client/dist/private-execution/property-test-review.js +4 -258
  160. package/packages/client/dist/private-execution/property-test-runtime.js +9 -372
  161. package/packages/client/dist/private-execution/property-test-source.js +1 -139
  162. package/packages/client/dist/private-execution/root-checkpoint.js +1 -80
  163. package/packages/client/dist/private-execution/root-memory-git-evidence.js +3 -135
  164. package/packages/client/dist/private-execution/root-memory-recovery.js +1 -93
  165. package/packages/client/dist/private-execution/root-memory-request.js +1 -29
  166. package/packages/client/dist/private-execution/root-memory-scope.js +1 -178
  167. package/packages/client/dist/private-execution/root-memory-writer.js +1 -102
  168. package/packages/client/dist/private-execution/session-lease.js +1 -63
  169. package/packages/client/dist/private-execution/shared-private-admission-types.js +1 -50
  170. package/packages/client/dist/private-execution/shared-project-validation.js +1 -66
  171. package/packages/client/dist/private-execution/shared-root-memory-types.js +1 -61
  172. package/packages/client/dist/private-execution/ux-source-validation.js +1 -372
  173. package/packages/client/dist/private-execution/ux-write-set.js +2 -191
  174. package/packages/client/dist/registry/registry-reconciliation.js +1 -234
  175. package/packages/client/dist/resilience/circuit-breaker.js +1 -170
  176. package/packages/client/dist/resilience/degradation-manager.js +1 -164
  177. package/packages/client/dist/resilience/freshness-indicator.js +1 -100
  178. package/packages/client/dist/resilience/index.js +1 -8
  179. package/packages/client/dist/resilience/recovery-detector.js +1 -74
  180. package/packages/client/dist/resolvers/asset-resolver.js +0 -13
  181. package/packages/client/dist/resolvers/local-resolver.js +8 -255
  182. package/packages/client/dist/resolvers/remote-resolver.js +1 -233
  183. package/packages/client/dist/runner/binary.js +1 -404
  184. package/packages/client/dist/runner/cli.js +17 -1571
  185. package/packages/client/dist/runner/headless/adapters.js +1 -49
  186. package/packages/client/dist/runner/headless/claude-code-adapter.js +1 -79
  187. package/packages/client/dist/runner/headless/continuous-story-workspace.js +11 -1628
  188. package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +1 -69
  189. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.d.ts +1 -1
  190. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +2 -110
  191. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.d.ts +2 -0
  192. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +10 -1236
  193. package/packages/client/dist/runner/headless/epic-terminal-git-runner.d.ts +18 -2
  194. package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +2 -318
  195. package/packages/client/dist/runner/headless/fake-adapter.js +1 -62
  196. package/packages/client/dist/runner/headless/managed-profile.js +1 -132
  197. package/packages/client/dist/runner/headless/opencode-adapter.js +2 -145
  198. package/packages/client/dist/runner/headless/prepared-story-bundle.js +1 -43
  199. package/packages/client/dist/runner/headless/resolver.js +1 -231
  200. package/packages/client/dist/runner/headless/run-yolo.js +1 -885
  201. package/packages/client/dist/runner/headless/run-yoloop.d.ts +1 -0
  202. package/packages/client/dist/runner/headless/run-yoloop.js +2 -2574
  203. package/packages/client/dist/runner/headless/types.js +2 -574
  204. package/packages/client/dist/runner/local-console.js +13 -396
  205. package/packages/client/dist/runner/portal-sync-worker.js +1 -1487
  206. package/packages/client/dist/runner/project-identity.js +1 -106
  207. package/packages/client/dist/runner/scheduler.js +7 -995
  208. package/packages/client/dist/runner-cli.js +2 -18
  209. package/packages/client/dist/state/invocation-shape.js +1 -13
  210. package/packages/client/dist/state/project-state-snapshot.js +1 -1731
  211. package/packages/client/dist/state/state-json-repair.js +3 -785
  212. package/packages/client/dist/state/structural-dag-projection.js +1 -118
  213. package/packages/client/dist/telemetry/index.js +1 -5
  214. package/packages/client/dist/telemetry/offline-queue.js +1 -131
  215. package/packages/client/dist/tier/index.js +1 -5
  216. package/packages/client/dist/tier/tier-aware-client.js +1 -405
  217. package/packages/client/dist/types/index.js +1 -38
  218. package/packages/client/dist/version.js +1 -27
  219. package/packages/client/dist/yolo/change-evidence-collector.js +1 -126
  220. package/packages/client/dist/yolo/invoke-envelope-capability.js +1 -62
  221. package/packages/client/dist/yolo/spec-envelope-collector.js +5 -184
  222. package/packages/client/dist/yoloop/discovery-hook.js +2 -300
  223. package/packages/client/dist/yoloop/index.js +1 -8
  224. package/packages/client/dist/yoloop/invoke-hooks.js +12 -669
  225. package/packages/client/dist/yoloop/shared-discover-epics.js +1 -1007
  226. package/packages/client/dist/yoloop/shared-yoloop-types.js +1 -33
  227. package/packages/client/dist/yoloop/target-normalization.js +1 -180
  228. package/packages/client/dist/yoloop/yoloop-client-state-store.js +1 -1920
  229. package/packages/shared/dist/yolo/change-evidence.js +1 -1
  230. package/packages/shared/dist/yoloop/epic-terminal-git.js +144 -2
  231. package/targets-stubs/antigravity/gemini.md +1 -1
  232. package/targets-stubs/antigravity/skill/SKILL.md +1 -1
  233. package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
  234. package/targets-stubs/claude-code/neocortex-root.md +2 -2
  235. package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
  236. package/targets-stubs/claude-code/neocortex.md +2 -2
  237. package/targets-stubs/codex/AGENTS.md +1 -1
  238. package/targets-stubs/cursor/agent.md +2 -2
  239. package/targets-stubs/gemini-cli/agent.md +2 -2
  240. package/targets-stubs/opencode/neocortex-root.md +1 -1
  241. package/targets-stubs/vscode/neocortex.agent.md +2 -2
@@ -1,1007 +1,40 @@
1
- /**
2
- * Project memory writer for platform-specific always-loaded files.
3
- *
4
- * The writer owns only the NEOCORTEX-MEMORY block. Everything outside that
5
- * block is preserved byte-for-byte so users can keep local instructions.
6
- */
7
- import { randomUUID } from 'node:crypto';
8
- import { constants as fsConstants, existsSync, lstatSync, mkdirSync, copyFileSync, readdirSync, readFileSync, readlinkSync, realpathSync, renameSync, rmSync, statSync, symlinkSync, unlinkSync, writeFileSync, } from 'node:fs';
9
- import { platform } from 'node:os';
10
- import { basename, dirname, join, relative, resolve } from 'node:path';
11
- import { CANONICAL_PROJECT_MEMORY_SOURCE, getProjectMemoryTarget, renderArchitectureReferenceTable, } from './shared-project-memory-types.js';
12
- import { assertProjectMemoryPaths, invalidProjectMemoryPath, isValidScopedRelativePath } from './project-memory-path-scope.js';
13
- function hasCanonicalUpdateInput(options) {
14
- return (options.memoryNotes?.length ?? 0) > 0 ||
15
- (options.architectureReferences?.length ?? 0) > 0 ||
16
- options.bootstrapArchitecture !== undefined;
17
- }
18
- const REFRESH_UPDATE_START = '<!-- NEOCORTEX-REFRESH-UPDATES-START -->';
19
- const REFRESH_UPDATE_END = '<!-- NEOCORTEX-REFRESH-UPDATES-END -->';
20
- function occurrenceCount(content, value) {
21
- return content.split(value).length - 1;
22
- }
23
- function canonicalRefreshRows(options) {
24
- assertSafeProjectMemoryInput(options.memoryNotes ?? [], options.architectureReferences ?? [], options.bootstrapArchitecture);
25
- const noteRows = classifyProjectMemoryUpdates(options.memoryNotes ?? [])
26
- .map((entry) => `- [${entry.reasonCode}] ${entry.summary} (layer: ${entry.layer})`);
27
- const referenceRows = (options.architectureReferences ?? []).map((reference) => `- [architecture-reference] \`${reference.path}\`: ${reference.title}; ${reference.purpose}; use: ${reference.suggestedUsage}`);
28
- const bootstrap = options.bootstrapArchitecture;
29
- const bootstrapRows = bootstrap === undefined ? [] : [
30
- ...(bootstrap.universalLaws ?? []).map((value) => `- [bootstrap-law] ${value}`),
31
- ...(bootstrap.businessContext ?? []).map((value) => `- [bootstrap-business] ${value}`),
32
- ...(bootstrap.activeContext ?? []).map((value) => `- [bootstrap-context] ${value}`),
33
- ...(bootstrap.repositoryMap ?? []).map((value) => `- [bootstrap-repository] ${value}`),
34
- ...(bootstrap.environmentPractices ?? []).map((value) => `- [bootstrap-environment] ${value}`),
35
- ...(bootstrap.platformMemoryContract ?? []).map((value) => `- [bootstrap-platform] ${value}`),
36
- ...(bootstrap.maintenanceRules ?? []).map((value) => `- [bootstrap-maintenance] ${value}`),
37
- ...(bootstrap.heuristics ?? []).map((value) => `- [bootstrap-heuristic] ${value}`),
38
- ];
39
- return uniqueLines([...noteRows, ...referenceRows, ...bootstrapRows]);
40
- }
41
- function assertWithinBudgetWithoutTruncation(target, content) {
42
- if (content.split('\n').length > target.budget.maxLines ||
43
- Buffer.byteLength(content, 'utf8') > target.budget.maxBytes) {
44
- throw new Error('Canonical project memory update exceeds its context budget; refusing to truncate existing content.');
45
- }
46
- }
47
- function mergeCanonicalRefreshUpdate(target, existingContent, options) {
48
- const rows = canonicalRefreshRows(options);
49
- if (rows.length === 0)
50
- return existingContent;
51
- const eol = existingContent.includes('\r\n') ? '\r\n' : '\n';
52
- const outerStart = markerStart(target);
53
- const outerEnd = markerEnd(target);
54
- const outerStartCount = occurrenceCount(existingContent, outerStart);
55
- const outerEndCount = occurrenceCount(existingContent, outerEnd);
56
- if (outerStartCount !== 1 || outerEndCount !== 1 || existingContent.indexOf(outerEnd) < existingContent.indexOf(outerStart)) {
57
- throw new Error('Canonical project memory has ambiguous managed-block markers; refusing refresh.');
58
- }
59
- const innerStartCount = occurrenceCount(existingContent, REFRESH_UPDATE_START);
60
- const innerEndCount = occurrenceCount(existingContent, REFRESH_UPDATE_END);
61
- if (innerStartCount !== innerEndCount || innerStartCount > 1) {
62
- throw new Error('Canonical project memory has ambiguous refresh-update markers; refusing refresh.');
63
- }
64
- let nextContent;
65
- if (innerStartCount === 0) {
66
- const outerEndIndex = existingContent.indexOf(outerEnd);
67
- const prefix = existingContent.slice(0, outerEndIndex);
68
- const block = [
69
- REFRESH_UPDATE_START,
70
- '## Durable Refresh Updates',
71
- ...rows,
72
- REFRESH_UPDATE_END,
73
- '',
74
- ].join(eol);
75
- nextContent = `${prefix}${prefix.endsWith(eol) ? '' : eol}${block}${existingContent.slice(outerEndIndex)}`;
76
- }
77
- else {
78
- const innerStartIndex = existingContent.indexOf(REFRESH_UPDATE_START);
79
- const innerEndIndex = existingContent.indexOf(REFRESH_UPDATE_END);
80
- const outerStartIndex = existingContent.indexOf(outerStart);
81
- const outerEndIndex = existingContent.indexOf(outerEnd);
82
- if (innerStartIndex < outerStartIndex || innerEndIndex > outerEndIndex || innerEndIndex < innerStartIndex) {
83
- throw new Error('Canonical refresh-update block is outside the managed block; refusing refresh.');
84
- }
85
- const currentBlock = existingContent.slice(innerStartIndex, innerEndIndex);
86
- const existingRows = new Set(currentBlock.split(/\r?\n/).map((line) => line.trim().toLowerCase()));
87
- const missingRows = rows.filter((row) => !existingRows.has(row.trim().toLowerCase()));
88
- if (missingRows.length === 0)
89
- return existingContent;
90
- const prefix = existingContent.slice(0, innerEndIndex);
91
- nextContent = `${prefix}${prefix.endsWith(eol) ? '' : eol}${missingRows.join(eol)}${eol}${existingContent.slice(innerEndIndex)}`;
92
- }
93
- assertWithinBudgetWithoutTruncation(target, nextContent);
94
- return nextContent;
95
- }
96
- export function createNodeProjectMemoryFs(backupDirectory) {
97
- return {
98
- readFile(path) {
99
- try {
100
- return readFileSync(path, 'utf8');
101
- }
102
- catch {
103
- return undefined;
104
- }
105
- },
106
- writeFileAtomic(path, content) {
107
- mkdirSync(dirname(path), { recursive: true });
108
- const nonce = randomUUID();
109
- const tempPath = `${path}.tmp-${nonce}`;
110
- const backupPath = existsSync(path) ? memoryBackupPath(path, nonce, backupDirectory) : undefined;
111
- try {
112
- writeFileSync(tempPath, content, { encoding: 'utf8', flag: 'wx', mode: 0o600 });
113
- if (backupPath !== undefined)
114
- copyFileSync(path, backupPath, fsConstants.COPYFILE_EXCL);
115
- renameSync(tempPath, path);
116
- return { backupPath };
117
- }
118
- catch (error) {
119
- rmSync(tempPath, { force: true });
120
- if (backupPath !== undefined && existsSync(backupPath)) {
121
- try {
122
- copyFileSync(backupPath, path);
123
- }
124
- catch { /* best-effort rollback */ }
125
- }
126
- throw error;
127
- }
128
- },
129
- join(...parts) {
130
- return join(...parts);
131
- },
132
- };
133
- }
134
- function markerStart(target) {
135
- return `<!-- ${target.ownedBlockName}-START -->`;
136
- }
137
- function markerEnd(target) {
138
- return `<!-- ${target.ownedBlockName}-END -->`;
139
- }
140
- function uniqueLines(values) {
141
- const seen = new Set();
142
- const result = [];
143
- for (const value of values) {
144
- const normalized = value.trim().replace(/\s+/g, ' ');
145
- if (normalized.length === 0 || seen.has(normalized.toLowerCase()))
146
- continue;
147
- seen.add(normalized.toLowerCase());
148
- result.push(normalized);
149
- }
150
- return result;
151
- }
152
- function uniqueArchitectureReferences(references) {
153
- const seen = new Set();
154
- const result = [];
155
- for (const reference of references) {
156
- if (seen.has(reference.path))
157
- continue;
158
- seen.add(reference.path);
159
- result.push(reference);
160
- }
161
- return result;
162
- }
163
- function templateTitle(target) {
164
- switch (target.templateKind) {
165
- case 'neocortex-md':
166
- return '# NEOCORTEX.md Project Memory';
167
- case 'claude-md':
168
- return '# CLAUDE.md Project Memory';
169
- case 'agents-md':
170
- return '# AGENTS.md Project Instructions';
171
- case 'gemini-md':
172
- return '# GEMINI.md Project Memory';
173
- case 'copilot-instructions':
174
- return '# GitHub Copilot Repository Instructions';
175
- case 'cursor-rule':
176
- return '---\ndescription: Neocortex project memory\nalwaysApply: true\n---\n\n# Cursor Project Memory';
177
- }
178
- }
179
- const DEFAULT_UNIVERSAL_LAWS = [
180
- 'Keep always-loaded memory compact: laws, active context, and references only.',
181
- 'Preserve human-authored instructions outside this owned block.',
182
- 'Prefer repository SSoT docs over duplicating architecture details here.',
183
- ];
184
- const DEFAULT_ARCHITECTURE_HEURISTICS = [
185
- 'Consult `docs/architecture/` before changes that affect architecture, contracts, data, security, performance, UX, or infrastructure.',
186
- 'Reference architecture documents by repository path, such as `docs/architecture/architecture.md`, before changing related implementation.',
187
- 'Summarize decisions and trade-offs in NEOCORTEX.md; do not paste complete architecture documents into memory.',
188
- 'Update memory for durable cross-layer changes across skills/agents, frontend, backend, API, DB, schemas, envs, DevOps, cyber security, testing/QA, observability, product, docs, and platform contracts.',
189
- 'Prioritize compact summaries of durable decisions, public contracts, operational risks, debts, command behavior, and repository paths; skip transient logs, raw payloads, secrets, private prompts, long documents, and duplicated canonical sources.',
190
- 'Keep platform memory files symlink-backed or include-backed to NEOCORTEX.md when supported.',
191
- ];
192
- export const PROJECT_MEMORY_LAYER_CATEGORIES = [
193
- 'skills-agents',
194
- 'frontend',
195
- 'backend',
196
- 'api',
197
- 'db',
198
- 'schemas',
199
- 'envs',
200
- 'devops',
201
- 'cyber-security',
202
- 'testing-qa',
203
- 'observability',
204
- 'product-business',
205
- 'docs-architecture',
206
- 'platform-memory',
207
- 'command-behavior',
208
- 'generic',
209
- ];
210
- const PROJECT_MEMORY_NOTE_BUDGET_BYTES = 360;
211
- const CHANGE_KIND_SECTIONS = {
212
- feature: 'Features',
213
- rule: 'Rules/Policies',
214
- flag: 'Flags',
215
- route: 'Routes',
216
- api: 'APIs/Contracts',
217
- 'db-schema': 'DB/Schema',
218
- architecture: 'Architecture',
219
- debt: 'Debts',
220
- decision: 'Decisions',
221
- generic: 'General Updates',
222
- };
223
- const IP_BOUNDARY_PATTERN = /\b(private|proprietary|server[- ]only|system)\s+prompt\b|\b(api[-_ ]?key|license[-_ ]?key|password|authorization|bearer|token|secret)\s*[:=]|\b(ghp_[A-Za-z0-9_]+|sk-[A-Za-z0-9_-]+|xox[baprs]-[A-Za-z0-9-]+)\b|\b(customer|cliente)\s+(payload|record|email|phone)\b|\b(customer|cliente)\s+(data|dados)\s+(payload|record|email|phone)\b|\b(customer|cliente)\s+(data|dados)\s*[:=]|\braw\s+payload\b/i;
224
- function classifyKind(value) {
225
- const text = value.toLowerCase();
226
- if (/\b(project memory|canonical memory|memory heuristic|neocortex\.md|\*update-memory)\b/.test(text))
227
- return 'decision';
228
- if (/\b(feature|capability|funcionalidade|release)\b/.test(text))
229
- return 'feature';
230
- if (/\b(rule|policy|guardrail|regra|lei|must|never)\b/.test(text))
231
- return 'rule';
232
- if (/\b(flag|feature[- ]?flag|toggle|env var|environment variable)\b/.test(text))
233
- return 'flag';
234
- if (/\b(api|contract|schema contract|graphql|rest|rpc|webhook)\b/.test(text))
235
- return 'api';
236
- if (/\b(db|database|schema|migration|table|column|prisma|sql)\b/.test(text))
237
- return 'db-schema';
238
- if (/\b(decision|decisão|decisao|accepted|rejected|trade[- ]?off)\b/.test(text))
239
- return 'decision';
240
- if (/\b(route|endpoint route|url path|pathname)\b|\/(api|admin|portal|v\d+)\//.test(text))
241
- return 'route';
242
- if (/\b(architecture|arquitetura|adr|topology|infra|boundary)\b/.test(text))
243
- return 'architecture';
244
- if (/\b(debt|follow[- ]?up|todo|tech[- ]?debt|dívida|divida)\b/.test(text))
245
- return 'debt';
246
- return 'generic';
247
- }
248
- function classifyLayer(value) {
249
- const text = value.toLowerCase();
250
- if (/\b(neocortex\.md|project memory|canonical memory|memory wrapper|symlink|owned block|memory heuristic)\b/.test(text))
251
- return 'platform-memory';
252
- if (/\b(command|cli|\*update-memory|\*create-epic|\*arch-plan|workflow behavior)\b/.test(text))
253
- return 'command-behavior';
254
- if (/\b(skill|agent|subagent|orchestrator|opencode|codex|claude|gemini|cursor)\b/.test(text))
255
- return 'skills-agents';
256
- if (/\b(frontend|ui|ux|react|component|design[- ]?system|css|accessibility|a11y)\b/.test(text))
257
- return 'frontend';
258
- if (/\b(backend|server|service|worker|job|queue)\b/.test(text))
259
- return 'backend';
260
- if (/\b(api|endpoint|rest|graphql|rpc|webhook|contract)\b/.test(text))
261
- return 'api';
262
- if (/\b(db|database|table|column|migration|sql|prisma)\b/.test(text))
263
- return 'db';
264
- if (/\b(schema|schemas|zod|json schema|protobuf|openapi)\b/.test(text))
265
- return 'schemas';
266
- if (/\b(env|environment|\.env\.example|config|configuration|feature[- ]?flag)\b/.test(text))
267
- return 'envs';
268
- if (/\b(devops|deploy|deployment|ci|cd|docker|container|kubernetes|infra|terraform|rollback)\b/.test(text))
269
- return 'devops';
270
- if (/\b(security|cyber|auth|authorization|permission|compliance|vulnerability|secret|token redaction)\b/.test(text))
271
- return 'cyber-security';
272
- if (/\b(test|testing|qa|e2e|unit|integration|snapshot|property[- ]?based|flaky)\b/.test(text))
273
- return 'testing-qa';
274
- if (/\b(observability|metric|metrics|log|logging|trace|tracing|alert|monitoring|slo)\b/.test(text))
275
- return 'observability';
276
- if (/\b(product|business|user journey|onboarding|pricing|tenant|customer-facing)\b/.test(text))
277
- return 'product-business';
278
- if (/\b(docs|documentation|architecture|arquitetura|adr|runbook|decision record)\b/.test(text))
279
- return 'docs-architecture';
280
- return 'generic';
281
- }
282
- function compactMemoryNote(value) {
283
- const normalized = value.trim().replace(/\s+/g, ' ');
284
- if (Buffer.byteLength(normalized, 'utf8') <= PROJECT_MEMORY_NOTE_BUDGET_BYTES)
285
- return normalized;
286
- const pathReference = normalized.match(/(?:^|\s)([\w.-]+(?:\/[\w.@-]+)+(?:\.[A-Za-z0-9]+)?)/)?.[1];
287
- const summary = normalized.slice(0, 220).replace(/\s+\S*$/, '').trim();
288
- return pathReference
289
- ? `${summary}… See ${pathReference} for details.`
290
- : `${summary}… Details omitted from always-loaded memory; keep source content in repository docs.`;
291
- }
292
- export function classifyProjectMemoryUpdate(value) {
293
- assertSafeMemoryText(value);
294
- const kind = classifyKind(value);
295
- const layer = classifyLayer(value);
296
- return {
297
- kind,
298
- layer,
299
- sectionTitle: CHANGE_KIND_SECTIONS[kind],
300
- summary: compactMemoryNote(value),
301
- reasonCode: `memory_update_${kind.replace('-', '_')}`,
302
- };
303
- }
304
- function classifyProjectMemoryUpdates(values) {
305
- const seen = new Set();
306
- const result = [];
307
- for (const value of values) {
308
- const classified = classifyProjectMemoryUpdate(value);
309
- const key = `${classified.kind}:${classified.summary.toLowerCase()}`;
310
- if (seen.has(key))
311
- continue;
312
- seen.add(key);
313
- result.push(classified);
314
- }
315
- return result;
316
- }
317
- function assertSafeMemoryText(value) {
318
- if (IP_BOUNDARY_PATTERN.test(value)) {
319
- throw new Error('Project memory input violates IP boundary guardrails.');
320
- }
321
- }
322
- export function createSafeProjectMemoryDiff(before, after) {
323
- const beforeLines = (before ?? '').split('\n');
324
- const afterLines = after.split('\n');
325
- const rows = [];
326
- const max = Math.max(beforeLines.length, afterLines.length);
327
- for (let index = 0; index < max; index++) {
328
- const oldLine = beforeLines[index];
329
- const newLine = afterLines[index];
330
- if (oldLine === newLine)
331
- continue;
332
- if (oldLine !== undefined)
333
- rows.push(`- ${oldLine}`);
334
- if (newLine !== undefined)
335
- rows.push(`+ ${newLine}`);
336
- if (rows.length >= 80) {
337
- rows.push('… diff truncated; inspect repository diff for full context.');
338
- break;
339
- }
340
- }
341
- const diff = rows.join('\n');
342
- assertSafeMemoryText(diff);
343
- return diff;
344
- }
345
- function assertSafeArchitectureReference(reference) {
346
- assertSafeMemoryText(reference.path);
347
- assertSafeMemoryText(reference.title);
348
- assertSafeMemoryText(reference.purpose);
349
- assertSafeMemoryText(reference.suggestedUsage);
350
- }
351
- function assertSafeProjectMemoryInput(memoryNotes, architectureReferences, bootstrapArchitecture) {
352
- memoryNotes.forEach(assertSafeMemoryText);
353
- architectureReferences.forEach(assertSafeArchitectureReference);
354
- bootstrapArchitecture?.universalLaws?.forEach(assertSafeMemoryText);
355
- bootstrapArchitecture?.businessContext?.forEach(assertSafeMemoryText);
356
- bootstrapArchitecture?.activeContext?.forEach(assertSafeMemoryText);
357
- bootstrapArchitecture?.repositoryMap?.forEach(assertSafeMemoryText);
358
- bootstrapArchitecture?.environmentPractices?.forEach(assertSafeMemoryText);
359
- bootstrapArchitecture?.platformMemoryContract?.forEach(assertSafeMemoryText);
360
- bootstrapArchitecture?.maintenanceRules?.forEach(assertSafeMemoryText);
361
- bootstrapArchitecture?.heuristics?.forEach(assertSafeMemoryText);
362
- bootstrapArchitecture?.importantReferences?.forEach(assertSafeArchitectureReference);
363
- bootstrapArchitecture?.architectureReferences?.forEach(assertSafeArchitectureReference);
364
- }
365
- function buildTemplate(target) {
366
- return [
367
- templateTitle(target),
368
- '',
369
- 'This file contains compact, always-loaded project memory. Keep detailed architecture in referenced docs.',
370
- '',
371
- ].join('\n');
372
- }
373
- function buildOwnedBlock(target, memoryNotes, architectureReferences, bootstrapArchitecture) {
374
- assertSafeProjectMemoryInput(memoryNotes, architectureReferences, bootstrapArchitecture);
375
- const laws = uniqueLines([
376
- ...DEFAULT_UNIVERSAL_LAWS,
377
- ...(bootstrapArchitecture?.universalLaws ?? []),
378
- ]);
379
- const notes = uniqueLines([
380
- ...(bootstrapArchitecture?.activeContext ?? []),
381
- ...memoryNotes.map(compactMemoryNote),
382
- ]);
383
- const classifiedUpdates = classifyProjectMemoryUpdates(memoryNotes);
384
- const businessContext = uniqueLines(bootstrapArchitecture?.businessContext ?? [
385
- 'Product/business context was not explicitly documented during bootstrap; treat conclusions as repository-inferred until a human updates this section.',
386
- ]);
387
- const repositoryMap = uniqueLines(bootstrapArchitecture?.repositoryMap ?? [
388
- 'Source, tests, package/build files, docs, CI, and runtime manifests are the permitted bootstrap sources for repository orientation.',
389
- ]);
390
- const environmentPractices = uniqueLines(bootstrapArchitecture?.environmentPractices ?? [
391
- 'Use `.env.example` for configuration shape only; do not read real environment files or credential-bearing files into always-loaded memory.',
392
- ]);
393
- const platformMemoryContract = uniqueLines(bootstrapArchitecture?.platformMemoryContract ?? [
394
- 'Semantic platform memory file must resolve to NEOCORTEX.md through a symlink; do not replace it with an unrelated wrapper or copied frontmatter.',
395
- 'Platform wrapper/frontmatter must be preserved via split/include/managed block while the semantic memory payload remains symlink-backed by NEOCORTEX.md.',
396
- ]);
397
- const maintenanceRules = uniqueLines(bootstrapArchitecture?.maintenanceRules ?? [
398
- 'Keep NEOCORTEX.md deterministic, compact, objective, and direct; store only summaries, indexes, and links to detailed docs.',
399
- 'Do not copy protected prompt bodies, proprietary wrappers, server-side internals, customer data, license keys, or long document bodies into always-loaded memory.',
400
- ]);
401
- const references = uniqueArchitectureReferences([
402
- ...(bootstrapArchitecture?.importantReferences ?? []),
403
- ...(bootstrapArchitecture?.architectureReferences ?? []),
404
- ...architectureReferences,
405
- ]);
406
- const heuristics = uniqueLines([
407
- ...DEFAULT_ARCHITECTURE_HEURISTICS,
408
- ...(bootstrapArchitecture?.heuristics ?? []),
409
- ]);
410
- const lawRows = laws.map((law) => `- ${law}`);
411
- const noteRows = notes.length > 0
412
- ? notes.map((note) => `- ${note}`)
413
- : ['- No active project notes recorded.'];
414
- const referenceSection = references.length > 0
415
- ? renderArchitectureReferenceTable(references)
416
- : 'No architecture references discovered.';
417
- const feedbackRows = classifiedUpdates.length > 0
418
- ? Object.entries(CHANGE_KIND_SECTIONS)
419
- .flatMap(([kind, sectionTitle]) => {
420
- const updates = classifiedUpdates.filter((entry) => entry.kind === kind);
421
- return updates.length > 0
422
- ? [`### ${sectionTitle}`, ...updates.map((entry) => `- [${entry.reasonCode}] ${entry.summary} (layer: ${entry.layer})`)]
423
- : [];
424
- })
425
- : ['- No durable change feedback recorded.'];
426
- return [
427
- markerStart(target),
428
- '## Universal Project Laws',
429
- ...lawRows,
430
- '',
431
- '## Business/Product Context',
432
- ...businessContext.map((line) => `- ${line}`),
433
- '',
434
- '## Active Context',
435
- ...noteRows,
436
- '',
437
- '## Durable Change Feedback',
438
- ...feedbackRows,
439
- '',
440
- '## Architecture References',
441
- referenceSection,
442
- '',
443
- '## Repository Map',
444
- ...repositoryMap.map((line) => `- ${line}`),
445
- '',
446
- '## Environment/Container/Production/Security/Dev Practices',
447
- ...environmentPractices.map((line) => `- ${line}`),
448
- '',
449
- '## Platform Memory Contract',
450
- ...platformMemoryContract.map((line) => `- ${line}`),
451
- `- NEOCORTEX.md is the canonical project memory source for every platform target; run \`${target.bidirectionalReference.feedbackTrigger}\` when durable context discovered in a platform wrapper should be fed back into canonical memory.`,
452
- '',
453
- '## Maintenance Rules',
454
- ...maintenanceRules.map((line) => `- ${line}`),
455
- '',
456
- '## Architecture Heuristics',
457
- ...heuristics.map((heuristic) => `- ${heuristic}`),
458
- markerEnd(target),
459
- ].join('\n');
460
- }
461
- const BOOTSTRAP_ARCHITECTURE_ROOTS = ['docs/architecture', 'docs/arquitetura-software'];
462
- const BOOTSTRAP_FALLBACK_DOCS = ['README.md', 'readme.md', 'architecture.md', 'docs/architecture.md'];
463
- const BOOTSTRAP_PACKAGE_FILES = ['package.json', 'pnpm-lock.yaml', 'package-lock.json', 'yarn.lock', 'tsconfig.json', 'vite.config.ts', 'turbo.json'];
464
- const BOOTSTRAP_RUNTIME_FILES = ['Dockerfile', 'docker-compose.yml', 'compose.yml', '.env.example', 'fly.toml'];
465
- const BOOTSTRAP_PLATFORM_FILES = ['AGENTS.md', 'CLAUDE.md', 'GEMINI.md', '.opencode/AGENTS.md', '.cursor/rules/neocortex.mdc'];
466
- function readOptional(projectRoot, relativePath, maxBytes = 24_000) {
467
- const fullPath = join(projectRoot, relativePath);
468
- try {
469
- if (!existsSync(fullPath) || lstatSync(fullPath).isDirectory())
470
- return undefined;
471
- const content = readFileSync(fullPath, 'utf8');
472
- return content.length <= maxBytes ? content : content.slice(0, maxBytes);
473
- }
474
- catch {
475
- return undefined;
476
- }
477
- }
478
- function memoryBackupPath(path, nonce, backupDirectory) {
479
- if (!backupDirectory)
480
- return `${path}.bak-${nonce}`;
481
- const directory = resolve(backupDirectory), info = lstatSync(directory);
482
- if (!info.isDirectory() || info.isSymbolicLink() || info.uid !== process.getuid?.()
483
- || (info.mode & 0o077) !== 0 || realpathSync(directory) !== directory)
484
- throw new Error('project-memory-backup-directory-unsafe');
485
- return join(directory, `${basename(path)}.bak-${nonce}`);
486
- }
487
- function writeFileWithBackup(path, content, backupDirectory) {
488
- mkdirSync(dirname(path), { recursive: true });
489
- const nonce = randomUUID();
490
- const tempPath = `${path}.tmp-${nonce}`;
491
- const backupPath = existsSync(path) ? memoryBackupPath(path, nonce, backupDirectory) : undefined;
492
- try {
493
- writeFileSync(tempPath, content, { encoding: 'utf8', flag: 'wx', mode: 0o600 });
494
- if (backupPath !== undefined)
495
- copyFileSync(path, backupPath, fsConstants.COPYFILE_EXCL);
496
- renameSync(tempPath, path);
497
- return backupPath;
498
- }
499
- catch (error) {
500
- rmSync(tempPath, { force: true });
501
- if (backupPath !== undefined && existsSync(backupPath)) {
502
- try {
503
- copyFileSync(backupPath, path);
504
- }
505
- catch { /* best-effort rollback */ }
506
- }
507
- throw error;
508
- }
509
- }
510
- function reasonCodesForNotes(memoryNotes) {
511
- return Array.from(new Set(classifyProjectMemoryUpdates(memoryNotes).map((entry) => entry.reasonCode)));
512
- }
513
- function safeExists(projectRoot, relativePath) {
514
- try {
515
- return existsSync(join(projectRoot, relativePath));
516
- }
517
- catch {
518
- return false;
519
- }
520
- }
521
- function listSourceChildren(projectRoot, relativePath) {
522
- try {
523
- return readdirSync(join(projectRoot, relativePath), { withFileTypes: true })
524
- .filter((entry) => !entry.name.startsWith('.') && !entry.name.toLowerCase().includes('secret'))
525
- .sort((a, b) => a.name.localeCompare(b.name))
526
- .slice(0, 8)
527
- .map((entry) => `${relativePath}/${entry.name}${entry.isDirectory() ? '/' : ''}`);
528
- }
529
- catch {
530
- return [];
531
- }
532
- }
533
- function collectMarkdownDocs(projectRoot, relativeDir, limit = 16) {
534
- try {
535
- const entries = readdirSync(join(projectRoot, relativeDir), { withFileTypes: true })
536
- .filter((entry) => !entry.name.startsWith('.') && !entry.name.startsWith('_'))
537
- .sort((a, b) => a.name.localeCompare(b.name));
538
- const files = [];
539
- for (const entry of entries) {
540
- const relativeEntry = `${relativeDir}/${entry.name}`;
541
- if (entry.isDirectory()) {
542
- files.push(...collectMarkdownDocs(projectRoot, relativeEntry, limit - files.length));
543
- }
544
- else if (/\.(md|markdown)$/i.test(entry.name)) {
545
- files.push(relativeEntry);
546
- }
547
- if (files.length >= limit)
548
- break;
549
- }
550
- return files;
551
- }
552
- catch {
553
- return [];
554
- }
555
- }
556
- function extractBootstrapTitle(path, content) {
557
- const heading = content.match(/^#\s+(.+?)\s*$/m)?.[1]?.trim();
558
- if (heading)
559
- return heading.replace(/[`*_#]/g, '');
560
- return path.split('/').pop()?.replace(/\.(md|markdown)$/i, '').replace(/[-_]+/g, ' ') ?? path;
561
- }
562
- function referencePurpose(path, content) {
563
- const stale = /\b(stale|deprecated|outdated|incerto|uncertain)\b/i.test(`${path}\n${content.slice(0, 2_000)}`);
564
- const firstParagraph = content
565
- .replace(/^---[\s\S]*?---\s*/, '')
566
- .split(/\n{2,}/)
567
- .map((part) => part.replace(/^#+\s+.+$/gm, '').replace(/[`*_>#]/g, '').replace(/\s+/g, ' ').trim())
568
- .find((part) => part.length > 0 && !part.startsWith('|'));
569
- const prefix = stale ? '[stale/uncertain evidence] ' : '';
570
- return `${prefix}${(firstParagraph ?? 'Architecture reference for this project.').slice(0, 180)}`;
571
- }
572
- function bootstrapSuggestedUsage(path) {
573
- const lower = path.toLowerCase();
574
- if (/deploy|infra|docker|production|runbook/.test(lower))
575
- return 'Use before changing deployment, runtime, containers, or production operations.';
576
- if (/security|auth|compliance/.test(lower))
577
- return 'Use before changing auth, compliance, or security-sensitive behavior.';
578
- if (/api|contract|state|schema/.test(lower))
579
- return 'Use before changing contracts, APIs, schemas, or persisted state.';
580
- return 'Use before implementation in this area; verify freshness when marked stale or uncertain.';
581
- }
582
- export function bootstrapArchitectureMemory(projectRoot) {
583
- const packageFiles = BOOTSTRAP_PACKAGE_FILES.filter((path) => safeExists(projectRoot, path));
584
- const runtimeFiles = BOOTSTRAP_RUNTIME_FILES.filter((path) => safeExists(projectRoot, path));
585
- const platformFiles = BOOTSTRAP_PLATFORM_FILES.filter((path) => safeExists(projectRoot, path));
586
- const sourceRoots = ['packages', 'src', 'apps', 'tests', '__tests__']
587
- .filter((path) => safeExists(projectRoot, path));
588
- const docs = [
589
- ...BOOTSTRAP_ARCHITECTURE_ROOTS.flatMap((root) => collectMarkdownDocs(projectRoot, root)),
590
- ...BOOTSTRAP_FALLBACK_DOCS.filter((path) => safeExists(projectRoot, path)),
591
- ];
592
- const references = docs.slice(0, 24).flatMap((path) => {
593
- const content = readOptional(projectRoot, path, 48_000);
594
- if (!content)
595
- return [];
596
- return [{
597
- path,
598
- title: extractBootstrapTitle(path, content),
599
- purpose: referencePurpose(path, content),
600
- suggestedUsage: bootstrapSuggestedUsage(path),
601
- source: BOOTSTRAP_ARCHITECTURE_ROOTS.some((root) => path.startsWith(`${root}/`)) ? 'architecture-root' : 'fallback-file',
602
- }];
603
- });
604
- const packageJson = readOptional(projectRoot, 'package.json');
605
- const packageName = packageJson?.match(/"name"\s*:\s*"([^"]+)"/)?.[1];
606
- const scripts = [...(packageJson?.matchAll(/"(test|build|lint|typecheck|dev)"\s*:/g) ?? [])].map((match) => match[1]);
607
- return {
608
- universalLaws: [
609
- 'Bootstrap canonical memory before creating or repairing platform memory wrappers when NEOCORTEX.md is absent.',
610
- 'Use only permitted repository sources for bootstrap: package/build files, architecture docs, README/deploy docs, .env.example, runtime manifests, CI, source/test layout, and platform memory wrappers.',
611
- ],
612
- businessContext: [
613
- packageName ? `Repository package name: ${packageName}.` : 'Repository product name was not declared in package metadata.',
614
- 'Business/product conclusions are bootstrap-inferred and must be refined from canonical docs or human input.',
615
- ],
616
- activeContext: [
617
- `Bootstrap scanned ${packageFiles.length} package/build files, ${references.length} architecture/readme docs, ${runtimeFiles.length} runtime/config templates, and ${platformFiles.length} platform memory wrappers.`,
618
- scripts.length > 0 ? `Detected quality scripts: ${scripts.sort().join(', ')}.` : 'No standard quality scripts were detected in package metadata.',
619
- ],
620
- repositoryMap: [
621
- packageFiles.length > 0 ? `Package/build files: ${packageFiles.join(', ')}.` : 'No package/build files detected in permitted bootstrap set.',
622
- sourceRoots.length > 0 ? `Source/test roots: ${sourceRoots.flatMap((root) => listSourceChildren(projectRoot, root)).slice(0, 16).join(', ')}.` : 'Source/test roots were not detected in standard locations.',
623
- platformFiles.length > 0 ? `Platform memory/wrappers: ${platformFiles.join(', ')}.` : 'No platform memory wrappers detected before bootstrap.',
624
- ],
625
- environmentPractices: [
626
- runtimeFiles.length > 0 ? `Runtime/config templates: ${runtimeFiles.join(', ')}.` : 'No Docker/compose/.env.example/runtime manifest detected in permitted bootstrap set.',
627
- 'Read `.env.example` only for configuration shape; never ingest real .env files, credentials, customer data, or license keys into memory.',
628
- 'Run repository quality scripts before commit and keep CI/deploy evidence referenced by path or URL rather than copied verbatim.',
629
- ],
630
- platformMemoryContract: [
631
- 'Semantic platform memory file must resolve to NEOCORTEX.md through a symlink; do not replace it with an unrelated wrapper or copied frontmatter.',
632
- 'Preserve wrappers/frontmatter via split/include/managed block while the semantic memory payload remains symlink-backed by NEOCORTEX.md.',
633
- ],
634
- maintenanceRules: [
635
- 'Generated memory must be deterministic, objective, direct, compact, and inside the target always-loaded context budget.',
636
- 'Cite long architecture docs by path with usage guidance; mark stale or uncertain docs when repository evidence indicates that status.',
637
- 'Do not copy protected prompt bodies, proprietary wrappers, server internals, secrets, customer data, license keys, or complete long documents.',
638
- ],
639
- architectureReferences: references,
640
- heuristics: [
641
- 'Before processing a story, consult the cited Architecture References whose path or suggested usage intersects the change.',
642
- 'When a referenced doc is marked stale/uncertain, validate against current package/runtime/source evidence before relying on it.',
643
- ],
644
- };
645
- }
646
- function replaceOwnedBlock(existingContent, target, ownedBlock) {
647
- const start = markerStart(target);
648
- const end = markerEnd(target);
649
- const startIndex = existingContent.indexOf(start);
650
- const endIndex = existingContent.indexOf(end);
651
- if (startIndex >= 0 && endIndex >= startIndex) {
652
- const afterEnd = endIndex + end.length;
653
- return `${existingContent.slice(0, startIndex)}${ownedBlock}${existingContent.slice(afterEnd)}`;
654
- }
655
- const separator = existingContent.endsWith('\n') ? '\n' : '\n\n';
656
- return `${existingContent}${separator}${ownedBlock}\n`;
657
- }
658
- function frontmatterForWrapper(relativeFile) {
659
- if (relativeFile.endsWith('.mdc')) {
660
- return '---\ndescription: Neocortex project memory\nalwaysApply: true\n---\n\n';
661
- }
662
- if (relativeFile.endsWith('.toml')) {
663
- return '[agent]\nname = "neocortex"\ndescription = "Neocortex project memory wrapper"\n\n';
664
- }
665
- if (relativeFile.endsWith('.agent.md')) {
666
- return '---\nname: neocortex\ndescription: Neocortex project memory wrapper\n---\n\n';
667
- }
668
- return '';
669
- }
670
- function relativeCanonicalLink(projectRoot, relativeFile) {
671
- const fromDir = dirname(join(projectRoot, relativeFile));
672
- const toFile = join(projectRoot, CANONICAL_PROJECT_MEMORY_SOURCE);
673
- const target = relative(fromDir, toFile);
674
- return target.length > 0 ? target : CANONICAL_PROJECT_MEMORY_SOURCE;
675
- }
676
- export function shouldUseManagedWrapper(target, relativeFile) {
677
- if (target.targetId === 'canonical')
678
- return true;
679
- if (target.requiresWrapperPreservation || target.linkRequirement.requiresWrapperPreservation)
680
- return true;
681
- if (target.linkRequirement.wrapperAllowed)
682
- return true;
683
- if (relativeFile === 'AGENTS.md')
684
- return true;
685
- if (relativeFile.includes('/agents/') || relativeFile.includes('/skills/'))
686
- return true;
687
- if (relativeFile.endsWith('.toml') || relativeFile.endsWith('.mdc'))
688
- return true;
689
- if (relativeFile.endsWith('.agent.md') || relativeFile.endsWith('.instructions.md'))
690
- return true;
691
- return false;
692
- }
693
- function isPolicySymlinkCapable(target) {
694
- return target.linkRequirement.symlinkBacked && target.linkRequirement.semanticFileRequired;
695
- }
696
- function safeSymlinkFailureReason(reason) {
697
- if (/EPERM|EACCES|operation not permitted|permission/i.test(reason))
698
- return 'EPERM: symlink permission denied';
699
- if (/ENOSYS|ENOTSUP|EOPNOTSUPP|not supported/i.test(reason))
700
- return 'symlink filesystem unsupported';
701
- return 'symlink capability unavailable';
702
- }
703
- function detectSymlinkCapability(projectRoot, target, relativeFile, override, dryRun = false) {
704
- if (override !== undefined)
705
- return override;
706
- const targetPolicyAllowsSymlink = isPolicySymlinkCapable(target);
707
- if (!targetPolicyAllowsSymlink) {
708
- return {
709
- os: platform(),
710
- supported: false,
711
- permission: 'unknown',
712
- filesystem: 'unknown',
713
- targetPolicy: target.linkRequirement.policy,
714
- targetPolicyAllowsSymlink,
715
- requiresWrapperPreservation: shouldUseManagedWrapper(target, relativeFile),
716
- reason: `target policy ${target.linkRequirement.policy} does not allow semantic symlink orchestration`,
717
- };
718
- }
719
- if (dryRun) {
720
- return {
721
- os: platform(),
722
- supported: true,
723
- permission: 'unknown',
724
- filesystem: 'unknown',
725
- targetPolicy: target.linkRequirement.policy,
726
- targetPolicyAllowsSymlink,
727
- requiresWrapperPreservation: shouldUseManagedWrapper(target, relativeFile),
728
- reason: 'dry-run does not probe filesystem symlink support',
729
- };
730
- }
731
- const probeDir = join(projectRoot, '.neocortex', 'tmp');
732
- const nonce = randomUUID();
733
- const probeSource = join(probeDir, `symlink-capability-source-${nonce}`);
734
- const probeLink = join(probeDir, `symlink-capability-link-${nonce}`);
735
- try {
736
- mkdirSync(probeDir, { recursive: true });
737
- writeFileSync(probeSource, 'probe', { encoding: 'utf8', flag: 'wx', mode: 0o600 });
738
- symlinkSync(probeSource, probeLink);
739
- const supported = lstatSync(probeLink).isSymbolicLink() && statSync(probeLink).isFile();
740
- return {
741
- os: platform(),
742
- supported,
743
- permission: supported ? 'ok' : 'unknown',
744
- filesystem: supported ? 'supports-symlink' : 'no-symlink-support',
745
- targetPolicy: target.linkRequirement.policy,
746
- targetPolicyAllowsSymlink,
747
- requiresWrapperPreservation: shouldUseManagedWrapper(target, relativeFile),
748
- reason: supported ? undefined : 'filesystem probe did not create a resolvable symbolic link',
749
- };
750
- }
751
- catch (error) {
752
- const message = error instanceof Error ? error.message : String(error);
753
- const denied = /EPERM|EACCES|operation not permitted|permission/i.test(message);
754
- return {
755
- os: platform(),
756
- supported: false,
757
- permission: denied ? 'denied' : 'unknown',
758
- filesystem: 'no-symlink-support',
759
- targetPolicy: target.linkRequirement.policy,
760
- targetPolicyAllowsSymlink,
761
- requiresWrapperPreservation: shouldUseManagedWrapper(target, relativeFile),
762
- reason: safeSymlinkFailureReason(message),
763
- };
764
- }
765
- finally {
766
- rmSync(probeLink, { force: true });
767
- rmSync(probeSource, { force: true });
768
- }
769
- }
770
- function buildManagedWrapperContent(target, relativeFile, existingContent, projectRoot) {
771
- const canonicalLink = relativeCanonicalLink(projectRoot, relativeFile);
772
- const baseContent = existingContent ?? `${frontmatterForWrapper(relativeFile)}# ${target.platformName} Neocortex Memory Wrapper\n\n`;
773
- const ownedBlock = [
774
- markerStart(target),
775
- '## Canonical Project Memory Source',
776
- '',
777
- `This platform wrapper preserves host-specific frontmatter/instructions while resolving project memory to \`${canonicalLink}\`.`,
778
- '',
779
- `- Canonical source: \`${CANONICAL_PROJECT_MEMORY_SOURCE}\``,
780
- `- Symlink-backed/source-of-truth file: \`${canonicalLink}\``,
781
- `- Feedback loop: read \`${canonicalLink}\` for shared context and run \`${target.bidirectionalReference.feedbackTrigger}\` when durable platform findings should update \`${CANONICAL_PROJECT_MEMORY_SOURCE}\`.`,
782
- '- Human-authored content outside this managed block is preserved byte-for-byte.',
783
- markerEnd(target),
784
- ].join('\n');
785
- return replaceOwnedBlock(baseContent, target, ownedBlock);
786
- }
787
- function writeCompatibilityFallback(projectRoot, relativeFile, canonicalContent, capability, fallbackReason, dryRun = false) {
788
- const filePath = join(projectRoot, relativeFile);
789
- const publicFallbackReason = safeSymlinkFailureReason(fallbackReason);
790
- const remediationDebt = [
791
- '[NEOCORTEX COMPATIBILITY REMEDIATION]',
792
- 'Materialized project memory fallback because semantic symlink orchestration failed.',
793
- 'This is not product mode; repair host permissions/filesystem support and restore the symlink to NEOCORTEX.md.',
794
- 'WARNING: materialized fallback is remediation debt, not a silent mode; run *update-memory for durable context and repair this file back to canonical NEOCORTEX.md resolution.',
795
- `Fallback reason: ${publicFallbackReason}`,
796
- '',
797
- ].join('\n');
798
- const content = `${remediationDebt}${canonicalContent}`;
799
- const existingContent = existsSync(filePath) ? readFileSync(filePath, 'utf8') : undefined;
800
- const changed = existingContent !== content;
801
- if (changed && !dryRun) {
802
- if (existsSync(filePath) && lstatSync(filePath).isSymbolicLink())
803
- unlinkSync(filePath);
804
- writeFileWithBackup(filePath, content);
805
- }
806
- return {
807
- filePath,
808
- relativeFile,
809
- changed,
810
- content,
811
- mode: 'compatibility-materialized-fallback',
812
- canonicalSource: CANONICAL_PROJECT_MEMORY_SOURCE,
813
- symlinkCapability: capability,
814
- fallbackReason: publicFallbackReason,
815
- remediationDebt: `Repair ${relativeFile} so it is a symlink to ${CANONICAL_PROJECT_MEMORY_SOURCE}; fallback is compatibility remediation only.`,
816
- };
817
- }
818
- function writeSemanticSymlink(projectRoot, relativeFile, target, canonicalContent, symlinkCapability, dryRun = false) {
819
- const filePath = join(projectRoot, relativeFile);
820
- const linkTarget = relativeCanonicalLink(projectRoot, relativeFile);
821
- if (!dryRun)
822
- mkdirSync(dirname(filePath), { recursive: true });
823
- const capability = detectSymlinkCapability(projectRoot, target, relativeFile, symlinkCapability, dryRun);
824
- if (!capability.supported) {
825
- return writeCompatibilityFallback(projectRoot, relativeFile, canonicalContent, capability, capability.reason ?? 'symlink capability detection reported unsupported host', dryRun);
826
- }
827
- if (dryRun) {
828
- let changed = true;
829
- try {
830
- changed = !(existsSync(filePath) && lstatSync(filePath).isSymbolicLink() && readlinkSync(filePath) === linkTarget);
831
- }
832
- catch {
833
- changed = true;
834
- }
835
- return {
836
- filePath,
837
- relativeFile,
838
- changed,
839
- content: canonicalContent,
840
- mode: 'semantic-symlink',
841
- canonicalSource: CANONICAL_PROJECT_MEMORY_SOURCE,
842
- symlinkCapability: capability,
843
- };
844
- }
845
- let changed = false;
846
- try {
847
- if (existsSync(filePath)) {
848
- const stat = lstatSync(filePath);
849
- if (stat.isSymbolicLink() && readlinkSync(filePath) === linkTarget) {
850
- changed = false;
851
- }
852
- else {
853
- unlinkSync(filePath);
854
- symlinkSync(linkTarget, filePath);
855
- changed = true;
856
- }
857
- }
858
- else {
859
- symlinkSync(linkTarget, filePath);
860
- changed = true;
861
- }
862
- return {
863
- filePath,
864
- relativeFile,
865
- changed,
866
- content: readFileSync(filePath, 'utf8'),
867
- mode: 'semantic-symlink',
868
- canonicalSource: CANONICAL_PROJECT_MEMORY_SOURCE,
869
- symlinkCapability: capability,
870
- };
871
- }
872
- catch (error) {
873
- const reason = error instanceof Error ? error.message : String(error);
874
- const publicReason = safeSymlinkFailureReason(reason);
875
- return writeCompatibilityFallback(projectRoot, relativeFile, canonicalContent, {
876
- ...capability,
877
- supported: false,
878
- permission: /EPERM|EACCES|operation not permitted|permission/i.test(reason) ? 'denied' : capability.permission,
879
- filesystem: 'no-symlink-support',
880
- reason: publicReason,
881
- }, publicReason, dryRun);
882
- }
883
- }
884
- function enforceBudget(target, content, memoryNotes, architectureReferences, bootstrapArchitecture) {
885
- const fits = (value) => value.split('\n').length <= target.budget.maxLines &&
886
- Buffer.byteLength(value, 'utf8') <= target.budget.maxBytes;
887
- if (fits(content))
888
- return content;
889
- const compactBlock = buildOwnedBlock(target, memoryNotes.slice(0, 8), architectureReferences.slice(0, 12), bootstrapArchitecture);
890
- const compactContent = replaceOwnedBlock(content.includes(markerStart(target)) ? content : buildTemplate(target), target, compactBlock);
891
- return fits(compactContent) ? compactContent : compactContent.slice(0, target.budget.maxBytes);
892
- }
893
- export function buildProjectMemoryContent(target, existingContent, memoryNotes = [], architectureReferences = [], bootstrapArchitecture) {
894
- const baseContent = existingContent ?? buildTemplate(target);
895
- const ownedBlock = buildOwnedBlock(target, memoryNotes, architectureReferences, bootstrapArchitecture);
896
- const content = replaceOwnedBlock(baseContent, target, ownedBlock);
897
- return enforceBudget(target, content, memoryNotes, architectureReferences, bootstrapArchitecture);
898
- }
899
- export function writeProjectMemory(options) {
900
- const target = getProjectMemoryTarget(options.targetId);
901
- const fs = options.fs ?? createNodeProjectMemoryFs(options.backupDirectory);
902
- const filePath = fs.join(options.projectRoot, target.primaryFile);
903
- if (options.fs === undefined && target.targetId === 'canonical') {
904
- assertProjectMemoryPaths(options.projectRoot, [CANONICAL_PROJECT_MEMORY_SOURCE]);
905
- }
906
- if (target.targetId !== 'canonical' && options.fs === undefined) {
907
- const result = writeProjectMemoryTargetFiles(options).files.find((file) => file.relativeFile === target.primaryFile);
908
- if (result === undefined)
909
- throw new Error(`Primary project memory file was not written for ${target.targetId}.`);
910
- return { filePath: result.filePath, changed: result.changed, content: result.content };
911
- }
912
- const existingContent = fs.readFile(filePath);
913
- const bootstrapArchitecture = options.bootstrapArchitecture ?? (options.fs === undefined && target.targetId === 'canonical' && existingContent === undefined
914
- ? bootstrapArchitectureMemory(options.projectRoot)
915
- : undefined);
916
- const content = target.targetId === 'canonical' && existingContent !== undefined
917
- ? (hasCanonicalUpdateInput(options) ? mergeCanonicalRefreshUpdate(target, existingContent, options) : existingContent)
918
- : buildProjectMemoryContent(target, existingContent, options.memoryNotes, options.architectureReferences, bootstrapArchitecture);
919
- if (existingContent === content) {
920
- return { filePath, changed: false, content };
921
- }
922
- const writeResult = options.dryRun ? undefined : fs.writeFileAtomic(filePath, content);
923
- return {
924
- filePath,
925
- changed: true,
926
- content,
927
- safeDiff: createSafeProjectMemoryDiff(existingContent, content),
928
- backupPath: writeResult?.backupPath,
929
- reasonCodes: reasonCodesForNotes(options.memoryNotes ?? []),
930
- };
931
- }
932
- export function writeCanonicalProjectMemory(options) {
933
- return writeProjectMemory({ ...options, targetId: 'canonical' });
934
- }
935
- export function writeProjectMemoryTargetFiles(options) {
936
- if (options.fs !== undefined) {
937
- throw new Error('writeProjectMemoryTargetFiles requires the node filesystem to manage symlinks safely.');
938
- }
939
- const target = getProjectMemoryTarget(options.targetId);
940
- const canonicalTarget = getProjectMemoryTarget('canonical');
941
- if (target.secondaryFiles.some((file) => file === null || (file.scope !== 'project' && file.scope !== 'user') || !isValidScopedRelativePath(file.path))) {
942
- invalidProjectMemoryPath('invalid');
943
- }
944
- const relativeFiles = Array.from(new Set([
945
- target.primaryFile,
946
- ...(target.targetId === 'canonical' ? [] : target.secondaryFiles.filter((file) => file.scope === 'project').map((file) => file.path)),
947
- ]));
948
- assertProjectMemoryPaths(options.projectRoot, [CANONICAL_PROJECT_MEMORY_SOURCE, ...relativeFiles, '.neocortex/tmp']);
949
- const canonicalFilePath = join(options.projectRoot, CANONICAL_PROJECT_MEMORY_SOURCE);
950
- const existingCanonicalContent = existsSync(canonicalFilePath) ? readFileSync(canonicalFilePath, 'utf8') : undefined;
951
- const bootstrapArchitecture = options.bootstrapArchitecture ?? (existingCanonicalContent === undefined ? bootstrapArchitectureMemory(options.projectRoot) : undefined);
952
- const canonicalContent = existingCanonicalContent !== undefined
953
- ? (hasCanonicalUpdateInput(options) ? mergeCanonicalRefreshUpdate(canonicalTarget, existingCanonicalContent, options) : existingCanonicalContent)
954
- : buildProjectMemoryContent(canonicalTarget, existingCanonicalContent, options.memoryNotes, options.architectureReferences, bootstrapArchitecture);
955
- const canonicalChanged = !existsSync(canonicalFilePath) || readFileSync(canonicalFilePath, 'utf8') !== canonicalContent;
956
- let canonicalBackupPath;
957
- if (canonicalChanged && !options.dryRun) {
958
- canonicalBackupPath = writeFileWithBackup(canonicalFilePath, canonicalContent, options.backupDirectory);
959
- }
960
- if (target.targetId === 'canonical') {
961
- return {
962
- targetId: target.targetId,
963
- canonicalFilePath,
964
- changed: canonicalChanged,
965
- files: [{
966
- filePath: canonicalFilePath,
967
- relativeFile: CANONICAL_PROJECT_MEMORY_SOURCE,
968
- changed: canonicalChanged,
969
- content: canonicalContent,
970
- mode: 'canonical',
971
- canonicalSource: CANONICAL_PROJECT_MEMORY_SOURCE,
972
- safeDiff: createSafeProjectMemoryDiff(existingCanonicalContent, canonicalContent),
973
- backupPath: canonicalBackupPath,
974
- reasonCodes: reasonCodesForNotes(options.memoryNotes ?? []),
975
- }],
976
- };
977
- }
978
- const files = relativeFiles.map((relativeFile) => {
979
- assertProjectMemoryPaths(options.projectRoot, [relativeFile, '.neocortex/tmp']);
980
- if (!shouldUseManagedWrapper(target, relativeFile)) {
981
- return writeSemanticSymlink(options.projectRoot, relativeFile, target, canonicalContent, options.symlinkCapability, options.dryRun);
982
- }
983
- const filePath = join(options.projectRoot, relativeFile);
984
- const existingContent = existsSync(filePath) ? readFileSync(filePath, 'utf8') : undefined;
985
- const content = buildManagedWrapperContent(target, relativeFile, existingContent, options.projectRoot);
986
- const changed = existingContent !== content;
987
- if (changed && !options.dryRun) {
988
- writeFileWithBackup(filePath, content, options.backupDirectory);
989
- }
990
- return {
991
- filePath,
992
- relativeFile,
993
- changed,
994
- content,
995
- mode: 'managed-wrapper',
996
- canonicalSource: CANONICAL_PROJECT_MEMORY_SOURCE,
997
- symlinkCapability: detectSymlinkCapability(options.projectRoot, target, relativeFile, options.symlinkCapability, options.dryRun),
998
- reasonCodes: reasonCodesForNotes(options.memoryNotes ?? []),
999
- };
1000
- });
1001
- return {
1002
- targetId: target.targetId,
1003
- canonicalFilePath,
1004
- files,
1005
- changed: canonicalChanged || files.some((file) => file.changed),
1006
- };
1007
- }
1
+ import{randomUUID as U}from"node:crypto";import{constants as V,existsSync as g,lstatSync as C,mkdirSync as O,copyFileSync as j,readdirSync as Q,readFileSync as E,readlinkSync as ee,realpathSync as we,renameSync as te,rmSync as N,statSync as Ee,symlinkSync as B,unlinkSync as re,writeFileSync as q}from"node:fs";import{platform as M}from"node:os";import{basename as Re,dirname as I,join as p,relative as Se,resolve as Ce}from"node:path";import{CANONICAL_PROJECT_MEMORY_SOURCE as b,getProjectMemoryTarget as W,renderArchitectureReferenceTable as Pe}from"./shared-project-memory-types.js";import{assertProjectMemoryPaths as F,invalidProjectMemoryPath as $e,isValidScopedRelativePath as Te}from"./project-memory-path-scope.js";function ne(e){return(e.memoryNotes?.length??0)>0||(e.architectureReferences?.length??0)>0||e.bootstrapArchitecture!==void 0}const X="<!-- NEOCORTEX-REFRESH-UPDATES-START -->",z="<!-- NEOCORTEX-REFRESH-UPDATES-END -->";function A(e,t){return e.split(t).length-1}function ve(e){ce(e.memoryNotes??[],e.architectureReferences??[],e.bootstrapArchitecture);const t=G(e.memoryNotes??[]).map(r=>`- [${r.reasonCode}] ${r.summary} (layer: ${r.layer})`),n=(e.architectureReferences??[]).map(r=>`- [architecture-reference] \`${r.path}\`: ${r.title}; ${r.purpose}; use: ${r.suggestedUsage}`),o=e.bootstrapArchitecture,s=o===void 0?[]:[...(o.universalLaws??[]).map(r=>`- [bootstrap-law] ${r}`),...(o.businessContext??[]).map(r=>`- [bootstrap-business] ${r}`),...(o.activeContext??[]).map(r=>`- [bootstrap-context] ${r}`),...(o.repositoryMap??[]).map(r=>`- [bootstrap-repository] ${r}`),...(o.environmentPractices??[]).map(r=>`- [bootstrap-environment] ${r}`),...(o.platformMemoryContract??[]).map(r=>`- [bootstrap-platform] ${r}`),...(o.maintenanceRules??[]).map(r=>`- [bootstrap-maintenance] ${r}`),...(o.heuristics??[]).map(r=>`- [bootstrap-heuristic] ${r}`)];return w([...t,...n,...s])}function xe(e,t){if(t.split(`
2
+ `).length>e.budget.maxLines||Buffer.byteLength(t,"utf8")>e.budget.maxBytes)throw new Error("Canonical project memory update exceeds its context budget; refusing to truncate existing content.")}function oe(e,t,n){const o=ve(n);if(o.length===0)return t;const s=t.includes(`\r
3
+ `)?`\r
4
+ `:`
5
+ `,r=$(e),a=L(e),c=A(t,r),u=A(t,a);if(c!==1||u!==1||t.indexOf(a)<t.indexOf(r))throw new Error("Canonical project memory has ambiguous managed-block markers; refusing refresh.");const l=A(t,X),i=A(t,z);if(l!==i||l>1)throw new Error("Canonical project memory has ambiguous refresh-update markers; refusing refresh.");let d;if(l===0){const m=t.indexOf(a),h=t.slice(0,m),k=[X,"## Durable Refresh Updates",...o,z,""].join(s);d=`${h}${h.endsWith(s)?"":s}${k}${t.slice(m)}`}else{const m=t.indexOf(X),h=t.indexOf(z),k=t.indexOf(r),R=t.indexOf(a);if(m<k||h>R||h<m)throw new Error("Canonical refresh-update block is outside the managed block; refusing refresh.");const _=t.slice(m,h),D=new Set(_.split(/\r?\n/).map(S=>S.trim().toLowerCase())),f=o.filter(S=>!D.has(S.trim().toLowerCase()));if(f.length===0)return t;const x=t.slice(0,h);d=`${x}${x.endsWith(s)?"":s}${f.join(s)}${s}${t.slice(h)}`}return xe(e,d),d}function Oe(e){return{readFile(t){try{return E(t,"utf8")}catch{return}},writeFileAtomic(t,n){O(I(t),{recursive:!0});const o=U(),s=`${t}.tmp-${o}`,r=g(t)?fe(t,o,e):void 0;try{return q(s,n,{encoding:"utf8",flag:"wx",mode:384}),r!==void 0&&j(t,r,V.COPYFILE_EXCL),te(s,t),{backupPath:r}}catch(a){if(N(s,{force:!0}),r!==void 0&&g(r))try{j(r,t)}catch{}throw a}},join(...t){return p(...t)}}}function $(e){return`<!-- ${e.ownedBlockName}-START -->`}function L(e){return`<!-- ${e.ownedBlockName}-END -->`}function w(e){const t=new Set,n=[];for(const o of e){const s=o.trim().replace(/\s+/g," ");s.length===0||t.has(s.toLowerCase())||(t.add(s.toLowerCase()),n.push(s))}return n}function je(e){const t=new Set,n=[];for(const o of e)t.has(o.path)||(t.add(o.path),n.push(o));return n}function Ne(e){switch(e.templateKind){case"neocortex-md":return"# NEOCORTEX.md Project Memory";case"claude-md":return"# CLAUDE.md Project Memory";case"agents-md":return"# AGENTS.md Project Instructions";case"gemini-md":return"# GEMINI.md Project Memory";case"copilot-instructions":return"# GitHub Copilot Repository Instructions";case"cursor-rule":return`---
6
+ description: Neocortex project memory
7
+ alwaysApply: true
8
+ ---
9
+
10
+ # Cursor Project Memory`}}const Me=["Keep always-loaded memory compact: laws, active context, and references only.","Preserve human-authored instructions outside this owned block.","Prefer repository SSoT docs over duplicating architecture details here."],Ie=["Consult `docs/architecture/` before changes that affect architecture, contracts, data, security, performance, UX, or infrastructure.","Reference architecture documents by repository path, such as `docs/architecture/architecture.md`, before changing related implementation.","Summarize decisions and trade-offs in NEOCORTEX.md; do not paste complete architecture documents into memory.","Update memory for durable cross-layer changes across skills/agents, frontend, backend, API, DB, schemas, envs, DevOps, cyber security, testing/QA, observability, product, docs, and platform contracts.","Prioritize compact summaries of durable decisions, public contracts, operational risks, debts, command behavior, and repository paths; skip transient logs, raw payloads, secrets, private prompts, long documents, and duplicated canonical sources.","Keep platform memory files symlink-backed or include-backed to NEOCORTEX.md when supported."],it=["skills-agents","frontend","backend","api","db","schemas","envs","devops","cyber-security","testing-qa","observability","product-business","docs-architecture","platform-memory","command-behavior","generic"],Ae=360,se={feature:"Features",rule:"Rules/Policies",flag:"Flags",route:"Routes",api:"APIs/Contracts","db-schema":"DB/Schema",architecture:"Architecture",debt:"Debts",decision:"Decisions",generic:"General Updates"},Le=/\b(private|proprietary|server[- ]only|system)\s+prompt\b|\b(api[-_ ]?key|license[-_ ]?key|password|authorization|bearer|token|secret)\s*[:=]|\b(ghp_[A-Za-z0-9_]+|sk-[A-Za-z0-9_-]+|xox[baprs]-[A-Za-z0-9-]+)\b|\b(customer|cliente)\s+(payload|record|email|phone)\b|\b(customer|cliente)\s+(data|dados)\s+(payload|record|email|phone)\b|\b(customer|cliente)\s+(data|dados)\s*[:=]|\braw\s+payload\b/i;function _e(e){const t=e.toLowerCase();return/\b(project memory|canonical memory|memory heuristic|neocortex\.md|\*update-memory)\b/.test(t)?"decision":/\b(feature|capability|funcionalidade|release)\b/.test(t)?"feature":/\b(rule|policy|guardrail|regra|lei|must|never)\b/.test(t)?"rule":/\b(flag|feature[- ]?flag|toggle|env var|environment variable)\b/.test(t)?"flag":/\b(api|contract|schema contract|graphql|rest|rpc|webhook)\b/.test(t)?"api":/\b(db|database|schema|migration|table|column|prisma|sql)\b/.test(t)?"db-schema":/\b(decision|decisão|decisao|accepted|rejected|trade[- ]?off)\b/.test(t)?"decision":/\b(route|endpoint route|url path|pathname)\b|\/(api|admin|portal|v\d+)\//.test(t)?"route":/\b(architecture|arquitetura|adr|topology|infra|boundary)\b/.test(t)?"architecture":/\b(debt|follow[- ]?up|todo|tech[- ]?debt|dívida|divida)\b/.test(t)?"debt":"generic"}function De(e){const t=e.toLowerCase();return/\b(neocortex\.md|project memory|canonical memory|memory wrapper|symlink|owned block|memory heuristic)\b/.test(t)?"platform-memory":/\b(command|cli|\*update-memory|\*create-epic|\*arch-plan|workflow behavior)\b/.test(t)?"command-behavior":/\b(skill|agent|subagent|orchestrator|opencode|codex|claude|gemini|cursor)\b/.test(t)?"skills-agents":/\b(frontend|ui|ux|react|component|design[- ]?system|css|accessibility|a11y)\b/.test(t)?"frontend":/\b(backend|server|service|worker|job|queue)\b/.test(t)?"backend":/\b(api|endpoint|rest|graphql|rpc|webhook|contract)\b/.test(t)?"api":/\b(db|database|table|column|migration|sql|prisma)\b/.test(t)?"db":/\b(schema|schemas|zod|json schema|protobuf|openapi)\b/.test(t)?"schemas":/\b(env|environment|\.env\.example|config|configuration|feature[- ]?flag)\b/.test(t)?"envs":/\b(devops|deploy|deployment|ci|cd|docker|container|kubernetes|infra|terraform|rollback)\b/.test(t)?"devops":/\b(security|cyber|auth|authorization|permission|compliance|vulnerability|secret|token redaction)\b/.test(t)?"cyber-security":/\b(test|testing|qa|e2e|unit|integration|snapshot|property[- ]?based|flaky)\b/.test(t)?"testing-qa":/\b(observability|metric|metrics|log|logging|trace|tracing|alert|monitoring|slo)\b/.test(t)?"observability":/\b(product|business|user journey|onboarding|pricing|tenant|customer-facing)\b/.test(t)?"product-business":/\b(docs|documentation|architecture|arquitetura|adr|runbook|decision record)\b/.test(t)?"docs-architecture":"generic"}function ae(e){const t=e.trim().replace(/\s+/g," ");if(Buffer.byteLength(t,"utf8")<=Ae)return t;const n=t.match(/(?:^|\s)([\w.-]+(?:\/[\w.@-]+)+(?:\.[A-Za-z0-9]+)?)/)?.[1],o=t.slice(0,220).replace(/\s+\S*$/,"").trim();return n?`${o}\u2026 See ${n} for details.`:`${o}\u2026 Details omitted from always-loaded memory; keep source content in repository docs.`}function Ue(e){y(e);const t=_e(e),n=De(e);return{kind:t,layer:n,sectionTitle:se[t],summary:ae(e),reasonCode:`memory_update_${t.replace("-","_")}`}}function G(e){const t=new Set,n=[];for(const o of e){const s=Ue(o),r=`${s.kind}:${s.summary.toLowerCase()}`;t.has(r)||(t.add(r),n.push(s))}return n}function y(e){if(Le.test(e))throw new Error("Project memory input violates IP boundary guardrails.")}function ie(e,t){const n=(e??"").split(`
11
+ `),o=t.split(`
12
+ `),s=[],r=Math.max(n.length,o.length);for(let c=0;c<r;c++){const u=n[c],l=o[c];if(u!==l&&(u!==void 0&&s.push(`- ${u}`),l!==void 0&&s.push(`+ ${l}`),s.length>=80)){s.push("\u2026 diff truncated; inspect repository diff for full context.");break}}const a=s.join(`
13
+ `);return y(a),a}function H(e){y(e.path),y(e.title),y(e.purpose),y(e.suggestedUsage)}function ce(e,t,n){e.forEach(y),t.forEach(H),n?.universalLaws?.forEach(y),n?.businessContext?.forEach(y),n?.activeContext?.forEach(y),n?.repositoryMap?.forEach(y),n?.environmentPractices?.forEach(y),n?.platformMemoryContract?.forEach(y),n?.maintenanceRules?.forEach(y),n?.heuristics?.forEach(y),n?.importantReferences?.forEach(H),n?.architectureReferences?.forEach(H)}function ue(e){return[Ne(e),"","This file contains compact, always-loaded project memory. Keep detailed architecture in referenced docs.",""].join(`
14
+ `)}function de(e,t,n,o){ce(t,n,o);const s=w([...Me,...o?.universalLaws??[]]),r=w([...o?.activeContext??[],...t.map(ae)]),a=G(t),c=w(o?.businessContext??["Product/business context was not explicitly documented during bootstrap; treat conclusions as repository-inferred until a human updates this section."]),u=w(o?.repositoryMap??["Source, tests, package/build files, docs, CI, and runtime manifests are the permitted bootstrap sources for repository orientation."]),l=w(o?.environmentPractices??["Use `.env.example` for configuration shape only; do not read real environment files or credential-bearing files into always-loaded memory."]),i=w(o?.platformMemoryContract??["Semantic platform memory file must resolve to NEOCORTEX.md through a symlink; do not replace it with an unrelated wrapper or copied frontmatter.","Platform wrapper/frontmatter must be preserved via split/include/managed block while the semantic memory payload remains symlink-backed by NEOCORTEX.md."]),d=w(o?.maintenanceRules??["Keep NEOCORTEX.md deterministic, compact, objective, and direct; store only summaries, indexes, and links to detailed docs.","Do not copy protected prompt bodies, proprietary wrappers, server-side internals, customer data, license keys, or long document bodies into always-loaded memory."]),m=je([...o?.importantReferences??[],...o?.architectureReferences??[],...n]),h=w([...Ie,...o?.heuristics??[]]),k=s.map(f=>`- ${f}`),R=r.length>0?r.map(f=>`- ${f}`):["- No active project notes recorded."],_=m.length>0?Pe(m):"No architecture references discovered.",D=a.length>0?Object.entries(se).flatMap(([f,x])=>{const S=a.filter(P=>P.kind===f);return S.length>0?[`### ${x}`,...S.map(P=>`- [${P.reasonCode}] ${P.summary} (layer: ${P.layer})`)]:[]}):["- No durable change feedback recorded."];return[$(e),"## Universal Project Laws",...k,"","## Business/Product Context",...c.map(f=>`- ${f}`),"","## Active Context",...R,"","## Durable Change Feedback",...D,"","## Architecture References",_,"","## Repository Map",...u.map(f=>`- ${f}`),"","## Environment/Container/Production/Security/Dev Practices",...l.map(f=>`- ${f}`),"","## Platform Memory Contract",...i.map(f=>`- ${f}`),`- NEOCORTEX.md is the canonical project memory source for every platform target; run \`${e.bidirectionalReference.feedbackTrigger}\` when durable context discovered in a platform wrapper should be fed back into canonical memory.`,"","## Maintenance Rules",...d.map(f=>`- ${f}`),"","## Architecture Heuristics",...h.map(f=>`- ${f}`),L(e)].join(`
15
+ `)}const le=["docs/architecture","docs/arquitetura-software"],Be=["README.md","readme.md","architecture.md","docs/architecture.md"],qe=["package.json","pnpm-lock.yaml","package-lock.json","yarn.lock","tsconfig.json","vite.config.ts","turbo.json"],We=["Dockerfile","docker-compose.yml","compose.yml",".env.example","fly.toml"],Fe=["AGENTS.md","CLAUDE.md","GEMINI.md",".opencode/AGENTS.md",".cursor/rules/neocortex.mdc"];function me(e,t,n=24e3){const o=p(e,t);try{if(!g(o)||C(o).isDirectory())return;const s=E(o,"utf8");return s.length<=n?s:s.slice(0,n)}catch{return}}function fe(e,t,n){if(!n)return`${e}.bak-${t}`;const o=Ce(n),s=C(o);if(!s.isDirectory()||s.isSymbolicLink()||s.uid!==process.getuid?.()||(s.mode&63)!==0||we(o)!==o)throw new Error("project-memory-backup-directory-unsafe");return p(o,`${Re(e)}.bak-${t}`)}function Y(e,t,n){O(I(e),{recursive:!0});const o=U(),s=`${e}.tmp-${o}`,r=g(e)?fe(e,o,n):void 0;try{return q(s,t,{encoding:"utf8",flag:"wx",mode:384}),r!==void 0&&j(e,r,V.COPYFILE_EXCL),te(s,e),r}catch(a){if(N(s,{force:!0}),r!==void 0&&g(r))try{j(r,e)}catch{}throw a}}function K(e){return Array.from(new Set(G(e).map(t=>t.reasonCode)))}function T(e,t){try{return g(p(e,t))}catch{return!1}}function Xe(e,t){try{return Q(p(e,t),{withFileTypes:!0}).filter(n=>!n.name.startsWith(".")&&!n.name.toLowerCase().includes("secret")).sort((n,o)=>n.name.localeCompare(o.name)).slice(0,8).map(n=>`${t}/${n.name}${n.isDirectory()?"/":""}`)}catch{return[]}}function pe(e,t,n=16){try{const o=Q(p(e,t),{withFileTypes:!0}).filter(r=>!r.name.startsWith(".")&&!r.name.startsWith("_")).sort((r,a)=>r.name.localeCompare(a.name)),s=[];for(const r of o){const a=`${t}/${r.name}`;if(r.isDirectory()?s.push(...pe(e,a,n-s.length)):/\.(md|markdown)$/i.test(r.name)&&s.push(a),s.length>=n)break}return s}catch{return[]}}function ze(e,t){const n=t.match(/^#\s+(.+?)\s*$/m)?.[1]?.trim();return n?n.replace(/[`*_#]/g,""):e.split("/").pop()?.replace(/\.(md|markdown)$/i,"").replace(/[-_]+/g," ")??e}function Ge(e,t){const n=/\b(stale|deprecated|outdated|incerto|uncertain)\b/i.test(`${e}
16
+ ${t.slice(0,2e3)}`),o=t.replace(/^---[\s\S]*?---\s*/,"").split(/\n{2,}/).map(r=>r.replace(/^#+\s+.+$/gm,"").replace(/[`*_>#]/g,"").replace(/\s+/g," ").trim()).find(r=>r.length>0&&!r.startsWith("|"));return`${n?"[stale/uncertain evidence] ":""}${(o??"Architecture reference for this project.").slice(0,180)}`}function He(e){const t=e.toLowerCase();return/deploy|infra|docker|production|runbook/.test(t)?"Use before changing deployment, runtime, containers, or production operations.":/security|auth|compliance/.test(t)?"Use before changing auth, compliance, or security-sensitive behavior.":/api|contract|state|schema/.test(t)?"Use before changing contracts, APIs, schemas, or persisted state.":"Use before implementation in this area; verify freshness when marked stale or uncertain."}function ye(e){const t=qe.filter(i=>T(e,i)),n=We.filter(i=>T(e,i)),o=Fe.filter(i=>T(e,i)),s=["packages","src","apps","tests","__tests__"].filter(i=>T(e,i)),a=[...le.flatMap(i=>pe(e,i)),...Be.filter(i=>T(e,i))].slice(0,24).flatMap(i=>{const d=me(e,i,48e3);return d?[{path:i,title:ze(i,d),purpose:Ge(i,d),suggestedUsage:He(i),source:le.some(m=>i.startsWith(`${m}/`))?"architecture-root":"fallback-file"}]:[]}),c=me(e,"package.json"),u=c?.match(/"name"\s*:\s*"([^"]+)"/)?.[1],l=[...c?.matchAll(/"(test|build|lint|typecheck|dev)"\s*:/g)??[]].map(i=>i[1]);return{universalLaws:["Bootstrap canonical memory before creating or repairing platform memory wrappers when NEOCORTEX.md is absent.","Use only permitted repository sources for bootstrap: package/build files, architecture docs, README/deploy docs, .env.example, runtime manifests, CI, source/test layout, and platform memory wrappers."],businessContext:[u?`Repository package name: ${u}.`:"Repository product name was not declared in package metadata.","Business/product conclusions are bootstrap-inferred and must be refined from canonical docs or human input."],activeContext:[`Bootstrap scanned ${t.length} package/build files, ${a.length} architecture/readme docs, ${n.length} runtime/config templates, and ${o.length} platform memory wrappers.`,l.length>0?`Detected quality scripts: ${l.sort().join(", ")}.`:"No standard quality scripts were detected in package metadata."],repositoryMap:[t.length>0?`Package/build files: ${t.join(", ")}.`:"No package/build files detected in permitted bootstrap set.",s.length>0?`Source/test roots: ${s.flatMap(i=>Xe(e,i)).slice(0,16).join(", ")}.`:"Source/test roots were not detected in standard locations.",o.length>0?`Platform memory/wrappers: ${o.join(", ")}.`:"No platform memory wrappers detected before bootstrap."],environmentPractices:[n.length>0?`Runtime/config templates: ${n.join(", ")}.`:"No Docker/compose/.env.example/runtime manifest detected in permitted bootstrap set.","Read `.env.example` only for configuration shape; never ingest real .env files, credentials, customer data, or license keys into memory.","Run repository quality scripts before commit and keep CI/deploy evidence referenced by path or URL rather than copied verbatim."],platformMemoryContract:["Semantic platform memory file must resolve to NEOCORTEX.md through a symlink; do not replace it with an unrelated wrapper or copied frontmatter.","Preserve wrappers/frontmatter via split/include/managed block while the semantic memory payload remains symlink-backed by NEOCORTEX.md."],maintenanceRules:["Generated memory must be deterministic, objective, direct, compact, and inside the target always-loaded context budget.","Cite long architecture docs by path with usage guidance; mark stale or uncertain docs when repository evidence indicates that status.","Do not copy protected prompt bodies, proprietary wrappers, server internals, secrets, customer data, license keys, or complete long documents."],architectureReferences:a,heuristics:["Before processing a story, consult the cited Architecture References whose path or suggested usage intersects the change.","When a referenced doc is marked stale/uncertain, validate against current package/runtime/source evidence before relying on it."]}}function J(e,t,n){const o=$(t),s=L(t),r=e.indexOf(o),a=e.indexOf(s);if(r>=0&&a>=r){const u=a+s.length;return`${e.slice(0,r)}${n}${e.slice(u)}`}const c=e.endsWith(`
17
+ `)?`
18
+ `:`
19
+
20
+ `;return`${e}${c}${n}
21
+ `}function Ye(e){return e.endsWith(".mdc")?`---
22
+ description: Neocortex project memory
23
+ alwaysApply: true
24
+ ---
25
+
26
+ `:e.endsWith(".toml")?`[agent]
27
+ name = "neocortex"
28
+ description = "Neocortex project memory wrapper"
29
+
30
+ `:e.endsWith(".agent.md")?`---
31
+ name: neocortex
32
+ description: Neocortex project memory wrapper
33
+ ---
34
+
35
+ `:""}function be(e,t){const n=I(p(e,t)),o=p(e,b),s=Se(n,o);return s.length>0?s:b}function v(e,t){return!!(e.targetId==="canonical"||e.requiresWrapperPreservation||e.linkRequirement.requiresWrapperPreservation||e.linkRequirement.wrapperAllowed||t==="AGENTS.md"||t.includes("/agents/")||t.includes("/skills/")||t.endsWith(".toml")||t.endsWith(".mdc")||t.endsWith(".agent.md")||t.endsWith(".instructions.md"))}function Ke(e){return e.linkRequirement.symlinkBacked&&e.linkRequirement.semanticFileRequired}function Z(e){return/EPERM|EACCES|operation not permitted|permission/i.test(e)?"EPERM: symlink permission denied":/ENOSYS|ENOTSUP|EOPNOTSUPP|not supported/i.test(e)?"symlink filesystem unsupported":"symlink capability unavailable"}function he(e,t,n,o,s=!1){if(o!==void 0)return o;const r=Ke(t);if(!r)return{os:M(),supported:!1,permission:"unknown",filesystem:"unknown",targetPolicy:t.linkRequirement.policy,targetPolicyAllowsSymlink:r,requiresWrapperPreservation:v(t,n),reason:`target policy ${t.linkRequirement.policy} does not allow semantic symlink orchestration`};if(s)return{os:M(),supported:!0,permission:"unknown",filesystem:"unknown",targetPolicy:t.linkRequirement.policy,targetPolicyAllowsSymlink:r,requiresWrapperPreservation:v(t,n),reason:"dry-run does not probe filesystem symlink support"};const a=p(e,".neocortex","tmp"),c=U(),u=p(a,`symlink-capability-source-${c}`),l=p(a,`symlink-capability-link-${c}`);try{O(a,{recursive:!0}),q(u,"probe",{encoding:"utf8",flag:"wx",mode:384}),B(u,l);const i=C(l).isSymbolicLink()&&Ee(l).isFile();return{os:M(),supported:i,permission:i?"ok":"unknown",filesystem:i?"supports-symlink":"no-symlink-support",targetPolicy:t.linkRequirement.policy,targetPolicyAllowsSymlink:r,requiresWrapperPreservation:v(t,n),reason:i?void 0:"filesystem probe did not create a resolvable symbolic link"}}catch(i){const d=i instanceof Error?i.message:String(i),m=/EPERM|EACCES|operation not permitted|permission/i.test(d);return{os:M(),supported:!1,permission:m?"denied":"unknown",filesystem:"no-symlink-support",targetPolicy:t.linkRequirement.policy,targetPolicyAllowsSymlink:r,requiresWrapperPreservation:v(t,n),reason:Z(d)}}finally{N(l,{force:!0}),N(u,{force:!0})}}function Je(e,t,n,o){const s=be(o,t),r=n??`${Ye(t)}# ${e.platformName} Neocortex Memory Wrapper
36
+
37
+ `,a=[$(e),"## Canonical Project Memory Source","",`This platform wrapper preserves host-specific frontmatter/instructions while resolving project memory to \`${s}\`.`,"",`- Canonical source: \`${b}\``,`- Symlink-backed/source-of-truth file: \`${s}\``,`- Feedback loop: read \`${s}\` for shared context and run \`${e.bidirectionalReference.feedbackTrigger}\` when durable platform findings should update \`${b}\`.`,"- Human-authored content outside this managed block is preserved byte-for-byte.",L(e)].join(`
38
+ `);return J(r,e,a)}function ge(e,t,n,o,s,r=!1){const a=p(e,t),c=Z(s),l=`${["[NEOCORTEX COMPATIBILITY REMEDIATION]","Materialized project memory fallback because semantic symlink orchestration failed.","This is not product mode; repair host permissions/filesystem support and restore the symlink to NEOCORTEX.md.","WARNING: materialized fallback is remediation debt, not a silent mode; run *update-memory for durable context and repair this file back to canonical NEOCORTEX.md resolution.",`Fallback reason: ${c}`,""].join(`
39
+ `)}${n}`,d=(g(a)?E(a,"utf8"):void 0)!==l;return d&&!r&&(g(a)&&C(a).isSymbolicLink()&&re(a),Y(a,l)),{filePath:a,relativeFile:t,changed:d,content:l,mode:"compatibility-materialized-fallback",canonicalSource:b,symlinkCapability:o,fallbackReason:c,remediationDebt:`Repair ${t} so it is a symlink to ${b}; fallback is compatibility remediation only.`}}function Ze(e,t,n,o,s,r=!1){const a=p(e,t),c=be(e,t);r||O(I(a),{recursive:!0});const u=he(e,n,t,s,r);if(!u.supported)return ge(e,t,o,u,u.reason??"symlink capability detection reported unsupported host",r);if(r){let i=!0;try{i=!(g(a)&&C(a).isSymbolicLink()&&ee(a)===c)}catch{i=!0}return{filePath:a,relativeFile:t,changed:i,content:o,mode:"semantic-symlink",canonicalSource:b,symlinkCapability:u}}let l=!1;try{return g(a)?C(a).isSymbolicLink()&&ee(a)===c?l=!1:(re(a),B(c,a),l=!0):(B(c,a),l=!0),{filePath:a,relativeFile:t,changed:l,content:E(a,"utf8"),mode:"semantic-symlink",canonicalSource:b,symlinkCapability:u}}catch(i){const d=i instanceof Error?i.message:String(i),m=Z(d);return ge(e,t,o,{...u,supported:!1,permission:/EPERM|EACCES|operation not permitted|permission/i.test(d)?"denied":u.permission,filesystem:"no-symlink-support",reason:m},m,r)}}function Ve(e,t,n,o,s){const r=u=>u.split(`
40
+ `).length<=e.budget.maxLines&&Buffer.byteLength(u,"utf8")<=e.budget.maxBytes;if(r(t))return t;const a=de(e,n.slice(0,8),o.slice(0,12),s),c=J(t.includes($(e))?t:ue(e),e,a);return r(c)?c:c.slice(0,e.budget.maxBytes)}function ke(e,t,n=[],o=[],s){const r=t??ue(e),a=de(e,n,o,s),c=J(r,e,a);return Ve(e,c,n,o,s)}function Qe(e){const t=W(e.targetId),n=e.fs??Oe(e.backupDirectory),o=n.join(e.projectRoot,t.primaryFile);if(e.fs===void 0&&t.targetId==="canonical"&&F(e.projectRoot,[b]),t.targetId!=="canonical"&&e.fs===void 0){const u=et(e).files.find(l=>l.relativeFile===t.primaryFile);if(u===void 0)throw new Error(`Primary project memory file was not written for ${t.targetId}.`);return{filePath:u.filePath,changed:u.changed,content:u.content}}const s=n.readFile(o),r=e.bootstrapArchitecture??(e.fs===void 0&&t.targetId==="canonical"&&s===void 0?ye(e.projectRoot):void 0),a=t.targetId==="canonical"&&s!==void 0?ne(e)?oe(t,s,e):s:ke(t,s,e.memoryNotes,e.architectureReferences,r);if(s===a)return{filePath:o,changed:!1,content:a};const c=e.dryRun?void 0:n.writeFileAtomic(o,a);return{filePath:o,changed:!0,content:a,safeDiff:ie(s,a),backupPath:c?.backupPath,reasonCodes:K(e.memoryNotes??[])}}function ct(e){return Qe({...e,targetId:"canonical"})}function et(e){if(e.fs!==void 0)throw new Error("writeProjectMemoryTargetFiles requires the node filesystem to manage symlinks safely.");const t=W(e.targetId),n=W("canonical");t.secondaryFiles.some(d=>d===null||d.scope!=="project"&&d.scope!=="user"||!Te(d.path))&&$e("invalid");const o=Array.from(new Set([t.primaryFile,...t.targetId==="canonical"?[]:t.secondaryFiles.filter(d=>d.scope==="project").map(d=>d.path)]));F(e.projectRoot,[b,...o,".neocortex/tmp"]);const s=p(e.projectRoot,b),r=g(s)?E(s,"utf8"):void 0,a=e.bootstrapArchitecture??(r===void 0?ye(e.projectRoot):void 0),c=r!==void 0?ne(e)?oe(n,r,e):r:ke(n,r,e.memoryNotes,e.architectureReferences,a),u=!g(s)||E(s,"utf8")!==c;let l;if(u&&!e.dryRun&&(l=Y(s,c,e.backupDirectory)),t.targetId==="canonical")return{targetId:t.targetId,canonicalFilePath:s,changed:u,files:[{filePath:s,relativeFile:b,changed:u,content:c,mode:"canonical",canonicalSource:b,safeDiff:ie(r,c),backupPath:l,reasonCodes:K(e.memoryNotes??[])}]};const i=o.map(d=>{if(F(e.projectRoot,[d,".neocortex/tmp"]),!v(t,d))return Ze(e.projectRoot,d,t,c,e.symlinkCapability,e.dryRun);const m=p(e.projectRoot,d),h=g(m)?E(m,"utf8"):void 0,k=Je(t,d,h,e.projectRoot),R=h!==k;return R&&!e.dryRun&&Y(m,k,e.backupDirectory),{filePath:m,relativeFile:d,changed:R,content:k,mode:"managed-wrapper",canonicalSource:b,symlinkCapability:he(e.projectRoot,t,d,e.symlinkCapability,e.dryRun),reasonCodes:K(e.memoryNotes??[])}});return{targetId:t.targetId,canonicalFilePath:s,files:i,changed:u||i.some(d=>d.changed)}}export{it as PROJECT_MEMORY_LAYER_CATEGORIES,ye as bootstrapArchitectureMemory,ke as buildProjectMemoryContent,Ue as classifyProjectMemoryUpdate,Oe as createNodeProjectMemoryFs,ie as createSafeProjectMemoryDiff,v as shouldUseManagedWrapper,ct as writeCanonicalProjectMemory,Qe as writeProjectMemory,et as writeProjectMemoryTargetFiles};