@bluefly/openstandardagents 0.2.7 → 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 (327) hide show
  1. package/.devfile.yaml +1 -1
  2. package/.env.example +1 -1
  3. package/.github/AGENTS.md +245 -0
  4. package/.github/agents/github-issue-triage.ossa.yaml +99 -0
  5. package/.github/agents/github-pr-triage.ossa.yaml +137 -0
  6. package/.github/workflows/issue-sync-to-gitlab.yml +138 -0
  7. package/.github/workflows/pr-triage-to-gitlab.yml +164 -0
  8. package/.version.json +2 -2
  9. package/.wiki-config.json +24 -0
  10. package/CHANGELOG.md +44 -18
  11. package/CODEOWNERS +75 -0
  12. package/CONTRIBUTING.md +103 -4
  13. package/README.md +178 -243
  14. package/dist/index.d.ts +1 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +2 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/repositories/schema.repository.d.ts +6 -1
  19. package/dist/repositories/schema.repository.d.ts.map +1 -1
  20. package/dist/repositories/schema.repository.js +49 -27
  21. package/dist/repositories/schema.repository.js.map +1 -1
  22. package/dist/services/migration.service.d.ts +4 -3
  23. package/dist/services/migration.service.d.ts.map +1 -1
  24. package/dist/services/migration.service.js +11 -10
  25. package/dist/services/migration.service.js.map +1 -1
  26. package/dist/services/release-automation/release.service.js +1 -1
  27. package/dist/services/release-automation/release.service.js.map +1 -1
  28. package/dist/services/release-automation/schemas/release.schema.js +1 -1
  29. package/dist/services/release-automation/webhook.service.js +3 -3
  30. package/dist/services/release-automation/webhook.service.js.map +1 -1
  31. package/dist/services/runtime/claude/claude-adapter.d.ts +1 -1
  32. package/dist/services/runtime/claude/claude-adapter.d.ts.map +1 -1
  33. package/dist/services/runtime/claude/claude-adapter.js +2 -2
  34. package/dist/services/runtime/claude/claude-adapter.js.map +1 -1
  35. package/dist/spec/v0.2.8/CHANGELOG.md +401 -0
  36. package/dist/spec/v0.2.8/README.md +72 -0
  37. package/dist/spec/v0.2.8/migrations/v0.2.3-to-v0.2.4.md +599 -0
  38. package/dist/spec/v0.2.8/migrations/v0.2.5-RC-to-v0.2.6.md +65 -0
  39. package/dist/spec/v0.2.8/migrations/v0.2.6-to-v0.2.8.md +81 -0
  40. package/{spec/v0.2.6-dev/ossa-0.2.5.schema.json → dist/spec/v0.2.8/ossa-0.2.8.schema.json} +1509 -52
  41. package/dist/spec/v0.2.8/ossa-0.2.8.yaml +581 -0
  42. package/dist/spec/v0.2.9/a2a-protocol.md +1337 -0
  43. package/dist/spec/v0.2.9/agent.md +1946 -0
  44. package/dist/spec/v0.2.9/capabilities/index.yaml +25 -0
  45. package/dist/spec/v0.2.9/capabilities/memory.yaml +251 -0
  46. package/dist/spec/v0.2.9/capability-schema.md +576 -0
  47. package/dist/spec/v0.2.9/compliance-profiles.md +533 -0
  48. package/dist/spec/v0.2.9/conformance-testing.md +1527 -0
  49. package/dist/spec/v0.2.9/gitlab-duo-integration.md +621 -0
  50. package/dist/spec/v0.2.9/ossa-0.2.9.schema.json +3699 -0
  51. package/dist/spec/v0.2.9/runtime-semantics.md +464 -0
  52. package/dist/spec/v0.2.9/security-model.md +1245 -0
  53. package/dist/spec/v0.2.9/semantic-conventions.md +347 -0
  54. package/dist/spec/v0.2.9/types.ts +522 -0
  55. package/dist/types/index.d.ts +3 -2
  56. package/dist/types/index.d.ts.map +1 -1
  57. package/dist/types/policy.d.ts +377 -0
  58. package/dist/types/policy.d.ts.map +1 -0
  59. package/dist/types/policy.js +84 -0
  60. package/dist/types/policy.js.map +1 -0
  61. package/dist/utils/index.d.ts +6 -0
  62. package/dist/utils/index.d.ts.map +1 -0
  63. package/dist/utils/index.js +6 -0
  64. package/dist/utils/index.js.map +1 -0
  65. package/dist/utils/version.d.ts +68 -0
  66. package/dist/utils/version.d.ts.map +1 -0
  67. package/dist/utils/version.js +156 -0
  68. package/dist/utils/version.js.map +1 -0
  69. package/docs/specs/policy-dsl.md +925 -0
  70. package/eslint-report.json +1 -0
  71. package/examples/adk-integration/code-review-workflow.yml +1 -1
  72. package/examples/adk-integration/customer-support.yml +1 -1
  73. package/examples/adk-integration/data-pipeline.yml +1 -1
  74. package/examples/advanced/reasoning-agent.yaml +136 -0
  75. package/examples/advanced/workflows/hybrid-model-strategy.yaml +1 -1
  76. package/examples/agent-manifests/critics/critic-agent.yaml +1 -1
  77. package/examples/agent-manifests/governors/governor-agent.yaml +1 -1
  78. package/examples/agent-manifests/integrators/integrator-agent.yaml +1 -1
  79. package/examples/agent-manifests/judges/judge-agent.yaml +1 -1
  80. package/examples/agent-manifests/monitors/monitor-agent.yaml +1 -1
  81. package/examples/agent-manifests/orchestrators/orchestrator-agent.yaml +1 -1
  82. package/examples/agent-manifests/sample-compliant-agent.yaml +1 -1
  83. package/examples/agent-manifests/workers/worker-agent.yaml +1 -1
  84. package/examples/agents-md/code-agent.ossa.json +100 -0
  85. package/examples/agents-md/monorepo-agent.ossa.yaml +180 -0
  86. package/examples/anthropic/claude-assistant.ossa.json +1 -1
  87. package/examples/autogen/multi-agent.ossa.json +1 -1
  88. package/examples/claude-code/code-reviewer.ossa.yaml +78 -0
  89. package/examples/claude-code/ossa-validator.ossa.yaml +80 -0
  90. package/examples/common_npm/agent-router.ossa.yaml +1 -0
  91. package/examples/common_npm/agent-router.v0.2.2.ossa.yaml +1 -1
  92. package/examples/crewai/research-team.ossa.json +1 -1
  93. package/examples/cursor/code-review-agent.ossa.json +1 -1
  94. package/examples/drupal/gitlab-ml-recommender.ossa.yaml +1 -0
  95. package/examples/drupal/gitlab-ml-recommender.v0.2.2.ossa.yaml +1 -1
  96. package/examples/extensions/agents-md-v1.yml +175 -0
  97. package/examples/extensions/drupal-v1.yml +1 -1
  98. package/examples/extensions/kagent-v1.yml +1 -1
  99. package/examples/getting-started/hello-world-complete.ossa.yaml +1 -1
  100. package/examples/integration-patterns/agent-to-agent-orchestration.ossa.yaml +4 -4
  101. package/examples/kagent/compliance-validator.ossa.yaml +1 -1
  102. package/examples/kagent/cost-optimizer.ossa.yaml +1 -1
  103. package/examples/kagent/documentation-agent.ossa.yaml +1 -1
  104. package/examples/kagent/k8s-troubleshooter-v1.ossa.yaml +1 -0
  105. package/examples/kagent/k8s-troubleshooter-v1.v0.2.2.ossa.yaml +1 -1
  106. package/examples/kagent/k8s-troubleshooter.ossa.yaml +1 -1
  107. package/examples/kagent/security-scanner.ossa.yaml +1 -1
  108. package/examples/langchain/chain-agent.ossa.json +1 -1
  109. package/examples/langflow/workflow-agent.ossa.json +1 -1
  110. package/examples/langgraph/state-machine-agent.ossa.json +1 -1
  111. package/examples/llamaindex/rag-agent.ossa.json +1 -1
  112. package/examples/migration-guides/from-langchain-to-ossa.yaml +4 -4
  113. package/examples/multi-agent/README.md +74 -0
  114. package/examples/multi-agent/conditional-router.ossa.yaml +42 -0
  115. package/examples/multi-agent/parallel-execution.ossa.yaml +54 -0
  116. package/examples/multi-agent/sequential-pipeline.ossa.yaml +45 -0
  117. package/examples/openai/basic-agent.ossa.yaml +1 -1
  118. package/examples/openai/multi-tool-agent.ossa.json +1 -1
  119. package/examples/openai/swarm-agent.ossa.json +1 -1
  120. package/examples/production/document-analyzer-openai.yml +1 -1
  121. package/examples/quickstart/support-agent.ossa.yaml +1 -1
  122. package/examples/spec-examples/audit-agent.yml +1 -1
  123. package/examples/spec-examples/chat-agent.yml +1 -1
  124. package/examples/spec-examples/compliance-agent.yml +1 -1
  125. package/examples/spec-examples/monitoring-agent.yml +1 -1
  126. package/examples/spec-examples/workflow-agent.yml +1 -1
  127. package/examples/templates/ossa-compliance.yaml +1 -1
  128. package/examples/vercel/edge-agent.ossa.json +1 -1
  129. package/gl-code-quality-report.json +62 -0
  130. package/llms-ctx-full.txt +39 -0
  131. package/llms-ctx.txt +39 -0
  132. package/llms.txt +47 -0
  133. package/package.json +6 -3
  134. package/scripts/README.md +25 -0
  135. package/scripts/compliance-audit.ts +796 -0
  136. package/scripts/eslint-to-codequality.cjs +34 -0
  137. package/scripts/generate-agents-catalog.ts +2 -1
  138. package/scripts/generate-api-docs.ts +2 -1
  139. package/scripts/generate-examples-docs.ts +2 -1
  140. package/scripts/generate-llms-ctx.sh +17 -0
  141. package/scripts/generate-schema-docs.ts +31 -10
  142. package/scripts/sync-version.js +4 -12
  143. package/scripts/validate-schema.ts +2 -1
  144. package/spec/v0.2.8/CHANGELOG.md +401 -0
  145. package/spec/v0.2.8/README.md +72 -0
  146. package/spec/v0.2.8/migrations/v0.2.3-to-v0.2.4.md +599 -0
  147. package/spec/v0.2.8/migrations/v0.2.5-RC-to-v0.2.6.md +65 -0
  148. package/spec/v0.2.8/migrations/v0.2.6-to-v0.2.8.md +81 -0
  149. package/spec/{v0.2.6-dev/ossa-0.2.6-dev.schema.json → v0.2.8/ossa-0.2.8.schema.json} +1509 -52
  150. package/spec/v0.2.8/ossa-0.2.8.yaml +581 -0
  151. package/spec/v0.2.9/a2a-protocol.md +1337 -0
  152. package/spec/v0.2.9/agent.md +1946 -0
  153. package/spec/v0.2.9/capabilities/index.yaml +25 -0
  154. package/spec/v0.2.9/capabilities/memory.yaml +251 -0
  155. package/spec/v0.2.9/capability-schema.md +576 -0
  156. package/spec/v0.2.9/compliance-profiles.md +533 -0
  157. package/spec/v0.2.9/conformance-testing.md +1527 -0
  158. package/spec/v0.2.9/gitlab-duo-integration.md +621 -0
  159. package/spec/v0.2.9/ossa-0.2.9.schema.json +3699 -0
  160. package/spec/v0.2.9/runtime-semantics.md +464 -0
  161. package/spec/v0.2.9/security-model.md +1245 -0
  162. package/spec/v0.2.9/semantic-conventions.md +347 -0
  163. package/spec/v0.2.9/types.ts +522 -0
  164. package/test-results/junit.xml +337 -0
  165. package/.github/workflows/pr-comment.yml +0 -33
  166. package/bin/validate-ossa-0.2.5-RC.ts +0 -244
  167. package/dist/spec/v0.2.6-dev/ossa-0.2.5.schema.json +0 -1696
  168. package/dist/spec/v0.2.6-dev/ossa-0.2.6-dev.schema.json +0 -1696
  169. package/scripts/lib/exec.ts +0 -37
  170. package/scripts/lib/file-ops.ts +0 -58
  171. package/scripts/lib/version.ts +0 -83
  172. package/website/.lighthouserc.ts +0 -24
  173. package/website/.prettierrc +0 -10
  174. package/website/DESIGN_SYSTEM_IMPLEMENTATION.md +0 -445
  175. package/website/Dockerfile +0 -30
  176. package/website/app/about/page.tsx +0 -304
  177. package/website/app/blog/[slug]/page.tsx +0 -208
  178. package/website/app/blog/page.tsx +0 -249
  179. package/website/app/design-guide/page.tsx +0 -511
  180. package/website/app/docs/[[...slug]]/page.tsx +0 -847
  181. package/website/app/docs/core-concepts/project-structure/page.tsx +0 -349
  182. package/website/app/ecosystem/page.tsx +0 -410
  183. package/website/app/examples/page.tsx +0 -133
  184. package/website/app/globals.scss +0 -370
  185. package/website/app/layout.tsx +0 -106
  186. package/website/app/license/page.tsx +0 -183
  187. package/website/app/not-found.tsx +0 -18
  188. package/website/app/page.tsx +0 -686
  189. package/website/app/page.tsx.bak +0 -679
  190. package/website/app/page.tsx.bak2 +0 -649
  191. package/website/app/playground/page.tsx +0 -487
  192. package/website/app/robots.ts +0 -19
  193. package/website/app/rss.xml/route.ts +0 -74
  194. package/website/app/schema/page.tsx +0 -1001
  195. package/website/app/sitemap.ts +0 -56
  196. package/website/app/specification/page.tsx +0 -287
  197. package/website/components/InstallCommand.tsx +0 -96
  198. package/website/components/Logo.tsx +0 -97
  199. package/website/components/StructuredData.tsx +0 -65
  200. package/website/components/docs/DocsSearch.tsx +0 -104
  201. package/website/components/docs/DocsSidebar.tsx +0 -155
  202. package/website/components/docs/MarkdownContent.tsx +0 -401
  203. package/website/components/docs/VersionSelector.tsx +0 -105
  204. package/website/components/examples/ExamplesViewer.tsx +0 -293
  205. package/website/components/layout/Footer.tsx +0 -116
  206. package/website/components/layout/Header.tsx +0 -172
  207. package/website/components/schema/SchemaComponentsAccordion.tsx +0 -84
  208. package/website/components/schema/SchemaExplorer.tsx +0 -213
  209. package/website/components/ui/Badge.tsx +0 -82
  210. package/website/components/ui/Button.tsx +0 -116
  211. package/website/components/ui/Card.tsx +0 -167
  212. package/website/components/ui/Checkbox.tsx +0 -141
  213. package/website/components/ui/Input.tsx +0 -169
  214. package/website/components/ui/Radio.tsx +0 -141
  215. package/website/components/ui/Select.tsx +0 -182
  216. package/website/components/ui/Tag.tsx +0 -158
  217. package/website/components/ui/Textarea.tsx +0 -195
  218. package/website/components/ui/index.ts +0 -11
  219. package/website/content/blog/OpenAPI-AI-Agents-Standard.md +0 -285
  220. package/website/content/blog/Why-Formal-Standards-Matter-Now.md +0 -198
  221. package/website/content/blog/gitlab-kubernetes-agent-ecosystem.md +0 -286
  222. package/website/content/blog/introducing-ossa-framework.md +0 -328
  223. package/website/content/blog/ossa-production-results.md +0 -279
  224. package/website/content/blog/welcome-to-ossa.md +0 -43
  225. package/website/content/blog/why-ai-agents-need-open-standard.md +0 -98
  226. package/website/content/docs/00-home.md +0 -153
  227. package/website/content/docs/adapters/openai-adapter.md +0 -693
  228. package/website/content/docs/agents/catalog.md +0 -28
  229. package/website/content/docs/aiflow-framework-integration-with-ossa.md +0 -107
  230. package/website/content/docs/api-reference/index.md +0 -38
  231. package/website/content/docs/api-reference/ossa-core-api.md +0 -634
  232. package/website/content/docs/api-reference/ossa-registry-api.md +0 -515
  233. package/website/content/docs/api-reference/unified-agent-gateway.md +0 -599
  234. package/website/content/docs/architecture/execution-flow.md +0 -335
  235. package/website/content/docs/architecture/multi-agent-systems.md +0 -737
  236. package/website/content/docs/architecture/overview.md +0 -121
  237. package/website/content/docs/architecture/stack-integration.md +0 -461
  238. package/website/content/docs/changelog.md +0 -246
  239. package/website/content/docs/cli-reference/index.md +0 -111
  240. package/website/content/docs/cli-reference/ossa-agents.md +0 -70
  241. package/website/content/docs/cli-reference/ossa-export.md +0 -56
  242. package/website/content/docs/cli-reference/ossa-generate.md +0 -66
  243. package/website/content/docs/cli-reference/ossa-gitlab-agent.md +0 -57
  244. package/website/content/docs/cli-reference/ossa-import.md +0 -56
  245. package/website/content/docs/cli-reference/ossa-init.md +0 -57
  246. package/website/content/docs/cli-reference/ossa-migrate.md +0 -62
  247. package/website/content/docs/cli-reference/ossa-run.md +0 -66
  248. package/website/content/docs/cli-reference/ossa-schema.md +0 -57
  249. package/website/content/docs/cli-reference/ossa-setup.md +0 -57
  250. package/website/content/docs/cli-reference/ossa-validate.md +0 -66
  251. package/website/content/docs/configuration/index.md +0 -97
  252. package/website/content/docs/contributing.md +0 -599
  253. package/website/content/docs/deployment/github-mirroring.md +0 -924
  254. package/website/content/docs/documentation.md +0 -100
  255. package/website/content/docs/ecosystem/framework-support.md +0 -1361
  256. package/website/content/docs/ecosystem/overview.md +0 -366
  257. package/website/content/docs/errors/index.md +0 -10
  258. package/website/content/docs/examples/aiflow-framework-integration-with-ossa.md +0 -107
  259. package/website/content/docs/examples/catalog.md +0 -300
  260. package/website/content/docs/for-audiences/students-researchers.md +0 -122
  261. package/website/content/docs/getting-started/index.md +0 -92
  262. package/website/content/docs/getting-started/installation.md +0 -155
  263. package/website/content/docs/getting-started/running-agents.md +0 -309
  264. package/website/content/docs/getting-started.md +0 -91
  265. package/website/content/docs/integrations/aiflow.md +0 -104
  266. package/website/content/docs/integrations/drupal.md +0 -105
  267. package/website/content/docs/migration-guides/agent-schema-comparison.md +0 -232
  268. package/website/content/docs/migration-guides/anthropic-mcp-to-ossa.md +0 -1750
  269. package/website/content/docs/migration-guides/crewai-to-ossa.md +0 -274
  270. package/website/content/docs/migration-guides/drupal-eca-to-ossa.md +0 -2017
  271. package/website/content/docs/migration-guides/general-agent-schema.yml +0 -247
  272. package/website/content/docs/migration-guides/index.md +0 -133
  273. package/website/content/docs/migration-guides/langchain-to-ossa.md +0 -1714
  274. package/website/content/docs/migration-guides/langflow-to-ossa.md +0 -2075
  275. package/website/content/docs/migration-guides/migration-manifest.json +0 -64
  276. package/website/content/docs/migration-guides/openai-to-ossa.md +0 -1202
  277. package/website/content/docs/openapi-extensions/examples.md +0 -550
  278. package/website/content/docs/openapi-extensions/index.md +0 -551
  279. package/website/content/docs/openapi-extensions/operation-extensions.md +0 -457
  280. package/website/content/docs/openapi-extensions/root-extensions.md +0 -410
  281. package/website/content/docs/ossa-compliant-badge.md +0 -251
  282. package/website/content/docs/pre-release/index.md +0 -175
  283. package/website/content/docs/quick-reference.md +0 -17
  284. package/website/content/docs/readme.md +0 -35
  285. package/website/content/docs/releases/v0.2.6.md +0 -99
  286. package/website/content/docs/schema-reference/agent-capabilities.md +0 -50
  287. package/website/content/docs/schema-reference/agent-id.md +0 -52
  288. package/website/content/docs/schema-reference/agent-name.md +0 -50
  289. package/website/content/docs/schema-reference/agent-role.md +0 -54
  290. package/website/content/docs/schema-reference/agent-spec.md +0 -406
  291. package/website/content/docs/schema-reference/agent-version.md +0 -50
  292. package/website/content/docs/schema-reference/autonomy.md +0 -568
  293. package/website/content/docs/schema-reference/constraints.md +0 -543
  294. package/website/content/docs/schema-reference/index.md +0 -45
  295. package/website/content/docs/schema-reference/llm-config.md +0 -445
  296. package/website/content/docs/schema-reference/observability.md +0 -654
  297. package/website/content/docs/schema-reference/ossa-manifest.md +0 -309
  298. package/website/content/docs/schema-reference/taxonomy.md +0 -509
  299. package/website/content/docs/schema-reference/tools.md +0 -628
  300. package/website/content/docs/templates/blog-post.md +0 -43
  301. package/website/content/docs/types-reference/index.md +0 -105
  302. package/website/content/docs/use-cases/00-index.md +0 -395
  303. package/website/content/docs/use-cases/cicd-code-review.md +0 -1236
  304. package/website/content/docs/use-cases/customer-support.md +0 -1234
  305. package/website/content/docs/use-cases/enterprise-compliance.md +0 -1208
  306. package/website/content/docs/use-cases/research-multi-agent.md +0 -1161
  307. package/website/content/docs/versioning.md +0 -288
  308. package/website/dev.sh +0 -53
  309. package/website/docker-compose.dev.yml +0 -36
  310. package/website/lib/version.ts +0 -35
  311. package/website/lib/versions.json +0 -103
  312. package/website/next.config.ts +0 -18
  313. package/website/nginx.conf +0 -32
  314. package/website/package-lock.json +0 -9679
  315. package/website/package.json +0 -59
  316. package/website/postcss.config.mjs +0 -9
  317. package/website/scripts/fetch-versions.js +0 -166
  318. package/website/scripts/generate-examples-index.js +0 -163
  319. package/website/scripts/merge-docs-to-wiki.ts +0 -207
  320. package/website/scripts/sync-version.js +0 -72
  321. package/website/scripts/sync-wiki.ts +0 -322
  322. package/website/scripts/upload-wiki.ts +0 -199
  323. package/website/styles/_spacing.scss +0 -453
  324. package/website/styles/_tokens.scss +0 -245
  325. package/website/styles/_typography.scss +0 -361
  326. package/website/styles/_variables.scss +0 -287
  327. package/website/tailwind.config.ts +0 -170
