@ornexus/neocortex 4.60.66 → 4.60.68

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 (236) hide show
  1. package/dist/release-source.json +1 -1
  2. package/dist/sbom.cdx.json +1598 -1640
  3. package/install.ps1 +1 -1
  4. package/install.sh +2 -1
  5. package/package.json +10 -7
  6. package/packages/client/dist/adapters/adapter-registry.js +108 -1
  7. package/packages/client/dist/adapters/antigravity-adapter.js +77 -2
  8. package/packages/client/dist/adapters/claude-code-adapter.js +79 -3
  9. package/packages/client/dist/adapters/codex-adapter.js +80 -2
  10. package/packages/client/dist/adapters/cursor-adapter.js +115 -4
  11. package/packages/client/dist/adapters/gemini-adapter.js +71 -2
  12. package/packages/client/dist/adapters/grok-adapter.js +42 -2
  13. package/packages/client/dist/adapters/index.js +22 -1
  14. package/packages/client/dist/adapters/platform-detector.js +212 -1
  15. package/packages/client/dist/adapters/target-adapter.js +12 -0
  16. package/packages/client/dist/adapters/vscode-adapter.js +72 -2
  17. package/packages/client/dist/agent/refresh-stubs.js +417 -2
  18. package/packages/client/dist/agent/update-agent-yaml.js +102 -1
  19. package/packages/client/dist/agent/update-description.js +251 -1
  20. package/packages/client/dist/cache/crypto-utils.js +78 -1
  21. package/packages/client/dist/cache/encrypted-cache.js +108 -1
  22. package/packages/client/dist/cache/in-memory-asset-cache.js +73 -1
  23. package/packages/client/dist/cache/index.js +13 -1
  24. package/packages/client/dist/cache/protected-pi-boundary.js +44 -1
  25. package/packages/client/dist/checkpoint/checkpoint-client-reader.js +127 -2
  26. package/packages/client/dist/checkpoint/index.js +11 -1
  27. package/packages/client/dist/checkpoint/shared-checkpoint-types.js +32 -1
  28. package/packages/client/dist/cli.js +303 -3
  29. package/packages/client/dist/commands/activate.js +394 -8
  30. package/packages/client/dist/commands/architecture-answers.js +274 -7
  31. package/packages/client/dist/commands/architecture-memory-terminal.js +253 -4
  32. package/packages/client/dist/commands/architecture-non-ui-continuation.js +100 -1
  33. package/packages/client/dist/commands/architecture-root-binding.js +51 -1
  34. package/packages/client/dist/commands/cache-status.js +112 -2
  35. package/packages/client/dist/commands/config.js +88 -1
  36. package/packages/client/dist/commands/doctor.d.ts +135 -0
  37. package/packages/client/dist/commands/doctor.js +635 -0
  38. package/packages/client/dist/commands/invoke.d.ts +18 -0
  39. package/packages/client/dist/commands/invoke.js +7140 -142
  40. package/packages/client/dist/commands/legacy-epic-terminal.js +71 -1
  41. package/packages/client/dist/commands/legacy-story-branch.js +96 -1
  42. package/packages/client/dist/commands/legacy-story-pr.js +276 -1
  43. package/packages/client/dist/commands/mutation-qa-checkpoint.js +285 -5
  44. package/packages/client/dist/commands/mutation-qa-gate.js +54 -1
  45. package/packages/client/dist/commands/mutation-qa-git.js +41 -1
  46. package/packages/client/dist/commands/mutation-qa-policy.js +102 -1
  47. package/packages/client/dist/commands/mutation-qa-run.js +219 -3
  48. package/packages/client/dist/commands/mutation-qa-verdict.js +149 -1
  49. package/packages/client/dist/commands/mutation-qa.js +220 -1
  50. package/packages/client/dist/commands/native-agent.js +136 -2
  51. package/packages/client/dist/commands/planning-diagnosis.js +43 -1
  52. package/packages/client/dist/commands/planning-generation.js +53 -1
  53. package/packages/client/dist/commands/planning-memory.js +40 -1
  54. package/packages/client/dist/commands/planning-research.js +42 -1
  55. package/packages/client/dist/commands/private-session.js +75 -1
  56. package/packages/client/dist/commands/property-qa.js +139 -1
  57. package/packages/client/dist/commands/qa-control.js +16 -1
  58. package/packages/client/dist/commands/qa-scan-browser-launch.js +55 -1
  59. package/packages/client/dist/commands/qa-scan-browser-route.js +28 -1
  60. package/packages/client/dist/commands/qa-scan-capture.js +663 -5
  61. package/packages/client/dist/commands/qa-scan-discovery.js +376 -3
  62. package/packages/client/dist/commands/qa-scan-review.js +826 -5
  63. package/packages/client/dist/commands/qa-synthetic-backend-capture.js +266 -1
  64. package/packages/client/dist/commands/qa-synthetic-backend-review.js +297 -1
  65. package/packages/client/dist/commands/qa-synthetic-backend.js +340 -2
  66. package/packages/client/dist/commands/qa-synthetic-broker-port.js +85 -1
  67. package/packages/client/dist/commands/qa-synthetic-browser.js +39 -1
  68. package/packages/client/dist/commands/refactor-characterization.js +671 -21
  69. package/packages/client/dist/commands/refactor-commit.js +399 -13
  70. package/packages/client/dist/commands/refactor-control.js +142 -3
  71. package/packages/client/dist/commands/refactor-cpp-assertions.js +93 -3
  72. package/packages/client/dist/commands/refactor-review.js +283 -5
  73. package/packages/client/dist/commands/refactor-root.js +588 -7
  74. package/packages/client/dist/commands/refresh-memory.js +156 -3
  75. package/packages/client/dist/commands/shared-architecture-non-ui.js +24 -1
  76. package/packages/client/dist/commands/story-checkpoint.js +171 -1
  77. package/packages/client/dist/config/profile-directory.js +13 -1
  78. package/packages/client/dist/config/resolver-selection.js +278 -1
  79. package/packages/client/dist/config/secure-config.js +281 -12
  80. package/packages/client/dist/config.js +223 -2
  81. package/packages/client/dist/constants.js +25 -1
  82. package/packages/client/dist/context/context-collector.js +222 -2
  83. package/packages/client/dist/context/context-sanitizer.js +145 -1
  84. package/packages/client/dist/continuity/autonomous-governance.js +285 -1
  85. package/packages/client/dist/continuity/continuity-client-state-store.js +1587 -1
  86. package/packages/client/dist/continuity/invoke-hooks.js +129 -1
  87. package/packages/client/dist/continuity/migrations/001-initial-schema.js +12 -2
  88. package/packages/client/dist/continuity/migrations/002-processing-output-summary-indexes.js +12 -2
  89. package/packages/client/dist/continuity/migrations/003-headless-execution-attempts.js +12 -2
  90. package/packages/client/dist/continuity/migrations/004-outbox-delivery-state.js +12 -2
  91. package/packages/client/dist/continuity/migrations/005-bounded-headless-queue.js +12 -2
  92. package/packages/client/dist/continuity/migrations/006-native-agent-dispatches.js +10 -2
  93. package/packages/client/dist/continuity/native-agent-store.js +69 -1
  94. package/packages/client/dist/continuity/native-agent-types.js +90 -1
  95. package/packages/client/dist/continuity/outbox.js +117 -1
  96. package/packages/client/dist/continuity/runner-bootstrap-policy.js +172 -1
  97. package/packages/client/dist/continuity/sqlite-store.js +2536 -81
  98. package/packages/client/dist/effects/invoke-effect-gateways.js +62 -1
  99. package/packages/client/dist/errors/error-messages.js +228 -2
  100. package/packages/client/dist/errors/frontmatter-diagnostics.js +125 -1
  101. package/packages/client/dist/evidence/collector.js +347 -1
  102. package/packages/client/dist/evidence/index.js +1 -1
  103. package/packages/client/dist/evidence/types.js +2 -0
  104. package/packages/client/dist/fs/atomic-state-replace.js +152 -1
  105. package/packages/client/dist/generated/command-registry.generated.js +4076 -1
  106. package/packages/client/dist/generated/public-projections.generated.js +149 -1
  107. package/packages/client/dist/git/direct-yolo-c01-retry.js +138 -4
  108. package/packages/client/dist/git/direct-yolo-checkpoint.js +1234 -4
  109. package/packages/client/dist/git/git-lifecycle-step-ids.js +14 -1
  110. package/packages/client/dist/git/git-lifecycle-store.js +1938 -9
  111. package/packages/client/dist/git/legacy-story-branch-checkpoint.js +259 -9
  112. package/packages/client/dist/git/legacy-story-branch-setup.js +348 -4
  113. package/packages/client/dist/git/legacy-story-pr.js +3538 -70
  114. package/packages/client/dist/git/secure-filesystem-boundary.js +182 -5
  115. package/packages/client/dist/graph-retrieval/pre-command-hook.js +284 -1
  116. package/packages/client/dist/graph-retrieval/shared-graph-retrieval-contract.js +237 -1
  117. package/packages/client/dist/i18n/first-run.js +60 -2
  118. package/packages/client/dist/index.js +47 -1
  119. package/packages/client/dist/invocation/invoke-input.js +24 -1
  120. package/packages/client/dist/license/index.js +5 -1
  121. package/packages/client/dist/license/license-client.js +304 -1
  122. package/packages/client/dist/machine/fingerprint.js +163 -2
  123. package/packages/client/dist/machine/index.js +5 -1
  124. package/packages/client/dist/memory/project-memory-path-scope.js +62 -1
  125. package/packages/client/dist/memory/project-memory-writer.js +1007 -40
  126. package/packages/client/dist/memory/shared-project-memory-types.js +253 -2
  127. package/packages/client/dist/migrations/project-identity-capability.js +50 -1
  128. package/packages/client/dist/migrations/project-identity.js +493 -7
  129. package/packages/client/dist/migrations/state-json.js +344 -4
  130. package/packages/client/dist/policy/architecture-policy.js +184 -2
  131. package/packages/client/dist/policy/index.js +7 -1
  132. package/packages/client/dist/policy/shared-policy-types.js +38 -0
  133. package/packages/client/dist/policy/subagent-approval-policy.js +55 -1
  134. package/packages/client/dist/private-execution/clean-baseline.js +279 -2
  135. package/packages/client/dist/private-execution/codex-executor.js +574 -4
  136. package/packages/client/dist/private-execution/codex-host-runtime.js +137 -1
  137. package/packages/client/dist/private-execution/codex-lifecycle-guard.js +97 -1
  138. package/packages/client/dist/private-execution/codex-scoped-permissions.js +128 -1
  139. package/packages/client/dist/private-execution/delivery.js +410 -13
  140. package/packages/client/dist/private-execution/diagnosis-root-review.js +294 -2
  141. package/packages/client/dist/private-execution/frozen-handoff.js +19 -6
  142. package/packages/client/dist/private-execution/implementation-validation.js +106 -2
  143. package/packages/client/dist/private-execution/isolated-admission.js +1432 -23
  144. package/packages/client/dist/private-execution/isolated-session.js +725 -5
  145. package/packages/client/dist/private-execution/isolated-workspace.js +122 -1
  146. package/packages/client/dist/private-execution/pact-consumer-runtime.js +409 -3
  147. package/packages/client/dist/private-execution/pact-test-source.js +258 -1
  148. package/packages/client/dist/private-execution/pact-toolchain.js +290 -3
  149. package/packages/client/dist/private-execution/pact-write-set.js +172 -1
  150. package/packages/client/dist/private-execution/planning-diagnosis-checkpoint.js +406 -5
  151. package/packages/client/dist/private-execution/planning-generation-checkpoint.js +462 -5
  152. package/packages/client/dist/private-execution/planning-memory-checkpoint.js +139 -3
  153. package/packages/client/dist/private-execution/planning-research-checkpoint.js +325 -4
  154. package/packages/client/dist/private-execution/private-report-store.js +42 -1
  155. package/packages/client/dist/private-execution/property-test-candidate.js +103 -1
  156. package/packages/client/dist/private-execution/property-test-choices.js +147 -3
  157. package/packages/client/dist/private-execution/property-test-plan.js +271 -7
  158. package/packages/client/dist/private-execution/property-test-review.js +258 -4
  159. package/packages/client/dist/private-execution/property-test-runtime.js +372 -9
  160. package/packages/client/dist/private-execution/property-test-source.js +139 -1
  161. package/packages/client/dist/private-execution/root-checkpoint.js +80 -1
  162. package/packages/client/dist/private-execution/root-memory-git-evidence.js +135 -3
  163. package/packages/client/dist/private-execution/root-memory-recovery.js +93 -1
  164. package/packages/client/dist/private-execution/root-memory-request.js +29 -1
  165. package/packages/client/dist/private-execution/root-memory-scope.js +178 -1
  166. package/packages/client/dist/private-execution/root-memory-writer.js +102 -1
  167. package/packages/client/dist/private-execution/session-lease.js +63 -1
  168. package/packages/client/dist/private-execution/shared-private-admission-types.js +50 -1
  169. package/packages/client/dist/private-execution/shared-project-validation.js +66 -1
  170. package/packages/client/dist/private-execution/shared-root-memory-types.js +61 -1
  171. package/packages/client/dist/private-execution/ux-source-validation.js +372 -1
  172. package/packages/client/dist/private-execution/ux-write-set.js +191 -2
  173. package/packages/client/dist/resilience/circuit-breaker.js +170 -1
  174. package/packages/client/dist/resilience/degradation-manager.js +164 -1
  175. package/packages/client/dist/resilience/freshness-indicator.js +100 -1
  176. package/packages/client/dist/resilience/index.js +8 -1
  177. package/packages/client/dist/resilience/recovery-detector.js +74 -1
  178. package/packages/client/dist/resolvers/asset-resolver.js +13 -0
  179. package/packages/client/dist/resolvers/local-resolver.js +255 -8
  180. package/packages/client/dist/resolvers/remote-resolver.js +233 -1
  181. package/packages/client/dist/runner/binary.js +404 -1
  182. package/packages/client/dist/runner/cli.js +1571 -17
  183. package/packages/client/dist/runner/headless/adapters.js +49 -1
  184. package/packages/client/dist/runner/headless/claude-code-adapter.js +79 -1
  185. package/packages/client/dist/runner/headless/continuous-story-workspace.js +1628 -11
  186. package/packages/client/dist/runner/headless/epic-git-reconciliation-inventory.js +69 -1
  187. package/packages/client/dist/runner/headless/epic-merge-candidate-qa.js +110 -2
  188. package/packages/client/dist/runner/headless/epic-terminal-git-cli-adapter.js +1236 -8
  189. package/packages/client/dist/runner/headless/epic-terminal-git-runner.js +318 -2
  190. package/packages/client/dist/runner/headless/fake-adapter.js +62 -1
  191. package/packages/client/dist/runner/headless/managed-profile.js +132 -1
  192. package/packages/client/dist/runner/headless/opencode-adapter.js +145 -2
  193. package/packages/client/dist/runner/headless/prepared-story-bundle.js +43 -1
  194. package/packages/client/dist/runner/headless/resolver.js +231 -1
  195. package/packages/client/dist/runner/headless/run-yolo.js +885 -1
  196. package/packages/client/dist/runner/headless/run-yoloop.d.ts +4 -1
  197. package/packages/client/dist/runner/headless/run-yoloop.js +2574 -2
  198. package/packages/client/dist/runner/headless/types.js +574 -2
  199. package/packages/client/dist/runner/local-console.js +396 -13
  200. package/packages/client/dist/runner/portal-sync-worker.js +1487 -1
  201. package/packages/client/dist/runner/project-identity.js +106 -1
  202. package/packages/client/dist/runner/scheduler.js +995 -7
  203. package/packages/client/dist/runner-cli.js +18 -2
  204. package/packages/client/dist/state/invocation-shape.js +13 -1
  205. package/packages/client/dist/state/project-state-snapshot.js +1677 -1
  206. package/packages/client/dist/state/state-json-repair.js +785 -3
  207. package/packages/client/dist/state/structural-dag-projection.js +118 -1
  208. package/packages/client/dist/telemetry/index.js +5 -1
  209. package/packages/client/dist/telemetry/offline-queue.js +131 -1
  210. package/packages/client/dist/tier/index.js +5 -1
  211. package/packages/client/dist/tier/tier-aware-client.js +405 -1
  212. package/packages/client/dist/types/index.js +38 -1
  213. package/packages/client/dist/version.js +27 -1
  214. package/packages/client/dist/yolo/change-evidence-collector.js +126 -1
  215. package/packages/client/dist/yolo/invoke-envelope-capability.js +62 -1
  216. package/packages/client/dist/yolo/spec-envelope-collector.js +185 -5
  217. package/packages/client/dist/yoloop/discovery-hook.js +300 -2
  218. package/packages/client/dist/yoloop/index.js +8 -1
  219. package/packages/client/dist/yoloop/invoke-hooks.js +669 -12
  220. package/packages/client/dist/yoloop/shared-discover-epics.js +1007 -1
  221. package/packages/client/dist/yoloop/shared-yoloop-types.js +33 -1
  222. package/packages/client/dist/yoloop/target-normalization.js +180 -1
  223. package/packages/client/dist/yoloop/yoloop-client-state-store.js +1723 -1
  224. package/packages/shared/dist/compatibility/index.js +476 -0
  225. package/packages/shared/dist/yoloop/release-blocked.js +653 -0
  226. package/targets-stubs/antigravity/gemini.md +1 -1
  227. package/targets-stubs/antigravity/skill/SKILL.md +3 -2
  228. package/targets-stubs/claude-code/neocortex-root.agent.yaml +1 -1
  229. package/targets-stubs/claude-code/neocortex-root.md +5 -5
  230. package/targets-stubs/claude-code/neocortex.agent.yaml +1 -1
  231. package/targets-stubs/claude-code/neocortex.md +8 -9
  232. package/targets-stubs/codex/AGENTS.md +3 -3
  233. package/targets-stubs/cursor/agent.md +4 -3
  234. package/targets-stubs/gemini-cli/agent.md +5 -4
  235. package/targets-stubs/opencode/neocortex-root.md +3 -3
  236. package/targets-stubs/vscode/neocortex.agent.md +5 -4
@@ -1,40 +1,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};
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
+ }