javi-forge 1.6.0 → 1.7.0

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 (322) hide show
  1. package/README.md +45 -1
  2. package/ci-local/README.md +56 -11
  3. package/ci-local/ci-local.ps1 +445 -0
  4. package/ci-local/ci-local.sh +52 -9
  5. package/ci-local/docker/python.Dockerfile +7 -0
  6. package/ci-local/hooks/commit-msg +120 -44
  7. package/ci-local/hooks/commit-msg.test.sh +286 -0
  8. package/ci-local/hooks/pre-commit +9 -1
  9. package/ci-local/hooks/pre-push +13 -2
  10. package/ci-local/install.ps1 +367 -0
  11. package/ci-local/install.sh +151 -5
  12. package/ci-local/uninstall.ps1 +85 -0
  13. package/ci-local/uninstall.sh +95 -0
  14. package/dist/cli/dispatch/ci.d.ts +12 -0
  15. package/dist/cli/dispatch/ci.js +38 -0
  16. package/dist/cli/dispatch/security.d.ts +11 -0
  17. package/dist/cli/dispatch/security.js +68 -0
  18. package/dist/cli/dispatch/simple-renderers.d.ts +16 -0
  19. package/dist/cli/dispatch/simple-renderers.js +65 -0
  20. package/dist/cli/dispatch/skill-publish.d.ts +15 -0
  21. package/dist/cli/dispatch/skill-publish.js +44 -0
  22. package/dist/cli/dispatch/skills-cmd.d.ts +13 -0
  23. package/dist/cli/dispatch/skills-cmd.js +93 -0
  24. package/dist/cli/dispatch/tdd.d.ts +11 -0
  25. package/dist/cli/dispatch/tdd.js +45 -0
  26. package/dist/cli/dispatch/types.d.ts +29 -0
  27. package/dist/cli/dispatch/types.js +2 -0
  28. package/dist/cli/dispatch/workflow.d.ts +11 -0
  29. package/dist/cli/dispatch/workflow.js +67 -0
  30. package/dist/cli/help.d.ts +127 -0
  31. package/dist/cli/help.js +138 -0
  32. package/dist/cli/runtime.d.ts +16 -0
  33. package/dist/cli/runtime.js +38 -0
  34. package/dist/cli/validators.d.ts +17 -0
  35. package/dist/cli/validators.js +35 -0
  36. package/dist/commands/analyze.d.ts +1 -1
  37. package/dist/commands/analyze.js +14 -16
  38. package/dist/commands/ci.d.ts +50 -3
  39. package/dist/commands/ci.js +537 -182
  40. package/dist/commands/doctor.d.ts +2 -2
  41. package/dist/commands/doctor.js +112 -62
  42. package/dist/commands/init/report.d.ts +13 -0
  43. package/dist/commands/init/report.js +13 -0
  44. package/dist/commands/init/steps/agent-skills.d.ts +13 -0
  45. package/dist/commands/init/steps/agent-skills.js +45 -0
  46. package/dist/commands/init/steps/ai-sync.d.ts +15 -0
  47. package/dist/commands/init/steps/ai-sync.js +62 -0
  48. package/dist/commands/init/steps/ci.d.ts +24 -0
  49. package/dist/commands/init/steps/ci.js +74 -0
  50. package/dist/commands/init/steps/claude-md.d.ts +14 -0
  51. package/dist/commands/init/steps/claude-md.js +54 -0
  52. package/dist/commands/init/steps/code-graph.d.ts +16 -0
  53. package/dist/commands/init/steps/code-graph.js +68 -0
  54. package/dist/commands/init/steps/context-dir.d.ts +13 -0
  55. package/dist/commands/init/steps/context-dir.js +47 -0
  56. package/dist/commands/init/steps/docker-deploy.d.ts +16 -0
  57. package/dist/commands/init/steps/docker-deploy.js +60 -0
  58. package/dist/commands/init/steps/ghagga.d.ts +15 -0
  59. package/dist/commands/init/steps/ghagga.js +58 -0
  60. package/dist/commands/init/steps/git.d.ts +25 -0
  61. package/dist/commands/init/steps/git.js +81 -0
  62. package/dist/commands/init/steps/gitignore.d.ts +13 -0
  63. package/dist/commands/init/steps/gitignore.js +39 -0
  64. package/dist/commands/init/steps/local-ai.d.ts +14 -0
  65. package/dist/commands/init/steps/local-ai.js +53 -0
  66. package/dist/commands/init/steps/manifest.d.ts +13 -0
  67. package/dist/commands/init/steps/manifest.js +58 -0
  68. package/dist/commands/init/steps/memory.d.ts +15 -0
  69. package/dist/commands/init/steps/memory.js +60 -0
  70. package/dist/commands/init/steps/mock.d.ts +13 -0
  71. package/dist/commands/init/steps/mock.js +64 -0
  72. package/dist/commands/init/steps/sdd.d.ts +12 -0
  73. package/dist/commands/init/steps/sdd.js +40 -0
  74. package/dist/commands/init/steps/security.d.ts +28 -0
  75. package/dist/commands/init/steps/security.js +103 -0
  76. package/dist/commands/init/types.d.ts +30 -0
  77. package/dist/commands/init/types.js +2 -0
  78. package/dist/commands/init.d.ts +5 -4
  79. package/dist/commands/init.js +42 -387
  80. package/dist/commands/llmstxt.d.ts +1 -1
  81. package/dist/commands/llmstxt.js +36 -34
  82. package/dist/commands/plugin.d.ts +10 -1
  83. package/dist/commands/plugin.js +92 -47
  84. package/dist/commands/security.d.ts +11 -5
  85. package/dist/commands/security.js +236 -77
  86. package/dist/commands/skills/analysis.d.ts +19 -0
  87. package/dist/commands/skills/analysis.js +211 -0
  88. package/dist/commands/skills/benchmark.d.ts +6 -0
  89. package/dist/commands/skills/benchmark.js +82 -0
  90. package/dist/commands/skills/constants.d.ts +10 -0
  91. package/dist/commands/skills/constants.js +27 -0
  92. package/dist/commands/skills/directives.d.ts +21 -0
  93. package/dist/commands/skills/directives.js +101 -0
  94. package/dist/commands/skills/parsing.d.ts +16 -0
  95. package/dist/commands/skills/parsing.js +108 -0
  96. package/dist/commands/skills/rules.d.ts +5 -0
  97. package/dist/commands/skills/rules.js +32 -0
  98. package/dist/commands/skills/scoring.d.ts +47 -0
  99. package/dist/commands/skills/scoring.js +306 -0
  100. package/dist/commands/skills.d.ts +8 -69
  101. package/dist/commands/skills.js +9 -490
  102. package/dist/commands/tdd-pipeline.d.ts +17 -0
  103. package/dist/commands/tdd-pipeline.js +144 -0
  104. package/dist/commands/tdd.d.ts +1 -1
  105. package/dist/commands/tdd.js +21 -18
  106. package/dist/commands/workflow.d.ts +23 -0
  107. package/dist/commands/workflow.js +116 -0
  108. package/dist/constants.d.ts +15 -1
  109. package/dist/constants.js +161 -122
  110. package/dist/index.js +43 -280
  111. package/dist/lib/agent-skills.d.ts +36 -1
  112. package/dist/lib/agent-skills.js +168 -19
  113. package/dist/lib/atlassian-mcp.d.ts +42 -0
  114. package/dist/lib/atlassian-mcp.js +98 -0
  115. package/dist/lib/auto-skill-install.d.ts +37 -0
  116. package/dist/lib/auto-skill-install.js +91 -0
  117. package/dist/lib/auto-wire.d.ts +20 -0
  118. package/dist/lib/auto-wire.js +240 -0
  119. package/dist/lib/ci-config.d.ts +65 -0
  120. package/dist/lib/ci-config.js +297 -0
  121. package/dist/lib/claudemd.d.ts +13 -1
  122. package/dist/lib/claudemd.js +174 -24
  123. package/dist/lib/codex-export.d.ts +1 -1
  124. package/dist/lib/codex-export.js +26 -32
  125. package/dist/lib/common.d.ts +1 -1
  126. package/dist/lib/common.js +52 -44
  127. package/dist/lib/context.d.ts +17 -2
  128. package/dist/lib/context.js +142 -13
  129. package/dist/lib/crash-recovery.d.ts +34 -0
  130. package/dist/lib/crash-recovery.js +123 -0
  131. package/dist/lib/docker.d.ts +23 -2
  132. package/dist/lib/docker.js +168 -131
  133. package/dist/lib/exec.d.ts +10 -0
  134. package/dist/lib/exec.js +11 -0
  135. package/dist/lib/frontmatter.d.ts +1 -1
  136. package/dist/lib/frontmatter.js +29 -15
  137. package/dist/lib/harness-audit.d.ts +35 -0
  138. package/dist/lib/harness-audit.js +277 -0
  139. package/dist/lib/parallel-batch.d.ts +42 -0
  140. package/dist/lib/parallel-batch.js +88 -0
  141. package/dist/lib/plugin.d.ts +9 -3
  142. package/dist/lib/plugin.js +127 -70
  143. package/dist/lib/secret-scanner.d.ts +30 -0
  144. package/dist/lib/secret-scanner.js +274 -0
  145. package/dist/lib/security-analysis.d.ts +74 -0
  146. package/dist/lib/security-analysis.js +489 -0
  147. package/dist/lib/skill-publish.d.ts +40 -0
  148. package/dist/lib/skill-publish.js +164 -0
  149. package/dist/lib/skill-scanner.d.ts +63 -0
  150. package/dist/lib/skill-scanner.js +383 -0
  151. package/dist/lib/stack-detector.d.ts +38 -0
  152. package/dist/lib/stack-detector.js +207 -0
  153. package/dist/lib/supply-chain.d.ts +23 -0
  154. package/dist/lib/supply-chain.js +126 -0
  155. package/dist/lib/team-presets.d.ts +53 -0
  156. package/dist/lib/team-presets.js +201 -0
  157. package/dist/lib/template.d.ts +16 -1
  158. package/dist/lib/template.js +46 -17
  159. package/dist/lib/workflow/discovery.d.ts +27 -0
  160. package/dist/lib/workflow/discovery.js +85 -0
  161. package/dist/lib/workflow/index.d.ts +5 -0
  162. package/dist/lib/workflow/index.js +5 -0
  163. package/dist/lib/workflow/parser.d.ts +16 -0
  164. package/dist/lib/workflow/parser.js +198 -0
  165. package/dist/lib/workflow/renderer.d.ts +9 -0
  166. package/dist/lib/workflow/renderer.js +152 -0
  167. package/dist/lib/workflow/validator.d.ts +10 -0
  168. package/dist/lib/workflow/validator.js +189 -0
  169. package/dist/tasks/index.d.ts +4 -0
  170. package/dist/tasks/index.js +4 -0
  171. package/dist/tasks/scaffold-tasks.d.ts +3 -0
  172. package/dist/tasks/scaffold-tasks.js +14 -0
  173. package/dist/tasks/task-id.d.ts +30 -0
  174. package/dist/tasks/task-id.js +59 -0
  175. package/dist/tasks/task-tracker.d.ts +15 -0
  176. package/dist/tasks/task-tracker.js +81 -0
  177. package/dist/types/index.d.ts +134 -6
  178. package/dist/types/index.js +11 -1
  179. package/dist/ui/AnalyzeUI.d.ts +1 -1
  180. package/dist/ui/AnalyzeUI.js +38 -39
  181. package/dist/ui/App.d.ts +5 -3
  182. package/dist/ui/App.js +86 -46
  183. package/dist/ui/AutoSkills.d.ts +9 -0
  184. package/dist/ui/AutoSkills.js +124 -0
  185. package/dist/ui/CI.d.ts +2 -2
  186. package/dist/ui/CI.js +24 -26
  187. package/dist/ui/CIContext.d.ts +1 -1
  188. package/dist/ui/CIContext.js +2 -2
  189. package/dist/ui/CISelector.d.ts +2 -2
  190. package/dist/ui/CISelector.js +23 -15
  191. package/dist/ui/Doctor.d.ts +1 -1
  192. package/dist/ui/Doctor.js +35 -29
  193. package/dist/ui/Header.d.ts +1 -1
  194. package/dist/ui/Header.js +14 -14
  195. package/dist/ui/HookProfileSelector.d.ts +9 -0
  196. package/dist/ui/HookProfileSelector.js +54 -0
  197. package/dist/ui/LlmsTxt.d.ts +1 -1
  198. package/dist/ui/LlmsTxt.js +31 -22
  199. package/dist/ui/MemorySelector.d.ts +2 -2
  200. package/dist/ui/MemorySelector.js +28 -16
  201. package/dist/ui/NameInput.d.ts +1 -1
  202. package/dist/ui/NameInput.js +21 -21
  203. package/dist/ui/OptionSelector.d.ts +6 -2
  204. package/dist/ui/OptionSelector.js +83 -32
  205. package/dist/ui/Plugin.d.ts +4 -3
  206. package/dist/ui/Plugin.js +78 -35
  207. package/dist/ui/Progress.d.ts +3 -3
  208. package/dist/ui/Progress.js +23 -22
  209. package/dist/ui/Skills.d.ts +2 -2
  210. package/dist/ui/Skills.js +61 -32
  211. package/dist/ui/StackSelector.d.ts +2 -2
  212. package/dist/ui/StackSelector.js +27 -17
  213. package/dist/ui/Summary.d.ts +3 -3
  214. package/dist/ui/Summary.js +60 -50
  215. package/dist/ui/Welcome.d.ts +1 -1
  216. package/dist/ui/Welcome.js +15 -16
  217. package/dist/ui/theme.d.ts +1 -1
  218. package/dist/ui/theme.js +6 -6
  219. package/lib/common.psm1 +167 -0
  220. package/lib/common.sh +30 -0
  221. package/package.json +46 -24
  222. package/templates/common/atlassian/mcp-atlassian-snippet.json +16 -0
  223. package/templates/common/repoforge/mcp-repoforge-snippet.json +11 -0
  224. package/templates/common/repoforge/repoforge.yaml +34 -0
  225. package/templates/github/deploy-docker-zero-downtime.yml +140 -0
  226. package/templates/github/repoforge-graph.yml +45 -0
  227. package/templates/gitlab/deploy-docker-zero-downtime.yml +57 -0
  228. package/templates/local-ai/.env.example +17 -0
  229. package/templates/local-ai/docker-compose.yml +95 -0
  230. package/templates/security-hooks/claude-settings-security.json +30 -0
  231. package/templates/security-hooks/commit-msg-signing +29 -0
  232. package/templates/security-hooks/pre-commit-permissions +74 -0
  233. package/templates/security-hooks/pre-commit-secrets +74 -0
  234. package/templates/security-hooks/pre-push-branch-protection +62 -0
  235. package/templates/security-hooks/pre-push-deps +83 -0
  236. package/templates/security-hooks/pre-push-signing +67 -0
  237. package/templates/woodpecker/deploy-docker-zero-downtime.yml +50 -0
  238. package/templates/workflows/ci-pipeline.dot +15 -0
  239. package/templates/workflows/feature-flow.dot +21 -0
  240. package/templates/workflows/release.dot +16 -0
  241. package/dist/__integration__/helpers.d.ts +0 -20
  242. package/dist/__integration__/helpers.d.ts.map +0 -1
  243. package/dist/__integration__/helpers.js +0 -31
  244. package/dist/__integration__/helpers.js.map +0 -1
  245. package/dist/commands/analyze.d.ts.map +0 -1
  246. package/dist/commands/analyze.js.map +0 -1
  247. package/dist/commands/ci.d.ts.map +0 -1
  248. package/dist/commands/ci.js.map +0 -1
  249. package/dist/commands/doctor.d.ts.map +0 -1
  250. package/dist/commands/doctor.js.map +0 -1
  251. package/dist/commands/init.d.ts.map +0 -1
  252. package/dist/commands/init.js.map +0 -1
  253. package/dist/commands/llmstxt.d.ts.map +0 -1
  254. package/dist/commands/llmstxt.js.map +0 -1
  255. package/dist/commands/plugin.d.ts.map +0 -1
  256. package/dist/commands/plugin.js.map +0 -1
  257. package/dist/commands/security.d.ts.map +0 -1
  258. package/dist/commands/security.js.map +0 -1
  259. package/dist/commands/skills.d.ts.map +0 -1
  260. package/dist/commands/skills.js.map +0 -1
  261. package/dist/commands/tdd.d.ts.map +0 -1
  262. package/dist/commands/tdd.js.map +0 -1
  263. package/dist/constants.d.ts.map +0 -1
  264. package/dist/constants.js.map +0 -1
  265. package/dist/index.d.ts.map +0 -1
  266. package/dist/index.js.map +0 -1
  267. package/dist/lib/agent-skills.d.ts.map +0 -1
  268. package/dist/lib/agent-skills.js.map +0 -1
  269. package/dist/lib/claudemd.d.ts.map +0 -1
  270. package/dist/lib/claudemd.js.map +0 -1
  271. package/dist/lib/codex-export.d.ts.map +0 -1
  272. package/dist/lib/codex-export.js.map +0 -1
  273. package/dist/lib/common.d.ts.map +0 -1
  274. package/dist/lib/common.js.map +0 -1
  275. package/dist/lib/context.d.ts.map +0 -1
  276. package/dist/lib/context.js.map +0 -1
  277. package/dist/lib/docker.d.ts.map +0 -1
  278. package/dist/lib/docker.js.map +0 -1
  279. package/dist/lib/frontmatter.d.ts.map +0 -1
  280. package/dist/lib/frontmatter.js.map +0 -1
  281. package/dist/lib/plugin.d.ts.map +0 -1
  282. package/dist/lib/plugin.js.map +0 -1
  283. package/dist/lib/template.d.ts.map +0 -1
  284. package/dist/lib/template.js.map +0 -1
  285. package/dist/types/index.d.ts.map +0 -1
  286. package/dist/types/index.js.map +0 -1
  287. package/dist/ui/AnalyzeUI.d.ts.map +0 -1
  288. package/dist/ui/AnalyzeUI.js.map +0 -1
  289. package/dist/ui/App.d.ts.map +0 -1
  290. package/dist/ui/App.js.map +0 -1
  291. package/dist/ui/CI.d.ts.map +0 -1
  292. package/dist/ui/CI.js.map +0 -1
  293. package/dist/ui/CIContext.d.ts.map +0 -1
  294. package/dist/ui/CIContext.js.map +0 -1
  295. package/dist/ui/CISelector.d.ts.map +0 -1
  296. package/dist/ui/CISelector.js.map +0 -1
  297. package/dist/ui/Doctor.d.ts.map +0 -1
  298. package/dist/ui/Doctor.js.map +0 -1
  299. package/dist/ui/Header.d.ts.map +0 -1
  300. package/dist/ui/Header.js.map +0 -1
  301. package/dist/ui/LlmsTxt.d.ts.map +0 -1
  302. package/dist/ui/LlmsTxt.js.map +0 -1
  303. package/dist/ui/MemorySelector.d.ts.map +0 -1
  304. package/dist/ui/MemorySelector.js.map +0 -1
  305. package/dist/ui/NameInput.d.ts.map +0 -1
  306. package/dist/ui/NameInput.js.map +0 -1
  307. package/dist/ui/OptionSelector.d.ts.map +0 -1
  308. package/dist/ui/OptionSelector.js.map +0 -1
  309. package/dist/ui/Plugin.d.ts.map +0 -1
  310. package/dist/ui/Plugin.js.map +0 -1
  311. package/dist/ui/Progress.d.ts.map +0 -1
  312. package/dist/ui/Progress.js.map +0 -1
  313. package/dist/ui/Skills.d.ts.map +0 -1
  314. package/dist/ui/Skills.js.map +0 -1
  315. package/dist/ui/StackSelector.d.ts.map +0 -1
  316. package/dist/ui/StackSelector.js.map +0 -1
  317. package/dist/ui/Summary.d.ts.map +0 -1
  318. package/dist/ui/Summary.js.map +0 -1
  319. package/dist/ui/Welcome.d.ts.map +0 -1
  320. package/dist/ui/Welcome.js.map +0 -1
  321. package/dist/ui/theme.d.ts.map +0 -1
  322. package/dist/ui/theme.js.map +0 -1
