@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.
- package/LICENSE +21 -0
- package/README.md +99 -0
- package/claude/.claude-plugin/plugin.json +19 -0
- package/claude/skills/bash-script/SKILL.md +96 -0
- package/claude/skills/bash-script/references/patterns.md +349 -0
- package/claude/skills/ci-workflow/SKILL.md +52 -0
- package/claude/skills/ci-workflow/references/workflows.md +74 -0
- package/claude/skills/claude-address-review/SKILL.md +107 -0
- package/claude/skills/claude-autoship/SKILL.md +137 -0
- package/claude/skills/claude-design-extract/SKILL.md +69 -0
- package/claude/skills/claude-design-propose/SKILL.md +75 -0
- package/claude/skills/claude-diagram/SKILL.md +126 -0
- package/claude/skills/claude-docs/SKILL.md +204 -0
- package/claude/skills/claude-feature/REQUIREMENT.md +35 -0
- package/claude/skills/claude-feature/SKILL.md +148 -0
- package/claude/skills/claude-groundwork/SKILL.md +148 -0
- package/claude/skills/claude-groundwork/references/folder-format.md +107 -0
- package/claude/skills/claude-memory-capture/SKILL.md +80 -0
- package/claude/skills/claude-memory-review/REQUIREMENT.md +37 -0
- package/claude/skills/claude-memory-review/SKILL.md +206 -0
- package/claude/skills/claude-orchestrate/SKILL.md +125 -0
- package/claude/skills/claude-pr-review/SKILL.md +150 -0
- package/claude/skills/claude-review/SKILL.md +136 -0
- package/claude/skills/claude-roadmap/SKILL.md +61 -0
- package/claude/skills/claude-roadmap/references/roadmap.md +52 -0
- package/claude/skills/claude-screencast/SKILL.md +160 -0
- package/claude/skills/claude-seed-sync/SKILL.md +141 -0
- package/claude/skills/claude-slides-draft/SKILL.md +62 -0
- package/claude/skills/claude-standards-audit/REQUIREMENT.md +33 -0
- package/claude/skills/claude-standards-audit/SKILL.md +79 -0
- package/claude/skills/claude-standards-audit/references/branch.md +49 -0
- package/claude/skills/claude-standards-audit/references/pr.md +124 -0
- package/claude/skills/claude-tasks/SKILL.md +123 -0
- package/claude/skills/claude-ui-test/SKILL.md +75 -0
- package/claude/skills/claude-ux-audit/SKILL.md +78 -0
- package/claude/skills/claude-worktree/SKILL.md +79 -0
- package/claude/skills/cli-script/SKILL.md +47 -0
- package/claude/skills/cli-script/references/template.md +43 -0
- package/claude/skills/create-rule/SKILL.md +68 -0
- package/claude/skills/create-skill/REQUIREMENT.md +30 -0
- package/claude/skills/create-skill/SKILL.md +24 -0
- package/claude/skills/create-snippet/SKILL.md +28 -0
- package/claude/skills/create-snippet/references/snippets.md +57 -0
- package/claude/skills/create-standard/SKILL.md +31 -0
- package/claude/skills/create-standard/references/standard.md +52 -0
- package/claude/skills/docs-sync/SKILL.md +87 -0
- package/claude/skills/git-branch/REQUIREMENT.md +38 -0
- package/claude/skills/git-branch/SKILL.md +60 -0
- package/claude/skills/git-branch/references/branch.md +49 -0
- package/claude/skills/git-commit/REQUIREMENT.md +33 -0
- package/claude/skills/git-commit/SKILL.md +51 -0
- package/claude/skills/git-commit/references/commit.md +63 -0
- package/claude/skills/git-followup/REQUIREMENT.md +38 -0
- package/claude/skills/git-followup/SKILL.md +35 -0
- package/claude/skills/git-issue/REQUIREMENT.md +38 -0
- package/claude/skills/git-issue/SKILL.md +69 -0
- package/claude/skills/git-issue/references/issue.md +84 -0
- package/claude/skills/git-pr/REQUIREMENT.md +37 -0
- package/claude/skills/git-pr/SKILL.md +119 -0
- package/claude/skills/git-pr/references/branch.md +49 -0
- package/claude/skills/git-pr/references/pr.md +124 -0
- package/claude/skills/git-ship/REQUIREMENT.md +34 -0
- package/claude/skills/git-ship/SKILL.md +39 -0
- package/claude/skills/git-split/REQUIREMENT.md +38 -0
- package/claude/skills/git-split/SKILL.md +160 -0
- package/claude/skills/git-split/references/branch.md +49 -0
- package/claude/skills/git-split/references/pr.md +124 -0
- package/claude/skills/git-stage/REQUIREMENT.md +36 -0
- package/claude/skills/git-stage/SKILL.md +74 -0
- package/claude/skills/git-stage/references/commit.md +63 -0
- package/claude/skills/git-worktree/REQUIREMENT.md +37 -0
- package/claude/skills/git-worktree/SKILL.md +120 -0
- package/claude/skills/migration-claude-md/SKILL.md +74 -0
- package/claude/skills/migration-context/SKILL.md +93 -0
- package/claude/skills/migration-standards/SKILL.md +75 -0
- package/claude/skills/project-commands/SKILL.md +73 -0
- package/claude/skills/session-resume/SKILL.md +36 -0
- package/claude/skills/setup-gov/SKILL.md +71 -0
- package/claude/skills/setup-indexes/SKILL.md +148 -0
- package/claude/skills/setup-init/SKILL.md +114 -0
- package/claude/skills/setup-plugins/SKILL.md +81 -0
- package/claude/skills/setup-plugins/references/plugin-catalog.md +53 -0
- package/claude/skills/setup-verify/SKILL.md +51 -0
- package/claude/skills/systematic-debugging/SKILL.md +70 -0
- package/claude/skills/toolkit-cli/SKILL.md +38 -0
- package/claude/skills/toolkit-feedback/SKILL.md +80 -0
- package/claude/skills/toolkit-operator/SKILL.md +45 -0
- package/claude/skills/toolkit-triage/SKILL.md +63 -0
- package/claude/skills/youtube-transcripts/SKILL.md +34 -0
- package/docs/agents.md +371 -0
- package/docs/ai-workflow.md +157 -0
- package/docs/index.md +20 -0
- package/docs/operating-model.md +106 -0
- package/docs/target-projects.md +183 -0
- package/docs/visual-design-workflow.md +136 -0
- package/docs/zshrc-aliases.md +65 -0
- package/governance/rules/claude/500-prose.md +12 -0
- package/governance/rules/claude/510-context.md +16 -0
- package/governance/rules/claude/520-wireframes.md +12 -0
- package/governance/rules/claude/530-requirements.md +11 -0
- package/governance/rules/claude/540-architecture.md +11 -0
- package/governance/rules/claude/550-design.md +11 -0
- package/governance/rules/claude/555-tasks.md +12 -0
- package/governance/rules/claude/560-diagrams.md +11 -0
- package/governance/rules/claude/570-skill.md +18 -0
- package/governance/rules/claude/580-readme.md +11 -0
- package/governance/rules/claude/590-rule-authoring.md +12 -0
- package/governance/rules/core/000-constitution.md +30 -0
- package/governance/rules/core/010-testing.md +35 -0
- package/governance/rules/core/020-concurrency.md +22 -0
- package/governance/rules/core/030-error-handling.md +31 -0
- package/governance/rules/core/040-performance.md +20 -0
- package/governance/rules/core/050-logging.md +20 -0
- package/governance/rules/core/060-naming.md +19 -0
- package/governance/rules/core/070-planning.md +13 -0
- package/governance/rules/core/080-config-comments.md +22 -0
- package/governance/rules/framework/200-react.md +51 -0
- package/governance/rules/framework/210-astro.md +41 -0
- package/governance/rules/framework/220-fastapi.md +43 -0
- package/governance/rules/framework/230-nextjs.md +48 -0
- package/governance/rules/framework/250-tailwind.md +32 -0
- package/governance/rules/framework/260-shadcn.md +34 -0
- package/governance/rules/lang/100-typescript.md +40 -0
- package/governance/rules/lang/110-python.md +42 -0
- package/governance/rules/lib/300-testing-ts.md +38 -0
- package/governance/rules/lib/310-zod.md +25 -0
- package/governance/rules/lib/320-tanstack-query.md +32 -0
- package/governance/rules/lib/330-testing-py.md +44 -0
- package/governance/rules/lib/340-pydantic.md +38 -0
- package/governance/rules/lib/350-security-web.md +31 -0
- package/governance/rules/ui/400-ui.md +34 -0
- package/governance/rules/ui/410-a11y.md +44 -0
- package/governance/rules/ui/420-forms.md +34 -0
- package/governance/rules/ui/430-ux-completeness.md +62 -0
- package/governance/stacks/astro.toml +2 -0
- package/governance/stacks/base.toml +2 -0
- package/governance/stacks/node.toml +2 -0
- package/governance/stacks/python-fastapi.toml +2 -0
- package/governance/stacks/python.toml +2 -0
- package/governance/stacks/react.toml +2 -0
- package/package.json +67 -0
- package/scripts/config.sh +11 -0
- package/scripts/core/bootstrap.sh +81 -0
- package/scripts/core/check-plugin-boundary.sh +45 -0
- package/scripts/core/check-skill-paths.sh +25 -0
- package/scripts/core/clean.sh +36 -0
- package/scripts/core/install-check.sh +93 -0
- package/scripts/core/regen-claude-copies.sh +26 -0
- package/scripts/core/regen-indexes.sh +10 -0
- package/scripts/core/regen-skill-references.sh +27 -0
- package/scripts/core/snapshot.sh +132 -0
- package/scripts/core/update.sh +35 -0
- package/scripts/core/verify.sh +201 -0
- package/scripts/docs/list.sh +129 -0
- package/scripts/gov/list.sh +234 -0
- package/scripts/lib/frontmatter.sh +30 -0
- package/scripts/lib/gov.sh +14 -0
- package/scripts/lib/sandbox-fixtures.sh +109 -0
- package/scripts/lib/sandbox-git.sh +73 -0
- package/scripts/lib/tooling.sh +24 -0
- package/scripts/lib/ui.sh +173 -0
- package/scripts/lib/worktree.sh +20 -0
- package/scripts/manage-sandbox.sh +453 -0
- package/scripts/snippets/create.sh +156 -0
- package/scripts/standards/list.sh +91 -0
- package/scripts/tooling/create.sh +109 -0
- package/scripts/tooling/ref.sh +141 -0
- package/scripts/tooling/verify.sh +179 -0
- package/snippets/claude/decision-memo.md +39 -0
- package/snippets/claude/feature-recap.md +19 -0
- package/snippets/claude/figma-steps.md +18 -0
- package/snippets/claude/orchestrator-resume.md +23 -0
- package/snippets/claude/orchestrator-sweep.md +10 -0
- package/snippets/compact-summary.md +5 -0
- package/snippets/create-snippet.md +7 -0
- package/snippets/decision-help.md +6 -0
- package/snippets/meta-prompt.md +14 -0
- package/snippets/research-prompt.md +7 -0
- package/snippets/session-notes.md +11 -0
- package/snippets/snippets.toml +6 -0
- package/snippets/step-by-step.md +10 -0
- package/snippets/web-research.md +21 -0
- package/src/claude/gitignore.ts +51 -0
- package/src/claude/seeds-list.ts +47 -0
- package/src/claude/seeds.ts +124 -0
- package/src/claude/settings.ts +151 -0
- package/src/cli-run.ts +43 -0
- package/src/cli.ts +111 -0
- package/src/commands/claude.ts +349 -0
- package/src/commands/design.ts +39 -0
- package/src/commands/docs.ts +59 -0
- package/src/commands/feedback-format.ts +23 -0
- package/src/commands/feedback.ts +103 -0
- package/src/commands/gov.ts +268 -0
- package/src/commands/indexes.ts +182 -0
- package/src/commands/init.ts +117 -0
- package/src/commands/pass-through.ts +39 -0
- package/src/commands/sandbox.ts +315 -0
- package/src/commands/slides.ts +106 -0
- package/src/commands/snippets.ts +236 -0
- package/src/commands/standards.ts +89 -0
- package/src/commands/sync.ts +211 -0
- package/src/commands/tasks.ts +185 -0
- package/src/commands/tooling.ts +432 -0
- package/src/commands/transcripts.ts +48 -0
- package/src/commands/wiki.ts +101 -0
- package/src/copy.ts +30 -0
- package/src/design/parse.ts +79 -0
- package/src/design/render.ts +170 -0
- package/src/docs/read.ts +59 -0
- package/src/exec.ts +17 -0
- package/src/frontmatter.ts +13 -0
- package/src/github-format.ts +13 -0
- package/src/github.ts +20 -0
- package/src/gov/adapter.ts +73 -0
- package/src/gov/install.ts +103 -0
- package/src/gov/payload.ts +39 -0
- package/src/gov/stacks.ts +124 -0
- package/src/indexes/frontmatter.ts +46 -0
- package/src/indexes/regen.ts +84 -0
- package/src/indexes/render.ts +181 -0
- package/src/indexes/walk.ts +111 -0
- package/src/init/flags.ts +66 -0
- package/src/init/plan.ts +128 -0
- package/src/init/run.ts +46 -0
- package/src/init/steps.ts +91 -0
- package/src/sandbox/coverage.ts +115 -0
- package/src/sandbox/expect.ts +412 -0
- package/src/slides/layouts.ts +391 -0
- package/src/slides/open.ts +18 -0
- package/src/slides/parse.ts +84 -0
- package/src/slides/render.ts +88 -0
- package/src/slides/styles.ts +44 -0
- package/src/snippets/adapter.ts +34 -0
- package/src/snippets/categories.ts +59 -0
- package/src/snippets/install.ts +125 -0
- package/src/snippets/list.ts +32 -0
- package/src/snippets/presets.ts +50 -0
- package/src/standards/adapter.ts +51 -0
- package/src/standards/index-refresh.ts +44 -0
- package/src/standards/install.ts +52 -0
- package/src/sync/check.ts +269 -0
- package/src/sync/engine.ts +416 -0
- package/src/sync/git.ts +204 -0
- package/src/sync/stamp.ts +202 -0
- package/src/sync/target.ts +79 -0
- package/src/sync/workflow.ts +201 -0
- package/src/target.ts +43 -0
- package/src/tasks/archive.ts +304 -0
- package/src/tooling/gitignore.ts +122 -0
- package/src/tooling/inject.ts +214 -0
- package/src/tooling/list.ts +39 -0
- package/src/tooling/manifest.ts +178 -0
- package/src/tooling/package.ts +166 -0
- package/src/tooling/scan.ts +186 -0
- package/src/transcripts/fetch.ts +156 -0
- package/src/transcripts/metadata.ts +54 -0
- package/src/transcripts/vtt.ts +114 -0
- package/src/ui.ts +187 -0
- package/src/wiki/init.ts +85 -0
- package/standards/architecture.md +41 -0
- package/standards/bundled/branch.md +49 -0
- package/standards/bundled/commit.md +63 -0
- package/standards/bundled/issue.md +84 -0
- package/standards/bundled/pr.md +124 -0
- package/standards/bundled/roadmap.md +52 -0
- package/standards/bundled/snippets.md +57 -0
- package/standards/bundled/standard.md +52 -0
- package/standards/context.md +132 -0
- package/standards/design.md +31 -0
- package/standards/diagrams.md +55 -0
- package/standards/index.md +21 -0
- package/standards/prose.md +122 -0
- package/standards/readme.md +163 -0
- package/standards/requirements.md +61 -0
- package/standards/rule.md +79 -0
- package/standards/skill.md +257 -0
- package/standards/tasks.md +140 -0
- package/standards/versioning.md +47 -0
- package/standards/wireframes.md +67 -0
- package/tooling/astro/configs/astro.config.mjs +17 -0
- package/tooling/astro/configs/eslint.config.js +79 -0
- package/tooling/astro/configs/playwright.config.ts +25 -0
- package/tooling/astro/configs/tsconfig.json +12 -0
- package/tooling/astro/configs/vitest.config.ts +22 -0
- package/tooling/astro/manifest.toml +32 -0
- package/tooling/astro/reference.md +60 -0
- package/tooling/base/configs/.editorconfig +5 -0
- package/tooling/base/configs/.github/pull_request_template.md +18 -0
- package/tooling/base/configs/.github/workflows/verify.yml +35 -0
- package/tooling/base/configs/.husky/commit-msg +1 -0
- package/tooling/base/configs/.husky/post-merge +61 -0
- package/tooling/base/configs/.husky/post-rewrite +21 -0
- package/tooling/base/configs/.husky/pre-commit +1 -0
- package/tooling/base/configs/.husky/pre-push +1 -0
- package/tooling/base/configs/.prettierrc +12 -0
- package/tooling/base/configs/.shellcheckrc +1 -0
- package/tooling/base/configs/.vscode/extensions.json +9 -0
- package/tooling/base/configs/.vscode/settings.json +3 -0
- package/tooling/base/configs/commitlint.config.js +11 -0
- package/tooling/base/configs/scripts/clean.sh +45 -0
- package/tooling/base/configs/scripts/update.sh +49 -0
- package/tooling/base/configs/scripts/verify.sh +64 -0
- package/tooling/base/manifest.toml +32 -0
- package/tooling/base/reference.md +114 -0
- package/tooling/base/seeds/.claude/context/ci.md +27 -0
- package/tooling/base/seeds/.claude/context/development.md +32 -0
- package/tooling/base/seeds/.cspell/project-terms.txt +0 -0
- package/tooling/base/seeds/.cspell/tech-stack.txt +19 -0
- package/tooling/base/seeds/.lintstagedrc +8 -0
- package/tooling/base/seeds/.prettierignore +0 -0
- package/tooling/base/seeds/cspell.json +20 -0
- package/tooling/claude/manifest.toml +11 -0
- package/tooling/claude/reference.md +62 -0
- package/tooling/claude/seeds/.claude/ARCHITECTURE.md +13 -0
- package/tooling/claude/seeds/.claude/DESIGN.md +62 -0
- package/tooling/claude/seeds/.claude/REQUIREMENTS.md +18 -0
- package/tooling/claude/seeds/.claude/context/index.md +8 -0
- package/tooling/claude/seeds/.claude/hooks/index-reminder.sh +43 -0
- package/tooling/claude/seeds/.claude/hooks/scratch-guard.sh +45 -0
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +61 -0
- package/tooling/claude/seeds/.claude/hooks/tasks-index.sh +60 -0
- package/tooling/claude/seeds/.claude/settings.json +39 -0
- package/tooling/claude/seeds/.claude/tasks/index.md +8 -0
- package/tooling/claude/seeds/.claude/wireframes/index.md +8 -0
- package/tooling/claude/seeds/CLAUDE.md +87 -0
- package/tooling/claude/user/settings.template.json +10 -0
- package/tooling/claude/user/statusline-command.sh +53 -0
- package/tooling/python/configs/.coveragerc +14 -0
- package/tooling/python/configs/.python-version +1 -0
- package/tooling/python/configs/mypy.ini +6 -0
- package/tooling/python/configs/pytest.ini +4 -0
- package/tooling/python/configs/ruff.toml +15 -0
- package/tooling/python/configs/scripts/verify.sh +77 -0
- package/tooling/python/manifest.toml +16 -0
- package/tooling/python/reference.md +66 -0
- package/tooling/python/seeds/.cspell/tech-stack.txt +19 -0
- package/tooling/python/seeds/tests/test_smoke.py +2 -0
- package/tooling/vite-react/configs/playwright.config.ts +25 -0
- package/tooling/vite-react/configs/tsconfig.json +35 -0
- package/tooling/vite-react/configs/vite.config.ts +14 -0
- package/tooling/vite-react/configs/vitest.config.ts +26 -0
- package/tooling/vite-react/manifest.toml +21 -0
- package/tooling/vite-react/reference.md +55 -0
- package/tooling/vite-react/seeds/.cspell/project-terms.txt +1 -0
- package/tooling/vite-react/seeds/.cspell/tech-stack.txt +1 -0
- package/tooling/web/configs/.github/workflows/verify.yml +134 -0
- package/tooling/web/configs/.vscode/extensions.json +13 -0
- package/tooling/web/configs/.vscode/settings.json +10 -0
- package/tooling/web/configs/e2e/home.spec.ts +6 -0
- package/tooling/web/configs/e2e/screenshot.ts +41 -0
- package/tooling/web/configs/eslint.config.js +82 -0
- package/tooling/web/configs/scripts/screenshot.sh +28 -0
- package/tooling/web/configs/scripts/verify.sh +80 -0
- package/tooling/web/configs/src/test/setup.ts +8 -0
- package/tooling/web/manifest.toml +57 -0
- package/tooling/web/reference.md +95 -0
- package/tooling/web/seeds/.cspell/tech-stack.txt +18 -0
- 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 "$@"
|