@erclx/aitk 0.8.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 (359) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +99 -0
  3. package/claude/.claude-plugin/plugin.json +19 -0
  4. package/claude/skills/bash-script/SKILL.md +96 -0
  5. package/claude/skills/bash-script/references/patterns.md +349 -0
  6. package/claude/skills/ci-workflow/SKILL.md +52 -0
  7. package/claude/skills/ci-workflow/references/workflows.md +74 -0
  8. package/claude/skills/claude-address-review/SKILL.md +107 -0
  9. package/claude/skills/claude-autoship/SKILL.md +137 -0
  10. package/claude/skills/claude-design-extract/SKILL.md +69 -0
  11. package/claude/skills/claude-design-propose/SKILL.md +75 -0
  12. package/claude/skills/claude-diagram/SKILL.md +126 -0
  13. package/claude/skills/claude-docs/SKILL.md +204 -0
  14. package/claude/skills/claude-feature/REQUIREMENT.md +35 -0
  15. package/claude/skills/claude-feature/SKILL.md +148 -0
  16. package/claude/skills/claude-groundwork/SKILL.md +148 -0
  17. package/claude/skills/claude-groundwork/references/folder-format.md +107 -0
  18. package/claude/skills/claude-memory-capture/SKILL.md +80 -0
  19. package/claude/skills/claude-memory-review/REQUIREMENT.md +37 -0
  20. package/claude/skills/claude-memory-review/SKILL.md +206 -0
  21. package/claude/skills/claude-orchestrate/SKILL.md +125 -0
  22. package/claude/skills/claude-pr-review/SKILL.md +150 -0
  23. package/claude/skills/claude-review/SKILL.md +136 -0
  24. package/claude/skills/claude-roadmap/SKILL.md +61 -0
  25. package/claude/skills/claude-roadmap/references/roadmap.md +52 -0
  26. package/claude/skills/claude-screencast/SKILL.md +160 -0
  27. package/claude/skills/claude-seed-sync/SKILL.md +141 -0
  28. package/claude/skills/claude-slides-draft/SKILL.md +62 -0
  29. package/claude/skills/claude-standards-audit/REQUIREMENT.md +33 -0
  30. package/claude/skills/claude-standards-audit/SKILL.md +79 -0
  31. package/claude/skills/claude-standards-audit/references/branch.md +49 -0
  32. package/claude/skills/claude-standards-audit/references/pr.md +124 -0
  33. package/claude/skills/claude-tasks/SKILL.md +123 -0
  34. package/claude/skills/claude-ui-test/SKILL.md +75 -0
  35. package/claude/skills/claude-ux-audit/SKILL.md +78 -0
  36. package/claude/skills/claude-worktree/SKILL.md +79 -0
  37. package/claude/skills/cli-script/SKILL.md +47 -0
  38. package/claude/skills/cli-script/references/template.md +43 -0
  39. package/claude/skills/create-rule/SKILL.md +68 -0
  40. package/claude/skills/create-skill/REQUIREMENT.md +30 -0
  41. package/claude/skills/create-skill/SKILL.md +24 -0
  42. package/claude/skills/create-snippet/SKILL.md +28 -0
  43. package/claude/skills/create-snippet/references/snippets.md +57 -0
  44. package/claude/skills/create-standard/SKILL.md +31 -0
  45. package/claude/skills/create-standard/references/standard.md +52 -0
  46. package/claude/skills/docs-sync/SKILL.md +87 -0
  47. package/claude/skills/git-branch/REQUIREMENT.md +38 -0
  48. package/claude/skills/git-branch/SKILL.md +60 -0
  49. package/claude/skills/git-branch/references/branch.md +49 -0
  50. package/claude/skills/git-commit/REQUIREMENT.md +33 -0
  51. package/claude/skills/git-commit/SKILL.md +51 -0
  52. package/claude/skills/git-commit/references/commit.md +63 -0
  53. package/claude/skills/git-followup/REQUIREMENT.md +38 -0
  54. package/claude/skills/git-followup/SKILL.md +35 -0
  55. package/claude/skills/git-issue/REQUIREMENT.md +38 -0
  56. package/claude/skills/git-issue/SKILL.md +69 -0
  57. package/claude/skills/git-issue/references/issue.md +84 -0
  58. package/claude/skills/git-pr/REQUIREMENT.md +37 -0
  59. package/claude/skills/git-pr/SKILL.md +119 -0
  60. package/claude/skills/git-pr/references/branch.md +49 -0
  61. package/claude/skills/git-pr/references/pr.md +124 -0
  62. package/claude/skills/git-ship/REQUIREMENT.md +34 -0
  63. package/claude/skills/git-ship/SKILL.md +39 -0
  64. package/claude/skills/git-split/REQUIREMENT.md +38 -0
  65. package/claude/skills/git-split/SKILL.md +160 -0
  66. package/claude/skills/git-split/references/branch.md +49 -0
  67. package/claude/skills/git-split/references/pr.md +124 -0
  68. package/claude/skills/git-stage/REQUIREMENT.md +36 -0
  69. package/claude/skills/git-stage/SKILL.md +74 -0
  70. package/claude/skills/git-stage/references/commit.md +63 -0
  71. package/claude/skills/git-worktree/REQUIREMENT.md +37 -0
  72. package/claude/skills/git-worktree/SKILL.md +120 -0
  73. package/claude/skills/migration-claude-md/SKILL.md +74 -0
  74. package/claude/skills/migration-context/SKILL.md +93 -0
  75. package/claude/skills/migration-standards/SKILL.md +75 -0
  76. package/claude/skills/project-commands/SKILL.md +73 -0
  77. package/claude/skills/session-resume/SKILL.md +36 -0
  78. package/claude/skills/setup-gov/SKILL.md +71 -0
  79. package/claude/skills/setup-indexes/SKILL.md +148 -0
  80. package/claude/skills/setup-init/SKILL.md +114 -0
  81. package/claude/skills/setup-plugins/SKILL.md +81 -0
  82. package/claude/skills/setup-plugins/references/plugin-catalog.md +53 -0
  83. package/claude/skills/setup-verify/SKILL.md +51 -0
  84. package/claude/skills/systematic-debugging/SKILL.md +70 -0
  85. package/claude/skills/toolkit-cli/SKILL.md +38 -0
  86. package/claude/skills/toolkit-feedback/SKILL.md +80 -0
  87. package/claude/skills/toolkit-operator/SKILL.md +45 -0
  88. package/claude/skills/toolkit-triage/SKILL.md +63 -0
  89. package/claude/skills/youtube-transcripts/SKILL.md +34 -0
  90. package/docs/agents.md +371 -0
  91. package/docs/ai-workflow.md +157 -0
  92. package/docs/index.md +20 -0
  93. package/docs/operating-model.md +106 -0
  94. package/docs/target-projects.md +183 -0
  95. package/docs/visual-design-workflow.md +136 -0
  96. package/docs/zshrc-aliases.md +65 -0
  97. package/governance/rules/claude/500-prose.md +12 -0
  98. package/governance/rules/claude/510-context.md +16 -0
  99. package/governance/rules/claude/520-wireframes.md +12 -0
  100. package/governance/rules/claude/530-requirements.md +11 -0
  101. package/governance/rules/claude/540-architecture.md +11 -0
  102. package/governance/rules/claude/550-design.md +11 -0
  103. package/governance/rules/claude/555-tasks.md +12 -0
  104. package/governance/rules/claude/560-diagrams.md +11 -0
  105. package/governance/rules/claude/570-skill.md +18 -0
  106. package/governance/rules/claude/580-readme.md +11 -0
  107. package/governance/rules/claude/590-rule-authoring.md +12 -0
  108. package/governance/rules/core/000-constitution.md +30 -0
  109. package/governance/rules/core/010-testing.md +35 -0
  110. package/governance/rules/core/020-concurrency.md +22 -0
  111. package/governance/rules/core/030-error-handling.md +31 -0
  112. package/governance/rules/core/040-performance.md +20 -0
  113. package/governance/rules/core/050-logging.md +20 -0
  114. package/governance/rules/core/060-naming.md +19 -0
  115. package/governance/rules/core/070-planning.md +13 -0
  116. package/governance/rules/core/080-config-comments.md +22 -0
  117. package/governance/rules/framework/200-react.md +51 -0
  118. package/governance/rules/framework/210-astro.md +41 -0
  119. package/governance/rules/framework/220-fastapi.md +43 -0
  120. package/governance/rules/framework/230-nextjs.md +48 -0
  121. package/governance/rules/framework/250-tailwind.md +32 -0
  122. package/governance/rules/framework/260-shadcn.md +34 -0
  123. package/governance/rules/lang/100-typescript.md +40 -0
  124. package/governance/rules/lang/110-python.md +42 -0
  125. package/governance/rules/lib/300-testing-ts.md +38 -0
  126. package/governance/rules/lib/310-zod.md +25 -0
  127. package/governance/rules/lib/320-tanstack-query.md +32 -0
  128. package/governance/rules/lib/330-testing-py.md +44 -0
  129. package/governance/rules/lib/340-pydantic.md +38 -0
  130. package/governance/rules/lib/350-security-web.md +31 -0
  131. package/governance/rules/ui/400-ui.md +34 -0
  132. package/governance/rules/ui/410-a11y.md +44 -0
  133. package/governance/rules/ui/420-forms.md +34 -0
  134. package/governance/rules/ui/430-ux-completeness.md +62 -0
  135. package/governance/stacks/astro.toml +2 -0
  136. package/governance/stacks/base.toml +2 -0
  137. package/governance/stacks/node.toml +2 -0
  138. package/governance/stacks/python-fastapi.toml +2 -0
  139. package/governance/stacks/python.toml +2 -0
  140. package/governance/stacks/react.toml +2 -0
  141. package/package.json +67 -0
  142. package/scripts/config.sh +11 -0
  143. package/scripts/core/bootstrap.sh +81 -0
  144. package/scripts/core/check-plugin-boundary.sh +45 -0
  145. package/scripts/core/check-skill-paths.sh +25 -0
  146. package/scripts/core/clean.sh +36 -0
  147. package/scripts/core/install-check.sh +93 -0
  148. package/scripts/core/regen-claude-copies.sh +26 -0
  149. package/scripts/core/regen-indexes.sh +10 -0
  150. package/scripts/core/regen-skill-references.sh +27 -0
  151. package/scripts/core/snapshot.sh +132 -0
  152. package/scripts/core/update.sh +35 -0
  153. package/scripts/core/verify.sh +201 -0
  154. package/scripts/docs/list.sh +129 -0
  155. package/scripts/gov/list.sh +234 -0
  156. package/scripts/lib/frontmatter.sh +30 -0
  157. package/scripts/lib/gov.sh +14 -0
  158. package/scripts/lib/sandbox-fixtures.sh +109 -0
  159. package/scripts/lib/sandbox-git.sh +73 -0
  160. package/scripts/lib/tooling.sh +24 -0
  161. package/scripts/lib/ui.sh +173 -0
  162. package/scripts/lib/worktree.sh +20 -0
  163. package/scripts/manage-sandbox.sh +453 -0
  164. package/scripts/snippets/create.sh +156 -0
  165. package/scripts/standards/list.sh +91 -0
  166. package/scripts/tooling/create.sh +109 -0
  167. package/scripts/tooling/ref.sh +141 -0
  168. package/scripts/tooling/verify.sh +179 -0
  169. package/snippets/claude/decision-memo.md +39 -0
  170. package/snippets/claude/feature-recap.md +19 -0
  171. package/snippets/claude/figma-steps.md +18 -0
  172. package/snippets/claude/orchestrator-resume.md +23 -0
  173. package/snippets/claude/orchestrator-sweep.md +10 -0
  174. package/snippets/compact-summary.md +5 -0
  175. package/snippets/create-snippet.md +7 -0
  176. package/snippets/decision-help.md +6 -0
  177. package/snippets/meta-prompt.md +14 -0
  178. package/snippets/research-prompt.md +7 -0
  179. package/snippets/session-notes.md +11 -0
  180. package/snippets/snippets.toml +6 -0
  181. package/snippets/step-by-step.md +10 -0
  182. package/snippets/web-research.md +21 -0
  183. package/src/claude/gitignore.ts +51 -0
  184. package/src/claude/seeds-list.ts +47 -0
  185. package/src/claude/seeds.ts +124 -0
  186. package/src/claude/settings.ts +151 -0
  187. package/src/cli-run.ts +43 -0
  188. package/src/cli.ts +111 -0
  189. package/src/commands/claude.ts +349 -0
  190. package/src/commands/design.ts +39 -0
  191. package/src/commands/docs.ts +59 -0
  192. package/src/commands/feedback-format.ts +23 -0
  193. package/src/commands/feedback.ts +103 -0
  194. package/src/commands/gov.ts +268 -0
  195. package/src/commands/indexes.ts +182 -0
  196. package/src/commands/init.ts +117 -0
  197. package/src/commands/pass-through.ts +39 -0
  198. package/src/commands/sandbox.ts +315 -0
  199. package/src/commands/slides.ts +106 -0
  200. package/src/commands/snippets.ts +236 -0
  201. package/src/commands/standards.ts +89 -0
  202. package/src/commands/sync.ts +211 -0
  203. package/src/commands/tasks.ts +185 -0
  204. package/src/commands/tooling.ts +432 -0
  205. package/src/commands/transcripts.ts +48 -0
  206. package/src/commands/wiki.ts +101 -0
  207. package/src/copy.ts +30 -0
  208. package/src/design/parse.ts +79 -0
  209. package/src/design/render.ts +170 -0
  210. package/src/docs/read.ts +59 -0
  211. package/src/exec.ts +17 -0
  212. package/src/frontmatter.ts +13 -0
  213. package/src/github-format.ts +13 -0
  214. package/src/github.ts +20 -0
  215. package/src/gov/adapter.ts +73 -0
  216. package/src/gov/install.ts +103 -0
  217. package/src/gov/payload.ts +39 -0
  218. package/src/gov/stacks.ts +124 -0
  219. package/src/indexes/frontmatter.ts +46 -0
  220. package/src/indexes/regen.ts +84 -0
  221. package/src/indexes/render.ts +181 -0
  222. package/src/indexes/walk.ts +111 -0
  223. package/src/init/flags.ts +66 -0
  224. package/src/init/plan.ts +128 -0
  225. package/src/init/run.ts +46 -0
  226. package/src/init/steps.ts +91 -0
  227. package/src/sandbox/coverage.ts +115 -0
  228. package/src/sandbox/expect.ts +412 -0
  229. package/src/slides/layouts.ts +391 -0
  230. package/src/slides/open.ts +18 -0
  231. package/src/slides/parse.ts +84 -0
  232. package/src/slides/render.ts +88 -0
  233. package/src/slides/styles.ts +44 -0
  234. package/src/snippets/adapter.ts +34 -0
  235. package/src/snippets/categories.ts +59 -0
  236. package/src/snippets/install.ts +125 -0
  237. package/src/snippets/list.ts +32 -0
  238. package/src/snippets/presets.ts +50 -0
  239. package/src/standards/adapter.ts +51 -0
  240. package/src/standards/index-refresh.ts +44 -0
  241. package/src/standards/install.ts +52 -0
  242. package/src/sync/check.ts +269 -0
  243. package/src/sync/engine.ts +416 -0
  244. package/src/sync/git.ts +204 -0
  245. package/src/sync/stamp.ts +202 -0
  246. package/src/sync/target.ts +79 -0
  247. package/src/sync/workflow.ts +201 -0
  248. package/src/target.ts +43 -0
  249. package/src/tasks/archive.ts +304 -0
  250. package/src/tooling/gitignore.ts +122 -0
  251. package/src/tooling/inject.ts +214 -0
  252. package/src/tooling/list.ts +39 -0
  253. package/src/tooling/manifest.ts +178 -0
  254. package/src/tooling/package.ts +166 -0
  255. package/src/tooling/scan.ts +186 -0
  256. package/src/transcripts/fetch.ts +156 -0
  257. package/src/transcripts/metadata.ts +54 -0
  258. package/src/transcripts/vtt.ts +114 -0
  259. package/src/ui.ts +187 -0
  260. package/src/wiki/init.ts +85 -0
  261. package/standards/architecture.md +41 -0
  262. package/standards/bundled/branch.md +49 -0
  263. package/standards/bundled/commit.md +63 -0
  264. package/standards/bundled/issue.md +84 -0
  265. package/standards/bundled/pr.md +124 -0
  266. package/standards/bundled/roadmap.md +52 -0
  267. package/standards/bundled/snippets.md +57 -0
  268. package/standards/bundled/standard.md +52 -0
  269. package/standards/context.md +132 -0
  270. package/standards/design.md +31 -0
  271. package/standards/diagrams.md +55 -0
  272. package/standards/index.md +21 -0
  273. package/standards/prose.md +122 -0
  274. package/standards/readme.md +163 -0
  275. package/standards/requirements.md +61 -0
  276. package/standards/rule.md +79 -0
  277. package/standards/skill.md +257 -0
  278. package/standards/tasks.md +140 -0
  279. package/standards/versioning.md +47 -0
  280. package/standards/wireframes.md +67 -0
  281. package/tooling/astro/configs/astro.config.mjs +17 -0
  282. package/tooling/astro/configs/eslint.config.js +79 -0
  283. package/tooling/astro/configs/playwright.config.ts +25 -0
  284. package/tooling/astro/configs/tsconfig.json +12 -0
  285. package/tooling/astro/configs/vitest.config.ts +22 -0
  286. package/tooling/astro/manifest.toml +32 -0
  287. package/tooling/astro/reference.md +60 -0
  288. package/tooling/base/configs/.editorconfig +5 -0
  289. package/tooling/base/configs/.github/pull_request_template.md +18 -0
  290. package/tooling/base/configs/.github/workflows/verify.yml +35 -0
  291. package/tooling/base/configs/.husky/commit-msg +1 -0
  292. package/tooling/base/configs/.husky/post-merge +61 -0
  293. package/tooling/base/configs/.husky/post-rewrite +21 -0
  294. package/tooling/base/configs/.husky/pre-commit +1 -0
  295. package/tooling/base/configs/.husky/pre-push +1 -0
  296. package/tooling/base/configs/.prettierrc +12 -0
  297. package/tooling/base/configs/.shellcheckrc +1 -0
  298. package/tooling/base/configs/.vscode/extensions.json +9 -0
  299. package/tooling/base/configs/.vscode/settings.json +3 -0
  300. package/tooling/base/configs/commitlint.config.js +11 -0
  301. package/tooling/base/configs/scripts/clean.sh +45 -0
  302. package/tooling/base/configs/scripts/update.sh +49 -0
  303. package/tooling/base/configs/scripts/verify.sh +64 -0
  304. package/tooling/base/manifest.toml +32 -0
  305. package/tooling/base/reference.md +114 -0
  306. package/tooling/base/seeds/.claude/context/ci.md +27 -0
  307. package/tooling/base/seeds/.claude/context/development.md +32 -0
  308. package/tooling/base/seeds/.cspell/project-terms.txt +0 -0
  309. package/tooling/base/seeds/.cspell/tech-stack.txt +19 -0
  310. package/tooling/base/seeds/.lintstagedrc +8 -0
  311. package/tooling/base/seeds/.prettierignore +0 -0
  312. package/tooling/base/seeds/cspell.json +20 -0
  313. package/tooling/claude/manifest.toml +11 -0
  314. package/tooling/claude/reference.md +62 -0
  315. package/tooling/claude/seeds/.claude/ARCHITECTURE.md +13 -0
  316. package/tooling/claude/seeds/.claude/DESIGN.md +62 -0
  317. package/tooling/claude/seeds/.claude/REQUIREMENTS.md +18 -0
  318. package/tooling/claude/seeds/.claude/context/index.md +8 -0
  319. package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +43 -0
  320. package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +45 -0
  321. package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +61 -0
  322. package/tooling/claude/seeds/.claude/hooks/tasks-index.sh +60 -0
  323. package/tooling/claude/seeds/.claude/settings.json +39 -0
  324. package/tooling/claude/seeds/.claude/tasks/index.md +8 -0
  325. package/tooling/claude/seeds/.claude/wireframes/index.md +8 -0
  326. package/tooling/claude/seeds/CLAUDE.md +87 -0
  327. package/tooling/claude/user/settings.template.json +10 -0
  328. package/tooling/claude/user/statusline-command.sh +53 -0
  329. package/tooling/python/configs/.coveragerc +14 -0
  330. package/tooling/python/configs/.python-version +1 -0
  331. package/tooling/python/configs/mypy.ini +6 -0
  332. package/tooling/python/configs/pytest.ini +4 -0
  333. package/tooling/python/configs/ruff.toml +15 -0
  334. package/tooling/python/configs/scripts/verify.sh +77 -0
  335. package/tooling/python/manifest.toml +16 -0
  336. package/tooling/python/reference.md +66 -0
  337. package/tooling/python/seeds/.cspell/tech-stack.txt +19 -0
  338. package/tooling/python/seeds/tests/test_smoke.py +2 -0
  339. package/tooling/vite-react/configs/playwright.config.ts +25 -0
  340. package/tooling/vite-react/configs/tsconfig.json +35 -0
  341. package/tooling/vite-react/configs/vite.config.ts +14 -0
  342. package/tooling/vite-react/configs/vitest.config.ts +26 -0
  343. package/tooling/vite-react/manifest.toml +21 -0
  344. package/tooling/vite-react/reference.md +55 -0
  345. package/tooling/vite-react/seeds/.cspell/project-terms.txt +1 -0
  346. package/tooling/vite-react/seeds/.cspell/tech-stack.txt +1 -0
  347. package/tooling/web/configs/.github/workflows/verify.yml +134 -0
  348. package/tooling/web/configs/.vscode/extensions.json +13 -0
  349. package/tooling/web/configs/.vscode/settings.json +10 -0
  350. package/tooling/web/configs/e2e/home.spec.ts +6 -0
  351. package/tooling/web/configs/e2e/screenshot.ts +41 -0
  352. package/tooling/web/configs/eslint.config.js +82 -0
  353. package/tooling/web/configs/scripts/screenshot.sh +28 -0
  354. package/tooling/web/configs/scripts/verify.sh +80 -0
  355. package/tooling/web/configs/src/test/setup.ts +8 -0
  356. package/tooling/web/manifest.toml +57 -0
  357. package/tooling/web/reference.md +95 -0
  358. package/tooling/web/seeds/.cspell/tech-stack.txt +18 -0
  359. package/tsconfig.json +14 -0
