@indigoai-us/hq-cli 5.77.14 → 5.79.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 (309) hide show
  1. package/CHANGELOG.md +31 -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/meetings.js +16 -9
  31. package/dist/commands/members.js +16 -5
  32. package/dist/commands/pack-install.d.ts +14 -0
  33. package/dist/commands/pack-install.js +42 -2
  34. package/dist/commands/pkg-install.js +39 -2
  35. package/dist/commands/run.js +6 -2
  36. package/dist/commands/secrets.js +120 -65
  37. package/dist/main.js +6 -0
  38. package/dist/utils/api-key-command-gate.d.ts +45 -0
  39. package/dist/utils/api-key-command-gate.js +51 -0
  40. package/dist/utils/hq-roots.d.ts +91 -0
  41. package/dist/utils/hq-roots.js +212 -0
  42. package/dist/utils/resolve-vault-credential.d.ts +63 -3
  43. package/dist/utils/resolve-vault-credential.js +152 -6
  44. package/dist/utils/run-bundled-script.d.ts +66 -0
  45. package/dist/utils/run-bundled-script.js +80 -0
  46. package/dist/utils/vault-api.d.ts +21 -0
  47. package/dist/utils/vault-api.js +74 -3
  48. package/package.json +7 -1
  49. package/.claude/policies/hq-cli-interactive-reads-use-sts-vend.md +0 -49
  50. package/.github/workflows/ci.yml +0 -46
  51. package/.github/workflows/publish.yml +0 -36
  52. package/.github/workflows/scripts/smoke-test-pkg.sh +0 -97
  53. package/e2e/cli.test.ts +0 -128
  54. package/eslint.config.js +0 -37
  55. package/pnpm-workspace.yaml +0 -4
  56. package/scripts/chmod-bins.mjs +0 -49
  57. package/scripts/generate-dsn.mjs +0 -26
  58. package/scripts/generate-scan-packages-table.mjs +0 -113
  59. package/scripts/smoke-sources-signals.sh +0 -103
  60. package/src/bin/hq-auth-refresh.ts +0 -43
  61. package/src/cli-version.ts +0 -13
  62. package/src/commands/__fixtures__/make-tar.ts +0 -126
  63. package/src/commands/_patterns.ts +0 -10
  64. package/src/commands/add.ts +0 -75
  65. package/src/commands/agents.test.ts +0 -527
  66. package/src/commands/agents.ts +0 -923
  67. package/src/commands/api-keys.test.ts +0 -291
  68. package/src/commands/api-keys.ts +0 -382
  69. package/src/commands/artifact-verify.test.ts +0 -177
  70. package/src/commands/auth.test.ts +0 -82
  71. package/src/commands/auth.ts +0 -213
  72. package/src/commands/billing.test.ts +0 -158
  73. package/src/commands/billing.ts +0 -146
  74. package/src/commands/channels.test.ts +0 -74
  75. package/src/commands/channels.ts +0 -91
  76. package/src/commands/cloud-demote.test.ts +0 -401
  77. package/src/commands/cloud-demote.ts +0 -278
  78. package/src/commands/cloud-provision.test.ts +0 -1117
  79. package/src/commands/cloud-provision.ts +0 -942
  80. package/src/commands/cloud.pull-all.test.ts +0 -711
  81. package/src/commands/cloud.pull-per-company.test.ts +0 -188
  82. package/src/commands/cloud.push-all.test.ts +0 -485
  83. package/src/commands/cloud.scope-excluded-warning.test.ts +0 -22
  84. package/src/commands/cloud.selectors.test.ts +0 -208
  85. package/src/commands/cloud.status.test.ts +0 -160
  86. package/src/commands/cloud.test.ts +0 -41
  87. package/src/commands/cloud.ts +0 -2130
  88. package/src/commands/company.test.ts +0 -202
  89. package/src/commands/company.ts +0 -148
  90. package/src/commands/creators.test.ts +0 -60
  91. package/src/commands/creators.ts +0 -117
  92. package/src/commands/crm.test.ts +0 -179
  93. package/src/commands/crm.ts +0 -236
  94. package/src/commands/db-migrate.ts +0 -55
  95. package/src/commands/db-provision.ts +0 -114
  96. package/src/commands/db-sql.ts +0 -124
  97. package/src/commands/db-status.ts +0 -108
  98. package/src/commands/db.ts +0 -26
  99. package/src/commands/dm.test.ts +0 -748
  100. package/src/commands/dm.ts +0 -1226
  101. package/src/commands/feedback.test.ts +0 -413
  102. package/src/commands/feedback.ts +0 -169
  103. package/src/commands/files-browse.test.ts +0 -1249
  104. package/src/commands/files-browse.ts +0 -1506
  105. package/src/commands/files-delete.test.ts +0 -484
  106. package/src/commands/files-recovery.test.ts +0 -370
  107. package/src/commands/files.test.ts +0 -722
  108. package/src/commands/files.ts +0 -1446
  109. package/src/commands/group-grants.test.ts +0 -330
  110. package/src/commands/group-grants.ts +0 -455
  111. package/src/commands/groups.test.ts +0 -44
  112. package/src/commands/groups.ts +0 -368
  113. package/src/commands/integrations.test.ts +0 -633
  114. package/src/commands/integrations.ts +0 -507
  115. package/src/commands/list.ts +0 -85
  116. package/src/commands/login.ts +0 -52
  117. package/src/commands/logout.ts +0 -30
  118. package/src/commands/marketplace-install.test.ts +0 -543
  119. package/src/commands/marketplace-security.test.ts +0 -646
  120. package/src/commands/mcp-registration.test.ts +0 -2787
  121. package/src/commands/mcp-registration.ts +0 -2612
  122. package/src/commands/mcp-status.test.ts +0 -483
  123. package/src/commands/mcp-status.ts +0 -575
  124. package/src/commands/mcp-status.us011.test.ts +0 -243
  125. package/src/commands/meetings.test.ts +0 -522
  126. package/src/commands/meetings.ts +0 -789
  127. package/src/commands/members.test.ts +0 -1474
  128. package/src/commands/members.ts +0 -1181
  129. package/src/commands/onboard-identity-guard.test.ts +0 -99
  130. package/src/commands/onboard-identity-guard.ts +0 -103
  131. package/src/commands/onboard-join.test.ts +0 -96
  132. package/src/commands/onboard-join.ts +0 -101
  133. package/src/commands/onboard-warning.test.ts +0 -26
  134. package/src/commands/onboard-warning.ts +0 -12
  135. package/src/commands/onboard.ts +0 -267
  136. package/src/commands/outposts-heartbeat.test.ts +0 -299
  137. package/src/commands/outposts-heartbeat.ts +0 -310
  138. package/src/commands/outposts-replica-sync.test.ts +0 -272
  139. package/src/commands/outposts-self-deploy.test.ts +0 -243
  140. package/src/commands/outposts.test.ts +0 -1039
  141. package/src/commands/outposts.ts +0 -1935
  142. package/src/commands/pack-install-secret-authorization.test.ts +0 -115
  143. package/src/commands/pack-install.test.ts +0 -1469
  144. package/src/commands/pack-install.ts +0 -2256
  145. package/src/commands/pack-update-cache.test.ts +0 -149
  146. package/src/commands/packs-update-api-key.test.ts +0 -105
  147. package/src/commands/packs.test.ts +0 -88
  148. package/src/commands/packs.ts +0 -622
  149. package/src/commands/people.test.ts +0 -807
  150. package/src/commands/people.ts +0 -422
  151. package/src/commands/pkg-install.dispatch.test.ts +0 -103
  152. package/src/commands/pkg-install.ts +0 -221
  153. package/src/commands/pkg-list.test.ts +0 -79
  154. package/src/commands/pkg-list.ts +0 -13
  155. package/src/commands/pkg-remove.ts +0 -71
  156. package/src/commands/pkg-update.ts +0 -189
  157. package/src/commands/publish.test.ts +0 -650
  158. package/src/commands/publish.ts +0 -580
  159. package/src/commands/reindex.test.ts +0 -259
  160. package/src/commands/reindex.ts +0 -274
  161. package/src/commands/rescue.test.ts +0 -39
  162. package/src/commands/rescue.ts +0 -210
  163. package/src/commands/run.env-local.test.ts +0 -88
  164. package/src/commands/run.test.ts +0 -45
  165. package/src/commands/run.ts +0 -190
  166. package/src/commands/safe-extract.test.ts +0 -459
  167. package/src/commands/safe-extract.ts +0 -444
  168. package/src/commands/secrets-scope.test.ts +0 -56
  169. package/src/commands/secrets-scope.ts +0 -32
  170. package/src/commands/secrets.parse-destination.test.ts +0 -38
  171. package/src/commands/secrets.test.ts +0 -2050
  172. package/src/commands/secrets.ts +0 -2249
  173. package/src/commands/signals.ts +0 -351
  174. package/src/commands/skill.test.ts +0 -770
  175. package/src/commands/skill.ts +0 -796
  176. package/src/commands/sources.ts +0 -362
  177. package/src/commands/sync-mode.test.ts +0 -366
  178. package/src/commands/sync-mode.ts +0 -387
  179. package/src/commands/sync-narrow.test.ts +0 -633
  180. package/src/commands/sync-narrow.ts +0 -555
  181. package/src/commands/sync.ts +0 -165
  182. package/src/commands/team-sync.ts +0 -628
  183. package/src/commands/update.ts +0 -71
  184. package/src/commands/whoami.ts +0 -83
  185. package/src/commands/workers.test.ts +0 -158
  186. package/src/commands/workers.ts +0 -301
  187. package/src/index.test.ts +0 -32
  188. package/src/index.ts +0 -20
  189. package/src/lib/db/control-plane.test.ts +0 -59
  190. package/src/lib/db/control-plane.ts +0 -113
  191. package/src/lib/db/local.test.ts +0 -81
  192. package/src/lib/db/local.ts +0 -148
  193. package/src/lib/db/migrate.test.ts +0 -133
  194. package/src/lib/db/migrate.ts +0 -137
  195. package/src/lib/db/paths.test.ts +0 -112
  196. package/src/lib/db/paths.ts +0 -128
  197. package/src/lib/db/remote-engine.test.ts +0 -44
  198. package/src/lib/db/remote-engine.ts +0 -148
  199. package/src/lib/db/remote-sql.test.ts +0 -32
  200. package/src/lib/db/remote-sql.ts +0 -62
  201. package/src/lib/db/sql.test.ts +0 -106
  202. package/src/lib/db/sql.ts +0 -192
  203. package/src/lib/hq-cloud-dep.smoke.test.ts +0 -75
  204. package/src/lib/local-tree-diff.test.ts +0 -301
  205. package/src/lib/local-tree-diff.ts +0 -362
  206. package/src/lib/narrow-hint-banner.test.ts +0 -235
  207. package/src/lib/narrow-hint-banner.ts +0 -212
  208. package/src/lib/plan-limit-nag.test.ts +0 -317
  209. package/src/lib/plan-limit-nag.ts +0 -264
  210. package/src/main.ts +0 -371
  211. package/src/node-preflight.test.ts +0 -60
  212. package/src/node-preflight.ts +0 -67
  213. package/src/outpost/session-heartbeat-bounds.test.ts +0 -195
  214. package/src/outpost/session-heartbeat-guard.test.ts +0 -105
  215. package/src/outpost/session-heartbeat-publisher.test.ts +0 -178
  216. package/src/outpost/session-heartbeat-publisher.ts +0 -186
  217. package/src/outpost/session-heartbeat-retain-guard.test.ts +0 -126
  218. package/src/outpost/session-heartbeat.test.ts +0 -459
  219. package/src/outpost/session-heartbeat.ts +0 -877
  220. package/src/packaging.test.ts +0 -109
  221. package/src/run/__fixtures__/discover-schemas/example.env.schema +0 -4
  222. package/src/run/discover-schemas.test.ts +0 -153
  223. package/src/run/discover-schemas.ts +0 -79
  224. package/src/run/hq-plugin.test.ts +0 -371
  225. package/src/run/hq-plugin.ts +0 -277
  226. package/src/run/varlock-shape.test.ts +0 -57
  227. package/src/schemas/hq-package.schema.json +0 -155
  228. package/src/sentry-before-send.test.ts +0 -359
  229. package/src/sentry-before-send.ts +0 -221
  230. package/src/sentry-epipe.test.ts +0 -37
  231. package/src/sentry-release.test.ts +0 -54
  232. package/src/sentry.test.ts +0 -24
  233. package/src/sentry.ts +0 -54
  234. package/src/strategies/link.ts +0 -62
  235. package/src/strategies/merge.ts +0 -142
  236. package/src/types.ts +0 -146
  237. package/src/utils/__fixtures__/scan-packages.generated-block.sh +0 -23
  238. package/src/utils/auth-error.test.ts +0 -40
  239. package/src/utils/auth-error.ts +0 -42
  240. package/src/utils/billing-gate.test.ts +0 -95
  241. package/src/utils/billing-gate.ts +0 -228
  242. package/src/utils/breadcrumb-buffer.ts +0 -18
  243. package/src/utils/cli-telemetry.test.ts +0 -153
  244. package/src/utils/cli-telemetry.ts +0 -61
  245. package/src/utils/cognito-session.machine.test.ts +0 -109
  246. package/src/utils/cognito-session.refresh.test.ts +0 -84
  247. package/src/utils/cognito-session.test.ts +0 -328
  248. package/src/utils/cognito-session.ts +0 -448
  249. package/src/utils/company-selection-error.test.ts +0 -42
  250. package/src/utils/company-selection-error.ts +0 -45
  251. package/src/utils/contribution-table.ts +0 -83
  252. package/src/utils/environmental-error.test.ts +0 -45
  253. package/src/utils/environmental-error.ts +0 -39
  254. package/src/utils/epipe.test.ts +0 -28
  255. package/src/utils/epipe.ts +0 -29
  256. package/src/utils/expected-cli-error.test.ts +0 -28
  257. package/src/utils/expected-cli-error.ts +0 -39
  258. package/src/utils/feedback-diagnostics.test.ts +0 -183
  259. package/src/utils/feedback-diagnostics.ts +0 -123
  260. package/src/utils/feedback-screenshots.test.ts +0 -134
  261. package/src/utils/feedback-screenshots.ts +0 -124
  262. package/src/utils/feedback-versions.test.ts +0 -98
  263. package/src/utils/feedback-versions.ts +0 -68
  264. package/src/utils/git.ts +0 -74
  265. package/src/utils/integrity.ts +0 -59
  266. package/src/utils/intercepted-process-exit.test.ts +0 -37
  267. package/src/utils/intercepted-process-exit.ts +0 -36
  268. package/src/utils/login-provider.test.ts +0 -42
  269. package/src/utils/login-provider.ts +0 -30
  270. package/src/utils/manifest.test.ts +0 -167
  271. package/src/utils/manifest.ts +0 -131
  272. package/src/utils/pack-contributions.test.ts +0 -603
  273. package/src/utils/pack-contributions.ts +0 -450
  274. package/src/utils/people.ts +0 -215
  275. package/src/utils/registry-client.ts +0 -204
  276. package/src/utils/registry.ts +0 -90
  277. package/src/utils/resolve-vault-credential.test.ts +0 -69
  278. package/src/utils/resolve-vault-credential.ts +0 -60
  279. package/src/utils/sandbox-runner-client.test.ts +0 -279
  280. package/src/utils/sandbox-runner-client.ts +0 -260
  281. package/src/utils/secrets-cache.ts +0 -183
  282. package/src/utils/sentry-identity.ts +0 -45
  283. package/src/utils/settle-with-timeout.test.ts +0 -21
  284. package/src/utils/settle-with-timeout.ts +0 -22
  285. package/src/utils/user-yaml-error.test.ts +0 -24
  286. package/src/utils/user-yaml-error.ts +0 -30
  287. package/src/utils/vault-api.test.ts +0 -536
  288. package/src/utils/vault-api.ts +0 -382
  289. package/src/utils/version-check.test.ts +0 -176
  290. package/src/utils/version-check.ts +0 -155
  291. package/src/utils/version-gate.test.ts +0 -1024
  292. package/src/utils/version-gate.ts +0 -702
  293. package/test/commands/db-tenant-isolation.test.ts +0 -94
  294. package/test/commands/db.test.ts +0 -85
  295. package/test/commands/signals.test.ts +0 -207
  296. package/test/commands/sources.test.ts +0 -231
  297. package/test/e2e/smoke-install-mcp.sh +0 -113
  298. package/test/fixtures/hq-pack-smoke-mcp/mcp/smoke-http.json +0 -1
  299. package/test/fixtures/hq-pack-smoke-mcp/package.yaml +0 -11
  300. package/test/fixtures/signals/action_item/sample.md +0 -16
  301. package/test/fixtures/signals/summary/sample.md +0 -12
  302. package/test/fixtures/sources/meetings/sample.md +0 -25
  303. package/test/helpers/cli-runner.ts +0 -150
  304. package/test/helpers/s3-list-mock.ts +0 -79
  305. package/test/helpers/vault-service-mock.ts +0 -295
  306. package/test/sources-signals/smoke.test.ts +0 -228
  307. package/tsconfig.json +0 -20
  308. package/vitest.config.ts +0 -11
  309. 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