@@ -0,0 +1,796 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * GitLab Ultimate Compliance Audit System
4
+ *
5
+ * Performs automated compliance checks for GitLab projects:
6
+ * - Protected branches configuration
7
+ * - Merge request approval rules
8
+ * - Security scanning (SAST, dependency scanning, container scanning)
9
+ * - DORA metrics configuration
10
+ * - Compliance frameworks
11
+ * - Pipeline configuration
12
+ *
13
+ * Usage:
14
+ * tsx scripts/compliance-audit.ts [project-id]
15
+ *
16
+ * Environment Variables:
17
+ * GITLAB_TOKEN - GitLab API token (required)
18
+ * GITLAB_HOST - GitLab instance URL (default: https://gitlab.com)
19
+ */
20
+
21
+ import { Gitlab } from '@gitbeaker/rest';
22
+ import { writeFileSync } from 'fs';
23
+
24
+ // Configuration
25
+ const DEFAULT_PROJECT = 'blueflyio/openstandardagents';
26
+ const GITLAB_HOST = process.env.GITLAB_HOST?.replace('gitlab.com', 'https://gitlab.com') || 'https://gitlab.com';
27
+ const TOKEN = process.env.GITLAB_TOKEN || process.env.SERVICE_ACCOUNT_OSSA_TOKEN || process.env.GITLAB_PUSH_TOKEN;
28
+
29
+ if (!TOKEN) {
30
+ console.error('āŒ Error: GITLAB_TOKEN required');
31
+ console.error(' Set environment variable: export GITLAB_TOKEN=<your-token>');
32
+ process.exit(1);
33
+ }
34
+
35
+ const gitlab = new Gitlab({
36
+ host: GITLAB_HOST.replace(/\/$/, ''),
37
+ token: TOKEN,
38
+ });
39
+
40
+ // Compliance check results interface
41
+ interface ComplianceCheck {
42
+ category: string;
43
+ check: string;
44
+ status: 'pass' | 'fail' | 'warning' | 'info';
45
+ message: string;
46
+ remediation?: string;
47
+ }
48
+
49
+ interface AuditReport {
50
+ projectId: string;
51
+ projectName: string;
52
+ auditDate: string;
53
+ overallStatus: 'compliant' | 'non-compliant' | 'partial';
54
+ checks: ComplianceCheck[];
55
+ summary: {
56
+ total: number;
57
+ passed: number;
58
+ failed: number;
59
+ warnings: number;
60
+ };
61
+ }
62
+
63
+ // Helper functions
64
+ function createCheck(
65
+ category: string,
66
+ check: string,
67
+ status: 'pass' | 'fail' | 'warning' | 'info',
68
+ message: string,
69
+ remediation?: string
70
+ ): ComplianceCheck {
71
+ return { category, check, status, message, remediation };
72
+ }
73
+
74
+ function getStatusIcon(status: string): string {
75
+ switch (status) {
76
+ case 'pass': return 'āœ…';
77
+ case 'fail': return 'āŒ';
78
+ case 'warning': return 'āš ļø';
79
+ case 'info': return 'ā„¹ļø';
80
+ default: return '•';
81
+ }
82
+ }
83
+
84
+ // Audit functions
85
+ async function auditProtectedBranches(projectId: string): Promise<ComplianceCheck[]> {
86
+ const checks: ComplianceCheck[] = [];
87
+ const category = 'Branch Protection';
88
+
89
+ try {
90
+ const protectedBranches = await gitlab.ProtectedBranches.all(projectId);
91
+
92
+ // Check main branch protection
93
+ const mainProtected = protectedBranches.find((pb: any) => pb.name === 'main' || pb.name === 'master');
94
+ if (mainProtected) {
95
+ checks.push(createCheck(
96
+ category,
97
+ 'Main Branch Protected',
98
+ 'pass',
99
+ `Main branch is protected with ${mainProtected.push_access_levels?.length || 0} push rules and ${mainProtected.merge_access_levels?.length || 0} merge rules`
100
+ ));
101
+
102
+ // Check force push is disabled
103
+ if (mainProtected.allow_force_push === false) {
104
+ checks.push(createCheck(
105
+ category,
106
+ 'Force Push Disabled',
107
+ 'pass',
108
+ 'Force push is disabled on main branch'
109
+ ));
110
+ } else {
111
+ checks.push(createCheck(
112
+ category,
113
+ 'Force Push Disabled',
114
+ 'fail',
115
+ 'Force push is enabled on main branch',
116
+ 'Disable force push: Settings > Repository > Protected branches'
117
+ ));
118
+ }
119
+ } else {
120
+ checks.push(createCheck(
121
+ category,
122
+ 'Main Branch Protected',
123
+ 'fail',
124
+ 'Main branch is not protected',
125
+ 'Protect main branch: Settings > Repository > Protected branches'
126
+ ));
127
+ }
128
+
129
+ // Check development branch protection
130
+ const devProtected = protectedBranches.find((pb: any) => pb.name === 'development' || pb.name === 'develop');
131
+ if (devProtected) {
132
+ checks.push(createCheck(
133
+ category,
134
+ 'Development Branch Protected',
135
+ 'pass',
136
+ 'Development branch is protected'
137
+ ));
138
+ } else {
139
+ checks.push(createCheck(
140
+ category,
141
+ 'Development Branch Protected',
142
+ 'warning',
143
+ 'Development branch is not protected',
144
+ 'Consider protecting development branch for better workflow control'
145
+ ));
146
+ }
147
+
148
+ } catch (error: any) {
149
+ checks.push(createCheck(
150
+ category,
151
+ 'Branch Protection Check',
152
+ 'fail',
153
+ `Failed to check branch protection: ${error.message}`
154
+ ));
155
+ }
156
+
157
+ return checks;
158
+ }
159
+
160
+ async function auditMergeRequestApprovals(projectId: string): Promise<ComplianceCheck[]> {
161
+ const checks: ComplianceCheck[] = [];
162
+ const category = 'Merge Request Approvals';
163
+
164
+ try {
165
+ // Get project settings to check approval requirements
166
+ const project = await gitlab.Projects.show(projectId);
167
+
168
+ // Check if approvals are required (available in Free tier via project settings)
169
+ if (project.approvals_before_merge && project.approvals_before_merge > 0) {
170
+ checks.push(createCheck(
171
+ category,
172
+ 'Approvals Required',
173
+ 'pass',
174
+ `${project.approvals_before_merge} approval(s) required before merge`
175
+ ));
176
+ } else {
177
+ checks.push(createCheck(
178
+ category,
179
+ 'Approvals Required',
180
+ 'warning',
181
+ 'No approvals required before merge',
182
+ 'Consider requiring at least 1 approval: Settings > Merge requests > Approvals'
183
+ ));
184
+ }
185
+
186
+ // Try to get advanced approval rules (Premium/Ultimate feature)
187
+ try {
188
+ const rules = await gitlab.ProjectApprovalRules.all(projectId);
189
+ if (rules && rules.length > 0) {
190
+ checks.push(createCheck(
191
+ category,
192
+ 'Approval Rules Defined',
193
+ 'pass',
194
+ `${rules.length} approval rule(s) defined`
195
+ ));
196
+
197
+ // Check for CODEOWNERS integration
198
+ const hasCodeOwners = rules.some((rule: any) => rule.rule_type === 'code_owner');
199
+ if (hasCodeOwners) {
200
+ checks.push(createCheck(
201
+ category,
202
+ 'Code Owner Approvals',
203
+ 'pass',
204
+ 'Code owner approval rules are configured'
205
+ ));
206
+ }
207
+ } else {
208
+ checks.push(createCheck(
209
+ category,
210
+ 'Approval Rules Defined',
211
+ 'info',
212
+ 'No specific approval rules defined',
213
+ 'Advanced approval rules require GitLab Premium/Ultimate'
214
+ ));
215
+ }
216
+ } catch (rulesError: any) {
217
+ // Advanced rules not available or not accessible
218
+ checks.push(createCheck(
219
+ category,
220
+ 'Advanced Approval Rules',
221
+ 'info',
222
+ 'Advanced approval rules not available',
223
+ 'This feature requires GitLab Premium/Ultimate'
224
+ ));
225
+ }
226
+
227
+ // Check merge request settings
228
+ if (project.only_allow_merge_if_pipeline_succeeds) {
229
+ checks.push(createCheck(
230
+ category,
231
+ 'Pipeline Success Required',
232
+ 'pass',
233
+ 'Merge is only allowed if pipeline succeeds'
234
+ ));
235
+ } else {
236
+ checks.push(createCheck(
237
+ category,
238
+ 'Pipeline Success Required',
239
+ 'warning',
240
+ 'Merges are allowed without successful pipeline',
241
+ 'Enable: Settings > Merge requests > Pipelines must succeed'
242
+ ));
243
+ }
244
+
245
+ if (project.only_allow_merge_if_all_discussions_are_resolved) {
246
+ checks.push(createCheck(
247
+ category,
248
+ 'Discussions Must Be Resolved',
249
+ 'pass',
250
+ 'All discussions must be resolved before merge'
251
+ ));
252
+ } else {
253
+ checks.push(createCheck(
254
+ category,
255
+ 'Discussions Must Be Resolved',
256
+ 'info',
257
+ 'Merges are allowed with unresolved discussions',
258
+ 'Enable: Settings > Merge requests > All discussions must be resolved'
259
+ ));
260
+ }
261
+
262
+ } catch (error: any) {
263
+ checks.push(createCheck(
264
+ category,
265
+ 'MR Approvals Check',
266
+ 'warning',
267
+ `Could not check approval settings: ${error.message}`,
268
+ 'This may require GitLab Premium/Ultimate'
269
+ ));
270
+ }
271
+
272
+ return checks;
273
+ }
274
+
275
+ async function auditSecurityScanning(projectId: string): Promise<ComplianceCheck[]> {
276
+ const checks: ComplianceCheck[] = [];
277
+ const category = 'Security Scanning';
278
+
279
+ try {
280
+ // Get CI/CD configuration
281
+ const cicdFile = await gitlab.RepositoryFiles.show(projectId, '.gitlab-ci.yml', 'HEAD');
282
+ const cicdContent = Buffer.from(cicdFile.content, 'base64').toString('utf-8');
283
+
284
+ // Check for SAST
285
+ if (cicdContent.includes('sast') || cicdContent.includes('SAST')) {
286
+ checks.push(createCheck(
287
+ category,
288
+ 'SAST Enabled',
289
+ 'pass',
290
+ 'Static Application Security Testing (SAST) is configured'
291
+ ));
292
+ } else {
293
+ checks.push(createCheck(
294
+ category,
295
+ 'SAST Enabled',
296
+ 'warning',
297
+ 'SAST is not configured',
298
+ 'Add SAST template: include: - template: Security/SAST.gitlab-ci.yml'
299
+ ));
300
+ }
301
+
302
+ // Check for dependency scanning
303
+ if (cicdContent.includes('dependency_scanning') || cicdContent.includes('Dependency-Scanning')) {
304
+ checks.push(createCheck(
305
+ category,
306
+ 'Dependency Scanning Enabled',
307
+ 'pass',
308
+ 'Dependency scanning is configured'
309
+ ));
310
+ } else {
311
+ checks.push(createCheck(
312
+ category,
313
+ 'Dependency Scanning Enabled',
314
+ 'warning',
315
+ 'Dependency scanning is not configured',
316
+ 'Add template: include: - template: Security/Dependency-Scanning.gitlab-ci.yml'
317
+ ));
318
+ }
319
+
320
+ // Check for container scanning
321
+ if (cicdContent.includes('container_scanning') || cicdContent.includes('Container-Scanning')) {
322
+ checks.push(createCheck(
323
+ category,
324
+ 'Container Scanning Enabled',
325
+ 'pass',
326
+ 'Container scanning is configured'
327
+ ));
328
+ } else {
329
+ checks.push(createCheck(
330
+ category,
331
+ 'Container Scanning Enabled',
332
+ 'info',
333
+ 'Container scanning is not configured',
334
+ 'If using containers, add: include: - template: Security/Container-Scanning.gitlab-ci.yml'
335
+ ));
336
+ }
337
+
338
+ // Check for secret detection
339
+ if (cicdContent.includes('secret_detection') || cicdContent.includes('Secret-Detection')) {
340
+ checks.push(createCheck(
341
+ category,
342
+ 'Secret Detection Enabled',
343
+ 'pass',
344
+ 'Secret detection is configured'
345
+ ));
346
+ } else {
347
+ checks.push(createCheck(
348
+ category,
349
+ 'Secret Detection Enabled',
350
+ 'warning',
351
+ 'Secret detection is not configured',
352
+ 'Add template: include: - template: Security/Secret-Detection.gitlab-ci.yml'
353
+ ));
354
+ }
355
+
356
+ } catch (error: any) {
357
+ checks.push(createCheck(
358
+ category,
359
+ 'Security Scanning Check',
360
+ 'fail',
361
+ `Failed to check CI/CD configuration: ${error.message}`
362
+ ));
363
+ }
364
+
365
+ return checks;
366
+ }
367
+
368
+ async function auditDORAMetrics(projectId: string): Promise<ComplianceCheck[]> {
369
+ const checks: ComplianceCheck[] = [];
370
+ const category = 'DORA Metrics';
371
+
372
+ try {
373
+ // Check if DORA metrics are available (GitLab Ultimate feature)
374
+ const project = await gitlab.Projects.show(projectId);
375
+
376
+ // Check for deployment frequency tracking
377
+ const cicdFile = await gitlab.RepositoryFiles.show(projectId, '.gitlab-ci.yml', 'HEAD');
378
+ const cicdContent = Buffer.from(cicdFile.content, 'base64').toString('utf-8');
379
+
380
+ if (cicdContent.includes('environment:') || cicdContent.includes('deployment')) {
381
+ checks.push(createCheck(
382
+ category,
383
+ 'Deployment Tracking Configured',
384
+ 'pass',
385
+ 'Environment deployments are configured for DORA tracking'
386
+ ));
387
+ } else {
388
+ checks.push(createCheck(
389
+ category,
390
+ 'Deployment Tracking Configured',
391
+ 'warning',
392
+ 'No deployment environments configured',
393
+ 'Configure environments in .gitlab-ci.yml to track deployment frequency'
394
+ ));
395
+ }
396
+
397
+ // Check for incident management
398
+ if (project.issues_enabled) {
399
+ checks.push(createCheck(
400
+ category,
401
+ 'Issues Enabled',
402
+ 'pass',
403
+ 'Issues are enabled for incident tracking'
404
+ ));
405
+ } else {
406
+ checks.push(createCheck(
407
+ category,
408
+ 'Issues Enabled',
409
+ 'warning',
410
+ 'Issues are disabled',
411
+ 'Enable issues: Settings > General > Visibility > Issues'
412
+ ));
413
+ }
414
+
415
+ // Check for merge request metrics
416
+ if (project.merge_requests_enabled) {
417
+ checks.push(createCheck(
418
+ category,
419
+ 'Merge Requests Enabled',
420
+ 'pass',
421
+ 'Merge requests enabled for lead time tracking'
422
+ ));
423
+ } else {
424
+ checks.push(createCheck(
425
+ category,
426
+ 'Merge Requests Enabled',
427
+ 'fail',
428
+ 'Merge requests are disabled',
429
+ 'Enable merge requests: Settings > General > Visibility > Merge requests'
430
+ ));
431
+ }
432
+
433
+ checks.push(createCheck(
434
+ category,
435
+ 'DORA Metrics Dashboard',
436
+ 'info',
437
+ 'View DORA metrics: Analytics > CI/CD Analytics',
438
+ 'DORA metrics require GitLab Ultimate'
439
+ ));
440
+
441
+ } catch (error: any) {
442
+ checks.push(createCheck(
443
+ category,
444
+ 'DORA Metrics Check',
445
+ 'info',
446
+ `Could not fully verify DORA metrics setup: ${error.message}`,
447
+ 'DORA metrics are a GitLab Ultimate feature'
448
+ ));
449
+ }
450
+
451
+ return checks;
452
+ }
453
+
454
+ async function auditSecurityPolicies(projectId: string): Promise<ComplianceCheck[]> {
455
+ const checks: ComplianceCheck[] = [];
456
+ const category = 'Security Policies';
457
+
458
+ try {
459
+ const project = await gitlab.Projects.show(projectId);
460
+
461
+ // Check for security policies project (modern approach)
462
+ // Security policies are stored in a separate project linked via .gitlab/security-policies/policy.yml
463
+ try {
464
+ const policyFile = await gitlab.RepositoryFiles.show(projectId, '.gitlab/security-policies/policy.yml', 'HEAD');
465
+ const policyContent = Buffer.from(policyFile.content, 'base64').toString('utf-8');
466
+
467
+ // Check for scan execution policies
468
+ if (policyContent.includes('scan_execution_policy:')) {
469
+ checks.push(createCheck(
470
+ category,
471
+ 'Scan Execution Policies',
472
+ 'pass',
473
+ 'Scan execution policies are defined to enforce security scans'
474
+ ));
475
+
476
+ // Check specific scan types
477
+ if (policyContent.includes('scan: sast')) {
478
+ checks.push(createCheck(category, 'SAST Policy', 'pass', 'SAST enforcement policy configured'));
479
+ }
480
+ if (policyContent.includes('scan: secret_detection')) {
481
+ checks.push(createCheck(category, 'Secret Detection Policy', 'pass', 'Secret detection enforcement policy configured'));
482
+ }
483
+ if (policyContent.includes('scan: dependency_scanning')) {
484
+ checks.push(createCheck(category, 'Dependency Scanning Policy', 'pass', 'Dependency scanning enforcement policy configured'));
485
+ }
486
+ if (policyContent.includes('scan: container_scanning')) {
487
+ checks.push(createCheck(category, 'Container Scanning Policy', 'pass', 'Container scanning enforcement policy configured'));
488
+ }
489
+ } else {
490
+ checks.push(createCheck(
491
+ category,
492
+ 'Scan Execution Policies',
493
+ 'warning',
494
+ 'No scan execution policies found',
495
+ 'Add scan_execution_policy to enforce security scans: Secure > Policies > New policy'
496
+ ));
497
+ }
498
+
499
+ // Check for scan result policies (approval gates)
500
+ if (policyContent.includes('scan_result_policy:')) {
501
+ checks.push(createCheck(
502
+ category,
503
+ 'Scan Result Policies',
504
+ 'pass',
505
+ 'Scan result policies are defined to block vulnerable code'
506
+ ));
507
+
508
+ if (policyContent.includes('vulnerabilities_allowed: 0')) {
509
+ checks.push(createCheck(
510
+ category,
511
+ 'Zero Vulnerability Policy',
512
+ 'pass',
513
+ 'Zero-tolerance vulnerability policy configured for critical findings'
514
+ ));
515
+ }
516
+ } else {
517
+ checks.push(createCheck(
518
+ category,
519
+ 'Scan Result Policies',
520
+ 'warning',
521
+ 'No scan result policies found',
522
+ 'Add scan_result_policy to require approvals for vulnerabilities'
523
+ ));
524
+ }
525
+
526
+ // Check for pipeline execution policies
527
+ if (policyContent.includes('pipeline_execution_policy:')) {
528
+ checks.push(createCheck(
529
+ category,
530
+ 'Pipeline Execution Policies',
531
+ 'pass',
532
+ 'Pipeline execution policies inject compliance jobs'
533
+ ));
534
+ }
535
+
536
+ } catch {
537
+ // Check if linked to group security policies project
538
+ checks.push(createCheck(
539
+ category,
540
+ 'Security Policies',
541
+ 'info',
542
+ 'No local security policies found - may be inherited from group',
543
+ 'Configure policies: Secure > Policies or link to group security-policies project'
544
+ ));
545
+ }
546
+
547
+ // Check for merge trains (still relevant)
548
+ if (project.merge_trains_enabled) {
549
+ checks.push(createCheck(
550
+ category,
551
+ 'Merge Trains Enabled',
552
+ 'pass',
553
+ 'Merge trains are enabled for better pipeline efficiency'
554
+ ));
555
+ } else {
556
+ checks.push(createCheck(
557
+ category,
558
+ 'Merge Trains Enabled',
559
+ 'info',
560
+ 'Merge trains are not enabled',
561
+ 'Enable: Settings > Merge requests > Merge trains (Premium/Ultimate)'
562
+ ));
563
+ }
564
+
565
+ // Check for code owners
566
+ try {
567
+ await gitlab.RepositoryFiles.show(projectId, 'CODEOWNERS', 'HEAD');
568
+ checks.push(createCheck(
569
+ category,
570
+ 'Code Owners Defined',
571
+ 'pass',
572
+ 'CODEOWNERS file exists for code review requirements'
573
+ ));
574
+ } catch {
575
+ checks.push(createCheck(
576
+ category,
577
+ 'Code Owners Defined',
578
+ 'info',
579
+ 'No CODEOWNERS file found',
580
+ 'Create CODEOWNERS file to define code ownership'
581
+ ));
582
+ }
583
+
584
+ } catch (error: any) {
585
+ checks.push(createCheck(
586
+ category,
587
+ 'Security Policies Check',
588
+ 'info',
589
+ `Could not check security policies: ${error.message}`,
590
+ 'Security policies require GitLab Ultimate'
591
+ ));
592
+ }
593
+
594
+ return checks;
595
+ }
596
+
597
+ async function auditPipelineConfiguration(projectId: string): Promise<ComplianceCheck[]> {
598
+ const checks: ComplianceCheck[] = [];
599
+ const category = 'Pipeline Configuration';
600
+
601
+ try {
602
+ // Check for CI/CD configuration
603
+ const cicdFile = await gitlab.RepositoryFiles.show(projectId, '.gitlab-ci.yml', 'HEAD');
604
+ checks.push(createCheck(
605
+ category,
606
+ 'CI/CD Configuration',
607
+ 'pass',
608
+ '.gitlab-ci.yml file exists and is configured'
609
+ ));
610
+
611
+ // Get recent pipelines
612
+ const pipelines = await gitlab.Pipelines.all(projectId, { per_page: 10 });
613
+ if (pipelines && pipelines.length > 0) {
614
+ const successCount = pipelines.filter((p: any) => p.status === 'success').length;
615
+ const failCount = pipelines.filter((p: any) => p.status === 'failed').length;
616
+
617
+ checks.push(createCheck(
618
+ category,
619
+ 'Pipeline Activity',
620
+ 'pass',
621
+ `Recent pipelines: ${successCount} passed, ${failCount} failed out of last ${pipelines.length}`
622
+ ));
623
+
624
+ const successRate = (successCount / pipelines.length) * 100;
625
+ if (successRate >= 80) {
626
+ checks.push(createCheck(
627
+ category,
628
+ 'Pipeline Success Rate',
629
+ 'pass',
630
+ `${successRate.toFixed(1)}% success rate`
631
+ ));
632
+ } else if (successRate >= 60) {
633
+ checks.push(createCheck(
634
+ category,
635
+ 'Pipeline Success Rate',
636
+ 'warning',
637
+ `${successRate.toFixed(1)}% success rate - consider improving pipeline stability`
638
+ ));
639
+ } else {
640
+ checks.push(createCheck(
641
+ category,
642
+ 'Pipeline Success Rate',
643
+ 'fail',
644
+ `${successRate.toFixed(1)}% success rate - pipeline needs attention`,
645
+ 'Review failing jobs and fix underlying issues'
646
+ ));
647
+ }
648
+ } else {
649
+ checks.push(createCheck(
650
+ category,
651
+ 'Pipeline Activity',
652
+ 'warning',
653
+ 'No recent pipeline runs found',
654
+ 'Ensure pipelines are running on commits'
655
+ ));
656
+ }
657
+
658
+ } catch (error: any) {
659
+ checks.push(createCheck(
660
+ category,
661
+ 'Pipeline Configuration Check',
662
+ 'fail',
663
+ `Failed to check pipeline configuration: ${error.message}`
664
+ ));
665
+ }
666
+
667
+ return checks;
668
+ }
669
+
670
+ // Main audit function
671
+ async function runAudit(projectId: string): Promise<AuditReport> {
672
+ console.log(`\nšŸ” Running GitLab Ultimate Compliance Audit\n`);
673
+ console.log(`Project: ${projectId}`);
674
+ console.log(`GitLab Host: ${GITLAB_HOST}`);
675
+ console.log(`Token: ${TOKEN.substring(0, 10)}...\n`);
676
+
677
+ // Get project info
678
+ const project = await gitlab.Projects.show(projectId);
679
+ console.log(`Project Name: ${project.name}`);
680
+ console.log(`Project Path: ${project.path_with_namespace}\n`);
681
+
682
+ const checks: ComplianceCheck[] = [];
683
+
684
+ // Run all audit checks
685
+ console.log('Running audit checks...\n');
686
+
687
+ checks.push(...await auditProtectedBranches(projectId));
688
+ checks.push(...await auditMergeRequestApprovals(projectId));
689
+ checks.push(...await auditSecurityScanning(projectId));
690
+ checks.push(...await auditDORAMetrics(projectId));
691
+ checks.push(...await auditSecurityPolicies(projectId));
692
+ checks.push(...await auditPipelineConfiguration(projectId));
693
+
694
+ // Calculate summary
695
+ const summary = {
696
+ total: checks.length,
697
+ passed: checks.filter(c => c.status === 'pass').length,
698
+ failed: checks.filter(c => c.status === 'fail').length,
699
+ warnings: checks.filter(c => c.status === 'warning').length,
700
+ };
701
+
702
+ // Determine overall status
703
+ let overallStatus: 'compliant' | 'non-compliant' | 'partial';
704
+ if (summary.failed === 0 && summary.warnings === 0) {
705
+ overallStatus = 'compliant';
706
+ } else if (summary.failed > 0) {
707
+ overallStatus = 'non-compliant';
708
+ } else {
709
+ overallStatus = 'partial';
710
+ }
711
+
712
+ return {
713
+ projectId: project.path_with_namespace,
714
+ projectName: project.name,
715
+ auditDate: new Date().toISOString(),
716
+ overallStatus,
717
+ checks,
718
+ summary,
719
+ };
720
+ }
721
+
722
+ // Display functions
723
+ function displayReport(report: AuditReport): void {
724
+ console.log('\n' + '='.repeat(80));
725
+ console.log('COMPLIANCE AUDIT REPORT');
726
+ console.log('='.repeat(80));
727
+ console.log(`Project: ${report.projectName} (${report.projectId})`);
728
+ console.log(`Date: ${new Date(report.auditDate).toLocaleString()}`);
729
+ console.log(`Overall Status: ${report.overallStatus.toUpperCase()}`);
730
+ console.log('='.repeat(80));
731
+
732
+ // Group checks by category
733
+ const categories = [...new Set(report.checks.map(c => c.category))];
734
+
735
+ for (const category of categories) {
736
+ console.log(`\nšŸ“‹ ${category}`);
737
+ console.log('-'.repeat(80));
738
+
739
+ const categoryChecks = report.checks.filter(c => c.category === category);
740
+ for (const check of categoryChecks) {
741
+ console.log(`${getStatusIcon(check.status)} ${check.check}`);
742
+ console.log(` ${check.message}`);
743
+ if (check.remediation) {
744
+ console.log(` šŸ’” ${check.remediation}`);
745
+ }
746
+ }
747
+ }
748
+
749
+ console.log('\n' + '='.repeat(80));
750
+ console.log('SUMMARY');
751
+ console.log('='.repeat(80));
752
+ console.log(`Total Checks: ${report.summary.total}`);
753
+ console.log(`āœ… Passed: ${report.summary.passed}`);
754
+ console.log(`āŒ Failed: ${report.summary.failed}`);
755
+ console.log(`āš ļø Warnings: ${report.summary.warnings}`);
756
+ console.log(`ā„¹ļø Info: ${report.summary.total - report.summary.passed - report.summary.failed - report.summary.warnings}`);
757
+ console.log('='.repeat(80) + '\n');
758
+ }
759
+
760
+ function saveReport(report: AuditReport, filename: string): void {
761
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
762
+ const outputFile = filename || `compliance-audit-${report.projectId.replace('/', '-')}-${timestamp}.json`;
763
+
764
+ writeFileSync(outputFile, JSON.stringify(report, null, 2));
765
+ console.log(`\nšŸ“„ Report saved to: ${outputFile}`);
766
+ }
767
+
768
+ // Main execution
769
+ async function main() {
770
+ const projectId = process.argv[2] || DEFAULT_PROJECT;
771
+ const outputFile = process.argv[3];
772
+
773
+ try {
774
+ const report = await runAudit(projectId);
775
+ displayReport(report);
776
+ saveReport(report, outputFile);
777
+
778
+ // Exit with appropriate code
779
+ if (report.overallStatus === 'non-compliant') {
780
+ console.log('āš ļø Project has compliance issues that need attention\n');
781
+ process.exit(1);
782
+ } else if (report.overallStatus === 'partial') {
783
+ console.log('āš ļø Project is partially compliant - review warnings\n');
784
+ process.exit(0);
785
+ } else {
786
+ console.log('āœ… Project is fully compliant!\n');
787
+ process.exit(0);
788
+ }
789
+ } catch (error: any) {
790
+ console.error('\nāŒ Audit failed:', error.message);
791
+ console.error(error.stack);
792
+ process.exit(1);
793
+ }
794
+ }
795
+
796
+ main();