package/package.json CHANGED
@@ -1,72 +1,94 @@
1
1
  {
2
2
  "name": "javi-forge",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Project scaffolding and AI-ready CI bootstrap",
5
5
  "type": "module",
6
6
  "bin": {
7
- "javi-forge": "./dist/index.js"
7
+ "javi-forge": "dist/index.js"
8
8
  },
9
9
  "scripts": {
10
10
  "build": "tsc",
11
11
  "dev": "tsx watch src/index.tsx",
12
12
  "start": "node dist/index.js",
13
13
  "typecheck": "tsc --noEmit",
14
+ "typecheck:test": "tsc --project tsconfig.test.json --noEmit",
14
15
  "test": "vitest run",
15
16
  "test:watch": "vitest",
16
17
  "test:coverage": "vitest run --coverage",
17
- "test:mutation": "stryker run"
18
+ "test:mutation": "stryker run",
19
+ "test:hooks": "bash ci-local/hooks/commit-msg.test.sh",
20
+ "lint": "biome check src/",
21
+ "lint:fix": "biome check --write src/",
22
+ "format": "biome format --write src/",
23
+ "validate": "pnpm typecheck && pnpm typecheck:test && pnpm lint && pnpm test",
24
+ "package:check": "npm pack --dry-run --json --cache /tmp/javi-forge-npm-pack-cache > /tmp/javi-forge-pack.json && node scripts/verify-package-contents.mjs /tmp/javi-forge-pack.json"
18
25
  },
19
26
  "files": [
20
- "dist/",
27
+ "dist/**/*.js",
28
+ "dist/**/*.d.ts",
29
+ "!dist/__integration__/**",
30
+ "!dist/e2e/**",
21
31
  "ci-local/",
22
32
  "modules/",
23
33
  "templates/",
24
34
  "workflows/",
25
35
  "lib/",
26
- "!dist/**/*.test.*",
27
- "!dist/**/__snapshots__",
28
- "package.json",
29
36
  "README.md",
37
+ "LICENSE*",
30
38
  ".gitignore.template"
31
39
  ],
32
40
  "author": "JNZader",
33
41
  "license": "MIT",
34
42
  "repository": {
35
43
  "type": "git",
36
- "url": "https://github.com/JNZader/javi-forge"
44
+ "url": "git+https://github.com/JNZader/javi-forge.git"
37
45
  },
38
46
  "engines": {
39
- "node": ">=18"
47
+ "node": ">=22"
40
48
  },
41
49
  "dependencies": {
42
50
  "chalk": "^5.6.2",
43
- "fs-extra": "^11.3.4",
51
+ "fs-extra": "^11.3.5",
44
52
  "glob": "^13.0.6",
45
- "ink": "^6.8.0",
53
+ "ink": "^7.0.3",
46
54
  "ink-spinner": "^5.0.0",
47
55
  "meow": "^14.1.0",
48
- "react": "^19.2.4",
56
+ "react": "^19.2.6",
49
57
  "update-notifier": "^7.3.1",
50
- "yaml": "^2.8.2"
58
+ "yaml": "^2.9.0"
51
59
  },
52
60
  "devDependencies": {
61
+ "@biomejs/biome": "^2.4.15",
53
62
  "@semantic-release/changelog": "^6.0.3",
54
63
  "@semantic-release/exec": "^7.1.0",
55
64
  "@semantic-release/git": "^10.0.1",
56
- "@semantic-release/github": "^12.0.6",
57
- "@stryker-mutator/core": "^9.6.0",
58
- "@stryker-mutator/typescript-checker": "^9.6.0",
59
- "@stryker-mutator/vitest-runner": "^9.6.0",
65
+ "@semantic-release/github": "^12.0.8",
66
+ "@stryker-mutator/core": "^9.6.1",
67
+ "@stryker-mutator/typescript-checker": "^9.6.1",
68
+ "@stryker-mutator/vitest-runner": "^9.6.1",
60
69
  "@types/fs-extra": "^11.0.4",
61
- "@types/glob": "^9.0.0",
62
- "@types/node": "^25.5.0",
70
+ "@types/node": "^25.8.0",
63
71
  "@types/react": "^19.2.14",
64
72
  "@types/update-notifier": "^6.0.8",
65
- "@vitest/coverage-v8": "^4.1.0",
66
- "conventional-changelog-conventionalcommits": "^9.3.0",
73
+ "@vitest/coverage-v8": "^4.1.6",
74
+ "conventional-changelog-conventionalcommits": "^9.3.1",
75
+ "ink-testing-library": "^4.0.0",
67
76
  "semantic-release": "^25.0.3",
68
- "tsx": "^4.21.0",
69
- "typescript": "^5.9.3",
70
- "vitest": "^4.1.0"
77
+ "tsx": "^4.22.1",
78
+ "typescript": "^6.0.3",
79
+ "vite": "^8.0.13",
80
+ "vitest": "^4.1.6"
81
+ },
82
+ "pnpm": {
83
+ "overrides": {
84
+ "handlebars": "^4.7.9",
85
+ "picomatch": "^4.0.4",
86
+ "brace-expansion": "^5.0.5",
87
+ "lodash": "^4.18.0",
88
+ "lodash-es": "^4.18.0",
89
+ "fast-uri": "^3.1.2",
90
+ "postcss": "^8.5.10",
91
+ "vite": "^8.0.5"
92
+ }
71
93
  }
72
94
  }
