-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathTaskfile.yml
More file actions
761 lines (660 loc) · 29.1 KB
/
Copy pathTaskfile.yml
File metadata and controls
761 lines (660 loc) · 29.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
# Taskfile for Harmon DevKit
# https://taskfile.dev
version: "3"
output:
group:
begin: "::group::{{.TASK}}"
end: "::endgroup::"
includes:
# Foreman — deterministic supervisor for milestone-driven agent dispatch
# (task foreman:plan / dispatch / shepherd / watch ...; the CLI is pinned
# and fetched via uvx in taskfiles/foreman.yml)
foreman: taskfiles/foreman.yml
# ── Entry Points ───────────────────────────────────────────────────
tasks:
default:
desc: Interactive task menu (tv)
cmds:
- task: menu-tv
menu:
desc: Interactive task menu (fzf)
silent: true
interactive: true
cmds:
- |
selected=$(task --list-all | fzf --reverse --header-lines=1)
if [ -n "$selected" ]; then
task_name=$(echo "$selected" | awk '{sub(/:$/, "", $2); print $2}')
task "$task_name"
fi
menu-tv:
desc: Interactive task menu (tv)
silent: true
interactive: true
cmds:
- |
selected=$(task --list-all | grep -v "task: Available" | grep -v "\* menu:" | tv)
if [ -n "$selected" ]; then
task_name=$(echo "$selected" | awk '{sub(/:$/, "", $2); print $2}')
task "$task_name"
fi
# ── CI / Verification Pipelines ────────────────────────────────
ci:
# Full local mirror of the CI pipeline — run before (or instead of) opening
# a PR to catch everything CI would, on demand, without waiting on a PR run.
# Builds on `verify` and adds every OTHER check the build workflow runs, so
# the two cannot drift apart. The daemon-free devcontainer permission
# assertion is already inside `verify` for this repo, so it is covered here
# transitively rather than listed again.
desc: Full CI mirror (verify → security)
cmds:
- task: verify
- task: security
verify:
# The definition-of-done gate — everything that must pass before a change
# is done. Foreman v2 calls this the "check + build + test" tier; the desc
# below lists this repo's exact steps. For the fast inner loop use `check`;
# heavier security checks stay in `ci`. The devcontainer permission
# assertion is daemon-free and subsecond, so it remains in this gate.
desc: Definition-of-done gate (check → validate → guards → devcontainer → test)
cmds:
- task: check
- task: audit:agent-instructions
- task: test:agent-instructions-size
- task: test:agent-skill-links
- task: validate
- task: test:agent-registry
- task: test:registry-drift
- task: test:registry-docs
- task: test:tasks
- task: test:ci-results
- task: test:status
- task: test:meta-install
- task: test:codex-review
- task: test:shepherd-codex
- task: test:shepherd-marker
- task: test:shepherd-readiness
- task: test:lint-hygiene
- task: test:release-title
- task: test:notify-release
- task: test:setup-github-project
- task: test:sync-devkit-release
- task: test:hooks
- task: test:statusline
- task: test:devcontainer:permissions
- task: test
# ── Quality Checks ────────────────────────────────────────────
check:
# Fast local gate (target: well under a minute) — safe for editors, git
# hooks, and AI agents to run on every change without getting bogged down.
desc: Fast local gate — all linters (parallel)
deps:
- lint
audit:agent-instructions:
desc: Warn when AGENTS.md exceeds Codex's default 32 KiB discovery budget
cmds:
- ./scripts/check-agent-instructions-size.sh AGENTS.md
lint:
desc: Run all linters in parallel
deps:
- lint:yaml
- lint:shell
- lint:markdown
- lint:actions
- lint:hygiene
lint:yaml:
desc: yamllint
cmds:
- yamllint {{.CLI_ARGS | default "."}}
lint:shell:
desc: shellcheck + shfmt
cmds:
# Shell templates are executable source assets too. Snippets remain
# excluded because they may be deliberately incomplete fragments.
- ./scripts/shell-quality.sh check --exclude-snippets {{.CLI_ARGS}}
lint:markdown:
# Check-only (no --fix) so this is a read-only gate like lint:prettier /
# lint:shell: CI and the pre-commit hook fail loudly instead of silently
# mutating the tree (and, in CI, discarding the fix while reporting green).
# Auto-fix lives in `task format` / `task format:file`. The bin-vs-npx
# dispatch + default glob set live in scripts/markdownlint.sh.
desc: markdownlint-cli2 (check mode)
cmds:
- ./scripts/markdownlint.sh check {{.CLI_ARGS}}
lint:actions:
desc: actionlint on GitHub workflows
env:
# SC2016 flags '$' in single quotes, but we intentionally single-quote
# jq expressions that use '$var' placeholders.
SHELLCHECK_OPTS: --exclude=SC2016
cmds:
- actionlint
lint:hygiene:
desc: File hygiene (trailing whitespace, EOF newlines, merge markers, etc.)
cmds:
- ./scripts/lint-hygiene.sh {{.CLI_ARGS}}
lint:commit-msg:
desc: Validate commit message (conventional commits)
cmds:
- npx --yes --package @commitlint/cli --package @commitlint/config-conventional -- commitlint --edit {{.CLI_ARGS}}
lint:commit-msg:text:
desc: "Validate a commit message read from stdin (usage: printf '%s' <message> | task lint:commit-msg:text)"
cmds:
- npx --yes --package @commitlint/cli --package @commitlint/config-conventional -- commitlint
# ── Validation ─────────────────────────────────────────────────
validate:
desc: Validate project config/artifacts
cmds:
- task: validate:skills
- task: validate:agents
- task: validate:independence
validate:skills:
desc: Guard shared agent skills (unique names across categories, valid SKILL.md frontmatter)
cmds:
- ./scripts/verify-skills.sh
validate:agents:
desc: Guard shared subagents (flat layout, valid frontmatter, no skill-name collisions)
cmds:
- ./scripts/verify-agents.sh
validate:independence:
desc: Guard shipped output against personal-dotfiles references
cmds:
- ./scripts/verify-independence.sh
guard:no-commit-to-main:
desc: Block direct commits to main
cmds:
- |
branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$branch" = "main" ]; then
echo "Direct commits to main are blocked. Use a feature branch."
exit 1
fi
guard:release-title:
# The CI counterpart of a pre-commit hook — enforced by release-content-guard.yml
# at PR time because the thing it checks (the PR title, which squash-merge feeds
# to release-please) does not exist locally. A non-releasing title over
# release-worthy content would merge without cutting a tag. RELEASE_CONTENT_PATHS
# comes from the release_content_paths copier answer. Pre-flight before opening a
# PR with an intended title:
# PR_TITLE="<title>" BASE_SHA=main task guard:release-title
# (CI passes PR_TITLE/PR_BODY + BASE_SHA/HEAD_SHA; CHANGED_FILES overrides the diff.)
desc: "Guard: a PR touching release-worthy paths needs a fix:/feat: title"
vars:
RELEASE_CONTENT_PATHS: "ai/skills ai/agents templates scripts"
cmds:
- ./scripts/require-release-title.sh {{.RELEASE_CONTENT_PATHS}}
guard:closing-keywords:
# The CI counterpart of the track-work skill — enforced by tracking-guard.yml
# at PR time because the thing it checks (the PR body, which GitHub acts on at
# merge) does not exist locally. A `Closes #N` over an issue that still holds
# outstanding work deletes that work from the backlog the moment the PR merges.
# The check ships inside the skill so consumers vendor it; this target is what
# keeps devkit's own PRs honest. All three paths a closing keyword can take to
# main are checked — the body, the title (squash-merge makes it the commit
# subject), and the commit messages (they land on main under rebase/merge, and
# under squash too because squash_merge_commit_message is COMMIT_MESSAGES).
# PR_COMMITS_FILE is optional; empty means none supplied. Pre-flight before
# opening a PR:
# PR_TITLE="<title>" PR_BODY="$(cat body.md)" task guard:closing-keywords
# (CI passes PR_TITLE/PR_BODY/PR_COMMITS_FILE + GH_REPO; unit-tested by
# `task test:track-work`.)
desc: "Guard: a PR must not auto-close an issue holding unfinished work"
cmds:
- ./ai/skills/universal/track-work/assets/check-closing-keywords.sh --body-env PR_BODY --title-env PR_TITLE --commits-file "${PR_COMMITS_FILE:-}"
# Entry point for .github/workflows/sync-harmon-devkit.yml: resolves the
# latest harmon-devkit release, bumps .skills-sync.yaml, vendors, verifies
# offline, and opens/updates ONE rolling bot/sync-harmon-devkit PR.
# Never merges anything. `{{.CLI_ARGS}}` passes an explicit tag for the
# manual recovery path (`task sync:devkit-release -- vX.Y.Z`).
sync:devkit-release:
desc: "Pin the latest harmon-devkit release, vendor it, verify, and open/update the sync PR"
cmds:
- ./scripts/sync-devkit-release.sh run {{.CLI_ARGS}}
test:agent-skill-links:
desc: Unit-test migration-safe Claude-to-.agents skill compatibility links
cmds:
- ./scripts/test-agent-skill-links.sh
# ── Skills sync ────────────────────────────────────────────────
# Vendor shared agent skills from harmon-devkit, pinned in .skills-sync.yaml.
# The verify:skills* drift checks skip cleanly until the first `task sync:skills`,
# so a fresh scaffold stays green. See docs/CHECKLIST.md.
sync:skills:
desc: Vendor the pinned skill categories from harmon-devkit into .agents/skills
cmds:
- ./scripts/sync-skills.sh sync
- ./scripts/link-agent-skills.sh sync
verify:skills:
desc: "Skill drift check (network): fail if vendored skills differ from the pinned ref"
cmds:
- ./scripts/link-agent-skills.sh verify
- ./scripts/sync-skills.sh verify
verify:skills:offline:
desc: "Fast offline check that the vendored ref matches the manifest (no network)"
cmds:
- ./scripts/link-agent-skills.sh verify
- ./scripts/sync-skills.sh verify-offline
format:
desc: Auto-format code
cmds:
- ./scripts/shell-quality.sh format --exclude-snippets
# Best-effort markdown auto-fix (bin-vs-npx dispatch in the helper); must
# NOT abort `format` on un-auto-fixable rule violations — `lint:markdown`
# (check-only) is the gate.
- ./scripts/markdownlint.sh fix
format:file:
desc: "Format a single file by extension (usage: task format:file -- <path>)"
cmds:
- |
f={{.CLI_ARGS}}
[ -z "$f" ] && exit 0
[ -f "$f" ] || exit 0
case "$f" in
*.sh | *.bash) shfmt -w "$f" ;;
*.md | *.mdx) ./scripts/markdownlint.sh fix "$f" ;;
esac
fix:
desc: Format then lint
cmds:
- task: format
- task: lint
# ── Tests ──────────────────────────────────────────────────────
test:
desc: Run all project tests
deps:
- test:tasks
- test:hooks
- test:skills
- test:agents
- test:hygiene
- test:archive-hook
- test:track-work
- test:skill-index-lifecycle
- test:independence
test:agent-registry:
desc: Schema-check the machine-readable agent registry and its cross-record invariants
cmds:
- ./scripts/test-agent-registry.sh
test:agent-instructions-size:
desc: Unit-test the non-blocking AGENTS.md size advisory
cmds:
- ./scripts/test-agent-instructions-size.sh
test:registry-drift:
desc: Bind label provisioning, provider wrappers, and Foreman adapters to the agent registry (offline drift gate)
cmds:
- ./scripts/test-registry-drift.sh
test:registry-docs:
# The answers-file name is configurable (`copier copy --answers-file …`),
# and the check reads project_management out of it to know whether this
# repo renders the GitHub Projects document at all. The script is a
# verbatim twin of harmon-init's own, so the name is passed in rather than
# templated into it — as an ENV value, not a command argument: a filename
# containing an apostrophe would terminate a quoted shell argument early.
# `tojson` emits a JSON string, which is also a valid YAML double-quoted
# scalar, so quotes and backslashes in the name survive both layers. The
# `string` filter first because copier hands this over as a PurePosixPath,
# which `tojson` cannot serialize.
desc: Bind the published family/harness tables in docs/project-management.md to the agent registry
env:
COPIER_ANSWERS_FILE: ".copier-answers.yml"
cmds:
- ./scripts/test-registry-docs.sh
test:tasks:
desc: Guard the Taskfile itself (compiles; setup tasks are safe no-ops)
cmds:
- ./scripts/test-tasks.sh
test:ci-results:
desc: Unit-test the fail-closed CI aggregate result helper
cmds:
- ./scripts/test-ci-results.sh
test:status:
desc: Unit-test the status board-writes + local-credential checks (stubbed CLIs, no network)
cmds:
- ./scripts/test-status.sh
test:meta-install:
desc: Unit-test the .meta sidecar installer's ~-expansion and refusals
cmds:
- ./scripts/test-meta-install.sh
test:codex-review:
desc: Unit-test codex-review.sh target selection (offline, stubbed codex)
cmds:
- ./scripts/test-codex-review.sh
test:shepherd-codex:
desc: Unit-test current-head Codex cloud-review evidence (offline, stubbed gh)
cmds:
- ./scripts/test-shepherd-codex.sh
test:shepherd-marker:
desc: Unit-test the shepherd gate-then-push marker parser (require-marker.sh)
cmds:
- ./scripts/test-shepherd-marker.sh
test:shepherd-readiness:
desc: Unit-test the shepherd readiness gate + read-only gh wrapper (offline, stubbed gh)
cmds:
- ./scripts/test-shepherd-readiness.sh
test:lint-hygiene:
desc: Behavioral fixtures for lint-hygiene's trigger-phrase adjacency scan
cmds:
- ./scripts/test-lint-hygiene.sh
test:release-title:
desc: Unit-test the release-content title guard (require-release-title.sh)
cmds:
- ./scripts/test-release-title.sh
test:notify-release:
desc: Unit-test the harmon-init release dispatch (offline, stubbed gh)
cmds:
- ./scripts/test-notify-devkit-release.sh
test:track-work:
desc: Unit-test the track-work skill's issue/PR tracking checks (offline)
cmds:
- ./scripts/test-track-work.sh
test:sync-devkit-release:
desc: "Unit-test the harmon-devkit release→sync-PR automation (offline, stubbed gh/task)"
cmds:
- ./scripts/test-sync-devkit-release.sh
test:setup-github-project:
desc: Unit-test the GitHub Project field reconciliation (stubbed gh, no network)
cmds:
- ./scripts/test-setup-github-project.sh
test:devcontainer:root:
desc: Smoke test the primary (AI bot) devcontainer via the devcontainers CLI
cmds:
- ./scripts/devcontainer-smoke.sh .devcontainer/devcontainer.json
test:devcontainer:dev:
desc: Smoke test the dev (human) devcontainer via the devcontainers CLI
cmds:
- ./scripts/devcontainer-smoke.sh .devcontainer/dev/devcontainer.json
test:devcontainer:permissions:
desc: Unit-assert devcontainer permission invariants (no container needed)
cmds:
- ./scripts/devcontainer-assert.sh unit
test:statusline:
desc: Unit-test the devcontainer status line renderer (no container needed)
cmds:
- ./scripts/test-statusline.sh
test:hooks:
desc: Round-trip the Taskfile targets the Claude hooks delegate to
cmds:
- ./scripts/test-hooks.sh
test:skills:
desc: Unit-test the skills tooling (source guard + pinned vendoring engine)
cmds:
- ./scripts/test-skills.sh
test:agents:
desc: Unit-test the shared-subagent source guard (offline)
cmds:
- ./scripts/test-agents.sh
test:hygiene:
desc: Unit-test lint-hygiene's exec-bit check on shebanged files
cmds:
- ./scripts/test-hygiene.sh
test:archive-hook:
desc: Offline regression tests for the SessionEnd transcript-archive hook template
cmds:
- ./scripts/test-archive-hook.sh
test:skill-index-lifecycle:
desc: Guard public skill indexes against stale lifecycle language
cmds:
- ./scripts/test-skill-index-lifecycle.sh
test:independence:
desc: Unit-test the shipped-output independence guard (offline, temp repos)
cmds:
- ./scripts/test-independence.sh
# ── Second-model review (Codex) ───────────────────────────────
# Optional second AI model (the OpenAI Codex CLI) reviewing the current
# change — local and advisory only, never part of verify/ci. Findings are
# hypotheses for the primary agent to adjudicate (AGENTS.md "Second-Model
# Review"). The codex:gate:* tasks toggle the Claude Code codex plugin's
# automatic stop-time review for this repo + machine. See
# docs/guides/codex-review.md for setup, mechanics, and the loop caps.
review:
desc: Second-model verification review of the current change (Codex)
cmds:
- task: review:codex
challenge:
desc: Second-model adversarial review of the current change (Codex)
cmds:
- task: challenge:codex
review:codex:
desc: "Codex verification review (usage: task review:codex -- [--base <ref>|--uncommitted] [focus text])"
cmds:
- ./scripts/codex-review.sh review {{.CLI_ARGS}}
challenge:codex:
desc: "Codex adversarial review (usage: task challenge:codex -- [--base <ref>|--uncommitted] [focus text])"
cmds:
- ./scripts/codex-review.sh challenge {{.CLI_ARGS}}
codex:gate:enable:
desc: Enable the automatic Claude → Codex stop-gate review (this repo, this machine)
cmds:
- ./scripts/codex-gate.sh enable
codex:gate:disable:
desc: Disable the automatic Claude → Codex stop-gate review
cmds:
- ./scripts/codex-gate.sh disable
codex:gate:status:
desc: Show whether the Claude → Codex stop-gate review is enabled
cmds:
- ./scripts/codex-gate.sh status
# ── Security ───────────────────────────────────────────────────
security:
desc: Run all free local security checks (Semgrep CE + gitleaks + dependency audit)
deps:
- security:sast
- security:secrets
- security:audit
security:secrets:
desc: Scan for leaked secrets (gitleaks)
cmds:
- |
if [ -z "$GITHUB_STEP_SUMMARY" ]; then
gitleaks detect --no-banner --redact --source .
else
rc=0
gitleaks detect --no-banner --redact --source . \
--report-format json --report-path /tmp/harmon-devkit-gitleaks.json || rc=$?
node scripts/summarize-gitleaks.mjs /tmp/harmon-devkit-gitleaks.json
exit $rc
fi
security:audit:
desc: Dependency audit
cmds:
- echo "No package manifests to audit yet."
security:sast:
desc: Static application security testing (Semgrep Community Edition)
cmds:
- ./scripts/run-semgrep.sh {{.CLI_ARGS}}
security:sca:
desc: Software composition analysis (free package-manager audit)
deps:
- security:audit
security:sast:snyk:
desc: "Optional Snyk Code second-opinion scan (manual/local or explicitly scheduled); needs SNYK_TOKEN"
cmds:
- snyk code test --exclude=.worktrees,.foreman,.claude --severity-threshold=high {{.CLI_ARGS}}
security:sca:snyk:
desc: "Optional Snyk Open Source second-opinion scan (manual/local or explicitly scheduled); needs SNYK_TOKEN"
cmds:
- snyk test --all-projects --exclude=.worktrees,.foreman,.claude --severity-threshold=high --show-vulnerable-paths=all {{.CLI_ARGS}}
# ── Secrets ────────────────────────────────────────────────────
# Destination-only helpers: the secret value stays on stdin — never in argv,
# env vars, Taskfile vars, or shell history (see docs/conventions.md).
secret:set:1p:
desc: "Set an existing 1Password field from stdin (usage: command | task secret:set:1p VAULT=... ITEM=... FIELD=... [SECTION=...])"
env:
VAULT: '{{.VAULT | default ""}}'
ITEM: '{{.ITEM | default ""}}'
FIELD: '{{.FIELD | default ""}}'
SECTION: '{{.SECTION | default ""}}'
cmds:
- ./scripts/secret-set-1p.sh
secret:set:gh:
desc: "Set a GitHub repo secret from stdin (usage: command | task secret:set:gh NAME=... REPO=owner/repo)"
env:
NAME: '{{.NAME | default ""}}'
REPO: '{{.REPO | default ""}}'
cmds:
- ./scripts/secret-set-gh.sh
# ── Setup ──────────────────────────────────────────────────────
bootstrap:
desc: One-time machine setup (Homebrew)
cmds:
# Idempotent: skip the installer when Homebrew is already present. The
# official installer's NONINTERACTIVE mode demands sudo up front and aborts
# even when there is nothing to install, so guard on `brew` existing.
- command -v brew >/dev/null 2>&1 || NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
install:
desc: Install all project dependencies and tools
cmds:
# Brewfile deps by absolute path (never a user/global one), but a graceful
# no-op when Homebrew is absent — this repo's devcontainer bakes its
# toolchain into the image and ships no brew.
- ./scripts/install-brewfile.sh
# copier is needed by the skills tooling tests, which render local Copier
# templates. On a brew host it comes from the Brewfile above; this script
# covers the brew-less devcontainer via uv. Local to this repo: generated
# repos don't render templates, so harmon-init keeps copier out of
# template/ and it cannot arrive from the template.
- ./scripts/install-copier.sh
- task: install:hooks
install:hooks:
desc: Install git hooks via lefthook
cmds:
- lefthook install
# ── GitHub setup ───────────────────────────────────────────────
setup:github-project:
desc: >-
Idempotently create/sync the owner's GitHub Project V2 board + Status
pipeline + Size number field (on a personal account also the other
metadata fields). Requires `gh auth refresh -s project`.
cmds:
- ./scripts/setup-github-project.sh --owner "evanharmon1" --title "evanharmon1 Project"
setup:github-labels:
desc: Idempotently create/update this repo's starter labels incl. the foreman arming labels. Requires `gh` with repo write.
vars:
REPO: "evanharmon1/harmon-devkit"
cmds:
- ./scripts/setup-github-labels.sh --repo "{{.REPO}}" --foreman
# One-time, idempotent repo settings applied via gh (safe to re-run); run once
# the repo exists on GitHub. The remaining setup steps need a browser or a real
# credential and stay manual — see docs/CHECKLIST.md (branch ruleset import,
# Renovate/optional third-party app installs, Actions secrets, the CI GitHub App).
setup:github:
desc: >-
Apply idempotent GitHub repo settings via gh (Dependabot alerts, private
vulnerability reporting)
vars:
REPO: "evanharmon1/harmon-devkit"
cmds:
- gh api "repos/{{.REPO}}/vulnerability-alerts" --method PUT
# Private vulnerability reporting is a public-repo-only feature; harmon-init
# creates repos --private, so guard on visibility to avoid a 404 that would
# abort the task. Applies cleanly if the repo is later made public.
- |
if [ "$(gh api "repos/{{.REPO}}" --jq '.private')" = "false" ]; then
gh api "repos/{{.REPO}}/private-vulnerability-reporting" --method PUT
else
echo "Skipping private vulnerability reporting ({{.REPO}} is private; public-repo-only feature)."
fi
# ── Release ────────────────────────────────────────────────────
# Releases are intentional via release-please (.github/workflows/release.yml):
# merge the rolling release PR to cut a release. release:init seeds the first
# v0.1.0 tag; the release:patch/minor/major tasks remain as a manual override.
release:init:
desc: Create initial v0.1.0 tag
cmds:
- git tag -a v0.1.0 -m "Initial release"
- git push origin v0.1.0
- gh release create v0.1.0 --generate-notes --title "v0.1.0"
release:patch:
desc: Bump patch version and release
vars:
CURRENT_TAG:
sh: git describe --tags --abbrev=0 --exclude="*-probe*" 2>/dev/null || echo "v0.0.0"
NEXT_TAG:
sh: echo "{{.CURRENT_TAG}}" | awk -F. '{printf "%s.%s.%d", $1, $2, $3+1}'
cmds:
- echo "Releasing {{.NEXT_TAG}} (was {{.CURRENT_TAG}})"
- git tag -a {{.NEXT_TAG}} -m "Release {{.NEXT_TAG}}"
- git push origin {{.NEXT_TAG}}
- gh release create {{.NEXT_TAG}} --generate-notes --title "{{.NEXT_TAG}}"
release:minor:
desc: Bump minor version and release
vars:
CURRENT_TAG:
sh: git describe --tags --abbrev=0 --exclude="*-probe*" 2>/dev/null || echo "v0.0.0"
NEXT_TAG:
sh: echo "{{.CURRENT_TAG}}" | awk -F. '{printf "%s.%d.0", $1, $2+1}'
cmds:
- echo "Releasing {{.NEXT_TAG}} (was {{.CURRENT_TAG}})"
- git tag -a {{.NEXT_TAG}} -m "Release {{.NEXT_TAG}}"
- git push origin {{.NEXT_TAG}}
- gh release create {{.NEXT_TAG}} --generate-notes --title "{{.NEXT_TAG}}"
release:major:
desc: Bump major version and release
vars:
CURRENT_TAG:
sh: git describe --tags --abbrev=0 --exclude="*-probe*" 2>/dev/null || echo "v0.0.0"
NEXT_TAG:
sh: echo "{{.CURRENT_TAG}}" | awk -F. '{printf "v%d.0.0", substr($1,2)+1}'
cmds:
- echo "Releasing {{.NEXT_TAG}} (was {{.CURRENT_TAG}})"
- git tag -a {{.NEXT_TAG}} -m "Release {{.NEXT_TAG}}"
- git push origin {{.NEXT_TAG}}
- gh release create {{.NEXT_TAG}} --generate-notes --title "{{.NEXT_TAG}}"
release:check-coverage:
desc: "Detect feat/fix commits a tag ships that its changelog section omits (swallowed entries from a release-please merge race)"
vars:
TAG: '{{default "" .CLI_ARGS}}'
env:
TAG: '{{.TAG}}'
cmds:
- ./scripts/check-changelog-coverage.sh "$TAG"
# ── Clean ──────────────────────────────────────────────────────
clean:
desc: Remove build artifacts and caches
cmds:
- rm -rf .task
# ── Status ─────────────────────────────────────────────────────
status:
desc: Project status dashboard
cmds:
- ./scripts/status.sh
status:git:
desc: Git status section
cmds:
- ./scripts/status.sh git
status:gh:
desc: GitHub status section
cmds:
- ./scripts/status.sh gh
status:creds:
desc: Local credential section (gh, Codex, Claude Code logins — local probes, no network)
cmds:
- ./scripts/status.sh creds
status:code:
desc: Codebase stats section
cmds:
- ./scripts/status.sh code
status:env:
desc: Environment info section
cmds:
- ./scripts/status.sh env
status:setup:
desc: Setup completeness audit (GitHub, toolchain, devcontainer, dev env)
cmds:
- ./scripts/status.sh setup
# ── Utilities ──────────────────────────────────────────────────
util:bunch-add:
desc: Scaffold the Bunch launcher in .meta (mirrors what the template generates)
cmds:
- ./scripts/meta-create.sh bunch 'Harmon DevKit' 'harmon-devkit' '~/git'
util:bunch-install:
desc: Move the Bunch file to iCloud and replace it with a symlink (macOS)
cmds:
- ./scripts/meta-install.sh bunch 'Harmon DevKit' '~/Library/Mobile Documents/com~apple~CloudDocs/Bunches'
util:obsidian-add:
desc: Scaffold the Obsidian project note in .meta (mirrors what the template generates)
cmds:
- ./scripts/meta-create.sh obsidian 'Harmon DevKit' 'Evan Harmon'
util:obsidian-install:
desc: Move the Obsidian note to the vault and replace it with a symlink (macOS)
cmds:
- ./scripts/meta-install.sh obsidian 'Harmon DevKit' '~/Local/Memex/Professional'