@@ -0,0 +1,453 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+ set -o pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)"
6
+ PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
7
+ export PROJECT_ROOT
8
+
9
+ source "$PROJECT_ROOT/scripts/config.sh"
10
+ source "$PROJECT_ROOT/scripts/lib/ui.sh"
11
+ source "$PROJECT_ROOT/scripts/lib/sandbox-git.sh"
12
+ source "$PROJECT_ROOT/scripts/lib/sandbox-fixtures.sh"
13
+
14
+ # Without this, git falls back to a terminal prompt when no helper supplies a
15
+ # credential, and an unauthenticated run blocks on /dev/tty instead of failing.
16
+ # The harness must never require a TTY.
17
+ export GIT_TERMINAL_PROMPT=0
18
+
19
+ show_help() {
20
+ echo -e "${GREY}┌${NC}"
21
+ echo -e "${GREY}├${NC} ${WHITE}Usage:${NC} aitk sandbox [cat:cmd]"
22
+ echo -e "${GREY}│${NC}"
23
+ echo -e "${GREY}│${NC} ${WHITE}Arguments:${NC}"
24
+ echo -e "${GREY}│${NC} cat:cmd ${GREY}# Scenario to provision (e.g. git:commit)${NC}"
25
+ echo -e "${GREY}│${NC}"
26
+ echo -e "${GREY}│${NC} ${WHITE}Commands:${NC}"
27
+ echo -e "${GREY}│${NC} reset ${GREY}# Reset sandbox to baseline${NC}"
28
+ echo -e "${GREY}│${NC} clean ${GREY}# Wipe the sandbox${NC}"
29
+ echo -e "${GREY}│${NC}"
30
+ echo -e "${GREY}│${NC} ${WHITE}Options:${NC}"
31
+ echo -e "${GREY}│${NC} -h, --help ${GREY}# Show this help message${NC}"
32
+ echo -e "${GREY}│${NC}"
33
+ echo -e "${GREY}│${NC} ${WHITE}Examples:${NC}"
34
+ echo -e "${GREY}│${NC} aitk sandbox"
35
+ echo -e "${GREY}│${NC} aitk sandbox git:commit"
36
+ echo -e "${GREY}│${NC} aitk sandbox reset"
37
+ echo -e "${GREY}│${NC} aitk sandbox clean"
38
+ echo -e "${GREY}└${NC}"
39
+ exit 0
40
+ }
41
+
42
+ ANCHOR_FIXTURE_DIR="$PROJECT_ROOT/scripts/sandbox/fixtures/anchor/create"
43
+
44
+ # Where an anchor scenario's tree comes from and where its remote points are two
45
+ # concerns, and only the remote needs a network. Taking the tree from a fixture
46
+ # stops an arm provisioning from whatever the previous arm force-pushed, which is
47
+ # what made two identical sweeps of the sixteen arms disagree on eleven.
48
+ stage_anchor_tree() {
49
+ # The nine anchor scenarios configure a real remote from their own stage_setup,
50
+ # and sandbox_anchor_url runs inside a command substitution there, where
51
+ # log_error would exit only the subshell and leave an empty remote behind. The
52
+ # guard has to fire here, in the main shell, before any of them run.
53
+ require_sandbox_anchor_config
54
+
55
+ log_step "Staging anchor tree from fixture"
56
+
57
+ assert_fixtures_suffixed "$ANCHOR_FIXTURE_DIR"
58
+ if [ "$(count_fixture_files "$ANCHOR_FIXTURE_DIR")" -eq 0 ]; then
59
+ log_error "Anchor fixture provisions nothing: $ANCHOR_FIXTURE_DIR"
60
+ fi
61
+
62
+ if [ -d "$SANDBOX" ]; then
63
+ rm -rf "$SANDBOX"
64
+ fi
65
+ mkdir -p "$SANDBOX"
66
+
67
+ (
68
+ cd "$SANDBOX"
69
+ create_from_fixtures "$ANCHOR_FIXTURE_DIR"
70
+ git init >/dev/null
71
+ git add .
72
+ git commit -m "feat(sandbox): initial sandbox setup from anchor" --no-verify >/dev/null
73
+ )
74
+ log_info "Anchor tree staged from fixture, remote stays $ANCHOR_REPO"
75
+ }
76
+
77
+ select_sandbox_category() {
78
+ local categories=()
79
+ if ls -d "$SANDBOX_DIR"/*/ >/dev/null 2>&1; then
80
+ # Twin of FIXTURES_DIR in src/commands/sandbox.ts. The exec boundary rules out
81
+ # a shared constant, so a new exclusion has to land on both sides.
82
+ mapfile -t categories < <(find "$SANDBOX_DIR" -mindepth 1 -maxdepth 1 -type d -not -name fixtures -exec basename {} \; | sort)
83
+ fi
84
+
85
+ if [ ${#categories[@]} -eq 0 ]; then
86
+ log_error "No sandbox categories found in $SANDBOX_DIR"
87
+ fi
88
+
89
+ select_option "Select category:" "${categories[@]}"
90
+ _CATEGORY=$SELECTED_OPTION
91
+ }
92
+
93
+ select_sandbox_command() {
94
+ local commands=()
95
+ if ls "$SANDBOX_DIR/$_CATEGORY/"*.sh >/dev/null 2>&1; then
96
+ mapfile -t commands < <(find "$SANDBOX_DIR/$_CATEGORY" -maxdepth 1 -name '*.sh' -exec basename {} .sh \; | sort)
97
+ fi
98
+
99
+ if [ ${#commands[@]} -eq 0 ]; then
100
+ log_error "No sandbox scripts found in $_CATEGORY"
101
+ fi
102
+
103
+ select_option "Select command:" "${commands[@]}"
104
+ _COMMAND=$SELECTED_OPTION
105
+ }
106
+
107
+ prompt_for_category_and_command() {
108
+ select_sandbox_category
109
+ select_sandbox_command
110
+ }
111
+
112
+ parse_command_argument() {
113
+ local input_arg="$1"
114
+
115
+ if [[ "$input_arg" != *":"* ]]; then
116
+ log_error "Invalid format. Use <category>:<command>, 'reset', 'clean', or --help"
117
+ fi
118
+ IFS=':' read -r _CATEGORY _COMMAND <<<"$input_arg"
119
+ }
120
+
121
+ resolve_command_and_category() {
122
+ local input_arg="$1"
123
+ if [ -z "$input_arg" ]; then
124
+ prompt_for_category_and_command
125
+ else
126
+ parse_command_argument "$input_arg"
127
+ fi
128
+ }
129
+
130
+ validate_environment() {
131
+ local current_category="$1"
132
+ local current_command="$2"
133
+
134
+ if [ ! -d "$SANDBOX_DIR" ]; then
135
+ log_error "Sandbox directory not found at: $SANDBOX_DIR"
136
+ fi
137
+
138
+ if [[ "$PWD" == *".sandbox"* ]]; then
139
+ log_warn "Detected execution inside .sandbox. Switching to project root..."
140
+ cd "$PROJECT_ROOT" || log_error "Failed to switch to project root."
141
+ fi
142
+ }
143
+
144
+ load_sandbox_script() {
145
+ local current_category="$1"
146
+ local current_command="$2"
147
+
148
+ local sandbox_file="$SANDBOX_DIR/$current_category/$current_command.sh"
149
+
150
+ if [ ! -f "$sandbox_file" ]; then
151
+ log_error "Sandbox script not found: $current_category/$current_command"
152
+ fi
153
+
154
+ # shellcheck source=/dev/null
155
+ source "$sandbox_file"
156
+ }
157
+
158
+ init_empty_sandbox() {
159
+ if [ -d "$SANDBOX" ]; then
160
+ rm -rf "$SANDBOX"
161
+ fi
162
+ mkdir -p "$SANDBOX"
163
+
164
+ cat <<EOF >"$SANDBOX/.gitignore"
165
+ .claude/.tmp/
166
+ node_modules
167
+ EOF
168
+ (
169
+ cd "$SANDBOX"
170
+ git init >/dev/null
171
+ git add .gitignore >/dev/null 2>/dev/null
172
+ git commit -m "feat(sandbox): initial empty sandbox setup" --no-verify >/dev/null
173
+ )
174
+ }
175
+
176
+ provision_sandbox() {
177
+ log_step "Provisioning $1:$2"
178
+
179
+ if [[ "$(type -t use_anchor)" == "function" ]]; then
180
+ use_anchor
181
+ stage_anchor_tree
182
+ else
183
+ init_empty_sandbox
184
+ fi
185
+
186
+ # Scoped to the throwaway sandbox repo so the operator's global git config is
187
+ # never written. Covers both the scenario pushes and the ones the agent makes
188
+ # itself once it is running inside the sandbox.
189
+ (cd "$SANDBOX" && configure_sandbox_git_credentials)
190
+ }
191
+
192
+ # Runs the real installer rather than copying the source tree. The two copies
193
+ # these replaced each reimplemented an installer's selection rules, so a change
194
+ # to what install produces left the sandbox provisioning the old shape and no
195
+ # scenario could observe the difference.
196
+ run_sandbox_install() {
197
+ local label="$1"
198
+ shift
199
+
200
+ local install_log
201
+ install_log="$(mktemp)"
202
+
203
+ # The installer's own frame would bury the provisioning timeline, so it stays
204
+ # captured unless it fails, where it is the only thing naming the cause.
205
+ if ! AITK_NON_INTERACTIVE=1 bun "$PROJECT_ROOT/src/cli.ts" "$@" \
206
+ >/dev/null 2>"$install_log"; then
207
+ cat "$install_log" >&2
208
+ rm -f "$install_log"
209
+ log_error "Could not install $label into the sandbox."
210
+ fi
211
+
212
+ rm -f "$install_log"
213
+ }
214
+
215
+ inject_documentation() {
216
+ [ ! -d "$PROJECT_ROOT/standards" ] && return
217
+
218
+ run_sandbox_install "standards" standards install "$SANDBOX"
219
+ }
220
+
221
+ # The stack decides which rules land. `base` carries the language-agnostic core,
222
+ # which is what a scenario asserting on rule behavior reads. A scenario needing a
223
+ # framework's rules overrides the variable in `use_config`.
224
+ inject_gov_rules() {
225
+ [ ! -d "$PROJECT_ROOT/governance/rules" ] && return
226
+
227
+ run_sandbox_install "gov rules" gov install "${SANDBOX_GOV_STACK:-base}" "$SANDBOX"
228
+ }
229
+
230
+ inject_seeds() {
231
+ local seeds_source="$PROJECT_ROOT/tooling/claude/seeds"
232
+ if [ -d "$seeds_source" ]; then
233
+ cp -r "$seeds_source/." "$SANDBOX/"
234
+ fi
235
+ }
236
+
237
+ commit_environment_setup() {
238
+ (
239
+ cd "$SANDBOX"
240
+ git add .
241
+ if ! git diff --cached --quiet; then
242
+ git commit -m "chore(sandbox): initial environment setup" --no-verify >/dev/null
243
+ fi
244
+ )
245
+ }
246
+
247
+ setup_sandbox_assets() {
248
+ [ -n "$SANDBOX_INJECT_SEEDS" ] && inject_seeds
249
+ [ -n "$SANDBOX_INJECT_STANDARDS" ] && inject_documentation
250
+ [ -n "$SANDBOX_INJECT_GOV" ] && inject_gov_rules
251
+ commit_environment_setup
252
+ }
253
+
254
+ initialize_sandbox_environment() {
255
+ local current_category="$1"
256
+ local current_command="$2"
257
+
258
+ load_sandbox_script "$current_category" "$current_command"
259
+ [[ "$(type -t use_config)" == "function" ]] && use_config
260
+ validate_environment "$current_category" "$current_command"
261
+ provision_sandbox "$current_category" "$current_command"
262
+ setup_sandbox_assets
263
+ }
264
+
265
+ commit_sandbox_changes() {
266
+ if [ -z "$SANDBOX_SKIP_AUTO_COMMIT" ]; then
267
+ log_step "Staging environment changes"
268
+ (
269
+ cd "$SANDBOX"
270
+ git add . >/dev/null 2>/dev/null
271
+ git commit -m 'chore(sandbox): apply scenario specific setup' --no-verify >/dev/null
272
+ )
273
+ log_info "Git state clean after setup"
274
+ else
275
+ log_info "Skipping auto-commit"
276
+ fi
277
+ }
278
+
279
+ tag_sandbox_baseline() {
280
+ (
281
+ cd "$SANDBOX"
282
+ git update-ref refs/sandbox/baseline HEAD >/dev/null 2>&1
283
+ git write-tree | xargs -I {} git update-ref refs/sandbox/baseline-index {} >/dev/null 2>&1
284
+ )
285
+ }
286
+
287
+ inject_changed_skills() {
288
+ local changed untracked
289
+ changed=$(git -C "$PROJECT_ROOT" diff main --name-only -- 'claude/skills/**/SKILL.md' 2>/dev/null)
290
+ untracked=$(git -C "$PROJECT_ROOT" ls-files --others --exclude-standard -- 'claude/skills/**/SKILL.md' 2>/dev/null)
291
+
292
+ local combined
293
+ combined=$(printf '%s\n%s\n' "$changed" "$untracked" | awk 'NF && !seen[$0]++')
294
+
295
+ [ -z "$combined" ] && return
296
+
297
+ while IFS= read -r skill_path; do
298
+ local skill_name
299
+ skill_name=$(basename "$(dirname "$skill_path")")
300
+ local target_dir="$SANDBOX/.claude/skills/$skill_name"
301
+ mkdir -p "$target_dir"
302
+ cp "$PROJECT_ROOT/$skill_path" "$target_dir/SKILL.md"
303
+ log_info "Injected dev skill: $skill_name"
304
+ done <<<"$combined"
305
+ }
306
+
307
+ execute_sandbox_and_commit() {
308
+ pushd "$SANDBOX" >/dev/null
309
+ stage_setup
310
+ popd >/dev/null
311
+
312
+ inject_changed_skills
313
+ commit_sandbox_changes
314
+ tag_sandbox_baseline
315
+ }
316
+
317
+ finalize_sandbox_run() {
318
+ trap - EXIT
319
+ close_timeline
320
+ echo "" >&2
321
+ echo -e "${GREEN}✓ Sandbox Ready${NC}" >&2
322
+ }
323
+
324
+ cmd_clean() {
325
+ log_step "Removing sandbox"
326
+ rm -rf "$SANDBOX"
327
+ log_rem ".sandbox/"
328
+ trap - EXIT
329
+ close_timeline
330
+ echo "" >&2
331
+ echo -e "${GREEN}✓ Sandbox clean${NC}" >&2
332
+ }
333
+
334
+ reset_sandbox() {
335
+ if [ ! -d "$SANDBOX/.git" ]; then
336
+ log_error "No sandbox found. Run \`aitk sandbox\` first."
337
+ fi
338
+
339
+ log_step "Sandbox state"
340
+
341
+ local has_baseline=0
342
+ (cd "$SANDBOX" && git rev-parse refs/sandbox/baseline >/dev/null 2>&1) && has_baseline=1
343
+
344
+ if [ "$has_baseline" -eq 0 ]; then
345
+ log_error "No baseline found. Re-provision with \`aitk sandbox <cat>:<cmd>\`."
346
+ fi
347
+
348
+ local is_dirty=0
349
+ (
350
+ cd "$SANDBOX"
351
+ if [ "$(git rev-parse HEAD)" != "$(git rev-parse refs/sandbox/baseline)" ]; then
352
+ exit 1
353
+ fi
354
+ local current_index baseline_index
355
+ current_index=$(git write-tree)
356
+ baseline_index=$(git rev-parse refs/sandbox/baseline-index 2>/dev/null || echo "")
357
+ if [ -n "$baseline_index" ] && [ "$current_index" != "$baseline_index" ]; then
358
+ exit 1
359
+ fi
360
+ if ! git diff --quiet 2>/dev/null; then
361
+ exit 1
362
+ fi
363
+ if [ -n "$(git ls-files --others --exclude-standard 2>/dev/null)" ]; then
364
+ exit 1
365
+ fi
366
+ ) || is_dirty=1
367
+
368
+ if [ "$is_dirty" -eq 0 ]; then
369
+ log_info "At baseline"
370
+ trap - EXIT
371
+ close_timeline
372
+ echo "" >&2
373
+ echo -e "${GREEN}✓ Sandbox at baseline${NC}" >&2
374
+ exit 0
375
+ fi
376
+
377
+ log_warn "Uncommitted changes detected"
378
+
379
+ select_option "Reset sandbox to initial state?" "Yes" "No"
380
+ if [ "$SELECTED_OPTION" == "No" ]; then
381
+ log_warn "Reset cancelled"
382
+ exit 0
383
+ fi
384
+
385
+ log_step "Resetting sandbox"
386
+ (
387
+ cd "$SANDBOX"
388
+ git reset --hard refs/sandbox/baseline --quiet
389
+ git clean -fd --quiet
390
+ local baseline_index
391
+ baseline_index=$(git rev-parse refs/sandbox/baseline-index 2>/dev/null || echo "")
392
+ if [ -n "$baseline_index" ] && [ "$baseline_index" != "$(git write-tree)" ]; then
393
+ git read-tree "$baseline_index"
394
+ git checkout-index -a -f
395
+ fi
396
+ )
397
+ log_info "Sandbox reset to baseline"
398
+
399
+ trap - EXIT
400
+ close_timeline
401
+ echo "" >&2
402
+ echo -e "${GREEN}✓ Sandbox reset complete${NC}" >&2
403
+ }
404
+
405
+ main() {
406
+ local no_header=0
407
+ if [[ "$1" == "--no-header" ]]; then
408
+ no_header=1
409
+ shift
410
+ fi
411
+
412
+ if [[ "$1" == "-h" || "$1" == "--help" ]]; then
413
+ show_help
414
+ fi
415
+
416
+ if [[ "$no_header" -eq 0 ]]; then
417
+ open_timeline "aitk sandbox"
418
+ fi
419
+ trap close_timeline EXIT
420
+
421
+ if [[ "$PWD" != "$PROJECT_ROOT"* ]]; then
422
+ log_error "Context error: you must run this command from inside the toolkit repository."
423
+ fi
424
+
425
+ SANDBOX="$PROJECT_ROOT/.sandbox"
426
+ SANDBOX_DIR="$PROJECT_ROOT/scripts/sandbox"
427
+
428
+ if [[ "$1" == "reset" ]]; then
429
+ reset_sandbox
430
+ exit 0
431
+ fi
432
+
433
+ if [[ "$1" == "clean" ]]; then
434
+ cmd_clean
435
+ exit 0
436
+ fi
437
+
438
+ resolve_command_and_category "$1"
439
+ local category="$_CATEGORY"
440
+ local command="$_COMMAND"
441
+
442
+ if [ -n "${2:-}" ]; then
443
+ export SANDBOX_SCENARIO="$2"
444
+ export AITK_NON_INTERACTIVE="1"
445
+ fi
446
+
447
+ initialize_sandbox_environment "$category" "$command"
448
+ execute_sandbox_and_commit
449
+
450
+ finalize_sandbox_run
451
+ }
452
+
453
+ main "$@"
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+ set -o pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ PROJECT_ROOT="${PROJECT_ROOT:-$(dirname "$(dirname "$SCRIPT_DIR")")}"
7
+
8
+ source "$PROJECT_ROOT/scripts/lib/ui.sh"
9
+ trap close_timeline EXIT
10
+
11
+ SNIPPETS_SOURCE="$PROJECT_ROOT/snippets"
12
+
13
+ show_help() {
14
+ echo -e "${GREY}┌${NC}"
15
+ echo -e "${GREY}├${NC} ${WHITE}Usage:${NC} aitk snippets create"
16
+ echo -e "${GREY}│${NC}"
17
+ echo -e "${GREY}│${NC} Creates a new snippet: prompts for category and name, writes the file."
18
+ echo -e "${GREY}│${NC}"
19
+ echo -e "${GREY}│${NC} ${WHITE}Options:${NC}"
20
+ echo -e "${GREY}│${NC} -h, --help ${GREY}# Show this help message${NC}"
21
+ echo -e "${GREY}└${NC}"
22
+ exit 0
23
+ }
24
+
25
+ list_categories() {
26
+ echo "base (root, no prefix)"
27
+ find "$SNIPPETS_SOURCE" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sort
28
+ echo "new category"
29
+ }
30
+
31
+ slug_error() {
32
+ local slug="$1"
33
+ local path="$2"
34
+ if [ -z "$slug" ]; then
35
+ echo "name cannot be empty"
36
+ return
37
+ fi
38
+ if [[ ! "$slug" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
39
+ echo "kebab-case only (e.g. my-snippet)"
40
+ return
41
+ fi
42
+ if [ -f "$path" ]; then
43
+ echo "file already exists: $path"
44
+ return
45
+ fi
46
+ echo ""
47
+ }
48
+
49
+ ask_new_category() {
50
+ while true; do
51
+ ask "Category name?" "CATEGORY_INPUT"
52
+ if [ -z "$CATEGORY_INPUT" ]; then
53
+ log_warn "Category name cannot be empty"
54
+ continue
55
+ fi
56
+ if [[ ! "$CATEGORY_INPUT" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then
57
+ log_warn "Invalid category: kebab-case only (e.g. my-category)"
58
+ continue
59
+ fi
60
+ if [ -d "$SNIPPETS_SOURCE/$CATEGORY_INPUT" ]; then
61
+ log_warn "Category '$CATEGORY_INPUT' already exists"
62
+ continue
63
+ fi
64
+ CATEGORY_NAME="$CATEGORY_INPUT"
65
+ break
66
+ done
67
+ }
68
+
69
+ ask_name() {
70
+ local category="$1"
71
+ local is_base="$2"
72
+ local label
73
+ local error
74
+
75
+ label="Snippet name?"
76
+
77
+ while true; do
78
+ ask "$label" "NAME_INPUT"
79
+ local dest_path
80
+ if [ "$is_base" = "true" ]; then
81
+ dest_path="$SNIPPETS_SOURCE/$NAME_INPUT.md"
82
+ else
83
+ dest_path="$SNIPPETS_SOURCE/$category/$NAME_INPUT.md"
84
+ fi
85
+ error=$(slug_error "$NAME_INPUT" "$dest_path")
86
+ if [ -z "$error" ]; then
87
+ NAME="$NAME_INPUT"
88
+ break
89
+ fi
90
+ log_warn "Invalid name: $error"
91
+ done
92
+ }
93
+
94
+ cmd_create() {
95
+ local categories=()
96
+ mapfile -t categories < <(list_categories)
97
+
98
+ select_option "Category?" "${categories[@]}"
99
+ local selected="$SELECTED_OPTION"
100
+
101
+ local category
102
+ local is_base="false"
103
+
104
+ if [ "$selected" = "base (root, no prefix)" ]; then
105
+ is_base="true"
106
+ category="base"
107
+ elif [ "$selected" = "new category" ]; then
108
+ ask_new_category
109
+ category="$CATEGORY_NAME"
110
+ else
111
+ category="$selected"
112
+ fi
113
+
114
+ ask_name "$category" "$is_base"
115
+ local name="$NAME"
116
+
117
+ local dest_path
118
+ local install_path
119
+ if [ "$is_base" = "true" ]; then
120
+ dest_path="$SNIPPETS_SOURCE/$name.md"
121
+ install_path="snippets/$name"
122
+ else
123
+ dest_path="$SNIPPETS_SOURCE/$category/$name.md"
124
+ install_path="snippets/$category/$name"
125
+ fi
126
+
127
+ log_info "Installs as: @${install_path}"
128
+
129
+ log_step "Creating file"
130
+
131
+ if [ "$is_base" = "false" ]; then
132
+ mkdir -p "$SNIPPETS_SOURCE/$category"
133
+ fi
134
+
135
+ printf '<!-- TODO: write %s prompt -->\n' "$slug" >"$dest_path"
136
+
137
+ if [ "$is_base" = "true" ]; then
138
+ log_add "snippets/$name.md"
139
+ else
140
+ log_add "snippets/$category/$name.md"
141
+ fi
142
+ }
143
+
144
+ main() {
145
+ if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
146
+ show_help
147
+ fi
148
+
149
+ cmd_create
150
+
151
+ trap - EXIT
152
+ echo -e "${GREY}└${NC}\n"
153
+ echo -e "${GREEN}✓ Snippet created${NC}"
154
+ }
155
+
156
+ main "$@"
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+ set -o pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ PROJECT_ROOT="${PROJECT_ROOT:-$(dirname "$(dirname "$SCRIPT_DIR")")}"
7
+
8
+ source "$PROJECT_ROOT/scripts/lib/ui.sh"
9
+ source "$PROJECT_ROOT/scripts/lib/frontmatter.sh"
10
+
11
+ STANDARDS_DIR="$PROJECT_ROOT/standards"
12
+
13
+ show_help() {
14
+ echo -e "${GREY}┌${NC}"
15
+ echo -e "${GREY}├${NC} ${WHITE}Usage:${NC} aitk standards list [options]"
16
+ echo -e "${GREY}│${NC}"
17
+ echo -e "${GREY}│${NC} ${WHITE}Options:${NC}"
18
+ echo -e "${GREY}│${NC} --json ${GREY}# Emit machine-readable JSON${NC}"
19
+ echo -e "${GREY}│${NC} -h, --help ${GREY}# Show this help message${NC}"
20
+ echo -e "${GREY}└${NC}"
21
+ exit 0
22
+ }
23
+
24
+ json_escape() {
25
+ local s="$1"
26
+ s="${s//\\/\\\\}"
27
+ s="${s//\"/\\\"}"
28
+ printf '%s' "$s"
29
+ }
30
+
31
+ list_text() {
32
+ log_step "Standards"
33
+ local file name title
34
+ while IFS= read -r file; do
35
+ name=$(basename "$file" .md)
36
+ [ "$name" = "index" ] && continue
37
+ title=$(read_frontmatter_field "$file" "description")
38
+ log_info "$name : $title"
39
+ done < <(find "$STANDARDS_DIR" -maxdepth 1 -type f -name "*.md" | sort)
40
+ }
41
+
42
+ list_json() {
43
+ local first=1
44
+ local file name title target
45
+ printf '['
46
+ while IFS= read -r file; do
47
+ name=$(basename "$file" .md)
48
+ [ "$name" = "index" ] && continue
49
+ title=$(read_frontmatter_field "$file" "description")
50
+ target=".claude/standards/$(basename "$file")"
51
+ if [ "$first" -eq 0 ]; then
52
+ printf ','
53
+ fi
54
+ jq -nc \
55
+ --arg name "$name" \
56
+ --arg description "$title" \
57
+ --arg target "$target" \
58
+ --rawfile content "$file" \
59
+ '{name: $name, description: $description, target: $target, content: $content}'
60
+ first=0
61
+ done < <(find "$STANDARDS_DIR" -maxdepth 1 -type f -name "*.md" | sort)
62
+ printf ']'
63
+ }
64
+
65
+ main() {
66
+ local json=0
67
+
68
+ while [[ $# -gt 0 ]]; do
69
+ case "$1" in
70
+ -h | --help) show_help ;;
71
+ --json)
72
+ json=1
73
+ shift
74
+ ;;
75
+ *) log_error "Unknown option: $1" ;;
76
+ esac
77
+ done
78
+
79
+ trap close_timeline EXIT
80
+
81
+ if [ "$json" -eq 1 ]; then
82
+ printf '{"standards":'
83
+ list_json
84
+ printf '}\n'
85
+ exit 0
86
+ fi
87
+
88
+ list_text
89
+ }
90
+
91
+ main "$@"