@thebackstoryis/engineering-with-ai 0.2.9

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 (334) hide show
  1. package/Docs/README.md +50 -0
  2. package/Docs/adoption/consultancy-and-multi-project-rollout.md +135 -0
  3. package/Docs/adoption/non-technical-team-guide.md +126 -0
  4. package/Docs/archaeology-technology-and-hosting-discovery.md +212 -0
  5. package/Docs/blast-radius-and-impact-routing-guide.md +325 -0
  6. package/Docs/blueprints/internal-blueprint-catalogue.md +146 -0
  7. package/Docs/blueprints/maintaining-organisation-blueprints.md +154 -0
  8. package/Docs/blueprints/validation-and-troubleshooting.md +168 -0
  9. package/Docs/cli-reference.md +113 -0
  10. package/Docs/completed-phase-evidence-amendments.md +74 -0
  11. package/Docs/consultancy-network-rollout-control-plane-guide.md +202 -0
  12. package/Docs/context-aware-delivery-companion-guide.md +198 -0
  13. package/Docs/context-aware-delivery-companion-user-guide.md +184 -0
  14. package/Docs/context-management-and-token-efficiency.md +113 -0
  15. package/Docs/design-systems/design-system-implementation-guide.md +85 -0
  16. package/Docs/design-systems/design-system-pack-authoring-guide.md +95 -0
  17. package/Docs/design-systems/design-system-review-guide.md +51 -0
  18. package/Docs/design-systems/design-system-user-guide.md +96 -0
  19. package/Docs/design-systems/product-owner-guide.md +49 -0
  20. package/Docs/designing-organisation-blueprint-packs.md +384 -0
  21. package/Docs/developer-delivery-guide.md +224 -0
  22. package/Docs/error-reporting-guide.md +110 -0
  23. package/Docs/error-reporting-provider-guide.md +49 -0
  24. package/Docs/examples/error-report-adapter.md +70 -0
  25. package/Docs/examples/meeting-review.md +76 -0
  26. package/Docs/examples/minimal-design-system.md +67 -0
  27. package/Docs/examples/prototype-review-inputs.md +175 -0
  28. package/Docs/examples/reproducible-archaeology-depth-example.md +144 -0
  29. package/Docs/examples/test-scenario-input.md +68 -0
  30. package/Docs/examples/worked-examples.md +147 -0
  31. package/Docs/existing-project-onboarding-guide.md +214 -0
  32. package/Docs/explanation/core-concepts.md +26 -0
  33. package/Docs/explanation/delivery-workflow.md +48 -0
  34. package/Docs/governance/governance-team-guide.md +139 -0
  35. package/Docs/governed-starter-project-materialisation-guide.md +284 -0
  36. package/Docs/guide-catalogue.md +117 -0
  37. package/Docs/guided-discovery-facilitator-guide.md +172 -0
  38. package/Docs/guided-intent-workspace-guide.md +109 -0
  39. package/Docs/guided-phase-evidence-drafting-guide.md +119 -0
  40. package/Docs/human-approval-and-assurance-guide.md +146 -0
  41. package/Docs/knowledge-proposals-implementer-guide.md +106 -0
  42. package/Docs/knowledge-proposals-user-guide.md +247 -0
  43. package/Docs/maintainers/context-benchmarks.md +29 -0
  44. package/Docs/maintainers/contributing.md +58 -0
  45. package/Docs/maintainers/evidence-depth-acceptance.md +72 -0
  46. package/Docs/maintainers/verification-walkthroughs.md +104 -0
  47. package/Docs/meeting-evidence-implementer-guide.md +132 -0
  48. package/Docs/meeting-evidence-user-guide.md +200 -0
  49. package/Docs/operations/dashboard-and-delivery-state.md +153 -0
  50. package/Docs/operations/dashboard-configuration.md +87 -0
  51. package/Docs/operations/installation-updating-and-entitlements.md +135 -0
  52. package/Docs/operations/premium-personas-setup.md +76 -0
  53. package/Docs/operations/troubleshooting-and-recovery.md +205 -0
  54. package/Docs/organisation-rollout-guide.md +142 -0
  55. package/Docs/persona-entitlement-provider-guide.md +199 -0
  56. package/Docs/persona-guided-prototype-iteration.md +129 -0
  57. package/Docs/personas/organisation-specific-personas.md +103 -0
  58. package/Docs/personas/persona-authoring-cookbook.md +176 -0
  59. package/Docs/personas/persona-engagement-ui.md +133 -0
  60. package/Docs/personas/persona-governance.md +118 -0
  61. package/Docs/platform-export-analysis-guide.md +336 -0
  62. package/Docs/policies/governance-owner-guide.md +36 -0
  63. package/Docs/policies/implementation-guide.md +42 -0
  64. package/Docs/policies/organisation-policy-design-gates.md +58 -0
  65. package/Docs/policies/policy-pack-authoring-guide.md +108 -0
  66. package/Docs/policies/product-owner-guide.md +43 -0
  67. package/Docs/policies/technical-owner-guide.md +37 -0
  68. package/Docs/product-owner-guide.md +327 -0
  69. package/Docs/project-portfolio-orchestration-guide.md +199 -0
  70. package/Docs/quality/manual-qa-and-acceptance.md +162 -0
  71. package/Docs/quality/persona-driven-test-scenarios.md +172 -0
  72. package/Docs/quality/reproducible-archaeology-depth-review-checklist.md +89 -0
  73. package/Docs/reference/capabilities-and-project-layout.md +678 -0
  74. package/Docs/reference/cli-and-configuration.md +398 -0
  75. package/Docs/reference/contributions-api.md +23 -0
  76. package/Docs/reference/security-adapter-authoring.md +81 -0
  77. package/Docs/reference/starter-adapter-authoring.md +74 -0
  78. package/Docs/repository-source-map-guide.md +381 -0
  79. package/Docs/reproducible-archaeology-and-discovery-depth.md +292 -0
  80. package/Docs/screen-prototype-creation-guide.md +324 -0
  81. package/Docs/security-validation-guide.md +353 -0
  82. package/Docs/solution-readiness-review-guide.md +123 -0
  83. package/Docs/standards/project-standards-authoring.md +157 -0
  84. package/Docs/team-hub-guide.md +162 -0
  85. package/Docs/team-hub-resource-registry-guide.md +167 -0
  86. package/Docs/tutorials/first-delivery.md +83 -0
  87. package/Docs/tutorials/first-session.md +62 -0
  88. package/Docs/using-lifecycle-hooks.md +381 -0
  89. package/Docs/working-with-personas.md +274 -0
  90. package/LICENSE +165 -0
  91. package/README.md +96 -0
  92. package/agents-src/claude/ewai-security-reviewer.md +15 -0
  93. package/bin/ewai +5 -0
  94. package/config/archaeology-record-families.yaml +59 -0
  95. package/config/delivery-artifacts.yaml +121 -0
  96. package/config/delivery-stages.yaml +77 -0
  97. package/config/design-system.schema.json +46 -0
  98. package/config/error-reporting.schema.json +79 -0
  99. package/config/evidence-depth.schema.json +53 -0
  100. package/config/intent.schema.json +90 -0
  101. package/config/knowledge-proposals-proposal.schema.json +34 -0
  102. package/config/lifecycle-event.schema.json +68 -0
  103. package/config/lifecycle-handler.schema.json +45 -0
  104. package/config/lifecycle-hook-ack.schema.json +19 -0
  105. package/config/meeting-evidence-candidate.schema.json +102 -0
  106. package/config/organisation-policy.schema.json +137 -0
  107. package/config/pack.schema.json +250 -0
  108. package/config/persona-pack.schema.json +21 -0
  109. package/config/persona.schema.json +17 -0
  110. package/config/policy-evaluation.schema.json +77 -0
  111. package/config/policy-facts.schema.json +140 -0
  112. package/config/portfolio.schema.json +68 -0
  113. package/config/project.schema.json +313 -0
  114. package/config/prototype-iteration.schema.json +128 -0
  115. package/config/rollout.schema.json +87 -0
  116. package/config/security-adapter.schema.json +31 -0
  117. package/config/security-scan-request.schema.json +64 -0
  118. package/config/security-scan-response.schema.json +52 -0
  119. package/config/security-validation-policy.schema.json +74 -0
  120. package/config/starter-source-acknowledgement.schema.json +13 -0
  121. package/config/starter-source-adapter.schema.json +38 -0
  122. package/config/starter-source-request.schema.json +61 -0
  123. package/package.json +77 -0
  124. package/packs/core/pack.yaml +7 -0
  125. package/packs/design-systems/default/experience-promise.md +9 -0
  126. package/packs/design-systems/default/intentional-review.md +10 -0
  127. package/packs/design-systems/default/interaction-and-entry.md +9 -0
  128. package/packs/design-systems/default/meaningful-content-and-states.md +9 -0
  129. package/packs/design-systems/default/pack.yaml +44 -0
  130. package/packs/design-systems/default/principles.md +10 -0
  131. package/packs/personas/core/pack.yaml +7 -0
  132. package/packs/personas/core/personas/archaeologist.md +37 -0
  133. package/packs/personas/core/personas/end-user.md +17 -0
  134. package/packs/personas/core/personas/maintainer.md +17 -0
  135. package/packs/personas/core/personas/operator.md +17 -0
  136. package/packs/personas/core/personas/specs-knowledge-curator.md +35 -0
  137. package/packs/technologies/laravel/pack.yaml +30 -0
  138. package/packs/technologies/laravel-nuxt/pack.yaml +30 -0
  139. package/packs/technologies/nuxt/pack.yaml +30 -0
  140. package/packs/technologies/power-platform/pack.yaml +31 -0
  141. package/packs/technologies/salesforce/pack.yaml +25 -0
  142. package/public/app.js +4896 -0
  143. package/public/apple-touch-icon.png +0 -0
  144. package/public/assets/backstory-icon.png +0 -0
  145. package/public/dashboard-navigation.js +98 -0
  146. package/public/favicon-16.png +0 -0
  147. package/public/favicon-32.png +0 -0
  148. package/public/favicon.ico +0 -0
  149. package/public/index.html +789 -0
  150. package/public/styles.css +2693 -0
  151. package/public/team-hub/app.js +202 -0
  152. package/public/team-hub/index.html +79 -0
  153. package/public/team-hub/styles.css +90 -0
  154. package/scripts/publication-check.mjs +140 -0
  155. package/scripts/setup.mjs +21 -0
  156. package/skills-src/ewai-archaeology/SKILL.md +334 -0
  157. package/skills-src/ewai-archaeology/agents/openai.yaml +4 -0
  158. package/skills-src/ewai-archaeology/references/archaeology-contract.md +201 -0
  159. package/skills-src/ewai-archaeology/references/lifecycle-reconstruction.md +177 -0
  160. package/skills-src/ewai-archaeology/references/maximum-detail-reconstruction.md +97 -0
  161. package/skills-src/ewai-archaeology/references/model-routing.md +26 -0
  162. package/skills-src/ewai-architecture/SKILL.md +108 -0
  163. package/skills-src/ewai-architecture/agents/openai.yaml +4 -0
  164. package/skills-src/ewai-architecture/references/architecture-contract.md +176 -0
  165. package/skills-src/ewai-context/SKILL.md +68 -0
  166. package/skills-src/ewai-context/agents/openai.yaml +4 -0
  167. package/skills-src/ewai-context-import/SKILL.md +118 -0
  168. package/skills-src/ewai-context-import/agents/openai.yaml +4 -0
  169. package/skills-src/ewai-context-import/references/context-import-contract.md +106 -0
  170. package/skills-src/ewai-dashboard-configuration/SKILL.md +20 -0
  171. package/skills-src/ewai-deliver/SKILL.md +122 -0
  172. package/skills-src/ewai-deliver/references/delivery-evidence.md +92 -0
  173. package/skills-src/ewai-deliver/references/phase-routing.md +31 -0
  174. package/skills-src/ewai-design-system-apply/SKILL.md +27 -0
  175. package/skills-src/ewai-design-system-apply/agents/openai.yaml +4 -0
  176. package/skills-src/ewai-design-system-apply/references/application-contract.md +36 -0
  177. package/skills-src/ewai-design-system-author/SKILL.md +28 -0
  178. package/skills-src/ewai-design-system-author/agents/openai.yaml +4 -0
  179. package/skills-src/ewai-design-system-author/references/authoring-contract.md +38 -0
  180. package/skills-src/ewai-design-system-review/SKILL.md +26 -0
  181. package/skills-src/ewai-design-system-review/agents/openai.yaml +4 -0
  182. package/skills-src/ewai-design-system-review/references/review-contract.md +40 -0
  183. package/skills-src/ewai-error-reporting/SKILL.md +46 -0
  184. package/skills-src/ewai-error-reporting/agents/openai.yaml +4 -0
  185. package/skills-src/ewai-error-reporting/references/provider-contract.md +74 -0
  186. package/skills-src/ewai-evidence-depth/SKILL.md +72 -0
  187. package/skills-src/ewai-evidence-depth/agents/openai.yaml +4 -0
  188. package/skills-src/ewai-evidence-depth/references/evidence-depth-contract.md +127 -0
  189. package/skills-src/ewai-intent/SKILL.md +68 -0
  190. package/skills-src/ewai-intent/agents/openai.yaml +4 -0
  191. package/skills-src/ewai-intent/references/intent-contract.md +42 -0
  192. package/skills-src/ewai-knowledge-proposals/SKILL.md +105 -0
  193. package/skills-src/ewai-knowledge-proposals/agents/openai.yaml +4 -0
  194. package/skills-src/ewai-knowledge-proposals/references/proposal-contract.md +59 -0
  195. package/skills-src/ewai-meeting-evidence/SKILL.md +106 -0
  196. package/skills-src/ewai-meeting-evidence/agents/openai.yaml +4 -0
  197. package/skills-src/ewai-meeting-evidence/references/candidate-contract.md +64 -0
  198. package/skills-src/ewai-organisation-policy/SKILL.md +62 -0
  199. package/skills-src/ewai-organisation-policy/agents/openai.yaml +4 -0
  200. package/skills-src/ewai-organisation-policy/references/policy-contract.md +94 -0
  201. package/skills-src/ewai-palace-housekeeping/SKILL.md +55 -0
  202. package/skills-src/ewai-palace-housekeeping/agents/openai.yaml +4 -0
  203. package/skills-src/ewai-persona-entitlement/SKILL.md +60 -0
  204. package/skills-src/ewai-persona-entitlement/agents/openai.yaml +4 -0
  205. package/skills-src/ewai-phase-evidence/SKILL.md +79 -0
  206. package/skills-src/ewai-phase-evidence/agents/openai.yaml +4 -0
  207. package/skills-src/ewai-pipeline/SKILL.md +130 -0
  208. package/skills-src/ewai-pipeline/agents/openai.yaml +4 -0
  209. package/skills-src/ewai-pipeline/references/cli.md +86 -0
  210. package/skills-src/ewai-pipeline/references/specs-contract.md +16 -0
  211. package/skills-src/ewai-portfolio/SKILL.md +70 -0
  212. package/skills-src/ewai-portfolio/agents/openai.yaml +4 -0
  213. package/skills-src/ewai-portfolio/references/portfolio-contract.md +67 -0
  214. package/skills-src/ewai-project-discovery/SKILL.md +95 -0
  215. package/skills-src/ewai-project-discovery/agents/openai.yaml +4 -0
  216. package/skills-src/ewai-project-discovery/references/discovery-contract.md +34 -0
  217. package/skills-src/ewai-prototype-iteration/SKILL.md +30 -0
  218. package/skills-src/ewai-prototype-iteration/agents/openai.yaml +4 -0
  219. package/skills-src/ewai-prototype-iteration/references/review-contract.md +49 -0
  220. package/skills-src/ewai-retro/SKILL.md +48 -0
  221. package/skills-src/ewai-retro/agents/openai.yaml +4 -0
  222. package/skills-src/ewai-retro/references/asset-routing.md +14 -0
  223. package/skills-src/ewai-rollout/SKILL.md +74 -0
  224. package/skills-src/ewai-rollout/agents/openai.yaml +4 -0
  225. package/skills-src/ewai-rollout/references/rollout-contract.md +74 -0
  226. package/skills-src/ewai-shape-intents/SKILL.md +84 -0
  227. package/skills-src/ewai-shape-intents/agents/openai.yaml +4 -0
  228. package/skills-src/ewai-shape-intents/references/intent-mapping-contract.md +109 -0
  229. package/skills-src/ewai-solution-readiness/SKILL.md +55 -0
  230. package/skills-src/ewai-solution-readiness/agents/openai.yaml +4 -0
  231. package/skills-src/ewai-standards-check/SKILL.md +93 -0
  232. package/skills-src/ewai-standards-check/agents/openai.yaml +4 -0
  233. package/skills-src/ewai-standards-check/references/report-contract.md +116 -0
  234. package/skills-src/ewai-test-scenarios/SKILL.md +94 -0
  235. package/skills-src/ewai-test-scenarios/agents/openai.yaml +4 -0
  236. package/skills-src/ewai-test-scenarios/references/scenario-contract.md +88 -0
  237. package/src/afk-worker.mjs +16 -0
  238. package/src/archaeology.mjs +1333 -0
  239. package/src/checkin.mjs +261 -0
  240. package/src/cli.mjs +2427 -0
  241. package/src/companion-guidance.mjs +257 -0
  242. package/src/companion-opening.mjs +62 -0
  243. package/src/companion.mjs +256 -0
  244. package/src/context.mjs +210 -0
  245. package/src/dashboard-preferences.mjs +80 -0
  246. package/src/delivery-artifacts.mjs +204 -0
  247. package/src/delivery-documents.mjs +248 -0
  248. package/src/delivery-gates.mjs +317 -0
  249. package/src/delivery.mjs +1433 -0
  250. package/src/design-system-application.mjs +291 -0
  251. package/src/design-system-authoring.mjs +101 -0
  252. package/src/design-systems.mjs +466 -0
  253. package/src/discovery.mjs +1314 -0
  254. package/src/error-reporting.mjs +323 -0
  255. package/src/evidence-depth.mjs +543 -0
  256. package/src/execution-state.mjs +243 -0
  257. package/src/install.mjs +166 -0
  258. package/src/intent-dependencies.mjs +117 -0
  259. package/src/intent-maps.mjs +402 -0
  260. package/src/intents.mjs +747 -0
  261. package/src/knowledge-proposals.mjs +717 -0
  262. package/src/launcher.mjs +51 -0
  263. package/src/meeting-evidence.mjs +703 -0
  264. package/src/network-rollout.mjs +386 -0
  265. package/src/organisation-blueprints.mjs +438 -0
  266. package/src/organisation-policies.mjs +448 -0
  267. package/src/packs.mjs +44 -0
  268. package/src/paths.mjs +62 -0
  269. package/src/persona-entitlements.mjs +438 -0
  270. package/src/persona-licence-config.mjs +98 -0
  271. package/src/persona-website-provider.mjs +134 -0
  272. package/src/persona-zip.mjs +87 -0
  273. package/src/personas.mjs +159 -0
  274. package/src/platform-metadata-analysis.mjs +314 -0
  275. package/src/policy-design-gates.mjs +623 -0
  276. package/src/policy-gate-integration.mjs +318 -0
  277. package/src/portfolio.mjs +509 -0
  278. package/src/power-platform-source-map.mjs +190 -0
  279. package/src/project.mjs +449 -0
  280. package/src/prototype-iterations.mjs +730 -0
  281. package/src/repository-source-map.mjs +603 -0
  282. package/src/runtime/afk-conductor.mjs +973 -0
  283. package/src/runtime/context-assembly.mjs +457 -0
  284. package/src/runtime/context-benchmarks.mjs +115 -0
  285. package/src/runtime/dashboard-actions.mjs +109 -0
  286. package/src/runtime/dashboard-handoffs.mjs +149 -0
  287. package/src/runtime/dashboard-server.mjs +1272 -0
  288. package/src/runtime/dashboard.mjs +197 -0
  289. package/src/runtime/database.mjs +789 -0
  290. package/src/runtime/error-reporting.mjs +581 -0
  291. package/src/runtime/evidence-depth-workspace.mjs +412 -0
  292. package/src/runtime/execution-leases.mjs +299 -0
  293. package/src/runtime/guided-discovery.mjs +350 -0
  294. package/src/runtime/guided-intents.mjs +517 -0
  295. package/src/runtime/impact-analysis.mjs +535 -0
  296. package/src/runtime/intents.mjs +222 -0
  297. package/src/runtime/knowledge.mjs +86 -0
  298. package/src/runtime/lifecycle-hooks.mjs +1239 -0
  299. package/src/runtime/mcp-config.mjs +110 -0
  300. package/src/runtime/mcp-server.mjs +1885 -0
  301. package/src/runtime/palace.mjs +362 -0
  302. package/src/runtime/paths.mjs +58 -0
  303. package/src/runtime/persona-engagement.mjs +255 -0
  304. package/src/runtime/phase-contributions.mjs +594 -0
  305. package/src/runtime/policy-workspace.mjs +170 -0
  306. package/src/runtime/prototype-iterations.mjs +235 -0
  307. package/src/runtime/provider-adapters.mjs +163 -0
  308. package/src/runtime/repository-index.mjs +838 -0
  309. package/src/runtime/runs.mjs +185 -0
  310. package/src/runtime/security-validation.mjs +1230 -0
  311. package/src/runtime/starter-materialisation.mjs +1155 -0
  312. package/src/runtime/team-hub-client.mjs +479 -0
  313. package/src/runtime/team-hub-database.mjs +288 -0
  314. package/src/runtime/team-hub-server.mjs +191 -0
  315. package/src/runtime/team-hub.mjs +110 -0
  316. package/src/runtime/tree-sitter-index.mjs +390 -0
  317. package/src/runtime/version.mjs +1 -0
  318. package/src/runtime/work.mjs +633 -0
  319. package/src/salesforce-source-map.mjs +212 -0
  320. package/src/security-validation-config.mjs +224 -0
  321. package/src/solution-readiness.mjs +620 -0
  322. package/src/starter-materialisation-contract.mjs +407 -0
  323. package/src/task-graph.mjs +544 -0
  324. package/src/team-hub-resources.mjs +239 -0
  325. package/src/team-hub.mjs +242 -0
  326. package/src/test-scenarios.mjs +622 -0
  327. package/src/validation-config.mjs +289 -0
  328. package/templates/SPECS/1.Scope/personas/registry.yaml +12 -0
  329. package/templates/SPECS/5.Strategy/patterns/context-packet.md +119 -0
  330. package/templates/SPECS/6.Build/_tracker-template.md +16 -0
  331. package/templates/SPECS/pipeline.yaml +62 -0
  332. package/templates/discovery-answers.yaml +86 -0
  333. package/templates/intent-body.md +21 -0
  334. package/tests/fixtures/context-benchmarks.json +9 -0
