@indigoai-us/hq-cli 5.77.13 → 5.78.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 (307) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/assets/scaffold/core/scripts/archive-old-threads.sh +86 -0
  3. package/assets/scaffold/core/scripts/backfill-company-skill-mirrors.sh +73 -0
  4. package/assets/scaffold/core/scripts/backfill-workspace-mirror.sh +51 -0
  5. package/assets/scaffold/core/scripts/codex-skill-bridge.sh +550 -0
  6. package/assets/scaffold/core/scripts/detect-stale-core-policy-mirror.sh +185 -0
  7. package/assets/scaffold/core/scripts/hq-status-summary.sh +187 -0
  8. package/assets/scaffold/core/scripts/ontology-readme-drift.sh +264 -0
  9. package/assets/scaffold/core/scripts/qmd-reindex-after-sync.sh +93 -0
  10. package/assets/scaffold/core/scripts/rebuild-all-indexes.sh +69 -0
  11. package/assets/scaffold/core/scripts/rebuild-companies-index.sh +206 -0
  12. package/assets/scaffold/core/scripts/rebuild-company-knowledge-index.sh +127 -0
  13. package/assets/scaffold/core/scripts/rebuild-orchestrator-index.sh +86 -0
  14. package/assets/scaffold/core/scripts/rebuild-projects-index.sh +105 -0
  15. package/assets/scaffold/core/scripts/rebuild-public-knowledge-index.sh +83 -0
  16. package/assets/scaffold/core/scripts/rebuild-reports-index.sh +114 -0
  17. package/assets/scaffold/core/scripts/rebuild-social-drafts-index.sh +135 -0
  18. package/assets/scaffold/core/scripts/rebuild-threads-index.sh +82 -0
  19. package/assets/scaffold/core/scripts/rebuild-workers-index.sh +97 -0
  20. package/assets/scaffold/core/scripts/resize-screenshot.sh +47 -0
  21. package/assets/scaffold/core/scripts/token-usage-report.sh +254 -0
  22. package/assets/scaffold/core/scripts/worktree.sh +136 -0
  23. package/dist/commands/agents.js +28 -12
  24. package/dist/commands/api-keys.js +100 -20
  25. package/dist/commands/core.d.ts +90 -0
  26. package/dist/commands/core.js +279 -0
  27. package/dist/commands/files-browse.d.ts +21 -0
  28. package/dist/commands/files-browse.js +145 -24
  29. package/dist/commands/files.js +33 -7
  30. package/dist/commands/members.js +16 -5
  31. package/dist/commands/pack-install.d.ts +14 -0
  32. package/dist/commands/pack-install.js +51 -2
  33. package/dist/commands/pkg-install.js +43 -0
  34. package/dist/commands/run.js +8 -0
  35. package/dist/commands/secrets.js +125 -65
  36. package/dist/main.js +6 -0
  37. package/dist/utils/api-key-command-gate.d.ts +45 -0
  38. package/dist/utils/api-key-command-gate.js +51 -0
  39. package/dist/utils/hq-roots.d.ts +91 -0
  40. package/dist/utils/hq-roots.js +212 -0
  41. package/dist/utils/resolve-vault-credential.d.ts +63 -3
  42. package/dist/utils/resolve-vault-credential.js +152 -6
  43. package/dist/utils/run-bundled-script.d.ts +66 -0
  44. package/dist/utils/run-bundled-script.js +80 -0
  45. package/dist/utils/vault-api.d.ts +21 -0
  46. package/dist/utils/vault-api.js +74 -3
  47. package/package.json +7 -1
  48. package/.claude/policies/hq-cli-interactive-reads-use-sts-vend.md +0 -49
  49. package/.github/workflows/ci.yml +0 -46
  50. package/.github/workflows/publish.yml +0 -36
  51. package/.github/workflows/scripts/smoke-test-pkg.sh +0 -97
  52. package/e2e/cli.test.ts +0 -128
  53. package/eslint.config.js +0 -37
  54. package/pnpm-workspace.yaml +0 -4
  55. package/scripts/chmod-bins.mjs +0 -49
  56. package/scripts/generate-dsn.mjs +0 -26
  57. package/scripts/generate-scan-packages-table.mjs +0 -113
  58. package/scripts/smoke-sources-signals.sh +0 -103
  59. package/src/bin/hq-auth-refresh.ts +0 -43
  60. package/src/cli-version.ts +0 -13
  61. package/src/commands/__fixtures__/make-tar.ts +0 -126
  62. package/src/commands/_patterns.ts +0 -10
  63. package/src/commands/add.ts +0 -75
  64. package/src/commands/agents.test.ts +0 -527
  65. package/src/commands/agents.ts +0 -923
  66. package/src/commands/api-keys.test.ts +0 -291
  67. package/src/commands/api-keys.ts +0 -382
  68. package/src/commands/artifact-verify.test.ts +0 -177
  69. package/src/commands/auth.test.ts +0 -82
  70. package/src/commands/auth.ts +0 -213
  71. package/src/commands/billing.test.ts +0 -158
  72. package/src/commands/billing.ts +0 -146
  73. package/src/commands/channels.test.ts +0 -74
  74. package/src/commands/channels.ts +0 -91
  75. package/src/commands/cloud-demote.test.ts +0 -401
  76. package/src/commands/cloud-demote.ts +0 -278
  77. package/src/commands/cloud-provision.test.ts +0 -1117
  78. package/src/commands/cloud-provision.ts +0 -942
  79. package/src/commands/cloud.pull-all.test.ts +0 -711
  80. package/src/commands/cloud.pull-per-company.test.ts +0 -188
  81. package/src/commands/cloud.push-all.test.ts +0 -485
  82. package/src/commands/cloud.scope-excluded-warning.test.ts +0 -22
  83. package/src/commands/cloud.selectors.test.ts +0 -208
  84. package/src/commands/cloud.status.test.ts +0 -160
  85. package/src/commands/cloud.test.ts +0 -41
  86. package/src/commands/cloud.ts +0 -2130
  87. package/src/commands/company.test.ts +0 -202
  88. package/src/commands/company.ts +0 -148
  89. package/src/commands/creators.test.ts +0 -60
  90. package/src/commands/creators.ts +0 -117
  91. package/src/commands/crm.test.ts +0 -179
  92. package/src/commands/crm.ts +0 -236
  93. package/src/commands/db-migrate.ts +0 -55
  94. package/src/commands/db-provision.ts +0 -114
  95. package/src/commands/db-sql.ts +0 -124
  96. package/src/commands/db-status.ts +0 -108
  97. package/src/commands/db.ts +0 -26
  98. package/src/commands/dm.test.ts +0 -748
  99. package/src/commands/dm.ts +0 -1226
  100. package/src/commands/feedback.test.ts +0 -413
  101. package/src/commands/feedback.ts +0 -169
  102. package/src/commands/files-browse.test.ts +0 -1249
  103. package/src/commands/files-browse.ts +0 -1506
  104. package/src/commands/files-delete.test.ts +0 -484
  105. package/src/commands/files-recovery.test.ts +0 -370
  106. package/src/commands/files.test.ts +0 -722
  107. package/src/commands/files.ts +0 -1446
  108. package/src/commands/group-grants.test.ts +0 -330
  109. package/src/commands/group-grants.ts +0 -455
  110. package/src/commands/groups.test.ts +0 -44
  111. package/src/commands/groups.ts +0 -368
  112. package/src/commands/integrations.test.ts +0 -633
  113. package/src/commands/integrations.ts +0 -507
  114. package/src/commands/list.ts +0 -85
  115. package/src/commands/login.ts +0 -52
  116. package/src/commands/logout.ts +0 -30
  117. package/src/commands/marketplace-install.test.ts +0 -543
  118. package/src/commands/marketplace-security.test.ts +0 -646
  119. package/src/commands/mcp-registration.test.ts +0 -2787
  120. package/src/commands/mcp-registration.ts +0 -2612
  121. package/src/commands/mcp-status.test.ts +0 -483
  122. package/src/commands/mcp-status.ts +0 -575
  123. package/src/commands/mcp-status.us011.test.ts +0 -243
  124. package/src/commands/meetings.test.ts +0 -522
  125. package/src/commands/meetings.ts +0 -789
  126. package/src/commands/members.test.ts +0 -1474
  127. package/src/commands/members.ts +0 -1181
  128. package/src/commands/onboard-identity-guard.test.ts +0 -99
  129. package/src/commands/onboard-identity-guard.ts +0 -103
  130. package/src/commands/onboard-join.test.ts +0 -96
  131. package/src/commands/onboard-join.ts +0 -101
  132. package/src/commands/onboard-warning.test.ts +0 -26
  133. package/src/commands/onboard-warning.ts +0 -12
  134. package/src/commands/onboard.ts +0 -267
  135. package/src/commands/outposts-heartbeat.test.ts +0 -299
  136. package/src/commands/outposts-heartbeat.ts +0 -310
  137. package/src/commands/outposts-replica-sync.test.ts +0 -272
  138. package/src/commands/outposts-self-deploy.test.ts +0 -243
  139. package/src/commands/outposts.test.ts +0 -1039
  140. package/src/commands/outposts.ts +0 -1935
  141. package/src/commands/pack-install-secret-authorization.test.ts +0 -115
  142. package/src/commands/pack-install.test.ts +0 -1469
  143. package/src/commands/pack-install.ts +0 -2247
  144. package/src/commands/pack-update-cache.test.ts +0 -149
  145. package/src/commands/packs.test.ts +0 -88
  146. package/src/commands/packs.ts +0 -622
  147. package/src/commands/people.test.ts +0 -807
  148. package/src/commands/people.ts +0 -422
  149. package/src/commands/pkg-install.dispatch.test.ts +0 -71
  150. package/src/commands/pkg-install.ts +0 -215
  151. package/src/commands/pkg-list.test.ts +0 -79
  152. package/src/commands/pkg-list.ts +0 -13
  153. package/src/commands/pkg-remove.ts +0 -71
  154. package/src/commands/pkg-update.ts +0 -189
  155. package/src/commands/publish.test.ts +0 -650
  156. package/src/commands/publish.ts +0 -580
  157. package/src/commands/reindex.test.ts +0 -259
  158. package/src/commands/reindex.ts +0 -274
  159. package/src/commands/rescue.test.ts +0 -39
  160. package/src/commands/rescue.ts +0 -210
  161. package/src/commands/run.env-local.test.ts +0 -88
  162. package/src/commands/run.test.ts +0 -45
  163. package/src/commands/run.ts +0 -184
  164. package/src/commands/safe-extract.test.ts +0 -459
  165. package/src/commands/safe-extract.ts +0 -444
  166. package/src/commands/secrets-scope.test.ts +0 -56
  167. package/src/commands/secrets-scope.ts +0 -32
  168. package/src/commands/secrets.parse-destination.test.ts +0 -38
  169. package/src/commands/secrets.test.ts +0 -2037
  170. package/src/commands/secrets.ts +0 -2240
  171. package/src/commands/signals.ts +0 -351
  172. package/src/commands/skill.test.ts +0 -770
  173. package/src/commands/skill.ts +0 -796
  174. package/src/commands/sources.ts +0 -362
  175. package/src/commands/sync-mode.test.ts +0 -366
  176. package/src/commands/sync-mode.ts +0 -387
  177. package/src/commands/sync-narrow.test.ts +0 -633
  178. package/src/commands/sync-narrow.ts +0 -555
  179. package/src/commands/sync.ts +0 -165
  180. package/src/commands/team-sync.ts +0 -628
  181. package/src/commands/update.ts +0 -71
  182. package/src/commands/whoami.ts +0 -83
  183. package/src/commands/workers.test.ts +0 -158
  184. package/src/commands/workers.ts +0 -301
  185. package/src/index.test.ts +0 -32
  186. package/src/index.ts +0 -20
  187. package/src/lib/db/control-plane.test.ts +0 -59
  188. package/src/lib/db/control-plane.ts +0 -113
  189. package/src/lib/db/local.test.ts +0 -81
  190. package/src/lib/db/local.ts +0 -148
  191. package/src/lib/db/migrate.test.ts +0 -133
  192. package/src/lib/db/migrate.ts +0 -137
  193. package/src/lib/db/paths.test.ts +0 -112
  194. package/src/lib/db/paths.ts +0 -128
  195. package/src/lib/db/remote-engine.test.ts +0 -44
  196. package/src/lib/db/remote-engine.ts +0 -148
  197. package/src/lib/db/remote-sql.test.ts +0 -32
  198. package/src/lib/db/remote-sql.ts +0 -62
  199. package/src/lib/db/sql.test.ts +0 -106
  200. package/src/lib/db/sql.ts +0 -192
  201. package/src/lib/hq-cloud-dep.smoke.test.ts +0 -75
  202. package/src/lib/local-tree-diff.test.ts +0 -301
  203. package/src/lib/local-tree-diff.ts +0 -362
  204. package/src/lib/narrow-hint-banner.test.ts +0 -235
  205. package/src/lib/narrow-hint-banner.ts +0 -212
  206. package/src/lib/plan-limit-nag.test.ts +0 -317
  207. package/src/lib/plan-limit-nag.ts +0 -264
  208. package/src/main.ts +0 -371
  209. package/src/node-preflight.test.ts +0 -60
  210. package/src/node-preflight.ts +0 -67
  211. package/src/outpost/session-heartbeat-bounds.test.ts +0 -195
  212. package/src/outpost/session-heartbeat-guard.test.ts +0 -105
  213. package/src/outpost/session-heartbeat-publisher.test.ts +0 -178
  214. package/src/outpost/session-heartbeat-publisher.ts +0 -186
  215. package/src/outpost/session-heartbeat-retain-guard.test.ts +0 -126
  216. package/src/outpost/session-heartbeat.test.ts +0 -459
  217. package/src/outpost/session-heartbeat.ts +0 -877
  218. package/src/packaging.test.ts +0 -109
  219. package/src/run/__fixtures__/discover-schemas/example.env.schema +0 -4
  220. package/src/run/discover-schemas.test.ts +0 -153
  221. package/src/run/discover-schemas.ts +0 -79
  222. package/src/run/hq-plugin.test.ts +0 -371
  223. package/src/run/hq-plugin.ts +0 -277
  224. package/src/run/varlock-shape.test.ts +0 -57
  225. package/src/schemas/hq-package.schema.json +0 -155
  226. package/src/sentry-before-send.test.ts +0 -359
  227. package/src/sentry-before-send.ts +0 -221
  228. package/src/sentry-epipe.test.ts +0 -37
  229. package/src/sentry-release.test.ts +0 -54
  230. package/src/sentry.test.ts +0 -24
  231. package/src/sentry.ts +0 -54
  232. package/src/strategies/link.ts +0 -62
  233. package/src/strategies/merge.ts +0 -142
  234. package/src/types.ts +0 -146
  235. package/src/utils/__fixtures__/scan-packages.generated-block.sh +0 -23
  236. package/src/utils/auth-error.test.ts +0 -40
  237. package/src/utils/auth-error.ts +0 -42
  238. package/src/utils/billing-gate.test.ts +0 -95
  239. package/src/utils/billing-gate.ts +0 -228
  240. package/src/utils/breadcrumb-buffer.ts +0 -18
  241. package/src/utils/cli-telemetry.test.ts +0 -153
  242. package/src/utils/cli-telemetry.ts +0 -61
  243. package/src/utils/cognito-session.machine.test.ts +0 -109
  244. package/src/utils/cognito-session.refresh.test.ts +0 -84
  245. package/src/utils/cognito-session.test.ts +0 -328
  246. package/src/utils/cognito-session.ts +0 -448
  247. package/src/utils/company-selection-error.test.ts +0 -42
  248. package/src/utils/company-selection-error.ts +0 -45
  249. package/src/utils/contribution-table.ts +0 -83
  250. package/src/utils/environmental-error.test.ts +0 -45
  251. package/src/utils/environmental-error.ts +0 -39
  252. package/src/utils/epipe.test.ts +0 -28
  253. package/src/utils/epipe.ts +0 -29
  254. package/src/utils/expected-cli-error.test.ts +0 -28
  255. package/src/utils/expected-cli-error.ts +0 -39
  256. package/src/utils/feedback-diagnostics.test.ts +0 -183
  257. package/src/utils/feedback-diagnostics.ts +0 -123
  258. package/src/utils/feedback-screenshots.test.ts +0 -134
  259. package/src/utils/feedback-screenshots.ts +0 -124
  260. package/src/utils/feedback-versions.test.ts +0 -98
  261. package/src/utils/feedback-versions.ts +0 -68
  262. package/src/utils/git.ts +0 -74
  263. package/src/utils/integrity.ts +0 -59
  264. package/src/utils/intercepted-process-exit.test.ts +0 -37
  265. package/src/utils/intercepted-process-exit.ts +0 -36
  266. package/src/utils/login-provider.test.ts +0 -42
  267. package/src/utils/login-provider.ts +0 -30
  268. package/src/utils/manifest.test.ts +0 -167
  269. package/src/utils/manifest.ts +0 -131
  270. package/src/utils/pack-contributions.test.ts +0 -603
  271. package/src/utils/pack-contributions.ts +0 -450
  272. package/src/utils/people.ts +0 -215
  273. package/src/utils/registry-client.ts +0 -204
  274. package/src/utils/registry.ts +0 -90
  275. package/src/utils/resolve-vault-credential.test.ts +0 -69
  276. package/src/utils/resolve-vault-credential.ts +0 -60
  277. package/src/utils/sandbox-runner-client.test.ts +0 -279
  278. package/src/utils/sandbox-runner-client.ts +0 -260
  279. package/src/utils/secrets-cache.ts +0 -183
  280. package/src/utils/sentry-identity.ts +0 -45
  281. package/src/utils/settle-with-timeout.test.ts +0 -21
  282. package/src/utils/settle-with-timeout.ts +0 -22
  283. package/src/utils/user-yaml-error.test.ts +0 -24
  284. package/src/utils/user-yaml-error.ts +0 -30
  285. package/src/utils/vault-api.test.ts +0 -536
  286. package/src/utils/vault-api.ts +0 -382
  287. package/src/utils/version-check.test.ts +0 -176
  288. package/src/utils/version-check.ts +0 -155
  289. package/src/utils/version-gate.test.ts +0 -1024
  290. package/src/utils/version-gate.ts +0 -702
  291. package/test/commands/db-tenant-isolation.test.ts +0 -94
  292. package/test/commands/db.test.ts +0 -85
  293. package/test/commands/signals.test.ts +0 -207
  294. package/test/commands/sources.test.ts +0 -231
  295. package/test/e2e/smoke-install-mcp.sh +0 -113
  296. package/test/fixtures/hq-pack-smoke-mcp/mcp/smoke-http.json +0 -1
  297. package/test/fixtures/hq-pack-smoke-mcp/package.yaml +0 -11
  298. package/test/fixtures/signals/action_item/sample.md +0 -16
  299. package/test/fixtures/signals/summary/sample.md +0 -12
  300. package/test/fixtures/sources/meetings/sample.md +0 -25
  301. package/test/helpers/cli-runner.ts +0 -150
  302. package/test/helpers/s3-list-mock.ts +0 -79
  303. package/test/helpers/vault-service-mock.ts +0 -295
  304. package/test/sources-signals/smoke.test.ts +0 -228
  305. package/tsconfig.json +0 -20
  306. package/vitest.config.ts +0 -11
  307. package/vitest.e2e.config.ts +0 -7
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env bash
2
+ # qmd-reindex-after-sync.sh — incremental qmd reindex after an HQ sync.
3
+ #
4
+ # Why this exists: the qmd index is a per-machine local SQLite store derived
5
+ # from local folder paths. Nothing re-indexed it after `hq-sync` pulled new
6
+ # files in, so search results diverged between teammates depending on who ran
7
+ # `qmd update` most recently. This script makes post-sync indexing automatic
8
+ # and deterministic, so every machine's personal index converges to the same
9
+ # content. The index itself stays personal (it is large, binary, and embeds
10
+ # absolute local paths); only the *freshness* is made automatic.
11
+ #
12
+ # Behavior:
13
+ # 1. Auto-registers any company knowledge dir that isn't yet a qmd
14
+ # collection (fixes the "newly-synced knowledge isn't searchable until I
15
+ # manually map it" gap).
16
+ # 2. Runs an incremental lexical reindex (`qmd update` — fast, skips
17
+ # unchanged files by mtime).
18
+ # 3. Rebuilds embeddings only when called with --embed (slow on a
19
+ # multi-GB index; meant for an idle/interval pass, not every sync).
20
+ #
21
+ # Idempotent and safe: no-op (exit 0) when qmd is absent or the path isn't an
22
+ # HQ root. Never blocks or fails a sync.
23
+ #
24
+ # Usage: qmd-reindex-after-sync.sh [hq_root] [--embed]
25
+ # hq_root Local HQ directory (default: $PWD). Must contain core/core.yaml.
26
+ # --embed Also rebuild embeddings (deferred-cost; omit for fast lexical-only).
27
+
28
+ set -uo pipefail
29
+
30
+ hq_root=""
31
+ embed=0
32
+ for arg in "$@"; do
33
+ case "$arg" in
34
+ --embed) embed=1 ;;
35
+ --*) : ;; # ignore unknown flags
36
+ *) [ -z "$hq_root" ] && hq_root="$arg" ;;
37
+ esac
38
+ done
39
+ [ -z "$hq_root" ] && hq_root="$PWD"
40
+
41
+ command -v qmd >/dev/null 2>&1 || exit 0
42
+ [ -f "$hq_root/core/core.yaml" ] || exit 0
43
+
44
+ cd "$hq_root" || exit 0
45
+
46
+ # 1. Auto-register company knowledge collections that don't exist yet.
47
+ # Convention matches /newcompany: --name <slug> --mask "**/*.md".
48
+ existing="$(qmd collection list 2>/dev/null || true)"
49
+ for kdir in companies/*/knowledge; do
50
+ [ -d "$kdir" ] || continue
51
+ [ -n "$(find "$kdir" -name '*.md' -not -name 'INDEX.md' -print -quit 2>/dev/null)" ] || continue
52
+ slug="$(basename "$(dirname "$kdir")")"
53
+ printf '%s\n' "$existing" | grep -Fq "qmd://$slug/" && continue
54
+ qmd collection add "$hq_root/$kdir" --name "$slug" --mask "**/*.md" >/dev/null 2>&1 || true
55
+ qmd context add "qmd://$slug" "Knowledge base for $slug." >/dev/null 2>&1 || true
56
+ done
57
+
58
+ # 1b. Auto-register company *projects* collections that don't exist yet.
59
+ # Convention matches the HQ-level `hq-projects` collection: --name <slug>-projects
60
+ # --mask "**/*.{md,json}" (so prd.json + project docs are searchable). Without
61
+ # this, company projects/ dirs are indexed by nothing and /startwork's global
62
+ # `qmd search "prd.json"` and /brainstorm's project discovery silently miss them.
63
+ for pdir in companies/*/projects; do
64
+ [ -d "$pdir" ] || continue
65
+ [ -n "$(find "$pdir" -type f \( -name '*.md' -o -name '*.json' \) -print -quit 2>/dev/null)" ] || continue
66
+ slug="$(basename "$(dirname "$pdir")")"
67
+ name="${slug}-projects"
68
+ printf '%s\n' "$existing" | grep -Fq "qmd://$name/" && continue
69
+ qmd collection add "$hq_root/$pdir" --name "$name" --mask "**/*.{md,json}" >/dev/null 2>&1 || true
70
+ qmd context add "qmd://$name" "Project PRDs and documentation for $slug." >/dev/null 2>&1 || true
71
+ done
72
+
73
+ # 1c. Auto-register the personal knowledge collection if it isn't one yet.
74
+ # personal/knowledge is now read DIRECTLY (the reindex symlink mirror into
75
+ # core/knowledge is retired), so it needs its own qmd collection to stay
76
+ # searchable rather than riding in under a core collection.
77
+ if [ -d "personal/knowledge" ] && \
78
+ [ -n "$(find "personal/knowledge" -name '*.md' -not -name 'INDEX.md' -print -quit 2>/dev/null)" ]; then
79
+ printf '%s\n' "$existing" | grep -Fq "qmd://personal-knowledge/" || {
80
+ qmd collection add "$hq_root/personal/knowledge" --name "personal-knowledge" --mask "**/*.md" >/dev/null 2>&1 || true
81
+ qmd context add "qmd://personal-knowledge" "Personal knowledge base (owner overlay)." >/dev/null 2>&1 || true
82
+ }
83
+ fi
84
+
85
+ # 2. Incremental lexical reindex (cheap; mtime-incremental inside qmd).
86
+ qmd update >/dev/null 2>&1 || true
87
+
88
+ # 3. Embeddings only on explicit request.
89
+ if [ "$embed" -eq 1 ]; then
90
+ qmd embed >/dev/null 2>&1 || true
91
+ fi
92
+
93
+ exit 0
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env bash
2
+ # rebuild-all-indexes.sh — fan out to every per-class INDEX.md regenerator.
3
+ #
4
+ # Stdout: JSON array of every INDEX.md path that was actually written
5
+ # (parsed from per-script "wrote {path}" lines on stderr).
6
+ # Stderr: passthrough log from each script, plus a final "all-indexes: ok"
7
+ # or "all-indexes: errors" line.
8
+ #
9
+ # Used by handoff-post.sh / handoff-finalize.sh / /cleanup --reindex.
10
+ # Exit 0 always (handoff must not fail because one INDEX class blew up).
11
+
12
+ set -uo pipefail
13
+
14
+ HQ_ROOT="${HQ_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
15
+ cd "$HQ_ROOT"
16
+
17
+ SCRIPTS=(
18
+ "rebuild-threads-index.sh"
19
+ "rebuild-orchestrator-index.sh"
20
+ "rebuild-companies-index.sh"
21
+ "rebuild-projects-index.sh"
22
+ "rebuild-company-knowledge-index.sh"
23
+ "rebuild-public-knowledge-index.sh"
24
+ "rebuild-workers-index.sh"
25
+ "rebuild-reports-index.sh"
26
+ "rebuild-social-drafts-index.sh"
27
+ )
28
+
29
+ TMP_STDERR=$(mktemp /tmp/rebuild-all-indexes.XXXXXX)
30
+ trap 'rm -f "$TMP_STDERR"' EXIT
31
+
32
+ ERRORS=0
33
+ for s in "${SCRIPTS[@]}"; do
34
+ path="core/scripts/${s}"
35
+ if [[ ! -x "$path" ]] && [[ -f "$path" ]]; then
36
+ chmod +x "$path" 2>/dev/null || true
37
+ fi
38
+ if [[ ! -f "$path" ]]; then
39
+ echo "rebuild-all-indexes: skip ${s} (missing)" >&2
40
+ continue
41
+ fi
42
+ if ! bash "$path" 2>>"$TMP_STDERR"; then
43
+ echo "rebuild-all-indexes: ERROR in ${s}" >&2
44
+ ERRORS=$((ERRORS+1))
45
+ fi
46
+ done
47
+
48
+ # Replay each rebuild's stderr to our stderr (so callers see logs)
49
+ cat "$TMP_STDERR" >&2
50
+
51
+ # Parse "wrote {path}" lines to build the JSON output array
52
+ paths_json=$(
53
+ grep -E 'wrote (workspace|companies|knowledge|workers|projects)' "$TMP_STDERR" 2>/dev/null \
54
+ | sed -E 's/.*wrote ([^ ]+).*/\1/' \
55
+ | sort -u \
56
+ | jq -R . \
57
+ | jq -s .
58
+ )
59
+ [[ -z "$paths_json" ]] && paths_json='[]'
60
+
61
+ printf '%s\n' "$paths_json"
62
+
63
+ if [[ "$ERRORS" -gt 0 ]]; then
64
+ echo "all-indexes: errors (${ERRORS})" >&2
65
+ else
66
+ echo "all-indexes: ok" >&2
67
+ fi
68
+
69
+ exit 0
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env bash
2
+ # rebuild-companies-index.sh — regenerate companies/{co}/INDEX.md for each company.
3
+ #
4
+ # Pure bash + jq. Zero Claude context. Iterates companies/*/ (skips _template,
5
+ # dotfiles), summarizes top-level dirs, scans projects/*/prd.json and
6
+ # workspace/orchestrator/{name}/state.json to populate the Projects table.
7
+ #
8
+ # Schema per knowledge/public/hq-core/index-md-spec.md (Company Root variant):
9
+ # - "# {Company Title}" + "> Auto-generated. Updated: YYYY-MM-DD"
10
+ # - Top-level Name | Description table (dirs and 1-line desc)
11
+ # - "## Projects" table (Project | Status | Description)
12
+ # - "## Deployments" placeholder (None for now — manifest-driven enrichment deferred)
13
+
14
+ set -euo pipefail
15
+
16
+ HQ_ROOT="${HQ_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
17
+ cd "$HQ_ROOT"
18
+
19
+ DATE=$(date -u +%Y-%m-%d)
20
+
21
+ # Title-case a slug: "example-co" -> "Example Co"
22
+ titleize() {
23
+ echo "$1" | awk -F'-' '{
24
+ for (i=1; i<=NF; i++) {
25
+ $i = toupper(substr($i,1,1)) substr($i,2)
26
+ }
27
+ print
28
+ }' OFS=' '
29
+ }
30
+
31
+ describe_dir() {
32
+ local co="$1"
33
+ local d="$2"
34
+ local path="companies/${co}/${d}"
35
+ [[ -d "$path" ]] || return 0
36
+ case "$d" in
37
+ data)
38
+ local n
39
+ n=$(find "$path" -mindepth 1 -maxdepth 1 ! -name '.*' 2>/dev/null | wc -l | tr -d ' ')
40
+ echo "${n} item(s)"
41
+ ;;
42
+ knowledge)
43
+ if [[ -e "${path}/.git" ]]; then
44
+ echo "Embedded git repo"
45
+ else
46
+ local n
47
+ n=$(find "$path" -mindepth 1 -maxdepth 1 -type d ! -name '.*' 2>/dev/null | wc -l | tr -d ' ')
48
+ echo "Inline knowledge (${n} subdirs)"
49
+ fi
50
+ ;;
51
+ settings)
52
+ local n
53
+ n=$(find "$path" -mindepth 1 -maxdepth 1 -type d ! -name '.*' 2>/dev/null | wc -l | tr -d ' ')
54
+ echo "${n} integration(s)"
55
+ ;;
56
+ policies)
57
+ local n
58
+ n=$(find "$path" -mindepth 1 -maxdepth 1 -name '*.md' 2>/dev/null | wc -l | tr -d ' ')
59
+ echo "${n} policy file(s)"
60
+ ;;
61
+ projects)
62
+ local n
63
+ n=$(find "$path" -mindepth 1 -maxdepth 1 -type d ! -name '_*' ! -name '.*' 2>/dev/null | wc -l | tr -d ' ')
64
+ echo "${n} project(s)"
65
+ ;;
66
+ workers)
67
+ local n
68
+ n=$(find "$path" -mindepth 1 -maxdepth 1 -type d ! -name '.*' 2>/dev/null | wc -l | tr -d ' ')
69
+ echo "${n} worker(s)"
70
+ ;;
71
+ repos)
72
+ local n
73
+ n=$(find "$path" -mindepth 1 -maxdepth 1 \( -type d -o -type l \) ! -name '.*' 2>/dev/null | wc -l | tr -d ' ')
74
+ echo "${n} repo link(s)"
75
+ ;;
76
+ registry)
77
+ local n
78
+ n=$(find "${path}/resources" -mindepth 1 -maxdepth 1 -name '*.yaml' 2>/dev/null | wc -l | tr -d ' ')
79
+ echo "Resource registry (${n} resource(s))"
80
+ ;;
81
+ scripts)
82
+ local n
83
+ n=$(find "$path" -mindepth 1 -maxdepth 1 -type f 2>/dev/null | wc -l | tr -d ' ')
84
+ echo "${n} script(s)"
85
+ ;;
86
+ *)
87
+ echo "—"
88
+ ;;
89
+ esac
90
+ }
91
+
92
+ # Resolve project status: orchestrator state.json wins, else story-pass fallback.
93
+ project_status() {
94
+ local project="$1"
95
+ local prd="$2"
96
+ local state="workspace/orchestrator/${project}/state.json"
97
+ if [[ -f "$state" ]]; then
98
+ local raw
99
+ raw=$(jq -r '.status // "active"' "$state" 2>/dev/null || echo "active")
100
+ case "$raw" in
101
+ completed|complete) echo "complete" ;;
102
+ in_progress|active|started) echo "active" ;;
103
+ archived) echo "archived" ;;
104
+ *) echo "$raw" ;;
105
+ esac
106
+ return
107
+ fi
108
+ if [[ -f "$prd" ]]; then
109
+ local total passes
110
+ total=$(jq '[.userStories[]?] | length' "$prd" 2>/dev/null || echo 0)
111
+ passes=$(jq '[.userStories[]? | select(.passes==true)] | length' "$prd" 2>/dev/null || echo 0)
112
+ if [[ "$total" -gt 0 ]] && [[ "$total" == "$passes" ]]; then
113
+ echo "complete"
114
+ return
115
+ fi
116
+ fi
117
+ echo "active"
118
+ }
119
+
120
+ # Sanitize a string for Markdown table cells (single line, no pipes).
121
+ sanitize_cell() {
122
+ printf '%s' "$1" | tr '\n' ' ' | sed -e 's/|/\\|/g' -e 's/ */ /g' -e 's/^ *//' -e 's/ *$//'
123
+ }
124
+
125
+ # Truncate to N chars.
126
+ trunc() {
127
+ local s="$1"
128
+ local n="$2"
129
+ if [[ ${#s} -le $n ]]; then
130
+ printf '%s' "$s"
131
+ else
132
+ printf '%s…' "${s:0:$((n-1))}"
133
+ fi
134
+ }
135
+
136
+ write_company_index() {
137
+ local co="$1"
138
+ local out="companies/${co}/INDEX.md"
139
+ local title
140
+ title=$(titleize "$co")
141
+
142
+ # Header + top-level dir table
143
+ {
144
+ echo "# ${title}"
145
+ echo ""
146
+ echo "> Auto-generated. Updated: ${DATE}"
147
+ echo ""
148
+ echo "| Name | Description |"
149
+ echo "|------|-------------|"
150
+ # Stable order matching company template.
151
+ for d in data knowledge policies projects registry repos scripts settings workers; do
152
+ if [[ -d "companies/${co}/${d}" ]]; then
153
+ local desc
154
+ desc=$(describe_dir "$co" "$d")
155
+ printf '| `%s/` | %s |\n' "$d" "$(sanitize_cell "$desc")"
156
+ fi
157
+ done
158
+ echo ""
159
+ echo "## Projects"
160
+ echo ""
161
+
162
+ local proj_dir="companies/${co}/projects"
163
+ if [[ -d "$proj_dir" ]]; then
164
+ echo "| Project | Status | Description |"
165
+ echo "|---------|--------|-------------|"
166
+ # List immediate project subdirs, skip _archive and dotfiles.
167
+ while IFS= read -r p; do
168
+ local name
169
+ name=$(basename "$p")
170
+ [[ "$name" == _* ]] && continue
171
+ [[ "$name" == .* ]] && continue
172
+ local prd="${p}/prd.json"
173
+ local desc=""
174
+ if [[ -f "$prd" ]]; then
175
+ desc=$(jq -r '.description // ""' "$prd" 2>/dev/null || echo "")
176
+ fi
177
+ local status
178
+ status=$(project_status "$name" "$prd")
179
+ desc=$(sanitize_cell "$desc")
180
+ desc=$(trunc "$desc" 110)
181
+ [[ -z "$desc" ]] && desc="—"
182
+ printf '| `%s` | %s | %s |\n' "$name" "$status" "$desc"
183
+ done < <(find "$proj_dir" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort)
184
+ else
185
+ echo "_No projects directory._"
186
+ fi
187
+
188
+ echo ""
189
+ echo "## Deployments"
190
+ echo ""
191
+ echo "_See \`companies/manifest.yaml\` for deployment targets._"
192
+ } > "$out"
193
+
194
+ echo "rebuild-companies-index: wrote ${out}" >&2
195
+ }
196
+
197
+ COUNT=0
198
+ while IFS= read -r dir; do
199
+ co=$(basename "$dir")
200
+ [[ "$co" == _* ]] && continue
201
+ [[ "$co" == .* ]] && continue
202
+ write_company_index "$co"
203
+ COUNT=$((COUNT+1))
204
+ done < <(find companies -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort)
205
+
206
+ echo "rebuild-companies-index: regenerated ${COUNT} company INDEX.md file(s)" >&2
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env bash
2
+ # rebuild-company-knowledge-index.sh — regenerate companies/{co}/knowledge/INDEX.md.
3
+ #
4
+ # Pure bash + jq. One INDEX.md per company knowledge dir, at the top level only
5
+ # (no descent into subdirs — embedded git repos manage their own internals).
6
+ #
7
+ # Description extraction:
8
+ # - .md files → first `#` heading (stripped)
9
+ # - .yaml files → `description:` field if present
10
+ # - .json files → `description` field if present
11
+ # - directories → "{N} item(s)"
12
+ #
13
+ # Most company knowledge dirs are embedded git repos (160000 gitlinks). The
14
+ # generated INDEX.md lives inside the inner repo; HQ git won't track its
15
+ # content directly, but it gets refreshed on every handoff.
16
+
17
+ set -euo pipefail
18
+
19
+ HQ_ROOT="${HQ_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
20
+ cd "$HQ_ROOT"
21
+
22
+ DATE=$(date -u +%Y-%m-%d)
23
+
24
+ titleize() {
25
+ echo "$1" | awk -F'-' '{
26
+ for (i=1; i<=NF; i++) { $i = toupper(substr($i,1,1)) substr($i,2) }
27
+ print
28
+ }' OFS=' '
29
+ }
30
+
31
+ sanitize_cell() {
32
+ printf '%s' "$1" | tr '\n' ' ' | sed -e 's/|/\\|/g' -e 's/ */ /g' -e 's/^ *//' -e 's/ *$//'
33
+ }
34
+ trunc() {
35
+ local s="$1" n="$2"
36
+ if [[ ${#s} -le $n ]]; then printf '%s' "$s"; else printf '%s…' "${s:0:$((n-1))}"; fi
37
+ }
38
+
39
+ describe_item() {
40
+ local path="$1"
41
+ local name="$2"
42
+ if [[ -d "$path" ]]; then
43
+ local n
44
+ n=$(find "$path" -mindepth 1 -maxdepth 1 ! -name '.*' 2>/dev/null | wc -l | tr -d ' ')
45
+ echo "${n} item(s)"
46
+ return
47
+ fi
48
+ case "$name" in
49
+ *.md)
50
+ local h
51
+ h=$(awk '/^# / { sub(/^# +/, ""); print; exit }' "$path" 2>/dev/null || true)
52
+ [[ -z "$h" ]] && h="$name"
53
+ echo "$h"
54
+ ;;
55
+ *.yaml|*.yml)
56
+ local d
57
+ d=$(awk -F: '/^description:/ { sub(/^description: */, ""); gsub(/^["\x27]|["\x27]$/, ""); print; exit }' "$path" 2>/dev/null || true)
58
+ [[ -z "$d" ]] && d="$name"
59
+ echo "$d"
60
+ ;;
61
+ *.json)
62
+ local d
63
+ d=$(jq -r '.description // .name // empty' "$path" 2>/dev/null || true)
64
+ [[ -z "$d" ]] && d="$name"
65
+ echo "$d"
66
+ ;;
67
+ *)
68
+ echo "$name"
69
+ ;;
70
+ esac
71
+ }
72
+
73
+ write_knowledge_index() {
74
+ local co="$1"
75
+ local kdir="companies/${co}/knowledge"
76
+ [[ -d "$kdir" ]] || return 0
77
+ local out="${kdir}/INDEX.md"
78
+ local title
79
+ title="$(titleize "$co") Knowledge"
80
+
81
+ {
82
+ echo "# ${title}"
83
+ echo ""
84
+ echo "> Auto-generated. Updated: ${DATE}"
85
+ echo ""
86
+ echo "| Name | Description |"
87
+ echo "|------|-------------|"
88
+
89
+ # Directories first (sorted), then files (sorted). Skip dotfiles, INDEX.md.
90
+ while IFS= read -r item; do
91
+ local name
92
+ name=$(basename "$item")
93
+ [[ "$name" == .* ]] && continue
94
+ [[ "$name" == "INDEX.md" ]] && continue
95
+ local desc
96
+ desc=$(describe_item "$item" "$name")
97
+ desc=$(sanitize_cell "$desc")
98
+ desc=$(trunc "$desc" 100)
99
+ [[ -z "$desc" ]] && desc="—"
100
+ if [[ -d "$item" ]]; then
101
+ printf '| `%s/` | %s |\n' "$name" "$desc"
102
+ else
103
+ printf '| `%s` | %s |\n' "$name" "$desc"
104
+ fi
105
+ done < <(
106
+ {
107
+ find -L "$kdir" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort
108
+ find -L "$kdir" -mindepth 1 -maxdepth 1 -type f 2>/dev/null | sort
109
+ }
110
+ )
111
+ } > "$out"
112
+
113
+ echo "rebuild-company-knowledge-index: wrote ${out}" >&2
114
+ }
115
+
116
+ COUNT=0
117
+ while IFS= read -r dir; do
118
+ co=$(basename "$dir")
119
+ [[ "$co" == _* ]] && continue
120
+ [[ "$co" == .* ]] && continue
121
+ if [[ -d "companies/${co}/knowledge" ]]; then
122
+ write_knowledge_index "$co"
123
+ COUNT=$((COUNT+1))
124
+ fi
125
+ done < <(find companies -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort)
126
+
127
+ echo "rebuild-company-knowledge-index: regenerated ${COUNT} knowledge INDEX.md file(s)" >&2
@@ -0,0 +1,86 @@
1
+ #!/usr/bin/env bash
2
+ # rebuild-orchestrator-index.sh — regenerate workspace/orchestrator/INDEX.md.
3
+ #
4
+ # Pure bash + jq. Reads workspace/orchestrator/<project>/state.json files.
5
+ # Tolerates two on-disk schemas: project-run state (run-project.sh — .project,
6
+ # .prd_path, .progress, terminal on .status == "completed") and garden state
7
+ # (/garden — .run_id, .scope, .findings_count, terminal on .phase == "complete").
8
+ # Lists only non-terminal runs; _archive/ and _pipeline/ excluded.
9
+ # Each state.json is parsed independently: a malformed file is reported to stderr
10
+ # and skipped, never silently wiping the rest of the index
11
+ # (see core/policies/hq-never-swallow-errors.md).
12
+
13
+ set -euo pipefail
14
+
15
+ HQ_ROOT="${HQ_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
16
+ cd "$HQ_ROOT"
17
+
18
+ OUT="workspace/orchestrator/INDEX.md"
19
+ TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
20
+
21
+ mkdir -p workspace/orchestrator
22
+
23
+ STATE_FILES=()
24
+ while IFS= read -r line; do
25
+ STATE_FILES+=("$line")
26
+ done < <(
27
+ find workspace/orchestrator -mindepth 2 -maxdepth 2 -name state.json 2>/dev/null \
28
+ | grep -v '/_archive/' \
29
+ | grep -v '/_pipeline/' \
30
+ || true
31
+ )
32
+
33
+ # Project column: .project (project-run) else .run_id (garden) else "-".
34
+ # Company column: parsed from .prd_path (project-run) or .scope (garden); a bare
35
+ # slug or non-companies/ path yields "-". Progress: only project-run rows carry
36
+ # .progress; garden rows render "-". Terminal exclusion keys off BOTH
37
+ # .status == "completed"/"complete" (project-run + drift guard) AND
38
+ # .phase == "complete" (garden's real terminal marker).
39
+ ROW_FILTER='
40
+ select((.status // "") != "completed" and (.status // "") != "complete")
41
+ | select((.phase // "") != "complete")
42
+ | [
43
+ (.project // .run_id // "-"),
44
+ (((.prd_path // .scope // "") | capture("companies/(?<co>[^/]+)/") | .co) // "-"),
45
+ (.status // "-"),
46
+ (if .progress
47
+ then ((.progress.completed // 0) | tostring) + "/" + ((.progress.total // 0) | tostring)
48
+ else "-" end),
49
+ (.updated_at // "-")
50
+ ] | @tsv
51
+ '
52
+
53
+ ERR_FILE=$(mktemp)
54
+ trap 'rm -f "$ERR_FILE"' EXIT
55
+
56
+ NL=$'\n'
57
+ rows=""
58
+ # Guard the array expansion: "${arr[@]}" on an empty array errors under `set -u`
59
+ # on bash 3.2 (macOS). See core/policies/hq-bash-discipline.md rule 4.
60
+ if [[ ${#STATE_FILES[@]} -gt 0 ]]; then
61
+ for sf in "${STATE_FILES[@]}"; do
62
+ row=""
63
+ if row=$(jq -r "$ROW_FILTER" "$sf" 2>"$ERR_FILE"); then
64
+ [ -n "$row" ] && rows="${rows:+$rows$NL}$row"
65
+ else
66
+ echo "rebuild-orchestrator-index: WARNING: skipped unparseable ${sf}: $(tr '\n' ' ' < "$ERR_FILE")" >&2
67
+ fi
68
+ done
69
+ fi
70
+
71
+ {
72
+ echo "# Orchestrator Projects"
73
+ echo ""
74
+ echo "Generated: ${TS}"
75
+ echo ""
76
+ echo "Active runs only. Archived runs in \`workspace/orchestrator/_archive/\`."
77
+ echo ""
78
+ echo "| Project | Company | Status | Progress | Last Updated |"
79
+ echo "|---------|---------|--------|----------|--------------|"
80
+ if [[ -n "$rows" ]]; then
81
+ printf "%s\n" "$rows" | awk -F'\t' 'NF>=5 { printf "| %s | %s | %s | %s | %s |\n", $1, $2, $3, $4, $5 }'
82
+ fi
83
+ } > "$OUT"
84
+
85
+ active=$(printf "%s" "$rows" | grep -c . || true)
86
+ echo "rebuild-orchestrator-index: wrote ${OUT} (${active} active)" >&2
@@ -0,0 +1,105 @@
1
+ #!/usr/bin/env bash
2
+ # rebuild-projects-index.sh — regenerate the personal projects INDEX.md.
3
+ #
4
+ # Pure bash + jq. Reads personal/projects/*/prd.json for the Personal/HQ
5
+ # Projects table and lists companies/*/projects directories for cross-reference.
6
+ # Status comes from workspace/orchestrator/{name}/state.json (fallback:
7
+ # passes-all → complete).
8
+
9
+ set -euo pipefail
10
+
11
+ HQ_ROOT="${HQ_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
12
+ cd "$HQ_ROOT"
13
+
14
+ PROJECTS_DIR="personal/projects"
15
+ OUT="${PROJECTS_DIR}/INDEX.md"
16
+ DATE=$(date -u +%Y-%m-%d)
17
+
18
+ [[ -d "$PROJECTS_DIR" ]] || { echo "rebuild-projects-index: ${PROJECTS_DIR}/ missing, skipping" >&2; exit 0; }
19
+
20
+ sanitize_cell() {
21
+ printf '%s' "$1" | tr '\n' ' ' | sed -e 's/|/\\|/g' -e 's/ */ /g' -e 's/^ *//' -e 's/ *$//'
22
+ }
23
+ trunc() {
24
+ local s="$1"
25
+ local n="$2"
26
+ if [[ ${#s} -le $n ]]; then printf '%s' "$s"; else printf '%s…' "${s:0:$((n-1))}"; fi
27
+ }
28
+
29
+ project_status() {
30
+ local project="$1"
31
+ local prd="$2"
32
+ local state="workspace/orchestrator/${project}/state.json"
33
+ if [[ -f "$state" ]]; then
34
+ local raw
35
+ raw=$(jq -r '.status // "active"' "$state" 2>/dev/null || echo "active")
36
+ case "$raw" in
37
+ completed|complete) echo "complete" ;;
38
+ in_progress|active|started) echo "active" ;;
39
+ archived) echo "archived" ;;
40
+ *) echo "$raw" ;;
41
+ esac
42
+ return
43
+ fi
44
+ if [[ -f "$prd" ]]; then
45
+ local total passes
46
+ total=$(jq '[.userStories[]?] | length' "$prd" 2>/dev/null || echo 0)
47
+ passes=$(jq '[.userStories[]? | select(.passes==true)] | length' "$prd" 2>/dev/null || echo 0)
48
+ if [[ "$total" -gt 0 ]] && [[ "$total" == "$passes" ]]; then
49
+ echo "complete"
50
+ return
51
+ fi
52
+ fi
53
+ echo "—"
54
+ }
55
+
56
+ {
57
+ echo "# Projects"
58
+ echo ""
59
+ echo "> Auto-generated. Updated: ${DATE}"
60
+ echo ""
61
+ echo "Personal/HQ projects only. Company-scoped projects live in \`companies/{co}/projects/\`."
62
+ echo ""
63
+ echo "## Company Project Directories"
64
+ echo ""
65
+ echo "| Company | Path |"
66
+ echo "|---------|------|"
67
+ while IFS= read -r dir; do
68
+ co=$(basename "$dir")
69
+ [[ "$co" == _* ]] && continue
70
+ [[ "$co" == .* ]] && continue
71
+ if [[ -d "companies/${co}/projects" ]]; then
72
+ printf '| %s | `companies/%s/projects/` |\n' "$co" "$co"
73
+ fi
74
+ done < <(find companies -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort)
75
+
76
+ echo ""
77
+ echo "## Personal/HQ Projects"
78
+ echo ""
79
+ echo "Projects here are HQ infrastructure or cross-company tools."
80
+ echo ""
81
+ echo "| Project | Stories | Status | Description |"
82
+ echo "|---------|---------|--------|-------------|"
83
+
84
+ while IFS= read -r p; do
85
+ name=$(basename "$p")
86
+ [[ "$name" == _* ]] && continue
87
+ [[ "$name" == .* ]] && continue
88
+ prd="${p}/prd.json"
89
+ desc=""
90
+ stories="—"
91
+ if [[ -f "$prd" ]]; then
92
+ desc=$(jq -r '.description // ""' "$prd" 2>/dev/null || echo "")
93
+ stories=$(jq '[.userStories[]?] | length' "$prd" 2>/dev/null || echo 0)
94
+ [[ "$stories" == "0" ]] && stories="—"
95
+ fi
96
+ status=$(project_status "$name" "$prd")
97
+ desc=$(sanitize_cell "$desc")
98
+ desc=$(trunc "$desc" 110)
99
+ [[ -z "$desc" ]] && desc="—"
100
+ printf '| `%s/` | %s | %s | %s |\n' "$name" "$stories" "$status" "$desc"
101
+ done < <(find "$PROJECTS_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort)
102
+ } > "$OUT"
103
+
104
+ count=$(find "$PROJECTS_DIR" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l | tr -d ' ')
105
+ echo "rebuild-projects-index: wrote ${OUT} (${count} HQ project(s))" >&2