claude-launchpad 1.9.0 → 1.10.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/README.md +29 -45
- package/dist/{chunk-GA3IUQUM.js → chunk-42Q2MAQB.js} +3 -3
- package/dist/{chunk-RCYLZUU6.js → chunk-5KQ2JDZN.js} +401 -55
- package/dist/chunk-5KQ2JDZN.js.map +1 -0
- package/dist/{chunk-72VWDNAE.js → chunk-OWOW5KFX.js} +2 -2
- package/dist/{chunk-DYHPVA6O.js → chunk-WVQBG4YR.js} +2 -2
- package/dist/{chunk-I4S4Q2IV.js → chunk-ZIEONJBY.js} +2 -2
- package/dist/cli.js +86 -42
- package/dist/cli.js.map +1 -1
- package/dist/commands/memory/server.js +3 -3
- package/dist/{context-X7UP2ODK.js → context-QLQLJOR2.js} +5 -5
- package/dist/{install-XBCEI5QK.js → install-5XZFLN3C.js} +7 -6
- package/dist/{install-XBCEI5QK.js.map → install-5XZFLN3C.js.map} +1 -1
- package/dist/{pull-VA62U3OP.js → pull-46YFKQ6S.js} +7 -7
- package/dist/{push-C3M6Q4V7.js → push-FMAHNK4U.js} +7 -7
- package/dist/{require-deps-UBU5CYM5.js → require-deps-H4SHQWD2.js} +3 -3
- package/dist/{stats-R4TWCPHW.js → stats-YGK6PZ3A.js} +6 -6
- package/dist/{sync-clean-P4S7V2JS.js → sync-clean-PCR3QCZK.js} +3 -3
- package/dist/{sync-status-TPYUF43G.js → sync-status-KZSPPHPY.js} +7 -7
- package/dist/{tui-FFLCUR7E.js → tui-XXYVOGJL.js} +4 -4
- package/package.json +2 -2
- package/dist/chunk-RCYLZUU6.js.map +0 -1
- /package/dist/{chunk-GA3IUQUM.js.map → chunk-42Q2MAQB.js.map} +0 -0
- /package/dist/{chunk-72VWDNAE.js.map → chunk-OWOW5KFX.js.map} +0 -0
- /package/dist/{chunk-DYHPVA6O.js.map → chunk-WVQBG4YR.js.map} +0 -0
- /package/dist/{chunk-I4S4Q2IV.js.map → chunk-ZIEONJBY.js.map} +0 -0
- /package/dist/{context-X7UP2ODK.js.map → context-QLQLJOR2.js.map} +0 -0
- /package/dist/{pull-VA62U3OP.js.map → pull-46YFKQ6S.js.map} +0 -0
- /package/dist/{push-C3M6Q4V7.js.map → push-FMAHNK4U.js.map} +0 -0
- /package/dist/{require-deps-UBU5CYM5.js.map → require-deps-H4SHQWD2.js.map} +0 -0
- /package/dist/{stats-R4TWCPHW.js.map → stats-YGK6PZ3A.js.map} +0 -0
- /package/dist/{sync-clean-P4S7V2JS.js.map → sync-clean-PCR3QCZK.js.map} +0 -0
- /package/dist/{sync-status-TPYUF43G.js.map → sync-status-KZSPPHPY.js.map} +0 -0
- /package/dist/{tui-FFLCUR7E.js.map → tui-XXYVOGJL.js.map} +0 -0
|
@@ -32,20 +32,20 @@ async function readJsonOrNull(path) {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// src/lib/settings.ts
|
|
35
|
-
import { readFile as
|
|
36
|
-
import { join as
|
|
35
|
+
import { readFile as readFile5, writeFile as writeFile5, mkdir as mkdir4 } from "fs/promises";
|
|
36
|
+
import { join as join7 } from "path";
|
|
37
37
|
|
|
38
38
|
// src/lib/output.ts
|
|
39
39
|
import chalk from "chalk";
|
|
40
40
|
|
|
41
41
|
// src/commands/doctor/fixer.ts
|
|
42
|
-
import { readFile as
|
|
43
|
-
import { join as
|
|
42
|
+
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir3, access as access2 } from "fs/promises";
|
|
43
|
+
import { join as join6 } from "path";
|
|
44
44
|
import { homedir } from "os";
|
|
45
45
|
|
|
46
46
|
// src/lib/sections.ts
|
|
47
47
|
var SESSION_START_CONTENT = "- ALWAYS read @TASKS.md first \u2014 it tracks progress across sessions\n- Check the Session Log at the bottom of TASKS.md for where we left off\n- Update TASKS.md as you complete work";
|
|
48
|
-
var BACKLOG_CONTENT = "- When a feature is discussed but deferred, add it to BACKLOG.md immediately\n- Never leave future ideas only in TASKS.md or conversation \u2014 they get lost\n- BACKLOG.md is the single source of truth for parked features";
|
|
48
|
+
var BACKLOG_CONTENT = "- When a feature is discussed but deferred, add it to BACKLOG.md immediately\n- Never leave future ideas only in TASKS.md or conversation \u2014 they get lost\n- BACKLOG.md is the single source of truth for parked features\n- Every WP uses the 7-field template in BACKLOG.md \u2014 no freeform entries\n- Pull a WP into a sprint = **move**, not copy. A WP lives in exactly one file at a time";
|
|
49
49
|
var STOP_AND_SWARM_CONTENT = "Three failed iterations on the same problem = stop iterating alone.\nOn the fourth attempt, spin up at least 3 parallel agents via the Agent tool, each investigating from a different angle:\n1. Root-cause debug agent\n2. Upstream library/docs research agent\n3. Alternative architecture agent\nWait for all agents to return, synthesize their findings, then act.\nDon't keep guessing in circles \u2014 rotate perspectives.";
|
|
50
50
|
var OFF_LIMITS_CONTENT = "- Never hardcode secrets \u2014 use environment variables\n- Never write to `.env` files\n- Never expose internal error details in API responses";
|
|
51
51
|
var SKILL_AUTHORING_CONTENT = 'When creating Claude Code skills (.claude/skills/*/SKILL.md):\n\n- Keep SKILL.md under 500 lines \u2014 move reference material to supporting files in the same directory\n- Front-load description (first 250 chars shown in listings) with TRIGGER when / DO NOT TRIGGER when clauses\n- Add allowed-tools in frontmatter to restrict tool access (e.g. Read, Glob, Grep for read-only skills)\n- Add argument-hint in frontmatter showing the expected input format (use $ARGUMENTS or $0, $1 for dynamic input)\n- Set disable-model-invocation: true for skills with side effects (deploy, send messages)\n- Structure as phases: Research, Plan, Execute, Verify with "Done when:" success criteria per phase\n- Handle edge cases and preconditions before execution';
|
|
@@ -350,7 +350,7 @@ DerivedData/
|
|
|
350
350
|
}
|
|
351
351
|
|
|
352
352
|
// src/commands/init/generators/skill-enhance.ts
|
|
353
|
-
var ENHANCE_SKILL_VERSION =
|
|
353
|
+
var ENHANCE_SKILL_VERSION = 9;
|
|
354
354
|
function generateEnhanceSkill() {
|
|
355
355
|
return [
|
|
356
356
|
"---",
|
|
@@ -392,7 +392,7 @@ function generateEnhanceSkill() {
|
|
|
392
392
|
"2. **## Architecture** - 3-5 bullets describing codebase shape",
|
|
393
393
|
"3. **## Conventions** - max 8 key patterns. Overflow to .claude/rules/conventions.md",
|
|
394
394
|
"4. **## Off-Limits** - max 8 guardrails specific to this project",
|
|
395
|
-
"5. **## Memory** - ONLY if agentic-memory is configured in settings.json. Max 6 bullets.",
|
|
395
|
+
"5. **## Memory (agentic-memory)** - ONLY if agentic-memory is configured in settings.json. Max 6 bullets. Use this exact heading so `memory install` stays idempotent.",
|
|
396
396
|
"6. **## Key Decisions** - only decisions that affect how Claude works in this codebase",
|
|
397
397
|
"",
|
|
398
398
|
"7. **Skill Authoring** - if .claude/rules/conventions.md lacks a Skill Authoring section, plan to add one",
|
|
@@ -557,6 +557,88 @@ function generateEnhanceSkill() {
|
|
|
557
557
|
].join("\n");
|
|
558
558
|
}
|
|
559
559
|
|
|
560
|
+
// src/commands/init/generators/backlog.ts
|
|
561
|
+
function generateBacklogMd(options) {
|
|
562
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
563
|
+
return `# ${options.name} \u2014 Backlog
|
|
564
|
+
|
|
565
|
+
> **Single source of truth for future work.** Every work package (WP) that's been proposed but not yet started lives here.
|
|
566
|
+
>
|
|
567
|
+
> Rules (see \`.claude/rules/workflow.md\` for the full lifecycle):
|
|
568
|
+
> - Every WP uses the template below \u2014 no freeform entries.
|
|
569
|
+
> - WPs are **moved** to \`TASKS.md\` when pulled into a sprint, not copied. A WP lives in exactly one file at a time.
|
|
570
|
+
> - Priority ordering: **P0 > P1 > P2 > P3**. P0 items sit at the top.
|
|
571
|
+
> - A WP ID is minted when the WP is first added here and never reused.
|
|
572
|
+
|
|
573
|
+
---
|
|
574
|
+
|
|
575
|
+
## Priority definitions
|
|
576
|
+
|
|
577
|
+
| Priority | Meaning |
|
|
578
|
+
|---|---|
|
|
579
|
+
| **P0** | Blocks launch or an active sprint. Next sprint or sooner. |
|
|
580
|
+
| **P1** | Important for MVP. Pulled within 2\u20133 sprints. |
|
|
581
|
+
| **P2** | Post-MVP or nice-to-have. Review monthly. |
|
|
582
|
+
| **P3** | Parked idea. Review quarterly; delete if still untouched. |
|
|
583
|
+
|
|
584
|
+
---
|
|
585
|
+
|
|
586
|
+
## Work package template (copy this exactly)
|
|
587
|
+
|
|
588
|
+
\`\`\`markdown
|
|
589
|
+
### WP-NNN \u2014 <short imperative title>
|
|
590
|
+
|
|
591
|
+
- **Priority:** P0 | P1 | P2 | P3
|
|
592
|
+
- **Proposed:** YYYY-MM-DD
|
|
593
|
+
- **Stories / Docs:** links to specs, issues, or "none yet"
|
|
594
|
+
- **Depends on:** WP-MMM (empty if none)
|
|
595
|
+
- **Estimate:** XS (<1h) | S (half-day) | M (1\u20132 days) | L (full sprint) | XL (>1 sprint; must decompose)
|
|
596
|
+
- **Trigger to pull:** What has to be true before this moves into a sprint.
|
|
597
|
+
- **Definition of done:** Exactly what "done" looks like.
|
|
598
|
+
|
|
599
|
+
One-paragraph description. Context, rationale, rough approach if known. Keep it tight.
|
|
600
|
+
\`\`\`
|
|
601
|
+
|
|
602
|
+
**Hard rules for entries:**
|
|
603
|
+
- Title is an imperative ("Add referral banner", "Wire up StoreKit upgrade flow") \u2014 not a noun.
|
|
604
|
+
- \`Trigger to pull\` and \`Definition of done\` are mandatory. A WP without either is incomplete.
|
|
605
|
+
- \`XL\` estimates must be broken into multiple WPs before pulling into a sprint.
|
|
606
|
+
- WP IDs are minted on first entry and never reused. Check the Changelog below for the highest ID before adding a new one.
|
|
607
|
+
|
|
608
|
+
---
|
|
609
|
+
|
|
610
|
+
## P0 \u2014 Next sprint
|
|
611
|
+
|
|
612
|
+
<!-- Empty. WPs appear here when promoted from P1, or when a new blocker surfaces. -->
|
|
613
|
+
|
|
614
|
+
---
|
|
615
|
+
|
|
616
|
+
## P1 \u2014 Soon (within 2\u20133 sprints)
|
|
617
|
+
|
|
618
|
+
<!-- Empty. Add WPs here as sprints approach. -->
|
|
619
|
+
|
|
620
|
+
---
|
|
621
|
+
|
|
622
|
+
## P2 \u2014 Post-MVP / nice-to-have
|
|
623
|
+
|
|
624
|
+
<!-- Empty. Review monthly. -->
|
|
625
|
+
|
|
626
|
+
---
|
|
627
|
+
|
|
628
|
+
## P3 \u2014 Parked
|
|
629
|
+
|
|
630
|
+
<!-- Empty. Move P2 items here if they survive 2 quarterly reviews without being pulled. -->
|
|
631
|
+
|
|
632
|
+
---
|
|
633
|
+
|
|
634
|
+
## Changelog
|
|
635
|
+
|
|
636
|
+
Capture WP promotions, demotions, and deletions so you can audit backlog drift.
|
|
637
|
+
|
|
638
|
+
- ${today}: Backlog established.
|
|
639
|
+
`;
|
|
640
|
+
}
|
|
641
|
+
|
|
560
642
|
// src/lib/memory-placement.ts
|
|
561
643
|
import { select } from "@inquirer/prompts";
|
|
562
644
|
function hasMemoryPermissions(settings) {
|
|
@@ -705,6 +787,55 @@ if [ "$backlog_deletions" -eq 0 ]; then
|
|
|
705
787
|
echo ""
|
|
706
788
|
fi
|
|
707
789
|
|
|
790
|
+
exit 0
|
|
791
|
+
`;
|
|
792
|
+
var WORKFLOW_CHECK = `#!/usr/bin/env bash
|
|
793
|
+
# Warns on BACKLOG.md / TASKS.md drift. Non-blocking (always exits 0).
|
|
794
|
+
# 1. Same WP ID present in BOTH BACKLOG.md and TASKS.md (violates move-not-copy).
|
|
795
|
+
# 2. TASKS.md longer than 80 lines.
|
|
796
|
+
# 3. \\\`## Current Sprint\\\` contains more than 15 items.
|
|
797
|
+
# 4. \\\`## Session Log\\\` has more than 3 entries.
|
|
798
|
+
|
|
799
|
+
set -u
|
|
800
|
+
fp="\${TOOL_INPUT_FILE_PATH:-}"
|
|
801
|
+
|
|
802
|
+
# Only act on edits to BACKLOG.md or TASKS.md.
|
|
803
|
+
echo "$fp" | grep -qE '(^|/)(BACKLOG|TASKS)\\.md$' || exit 0
|
|
804
|
+
|
|
805
|
+
warn() { printf '%s\\n' "$*"; }
|
|
806
|
+
|
|
807
|
+
# 1. Duplicate WP IDs across both files.
|
|
808
|
+
if [ -f BACKLOG.md ] && [ -f TASKS.md ]; then
|
|
809
|
+
dupes=$(grep -oE 'WP-[0-9]{3}' BACKLOG.md 2>/dev/null | sort -u | while read -r wp; do
|
|
810
|
+
grep -q "$wp" TASKS.md 2>/dev/null && echo "$wp"
|
|
811
|
+
done)
|
|
812
|
+
if [ -n "$dupes" ]; then
|
|
813
|
+
warn "Workflow bug: WP ID present in BOTH BACKLOG.md and TASKS.md (violates move-not-copy \u2014 see .claude/rules/workflow.md):"
|
|
814
|
+
printf '%s\\n' "$dupes"
|
|
815
|
+
warn "Move each listed WP to exactly one file."
|
|
816
|
+
fi
|
|
817
|
+
fi
|
|
818
|
+
|
|
819
|
+
# 2. TASKS.md length.
|
|
820
|
+
if [ -f TASKS.md ]; then
|
|
821
|
+
tasks_lines=$(wc -l < TASKS.md 2>/dev/null | tr -d ' ')
|
|
822
|
+
if [ "\${tasks_lines:-0}" -gt 80 ]; then
|
|
823
|
+
warn "TASKS.md is $tasks_lines lines \u2014 should stay under 80. Prune Completed Sprints or Session Log."
|
|
824
|
+
fi
|
|
825
|
+
|
|
826
|
+
# 3. Current Sprint size.
|
|
827
|
+
current_count=$(awk '/^## Current/{flag=1; next} /^## /{flag=0} flag' TASKS.md 2>/dev/null | grep -cE '^[[:space:]]*- \\[[ x]\\]' || true)
|
|
828
|
+
if [ "\${current_count:-0}" -gt 15 ]; then
|
|
829
|
+
warn "## Current Sprint has $current_count items \u2014 split the sprint (see .claude/rules/workflow.md)."
|
|
830
|
+
fi
|
|
831
|
+
|
|
832
|
+
# 4. Session Log size.
|
|
833
|
+
log_count=$(awk '/^## Session Log/{flag=1; next} /^## /{flag=0} flag' TASKS.md 2>/dev/null | grep -cE '^- \\*\\*' || true)
|
|
834
|
+
if [ "\${log_count:-0}" -gt 3 ]; then
|
|
835
|
+
warn "## Session Log has $log_count entries \u2014 keep to 3 max."
|
|
836
|
+
fi
|
|
837
|
+
fi
|
|
838
|
+
|
|
708
839
|
exit 0
|
|
709
840
|
`;
|
|
710
841
|
async function writeSprintHygieneScripts(root) {
|
|
@@ -718,6 +849,14 @@ async function writeSprintHygieneScripts(root) {
|
|
|
718
849
|
await chmod(openPath, 493);
|
|
719
850
|
return { sizePath, openPath };
|
|
720
851
|
}
|
|
852
|
+
async function writeWorkflowCheckScript(root) {
|
|
853
|
+
const hooksDir = join2(root, ".claude", "hooks");
|
|
854
|
+
await mkdir(hooksDir, { recursive: true });
|
|
855
|
+
const scriptPath = join2(hooksDir, "workflow-check.sh");
|
|
856
|
+
await writeFile(scriptPath, WORKFLOW_CHECK);
|
|
857
|
+
await chmod(scriptPath, 493);
|
|
858
|
+
return scriptPath;
|
|
859
|
+
}
|
|
721
860
|
|
|
722
861
|
// src/commands/doctor/fixer-sprint.ts
|
|
723
862
|
var WORKTREE_INCLUDE_TEMPLATE = `# Files copied into git worktrees that Claude Code creates for subagents.
|
|
@@ -758,6 +897,211 @@ async function addSprintCompleteNudge(root) {
|
|
|
758
897
|
}]
|
|
759
898
|
}, "Added sprint-complete nudge hook");
|
|
760
899
|
}
|
|
900
|
+
async function addWorkflowCheckHook(root) {
|
|
901
|
+
await writeWorkflowCheckScript(root);
|
|
902
|
+
return addHookToSettings(root, "PostToolUse", "workflow-check.sh", {
|
|
903
|
+
matcher: "Edit|Write",
|
|
904
|
+
hooks: [{ type: "command", command: "bash .claude/hooks/workflow-check.sh 2>/dev/null; exit 0" }]
|
|
905
|
+
}, "Added workflow-check hook (BACKLOG/TASKS staleness warnings)");
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// src/commands/doctor/fixer-quality.ts
|
|
909
|
+
import { readFile as readFile2, writeFile as writeFile3, mkdir as mkdir2 } from "fs/promises";
|
|
910
|
+
import { join as join4 } from "path";
|
|
911
|
+
|
|
912
|
+
// src/commands/init/generators/workflow-rule.ts
|
|
913
|
+
var WORKFLOW_RULE_VERSION = 1;
|
|
914
|
+
function generateWorkflowRule() {
|
|
915
|
+
return `---
|
|
916
|
+
paths: ["BACKLOG.md", "TASKS.md"]
|
|
917
|
+
---
|
|
918
|
+
|
|
919
|
+
# Backlog \u2192 Tasks \u2192 Sprint Workflow Rules
|
|
920
|
+
|
|
921
|
+
<!-- lp-workflow-version: ${WORKFLOW_RULE_VERSION} -->
|
|
922
|
+
|
|
923
|
+
These rules apply whenever editing \`BACKLOG.md\` or \`TASKS.md\`. The workflow is load-bearing \u2014 drift here breaks sprint integrity.
|
|
924
|
+
|
|
925
|
+
## The single rule that makes everything else work
|
|
926
|
+
|
|
927
|
+
**A work package (WP) lives in exactly one of \`BACKLOG.md\` or \`TASKS.md\` at any time.**
|
|
928
|
+
|
|
929
|
+
- Pulled into a sprint \u2192 move (delete from BACKLOG.md, add to TASKS.md under \`## Current Sprint\`) in a single edit.
|
|
930
|
+
- Sprint closed \u2192 the WP leaves TASKS.md entirely (summarized into \`## Completed Sprints\`) and does not return to the backlog.
|
|
931
|
+
- A WP ID appearing in both files at once = bug. A PostToolUse hook warns.
|
|
932
|
+
|
|
933
|
+
## WP IDs
|
|
934
|
+
|
|
935
|
+
- Format: \`WP-NNN\` (three digits, zero-padded).
|
|
936
|
+
- Minted on first entry to \`BACKLOG.md\`.
|
|
937
|
+
- Never reused, never renamed.
|
|
938
|
+
- Highest-used ID lives at the end of \`BACKLOG.md ## Changelog\` \u2014 check there before minting a new one.
|
|
939
|
+
|
|
940
|
+
## BACKLOG.md structure (mandatory sections, in order)
|
|
941
|
+
|
|
942
|
+
1. Header + rules comment block
|
|
943
|
+
2. \`## Priority definitions\` table
|
|
944
|
+
3. \`## Work package template\` (the canonical format)
|
|
945
|
+
4. \`## P0 \u2014 Next sprint\`
|
|
946
|
+
5. \`## P1 \u2014 Soon (within 2\u20133 sprints)\`
|
|
947
|
+
6. \`## P2 \u2014 Post-MVP / nice-to-have\`
|
|
948
|
+
7. \`## P3 \u2014 Parked\`
|
|
949
|
+
8. \`## Changelog\`
|
|
950
|
+
|
|
951
|
+
### Every WP entry must include
|
|
952
|
+
|
|
953
|
+
- \`Priority:\` exactly one of P0/P1/P2/P3
|
|
954
|
+
- \`Proposed:\` ISO date (YYYY-MM-DD)
|
|
955
|
+
- \`Stories / Docs:\` spec references, issues, or empty
|
|
956
|
+
- \`Depends on:\` WP IDs or empty
|
|
957
|
+
- \`Estimate:\` one of XS/S/M/L/XL (XL must be decomposed before pulling)
|
|
958
|
+
- \`Trigger to pull:\` what event promotes this to an active sprint
|
|
959
|
+
- \`Definition of done:\` concrete acceptance criteria
|
|
960
|
+
- One-paragraph description
|
|
961
|
+
|
|
962
|
+
### Priority discipline
|
|
963
|
+
|
|
964
|
+
- P0 items are for the next sprint. If P0 is empty at sprint start, pull the top P1.
|
|
965
|
+
- P1 \u2192 P0 promotion happens during sprint wrap-up, not mid-sprint.
|
|
966
|
+
- P2 items get a monthly review. P3 items get a quarterly review and are deleted if still untouched.
|
|
967
|
+
- Stale P0 items (>2 weeks without movement) are a flag: either pull or demote.
|
|
968
|
+
|
|
969
|
+
### Changelog discipline
|
|
970
|
+
|
|
971
|
+
- Every WP promotion, demotion, or deletion gets a one-line entry with the date.
|
|
972
|
+
- A backlog audit that finds no changelog entries for 30+ days = staleness; force a review.
|
|
973
|
+
|
|
974
|
+
## TASKS.md structure (mandatory sections, in order)
|
|
975
|
+
|
|
976
|
+
1. Header + rules comment
|
|
977
|
+
2. \`## Current Sprint\`
|
|
978
|
+
3. \`## Completed Sprints\`
|
|
979
|
+
4. \`## Session Log\`
|
|
980
|
+
|
|
981
|
+
### \`## Current Sprint\` discipline
|
|
982
|
+
|
|
983
|
+
- Contains ONLY the active sprint's WPs. Empty between sprints.
|
|
984
|
+
- Format: \`- [ ] WP-NNN \u2014 short title\` (checkbox + WP ID + title from backlog).
|
|
985
|
+
- Each pulled WP becomes one checklist item. Sub-tasks nest with two-space indent, still checkbox.
|
|
986
|
+
- New ideas that surface mid-sprint go to \`BACKLOG.md\`, never appended here.
|
|
987
|
+
|
|
988
|
+
### \`## Completed Sprints\` discipline
|
|
989
|
+
|
|
990
|
+
- One line per sprint. Format: \`- **SN**: one-sentence outcome + key metric if any.\`
|
|
991
|
+
- Never a wall of text. Detail lives in git history (or your review file).
|
|
992
|
+
|
|
993
|
+
### \`## Session Log\` discipline
|
|
994
|
+
|
|
995
|
+
- Max 3 entries, most recent 3 sessions only.
|
|
996
|
+
- Each entry: \`- **YYYY-MM-DD (optional window):** what changed this session + what's next.\`
|
|
997
|
+
- Older entries deleted, not archived \u2014 they're in git history if needed.
|
|
998
|
+
|
|
999
|
+
### Size discipline
|
|
1000
|
+
|
|
1001
|
+
- Whole file stays under 80 lines.
|
|
1002
|
+
- If \`## Current Sprint\` exceeds 15 checkboxes, the sprint is too big \u2014 split it (move some WPs back to \`BACKLOG.md\` P0).
|
|
1003
|
+
|
|
1004
|
+
## Sprint lifecycle (the exact edit sequence)
|
|
1005
|
+
|
|
1006
|
+
### Starting a sprint (one session, one commit)
|
|
1007
|
+
|
|
1008
|
+
1. Pick top-priority WPs from \`BACKLOG.md\` (P0 first).
|
|
1009
|
+
2. **Same edit:** delete them from \`BACKLOG.md\`, add them to \`TASKS.md ## Current Sprint\`.
|
|
1010
|
+
3. Update \`BACKLOG.md ## Changelog\`: \`YYYY-MM-DD: WP-NNN pulled into Sprint SN\`.
|
|
1011
|
+
4. Write the sprint plan (outline approach, success criteria, tests to add).
|
|
1012
|
+
5. For hard-TDD surfaces, write the test spec **before** implementation.
|
|
1013
|
+
6. Commit the pull + plan together: \`chore(sprint-N): pull WP-NNN into sprint + plan\`.
|
|
1014
|
+
|
|
1015
|
+
### Closing a sprint (one session, one commit)
|
|
1016
|
+
|
|
1017
|
+
1. All \`## Current Sprint\` items checked off, or explicitly moved back to backlog with rationale.
|
|
1018
|
+
2. Run your review workflow \u2014 verify typecheck, tests, and convention compliance before declaring done.
|
|
1019
|
+
3. Add one-line summary to \`## Completed Sprints\`.
|
|
1020
|
+
4. Empty \`## Current Sprint\` back to the placeholder comment.
|
|
1021
|
+
5. Update \`## Session Log\` (prune to 3 entries).
|
|
1022
|
+
6. \`BACKLOG.md ## Changelog\`: \`YYYY-MM-DD: Sprint SN closed. WP-NNN done.\`
|
|
1023
|
+
|
|
1024
|
+
## What triggers a staleness warning
|
|
1025
|
+
|
|
1026
|
+
A PostToolUse hook fires warnings on these conditions (treat as bugs):
|
|
1027
|
+
|
|
1028
|
+
- A WP ID appears in both \`BACKLOG.md\` and \`TASKS.md\`.
|
|
1029
|
+
- \`TASKS.md\` exceeds 80 lines.
|
|
1030
|
+
- \`## Current Sprint\` has >15 items.
|
|
1031
|
+
- \`## Session Log\` has >3 entries.
|
|
1032
|
+
|
|
1033
|
+
## Do not
|
|
1034
|
+
|
|
1035
|
+
- Don't append to \`TASKS.md\` without first checking \`BACKLOG.md\` for the WP \u2014 silent duplication breaks the source-of-truth rule.
|
|
1036
|
+
- Don't leave \`## Current Sprint\` populated between sprints. An empty sprint section is how you know the last sprint closed cleanly.
|
|
1037
|
+
- Don't invent ad-hoc WP formats. Use the template or update the template \u2014 never both.
|
|
1038
|
+
- Don't rewrite \`## Completed Sprints\` into prose. One line each. Forever.
|
|
1039
|
+
- Don't put anything in \`TASKS.md\` that hasn't passed through \`BACKLOG.md\` first. Ideas \u2192 backlog \u2192 sprint. No shortcuts.
|
|
1040
|
+
`;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// src/commands/doctor/fixer-quality.ts
|
|
1044
|
+
async function createWorkflowRule(root) {
|
|
1045
|
+
const rulesDir = join4(root, ".claude", "rules");
|
|
1046
|
+
const workflowPath = join4(rulesDir, "workflow.md");
|
|
1047
|
+
if (await fileExists(workflowPath)) return false;
|
|
1048
|
+
await mkdir2(rulesDir, { recursive: true });
|
|
1049
|
+
await writeFile3(workflowPath, generateWorkflowRule());
|
|
1050
|
+
log.success("Created .claude/rules/workflow.md (path-scoped BACKLOG/TASKS workflow rules)");
|
|
1051
|
+
return true;
|
|
1052
|
+
}
|
|
1053
|
+
function isMemoryHeading(line) {
|
|
1054
|
+
return /^## Memory( \(agentic-memory\))?\s*$/.test(line);
|
|
1055
|
+
}
|
|
1056
|
+
function findMemoryBlocks(lines) {
|
|
1057
|
+
const blocks = [];
|
|
1058
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1059
|
+
if (!isMemoryHeading(lines[i])) continue;
|
|
1060
|
+
let end = lines.length;
|
|
1061
|
+
for (let j = i + 1; j < lines.length; j++) {
|
|
1062
|
+
if (/^## /.test(lines[j])) {
|
|
1063
|
+
end = j;
|
|
1064
|
+
break;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
blocks.push({ startIdx: i, endIdx: end, tagged: lines[i].includes("(agentic-memory)") });
|
|
1068
|
+
i = end - 1;
|
|
1069
|
+
}
|
|
1070
|
+
return blocks;
|
|
1071
|
+
}
|
|
1072
|
+
async function collapseMemoryHeadings(root) {
|
|
1073
|
+
const claudeMdPath = join4(root, "CLAUDE.md");
|
|
1074
|
+
let content;
|
|
1075
|
+
try {
|
|
1076
|
+
content = await readFile2(claudeMdPath, "utf-8");
|
|
1077
|
+
} catch {
|
|
1078
|
+
return false;
|
|
1079
|
+
}
|
|
1080
|
+
const lines = content.split("\n");
|
|
1081
|
+
const blocks = findMemoryBlocks(lines);
|
|
1082
|
+
if (blocks.length <= 1) return false;
|
|
1083
|
+
const keeper = blocks.find((b) => b.tagged) ?? blocks[0];
|
|
1084
|
+
const droppedByStart = new Map(
|
|
1085
|
+
blocks.filter((b) => b !== keeper).map((b) => [b.startIdx, b])
|
|
1086
|
+
);
|
|
1087
|
+
const kept = [];
|
|
1088
|
+
let skipUntil = -1;
|
|
1089
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1090
|
+
if (i < skipUntil) continue;
|
|
1091
|
+
const dropped = droppedByStart.get(i);
|
|
1092
|
+
if (dropped) {
|
|
1093
|
+
skipUntil = dropped.endIdx;
|
|
1094
|
+
continue;
|
|
1095
|
+
}
|
|
1096
|
+
kept.push(lines[i]);
|
|
1097
|
+
}
|
|
1098
|
+
const canonical = kept.map(
|
|
1099
|
+
(line) => /^## Memory\s*$/.test(line) ? "## Memory (agentic-memory)" : line
|
|
1100
|
+
);
|
|
1101
|
+
await writeFile3(claudeMdPath, canonical.join("\n"));
|
|
1102
|
+
log.success(`Collapsed ${blocks.length - 1} duplicate ## Memory section(s) in CLAUDE.md`);
|
|
1103
|
+
return true;
|
|
1104
|
+
}
|
|
761
1105
|
|
|
762
1106
|
// src/commands/doctor/fixer-hooks.ts
|
|
763
1107
|
var FORMATTERS = {
|
|
@@ -814,8 +1158,8 @@ async function addSessionStartHook(root) {
|
|
|
814
1158
|
}
|
|
815
1159
|
|
|
816
1160
|
// src/commands/doctor/fixer-memory.ts
|
|
817
|
-
import { readFile as
|
|
818
|
-
import { join as
|
|
1161
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1162
|
+
import { join as join5 } from "path";
|
|
819
1163
|
async function addPlacementHook(root, placement, event, dedupKeyword, entry, prepend, successMsg) {
|
|
820
1164
|
const read = placement === "local" ? readSettingsLocalJson : readSettingsJson;
|
|
821
1165
|
const write = placement === "local" ? writeSettingsLocalJson : writeSettingsJson;
|
|
@@ -969,9 +1313,9 @@ async function addAllowedMcpServers(root, placement) {
|
|
|
969
1313
|
if (settingsServers && typeof settingsServers === "object") {
|
|
970
1314
|
for (const name of Object.keys(settingsServers)) serverNames.add(name);
|
|
971
1315
|
}
|
|
972
|
-
const mcpJsonPath =
|
|
1316
|
+
const mcpJsonPath = join5(root, ".mcp.json");
|
|
973
1317
|
try {
|
|
974
|
-
const mcpJson = JSON.parse(await
|
|
1318
|
+
const mcpJson = JSON.parse(await readFile3(mcpJsonPath, "utf-8"));
|
|
975
1319
|
const mcpServers = mcpJson.mcpServers;
|
|
976
1320
|
if (mcpServers && typeof mcpServers === "object") {
|
|
977
1321
|
for (const name of Object.keys(mcpServers)) serverNames.add(name);
|
|
@@ -1032,8 +1376,10 @@ var FIX_TABLE = [
|
|
|
1032
1376
|
{ analyzer: "Quality", match: "Session Start", fix: (root) => addClaudeMdSection(root, "## Session Start", wrapStub(SESSION_START_CONTENT)) },
|
|
1033
1377
|
{ analyzer: "Quality", match: "Backlog", fix: (root) => addClaudeMdSection(root, "## Backlog", wrapStub(BACKLOG_CONTENT)) },
|
|
1034
1378
|
{ analyzer: "Quality", match: "Stop-and-Swarm", fix: (root) => addClaudeMdSection(root, "## Stop-and-Swarm", wrapStub(STOP_AND_SWARM_CONTENT)) },
|
|
1379
|
+
{ analyzer: "Quality", match: "Duplicate ## Memory", fix: (root) => collapseMemoryHeadings(root) },
|
|
1035
1380
|
{ analyzer: "Rules", match: "No BACKLOG.md", fix: (root) => createBacklogMd(root) },
|
|
1036
1381
|
{ analyzer: "Rules", match: "No .claudeignore", fix: (root, detected) => createClaudeignore(root, detected) },
|
|
1382
|
+
{ analyzer: "Rules", match: "No .claude/rules/workflow.md", fix: (root) => createWorkflowRule(root) },
|
|
1037
1383
|
{ analyzer: "Rules", match: "No .claude/rules/", fix: (root) => createStarterRules(root) },
|
|
1038
1384
|
{ analyzer: "Hooks", match: "PostCompact", fix: (root) => addPostCompactHook(root) },
|
|
1039
1385
|
{ analyzer: "Permissions", match: "force-push", fix: (root) => addForcePushProtection(root) },
|
|
@@ -1045,6 +1391,7 @@ var FIX_TABLE = [
|
|
|
1045
1391
|
{ analyzer: "Hooks", match: "sprint-size-check", fix: (root) => addSprintSizeHook(root) },
|
|
1046
1392
|
{ analyzer: "Hooks", match: "sprint-open-check", fix: (root) => addSprintOpenHook(root) },
|
|
1047
1393
|
{ analyzer: "Hooks", match: "sprint-complete nudge", fix: (root) => addSprintCompleteNudge(root) },
|
|
1394
|
+
{ analyzer: "Hooks", match: "workflow-check.sh", fix: (root) => addWorkflowCheckHook(root) },
|
|
1048
1395
|
{ analyzer: "Rules", match: "No skill authoring conventions", fix: (root) => addSkillAuthoringConventions(root) },
|
|
1049
1396
|
{ analyzer: "Rules", match: "No /lp-enhance skill", fix: (root) => createEnhanceSkill(root) },
|
|
1050
1397
|
{ analyzer: "Rules", match: "lp-enhance skill is outdated", fix: (root) => updateEnhanceSkill(root) },
|
|
@@ -1060,7 +1407,7 @@ var FIX_TABLE = [
|
|
|
1060
1407
|
{ analyzer: "Memory", match: "SessionEnd push hook is not nohup-wrapped", fix: (root) => upgradeStaleSessionEndPushHook(root) },
|
|
1061
1408
|
{ analyzer: "Memory", match: "CLAUDE.md missing memory guidance", fix: (root, _det, placement) => {
|
|
1062
1409
|
const content = "Use agentic-memory to persist knowledge across sessions:\n- Memories are automatically injected at session start\n- STORE IMMEDIATELY when: a dependency strategy changes, an architecture decision is made, a convention is established, a bug pattern is discovered, or a feature is killed/added\n- Use memory_search before memory_store to check for duplicates\n- NEVER store credentials, API keys, tokens, or secrets in memories";
|
|
1063
|
-
const target = placement === "local" ?
|
|
1410
|
+
const target = placement === "local" ? join6(root, ".claude", "CLAUDE.md") : void 0;
|
|
1064
1411
|
return addClaudeMdSection(root, "## Memory", wrapStub(content), target);
|
|
1065
1412
|
} }
|
|
1066
1413
|
];
|
|
@@ -1117,27 +1464,27 @@ async function removeSandboxSettings(root) {
|
|
|
1117
1464
|
return true;
|
|
1118
1465
|
}
|
|
1119
1466
|
async function addEnvToClaudeignore(root) {
|
|
1120
|
-
const ignorePath =
|
|
1467
|
+
const ignorePath = join6(root, ".claudeignore");
|
|
1121
1468
|
let content;
|
|
1122
1469
|
try {
|
|
1123
|
-
content = await
|
|
1470
|
+
content = await readFile4(ignorePath, "utf-8");
|
|
1124
1471
|
} catch {
|
|
1125
1472
|
return false;
|
|
1126
1473
|
}
|
|
1127
1474
|
const lines = content.split("\n").map((l) => l.trim());
|
|
1128
1475
|
if (lines.some((l) => l === ".env" || l === ".env.*" || l === ".env*")) return false;
|
|
1129
|
-
await
|
|
1476
|
+
await writeFile4(ignorePath, content.trimEnd() + "\n.env\n.env.*\n");
|
|
1130
1477
|
log.success("Added .env to .claudeignore");
|
|
1131
1478
|
return true;
|
|
1132
1479
|
}
|
|
1133
1480
|
async function addClaudeMdSection(root, heading, content, targetPath) {
|
|
1134
|
-
const claudeMdPath = targetPath ??
|
|
1481
|
+
const claudeMdPath = targetPath ?? join6(root, "CLAUDE.md");
|
|
1135
1482
|
let existing;
|
|
1136
1483
|
try {
|
|
1137
|
-
existing = await
|
|
1484
|
+
existing = await readFile4(claudeMdPath, "utf-8");
|
|
1138
1485
|
} catch {
|
|
1139
1486
|
if (!targetPath) return false;
|
|
1140
|
-
await
|
|
1487
|
+
await mkdir3(join6(root, ".claude"), { recursive: true });
|
|
1141
1488
|
existing = "# Local Claude Config\n";
|
|
1142
1489
|
}
|
|
1143
1490
|
if (existing.includes(heading)) return false;
|
|
@@ -1149,36 +1496,32 @@ ${content}
|
|
|
1149
1496
|
|
|
1150
1497
|
`;
|
|
1151
1498
|
const updated = existing.slice(0, insertAt) + section + existing.slice(insertAt);
|
|
1152
|
-
await
|
|
1499
|
+
await writeFile4(claudeMdPath, updated);
|
|
1153
1500
|
const label = targetPath ? ".claude/CLAUDE.md" : "CLAUDE.md";
|
|
1154
1501
|
log.success(`Added "${heading}" section to ${label}`);
|
|
1155
1502
|
return true;
|
|
1156
1503
|
}
|
|
1157
1504
|
async function createBacklogMd(root) {
|
|
1158
|
-
const backlogPath =
|
|
1505
|
+
const backlogPath = join6(root, "BACKLOG.md");
|
|
1159
1506
|
try {
|
|
1160
1507
|
await access2(backlogPath);
|
|
1161
1508
|
return false;
|
|
1162
1509
|
} catch {
|
|
1163
1510
|
}
|
|
1164
1511
|
const name = root.split("/").pop() ?? "Project";
|
|
1165
|
-
await
|
|
1166
|
-
|
|
1167
|
-
> Features discussed but deferred. Pick up when relevant.
|
|
1168
|
-
> Priority: P0 = next sprint, P1 = soon, P2 = when relevant.
|
|
1169
|
-
`);
|
|
1170
|
-
log.success("Generated BACKLOG.md");
|
|
1512
|
+
await writeFile4(backlogPath, generateBacklogMd({ name, description: "" }));
|
|
1513
|
+
log.success("Generated BACKLOG.md (WP template + P0\u2013P3 sections + changelog)");
|
|
1171
1514
|
return true;
|
|
1172
1515
|
}
|
|
1173
1516
|
async function createClaudeignore(root, detected) {
|
|
1174
|
-
const ignorePath =
|
|
1517
|
+
const ignorePath = join6(root, ".claudeignore");
|
|
1175
1518
|
try {
|
|
1176
1519
|
await access2(ignorePath);
|
|
1177
1520
|
return false;
|
|
1178
1521
|
} catch {
|
|
1179
1522
|
}
|
|
1180
1523
|
const content = generateClaudeignore(detected);
|
|
1181
|
-
await
|
|
1524
|
+
await writeFile4(ignorePath, content);
|
|
1182
1525
|
log.success("Generated .claudeignore with language-specific ignore patterns");
|
|
1183
1526
|
return true;
|
|
1184
1527
|
}
|
|
@@ -1188,15 +1531,15 @@ var SKILL_AUTHORING_SECTION = `
|
|
|
1188
1531
|
${SKILL_AUTHORING_CONTENT}
|
|
1189
1532
|
`;
|
|
1190
1533
|
async function createStarterRules(root) {
|
|
1191
|
-
const rulesDir =
|
|
1534
|
+
const rulesDir = join6(root, ".claude", "rules");
|
|
1192
1535
|
try {
|
|
1193
1536
|
await access2(rulesDir);
|
|
1194
1537
|
return false;
|
|
1195
1538
|
} catch {
|
|
1196
1539
|
}
|
|
1197
|
-
await
|
|
1198
|
-
await
|
|
1199
|
-
|
|
1540
|
+
await mkdir3(rulesDir, { recursive: true });
|
|
1541
|
+
await writeFile4(
|
|
1542
|
+
join6(rulesDir, "conventions.md"),
|
|
1200
1543
|
`# Project Conventions
|
|
1201
1544
|
|
|
1202
1545
|
- Use conventional commits (feat:, fix:, docs:, refactor:, test:, chore:)
|
|
@@ -1209,36 +1552,36 @@ ${SKILL_AUTHORING_SECTION}`
|
|
|
1209
1552
|
return true;
|
|
1210
1553
|
}
|
|
1211
1554
|
async function addSkillAuthoringConventions(root) {
|
|
1212
|
-
const conventionsPath =
|
|
1555
|
+
const conventionsPath = join6(root, ".claude", "rules", "conventions.md");
|
|
1213
1556
|
let content;
|
|
1214
1557
|
try {
|
|
1215
|
-
content = await
|
|
1558
|
+
content = await readFile4(conventionsPath, "utf-8");
|
|
1216
1559
|
} catch {
|
|
1217
1560
|
return false;
|
|
1218
1561
|
}
|
|
1219
1562
|
if (/^##\s+Skill\s+Authoring/im.test(content)) return false;
|
|
1220
|
-
await
|
|
1563
|
+
await writeFile4(conventionsPath, content.trimEnd() + "\n" + SKILL_AUTHORING_SECTION);
|
|
1221
1564
|
log.success("Added Skill Authoring section to .claude/rules/conventions.md");
|
|
1222
1565
|
return true;
|
|
1223
1566
|
}
|
|
1224
1567
|
async function createEnhanceSkill(root) {
|
|
1225
|
-
const skillDir =
|
|
1226
|
-
const skillPath =
|
|
1227
|
-
const globalPath =
|
|
1228
|
-
const legacyProject =
|
|
1229
|
-
const legacyGlobal =
|
|
1568
|
+
const skillDir = join6(root, ".claude", "skills", "lp-enhance");
|
|
1569
|
+
const skillPath = join6(skillDir, "SKILL.md");
|
|
1570
|
+
const globalPath = join6(homedir(), ".claude", "skills", "lp-enhance", "SKILL.md");
|
|
1571
|
+
const legacyProject = join6(root, ".claude", "commands", "lp-enhance.md");
|
|
1572
|
+
const legacyGlobal = join6(homedir(), ".claude", "commands", "lp-enhance.md");
|
|
1230
1573
|
if (await fileExists(skillPath) || await fileExists(globalPath) || await fileExists(legacyProject) || await fileExists(legacyGlobal)) return false;
|
|
1231
|
-
await
|
|
1232
|
-
await
|
|
1574
|
+
await mkdir3(skillDir, { recursive: true });
|
|
1575
|
+
await writeFile4(skillPath, generateEnhanceSkill());
|
|
1233
1576
|
log.success("Generated /lp-enhance skill (.claude/skills/lp-enhance/)");
|
|
1234
1577
|
return true;
|
|
1235
1578
|
}
|
|
1236
1579
|
async function updateEnhanceSkill(root) {
|
|
1237
|
-
const projectPath =
|
|
1238
|
-
const globalPath =
|
|
1580
|
+
const projectPath = join6(root, ".claude", "skills", "lp-enhance", "SKILL.md");
|
|
1581
|
+
const globalPath = join6(homedir(), ".claude", "skills", "lp-enhance", "SKILL.md");
|
|
1239
1582
|
const targetPath = await fileExists(projectPath) ? projectPath : await fileExists(globalPath) ? globalPath : null;
|
|
1240
1583
|
if (!targetPath) return false;
|
|
1241
|
-
await
|
|
1584
|
+
await writeFile4(targetPath, generateEnhanceSkill());
|
|
1242
1585
|
log.success("Updated /lp-enhance skill to latest version");
|
|
1243
1586
|
return true;
|
|
1244
1587
|
}
|
|
@@ -1349,7 +1692,7 @@ function renderDoctorReport(results, options) {
|
|
|
1349
1692
|
async function readJsonFile(path) {
|
|
1350
1693
|
let raw;
|
|
1351
1694
|
try {
|
|
1352
|
-
raw = await
|
|
1695
|
+
raw = await readFile5(path, "utf-8");
|
|
1353
1696
|
} catch (err) {
|
|
1354
1697
|
const code = err.code;
|
|
1355
1698
|
if (code === "ENOENT") return {};
|
|
@@ -1364,20 +1707,20 @@ async function readJsonFile(path) {
|
|
|
1364
1707
|
}
|
|
1365
1708
|
}
|
|
1366
1709
|
async function readSettingsJson(root) {
|
|
1367
|
-
return readJsonFile(
|
|
1710
|
+
return readJsonFile(join7(root, ".claude", "settings.json"));
|
|
1368
1711
|
}
|
|
1369
1712
|
async function writeSettingsJson(root, settings) {
|
|
1370
|
-
const dir =
|
|
1371
|
-
await
|
|
1372
|
-
await
|
|
1713
|
+
const dir = join7(root, ".claude");
|
|
1714
|
+
await mkdir4(dir, { recursive: true });
|
|
1715
|
+
await writeFile5(join7(dir, "settings.json"), JSON.stringify(settings, null, 2) + "\n");
|
|
1373
1716
|
}
|
|
1374
1717
|
async function readSettingsLocalJson(root) {
|
|
1375
|
-
return readJsonFile(
|
|
1718
|
+
return readJsonFile(join7(root, ".claude", "settings.local.json"));
|
|
1376
1719
|
}
|
|
1377
1720
|
async function writeSettingsLocalJson(root, settings) {
|
|
1378
|
-
const dir =
|
|
1379
|
-
await
|
|
1380
|
-
await
|
|
1721
|
+
const dir = join7(root, ".claude");
|
|
1722
|
+
await mkdir4(dir, { recursive: true });
|
|
1723
|
+
await writeFile5(join7(dir, "settings.local.json"), JSON.stringify(settings, null, 2) + "\n");
|
|
1381
1724
|
}
|
|
1382
1725
|
|
|
1383
1726
|
export {
|
|
@@ -1393,6 +1736,7 @@ export {
|
|
|
1393
1736
|
generateClaudeignore,
|
|
1394
1737
|
ENHANCE_SKILL_VERSION,
|
|
1395
1738
|
generateEnhanceSkill,
|
|
1739
|
+
generateBacklogMd,
|
|
1396
1740
|
readSettingsJson,
|
|
1397
1741
|
writeSettingsJson,
|
|
1398
1742
|
readSettingsLocalJson,
|
|
@@ -1401,10 +1745,12 @@ export {
|
|
|
1401
1745
|
LP_STUB_OPEN,
|
|
1402
1746
|
addOrUpdateHook,
|
|
1403
1747
|
writeSprintHygieneScripts,
|
|
1748
|
+
writeWorkflowCheckScript,
|
|
1749
|
+
generateWorkflowRule,
|
|
1404
1750
|
applyFixes,
|
|
1405
1751
|
log,
|
|
1406
1752
|
printBanner,
|
|
1407
1753
|
printScoreCard,
|
|
1408
1754
|
renderDoctorReport
|
|
1409
1755
|
};
|
|
1410
|
-
//# sourceMappingURL=chunk-
|
|
1756
|
+
//# sourceMappingURL=chunk-5KQ2JDZN.js.map
|