@@ -0,0 +1,16 @@
1
+ {
2
+ "mcpServers": {
3
+ "atlassian": {
4
+ "command": "uvx",
5
+ "args": ["mcp-atlassian"],
6
+ "env": {
7
+ "CONFLUENCE_URL": "__CONFLUENCE_URL__",
8
+ "CONFLUENCE_USERNAME": "__CONFLUENCE_USERNAME__",
9
+ "CONFLUENCE_API_TOKEN": "__CONFLUENCE_API_TOKEN__",
10
+ "JIRA_URL": "__JIRA_URL__",
11
+ "JIRA_USERNAME": "__JIRA_USERNAME__",
12
+ "JIRA_API_TOKEN": "__JIRA_API_TOKEN__"
13
+ }
14
+ }
15
+ }
16
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "mcpServers": {
3
+ "repoforge": {
4
+ "command": "npx",
5
+ "args": ["-y", "repoforge", "mcp"],
6
+ "env": {
7
+ "REPOFORGE_PROJECT": "__PROJECT_NAME__"
8
+ }
9
+ }
10
+ }
11
+ }
@@ -0,0 +1,34 @@
1
+ # ===========================================
2
+ # RepoForge Graph Configuration
3
+ # ===========================================
4
+ # Docs: https://github.com/JNZader/repoforge
5
+ # ===========================================
6
+
7
+ graph:
8
+ # Graph type: calls | imports | dependencies
9
+ type: calls
10
+
11
+ # Output format: json | dot | mermaid
12
+ output: json
13
+
14
+ # Where to store the generated graph artifact
15
+ artifact: .repoforge/call-graph.json
16
+
17
+ # Directories to include (globs supported)
18
+ include:
19
+ - src/**
20
+
21
+ # Directories to exclude
22
+ exclude:
23
+ - node_modules/**
24
+ - dist/**
25
+ - coverage/**
26
+ - '**/*.test.*'
27
+ - '**/*.spec.*'
28
+
29
+ query:
30
+ # Enable MCP-based code intelligence queries
31
+ mcp: true
32
+
33
+ # Max depth for transitive dependency lookups
34
+ max_depth: 5
@@ -0,0 +1,140 @@
1
+ # ===========================================
2
+ # Zero-Downtime Docker Deploy (docker rollout)
3
+ # ===========================================
4
+ # Copy to: .github/workflows/deploy.yml
5
+ #
6
+ # Requires:
7
+ # - SSH access to the deploy target (secrets.DEPLOY_HOST, secrets.DEPLOY_USER, secrets.DEPLOY_KEY)
8
+ # - docker-rollout installed on the target: https://github.com/Wowu/docker-rollout
9
+ # - A docker-compose.yml on the target with healthcheck-enabled services
10
+ #
11
+ # Flow:
12
+ # 1. Build & push image to registry
13
+ # 2. SSH into deploy target
14
+ # 3. Pull new image
15
+ # 4. `docker rollout <service>` — spins up new container, waits for healthcheck, drains old
16
+ # ===========================================
17
+
18
+ name: Deploy (Zero-Downtime)
19
+
20
+ on:
21
+ push:
22
+ branches: [main]
23
+ paths-ignore:
24
+ - '**/*.md'
25
+ - '.gitignore'
26
+ workflow_dispatch:
27
+ inputs:
28
+ service:
29
+ description: 'Service to deploy (from docker-compose.yml)'
30
+ required: true
31
+ default: '__SERVICE_NAME__'
32
+ skip_build:
33
+ description: 'Skip image build (deploy existing latest)'
34
+ default: false
35
+ type: boolean
36
+
37
+ permissions:
38
+ contents: read
39
+ packages: write
40
+
41
+ concurrency:
42
+ group: deploy-${{ github.ref }}
43
+ cancel-in-progress: false # Never cancel in-progress deploys
44
+
45
+ env:
46
+ REGISTRY: ghcr.io
47
+ IMAGE_NAME: ${{ github.repository }}
48
+ DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
49
+ DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
50
+
51
+ jobs:
52
+ build-and-push:
53
+ if: ${{ github.event.inputs.skip_build != 'true' }}
54
+ runs-on: ubuntu-latest
55
+ outputs:
56
+ image_tag: ${{ steps.meta.outputs.tags }}
57
+ steps:
58
+ - uses: actions/checkout@v4
59
+
60
+ - name: Log in to Container Registry
61
+ uses: docker/login-action@v3
62
+ with:
63
+ registry: ${{ env.REGISTRY }}
64
+ username: ${{ github.actor }}
65
+ password: ${{ secrets.GITHUB_TOKEN }}
66
+
67
+ - name: Extract metadata
68
+ id: meta
69
+ uses: docker/metadata-action@v5
70
+ with:
71
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
72
+ tags: |
73
+ type=sha
74
+ type=raw,value=latest
75
+
76
+ - name: Build and push
77
+ uses: docker/build-push-action@v5
78
+ with:
79
+ context: .
80
+ push: true
81
+ tags: ${{ steps.meta.outputs.tags }}
82
+ labels: ${{ steps.meta.outputs.labels }}
83
+
84
+ deploy:
85
+ needs: [build-and-push]
86
+ if: always() && (needs.build-and-push.result == 'success' || github.event.inputs.skip_build == 'true')
87
+ runs-on: ubuntu-latest
88
+ environment: production
89
+ steps:
90
+ - name: Deploy via SSH + docker rollout
91
+ uses: appleboy/ssh-action@v1
92
+ with:
93
+ host: ${{ env.DEPLOY_HOST }}
94
+ username: ${{ env.DEPLOY_USER }}
95
+ key: ${{ secrets.DEPLOY_KEY }}
96
+ script: |
97
+ set -euo pipefail
98
+
99
+ cd ${{ secrets.DEPLOY_DIR || '~/app' }}
100
+
101
+ # Pull latest images
102
+ docker compose pull
103
+
104
+ # Zero-downtime rollout for the target service
105
+ SERVICE="${{ github.event.inputs.service || '__SERVICE_NAME__' }}"
106
+ echo "::group::Rolling out ${SERVICE}"
107
+ docker rollout "${SERVICE}"
108
+ echo "::endgroup::"
109
+
110
+ # Cleanup dangling images
111
+ docker image prune -f
112
+
113
+ - name: Verify deployment
114
+ uses: appleboy/ssh-action@v1
115
+ with:
116
+ host: ${{ env.DEPLOY_HOST }}
117
+ username: ${{ env.DEPLOY_USER }}
118
+ key: ${{ secrets.DEPLOY_KEY }}
119
+ script: |
120
+ set -euo pipefail
121
+ cd ${{ secrets.DEPLOY_DIR || '~/app' }}
122
+ SERVICE="${{ github.event.inputs.service || '__SERVICE_NAME__' }}"
123
+
124
+ # Wait for healthcheck (max 60s)
125
+ TIMEOUT=60
126
+ ELAPSED=0
127
+ while [ $ELAPSED -lt $TIMEOUT ]; do
128
+ HEALTH=$(docker compose ps "${SERVICE}" --format json | jq -r '.[0].Health // "none"' 2>/dev/null || echo "unknown")
129
+ if [ "$HEALTH" = "healthy" ]; then
130
+ echo "✅ ${SERVICE} is healthy"
131
+ exit 0
132
+ fi
133
+ sleep 5
134
+ ELAPSED=$((ELAPSED + 5))
135
+ echo "⏳ Waiting for healthcheck... (${ELAPSED}s/${TIMEOUT}s)"
136
+ done
137
+
138
+ echo "❌ ${SERVICE} did not become healthy within ${TIMEOUT}s"
139
+ docker compose logs --tail=50 "${SERVICE}"
140
+ exit 1
@@ -0,0 +1,45 @@
1
+ # ===========================================
2
+ # RepoForge Call Graph — keep graph fresh on every push
3
+ # ===========================================
4
+ # Copy to: .github/workflows/repoforge-graph.yml
5
+ # ===========================================
6
+
7
+ name: RepoForge Graph
8
+
9
+ on:
10
+ push:
11
+ branches: [main]
12
+ paths-ignore:
13
+ - '**/*.md'
14
+ - '.gitignore'
15
+ pull_request:
16
+ branches: [main]
17
+ paths-ignore:
18
+ - '**/*.md'
19
+ - '.gitignore'
20
+
21
+ permissions:
22
+ contents: read
23
+
24
+ concurrency:
25
+ group: repoforge-graph-${{ github.ref }}
26
+ cancel-in-progress: true
27
+
28
+ jobs:
29
+ graph:
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+
34
+ - name: Install repoforge
35
+ run: npm install -g repoforge
36
+
37
+ - name: Generate call graph
38
+ run: repoforge graph --type calls --output .repoforge/call-graph.json
39
+
40
+ - name: Upload graph artifact
41
+ uses: actions/upload-artifact@v4
42
+ with:
43
+ name: call-graph
44
+ path: .repoforge/call-graph.json
45
+ retention-days: 30
@@ -0,0 +1,57 @@
1
+ # ===========================================
2
+ # Zero-Downtime Docker Deploy (docker rollout)
3
+ # ===========================================
4
+ # Merge into: .gitlab-ci.yml
5
+ #
6
+ # Requires:
7
+ # - CI/CD variables: DEPLOY_HOST, DEPLOY_USER, DEPLOY_KEY, DEPLOY_DIR
8
+ # - docker-rollout installed on target: https://github.com/Wowu/docker-rollout
9
+ # - A docker-compose.yml on the target with healthcheck-enabled services
10
+ # ===========================================
11
+
12
+ stages:
13
+ - build
14
+ - deploy
15
+
16
+ variables:
17
+ SERVICE_NAME: "__SERVICE_NAME__"
18
+
19
+ build-image:
20
+ stage: build
21
+ image: docker:latest
22
+ services:
23
+ - docker:dind
24
+ before_script:
25
+ - docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
26
+ script:
27
+ - docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA" -t "$CI_REGISTRY_IMAGE:latest" .
28
+ - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA"
29
+ - docker push "$CI_REGISTRY_IMAGE:latest"
30
+ only:
31
+ - main
32
+
33
+ deploy-zero-downtime:
34
+ stage: deploy
35
+ image: alpine:latest
36
+ needs: ["build-image"]
37
+ before_script:
38
+ - apk add --no-cache openssh-client
39
+ - eval "$(ssh-agent -s)"
40
+ - echo "$DEPLOY_KEY" | ssh-add -
41
+ - mkdir -p ~/.ssh && chmod 700 ~/.ssh
42
+ - ssh-keyscan "$DEPLOY_HOST" >> ~/.ssh/known_hosts
43
+ script:
44
+ - |
45
+ ssh "$DEPLOY_USER@$DEPLOY_HOST" << 'ENDSSH'
46
+ set -euo pipefail
47
+ cd ${DEPLOY_DIR:-~/app}
48
+ docker compose pull
49
+ docker rollout "${SERVICE_NAME}"
50
+ docker image prune -f
51
+ echo "Deploy complete"
52
+ ENDSSH
53
+ environment:
54
+ name: production
55
+ only:
56
+ - main
57
+ when: manual
@@ -0,0 +1,17 @@
1
+ # Local AI Stack — Environment Variables
2
+ # Copy to .env in your project: cp .env.example .env
3
+
4
+ # ── Ollama ──────────────────────────────────────────────────────────────────
5
+ OLLAMA_PORT=11434
6
+
7
+ # ── Open WebUI ──────────────────────────────────────────────────────────────
8
+ WEBUI_PORT=3000
9
+ WEBUI_AUTH=false
10
+
11
+ # ── n8n ─────────────────────────────────────────────────────────────────────
12
+ N8N_PORT=5678
13
+
14
+ # ── Supabase (Postgres) ────────────────────────────────────────────────────
15
+ SUPABASE_DB_PORT=5432
16
+ SUPABASE_DB_PASSWORD=postgres
17
+ SUPABASE_DB_NAME=app
@@ -0,0 +1,95 @@
1
+ # Local AI Development Stack
2
+ # Generated by javi-forge — https://github.com/JNZader/javi-forge
3
+ #
4
+ # Usage:
5
+ # docker compose up -d # Start Ollama (core)
6
+ # docker compose --profile ui up -d # Start Ollama + Open WebUI
7
+ # docker compose --profile auto up -d # Start Ollama + n8n + Open WebUI
8
+ # docker compose --profile full up -d # Start everything
9
+ #
10
+ # Pull a model after starting:
11
+ # docker exec ollama ollama pull llama3.2
12
+ # docker exec ollama ollama pull codellama
13
+
14
+ services:
15
+ # ── Core: Ollama (local LLM inference) ────────────────────────────────────
16
+ ollama:
17
+ image: ollama/ollama:latest
18
+ container_name: ollama
19
+ restart: unless-stopped
20
+ ports:
21
+ - "${OLLAMA_PORT:-11434}:11434"
22
+ volumes:
23
+ - ollama_data:/root/.ollama
24
+ environment:
25
+ - OLLAMA_HOST=0.0.0.0
26
+ # Uncomment for NVIDIA GPU support:
27
+ # deploy:
28
+ # resources:
29
+ # reservations:
30
+ # devices:
31
+ # - driver: nvidia
32
+ # count: all
33
+ # capabilities: [gpu]
34
+ healthcheck:
35
+ test: ["CMD-SHELL", "curl -f http://localhost:11434/api/tags || exit 1"]
36
+ interval: 30s
37
+ timeout: 10s
38
+ retries: 3
39
+ start_period: 10s
40
+
41
+ # ── Optional: Open WebUI (chat interface) ─────────────────────────────────
42
+ open-webui:
43
+ image: ghcr.io/open-webui/open-webui:main
44
+ container_name: open-webui
45
+ profiles: ["ui", "auto", "full"]
46
+ restart: unless-stopped
47
+ ports:
48
+ - "${WEBUI_PORT:-3000}:8080"
49
+ volumes:
50
+ - open_webui_data:/app/backend/data
51
+ environment:
52
+ - OLLAMA_BASE_URL=http://ollama:11434
53
+ - WEBUI_AUTH=${WEBUI_AUTH:-false}
54
+ depends_on:
55
+ ollama:
56
+ condition: service_healthy
57
+
58
+ # ── Optional: n8n (workflow automation) ───────────────────────────────────
59
+ n8n:
60
+ image: n8nio/n8n:latest
61
+ container_name: n8n
62
+ profiles: ["auto", "full"]
63
+ restart: unless-stopped
64
+ ports:
65
+ - "${N8N_PORT:-5678}:5678"
66
+ volumes:
67
+ - n8n_data:/home/node/.n8n
68
+ environment:
69
+ - N8N_SECURE_COOKIE=false
70
+ - OLLAMA_HOST=http://ollama:11434
71
+
72
+ # ── Optional: Supabase (Postgres + Auth + Realtime) ───────────────────────
73
+ supabase-db:
74
+ image: supabase/postgres:15.6.1.145
75
+ container_name: supabase-db
76
+ profiles: ["full"]
77
+ restart: unless-stopped
78
+ ports:
79
+ - "${SUPABASE_DB_PORT:-5432}:5432"
80
+ volumes:
81
+ - supabase_db_data:/var/lib/postgresql/data
82
+ environment:
83
+ POSTGRES_PASSWORD: ${SUPABASE_DB_PASSWORD:-postgres}
84
+ POSTGRES_DB: ${SUPABASE_DB_NAME:-app}
85
+ healthcheck:
86
+ test: ["CMD-SHELL", "pg_isready -U postgres"]
87
+ interval: 10s
88
+ timeout: 5s
89
+ retries: 5
90
+
91
+ volumes:
92
+ ollama_data:
93
+ open_webui_data:
94
+ n8n_data:
95
+ supabase_db_data:
@@ -0,0 +1,30 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/anthropics/claude-code/main/settings-schema.json",
3
+ "_comment": "Runtime security hooks (kiteguard-style) — scaffolded by javi-forge",
4
+ "hooks": {
5
+ "PreToolUse": [
6
+ {
7
+ "matcher": "Bash",
8
+ "hook": "COMMAND=\"$CLAUDE_TOOL_INPUT\"\nBLOCKED_PATTERNS=(\n 'rm -rf /'\n 'rm -rf ~'\n 'chmod 777'\n 'curl.*|.*sh'\n 'wget.*|.*sh'\n 'eval.*\\$'\n 'base64.*-d.*|.*sh'\n ':(){:|:&};:'\n)\nfor pattern in \"${BLOCKED_PATTERNS[@]}\"; do\n if echo \"$COMMAND\" | grep -qE \"$pattern\"; then\n echo \"BLOCKED: Dangerous command pattern detected: $pattern\"\n exit 2\n fi\ndone\nexit 0",
9
+ "description": "Block dangerous shell commands (rm -rf /, chmod 777, pipe-to-sh, fork bombs)"
10
+ },
11
+ {
12
+ "matcher": "Bash",
13
+ "hook": "COMMAND=\"$CLAUDE_TOOL_INPUT\"\nSENSITIVE_PATHS=(\n '.env'\n '.env.local'\n '.env.production'\n 'credentials'\n 'secrets'\n '.ssh'\n '.gnupg'\n '.aws/credentials'\n)\nfor path in \"${SENSITIVE_PATHS[@]}\"; do\n if echo \"$COMMAND\" | grep -qE \"(cat|less|head|tail|bat|read).*$path\"; then\n echo \"BLOCKED: Attempt to read sensitive file: $path\"\n exit 2\n fi\ndone\nexit 0",
14
+ "description": "Prevent reading sensitive files (.env, credentials, SSH keys)"
15
+ },
16
+ {
17
+ "matcher": "Write|Edit",
18
+ "hook": "INPUT=\"$CLAUDE_TOOL_INPUT\"\nPROTECTED_FILES=(\n '.env'\n '.env.production'\n 'credentials.json'\n 'serviceAccountKey.json'\n '.ssh/'\n '.gnupg/'\n)\nfor pf in \"${PROTECTED_FILES[@]}\"; do\n if echo \"$INPUT\" | grep -q \"$pf\"; then\n echo \"BLOCKED: Cannot modify protected file: $pf\"\n exit 2\n fi\ndone\nexit 0",
19
+ "description": "Prevent writing to credential and secret files"
20
+ }
21
+ ],
22
+ "PostToolUse": [
23
+ {
24
+ "matcher": "Bash",
25
+ "hook": "OUTPUT=\"$CLAUDE_TOOL_OUTPUT\"\nLEAK_PATTERNS=(\n 'AKIA[0-9A-Z]{16}'\n 'ghp_[A-Za-z0-9]{36}'\n 'sk_live_[0-9a-zA-Z]{24,}'\n 'xox[baprs]-[0-9a-zA-Z-]+'\n '-----BEGIN.*PRIVATE KEY-----'\n)\nfor pattern in \"${LEAK_PATTERNS[@]}\"; do\n if echo \"$OUTPUT\" | grep -qE \"$pattern\"; then\n echo \"WARNING: Command output may contain secrets. Review carefully.\"\n exit 0\n fi\ndone\nexit 0",
26
+ "description": "Warn if command output contains potential secrets"
27
+ }
28
+ ]
29
+ }
30
+ }
@@ -0,0 +1,29 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # SECURITY LAYER 6: Commit Message Signing Reminder (commit-msg)
4
+ # =============================================================================
5
+ # Reminds developers to sign commits if signing is configured but the
6
+ # current commit is not signed. Non-blocking — just a nudge.
7
+ #
8
+ # This complements layer 4 (pre-push signing) with an earlier reminder.
9
+ # =============================================================================
10
+
11
+ set -e
12
+
13
+ YELLOW='\033[1;33m'
14
+ CYAN='\033[0;36m'
15
+ GREEN='\033[0;32m'
16
+ NC='\033[0m'
17
+
18
+ echo "SECURITY [6/6]: Commit signing reminder..."
19
+
20
+ GPG_SIGN=$(git config --get commit.gpgsign 2>/dev/null || echo "false")
21
+
22
+ if [ "$GPG_SIGN" = "true" ]; then
23
+ echo -e "${GREEN} Commit signing is enabled. Good.${NC}"
24
+ else
25
+ echo -e "${YELLOW} Tip: Enable commit signing for supply-chain security.${NC}"
26
+ echo -e "${CYAN} git config commit.gpgsign true${NC}"
27
+ fi
28
+
29
+ exit 0
@@ -0,0 +1,74 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # SECURITY LAYER 3: Permission Boundaries (pre-commit)
4
+ # =============================================================================
5
+ # Validates that staged files don't introduce overly permissive file modes,
6
+ # executable flags on non-script files, or world-writable permissions.
7
+ #
8
+ # To skip: git commit --no-verify (NOT recommended)
9
+ # =============================================================================
10
+
11
+ set -e
12
+
13
+ RED='\033[0;31m'
14
+ YELLOW='\033[1;33m'
15
+ GREEN='\033[0;32m'
16
+ NC='\033[0m'
17
+
18
+ echo "SECURITY [3/6]: Permission boundary check..."
19
+
20
+ STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
21
+
22
+ if [ -z "$STAGED_FILES" ]; then
23
+ echo -e "${GREEN} No staged files to check.${NC}"
24
+ exit 0
25
+ fi
26
+
27
+ ISSUES=0
28
+
29
+ # Check for world-writable files (o+w)
30
+ for file in $STAGED_FILES; do
31
+ if [ -f "$file" ]; then
32
+ PERMS=$(stat -c '%a' "$file" 2>/dev/null || stat -f '%Lp' "$file" 2>/dev/null || echo "")
33
+ if [ -n "$PERMS" ]; then
34
+ # Check if last digit (other permissions) has write (2, 3, 6, 7)
35
+ OTHERS=${PERMS: -1}
36
+ if [[ "$OTHERS" =~ [2367] ]]; then
37
+ echo -e "${RED} World-writable: $file (mode $PERMS)${NC}"
38
+ ISSUES=$((ISSUES + 1))
39
+ fi
40
+ fi
41
+ fi
42
+ done
43
+
44
+ # Check for executable flag on non-script files
45
+ SCRIPT_EXTENSIONS='\.sh$|\.bash$|\.zsh$|\.py$|\.rb$|\.pl$'
46
+ for file in $STAGED_FILES; do
47
+ if [ -f "$file" ] && [ -x "$file" ]; then
48
+ # Allow scripts and hooks (no extension = likely a hook)
49
+ BASENAME=$(basename "$file")
50
+ if echo "$file" | grep -qE "$SCRIPT_EXTENSIONS"; then
51
+ continue
52
+ fi
53
+ # Allow files in hooks/ directories
54
+ if echo "$file" | grep -q '/hooks/'; then
55
+ continue
56
+ fi
57
+ # Allow if it has a shebang
58
+ if head -1 "$file" 2>/dev/null | grep -q '^#!'; then
59
+ continue
60
+ fi
61
+ echo -e "${YELLOW} Unexpected executable: $file${NC}"
62
+ ISSUES=$((ISSUES + 1))
63
+ fi
64
+ done
65
+
66
+ if [ "$ISSUES" -gt 0 ]; then
67
+ echo -e ""
68
+ echo -e "${RED}COMMIT BLOCKED: $ISSUES permission issue(s) found.${NC}"
69
+ echo -e "${YELLOW} Fix file permissions before committing.${NC}"
70
+ exit 1
71
+ fi
72
+
73
+ echo -e "${GREEN} Permissions OK.${NC}"
74
+ exit 0