@mhd-ghaith-abtah/flow 0.7.2-beta.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/CHANGELOG.md +87 -0
- package/LICENSE +21 -0
- package/README.md +162 -0
- package/adapters/e2e/_interface.md +42 -0
- package/adapters/e2e/none.md +15 -0
- package/adapters/e2e/playwright-mcp.md +86 -0
- package/adapters/issue-tracker/_interface.md +46 -0
- package/adapters/issue-tracker/github-issues.md +103 -0
- package/adapters/issue-tracker/linear.md +65 -0
- package/adapters/issue-tracker/none.md +26 -0
- package/adapters/pr/_interface.md +29 -0
- package/adapters/pr/github.md +61 -0
- package/adapters/pr/none.md +32 -0
- package/adapters/verify/_interface.md +26 -0
- package/adapters/verify/custom.md +27 -0
- package/adapters/verify/make.md +30 -0
- package/adapters/verify/pnpm.md +27 -0
- package/bin/flow.js +129 -0
- package/catalog.yaml +364 -0
- package/docs/adapters.md +99 -0
- package/docs/migrate-from-bmad.md +95 -0
- package/docs/profiles.md +81 -0
- package/docs/quickstart.md +82 -0
- package/lib/catalog.js +164 -0
- package/lib/commands/add.js +147 -0
- package/lib/commands/doctor.js +392 -0
- package/lib/commands/init.js +86 -0
- package/lib/commands/install.js +181 -0
- package/lib/commands/plan.js +108 -0
- package/lib/commands/remove.js +87 -0
- package/lib/commands/uninstall.js +157 -0
- package/lib/repo-root.js +53 -0
- package/package.json +62 -0
- package/schemas/catalog.schema.json +155 -0
- package/schemas/flow-config.schema.json +85 -0
- package/schemas/install-state.schema.json +79 -0
- package/skills/flow-doctor/SKILL.md +15 -0
- package/skills/flow-doctor/workflow.md +157 -0
- package/skills/flow-init/SKILL.md +10 -0
- package/skills/flow-init/workflow.md +420 -0
- package/skills/flow-sprint/SKILL.md +10 -0
- package/skills/flow-sprint/workflow.md +394 -0
- package/skills/flow-story/SKILL.md +12 -0
- package/skills/flow-story/workflow.md +531 -0
- package/templates/claude-md-section.md.tmpl +55 -0
- package/templates/flow-readme.md.tmpl +34 -0
- package/templates/flow.config.yaml.tmpl +94 -0
- package/templates/pr.md.tmpl +40 -0
- package/templates/retro.md.tmpl +58 -0
- package/templates/sprint.yaml.tmpl +18 -0
- package/templates/story.md.tmpl +35 -0
- package/tools/fix-caveman-shrink.sh +68 -0
- package/tools/lint-changelog.js +98 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Flow project configuration — generated by /flow-init.
|
|
2
|
+
# Re-run /flow-init --update to regenerate. Hand-edit when needed.
|
|
3
|
+
#
|
|
4
|
+
# This file is intended to be **committed** — it captures team-wide conventions
|
|
5
|
+
# (adapter choices, review policy, verify command, etc) with no secrets. Per-
|
|
6
|
+
# developer overrides live in `flow.config.local.yaml` (gitignored) which Flow
|
|
7
|
+
# merges on top of this one when both are present. Secrets live in
|
|
8
|
+
# `~/.claude/.env.flow` (chmod 600, never committed).
|
|
9
|
+
|
|
10
|
+
version: 1
|
|
11
|
+
generated: {{date}}
|
|
12
|
+
flow_version: {{flow_version}}
|
|
13
|
+
|
|
14
|
+
# Mode controls how much ceremony flow-story applies per story.
|
|
15
|
+
mode: {{mode}} # mini | standard | team
|
|
16
|
+
|
|
17
|
+
# File paths (relative to project root)
|
|
18
|
+
sprint_file: docs/flow/sprint.yaml
|
|
19
|
+
stories_dir: docs/flow/stories
|
|
20
|
+
journeys_dir: docs/flow/journeys
|
|
21
|
+
retros_dir: docs/flow/retros
|
|
22
|
+
archive_dir: docs/flow/archive
|
|
23
|
+
deferred_file: docs/flow/deferred.md
|
|
24
|
+
artifacts_dir: docs/flow/artifacts
|
|
25
|
+
|
|
26
|
+
# Active adapters — pick one per category. Swap with /flow adapter swap.
|
|
27
|
+
adapters:
|
|
28
|
+
issue_tracker: {{adapters.issue_tracker}} # linear | github-issues | none | ...
|
|
29
|
+
pr: {{adapters.pr}} # github | gitlab | none | ...
|
|
30
|
+
e2e: {{adapters.e2e}} # playwright-mcp | playwright-cli | none | ...
|
|
31
|
+
verify: {{adapters.verify}} # make | pnpm | custom | ...
|
|
32
|
+
|
|
33
|
+
# Adapter config (consumed by the active adapters above)
|
|
34
|
+
integrations:
|
|
35
|
+
{{#if adapters.issue_tracker == 'github-issues'}}
|
|
36
|
+
issue_tracker:
|
|
37
|
+
repo: {{integrations.issue_tracker.repo}}
|
|
38
|
+
label_prefix: flow
|
|
39
|
+
{{/if}}
|
|
40
|
+
{{#if adapters.issue_tracker == 'linear'}}
|
|
41
|
+
issue_tracker:
|
|
42
|
+
team_key: {{integrations.issue_tracker.team_key}}
|
|
43
|
+
{{/if}}
|
|
44
|
+
{{#if adapters.e2e == 'playwright-mcp'}}
|
|
45
|
+
e2e:
|
|
46
|
+
base_url: {{integrations.e2e.base_url}}
|
|
47
|
+
journeys_dir: docs/flow/journeys
|
|
48
|
+
viewport: { width: 1280, height: 720 }
|
|
49
|
+
{{/if}}
|
|
50
|
+
{{#if adapters.verify == 'pnpm'}}
|
|
51
|
+
verify:
|
|
52
|
+
script: {{integrations.verify.script}}
|
|
53
|
+
{{/if}}
|
|
54
|
+
{{#if adapters.verify == 'custom'}}
|
|
55
|
+
verify:
|
|
56
|
+
command: "{{integrations.verify.command}}"
|
|
57
|
+
{{/if}}
|
|
58
|
+
|
|
59
|
+
# Review behaviour
|
|
60
|
+
review:
|
|
61
|
+
use_separate_model: {{review.use_separate_model}} # team mode → true; spawns reviewer agent with model override
|
|
62
|
+
auto_hard_review_tags: [auth, payments, migration, pii, security]
|
|
63
|
+
language_reviewer: {{review.language_reviewer}} # e.g. typescript-reviewer | python-reviewer | null
|
|
64
|
+
barrier_timeout_seconds: 900 # max wall-clock to wait on background reviewers before halting (default 15 min)
|
|
65
|
+
|
|
66
|
+
# PR behaviour
|
|
67
|
+
pr:
|
|
68
|
+
auto_merge_wait_seconds: 90 # single bounded wait under --auto-merge; CI longer than this → end turn + handoff
|
|
69
|
+
|
|
70
|
+
# Implementation behaviour
|
|
71
|
+
implement:
|
|
72
|
+
default: prp-implement # or 'direct' (no /prp-implement wrapper)
|
|
73
|
+
use_tdd: {{implement.use_tdd}} # invoke /tdd before /prp-implement
|
|
74
|
+
e2e_auto_trigger_tags: [ui, e2e, marketing, navigation]
|
|
75
|
+
docs_auto_trigger: {{implement.docs_auto_trigger}} # always run /update-docs in commit phase
|
|
76
|
+
|
|
77
|
+
# Secrets storage (used by env-var-auth MCPs)
|
|
78
|
+
secrets:
|
|
79
|
+
store: {{secrets.store}} # env_file | shell | onepassword
|
|
80
|
+
|
|
81
|
+
# Reference docs (Flow does NOT regenerate these — just paths for context)
|
|
82
|
+
reference_docs:
|
|
83
|
+
{{#each reference_docs}}
|
|
84
|
+
- {{this}}
|
|
85
|
+
{{/each}}
|
|
86
|
+
|
|
87
|
+
# Upstream installs (recorded for repair / update)
|
|
88
|
+
upstreams:
|
|
89
|
+
bmad:
|
|
90
|
+
installed: {{upstreams.bmad.installed}}
|
|
91
|
+
subset: {{upstreams.bmad.subset}}
|
|
92
|
+
ecc:
|
|
93
|
+
installed: {{upstreams.ecc.installed}}
|
|
94
|
+
subset: {{upstreams.ecc.subset}}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
{{summary}}
|
|
4
|
+
|
|
5
|
+
{{issue_ref}}
|
|
6
|
+
|
|
7
|
+
## What changed
|
|
8
|
+
|
|
9
|
+
{{#each changes}}
|
|
10
|
+
- {{this}}
|
|
11
|
+
{{/each}}
|
|
12
|
+
|
|
13
|
+
## Test plan
|
|
14
|
+
|
|
15
|
+
{{#each acceptance_criteria}}
|
|
16
|
+
- [ ] {{this}}
|
|
17
|
+
{{/each}}
|
|
18
|
+
|
|
19
|
+
{{#if has_e2e}}
|
|
20
|
+
## E2E
|
|
21
|
+
|
|
22
|
+
Journey ran against {{base_url}}. Artifacts at `docs/flow/artifacts/{{story_id}}/`.
|
|
23
|
+
|
|
24
|
+
{{#each e2e_passed}}
|
|
25
|
+
- ✓ {{this}}
|
|
26
|
+
{{/each}}
|
|
27
|
+
{{/if}}
|
|
28
|
+
|
|
29
|
+
{{#if has_verify}}
|
|
30
|
+
## Verify
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
$ {{verify_cmd}}
|
|
34
|
+
{{verify_summary}}
|
|
35
|
+
```
|
|
36
|
+
{{/if}}
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
🌊 Created via [Flow](https://github.com/mhd-ghaith-abtah/flow) · story `{{story_id}}`
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# {{epic_id}} — {{epic_title}} — Retro
|
|
2
|
+
|
|
3
|
+
*Generated by /flow-sprint retro on {{date}}*
|
|
4
|
+
|
|
5
|
+
## Shipped
|
|
6
|
+
|
|
7
|
+
- **{{shipped_count}} stories**, {{commit_count}} commits, {{pr_count}} PRs
|
|
8
|
+
- **Cycle time:** {{avg_cycle_days}} days/story (median {{median_cycle_days}})
|
|
9
|
+
- **Date range:** {{date_start}} → {{date_end}}
|
|
10
|
+
|
|
11
|
+
### Stories
|
|
12
|
+
|
|
13
|
+
{{#each stories}}
|
|
14
|
+
- ✓ {{this.id}} {{this.title}} ({{this.cycle_days}}d)
|
|
15
|
+
{{/each}}
|
|
16
|
+
|
|
17
|
+
## What worked
|
|
18
|
+
|
|
19
|
+
<!-- 2-4 bullets. Concrete, not platitudes. Examples:
|
|
20
|
+
- Tokens-first approach kept components consistent (E1-002, E1-003 reused tokens cleanly)
|
|
21
|
+
- Playwright MCP smoke test caught a console error in E1-002 (PR #14)
|
|
22
|
+
-->
|
|
23
|
+
|
|
24
|
+
-
|
|
25
|
+
|
|
26
|
+
## What didn't
|
|
27
|
+
|
|
28
|
+
<!-- 1-3 bullets. Specific cases, not generalities. Examples:
|
|
29
|
+
- E1-004 theme switcher hit a Tailwind v4 SSR issue mid-implement; scope expanded — should have planned with /plan first
|
|
30
|
+
- Two deferred items still open (see deferred.md): X, Y
|
|
31
|
+
-->
|
|
32
|
+
|
|
33
|
+
-
|
|
34
|
+
|
|
35
|
+
## Carry into next epic
|
|
36
|
+
|
|
37
|
+
<!-- Adjustments to make next time. Examples:
|
|
38
|
+
- Always use /plan for stories tagged 'client-side'
|
|
39
|
+
- Add a `client_side: true` story field that triggers --plan auto
|
|
40
|
+
-->
|
|
41
|
+
|
|
42
|
+
-
|
|
43
|
+
|
|
44
|
+
## Open deferred items at epic close
|
|
45
|
+
|
|
46
|
+
{{#if open_deferred_count > 0}}
|
|
47
|
+
{{#each open_deferred}}
|
|
48
|
+
- {{this}}
|
|
49
|
+
{{/each}}
|
|
50
|
+
{{else}}
|
|
51
|
+
*(none)*
|
|
52
|
+
{{/if}}
|
|
53
|
+
|
|
54
|
+
## Next
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
/flow-sprint add "<title>" --epic E{{next_epic_num}} --tags <list>
|
|
58
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Flow sprint state — single source of truth for story tracking.
|
|
2
|
+
# Edit via /flow-sprint subcommands rather than by hand when possible.
|
|
3
|
+
|
|
4
|
+
version: 1
|
|
5
|
+
project: {{project_name}}
|
|
6
|
+
generated: {{date}}
|
|
7
|
+
last_updated: {{timestamp}}
|
|
8
|
+
|
|
9
|
+
# STATUS LEGEND
|
|
10
|
+
# backlog — not started
|
|
11
|
+
# doing — branch checked out, work in progress
|
|
12
|
+
# review — PR open, awaiting merge
|
|
13
|
+
# done — PR merged, story archived
|
|
14
|
+
# cancelled — abandoned (see story file Notes for reason)
|
|
15
|
+
|
|
16
|
+
epics: []
|
|
17
|
+
|
|
18
|
+
stories: []
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# {{story_id}} — {{title}}
|
|
2
|
+
|
|
3
|
+
**Epic:** {{epic_id}} — {{epic_title}}
|
|
4
|
+
{{#if issue}}**Issue:** {{issue}}{{/if}}
|
|
5
|
+
**Tags:** {{tags_csv}}
|
|
6
|
+
**Status:** {{status}}
|
|
7
|
+
|
|
8
|
+
**Why:** {{why}}
|
|
9
|
+
|
|
10
|
+
{{#if refs}}
|
|
11
|
+
**Refs:**
|
|
12
|
+
{{#each refs}}
|
|
13
|
+
- {{this}}
|
|
14
|
+
{{/each}}
|
|
15
|
+
{{/if}}
|
|
16
|
+
|
|
17
|
+
## Acceptance
|
|
18
|
+
{{#each acceptance_criteria}}
|
|
19
|
+
- [ ] {{this}}
|
|
20
|
+
{{/each}}
|
|
21
|
+
|
|
22
|
+
## Files
|
|
23
|
+
{{#each files}}
|
|
24
|
+
- {{this}}
|
|
25
|
+
{{/each}}
|
|
26
|
+
|
|
27
|
+
{{#if has_e2e}}
|
|
28
|
+
## E2E Journey
|
|
29
|
+
- navigate /
|
|
30
|
+
- assert visible "{{expected_heading}}"
|
|
31
|
+
- assert no console errors
|
|
32
|
+
{{/if}}
|
|
33
|
+
|
|
34
|
+
## Notes
|
|
35
|
+
<!-- Decisions, deferred items, follow-ups appear here during implementation. -->
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# tools/fix-caveman-shrink.sh — print the commands needed to fix a broken
|
|
3
|
+
# caveman-shrink MCP registration.
|
|
4
|
+
#
|
|
5
|
+
# Background: caveman-shrink is an MCP *proxy* — it must wrap an upstream MCP
|
|
6
|
+
# server (passed as extra command-line args). The Caveman installer sometimes
|
|
7
|
+
# registers it standalone (no upstream args), which crashes the proxy on first
|
|
8
|
+
# call with the error "-32000: server failed to start" because index.js exits
|
|
9
|
+
# immediately at the "missing upstream command" guard.
|
|
10
|
+
#
|
|
11
|
+
# This script does NOT execute `claude mcp` for you — modifying MCP
|
|
12
|
+
# registration affects every Claude Code session, so we print the exact
|
|
13
|
+
# commands and let you run them yourself.
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# tools/fix-caveman-shrink.sh # wraps context7 (default)
|
|
17
|
+
# tools/fix-caveman-shrink.sh playwright # wrap a known preset
|
|
18
|
+
# tools/fix-caveman-shrink.sh 'npx -y @org/x' # pass full upstream command
|
|
19
|
+
|
|
20
|
+
set -euo pipefail
|
|
21
|
+
|
|
22
|
+
UPSTREAM="${1:-context7}"
|
|
23
|
+
|
|
24
|
+
# Bash 3.2 (macOS default) doesn't support associative arrays — use case.
|
|
25
|
+
case "$UPSTREAM" in
|
|
26
|
+
context7)
|
|
27
|
+
UPSTREAM_CMD="npx -y @upstash/context7-mcp@latest"
|
|
28
|
+
;;
|
|
29
|
+
playwright)
|
|
30
|
+
UPSTREAM_CMD="npx -y @playwright/mcp@latest"
|
|
31
|
+
;;
|
|
32
|
+
filesystem)
|
|
33
|
+
UPSTREAM_CMD="npx -y @modelcontextprotocol/server-filesystem $HOME"
|
|
34
|
+
;;
|
|
35
|
+
*)
|
|
36
|
+
# Treat as a literal upstream command.
|
|
37
|
+
UPSTREAM_CMD="$UPSTREAM"
|
|
38
|
+
UPSTREAM="(custom)"
|
|
39
|
+
;;
|
|
40
|
+
esac
|
|
41
|
+
|
|
42
|
+
cat <<EOF
|
|
43
|
+
━━━ fix-caveman-shrink ━━━
|
|
44
|
+
|
|
45
|
+
caveman-shrink is registered standalone (no upstream wrap) — it will crash with
|
|
46
|
+
\`-32000: server failed to start\` on the first call.
|
|
47
|
+
|
|
48
|
+
To fix, run these two commands yourself (they modify your Claude Code MCP
|
|
49
|
+
registration globally — review them before executing):
|
|
50
|
+
|
|
51
|
+
1. Remove the broken registration:
|
|
52
|
+
|
|
53
|
+
claude mcp remove caveman-shrink
|
|
54
|
+
|
|
55
|
+
2. Re-add it as a wrapper around '${UPSTREAM}' (${UPSTREAM_CMD}):
|
|
56
|
+
|
|
57
|
+
claude mcp add caveman-shrink -- npx caveman-shrink ${UPSTREAM_CMD}
|
|
58
|
+
|
|
59
|
+
Verify:
|
|
60
|
+
|
|
61
|
+
claude mcp get caveman-shrink
|
|
62
|
+
|
|
63
|
+
The fixed registration should show 'caveman-shrink' followed by the upstream
|
|
64
|
+
command. Restart Claude Code if MCP isn't picked up.
|
|
65
|
+
|
|
66
|
+
Known upstream presets: context7, playwright, filesystem.
|
|
67
|
+
Or pass the full upstream command as a single arg.
|
|
68
|
+
EOF
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// tools/lint-changelog.js — enforce CONTRIBUTING.md's "one-line entries" rule.
|
|
3
|
+
//
|
|
4
|
+
// CHANGELOG entries under ### Added / ### Changed / ### Fixed / ### Removed
|
|
5
|
+
// must each be a single line. A wrapped entry (a continuation line that
|
|
6
|
+
// doesn't start with `- `, `#`, or a blank line) is a violation. Excludes
|
|
7
|
+
// indented continuation under fenced code blocks and list-of-list bullets.
|
|
8
|
+
//
|
|
9
|
+
// Exit codes:
|
|
10
|
+
// 0 — all entries one-line
|
|
11
|
+
// 1 — at least one wrapped entry
|
|
12
|
+
// 2 — usage error / file not found
|
|
13
|
+
|
|
14
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
15
|
+
import { resolve } from 'node:path';
|
|
16
|
+
|
|
17
|
+
const path = process.argv[2];
|
|
18
|
+
if (!path) {
|
|
19
|
+
console.error('usage: lint-changelog.js <CHANGELOG.md>');
|
|
20
|
+
process.exit(2);
|
|
21
|
+
}
|
|
22
|
+
if (!existsSync(path)) {
|
|
23
|
+
console.error(`✗ File not found: ${path}`);
|
|
24
|
+
process.exit(2);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const text = readFileSync(resolve(path), 'utf-8');
|
|
28
|
+
const lines = text.split('\n');
|
|
29
|
+
|
|
30
|
+
let inEntrySection = false;
|
|
31
|
+
let inCodeBlock = false;
|
|
32
|
+
let currentEntryStart = -1; // line number of the most recent `- ` entry, or -1
|
|
33
|
+
const violations = [];
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < lines.length; i++) {
|
|
36
|
+
const line = lines[i];
|
|
37
|
+
|
|
38
|
+
// Track fenced code blocks — entries inside them are not subject to the rule.
|
|
39
|
+
if (line.trim().startsWith('```')) {
|
|
40
|
+
inCodeBlock = !inCodeBlock;
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (inCodeBlock) continue;
|
|
44
|
+
|
|
45
|
+
// Entry sections start with `### Added | Changed | Fixed | Removed | Deprecated | Security`.
|
|
46
|
+
const sectionMatch = line.match(/^###\s+(Added|Changed|Fixed|Removed|Deprecated|Security)\b/);
|
|
47
|
+
if (sectionMatch) {
|
|
48
|
+
inEntrySection = true;
|
|
49
|
+
currentEntryStart = -1;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Any other heading ends the entry section.
|
|
54
|
+
if (line.startsWith('#')) {
|
|
55
|
+
inEntrySection = false;
|
|
56
|
+
currentEntryStart = -1;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!inEntrySection) continue;
|
|
61
|
+
|
|
62
|
+
// Blank line ends the current entry (no continuation).
|
|
63
|
+
if (line.trim() === '') {
|
|
64
|
+
currentEntryStart = -1;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Entry line starts with `- ` (top-level bullet).
|
|
69
|
+
if (line.startsWith('- ')) {
|
|
70
|
+
currentEntryStart = i + 1; // 1-indexed
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Indented sub-bullet or literal block — allowed for nested lists.
|
|
75
|
+
if (line.startsWith(' ') || line.startsWith('\t')) {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Otherwise it's wrapped prose (starts at column 0, follows a `- ` entry).
|
|
80
|
+
if (currentEntryStart > 0) {
|
|
81
|
+
violations.push({ entryLine: currentEntryStart, wrapLine: i + 1, sample: line.slice(0, 80) });
|
|
82
|
+
currentEntryStart = -1; // only flag once per entry
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (violations.length === 0) {
|
|
87
|
+
console.log(`✓ CHANGELOG entries are one-line (${path})`);
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
console.error(`✗ Found ${violations.length} wrapped CHANGELOG entr${violations.length === 1 ? 'y' : 'ies'} in ${path}:`);
|
|
92
|
+
for (const v of violations) {
|
|
93
|
+
console.error(` Line ${v.wrapLine} continues the entry that started at line ${v.entryLine}:`);
|
|
94
|
+
console.error(` "${v.sample}${v.sample.length === 80 ? '…' : ''}"`);
|
|
95
|
+
}
|
|
96
|
+
console.error('');
|
|
97
|
+
console.error('CONTRIBUTING.md requires one-line entries. Move detail into a referenced doc or split into multiple entries.');
|
|
98
|
+
process.exit(1);
|