mishkan-harness 0.1.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 (186) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +205 -0
  3. package/bin/mishkan.js +221 -0
  4. package/docs/design/MISHKAN_agent_aliases.md +140 -0
  5. package/docs/design/MISHKAN_decisions.md +172 -0
  6. package/docs/design/MISHKAN_harness_design.md +820 -0
  7. package/docs/design/MISHKAN_ontology.md +87 -0
  8. package/docs/design/MISHKAN_token_optimisation.md +181 -0
  9. package/docs/engineer/README.md +37 -0
  10. package/docs/engineer/profile.example.md +79 -0
  11. package/docs/usage/01-installation.md +178 -0
  12. package/docs/usage/02-project-init.md +151 -0
  13. package/docs/usage/03-orchestration.md +218 -0
  14. package/docs/usage/04-memory-layer.md +201 -0
  15. package/docs/usage/05-selective-ingest.md +177 -0
  16. package/docs/usage/06-llm-providers.md +195 -0
  17. package/docs/usage/07-troubleshooting.md +316 -0
  18. package/docs/usage/08-glossary.md +154 -0
  19. package/docs/usage/09-workflows.md +123 -0
  20. package/docs/usage/README.md +77 -0
  21. package/package.json +43 -0
  22. package/payload/install/settings.hooks.json +47 -0
  23. package/payload/mishkan/AGENT_SPEC.md +154 -0
  24. package/payload/mishkan/agents/ahikam.md +58 -0
  25. package/payload/mishkan/agents/aholiab.md +68 -0
  26. package/payload/mishkan/agents/asaph.md +73 -0
  27. package/payload/mishkan/agents/baruch.md +88 -0
  28. package/payload/mishkan/agents/benaiah.md +76 -0
  29. package/payload/mishkan/agents/bezalel.md +83 -0
  30. package/payload/mishkan/agents/caleb.md +74 -0
  31. package/payload/mishkan/agents/deborah.md +63 -0
  32. package/payload/mishkan/agents/elasah.md +58 -0
  33. package/payload/mishkan/agents/eliashib.md +68 -0
  34. package/payload/mishkan/agents/ezra.md +69 -0
  35. package/payload/mishkan/agents/hanun.md +64 -0
  36. package/payload/mishkan/agents/hiram.md +68 -0
  37. package/payload/mishkan/agents/hizkiah.md +76 -0
  38. package/payload/mishkan/agents/huldah.md +59 -0
  39. package/payload/mishkan/agents/huram.md +66 -0
  40. package/payload/mishkan/agents/hushai.md +59 -0
  41. package/payload/mishkan/agents/igal.md +58 -0
  42. package/payload/mishkan/agents/ira.md +86 -0
  43. package/payload/mishkan/agents/jahaziel.md +71 -0
  44. package/payload/mishkan/agents/jakin.md +66 -0
  45. package/payload/mishkan/agents/jehonathan.md +62 -0
  46. package/payload/mishkan/agents/jehoshaphat.md +68 -0
  47. package/payload/mishkan/agents/joab.md +71 -0
  48. package/payload/mishkan/agents/joah.md +62 -0
  49. package/payload/mishkan/agents/maaseiah.md +61 -0
  50. package/payload/mishkan/agents/meremoth.md +65 -0
  51. package/payload/mishkan/agents/meshullam.md +67 -0
  52. package/payload/mishkan/agents/nathan.md +70 -0
  53. package/payload/mishkan/agents/nehemiah.md +93 -0
  54. package/payload/mishkan/agents/obed.md +60 -0
  55. package/payload/mishkan/agents/oholiab.md +67 -0
  56. package/payload/mishkan/agents/palal.md +63 -0
  57. package/payload/mishkan/agents/phinehas.md +73 -0
  58. package/payload/mishkan/agents/rehum.md +60 -0
  59. package/payload/mishkan/agents/salma.md +69 -0
  60. package/payload/mishkan/agents/seraiah.md +73 -0
  61. package/payload/mishkan/agents/shallum.md +66 -0
  62. package/payload/mishkan/agents/shaphan.md +64 -0
  63. package/payload/mishkan/agents/shemaiah.md +67 -0
  64. package/payload/mishkan/agents/shevna.md +58 -0
  65. package/payload/mishkan/agents/uriah.md +70 -0
  66. package/payload/mishkan/agents/zaccur.md +58 -0
  67. package/payload/mishkan/agents/zadok.md +67 -0
  68. package/payload/mishkan/agents/zerubbabel.md +69 -0
  69. package/payload/mishkan/cognee/.env.curated.example +61 -0
  70. package/payload/mishkan/cognee/.env.example +165 -0
  71. package/payload/mishkan/cognee/Dockerfile +50 -0
  72. package/payload/mishkan/cognee/README.md +129 -0
  73. package/payload/mishkan/cognee/docker-compose.curated-ui.yml +61 -0
  74. package/payload/mishkan/cognee/docker-compose.curated.yml +85 -0
  75. package/payload/mishkan/cognee/docker-compose.hardening.yml +16 -0
  76. package/payload/mishkan/cognee/docker-compose.selfhosted.yml +114 -0
  77. package/payload/mishkan/cognee/docker-compose.ui.yml +70 -0
  78. package/payload/mishkan/cognee/docker-compose.yml +71 -0
  79. package/payload/mishkan/cognee/ingest-curated.py +92 -0
  80. package/payload/mishkan/commands/dep-audit.md +24 -0
  81. package/payload/mishkan/commands/mishkan-init.md +25 -0
  82. package/payload/mishkan/commands/mishkan-resume.md +21 -0
  83. package/payload/mishkan/commands/promote.md +19 -0
  84. package/payload/mishkan/commands/sefer-pull.md +19 -0
  85. package/payload/mishkan/commands/sprint-close.md +21 -0
  86. package/payload/mishkan/config/curated-library.yaml +113 -0
  87. package/payload/mishkan/config/improvement-queries.md +29 -0
  88. package/payload/mishkan/config/model-routing.yaml +87 -0
  89. package/payload/mishkan/config/projects.yaml +38 -0
  90. package/payload/mishkan/evals/baruch/README.md +93 -0
  91. package/payload/mishkan/evals/baruch/fixtures/invalid/bad-outcome-enum.json +15 -0
  92. package/payload/mishkan/evals/baruch/fixtures/invalid/bad-sprint-pattern.json +15 -0
  93. package/payload/mishkan/evals/baruch/fixtures/invalid/bad-trigger-enum.json +15 -0
  94. package/payload/mishkan/evals/baruch/fixtures/invalid/malformed-json.json +7 -0
  95. package/payload/mishkan/evals/baruch/fixtures/invalid/missing-required-field.json +14 -0
  96. package/payload/mishkan/evals/baruch/fixtures/valid/blocked-vendor.json +15 -0
  97. package/payload/mishkan/evals/baruch/fixtures/valid/curated-shortcircuit.json +15 -0
  98. package/payload/mishkan/evals/baruch/fixtures/valid/partial-no-write.json +14 -0
  99. package/payload/mishkan/evals/baruch/fixtures/valid/resolved-cross-harness.json +15 -0
  100. package/payload/mishkan/evals/baruch/golden_case/expected.yaml +35 -0
  101. package/payload/mishkan/evals/baruch/golden_case/input.yaml +47 -0
  102. package/payload/mishkan/evals/baruch/golden_case/produced.json +15 -0
  103. package/payload/mishkan/evals/baruch/run.sh +129 -0
  104. package/payload/mishkan/hooks/model-route.py +96 -0
  105. package/payload/mishkan/hooks/post-tool-observe.sh +45 -0
  106. package/payload/mishkan/hooks/pre-tool-security.sh +150 -0
  107. package/payload/mishkan/hooks/session-start.sh +20 -0
  108. package/payload/mishkan/hooks/stop-reporter.sh +29 -0
  109. package/payload/mishkan/ontology.md +87 -0
  110. package/payload/mishkan/rules/backend/yasad.md +23 -0
  111. package/payload/mishkan/rules/common/dependencies.md +53 -0
  112. package/payload/mishkan/rules/common/quality.md +16 -0
  113. package/payload/mishkan/rules/common/security.md +20 -0
  114. package/payload/mishkan/rules/documentation/sefer.md +19 -0
  115. package/payload/mishkan/rules/frontend/panim.md +21 -0
  116. package/payload/mishkan/rules/infrastructure/migdal.md +22 -0
  117. package/payload/mishkan/scripts/dependency-audit.sh +171 -0
  118. package/payload/mishkan/scripts/ensure-curated-box.sh +66 -0
  119. package/payload/mishkan/scripts/mishkan-ingest.sh +92 -0
  120. package/payload/mishkan/scripts/observability-aggregate.sh +57 -0
  121. package/payload/mishkan/scripts/seed-curated-library.sh +62 -0
  122. package/payload/mishkan/scripts/sync-profile.sh +65 -0
  123. package/payload/mishkan/scripts/validate-research-log.sh +108 -0
  124. package/payload/mishkan/skills/asaph-a11y-seo-craft/SKILL.md +289 -0
  125. package/payload/mishkan/skills/baruch-research-reporting-craft/SKILL.md +460 -0
  126. package/payload/mishkan/skills/benaiah-devsecops-craft/SKILL.md +329 -0
  127. package/payload/mishkan/skills/bezalel-cto-craft/SKILL.md +391 -0
  128. package/payload/mishkan/skills/caleb-web-research-craft/SKILL.md +306 -0
  129. package/payload/mishkan/skills/cognee-promote/SKILL.md +40 -0
  130. package/payload/mishkan/skills/cognee-quickstart/SKILL.md +66 -0
  131. package/payload/mishkan/skills/context-compress/SKILL.md +36 -0
  132. package/payload/mishkan/skills/deborah-ux-craft/SKILL.md +295 -0
  133. package/payload/mishkan/skills/dependency-audit/SKILL.md +59 -0
  134. package/payload/mishkan/skills/dependency-vetting/SKILL.md +59 -0
  135. package/payload/mishkan/skills/documentation-craft/SKILL.md +468 -0
  136. package/payload/mishkan/skills/ezra-research-formulation-craft/SKILL.md +319 -0
  137. package/payload/mishkan/skills/hanun-observability-craft/SKILL.md +312 -0
  138. package/payload/mishkan/skills/hiram-ui-craft/SKILL.md +334 -0
  139. package/payload/mishkan/skills/hizkiah-implementation-craft/SKILL.md +701 -0
  140. package/payload/mishkan/skills/hushai-security-advisor-craft/SKILL.md +282 -0
  141. package/payload/mishkan/skills/ira-code-security-craft/SKILL.md +553 -0
  142. package/payload/mishkan/skills/jakin-intent-clarification-craft/SKILL.md +299 -0
  143. package/payload/mishkan/skills/jehonathan-publication-craft/SKILL.md +262 -0
  144. package/payload/mishkan/skills/joab-app-security-craft/SKILL.md +266 -0
  145. package/payload/mishkan/skills/meremoth-devops-craft/SKILL.md +298 -0
  146. package/payload/mishkan/skills/meshullam-infra-design-craft/SKILL.md +302 -0
  147. package/payload/mishkan/skills/mishkan-ingest/SKILL.md +65 -0
  148. package/payload/mishkan/skills/mishkan-init/SKILL.md +65 -0
  149. package/payload/mishkan/skills/nathan-architecture-craft/SKILL.md +547 -0
  150. package/payload/mishkan/skills/nehemiah-pm-craft/SKILL.md +484 -0
  151. package/payload/mishkan/skills/obed-asset-pipeline-craft/SKILL.md +286 -0
  152. package/payload/mishkan/skills/oholiab-design-system-craft/SKILL.md +334 -0
  153. package/payload/mishkan/skills/palal-systems-craft/SKILL.md +281 -0
  154. package/payload/mishkan/skills/qa-evaluation-craft/SKILL.md +406 -0
  155. package/payload/mishkan/skills/rehum-sre-advisor-craft/SKILL.md +228 -0
  156. package/payload/mishkan/skills/reporter-discipline-craft/SKILL.md +351 -0
  157. package/payload/mishkan/skills/research-pipeline/SKILL.md +55 -0
  158. package/payload/mishkan/skills/salma-frontend-implementation-craft/SKILL.md +369 -0
  159. package/payload/mishkan/skills/sefer-pull/SKILL.md +37 -0
  160. package/payload/mishkan/skills/shallum-database-craft/SKILL.md +347 -0
  161. package/payload/mishkan/skills/shaphan-summarisation-craft/SKILL.md +271 -0
  162. package/payload/mishkan/skills/shemaiah-evaluation-craft/SKILL.md +342 -0
  163. package/payload/mishkan/skills/sprint-report/SKILL.md +28 -0
  164. package/payload/mishkan/skills/team-lead-craft/SKILL.md +457 -0
  165. package/payload/mishkan/skills/zadok-contract-craft/SKILL.md +520 -0
  166. package/payload/mishkan/templates/case-node.schema.json +22 -0
  167. package/payload/mishkan/templates/mcp.json +22 -0
  168. package/payload/mishkan/templates/observability-log.schema.json +24 -0
  169. package/payload/mishkan/templates/project-CLAUDE.md +47 -0
  170. package/payload/mishkan/templates/research-log.schema.json +40 -0
  171. package/payload/mishkan/templates/settings.json +12 -0
  172. package/payload/mishkan/templates/settings.local.json +6 -0
  173. package/payload/mishkan/templates/sprint-state.schema.json +47 -0
  174. package/payload/mishkan/templates/team-report.schema.json +50 -0
  175. package/payload/mishkan/templates/user-CLAUDE.md +62 -0
  176. package/payload/mishkan/workflows/README.md +88 -0
  177. package/payload/mishkan/workflows/mishkan-architecture-panel.js +156 -0
  178. package/payload/mishkan/workflows/mishkan-codebase-audit.js +188 -0
  179. package/payload/mishkan/workflows/mishkan-deep-research.js +251 -0
  180. package/payload/mishkan/workflows/mishkan-init.js +156 -0
  181. package/payload/mishkan/workflows/mishkan-migration-wave.js +180 -0
  182. package/payload/mishkan/workflows/mishkan-release-readiness.js +163 -0
  183. package/payload/mishkan/workflows/mishkan-sprint-close.js +112 -0
  184. package/payload/user/CLAUDE.md +62 -0
  185. package/payload/user/rules/engineer-standards.md +66 -0
  186. package/payload/user/rules/y4nn-standards.md +167 -0
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env bash
2
+ # MISHKAN PreToolUse security hook — Ira (Mishmar).
3
+ # Inspects Write/Edit/MultiEdit content before it lands.
4
+ # Blocks: hardcoded secrets, eval of untrusted input, SQL string concatenation,
5
+ # :latest Docker tags. Returns a PreToolUse deny decision on violation.
6
+ #
7
+ # Fail-open by design: if the payload cannot be parsed (e.g. jq missing), the
8
+ # hook allows the operation and notes it on stderr rather than bricking all
9
+ # writes. Positive detections always block.
10
+ set -uo pipefail
11
+
12
+ INPUT="$(cat)"
13
+
14
+ # Need jq to inspect structured payload. Fail open if absent.
15
+ if ! command -v jq >/dev/null 2>&1; then
16
+ echo "mishkan/pre-tool-security: jq not found — skipping inspection" >&2
17
+ exit 0
18
+ fi
19
+
20
+ tool_name="$(printf '%s' "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null)"
21
+ file_path="$(printf '%s' "$INPUT" | jq -r '.tool_input.file_path // empty' 2>/dev/null)"
22
+
23
+ # Gather the content being written, across Write/Edit/MultiEdit shapes.
24
+ content="$(printf '%s' "$INPUT" | jq -r '
25
+ [ .tool_input.content?,
26
+ .tool_input.new_string?,
27
+ ( .tool_input.edits? // [] | .[].new_string? )
28
+ ] | map(select(. != null)) | join("\n")
29
+ ' 2>/dev/null)"
30
+
31
+ # Nothing to inspect.
32
+ [ -z "$content" ] && exit 0
33
+
34
+ deny() {
35
+ # PreToolUse deny via structured output.
36
+ jq -n --arg reason "$1" '{
37
+ hookSpecificOutput: {
38
+ hookEventName: "PreToolUse",
39
+ permissionDecision: "deny",
40
+ permissionDecisionReason: $reason
41
+ }
42
+ }'
43
+ exit 0
44
+ }
45
+
46
+ lc_path="$(printf '%s' "$file_path" | tr '[:upper:]' '[:lower:]')"
47
+
48
+ # 1. Hardcoded secrets — assignment of a non-env, non-placeholder literal.
49
+ if printf '%s' "$content" | grep -Eiq \
50
+ '(password|passwd|secret|api[_-]?key|access[_-]?key|token|client[_-]?secret)[[:space:]]*[:=][[:space:]]*["'"'"'][^"'"'"']{6,}["'"'"']'; then
51
+ # Allow obvious placeholders / env reads.
52
+ if ! printf '%s' "$content" | grep -Eiq '(CHANGEME|<[^>]+>|\$\{?[A-Z_]+\}?|os\.environ|process\.env|getenv|settings\.|pydantic|vault|sops)'; then
53
+ deny "Mishmar/Ira: hardcoded secret literal detected. Use SOPS/age + env injection, never literals in source. (rules/common/security.md)"
54
+ fi
55
+ fi
56
+
57
+ # 2. Key material, private keys, and known provider token formats.
58
+ if printf '%s' "$content" | grep -Eq '(AKIA[0-9A-Z]{16}|-----BEGIN [A-Z ]*PRIVATE KEY-----)'; then
59
+ deny "Mishmar/Ira: key material detected in source. Move to SOPS-managed secrets. (rules/common/security.md)"
60
+ fi
61
+ if printf '%s' "$content" | grep -Eq '(gh[posu]_[A-Za-z0-9]{30,}|glpat-[A-Za-z0-9_-]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}|sk_(live|test)_[A-Za-z0-9]{16,}|AIza[0-9A-Za-z_-]{30,}|ya29\.[0-9A-Za-z_-]+|SG\.[A-Za-z0-9_-]{16,}\.[A-Za-z0-9_-]{16,}|dckr_pat_[A-Za-z0-9_-]{20,}|npm_[A-Za-z0-9]{30,})'; then
62
+ deny "Mishmar/Ira: a provider access token (GitHub/GitLab/Slack/Stripe/Google/SendGrid/Docker/npm) is hardcoded. Revoke it and move to SOPS/age + env. (rules/common/security.md)"
63
+ fi
64
+
65
+ # 2b. Connection strings with embedded credentials.
66
+ if printf '%s' "$content" | grep -Eiq '(postgres|postgresql|mysql|mongodb|mongodb\+srv|redis|amqp|amqps)://[^:@/[:space:]]+:[^@/[:space:]]+@'; then
67
+ if ! printf '%s' "$content" | grep -Eiq '(<[^>]+>|\$\{?[A-Za-z_]+\}?|CHANGEME|:password@|:pass@|user:pass|env|getenv|os\.environ)'; then
68
+ deny "Mishmar/Ira: connection string with an embedded credential. Inject the password from SOPS/env, not the URL literal. (rules/common/security.md)"
69
+ fi
70
+ fi
71
+
72
+ # 3. Dynamic code execution / unsafe deserialization (py/js/ts).
73
+ case "$lc_path" in
74
+ *.py|*.js|*.ts|*.tsx|*.jsx|*.mjs|*.cjs)
75
+ if printf '%s' "$content" | grep -Eq '(^|[^a-zA-Z0-9_.])eval[[:space:]]*\(' \
76
+ && ! printf '%s' "$content" | grep -Eiq '(ast\.literal_eval|# *safe-eval|json\.parse)'; then
77
+ deny "Mishmar/Ira: eval() on dynamic input is forbidden. (rules/common/security.md)"
78
+ fi
79
+ # new Function(...) constructor (JS/TS code-from-string)
80
+ if printf '%s' "$content" | grep -Eq 'new[[:space:]]+Function[[:space:]]*\('; then
81
+ deny "Mishmar/Ira: new Function() builds code from a string. Forbidden. (rules/common/security.md)"
82
+ fi
83
+ ;;
84
+ esac
85
+ case "$lc_path" in
86
+ *.py|*.pyi)
87
+ # exec() on dynamic input
88
+ if printf '%s' "$content" | grep -Eq '(^|[^a-zA-Z0-9_.])exec[[:space:]]*\(' \
89
+ && ! printf '%s' "$content" | grep -Eiq '# *safe-exec'; then
90
+ deny "Mishmar/Ira: exec() executes dynamic code. Forbidden. (rules/common/security.md)"
91
+ fi
92
+ # unsafe deserialization
93
+ if printf '%s' "$content" | grep -Eq '(pickle\.loads?|cloudpickle\.loads?|marshal\.loads)[[:space:]]*\('; then
94
+ deny "Mishmar/Ira: pickle/marshal deserialization of untrusted data is an RCE vector. Use a safe format (JSON). (rules/common/security.md)"
95
+ fi
96
+ # yaml.load without a safe loader
97
+ if printf '%s' "$content" | grep -Eq 'yaml\.load[[:space:]]*\(' \
98
+ && ! printf '%s' "$content" | grep -Eq '(SafeLoader|safe_load|Loader[[:space:]]*=[[:space:]]*yaml\.Safe)'; then
99
+ deny "Mishmar/Ira: yaml.load() without SafeLoader can execute arbitrary objects. Use yaml.safe_load(). (rules/common/security.md)"
100
+ fi
101
+ # shell command injection vectors
102
+ if printf '%s' "$content" | grep -Eq 'os\.system[[:space:]]*\('; then
103
+ deny "Mishmar/Ira: os.system() invites command injection. Use subprocess with an argument list and no shell. (rules/common/security.md)"
104
+ fi
105
+ if printf '%s' "$content" | grep -Eq 'subprocess\.[A-Za-z_]+\(.*shell[[:space:]]*=[[:space:]]*True'; then
106
+ deny "Mishmar/Ira: subprocess with shell=True is a command-injection risk. Pass an argument list, shell=False. (rules/common/security.md)"
107
+ fi
108
+ ;;
109
+ esac
110
+ case "$lc_path" in
111
+ *.js|*.ts|*.tsx|*.jsx|*.mjs|*.cjs)
112
+ # child_process exec with interpolation
113
+ if printf '%s' "$content" | grep -Eq '(child_process|require\(.child_process.\))' \
114
+ && printf '%s' "$content" | grep -Eq 'exec(Sync)?[[:space:]]*\(.*(\$\{|`.*\$|"[[:space:]]*\+)'; then
115
+ deny "Mishmar/Ira: child_process exec with string interpolation is a command-injection risk. Use execFile with an args array. (rules/common/security.md)"
116
+ fi
117
+ ;;
118
+ esac
119
+
120
+ # 4. SQL string concatenation / f-string interpolation.
121
+ if printf '%s' "$content" | grep -Eiq '(select|insert|update|delete)[[:space:]].*("[[:space:]]*\+|\+[[:space:]]*"|f"[^"]*\{|%[[:space:]]*\()'; then
122
+ deny "Mishmar/Ira: SQL built by string concatenation/interpolation. Use parameterised queries (asyncpg params / ORM bindings). (rules/common/security.md)"
123
+ fi
124
+
125
+ # 5. :latest Docker tags (and untagged FROM).
126
+ case "$lc_path" in
127
+ *dockerfile*|*docker-compose*|*compose*|*.yaml|*.yml)
128
+ if printf '%s' "$content" | grep -Eiq '(image:[[:space:]]*[^[:space:]]+:latest|^[[:space:]]*FROM[[:space:]]+[^[:space:]]+:latest)'; then
129
+ deny "Mishmar/Ira: ':latest' tag detected. Pin all image versions. (rules/infrastructure/migdal.md, y4nn-standards)"
130
+ fi
131
+ ;;
132
+ esac
133
+
134
+ # 6. TLS/certificate verification disabled.
135
+ if printf '%s' "$content" | grep -Eiq '(verify[[:space:]]*=[[:space:]]*False|rejectUnauthorized[[:space:]]*:[[:space:]]*false|InsecureSkipVerify[[:space:]]*:[[:space:]]*true|NODE_TLS_REJECT_UNAUTHORIZED[[:space:]]*=[[:space:]]*.?0|curl_setopt.*CURLOPT_SSL_VERIFYPEER.*false|ssl[._]?verify[[:space:]]*=[[:space:]]*false)'; then
136
+ deny "Mishmar/Ira: TLS/certificate verification is being disabled. This breaks transport security. (rules/common/security.md)"
137
+ fi
138
+
139
+ # 7. Weak hashing used on a password/secret.
140
+ if printf '%s' "$content" | grep -Eiq '(md5|sha1)[[:space:]]*\([^)]*(pass|pwd|secret|token|credential)'; then
141
+ deny "Mishmar/Ira: weak hash (MD5/SHA1) applied to a credential. Use bcrypt/argon2/scrypt for passwords. (rules/common/security.md)"
142
+ fi
143
+
144
+ # 8. CORS wildcard combined with credentials (same write).
145
+ if printf '%s' "$content" | grep -Eiq 'access-control-allow-origin["'"'"'[:space:]]*[:=][[:space:]]*["'"'"']?\*' \
146
+ && printf '%s' "$content" | grep -Eiq 'access-control-allow-credentials["'"'"'[:space:]]*[:=][[:space:]]*["'"'"']?true'; then
147
+ deny "Mishmar/Ira: CORS '*' origin together with credentials:true is forbidden by the CORS spec and leaks credentials. Pin explicit origins. (rules/common/security.md, OWASP API Top 10)"
148
+ fi
149
+
150
+ exit 0
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+ # MISHKAN SessionStart hook — DEFERRED (design §20, pending Claude Code stability).
3
+ #
4
+ # Intended behaviour: on a new context window, load sprint state from the
5
+ # project ./CLAUDE.md and query Cognee for active blockers, so Nehemiah greets
6
+ # with current context. Until the SessionStart hook event is validated as
7
+ # stable, this work lives in the /mishkan-resume command instead, and this
8
+ # script is NOT registered in settings.json.
9
+ #
10
+ # Kept here so that, once SessionStart is confirmed, wiring it is a one-line
11
+ # settings change rather than new development.
12
+ set -uo pipefail
13
+
14
+ PROJECT_CLAUDE="./CLAUDE.md"
15
+ ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
16
+
17
+ if [ -f "$PROJECT_CLAUDE" ] && grep -q "Cognee namespace" "$PROJECT_CLAUDE" 2>/dev/null; then
18
+ echo "mishkan/session-start: MISHKAN project detected at ${ts}. Run /mishkan-resume to restore sprint state." >&2
19
+ fi
20
+ exit 0
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env bash
2
+ # MISHKAN Stop hook — Team Reporter milestone trigger.
3
+ #
4
+ # Claude Code's Stop fires on main-agent stop; SubagentStop fires for subagents.
5
+ # Detecting "a Team Reporter is finishing" from the hook payload alone is not
6
+ # reliable, so this hook uses a marker file convention: a Reporter agent (or the
7
+ # /sprint-close command) touches ~/.claude/mishkan/logs/.reporter-active with the
8
+ # team name before assembling its report. This hook checks for that marker and,
9
+ # if present, signals the sprint-report skill to run, then clears the marker.
10
+ #
11
+ # Without the marker this hook is a no-op so it never interferes with ordinary
12
+ # session stops (which already play the finish sound via the existing Stop hook).
13
+ set -uo pipefail
14
+
15
+ MARKER="${HOME}/.claude/mishkan/logs/.reporter-active"
16
+
17
+ [ -f "$MARKER" ] || exit 0
18
+
19
+ team="$(cat "$MARKER" 2>/dev/null || echo unknown)"
20
+ ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
21
+
22
+ # Record that a reporter milestone assembly is due. The sprint-report skill,
23
+ # invoked by the reporter agent, performs the actual team-report.json assembly;
24
+ # this hook only emits the trigger breadcrumb and clears the marker.
25
+ echo "{\"event\":\"reporter_milestone\",\"team\":\"${team}\",\"timestamp\":\"${ts}\"}" \
26
+ >> "${HOME}/.claude/mishkan/logs/milestones.jsonl" 2>/dev/null
27
+
28
+ rm -f "$MARKER" 2>/dev/null
29
+ exit 0
@@ -0,0 +1,87 @@
1
+ # MISHKAN — Cognee Graph Ontology
2
+
3
+ The schema for the shared knowledge graph. Entity types are nodes; relationship
4
+ types are edges. The graph starts near-empty and grows through working sessions —
5
+ nothing is pre-seeded except the curated library (Phase 8).
6
+
7
+ This ontology is versioned in `harness/` so changes are reviewable. Schema drift
8
+ is expected; amend with a dated entry rather than rewriting silently.
9
+
10
+ ---
11
+
12
+ ## Entity types (nodes)
13
+
14
+ | Entity | Description | Key properties |
15
+ |---|---|---|
16
+ | **Agent** | One of the 45 MISHKAN agents | `alias`, `team`, `role`, `model_tier` |
17
+ | **Team** | One of the six teams | `name`, `hebrew`, `domain`, `lead_alias` |
18
+ | **Project** | A repo MISHKAN was initialised on | `name`, `path`, `stack`, `created_sprint` |
19
+ | **Sprint** | A delivery cycle within a project | `id` (S0, S1…), `project`, `started`, `closed`, `milestone` |
20
+ | **Task** | A scoped unit of work | `id` (T-N), `sprint`, `description`, `status`, `assigned_team`, `assigned_agent` |
21
+ | **Decision** | An architectural or scope decision | `id`, `made_by`, `sprint`, `summary`, `drivers`, `consequences`, `adr_ref` |
22
+ | **ResearchOutput** | A resolved research pipeline result | `id`, `agent`, `team`, `query_intent`, `summary`, `outcome`, `applied_to_task` |
23
+ | **CaseNode** | A problem solved using a curated resource | `team`, `agent`, `problem_class`, `resource_applied`, `resolution`, `outcome`, `sprint`, `task` |
24
+ | **CuratedResource** | A vetted professional reference | `name`, `url`, `team`, `problem_class`, `source_tier` |
25
+ | **Incident** | A documented failure + recovery | `id`, `date`, `service`, `root_causes[]`, `resolution`, `postmortem_ref` |
26
+ | **ADR** | Architecture Decision Record | `id`, `title`, `date`, `status`, `drivers`, `decision`, `consequences` |
27
+ | **RunbookProcedure** | An operational procedure | `id`, `title`, `service`, `trigger`, `steps_ref`, `status` |
28
+ | **SecurityFinding** | A finding from Mishmar | `id`, `severity`, `location`, `rule_violated`, `remediation`, `status`, `sprint` |
29
+ | **CuratedLibraryHit** | Telemetry: a curated resource was used | `resource`, `team`, `problem_class`, `sprint`, `count` |
30
+
31
+ ---
32
+
33
+ ## Relationship types (edges)
34
+
35
+ | Edge | From → To | Meaning |
36
+ |---|---|---|
37
+ | **member-of** | Agent → Team | agent belongs to team |
38
+ | **leads** | Agent → Team | agent is the team lead |
39
+ | **assigned-to** | Task → Agent / Team | who owns the task |
40
+ | **part-of** | Task → Sprint, Sprint → Project | hierarchy |
41
+ | **applies-to** | CuratedResource → Task / problem_class | resource is relevant here |
42
+ | **supersedes** | ADR → ADR, Decision → Decision, Doc → Doc | replaces a prior |
43
+ | **validated-by** | Decision → ResearchOutput / CaseNode | evidence backing a decision |
44
+ | **blocks** | Task → Task, SecurityFinding → Task | dependency / gate |
45
+ | **derives-from** | Decision → ResearchOutput, ADR → Decision | provenance |
46
+ | **escalated-to** | Task / Decision → Agent | who it was escalated to |
47
+ | **written-by** | ResearchOutput / Decision / CaseNode → Agent | authorship |
48
+ | **references** | any → any | loose citation link |
49
+ | **mitigates** | Decision / Task → SecurityFinding / Incident | what addresses a risk |
50
+ | **resolved-by** | Incident / SecurityFinding → Task / Decision | closure link |
51
+ | **uses** | CaseNode → CuratedResource | which resource solved the case |
52
+ | **documents** | ADR / RunbookProcedure → Decision / Incident / Task | doc covers this |
53
+
54
+ ---
55
+
56
+ ## Blast-radius tagging
57
+
58
+ Every node written to the graph carries a `blast_radius` property used by the
59
+ knowledge-promotion model (design §9):
60
+
61
+ - `agent-private` — stays in subagent MEMORY.md, not promoted to graph
62
+ (these generally do not reach Cognee at all).
63
+ - `team-level` — promoted by Team Lead; tagged with `team`.
64
+ - `cross-harness` — promoted by Nehemiah + Bezalel at sprint close; visible
65
+ to all agents.
66
+
67
+ Only `team-level` and `cross-harness` nodes live in Cognee. `agent-private`
68
+ knowledge stays in flat MEMORY.md files per the promotion model.
69
+
70
+ ---
71
+
72
+ ## Query patterns the improvement layer relies on
73
+
74
+ These saved queries (Phase 8) read the graph:
75
+
76
+ 1. **Most expensive agents per sprint** — `Agent` nodes with aggregated
77
+ observability metrics, ordered by cost.
78
+ 2. **Tools called most per team** — observability metrics grouped by `team`.
79
+ 3. **Blocker hot spots** — `Task` nodes with `blocks` edges, clustered.
80
+ 4. **Components accumulating findings** — `SecurityFinding` nodes grouped by
81
+ `location`.
82
+ 5. **Curated library hit rate per problem class** — `CuratedLibraryHit`
83
+ aggregated by `problem_class`, joined to `CuratedResource`.
84
+
85
+ ---
86
+
87
+ *Ontology v1, locked May 2026. Amend with dated entries on schema drift.*
@@ -0,0 +1,23 @@
1
+ ---
2
+ description: Yasad (Backend) rules — load on backend files
3
+ globs: ["**/*.py", "**/*.pyi", "**/*.ts", "**/*.go", "**/*.rs", "**/*.java", "**/*.kt", "**/*.sql", "**/*.prisma", "**/*.proto", "**/*.graphql", "**/api/**", "**/migrations/**", "**/alembic/**", "**/models/**", "**/schemas/**", "**/services/**", "**/repositories/**", "**/routers/**", "**/handlers/**", "**/middleware/**", "**/tests/**", "**/conftest.py", "**/pyproject.toml", "**/poetry.lock", "**/uv.lock", "**/requirements*.txt", "**/go.mod", "**/go.sum", "**/Cargo.toml", "**/Cargo.lock", "**/openapi*.{yaml,yml,json}", "**/asyncapi*.{yaml,yml,json}"]
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Yasad — Backend Rules
8
+
9
+ Load only on `.py`/`.ts`/`.go`/`.rs`/`api/**`. Owned by Zerubbabel (Team Lead).
10
+
11
+ - **OpenAPI 3.1 contract defined before any endpoint implementation.**
12
+ - **FastAPI (primary Python):** Pydantic v2 models for all request/response. Lifespan for startup/shutdown. asyncpg for PostgreSQL. Dependency injection for shared resources.
13
+ - **No raw SQL string formatting.** asyncpg parameters always.
14
+ - **Alembic for all schema migrations.** No manual `ALTER TABLE`.
15
+ - **Repository pattern** for data access — no ORM calls in route handlers.
16
+ - **LangGraph for stateful AI workflows** — not raw LangChain chains.
17
+ - **pydantic-settings** for environment config — not `os.environ` directly.
18
+ - **Hono (TS):** typed routes, Zod validation on all inputs.
19
+ - **NestJS (TS):** modules, providers, guards pattern — no God controllers.
20
+ - **Fastify (TS):** schema-validated routes.
21
+ - Alternate backends: Go, Rust, PHP/Laravel (working level).
22
+ - AI/ML layer: LangChain, LangGraph, HuggingFace Hub, OpenRouter, ChromaDB, sentence-transformers.
23
+ - Databases: PostgreSQL primary (indexing, query planning, extensions); MongoDB, DynamoDB also in use.
@@ -0,0 +1,53 @@
1
+ ---
2
+ description: Dependency management & supply-chain security — load on dependency manifests and lockfiles (Mishmar/Benaiah-owned)
3
+ globs: ["**/package.json", "**/pnpm-lock.yaml", "**/pnpm-workspace.yaml", "**/requirements*.txt", "**/pyproject.toml", "**/poetry.lock", "**/uv.lock", "**/Pipfile*", "**/go.mod", "**/go.sum", "**/Cargo.toml", "**/Cargo.lock", "**/composer.json", "**/composer.lock", "**/Gemfile*", "**/pom.xml", "**/build.gradle*", "**/*.csproj"]
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Dependency Management & Supply-Chain Security
8
+
9
+ Owned by Benaiah (supply-chain) with Ira at the code level. Loads whenever a
10
+ dependency manifest or lockfile is touched. Adding or upgrading a dependency is a
11
+ **security decision**, not a convenience.
12
+
13
+ ## Before adding or upgrading any dependency — vet it first
14
+
15
+ Run the **dependency-vetting** skill (drives the research pipeline). Do not adopt
16
+ a package until the following are checked and recorded in a research log:
17
+
18
+ 1. **Known vulnerabilities** — query OSV.dev / NVD for the exact version.
19
+ 2. **Maintenance health** — last release, open critical issues, single-maintainer
20
+ risk, deprecation status.
21
+ 3. **Typosquatting / impersonation** — confirm the package name and namespace are
22
+ the genuine, intended ones (a frequent supply-chain vector).
23
+ 4. **Provenance** — signed releases / SLSA level where available; source repo matches.
24
+ 5. **Transitive blast radius** — what it pulls in; new transitive risk.
25
+
26
+ If vetting is not clean, do not add it — surface the finding to the team lead.
27
+
28
+ ## Pinning & lockfiles
29
+
30
+ - **Pin exact versions.** No `"*"`, no `"latest"`, no unbounded ranges in manifests.
31
+ - **Commit the lockfile** and treat it as the source of truth. `pnpm-lock.yaml` for
32
+ JS/TS (pnpm only — never `package-lock.json`/`yarn.lock`), `poetry.lock`/`uv.lock`
33
+ for Python, `go.sum`, `Cargo.lock`, `composer.lock`.
34
+ - **Never hand-edit a lockfile.** Regenerate it via the package manager so the
35
+ integrity hashes stay valid.
36
+ - **CVE pin comment** — when a version is pinned to dodge a CVE, cite the CVE id inline.
37
+
38
+ ## Sources & integrity
39
+
40
+ - Only HTTPS package sources. No `http://`, no `git+http://`.
41
+ - No installing from arbitrary URLs or unpinned git refs (`git+https#<commit-sha>` only).
42
+ - Verify registry integrity / hashes are enabled (`pnpm` strict, pip hash-checking
43
+ where used).
44
+
45
+ ## Updates during the SDLC
46
+
47
+ - Staged upgrades, not blind bumps: use the **dependency-upgrade** skill —
48
+ compatibility analysis, changelog/breaking-change review, test before merge.
49
+ - Group security patches separately from feature bumps.
50
+ - Re-vet (steps 1–5) on every **major** version change and on any maintainer/owner
51
+ change of the package.
52
+ - Dependency scanning (`trivy`, OSV-Scanner) runs in CI as a gate (Ira), and the
53
+ `security:scan` stage blocks merge on a new critical/high.
@@ -0,0 +1,16 @@
1
+ ---
2
+ description: MISHKAN common quality rules — apply to all files (Bezalel-owned)
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Common Quality Rules
7
+
8
+ Owned by Bezalel. Apply to all files.
9
+
10
+ - **No commented-out code** in commits.
11
+ - **No TODO without an owner and a linked task.**
12
+ - **No magic numbers** — use named constants.
13
+ - **Tests required** for any business logic.
14
+ - **Diagnose before fix** — root cause documented before solution implemented.
15
+ - **Two root causes** on any non-trivial failure.
16
+ - **OpenAPI 3.1 contract before implementation** on any API endpoint.
@@ -0,0 +1,20 @@
1
+ ---
2
+ description: MISHKAN common security rules — apply to all files (Mishmar-owned)
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Common Security Rules
7
+
8
+ Owned by Mishmar. Apply to all files. Security is a constraint shaping output
9
+ from the start, not an audit at the end.
10
+
11
+ - **No hardcoded secrets.** No passwords, tokens, API keys, or connection strings in source.
12
+ - **No `eval()`** or equivalent dynamic code execution from untrusted input.
13
+ - **No SQL string concatenation.** Parameterised queries always (asyncpg parameters, ORM bindings).
14
+ - **Input validation** on every API boundary — validate type, range, and shape before use.
15
+ - **Output encoding** for all user-facing content (prevent XSS, injection in rendered output).
16
+ - **Rate limiting** on all public API endpoints.
17
+ - **Session security middleware** always present on stateful services.
18
+ - **SOPS + age** for secret management. Never commit plaintext `.env` to version control.
19
+ - **Hardening overlay** re-applied on every container recreate — not optional, not one-time.
20
+ - **Keycloak SSO** as the identity source for any multi-service system.
@@ -0,0 +1,19 @@
1
+ ---
2
+ description: Sefer (Documentation) rules — load on docs files
3
+ globs: ["**/docs/**", "**/*.md", "**/*.mdx", "**/*.rst", "**/*.adoc", "**/adr/**", "**/rfc/**", "**/runbooks/**", "**/diagrams/**", "**/CHANGELOG.md", "**/CHANGES.md", "**/README.md", "**/CONTRIBUTING.md", "**/SECURITY.md", "**/ARCHITECTURE.md", "**/mkdocs.y*ml", "**/docusaurus.config.*", "**/.github/ISSUE_TEMPLATE/**", "**/.github/PULL_REQUEST_TEMPLATE*"]
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Sefer — Documentation Rules
8
+
9
+ Load only on `docs/**` and markdown. Owned by Jehoshaphat (Team Lead).
10
+
11
+ - **Diátaxis quadrant declared** on every doc: Tutorial / How-to / Reference / Explanation.
12
+ - **ADR format: MADR template.** Decision drivers explicit. Consequences documented (positive/negative/risk).
13
+ - **Changelog: Keep a Changelog format.** Semantic versioning.
14
+ - **Commit messages feed the changelog** — Conventional Commits convention.
15
+ - **README: 50–150 lines.** Written for builders, not end users. Terse.
16
+ - **Design documents: 300–800 lines.** Heavy. Future-engineer audience.
17
+ - **Runbooks: copy-paste safe under stress.** One command per failure mode, no thinking required at execution time.
18
+ - **No documentation without a date.** No undated decisions.
19
+ - Sefer writes to `docs/` only — never to the codebase.
@@ -0,0 +1,21 @@
1
+ ---
2
+ description: Panim (Frontend) rules — load on frontend files
3
+ globs: ["**/*.tsx", "**/*.jsx", "**/*.ts", "**/*.js", "**/*.mjs", "**/*.cjs", "**/*.vue", "**/*.svelte", "**/*.astro", "**/*.css", "**/*.scss", "**/*.sass", "**/*.less", "**/*.html", "**/*.webmanifest", "**/components/**", "**/pages/**", "**/layouts/**", "**/composables/**", "**/stores/**", "**/hooks/**", "**/*.stories.*", "**/*.spec.{ts,tsx,js,jsx}", "**/*.test.{ts,tsx,js,jsx}", "**/.storybook/**", "**/vite.config.*", "**/vitest.config.*", "**/playwright.config.*", "**/tailwind.config.*", "**/postcss.config.*", "**/nuxt.config.*", "**/next.config.*", "**/.eslintrc*", "**/eslint.config.*"]
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Panim — Frontend Rules
8
+
9
+ Load only on `.tsx`/`.jsx`/`.vue`/`.css`/`.html`. Owned by Huram (Team Lead).
10
+
11
+ - **pnpm only.** No `package-lock.json` or `yarn.lock` committed.
12
+ - **TailwindCSS utility classes** — no arbitrary CSS unless justified in a comment.
13
+ - **WCAG 2.2 AA minimum** on all interactive components.
14
+ - **Core Web Vitals budgets:** LCP < 2.5s, INP < 200ms, CLS < 0.1.
15
+ - **TanStack Query** for all data fetching — no raw `fetch` in components.
16
+ - **TanStack Router** for routing — no `react-router` unless maintaining legacy.
17
+ - **Component co-location** — component, test, and story in the same directory.
18
+ - **No inline styles. No `!important`.**
19
+ - **WAI-ARIA roles and labels** on all interactive elements.
20
+ - **Vercel deployment config** present for frontend projects.
21
+ - Stack: HTML/CSS/Tailwind, JS/TS, React, TanStack, Vite, Storybook; Nuxt 3 / Vue 3 where used.
@@ -0,0 +1,22 @@
1
+ ---
2
+ description: Migdal (Infrastructure) rules — load on infra files
3
+ globs: ["**/Dockerfile*", "**/*.dockerfile", "**/.dockerignore", "**/docker-compose*", "**/compose*.y*ml", "**/*.tf", "**/*.tfvars", "**/*.hcl", "**/*.yaml", "**/*.yml", "**/*.sh", "**/*.bash", "**/*.conf", "**/*.cnf", "**/*.ini", "**/*.toml", "**/*.service", "**/*.timer", "**/*.tpl", "**/Makefile", "**/Justfile", "**/Caddyfile", "**/.gitlab-ci.yml", "**/.gitlab/**", "**/infra/**", "**/deploy/**", "**/ops/**", "**/ansible/**", "**/playbooks/**", "**/helm/**", "**/charts/**", "**/k8s/**", "**/kustomize/**", "**/kustomization*", "**/.github/workflows/**", "**/nginx*", "**/traefik*", "**/prometheus*", "**/grafana/**"]
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Migdal — Infrastructure Rules
8
+
9
+ Load only on `Dockerfile`/`docker-compose*`/`*.tf`/`*.yaml`/`infra/**`. Owned by Eliashib (Team Lead).
10
+
11
+ - **No `:latest` tags.** Pin all image versions.
12
+ - **All resources tagged** with environment, owner, project.
13
+ - **Least privilege** — no root processes in containers unless strictly required.
14
+ - **Hardening overlay always applied** — not optional on recreate.
15
+ - **Hash-based drift detection** on docker-compose changes (sha256 diff triggers recreate).
16
+ - **SOPS-encrypted secrets** — no plaintext `.env` files committed.
17
+ - **Traefik as reverse proxy** — nginx as static/fallback only.
18
+ - **GitLab CI:** environment scoping on all jobs. Protected branches gate production deploys. Runner-to-runtime SSH patterns documented.
19
+ - **Health checks on every service.** Idempotent recreate logic.
20
+ - **Ansible for configuration management** — no manual server config.
21
+ - Orchestration: Docker Compose primary; Kubernetes where scale requires. Terraform for declarative IaC. AWS + GCP.
22
+ - Observability: Prometheus, Grafana, Loki, Sentry, GlitchTip. OpenTelemetry for instrumentation.
@@ -0,0 +1,171 @@
1
+ #!/usr/bin/env bash
2
+ # MISHKAN — cross-project dependency audit.
3
+ # Reads config/projects.yaml, inventories manifests across every project root,
4
+ # parses declared dependencies, computes cross-project shared packages + version
5
+ # drift, and (where OSV-Scanner / trivy are installed) collects vulnerabilities
6
+ # per project. Writes a portfolio report under logs/.
7
+ #
8
+ # Read-only: never installs, never edits manifests. Prepares the picture; the
9
+ # dependency-audit skill turns it into a coordinated, vetted update plan.
10
+ set -uo pipefail
11
+
12
+ MISHKAN="${HOME}/.claude/mishkan"
13
+ REG="${MISHKAN}/config/projects.yaml"
14
+ TS="$(date -u +%Y%m%dT%H%M%SZ)"
15
+ OUT="${MISHKAN}/logs/dep-audit-${TS}.json"
16
+
17
+ command -v python3 >/dev/null 2>&1 || { echo "python3 required" >&2; exit 1; }
18
+ [ -f "$REG" ] || { echo "project registry not found: $REG" >&2; exit 1; }
19
+
20
+ OSV_BIN="$(command -v osv-scanner || true)"
21
+ TRIVY_BIN="$(command -v trivy || true)"
22
+ [ -n "$OSV_BIN" ] && echo "osv-scanner: $OSV_BIN" || echo "osv-scanner: not installed (skipping CVE scan; inventory + drift still produced)"
23
+ [ -n "$TRIVY_BIN" ] && echo "trivy: $TRIVY_BIN" || echo "trivy: not installed"
24
+
25
+ OSV_BIN="$OSV_BIN" TRIVY_BIN="$TRIVY_BIN" python3 - "$REG" "$OUT" <<'PY'
26
+ import sys, os, json, glob, re, subprocess
27
+ from collections import defaultdict
28
+ try:
29
+ import yaml
30
+ except ImportError:
31
+ sys.exit("pyyaml required: pip install pyyaml")
32
+
33
+ reg_path, out_path = sys.argv[1], sys.argv[2]
34
+ reg = yaml.safe_load(open(reg_path)) or {}
35
+ manifest_globs = reg.get("manifest_globs", [])
36
+ exclude = set(reg.get("exclude_dirs", []))
37
+
38
+ def expand(p):
39
+ return os.path.expanduser(os.path.expandvars(p)) if p else p
40
+
41
+ # Resolve project roots portably: explicit override > discovery under workspace.
42
+ roots = [expand(r) for r in (reg.get("project_roots") or []) if r]
43
+ if not roots:
44
+ ws = os.environ.get("MISHKAN_WORKSPACE") or expand(reg.get("workspace_root") or "")
45
+ if not ws:
46
+ ws = os.path.dirname(os.getcwd()) # cwd's parent
47
+ # discover git repositories under the workspace root (one level of nesting)
48
+ found = []
49
+ if os.path.isdir(ws):
50
+ for entry in sorted(os.listdir(ws)):
51
+ full = os.path.join(ws, entry)
52
+ if os.path.isdir(os.path.join(full, ".git")):
53
+ found.append(full)
54
+ roots = found
55
+ print(f"discovery: workspace={ws} -> {len(roots)} git repos", file=sys.stderr)
56
+ osv = os.environ.get("OSV_BIN") or None
57
+ trivy = os.environ.get("TRIVY_BIN") or None
58
+
59
+ def find_manifests(root):
60
+ hits = []
61
+ for dirpath, dirnames, filenames in os.walk(root):
62
+ dirnames[:] = [d for d in dirnames if d not in exclude]
63
+ for fn in filenames:
64
+ for g in manifest_globs:
65
+ # simple glob match on filename
66
+ if glob.fnmatch.fnmatch(fn, g):
67
+ hits.append(os.path.join(dirpath, fn)); break
68
+ return hits
69
+
70
+ def parse_deps(path):
71
+ """Return {package: version_or_range} best-effort across ecosystems."""
72
+ deps = {}
73
+ fn = os.path.basename(path)
74
+ try:
75
+ if fn == "package.json":
76
+ d = json.load(open(path))
77
+ for k in ("dependencies", "devDependencies", "peerDependencies"):
78
+ deps.update({n: v for n, v in (d.get(k) or {}).items()})
79
+ elif fn.startswith("requirements") and fn.endswith(".txt"):
80
+ for line in open(path):
81
+ line = line.strip()
82
+ if not line or line.startswith("#"): continue
83
+ m = re.match(r"^([A-Za-z0-9_.\-]+)\s*([=<>!~]=?.*)?$", line)
84
+ if m: deps[m.group(1).lower()] = (m.group(2) or "").strip()
85
+ elif fn == "pyproject.toml":
86
+ txt = open(path).read()
87
+ for m in re.finditer(r'"([A-Za-z0-9_.\-]+)\s*([=<>!~][^"]*)?"', txt):
88
+ deps[m.group(1).lower()] = (m.group(2) or "").strip()
89
+ elif fn == "go.mod":
90
+ for m in re.finditer(r'^\s*([^\s]+/[^\s]+)\s+(v[0-9][^\s]*)', open(path).read(), re.M):
91
+ deps[m.group(1)] = m.group(2)
92
+ elif fn == "Cargo.toml":
93
+ in_deps = False
94
+ for line in open(path):
95
+ s = line.strip()
96
+ if s.startswith("["):
97
+ in_deps = s.startswith("[dependencies") or s.startswith("[dev-dependencies")
98
+ continue
99
+ if in_deps:
100
+ m = re.match(r'^([A-Za-z0-9_\-]+)\s*=\s*"?([^"\n]*)"?', s)
101
+ if m: deps[m.group(1)] = m.group(2).strip()
102
+ elif fn in ("composer.json",):
103
+ d = json.load(open(path))
104
+ for k in ("require", "require-dev"):
105
+ deps.update({n: v for n, v in (d.get(k) or {}).items()})
106
+ except Exception as e:
107
+ deps["_parse_error"] = str(e)
108
+ return deps
109
+
110
+ def run_osv(root):
111
+ if not osv: return None
112
+ try:
113
+ r = subprocess.run([osv, "--format", "json", "-r", root],
114
+ capture_output=True, text=True, timeout=180)
115
+ return json.loads(r.stdout) if r.stdout.strip() else {}
116
+ except Exception as e:
117
+ return {"_error": str(e)}
118
+
119
+ # pkg -> {project: version}
120
+ pkg_projects = defaultdict(dict)
121
+ project_inventory = {}
122
+ osv_results = {}
123
+
124
+ for root in roots:
125
+ name = os.path.basename(root.rstrip("/"))
126
+ if not os.path.isdir(root):
127
+ project_inventory[name] = {"present": False}
128
+ continue
129
+ manifests = find_manifests(root)
130
+ declared = {}
131
+ for mpath in manifests:
132
+ for pkg, ver in parse_deps(mpath).items():
133
+ if pkg.startswith("_"): continue
134
+ declared[pkg] = ver
135
+ pkg_projects[pkg][name] = ver
136
+ project_inventory[name] = {"present": True,
137
+ "manifests": [os.path.relpath(m, root) for m in manifests],
138
+ "declared_count": len(declared)}
139
+ res = run_osv(root)
140
+ if res is not None:
141
+ osv_results[name] = res
142
+
143
+ # cross-project: packages used in >1 project, and version drift
144
+ shared = {}
145
+ drift = {}
146
+ for pkg, byproj in pkg_projects.items():
147
+ if len(byproj) > 1:
148
+ shared[pkg] = byproj
149
+ versions = set(v for v in byproj.values() if v)
150
+ if len(versions) > 1:
151
+ drift[pkg] = byproj
152
+
153
+ report = {
154
+ "audit_date": out_path.split("dep-audit-")[-1].replace(".json", ""),
155
+ "scanners": {"osv_scanner": bool(osv), "trivy": bool(trivy)},
156
+ "projects_scanned": [n for n, v in project_inventory.items() if v.get("present")],
157
+ "projects_missing": [n for n, v in project_inventory.items() if not v.get("present")],
158
+ "inventory": project_inventory,
159
+ "shared_packages_count": len(shared),
160
+ "version_drift": drift,
161
+ "shared_packages": shared,
162
+ "osv_results_present": list(osv_results.keys()),
163
+ "note": "CVE detail in osv_results requires osv-scanner installed; otherwise this is inventory + drift only. Feed into the dependency-audit skill for prioritisation + vetted update plan.",
164
+ }
165
+ json.dump(report, open(out_path, "w"), indent=2)
166
+ print(f"audited {len(report['projects_scanned'])} projects -> {out_path}")
167
+ print(f"shared packages (>1 project): {len(shared)}; version drift: {len(drift)}")
168
+ PY
169
+
170
+ echo "Report written. Run the dependency-audit skill to prioritise (severity x blast"
171
+ echo "radius), vet target versions, and produce the coordinated update plan."