@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,349 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { chmod, readFile } from 'node:fs/promises'
|
|
3
|
+
import { homedir } from 'node:os'
|
|
4
|
+
import { join, resolve } from 'node:path'
|
|
5
|
+
import type { Command } from 'commander'
|
|
6
|
+
import { claudeChain, pendingEntries, planGitignore } from '@/claude/gitignore'
|
|
7
|
+
import {
|
|
8
|
+
applySeeds,
|
|
9
|
+
countByScope,
|
|
10
|
+
pendingSeeds,
|
|
11
|
+
planSeeds,
|
|
12
|
+
type Seed,
|
|
13
|
+
} from '@/claude/seeds'
|
|
14
|
+
import { listSeeds, readSeedContents } from '@/claude/seeds-list'
|
|
15
|
+
import {
|
|
16
|
+
planSettings,
|
|
17
|
+
readSettings,
|
|
18
|
+
serializeSettings,
|
|
19
|
+
writeSettings,
|
|
20
|
+
} from '@/claude/settings'
|
|
21
|
+
import { copyPreservingMode } from '@/copy'
|
|
22
|
+
import { execScript, PROJECT_ROOT } from '@/exec'
|
|
23
|
+
import { isDirectory, resolveTarget } from '@/target'
|
|
24
|
+
import { injectGitignore, pruneGitignore } from '@/tooling/inject'
|
|
25
|
+
import {
|
|
26
|
+
intro,
|
|
27
|
+
isNonInteractive,
|
|
28
|
+
logAdd,
|
|
29
|
+
logError,
|
|
30
|
+
logInfo,
|
|
31
|
+
logStep,
|
|
32
|
+
logWarn,
|
|
33
|
+
outro,
|
|
34
|
+
select,
|
|
35
|
+
} from '@/ui'
|
|
36
|
+
|
|
37
|
+
const GREEN = '\x1b[0;32m'
|
|
38
|
+
const GREY = '\x1b[0;90m'
|
|
39
|
+
const NC = '\x1b[0m'
|
|
40
|
+
|
|
41
|
+
interface SeedsListOptions {
|
|
42
|
+
readonly json?: boolean
|
|
43
|
+
readonly names?: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const SEEDED_FILES: readonly string[] = [
|
|
47
|
+
'ARCHITECTURE.md',
|
|
48
|
+
'REQUIREMENTS.md',
|
|
49
|
+
'DESIGN.md',
|
|
50
|
+
]
|
|
51
|
+
const SEEDED_DIRS: readonly string[] = ['tasks', 'wireframes']
|
|
52
|
+
const USER_DIR = join('tooling', 'claude', 'user')
|
|
53
|
+
const STATUSLINE = 'statusline-command.sh'
|
|
54
|
+
|
|
55
|
+
export function register(program: Command): void {
|
|
56
|
+
const claude = program
|
|
57
|
+
.command('claude')
|
|
58
|
+
.description('Claude workflow (init, seeds, sync, setup)')
|
|
59
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
60
|
+
.addHelpText(
|
|
61
|
+
'after',
|
|
62
|
+
[
|
|
63
|
+
'',
|
|
64
|
+
'Examples:',
|
|
65
|
+
' aitk claude init',
|
|
66
|
+
' aitk claude seeds list --json',
|
|
67
|
+
' aitk claude sync ../my-app',
|
|
68
|
+
'',
|
|
69
|
+
].join('\n'),
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
claude
|
|
73
|
+
.command('init')
|
|
74
|
+
.description('Seed .claude/ workflow docs into a project')
|
|
75
|
+
.argument('[target]', 'Target directory', '.')
|
|
76
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
77
|
+
.action(async (target: string) => {
|
|
78
|
+
process.exitCode = await runInit(target)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
claude
|
|
82
|
+
.command('sync')
|
|
83
|
+
.description('Reconcile .gitignore against the claude manifest')
|
|
84
|
+
.argument('[target]', 'Target directory', '.')
|
|
85
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
86
|
+
.action(async (target: string) => {
|
|
87
|
+
process.exitCode = await runSync(target)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
claude
|
|
91
|
+
.command('setup')
|
|
92
|
+
.description('One-shot user-level config (statusline, attribution)')
|
|
93
|
+
.argument('[dest]', 'User config directory', join(homedir(), '.claude'))
|
|
94
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
95
|
+
.action(async (dest: string) => {
|
|
96
|
+
process.exitCode = await runSetup(dest)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const seeds = claude
|
|
100
|
+
.command('seeds')
|
|
101
|
+
.description('Seed doc sources (list)')
|
|
102
|
+
.argument('[subcommand]', "Only 'list' is supported")
|
|
103
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
104
|
+
.action((subcommand: string | undefined) => {
|
|
105
|
+
intro('aitk claude')
|
|
106
|
+
logError(
|
|
107
|
+
subcommand === undefined
|
|
108
|
+
? "Missing subcommand. Use 'list'."
|
|
109
|
+
: `Unknown subcommand: ${subcommand}. Use 'list'.`,
|
|
110
|
+
)
|
|
111
|
+
outro()
|
|
112
|
+
process.exitCode = 1
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
seeds
|
|
116
|
+
.command('list')
|
|
117
|
+
.description('List toolkit seed docs as installed by aitk claude init')
|
|
118
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
119
|
+
.option('--json', 'Emit JSON with name, source, target, content')
|
|
120
|
+
.option('--names', 'Only list target paths, one per line')
|
|
121
|
+
.addHelpText(
|
|
122
|
+
'after',
|
|
123
|
+
[
|
|
124
|
+
'',
|
|
125
|
+
'Notes:',
|
|
126
|
+
' JSON is intended for skills that audit drift in target projects.',
|
|
127
|
+
'',
|
|
128
|
+
].join('\n'),
|
|
129
|
+
)
|
|
130
|
+
.action(async (opts: SeedsListOptions) => {
|
|
131
|
+
process.exitCode = await runSeedsList(opts)
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function succeed(message: string): number {
|
|
136
|
+
outro()
|
|
137
|
+
process.stderr.write(`${GREEN}✓ ${message}${NC}\n`)
|
|
138
|
+
return 0
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
async function runInit(target: string): Promise<number> {
|
|
142
|
+
intro('aitk claude')
|
|
143
|
+
|
|
144
|
+
const resolved = resolveTarget(target, PROJECT_ROOT)
|
|
145
|
+
if (typeof resolved === 'number') return resolved
|
|
146
|
+
|
|
147
|
+
logStep('Scanning .claude/')
|
|
148
|
+
const seedEntries = planSeeds(PROJECT_ROOT, resolved)
|
|
149
|
+
for (const entry of seedEntries) {
|
|
150
|
+
if (entry.present) logInfo(entry.seed.scanLabel)
|
|
151
|
+
else logAdd(entry.seed.scanLabel)
|
|
152
|
+
}
|
|
153
|
+
const seeds = pendingSeeds(seedEntries)
|
|
154
|
+
|
|
155
|
+
logStep('Scanning .gitignore')
|
|
156
|
+
const chain = claudeChain(PROJECT_ROOT)
|
|
157
|
+
const gitignoreEntries = planGitignore(chain, resolved)
|
|
158
|
+
for (const entry of gitignoreEntries) {
|
|
159
|
+
if (entry.present) logInfo(entry.entry)
|
|
160
|
+
else logAdd(entry.entry)
|
|
161
|
+
}
|
|
162
|
+
const missing = pendingEntries(gitignoreEntries)
|
|
163
|
+
|
|
164
|
+
const total = seeds.length + missing.length
|
|
165
|
+
if (total === 0) return succeed('Claude already initialized')
|
|
166
|
+
|
|
167
|
+
const shouldApply = await select({
|
|
168
|
+
message: `Apply ${total} change(s) (${summarize(seeds, missing.length)})?`,
|
|
169
|
+
options: [
|
|
170
|
+
{ value: true, label: 'Apply all' },
|
|
171
|
+
{ value: false, label: 'Cancel' },
|
|
172
|
+
],
|
|
173
|
+
nonInteractiveDefault: true,
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
if (!shouldApply) {
|
|
177
|
+
logWarn('Cancelled')
|
|
178
|
+
outro()
|
|
179
|
+
return 0
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
logStep('Applying changes')
|
|
183
|
+
for (const label of await applySeeds(seeds)) logAdd(label)
|
|
184
|
+
if (missing.length > 0) await injectGitignore(chain, resolved)
|
|
185
|
+
|
|
186
|
+
return succeed('Claude ready')
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function summarize(seeds: readonly Seed[], gitignoreCount: number): string {
|
|
190
|
+
const counts = countByScope(seeds)
|
|
191
|
+
const parts: string[] = []
|
|
192
|
+
if (counts.claude > 0) parts.push(`${counts.claude} .claude`)
|
|
193
|
+
if (counts.root > 0) parts.push(`${counts.root} CLAUDE.md`)
|
|
194
|
+
if (gitignoreCount > 0) parts.push(`${gitignoreCount} .gitignore`)
|
|
195
|
+
return parts.join(', ')
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async function runSync(target: string): Promise<number> {
|
|
199
|
+
intro('aitk claude')
|
|
200
|
+
|
|
201
|
+
const resolved = resolveTarget(target, PROJECT_ROOT)
|
|
202
|
+
if (typeof resolved === 'number') return resolved
|
|
203
|
+
|
|
204
|
+
logStep('Seeded')
|
|
205
|
+
for (const name of SEEDED_FILES) {
|
|
206
|
+
if (existsSync(join(resolved, '.claude', name))) logInfo(name)
|
|
207
|
+
else logWarn(`${name} missing. Run \`aitk claude init\``)
|
|
208
|
+
}
|
|
209
|
+
for (const name of SEEDED_DIRS) {
|
|
210
|
+
if (isDirectory(join(resolved, '.claude', name))) logInfo(`${name}/`)
|
|
211
|
+
else logWarn(`${name}/ missing. Run \`aitk claude init\``)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
logStep('Scanning .gitignore')
|
|
215
|
+
const chain = claudeChain(PROJECT_ROOT)
|
|
216
|
+
const pruned = await pruneGitignore(chain, resolved)
|
|
217
|
+
const gitignoreEntries = planGitignore(chain, resolved)
|
|
218
|
+
for (const entry of gitignoreEntries) {
|
|
219
|
+
if (entry.present) logInfo(entry.entry)
|
|
220
|
+
else logAdd(entry.entry)
|
|
221
|
+
}
|
|
222
|
+
const missing = pendingEntries(gitignoreEntries)
|
|
223
|
+
|
|
224
|
+
if (missing.length === 0 && pruned.length === 0) {
|
|
225
|
+
return succeed('Claude workflow up to date')
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (missing.length > 0) {
|
|
229
|
+
if (isNonInteractive()) {
|
|
230
|
+
logInfo(`Applying ${missing.length} update(s) (non-interactive)`)
|
|
231
|
+
} else {
|
|
232
|
+
const shouldApply = await select({
|
|
233
|
+
message: `Apply ${missing.length} update(s) (${missing.length} .gitignore)?`,
|
|
234
|
+
options: [
|
|
235
|
+
{ value: true, label: 'Apply all' },
|
|
236
|
+
{ value: false, label: 'Cancel' },
|
|
237
|
+
],
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
if (!shouldApply) {
|
|
241
|
+
logWarn('Cancelled')
|
|
242
|
+
outro()
|
|
243
|
+
return 0
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
logStep('Applying changes')
|
|
248
|
+
await injectGitignore(chain, resolved)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return succeed('Claude workflow synced')
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Writes to the operator's own machine rather than a project, so the
|
|
256
|
+
* destination is a parameter. That lets a test cover the merge without
|
|
257
|
+
* pointing it at a real home directory.
|
|
258
|
+
*/
|
|
259
|
+
async function runSetup(dest: string): Promise<number> {
|
|
260
|
+
intro('aitk claude')
|
|
261
|
+
|
|
262
|
+
const resolved = resolve(dest)
|
|
263
|
+
if (resolved === join(PROJECT_ROOT, '.claude')) {
|
|
264
|
+
logError(
|
|
265
|
+
'Cannot run against the toolkit .claude/. Files here are the source of truth.',
|
|
266
|
+
)
|
|
267
|
+
outro()
|
|
268
|
+
return 1
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const userDir = join(PROJECT_ROOT, USER_DIR)
|
|
272
|
+
const scriptSrc = join(userDir, STATUSLINE)
|
|
273
|
+
const scriptDest = join(resolved, STATUSLINE)
|
|
274
|
+
const settingsPath = join(resolved, 'settings.json')
|
|
275
|
+
|
|
276
|
+
logStep('Statusline script')
|
|
277
|
+
if (await sameContent(scriptSrc, scriptDest)) {
|
|
278
|
+
logInfo(STATUSLINE)
|
|
279
|
+
} else {
|
|
280
|
+
await copyPreservingMode(scriptSrc, scriptDest)
|
|
281
|
+
await chmod(scriptDest, 0o755)
|
|
282
|
+
logAdd(STATUSLINE)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
logStep('Settings')
|
|
286
|
+
const template = await readSettings(join(userDir, 'settings.template.json'))
|
|
287
|
+
|
|
288
|
+
let current: Awaited<ReturnType<typeof readSettings>>
|
|
289
|
+
try {
|
|
290
|
+
current = await readSettings(settingsPath)
|
|
291
|
+
} catch (error) {
|
|
292
|
+
logError(error instanceof Error ? error.message : String(error))
|
|
293
|
+
logError('Fix the file by hand, or move it aside and rerun.')
|
|
294
|
+
outro()
|
|
295
|
+
return 1
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const { value, indent } = current
|
|
299
|
+
const plan = planSettings(value, template.value, `bash ${scriptDest}`)
|
|
300
|
+
|
|
301
|
+
for (const key of plan.keys) {
|
|
302
|
+
if (key.changed) logAdd(key.label)
|
|
303
|
+
else logInfo(key.label)
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (plan.changed) {
|
|
307
|
+
await writeSettings(settingsPath, serializeSettings(plan.next, indent))
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
return succeed('Claude user config ready')
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async function sameContent(src: string, dest: string): Promise<boolean> {
|
|
314
|
+
if (!existsSync(dest)) return false
|
|
315
|
+
const [left, right] = await Promise.all([
|
|
316
|
+
readFile(src, 'utf8'),
|
|
317
|
+
readFile(dest, 'utf8'),
|
|
318
|
+
])
|
|
319
|
+
return left === right
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* `--json` and `--names` write to stdout so a skill can pipe them, while the
|
|
324
|
+
* human listing stays on the timeline. Only the human mode opens a frame.
|
|
325
|
+
*/
|
|
326
|
+
async function runSeedsList(opts: SeedsListOptions): Promise<number> {
|
|
327
|
+
const listings = listSeeds(PROJECT_ROOT)
|
|
328
|
+
|
|
329
|
+
if (opts.json) {
|
|
330
|
+
const withContent = await readSeedContents(listings)
|
|
331
|
+
process.stdout.write(`${JSON.stringify(withContent)}\n`)
|
|
332
|
+
return 0
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (opts.names) {
|
|
336
|
+
process.stdout.write(
|
|
337
|
+
listings.map((listing) => listing.target).join('\n') + '\n',
|
|
338
|
+
)
|
|
339
|
+
return 0
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
intro('aitk claude')
|
|
343
|
+
logStep('Seed docs')
|
|
344
|
+
for (const listing of listings) {
|
|
345
|
+
logInfo(`${listing.target} ${GREY}← ${listing.source}${NC}`)
|
|
346
|
+
}
|
|
347
|
+
outro()
|
|
348
|
+
return 0
|
|
349
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
import type { Command } from 'commander'
|
|
4
|
+
import { renderDesignDoc } from '@/design/render'
|
|
5
|
+
|
|
6
|
+
const GREY = '\x1b[0;90m'
|
|
7
|
+
const WHITE = '\x1b[1;37m'
|
|
8
|
+
const RED = '\x1b[0;31m'
|
|
9
|
+
const GREEN = '\x1b[0;32m'
|
|
10
|
+
const NC = '\x1b[0m'
|
|
11
|
+
|
|
12
|
+
export function register(program: Command): void {
|
|
13
|
+
const design = program
|
|
14
|
+
.command('design')
|
|
15
|
+
.description('Design system commands (render)')
|
|
16
|
+
|
|
17
|
+
design
|
|
18
|
+
.command('render')
|
|
19
|
+
.description('Render DESIGN.md tokens to HTML and CSS preview')
|
|
20
|
+
.option('-s, --source <path>', 'Source DESIGN.md path', '.claude/DESIGN.md')
|
|
21
|
+
.option('-o, --out <path>', 'Output directory', '.claude/review/design')
|
|
22
|
+
.action((opts: { source: string; out: string }) => {
|
|
23
|
+
const sourcePath = resolve(process.cwd(), opts.source)
|
|
24
|
+
const outDir = resolve(process.cwd(), opts.out)
|
|
25
|
+
if (!existsSync(sourcePath)) {
|
|
26
|
+
process.stderr.write(
|
|
27
|
+
`${GREY}┌${NC}\n${GREY}│${NC} ${RED}✗${NC} ${opts.source} not found\n${GREY}└${NC}\n`,
|
|
28
|
+
)
|
|
29
|
+
process.exit(1)
|
|
30
|
+
}
|
|
31
|
+
process.stderr.write(
|
|
32
|
+
`${GREY}┌${NC}\n${GREY}│${NC} ${WHITE}Render design tokens${NC}\n`,
|
|
33
|
+
)
|
|
34
|
+
const result = renderDesignDoc(sourcePath, outDir)
|
|
35
|
+
process.stderr.write(
|
|
36
|
+
`${GREY}│${NC} ${GREEN}✓${NC} ${result.htmlPath}\n${GREY}│${NC} ${GREEN}✓${NC} ${result.cssPath}\n${GREY}└${NC}\n`,
|
|
37
|
+
)
|
|
38
|
+
})
|
|
39
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Command } from 'commander'
|
|
2
|
+
import { registerPassThroughVerbs } from '@/commands/pass-through'
|
|
3
|
+
import { listTopics, readTopic, resolveTopic } from '@/docs/read'
|
|
4
|
+
import { execScript, PROJECT_ROOT } from '@/exec'
|
|
5
|
+
import { intro, logError, logInfo, logStep, logWarn, outro } from '@/ui'
|
|
6
|
+
|
|
7
|
+
export function register(program: Command): void {
|
|
8
|
+
const docs = program
|
|
9
|
+
.command('docs')
|
|
10
|
+
.description('Emit toolkit reference docs (list, <topic>)')
|
|
11
|
+
.argument('[topic]', 'Doc to print, by exact name')
|
|
12
|
+
.helpOption('-h, --help', 'Show this help message')
|
|
13
|
+
.addHelpText(
|
|
14
|
+
'after',
|
|
15
|
+
[
|
|
16
|
+
'',
|
|
17
|
+
'Examples:',
|
|
18
|
+
' aitk docs list',
|
|
19
|
+
' aitk docs list --json',
|
|
20
|
+
' aitk docs agents',
|
|
21
|
+
'',
|
|
22
|
+
].join('\n'),
|
|
23
|
+
)
|
|
24
|
+
.action(async (topic: string | undefined) => {
|
|
25
|
+
if (topic === undefined) {
|
|
26
|
+
intro('aitk docs')
|
|
27
|
+
await execScript('docs/list.sh', [])
|
|
28
|
+
return
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
process.exitCode = get(topic)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
registerPassThroughVerbs(docs, 'docs', ['list'])
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Writes the document body to stdout and every frame line to stderr, so a
|
|
39
|
+
* caller capturing the output with `$(...)` receives the document alone.
|
|
40
|
+
*/
|
|
41
|
+
function get(topic: string): number {
|
|
42
|
+
intro('aitk docs')
|
|
43
|
+
|
|
44
|
+
const resolved = resolveTopic(PROJECT_ROOT, topic)
|
|
45
|
+
|
|
46
|
+
if (!resolved) {
|
|
47
|
+
logWarn(`Unknown topic: ${topic}`)
|
|
48
|
+
logStep('Available topics')
|
|
49
|
+
for (const name of listTopics(PROJECT_ROOT)) logInfo(name)
|
|
50
|
+
logError("Run 'aitk docs list' for descriptions.")
|
|
51
|
+
outro()
|
|
52
|
+
return 1
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
logStep(resolved.rel)
|
|
56
|
+
process.stdout.write(readTopic(resolved))
|
|
57
|
+
outro()
|
|
58
|
+
return 0
|
|
59
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const SLUG_FALLBACK = 'general'
|
|
2
|
+
const SLUG_MAX_LENGTH = 40
|
|
3
|
+
const TITLE_MAX_LENGTH = 72
|
|
4
|
+
|
|
5
|
+
function surfaceField(body: string): string | undefined {
|
|
6
|
+
return body.match(/\*\*Surface:\*\*\s*([^\n]+)/i)?.[1]?.trim()
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function deriveSlug(body: string): string {
|
|
10
|
+
const slug = (surfaceField(body) ?? '')
|
|
11
|
+
.toLowerCase()
|
|
12
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
13
|
+
.replace(/^-+|-+$/g, '')
|
|
14
|
+
.slice(0, SLUG_MAX_LENGTH)
|
|
15
|
+
.replace(/-+$/g, '')
|
|
16
|
+
return slug || SLUG_FALLBACK
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function deriveTitle(body: string): string {
|
|
20
|
+
const surface = surfaceField(body)
|
|
21
|
+
const title = surface ? `feedback: ${surface}` : 'toolkit feedback'
|
|
22
|
+
return title.slice(0, TITLE_MAX_LENGTH)
|
|
23
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import type { Command } from 'commander'
|
|
4
|
+
import { deriveSlug, deriveTitle } from '@/commands/feedback-format'
|
|
5
|
+
import { PROJECT_ROOT } from '@/exec'
|
|
6
|
+
import { createGithubIssue } from '@/github'
|
|
7
|
+
import { frameError, frameSuccess } from '@/ui'
|
|
8
|
+
|
|
9
|
+
const YELLOW = '\x1b[0;33m'
|
|
10
|
+
const NC = '\x1b[0m'
|
|
11
|
+
|
|
12
|
+
function readStdin(): Promise<string> {
|
|
13
|
+
return new Promise((resolveStream, rejectStream) => {
|
|
14
|
+
const chunks: Buffer[] = []
|
|
15
|
+
process.stdin.on('data', (chunk: Buffer) => chunks.push(chunk))
|
|
16
|
+
process.stdin.on('end', () =>
|
|
17
|
+
resolveStream(Buffer.concat(chunks).toString('utf8')),
|
|
18
|
+
)
|
|
19
|
+
process.stdin.on('error', rejectStream)
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function timestamp(): string {
|
|
24
|
+
const d = new Date()
|
|
25
|
+
const pad = (n: number): string => n.toString().padStart(2, '0')
|
|
26
|
+
return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A registry install ships no `.claude/`, so a local write would land inside
|
|
31
|
+
* `node_modules/` and report a project-relative path the operator cannot find.
|
|
32
|
+
* The toolkit source is the only root where local scratch reaches a maintainer.
|
|
33
|
+
*/
|
|
34
|
+
function isToolkitSource(): boolean {
|
|
35
|
+
return existsSync(join(PROJECT_ROOT, '.claude'))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function writeLocal(body: string): string {
|
|
39
|
+
const reviewDir = join(PROJECT_ROOT, '.claude', 'review')
|
|
40
|
+
mkdirSync(reviewDir, { recursive: true })
|
|
41
|
+
const filename = `feedback-${deriveSlug(body)}-${timestamp()}.md`
|
|
42
|
+
const filePath = join(reviewDir, filename)
|
|
43
|
+
writeFileSync(filePath, `${body}\n`, 'utf8')
|
|
44
|
+
frameSuccess('aitk feedback', `.claude/review/${filename}`)
|
|
45
|
+
return filePath
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function register(program: Command): void {
|
|
49
|
+
program
|
|
50
|
+
.command('feedback')
|
|
51
|
+
.description(
|
|
52
|
+
'Write toolkit feedback from stdin to .claude/review/, or open a GitHub issue with --github',
|
|
53
|
+
)
|
|
54
|
+
.option(
|
|
55
|
+
'--github',
|
|
56
|
+
'Open a GitHub issue on the toolkit repo instead of writing local scratch',
|
|
57
|
+
)
|
|
58
|
+
.action(async (opts: { github?: boolean }) => {
|
|
59
|
+
if (process.stdin.isTTY) {
|
|
60
|
+
frameError(
|
|
61
|
+
'No feedback on stdin. Pipe a markdown block: pbpaste | aitk feedback',
|
|
62
|
+
)
|
|
63
|
+
process.exit(1)
|
|
64
|
+
}
|
|
65
|
+
const body = (await readStdin()).trim()
|
|
66
|
+
if (!body) {
|
|
67
|
+
frameError('Empty feedback body. Provide a markdown block on stdin.')
|
|
68
|
+
process.exit(1)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (opts.github) {
|
|
72
|
+
const url = await createGithubIssue({
|
|
73
|
+
title: deriveTitle(body),
|
|
74
|
+
body,
|
|
75
|
+
labels: ['feedback'],
|
|
76
|
+
})
|
|
77
|
+
if (url) {
|
|
78
|
+
frameSuccess('aitk feedback', url)
|
|
79
|
+
process.stdout.write(`${url}\n`)
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
if (!isToolkitSource()) {
|
|
83
|
+
frameError(
|
|
84
|
+
'gh unavailable and no toolkit source to fall back to. Install gh, or file it at https://github.com/erclx/aitk/issues/new',
|
|
85
|
+
)
|
|
86
|
+
process.exit(1)
|
|
87
|
+
}
|
|
88
|
+
process.stderr.write(
|
|
89
|
+
`${YELLOW}! gh unavailable, wrote local scratch instead${NC}\n`,
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!isToolkitSource()) {
|
|
94
|
+
frameError(
|
|
95
|
+
'Local scratch needs the toolkit source. Re-run with --github to open an issue instead.',
|
|
96
|
+
)
|
|
97
|
+
process.exit(1)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const filePath = writeLocal(body)
|
|
101
|
+
process.stdout.write(`${filePath}\n`)
|
|
102
|
+
})
|
|
103
|
+
}
|