@@ -0,0 +1,212 @@
1
+ import { basename } from 'node:path';
2
+ import {
3
+ PlatformAnalysisError,
4
+ assertBoundedPlatformStructure,
5
+ createPlatformFactCollector,
6
+ normalisePlatformIdentifier,
7
+ normalisePlatformPathIdentifier,
8
+ parseSafePlatformXml,
9
+ platformBoolean,
10
+ platformFactsFile,
11
+ xmlChildText,
12
+ xmlChildren,
13
+ xmlDescendants
14
+ } from './platform-metadata-analysis.mjs';
15
+
16
+ const parser = 'ewai-salesforce-metadata/v1';
17
+
18
+ function normalPath(path) {
19
+ return String(path ?? '').replaceAll('\\', '/').replace(/^\.\//, '');
20
+ }
21
+
22
+ function stem(path, suffix) {
23
+ return basename(path).slice(0, -suffix.length);
24
+ }
25
+
26
+ function analysePackage(path, content) {
27
+ const root = parseSafePlatformXml(content);
28
+ if (root.name.toLowerCase() !== 'package') throw new PlatformAnalysisError('unsupported-platform-layout');
29
+ const collector = createPlatformFactCollector({ platform: 'salesforce', layout: 'metadata-package-xml' });
30
+ const packageName = 'package';
31
+ collector.addSymbol('salesforce_package', packageName);
32
+ for (const type of xmlChildren(root, 'types')) {
33
+ const typeName = normalisePlatformIdentifier(xmlChildText(type, 'name'));
34
+ if (!typeName) {
35
+ collector.markPartial();
36
+ continue;
37
+ }
38
+ for (const member of xmlChildren(type, 'members')) {
39
+ const memberName = normalisePlatformIdentifier(member.text);
40
+ if (!memberName) continue;
41
+ const componentName = `${typeName}:${memberName}`;
42
+ collector.addSymbol('salesforce_component', componentName, { component_type: typeName });
43
+ collector.addRelationship('salesforce_package', packageName, 'contains', 'salesforce_component', componentName);
44
+ }
45
+ }
46
+ return platformFactsFile(collector.finish({ format_version: xmlChildText(root, 'version') }), {
47
+ language: 'xml', parser, content
48
+ });
49
+ }
50
+
51
+ function analyseProject(content) {
52
+ let value;
53
+ try {
54
+ value = JSON.parse(String(content ?? '').replace(/^\uFEFF/, ''));
55
+ assertBoundedPlatformStructure(value);
56
+ } catch (error) {
57
+ if (error instanceof PlatformAnalysisError) throw error;
58
+ throw new PlatformAnalysisError('invalid-platform-document');
59
+ }
60
+ const collector = createPlatformFactCollector({ platform: 'salesforce', layout: 'sfdx-project-json' });
61
+ collector.addSymbol('salesforce_project', 'sfdx-project');
62
+ for (const entry of Array.isArray(value.packageDirectories) ? value.packageDirectories : []) {
63
+ const directory = normalisePlatformPathIdentifier(entry?.path);
64
+ if (!directory) {
65
+ collector.markPartial(1);
66
+ continue;
67
+ }
68
+ collector.addSymbol('salesforce_package_directory', directory);
69
+ collector.addRelationship('salesforce_project', 'sfdx-project', 'contains', 'salesforce_package_directory', directory);
70
+ }
71
+ return platformFactsFile(collector.finish(), { language: 'json', parser, content });
72
+ }
73
+
74
+ function analyseObject(path, content) {
75
+ parseSafePlatformXml(content);
76
+ const object = normalPath(path).match(/(?:^|\/)objects\/([^/]+)\/[^/]+\.object-meta\.xml$/i)?.[1];
77
+ const collector = createPlatformFactCollector({ platform: 'salesforce', layout: 'decomposed-custom-object' });
78
+ if (object) collector.addSymbol('salesforce_object', object);
79
+ else collector.markPartial();
80
+ return platformFactsFile(collector.finish(), { language: 'xml', parser, content });
81
+ }
82
+
83
+ function analyseField(path, content) {
84
+ const root = parseSafePlatformXml(content);
85
+ const match = normalPath(path).match(/(?:^|\/)objects\/([^/]+)\/fields\/([^/]+)\.field-meta\.xml$/i);
86
+ const object = normalisePlatformIdentifier(match?.[1]);
87
+ const field = normalisePlatformIdentifier(xmlChildText(root, 'fullName') || match?.[2]);
88
+ const collector = createPlatformFactCollector({ platform: 'salesforce', layout: 'decomposed-custom-field' });
89
+ if (object && field) {
90
+ const qualified = field.includes('.') ? field : `${object}.${field}`;
91
+ collector.addSymbol('salesforce_object', object);
92
+ collector.addSymbol('salesforce_field', qualified);
93
+ collector.addRelationship('salesforce_object', object, 'contains', 'salesforce_field', qualified);
94
+ } else collector.markPartial();
95
+ return platformFactsFile(collector.finish(), { language: 'xml', parser, content });
96
+ }
97
+
98
+ function booleanMetadata(node, fields) {
99
+ const metadata = {};
100
+ for (const [xml, output] of fields) {
101
+ const parsed = platformBoolean(xmlChildText(node, xml));
102
+ if (parsed !== null) metadata[output] = parsed;
103
+ }
104
+ return metadata;
105
+ }
106
+
107
+ function analysePermissions(path, content, layout, kind, suffix) {
108
+ const root = parseSafePlatformXml(content);
109
+ const name = normalisePlatformIdentifier(stem(path, suffix));
110
+ const collector = createPlatformFactCollector({ platform: 'salesforce', layout });
111
+ if (!name) collector.markPartial();
112
+ else collector.addSymbol(kind, name);
113
+
114
+ const addAccess = (element, targetElement, targetKind, relationshipKind, metadata = {}) => {
115
+ const target = normalisePlatformIdentifier(xmlChildText(element, targetElement));
116
+ if (!name || !target) return;
117
+ collector.addSymbol(targetKind, target);
118
+ collector.addRelationship(kind, name, relationshipKind, targetKind, target, metadata);
119
+ };
120
+ for (const item of xmlDescendants(root, 'objectPermissions')) {
121
+ addAccess(item, 'object', 'salesforce_object', 'object_permission', booleanMetadata(item, [
122
+ ['allowCreate', 'allow_create'], ['allowDelete', 'allow_delete'], ['allowEdit', 'allow_edit'],
123
+ ['allowRead', 'allow_read'], ['modifyAllRecords', 'modify_all_records'], ['viewAllRecords', 'view_all_records']
124
+ ]));
125
+ }
126
+ for (const item of xmlDescendants(root, 'fieldPermissions')) {
127
+ addAccess(item, 'field', 'salesforce_field', 'field_permission', booleanMetadata(item, [
128
+ ['editable', 'editable'], ['readable', 'readable']
129
+ ]));
130
+ }
131
+ for (const item of xmlDescendants(root, 'classAccesses')) {
132
+ addAccess(item, 'apexClass', 'salesforce_apex_class', 'apex_class_access', booleanMetadata(item, [['enabled', 'enabled']]));
133
+ }
134
+ for (const item of xmlDescendants(root, 'applicationVisibilities')) {
135
+ addAccess(item, 'application', 'salesforce_application', 'application_visibility', booleanMetadata(item, [['visible', 'visible']]));
136
+ }
137
+ for (const item of xmlDescendants(root, 'tabSettings')) {
138
+ addAccess(item, 'tab', 'salesforce_tab', 'tab_visibility', { visibility: xmlChildText(item, 'visibility') });
139
+ }
140
+ return platformFactsFile(collector.finish(), { language: 'xml', parser, content });
141
+ }
142
+
143
+ function analyseFlow(path, content) {
144
+ const root = parseSafePlatformXml(content);
145
+ const name = normalisePlatformIdentifier(stem(path, '.flow-meta.xml'));
146
+ const collector = createPlatformFactCollector({ platform: 'salesforce', layout: 'flow-metadata' });
147
+ if (name) collector.addSymbol('salesforce_flow', name);
148
+ else collector.markPartial();
149
+ for (const [element, relationship] of [
150
+ ['recordCreates', 'creates_records'],
151
+ ['recordLookups', 'reads_records'],
152
+ ['recordUpdates', 'updates_records'],
153
+ ['recordDeletes', 'deletes_records']
154
+ ]) {
155
+ for (const operation of xmlDescendants(root, element)) {
156
+ const object = normalisePlatformIdentifier(xmlChildText(operation, 'object'));
157
+ if (!name || !object) continue;
158
+ collector.addSymbol('salesforce_object', object);
159
+ collector.addRelationship('salesforce_flow', name, relationship, 'salesforce_object', object);
160
+ }
161
+ }
162
+ return platformFactsFile(collector.finish(), { language: 'xml', parser, content });
163
+ }
164
+
165
+ function analyseCompanion(path, content) {
166
+ parseSafePlatformXml(content);
167
+ const value = normalPath(path);
168
+ let kind = '';
169
+ let name = '';
170
+ let layout = '';
171
+ const apex = value.match(/(?:^|\/)classes\/([^/]+)\.cls-meta\.xml$/i);
172
+ const lightning = value.match(/(?:^|\/)(lwc|aura)\/([^/]+)\/[^/]+\.(?:js|cmp|app|evt)-meta\.xml$/i);
173
+ if (apex) {
174
+ kind = 'salesforce_apex_class';
175
+ name = apex[1];
176
+ layout = 'apex-companion';
177
+ } else if (lightning) {
178
+ kind = lightning[1].toLowerCase() === 'lwc' ? 'salesforce_lwc' : 'salesforce_aura';
179
+ name = lightning[2];
180
+ layout = 'lightning-companion';
181
+ }
182
+ if (!kind || !name) throw new PlatformAnalysisError('unsupported-platform-layout');
183
+ const collector = createPlatformFactCollector({ platform: 'salesforce', layout });
184
+ collector.addSymbol(kind, name);
185
+ return platformFactsFile(collector.finish(), { language: 'xml', parser, content });
186
+ }
187
+
188
+ export function salesforceLayout(path) {
189
+ const value = normalPath(path).toLowerCase();
190
+ if (/(?:^|\/)(?:manifest\/|unpackaged\/)?package\.xml$/.test(value)) return 'metadata-package-xml';
191
+ if (/(?:^|\/)sfdx-project\.json$/.test(value)) return 'sfdx-project-json';
192
+ if (/\.object-meta\.xml$/.test(value)) return 'decomposed-custom-object';
193
+ if (/\.field-meta\.xml$/.test(value)) return 'decomposed-custom-field';
194
+ if (/\.permissionset-meta\.xml$/.test(value)) return 'permission-set-metadata';
195
+ if (/\.profile-meta\.xml$/.test(value)) return 'profile-metadata';
196
+ if (/\.flow-meta\.xml$/.test(value)) return 'flow-metadata';
197
+ if (/\.cls-meta\.xml$/.test(value) || /(?:^|\/)(?:lwc|aura)\/[^/]+\/[^/]+\.(?:js|cmp|app|evt)-meta\.xml$/.test(value)) return 'component-companion';
198
+ return '';
199
+ }
200
+
201
+ export function analyseSalesforceMetadata(path, content) {
202
+ const layout = salesforceLayout(path);
203
+ if (layout === 'metadata-package-xml') return analysePackage(path, content);
204
+ if (layout === 'sfdx-project-json') return analyseProject(content);
205
+ if (layout === 'decomposed-custom-object') return analyseObject(path, content);
206
+ if (layout === 'decomposed-custom-field') return analyseField(path, content);
207
+ if (layout === 'permission-set-metadata') return analysePermissions(path, content, layout, 'salesforce_permission_set', '.permissionset-meta.xml');
208
+ if (layout === 'profile-metadata') return analysePermissions(path, content, layout, 'salesforce_profile', '.profile-meta.xml');
209
+ if (layout === 'flow-metadata') return analyseFlow(path, content);
210
+ if (layout === 'component-companion') return analyseCompanion(path, content);
211
+ throw new PlatformAnalysisError('unsupported-platform-layout');
212
+ }
@@ -0,0 +1,224 @@
1
+ import { isAbsolute } from 'node:path';
2
+
3
+ export const ASSURANCE_NOTICE = 'Security validation is evidence, not certification or proof that this system is secure. Tools can miss vulnerabilities and produce false positives. A qualified human must review the scope, findings, limitations and residual risk before release.';
4
+
5
+ export const SECURITY_CAPABILITIES = Object.freeze([
6
+ 'source-static',
7
+ 'dependency-sbom',
8
+ 'secret-detection',
9
+ 'infrastructure-configuration',
10
+ 'llm-runtime-red-team',
11
+ ]);
12
+
13
+ export const SECURITY_INTERACTION_MODES = Object.freeze(['command', 'skill', 'artifact-import']);
14
+
15
+ export const SECURITY_PROVIDER_CATALOGUE = Object.freeze([
16
+ Object.freeze({
17
+ id: 'agentic-security',
18
+ name: 'Agentic Security',
19
+ publisher: 'Clear Capabilities',
20
+ official_sources: Object.freeze(['https://github.com/Clear-Capabilities/agentic-security']),
21
+ capabilities: Object.freeze([...SECURITY_CAPABILITIES]),
22
+ artifact_import_supported: true,
23
+ summary: 'Security analysis across source, dependencies, secrets, infrastructure, agent and LLM surfaces, with reporting and remediation workflows.',
24
+ cautions: Object.freeze([
25
+ 'EWAI does not install, redistribute, license or endorse this provider.',
26
+ 'Review the provider licence before repackaging, resale or use in a competing scanner.',
27
+ 'Automated findings can be incomplete or false positive and require qualified human review.',
28
+ ]),
29
+ discovery: Object.freeze({ root: '.agentic-security', signals: Object.freeze(['findings.json', 'last-scan.json']) }),
30
+ }),
31
+ Object.freeze({
32
+ id: 'deepsec',
33
+ name: 'DeepSec',
34
+ publisher: 'Vercel Labs',
35
+ official_sources: Object.freeze([
36
+ 'https://github.com/vercel-labs/deepsec',
37
+ 'https://www.npmjs.com/package/deepsec',
38
+ ]),
39
+ capabilities: Object.freeze(['source-static', 'secret-detection']),
40
+ artifact_import_supported: true,
41
+ summary: 'Agent-powered source vulnerability investigation with resumable scans, revalidation, diff review and Markdown or JSON reporting.',
42
+ cautions: Object.freeze([
43
+ 'EWAI does not install, configure, license or endorse this provider.',
44
+ 'Model use can be expensive on large repositories and the tool should be trusted like a coding agent with shell access.',
45
+ 'Optional sandbox execution can upload a working-tree archive; review data handling before use.',
46
+ ]),
47
+ discovery: Object.freeze({ root: '.deepsec', signals: Object.freeze(['report.json', 'run.json', 'runs', 'reports']) }),
48
+ }),
49
+ Object.freeze({
50
+ id: 'visa-vvah',
51
+ name: 'Visa Vulnerability Agentic Harness (VVAH)',
52
+ publisher: 'Visa',
53
+ official_sources: Object.freeze(['https://github.com/visa/visa-vulnerability-agentic-harness']),
54
+ capabilities: Object.freeze(['source-static']),
55
+ artifact_import_supported: false,
56
+ summary: 'Threat-modelled agentic source vulnerability discovery and structured reporting, with optional upstream remediation and validation stages.',
57
+ cautions: Object.freeze([
58
+ 'EWAI does not install, invoke, redistribute, license or endorse this provider.',
59
+ 'Use detection-only mode with vvaharness scan --repo <path> --stop-after s9; the upstream default can continue into remediation and edit target source.',
60
+ 'VVAH can send source-derived prompt data to configured model providers, uses elevated local privileges, can be token-intensive and produces nondeterministic triage candidates requiring qualified human review.',
61
+ 'Native VVAH artefact import is not supported; use the revision-bound skill handoff and an organisation-controlled translator to ewai.security-scan-response/v1.',
62
+ ]),
63
+ discovery: Object.freeze({ root: '.', signals: Object.freeze(['security-scan', 'run_manifest.json']) }),
64
+ }),
65
+ ]);
66
+
67
+ const configFields = new Set(['enabled', 'profiles']);
68
+ const profileFields = new Set([
69
+ 'id', 'capability', 'required', 'freshness_hours', 'timeout_seconds', 'accountable_role',
70
+ 'modes', 'provider', 'adapter', 'checkpoint', 'thresholds', 'scope', 'target_class', 'target_ref',
71
+ ]);
72
+ const profileIdPattern = /^[a-z][a-z0-9-]{1,63}$/;
73
+ const adapterIdPattern = /^[a-z][a-z0-9.-]{1,79}$/;
74
+ const thresholdFields = new Set(['blocking_severities']);
75
+ const scopeFields = new Set(['include', 'exclude']);
76
+ const securitySeverities = Object.freeze(['critical', 'high', 'medium', 'low', 'info', 'unknown']);
77
+ const targetClasses = new Set(['local', 'development', 'test', 'staging', 'production']);
78
+
79
+ function credentialShaped(value) {
80
+ return /(?:Bearer\s+[A-Za-z0-9._~+\/-]{12,}|sk-[A-Za-z0-9_-]{16,}|-----BEGIN [A-Z ]*PRIVATE KEY-----)/i.test(String(value ?? ''));
81
+ }
82
+
83
+ function assertObject(value, label) {
84
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`${label} must be an object.`);
85
+ }
86
+
87
+ function rejectUnknown(value, allowed, label) {
88
+ for (const key of Object.keys(value)) {
89
+ if (!allowed.has(key)) throw new Error(`${label} contains unknown field: ${key}`);
90
+ }
91
+ }
92
+
93
+ function boundedInteger(value, label, minimum, maximum, fallback) {
94
+ const selected = value === undefined ? fallback : value;
95
+ if (!Number.isInteger(selected) || selected < minimum || selected > maximum) {
96
+ throw new Error(`${label} must be an integer between ${minimum} and ${maximum}.`);
97
+ }
98
+ return selected;
99
+ }
100
+
101
+ function normaliseThresholds(value) {
102
+ if (value === undefined) return { blocking_severities: ['critical', 'high'] };
103
+ assertObject(value, 'Security profile thresholds');
104
+ rejectUnknown(value, thresholdFields, 'Security profile thresholds');
105
+ const severities = value.blocking_severities ?? ['critical', 'high'];
106
+ if (!Array.isArray(severities) || !severities.length || severities.length > securitySeverities.length) {
107
+ throw new Error('Security profile blocking_severities must be a non-empty severity array.');
108
+ }
109
+ const normalised = severities.map((severity) => String(severity).trim().toLowerCase());
110
+ if (new Set(normalised).size !== normalised.length || normalised.some((severity) => !securitySeverities.includes(severity))) {
111
+ throw new Error('Security profile blocking_severities contains a duplicate or unsupported severity.');
112
+ }
113
+ return { blocking_severities: normalised };
114
+ }
115
+
116
+ function normaliseScopeList(value, label) {
117
+ if (value === undefined) return [];
118
+ if (!Array.isArray(value) || value.length > 100) throw new Error(`${label} must be an array of at most 100 project-relative paths.`);
119
+ const seen = new Set();
120
+ return value.map((candidate) => {
121
+ const path = String(candidate ?? '').trim().replaceAll('\\', '/');
122
+ if (!path || path.length > 240 || credentialShaped(path) || isAbsolute(path) || path === '..' || path.startsWith('../') || path.includes('/../')) {
123
+ throw new Error(`${label} must contain safe project-relative paths.`);
124
+ }
125
+ if (seen.has(path)) throw new Error(`${label} contains a duplicate path: ${path}`);
126
+ seen.add(path);
127
+ return path;
128
+ });
129
+ }
130
+
131
+ function normaliseScope(value) {
132
+ if (value === undefined) return { include: [], exclude: [] };
133
+ assertObject(value, 'Security profile scope');
134
+ rejectUnknown(value, scopeFields, 'Security profile scope');
135
+ return {
136
+ include: normaliseScopeList(value.include, 'Security profile scope.include'),
137
+ exclude: normaliseScopeList(value.exclude, 'Security profile scope.exclude'),
138
+ };
139
+ }
140
+
141
+ function normaliseProfile(profile, seen) {
142
+ assertObject(profile, 'Security profile');
143
+ rejectUnknown(profile, profileFields, 'Security profile');
144
+ const id = String(profile.id ?? '').trim();
145
+ if (!profileIdPattern.test(id)) throw new Error('Security profile id must be lower-kebab-case and 2-64 characters.');
146
+ if (seen.has(id)) throw new Error(`Duplicate profile id: ${id}`);
147
+ seen.add(id);
148
+ const capability = String(profile.capability ?? '').trim();
149
+ if (!SECURITY_CAPABILITIES.includes(capability)) throw new Error(`Unsupported security capability: ${capability || '(empty)'}`);
150
+ const required = profile.required !== false;
151
+ const accountableRole = String(profile.accountable_role ?? '').trim();
152
+ if (!accountableRole || accountableRole.length > 120 || credentialShaped(accountableRole)) throw new Error(`Security profile ${id} requires a bounded accountable_role.`);
153
+ if (required && profile.freshness_hours === undefined) throw new Error(`Required security profile ${id} requires freshness_hours.`);
154
+ const freshnessHours = boundedInteger(profile.freshness_hours, `${id}.freshness_hours`, 1, 8760, 168);
155
+ const timeoutSeconds = boundedInteger(profile.timeout_seconds, `${id}.timeout_seconds`, 1, 3600, 300);
156
+ const modes = profile.modes === undefined ? [...SECURITY_INTERACTION_MODES] : profile.modes;
157
+ if (!Array.isArray(modes) || !modes.length || modes.some((mode) => !SECURITY_INTERACTION_MODES.includes(mode))) {
158
+ throw new Error(`Security profile ${id} modes must contain supported interaction modes.`);
159
+ }
160
+ if (new Set(modes).size !== modes.length) throw new Error(`Security profile ${id} contains duplicate interaction modes.`);
161
+ const provider = String(profile.provider ?? '').trim();
162
+ if (profile.provider !== undefined && !provider) throw new Error(`Security profile ${id} provider is invalid.`);
163
+ if (provider && !SECURITY_PROVIDER_CATALOGUE.some((candidate) => candidate.id === provider)) {
164
+ throw new Error(`Unknown security provider: ${provider}`);
165
+ }
166
+ const providerDefinition = provider ? securityProvider(provider) : null;
167
+ if (providerDefinition?.artifact_import_supported === false && modes.includes('artifact-import')) {
168
+ throw new Error(`${providerDefinition.name} does not support native artefact import; use a skill handoff.`);
169
+ }
170
+ const adapter = profile.adapter ? String(profile.adapter).trim() : '';
171
+ if (profile.adapter !== undefined && !adapter) throw new Error(`Security profile ${id} adapter identity is invalid.`);
172
+ if (adapter && !adapterIdPattern.test(adapter)) throw new Error(`Security profile ${id} adapter identity is invalid.`);
173
+ const checkpoint = String(profile.checkpoint ?? 'release').trim();
174
+ if (!checkpoint || checkpoint.length > 80) throw new Error(`Security profile ${id} checkpoint is invalid.`);
175
+ const targetClass = profile.target_class ? String(profile.target_class).trim() : '';
176
+ if (profile.target_class !== undefined && !targetClass) throw new Error(`Security profile ${id} target_class is invalid.`);
177
+ if (targetClass && !targetClasses.has(targetClass)) throw new Error(`Security profile ${id} target_class is invalid.`);
178
+ const targetRef = profile.target_ref ? String(profile.target_ref).trim() : '';
179
+ if (profile.target_ref !== undefined && !targetRef) throw new Error(`Security profile ${id} target_ref is invalid.`);
180
+ if (targetRef.length > 160 || credentialShaped(targetRef)) throw new Error(`Security profile ${id} target_ref is unsafe.`);
181
+ return {
182
+ id,
183
+ capability,
184
+ required,
185
+ freshness_hours: freshnessHours,
186
+ timeout_seconds: timeoutSeconds,
187
+ accountable_role: accountableRole,
188
+ modes: [...modes],
189
+ provider: provider || null,
190
+ adapter: adapter || null,
191
+ checkpoint,
192
+ thresholds: normaliseThresholds(profile.thresholds),
193
+ scope: normaliseScope(profile.scope),
194
+ target_class: targetClass || null,
195
+ target_ref: targetRef || null,
196
+ };
197
+ }
198
+
199
+ export function normaliseSecurityValidationConfig(config = {}) {
200
+ assertObject(config, 'Project configuration');
201
+ const raw = config.security_validation;
202
+ if (raw === undefined || raw === null || raw === false) {
203
+ return { schema: 'ewai.security-policy/v1', status: 'not-configured', enabled: false, profiles: [], assurance_notice: ASSURANCE_NOTICE };
204
+ }
205
+ assertObject(raw, 'security_validation');
206
+ rejectUnknown(raw, configFields, 'security_validation');
207
+ if (raw.enabled === false) {
208
+ return { schema: 'ewai.security-policy/v1', status: 'not-configured', enabled: false, profiles: [], assurance_notice: ASSURANCE_NOTICE };
209
+ }
210
+ if (raw.enabled !== true) throw new Error('security_validation.enabled must be true or false.');
211
+ if (!Array.isArray(raw.profiles) || !raw.profiles.length) throw new Error('Enabled security validation requires at least one profile.');
212
+ const seen = new Set();
213
+ return {
214
+ schema: 'ewai.security-policy/v1',
215
+ status: 'configured',
216
+ enabled: true,
217
+ profiles: raw.profiles.map((profile) => normaliseProfile(profile, seen)),
218
+ assurance_notice: ASSURANCE_NOTICE,
219
+ };
220
+ }
221
+
222
+ export function securityProvider(providerId) {
223
+ return SECURITY_PROVIDER_CATALOGUE.find((provider) => provider.id === providerId) ?? null;
224
+ }