create-dokio 0.1.9 → 0.1.12
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 +109 -47
- package/dist/index.js +273 -7
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# create-dokio
|
|
2
2
|
|
|
3
|
-
CLI scaffold for Dokio templates. Generates
|
|
3
|
+
CLI scaffold for Dokio hub repos and templates. Generates correct file structure, `data.yaml`, HTML boilerplate, SCSS partials, and auto-wires changelog tooling.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
@@ -14,6 +14,77 @@ npx create-dokio pool-care
|
|
|
14
14
|
|
|
15
15
|
> Using pnpm? `pnpm dlx create-dokio`
|
|
16
16
|
|
|
17
|
+
Run from your **repos workspace folder** — the folder where all your hub repos live (e.g. `~/Projects/dokio/`).
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Commands
|
|
22
|
+
|
|
23
|
+
### `create-dokio hub`
|
|
24
|
+
|
|
25
|
+
Creates a new hub repository locally with full structure ready to push.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx create-dokio hub
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Prompts:**
|
|
32
|
+
- Hub ID (kebab-case, e.g. `bupa-sam`)
|
|
33
|
+
- Hub display name (e.g. `Bupa Sales And Marketing Hub`)
|
|
34
|
+
|
|
35
|
+
**Generates:**
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
bupa-sam-templates/
|
|
39
|
+
├── .githooks/
|
|
40
|
+
│ ├── commit-msg # Enforces conventional commits
|
|
41
|
+
│ └── post-commit # Auto-updates template CHANGELOGs on commit
|
|
42
|
+
├── .vscode/
|
|
43
|
+
│ └── settings.json # scss.validate: false, css.validate: false
|
|
44
|
+
├── templates/ # All templates live here
|
|
45
|
+
├── tools/
|
|
46
|
+
│ └── changelog/
|
|
47
|
+
│ ├── update_changelog.py # Changelog generator script
|
|
48
|
+
│ └── .env.example # ANTHROPIC_API_KEY for AI descriptions
|
|
49
|
+
├── .gitignore
|
|
50
|
+
└── README.md
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Next steps after hub creation:**
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
cd bupa-sam-templates
|
|
57
|
+
# Create GitHub repo at github.com/dokioco/bupa-sam-templates
|
|
58
|
+
git remote add origin https://github.com/dokioco/bupa-sam-templates
|
|
59
|
+
git push -u origin main
|
|
60
|
+
|
|
61
|
+
# Optional — enable AI changelog descriptions:
|
|
62
|
+
cp tools/changelog/.env.example tools/changelog/.env
|
|
63
|
+
# Add your ANTHROPIC_API_KEY to tools/changelog/.env
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### `create-dokio template`
|
|
69
|
+
|
|
70
|
+
Scaffolds a new template inside an existing hub repo. Clones the hub if not found locally.
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
npx create-dokio template
|
|
74
|
+
# or just:
|
|
75
|
+
npx create-dokio
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Prompts:**
|
|
79
|
+
- Template ID (e.g. `HW485`)
|
|
80
|
+
- Template name (e.g. `My Cool Template`)
|
|
81
|
+
- Template type (PDF / General / Email / Video)
|
|
82
|
+
- Hub selection
|
|
83
|
+
|
|
84
|
+
If the hub repo exists locally, it pulls latest. If not, it clones it.
|
|
85
|
+
|
|
86
|
+
If the hub is missing any tooling (changelog script, hooks, `.vscode`, `.gitignore`, `README.md`), it adds them automatically.
|
|
87
|
+
|
|
17
88
|
---
|
|
18
89
|
|
|
19
90
|
## Template types
|
|
@@ -22,15 +93,14 @@ npx create-dokio pool-care
|
|
|
22
93
|
PrinceXML-rendered print documents. Supports multi-page, resizable layouts, orderable exports, and proof downloads.
|
|
23
94
|
|
|
24
95
|
```
|
|
25
|
-
|
|
96
|
+
HW485-my-cool-template/
|
|
26
97
|
├── CHANGELOG.md
|
|
27
98
|
├── data.yaml
|
|
28
99
|
├── index.html
|
|
29
100
|
├── assets/
|
|
30
101
|
├── partials/
|
|
31
102
|
│ ├── page1-cover.html
|
|
32
|
-
│
|
|
33
|
-
│ └── page3-cta.html
|
|
103
|
+
│ └── page2-content.html
|
|
34
104
|
└── scss/
|
|
35
105
|
├── style.scss.hbs
|
|
36
106
|
├── _fonts.scss
|
|
@@ -38,35 +108,24 @@ dokio-pool-care/
|
|
|
38
108
|
├── _variables.scss
|
|
39
109
|
└── pages/
|
|
40
110
|
├── _page1.scss
|
|
41
|
-
|
|
42
|
-
└── _page3.scss
|
|
111
|
+
└── _page2.scss
|
|
43
112
|
```
|
|
44
113
|
|
|
45
|
-
**Prompts:**
|
|
46
|
-
- Page dimensions (mm)
|
|
47
|
-
- Page count
|
|
48
|
-
- PrinceXML version (11 or 15 — use 15 for flexbox)
|
|
49
|
-
- Resizable template
|
|
50
|
-
- Proof downloads (`draft_proofable`)
|
|
51
|
-
- Orderable export (Print from Snap)
|
|
114
|
+
**Prompts:** page dimensions (mm), page count, PrinceXML version (11 or 15), resizable, proof downloads, orderable export.
|
|
52
115
|
|
|
53
116
|
---
|
|
54
117
|
|
|
55
118
|
### General (image)
|
|
56
119
|
Chrome-rendered image output — JPG and/or PNG. Single or multi-section layouts.
|
|
57
120
|
|
|
58
|
-
**Prompts:**
|
|
59
|
-
- Dimensions (px)
|
|
60
|
-
- Page/section count
|
|
61
|
-
- Export formats (JPG, PNG, or both)
|
|
121
|
+
**Prompts:** dimensions (px), page/section count, export formats (JPG, PNG, or both).
|
|
62
122
|
|
|
63
123
|
---
|
|
64
124
|
|
|
65
125
|
### Email
|
|
66
126
|
HTML email scaffold. Includes Outlook conditional comments, `{{{___assembled_css}}}` injection, `{{{subject}}}` title, and Gmail-safe table structure.
|
|
67
127
|
|
|
68
|
-
**Prompts:**
|
|
69
|
-
- Subdomain only — email structure is fixed at 600px
|
|
128
|
+
**Prompts:** subdomain only — email structure is fixed at 600px.
|
|
70
129
|
|
|
71
130
|
---
|
|
72
131
|
|
|
@@ -75,36 +134,34 @@ HTML email scaffold. Includes Outlook conditional comments, `{{{___assembled_css
|
|
|
75
134
|
|
|
76
135
|
---
|
|
77
136
|
|
|
78
|
-
##
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
| `scss/pages/_pageN.scss` | Per-page style partials |
|
|
90
|
-
| `assets/` | Drop fonts, images, and local assets here |
|
|
91
|
-
| `CHANGELOG.md` | Template-level changelog |
|
|
137
|
+
## Changelog generator
|
|
138
|
+
|
|
139
|
+
Each template has its own `CHANGELOG.md`. After every commit that touches a template folder, the `post-commit` hook automatically:
|
|
140
|
+
|
|
141
|
+
1. Detects which templates changed
|
|
142
|
+
2. Logs the commit to each affected template's `CHANGELOG.md` (author, date, branch, title, files with status labels)
|
|
143
|
+
3. Amends the commit silently to include the changelog update
|
|
144
|
+
|
|
145
|
+
**Description generation:**
|
|
146
|
+
- If `tools/changelog/.env` exists with a valid `ANTHROPIC_API_KEY` → Claude Haiku generates a description
|
|
147
|
+
- If no key → falls back to `TODO - INTEGRATE WITH AI (JAKE TASK)`
|
|
92
148
|
|
|
93
149
|
---
|
|
94
150
|
|
|
95
|
-
## After scaffolding
|
|
151
|
+
## After scaffolding a template
|
|
96
152
|
|
|
97
153
|
```bash
|
|
98
|
-
cd
|
|
99
|
-
|
|
100
|
-
# 1. Set your template ID in data.yaml
|
|
101
|
-
# name: TEMPLATEID - dokio-<name> ← replace TEMPLATEID
|
|
154
|
+
cd bupa-sam-templates/templates/HW485-my-cool-template
|
|
102
155
|
|
|
103
|
-
#
|
|
104
|
-
#
|
|
156
|
+
# 1. Check data.yaml — name and subdomain are auto-set
|
|
157
|
+
# 2. Add assets to assets/
|
|
158
|
+
# 3. Edit partials/
|
|
105
159
|
|
|
106
|
-
#
|
|
107
|
-
|
|
160
|
+
# When ready to commit (from hub root):
|
|
161
|
+
cd ../../
|
|
162
|
+
git add templates/HW485-my-cool-template/
|
|
163
|
+
git commit -m "feat: add My Cool Template"
|
|
164
|
+
git push
|
|
108
165
|
```
|
|
109
166
|
|
|
110
167
|
---
|
|
@@ -112,23 +169,28 @@ cd dokio-<name>
|
|
|
112
169
|
## Development
|
|
113
170
|
|
|
114
171
|
```bash
|
|
115
|
-
git clone
|
|
172
|
+
git clone https://github.com/dokioco/create-dokio
|
|
173
|
+
cd create-dokio
|
|
116
174
|
pnpm install
|
|
117
|
-
pnpm
|
|
118
|
-
pnpm
|
|
175
|
+
pnpm build # compile to dist/
|
|
176
|
+
pnpm link --global
|
|
119
177
|
```
|
|
120
178
|
|
|
121
179
|
### Commit format
|
|
122
180
|
|
|
123
|
-
|
|
181
|
+
Enforced by commitlint on every commit:
|
|
124
182
|
|
|
125
183
|
```
|
|
126
184
|
feat: add resizable PDF option
|
|
127
185
|
fix: correct page count in data.yaml
|
|
128
186
|
chore: update dependencies
|
|
187
|
+
docs: update README
|
|
129
188
|
```
|
|
130
189
|
|
|
131
|
-
|
|
190
|
+
Breaking changes:
|
|
191
|
+
```
|
|
192
|
+
feat!: redesign template API
|
|
193
|
+
```
|
|
132
194
|
|
|
133
195
|
---
|
|
134
196
|
|
package/dist/index.js
CHANGED
|
@@ -881,6 +881,217 @@ import { join, basename } from "path";
|
|
|
881
881
|
import { execSync } from "child_process";
|
|
882
882
|
import fse from "fs-extra";
|
|
883
883
|
import kleur2 from "kleur";
|
|
884
|
+
|
|
885
|
+
// src/templates/changelog.ts
|
|
886
|
+
function changelogScript() {
|
|
887
|
+
return `#!/usr/bin/env python3
|
|
888
|
+
"""Prepend a new entry into a template's CHANGELOG.md."""
|
|
889
|
+
import sys
|
|
890
|
+
import re
|
|
891
|
+
import os
|
|
892
|
+
import datetime
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
def load_dotenv():
|
|
896
|
+
env_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".env")
|
|
897
|
+
if not os.path.isfile(env_path):
|
|
898
|
+
return
|
|
899
|
+
with open(env_path) as f:
|
|
900
|
+
for line in f:
|
|
901
|
+
line = line.strip()
|
|
902
|
+
if not line or line.startswith("#") or "=" not in line:
|
|
903
|
+
continue
|
|
904
|
+
key, _, val = line.partition("=")
|
|
905
|
+
key = key.strip()
|
|
906
|
+
val = val.strip().strip('"').strip("'")
|
|
907
|
+
if key and key not in os.environ:
|
|
908
|
+
os.environ[key] = val
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
load_dotenv()
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
STATUS_LABELS = {
|
|
915
|
+
"A": "Added",
|
|
916
|
+
"M": "Modified",
|
|
917
|
+
"D": "Deleted",
|
|
918
|
+
"R": "Renamed",
|
|
919
|
+
"C": "Copied",
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
def format_date(today_str):
|
|
924
|
+
d = datetime.datetime.strptime(today_str, "%Y-%m-%d")
|
|
925
|
+
day = str(int(d.strftime("%d")))
|
|
926
|
+
return d.strftime(f"%B {day}, %Y")
|
|
927
|
+
|
|
928
|
+
|
|
929
|
+
def generate_description(title, files, diff):
|
|
930
|
+
try:
|
|
931
|
+
import anthropic
|
|
932
|
+
except ImportError:
|
|
933
|
+
return None
|
|
934
|
+
|
|
935
|
+
api_key = os.environ.get("ANTHROPIC_API_KEY")
|
|
936
|
+
if not api_key:
|
|
937
|
+
return None
|
|
938
|
+
|
|
939
|
+
try:
|
|
940
|
+
client = anthropic.Anthropic(api_key=api_key)
|
|
941
|
+
files_str = "\\n".join(f"- {label}: {path}" for label, path in files)
|
|
942
|
+
diff_section = f"\\n\\nDiff:\\n\`\`\`\\n{diff[:4000]}\\n\`\`\`" if diff.strip() else ""
|
|
943
|
+
|
|
944
|
+
prompt = (
|
|
945
|
+
f"Commit: {title}\\n"
|
|
946
|
+
f"Files:\\n{files_str}"
|
|
947
|
+
f"{diff_section}\\n\\n"
|
|
948
|
+
f"Write one sentence describing what changed. Be direct and specific. No filler words."
|
|
949
|
+
)
|
|
950
|
+
|
|
951
|
+
message = client.messages.create(
|
|
952
|
+
model="claude-haiku-4-5-20251001",
|
|
953
|
+
max_tokens=256,
|
|
954
|
+
messages=[{"role": "user", "content": prompt}],
|
|
955
|
+
)
|
|
956
|
+
return message.content[0].text.strip()
|
|
957
|
+
except Exception:
|
|
958
|
+
return None
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
def parse_files(files_raw, template_prefix):
|
|
962
|
+
files = []
|
|
963
|
+
for line in files_raw.strip().splitlines():
|
|
964
|
+
line = line.strip()
|
|
965
|
+
if not line:
|
|
966
|
+
continue
|
|
967
|
+
parts = line.split("\\t")
|
|
968
|
+
if len(parts) >= 2:
|
|
969
|
+
status_key = parts[0].strip()[0].upper()
|
|
970
|
+
label = STATUS_LABELS.get(status_key, "Modified")
|
|
971
|
+
path = parts[-1].strip()
|
|
972
|
+
else:
|
|
973
|
+
label = "Modified"
|
|
974
|
+
path = parts[0].strip()
|
|
975
|
+
if template_prefix and path.startswith(template_prefix + "/"):
|
|
976
|
+
path = path[len(template_prefix):].lstrip("/")
|
|
977
|
+
files.append((label, path))
|
|
978
|
+
return files
|
|
979
|
+
|
|
980
|
+
|
|
981
|
+
def build_entry_content(branch, title, description, author, today_str, files):
|
|
982
|
+
date_str = format_date(today_str)
|
|
983
|
+
lines = [
|
|
984
|
+
f"**{author}** \u2022 *{date_str}*",
|
|
985
|
+
f"**branch:** \`{branch}\`",
|
|
986
|
+
"",
|
|
987
|
+
f"**title:** {title}",
|
|
988
|
+
"",
|
|
989
|
+
f"**description:** {description}",
|
|
990
|
+
"",
|
|
991
|
+
"### Files Changed",
|
|
992
|
+
]
|
|
993
|
+
for label, path in files:
|
|
994
|
+
lines.append(f"- **{label}** \`{path}\`")
|
|
995
|
+
return "\\n".join(lines)
|
|
996
|
+
|
|
997
|
+
|
|
998
|
+
def update_changelog(path, branch, today, title, author, files, diff):
|
|
999
|
+
with open(path, "r") as f:
|
|
1000
|
+
content = f.read()
|
|
1001
|
+
|
|
1002
|
+
description = generate_description(title, files, diff) or "TODO - INTEGRATE WITH AI (JAKE TASK)"
|
|
1003
|
+
entry_content = build_entry_content(branch, title, description, author, today, files)
|
|
1004
|
+
|
|
1005
|
+
insert_match = re.search(r"^(---|## )", content, re.MULTILINE)
|
|
1006
|
+
|
|
1007
|
+
if insert_match:
|
|
1008
|
+
insert_pos = insert_match.start()
|
|
1009
|
+
if content[insert_pos:].startswith("---"):
|
|
1010
|
+
new_block = f"---\\n\\n{entry_content}\\n\\n"
|
|
1011
|
+
else:
|
|
1012
|
+
new_block = f"---\\n\\n{entry_content}\\n\\n---\\n\\n"
|
|
1013
|
+
content = content[:insert_pos] + new_block + content[insert_pos:]
|
|
1014
|
+
else:
|
|
1015
|
+
content = content.rstrip("\\n") + f"\\n\\n---\\n\\n{entry_content}\\n\\n---\\n"
|
|
1016
|
+
|
|
1017
|
+
with open(path, "w") as f:
|
|
1018
|
+
f.write(content)
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
if __name__ == "__main__":
|
|
1022
|
+
path = sys.argv[1]
|
|
1023
|
+
today = sys.argv[2]
|
|
1024
|
+
branch = os.environ.get("CHANGELOG_BRANCH", "")
|
|
1025
|
+
message = os.environ.get("CHANGELOG_MESSAGE", "")
|
|
1026
|
+
author = os.environ.get("CHANGELOG_AUTHOR", "")
|
|
1027
|
+
files_raw = os.environ.get("CHANGELOG_FILES", "")
|
|
1028
|
+
template_prefix = os.environ.get("CHANGELOG_TEMPLATE_PREFIX", "")
|
|
1029
|
+
diff = os.environ.get("CHANGELOG_DIFF", "")
|
|
1030
|
+
|
|
1031
|
+
files = parse_files(files_raw, template_prefix)
|
|
1032
|
+
update_changelog(path, branch, today, message, author, files, diff)
|
|
1033
|
+
`;
|
|
1034
|
+
}
|
|
1035
|
+
function changelogHook() {
|
|
1036
|
+
return `#!/bin/bash
|
|
1037
|
+
# Auto-updates CHANGELOG.md in modified template folders after each commit.
|
|
1038
|
+
# Amends the commit silently to include changelog changes.
|
|
1039
|
+
|
|
1040
|
+
REPO_ROOT=$(git rev-parse --show-toplevel)
|
|
1041
|
+
LOCK="$REPO_ROOT/.git/changelog-hook-running"
|
|
1042
|
+
|
|
1043
|
+
[ -f "$LOCK" ] && exit 0
|
|
1044
|
+
|
|
1045
|
+
CHANGED=$(git diff-tree --no-commit-id -r --name-only HEAD | grep -v "CHANGELOG\\.md$")
|
|
1046
|
+
[ -z "$CHANGED" ] && exit 0
|
|
1047
|
+
|
|
1048
|
+
# Detect changed templates (expects templates/<template-id>/ structure)
|
|
1049
|
+
TEMPLATES=$(echo "$CHANGED" | grep "^templates/" | awk -F'/' '{print $1"/"$2}' | sort -u)
|
|
1050
|
+
[ -z "$TEMPLATES" ] && exit 0
|
|
1051
|
+
|
|
1052
|
+
COMMIT_MSG=$(git log -1 --pretty=%s)
|
|
1053
|
+
AUTHOR=$(git log -1 --pretty="%an")
|
|
1054
|
+
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
1055
|
+
TODAY=$(date +%Y-%m-%d)
|
|
1056
|
+
|
|
1057
|
+
touch "$LOCK"
|
|
1058
|
+
trap 'rm -f "$LOCK"' EXIT
|
|
1059
|
+
|
|
1060
|
+
AMENDED=0
|
|
1061
|
+
while IFS= read -r TEMPLATE; do
|
|
1062
|
+
[ -z "$TEMPLATE" ] && continue
|
|
1063
|
+
CHANGELOG="$REPO_ROOT/$TEMPLATE/CHANGELOG.md"
|
|
1064
|
+
[ ! -f "$CHANGELOG" ] && continue
|
|
1065
|
+
|
|
1066
|
+
TEMPLATE_FILES=$(git diff-tree --no-commit-id -r --name-status HEAD \\
|
|
1067
|
+
| grep -v "CHANGELOG\\.md$" \\
|
|
1068
|
+
| grep -E $'\\t'"$TEMPLATE/")
|
|
1069
|
+
|
|
1070
|
+
TEMPLATE_DIFF=$(git diff-tree --no-commit-id -r -p HEAD -- "$TEMPLATE/" 2>/dev/null | head -c 4000)
|
|
1071
|
+
|
|
1072
|
+
export CHANGELOG_BRANCH="$BRANCH"
|
|
1073
|
+
export CHANGELOG_MESSAGE="$COMMIT_MSG"
|
|
1074
|
+
export CHANGELOG_AUTHOR="$AUTHOR"
|
|
1075
|
+
export CHANGELOG_FILES="$TEMPLATE_FILES"
|
|
1076
|
+
export CHANGELOG_TEMPLATE_PREFIX="$TEMPLATE"
|
|
1077
|
+
export CHANGELOG_DIFF="$TEMPLATE_DIFF"
|
|
1078
|
+
python3 "$REPO_ROOT/tools/changelog/update_changelog.py" "$CHANGELOG" "$TODAY"
|
|
1079
|
+
|
|
1080
|
+
git add "$CHANGELOG"
|
|
1081
|
+
AMENDED=1
|
|
1082
|
+
done <<< "$TEMPLATES"
|
|
1083
|
+
|
|
1084
|
+
if [ "$AMENDED" = "1" ]; then
|
|
1085
|
+
git commit --amend --no-edit
|
|
1086
|
+
fi
|
|
1087
|
+
`;
|
|
1088
|
+
}
|
|
1089
|
+
function changelogEnvExample() {
|
|
1090
|
+
return `ANTHROPIC_API_KEY=sk-ant-...
|
|
1091
|
+
`;
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// src/git.ts
|
|
884
1095
|
async function ensureHubRepo(hubId) {
|
|
885
1096
|
const cwd = process.cwd();
|
|
886
1097
|
const dirName = hubRepoDirName(hubId);
|
|
@@ -898,13 +1109,61 @@ async function ensureHubRepo(hubId) {
|
|
|
898
1109
|
return hubDir;
|
|
899
1110
|
}
|
|
900
1111
|
async function setupHooks(hubDir) {
|
|
901
|
-
const
|
|
902
|
-
if (!await fse.pathExists(
|
|
903
|
-
await fse.ensureDir(join(
|
|
904
|
-
await fse.writeFile(
|
|
905
|
-
await fse.chmod(
|
|
1112
|
+
const commitMsgPath = join(hubDir, ".githooks", "commit-msg");
|
|
1113
|
+
if (!await fse.pathExists(commitMsgPath)) {
|
|
1114
|
+
await fse.ensureDir(join(commitMsgPath, ".."));
|
|
1115
|
+
await fse.writeFile(commitMsgPath, commitMsgHook(), "utf8");
|
|
1116
|
+
await fse.chmod(commitMsgPath, 493);
|
|
906
1117
|
console.log(kleur2.dim(` + .githooks/commit-msg`));
|
|
907
1118
|
}
|
|
1119
|
+
const postCommitPath = join(hubDir, ".githooks", "post-commit");
|
|
1120
|
+
const changelogScriptPath = join(hubDir, "tools", "changelog", "update_changelog.py");
|
|
1121
|
+
if (!await fse.pathExists(changelogScriptPath)) {
|
|
1122
|
+
await fse.ensureDir(join(changelogScriptPath, ".."));
|
|
1123
|
+
await fse.writeFile(changelogScriptPath, changelogScript(), "utf8");
|
|
1124
|
+
await fse.chmod(changelogScriptPath, 493);
|
|
1125
|
+
console.log(kleur2.dim(` + tools/changelog/update_changelog.py`));
|
|
1126
|
+
const envExamplePath = join(hubDir, "tools", "changelog", ".env.example");
|
|
1127
|
+
await fse.writeFile(envExamplePath, changelogEnvExample(), "utf8");
|
|
1128
|
+
console.log(kleur2.dim(` + tools/changelog/.env.example`));
|
|
1129
|
+
if (!await fse.pathExists(postCommitPath)) {
|
|
1130
|
+
await fse.writeFile(postCommitPath, changelogHook(), "utf8");
|
|
1131
|
+
await fse.chmod(postCommitPath, 493);
|
|
1132
|
+
console.log(kleur2.dim(` + .githooks/post-commit`));
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
const vscodePath = join(hubDir, ".vscode", "settings.json");
|
|
1136
|
+
if (!await fse.pathExists(vscodePath)) {
|
|
1137
|
+
await fse.ensureDir(join(vscodePath, ".."));
|
|
1138
|
+
await fse.writeFile(vscodePath, JSON.stringify({ "scss.validate": false, "css.validate": false }, null, 2) + "\n", "utf8");
|
|
1139
|
+
console.log(kleur2.dim(` + .vscode/settings.json`));
|
|
1140
|
+
}
|
|
1141
|
+
const gitignorePath = join(hubDir, ".gitignore");
|
|
1142
|
+
const requiredIgnores = [".DS_Store", "node_modules/", "*.log", "tools/changelog/.env"];
|
|
1143
|
+
if (!await fse.pathExists(gitignorePath)) {
|
|
1144
|
+
await fse.writeFile(gitignorePath, requiredIgnores.join("\n") + "\n", "utf8");
|
|
1145
|
+
console.log(kleur2.dim(` + .gitignore`));
|
|
1146
|
+
} else {
|
|
1147
|
+
const existing = await fse.readFile(gitignorePath, "utf8");
|
|
1148
|
+
const missing = requiredIgnores.filter((e) => !existing.includes(e));
|
|
1149
|
+
if (missing.length) {
|
|
1150
|
+
await fse.appendFile(gitignorePath, missing.join("\n") + "\n");
|
|
1151
|
+
console.log(kleur2.dim(` ~ .gitignore (added: ${missing.join(", ")})`));
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
const readmePath = join(hubDir, "README.md");
|
|
1155
|
+
if (!await fse.pathExists(readmePath)) {
|
|
1156
|
+
const hubName = basename(hubDir);
|
|
1157
|
+
await fse.writeFile(readmePath, `# ${hubName}
|
|
1158
|
+
|
|
1159
|
+
Templates for ${hubName} on Dokio.
|
|
1160
|
+
|
|
1161
|
+
## Creating a new template
|
|
1162
|
+
|
|
1163
|
+
Run \`create-dokio template\` from inside this repo.
|
|
1164
|
+
`, "utf8");
|
|
1165
|
+
console.log(kleur2.dim(` + README.md`));
|
|
1166
|
+
}
|
|
908
1167
|
execSync("git config core.hooksPath .githooks", { cwd: hubDir, stdio: "ignore" });
|
|
909
1168
|
}
|
|
910
1169
|
|
|
@@ -1000,12 +1259,15 @@ async function runHub() {
|
|
|
1000
1259
|
}
|
|
1001
1260
|
const files = {
|
|
1002
1261
|
".githooks/commit-msg": commitMsgHook(),
|
|
1262
|
+
".githooks/post-commit": changelogHook(),
|
|
1003
1263
|
".vscode/settings.json": JSON.stringify({ "scss.validate": false, "css.validate": false }, null, 2) + "\n",
|
|
1004
1264
|
"templates/.gitkeep": "",
|
|
1005
|
-
"tools
|
|
1265
|
+
"tools/changelog/update_changelog.py": changelogScript(),
|
|
1266
|
+
"tools/changelog/.env.example": changelogEnvExample(),
|
|
1006
1267
|
".gitignore": `.DS_Store
|
|
1007
1268
|
node_modules/
|
|
1008
1269
|
*.log
|
|
1270
|
+
tools/changelog/.env
|
|
1009
1271
|
`,
|
|
1010
1272
|
"README.md": `# ${hubName}
|
|
1011
1273
|
|
|
@@ -1021,7 +1283,7 @@ Run \`create-dokio template\` from inside this repo.
|
|
|
1021
1283
|
const fullPath = join4(outDir, rel);
|
|
1022
1284
|
await fse4.ensureDir(join4(fullPath, ".."));
|
|
1023
1285
|
await fse4.writeFile(fullPath, content, "utf8");
|
|
1024
|
-
if (rel === ".githooks/commit-msg") await fse4.chmod(fullPath, 493);
|
|
1286
|
+
if (rel === ".githooks/commit-msg" || rel === ".githooks/post-commit" || rel === "tools/changelog/update_changelog.py") await fse4.chmod(fullPath, 493);
|
|
1025
1287
|
console.log(kleur5.dim(` + ${rel}`));
|
|
1026
1288
|
}
|
|
1027
1289
|
execSync2("git init", { cwd: outDir, stdio: "ignore" });
|
|
@@ -1037,6 +1299,10 @@ Run \`create-dokio template\` from inside this repo.
|
|
|
1037
1299
|
console.log(kleur5.dim(` git remote add origin https://github.com/dokioco/${dirName}`));
|
|
1038
1300
|
console.log(kleur5.dim(` git push -u origin main`));
|
|
1039
1301
|
console.log("");
|
|
1302
|
+
console.log(kleur5.dim(` Changelog (optional \u2014 for AI descriptions):`));
|
|
1303
|
+
console.log(kleur5.dim(` cp tools/changelog/.env.example tools/changelog/.env`));
|
|
1304
|
+
console.log(kleur5.dim(` # Add your ANTHROPIC_API_KEY to tools/changelog/.env`));
|
|
1305
|
+
console.log("");
|
|
1040
1306
|
}
|
|
1041
1307
|
|
|
1042
1308
|
// src/index.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-dokio",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "CLI scaffold for Dokio templates",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -18,17 +18,21 @@
|
|
|
18
18
|
"prompts": "^2.4.2"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
+
"@anthropic-ai/sdk": "^0.100.1",
|
|
21
22
|
"@commitlint/cli": "^19.0.0",
|
|
22
23
|
"@commitlint/config-conventional": "^19.0.0",
|
|
23
24
|
"@commitlint/types": "^19.0.0",
|
|
25
|
+
"@eslint/js": "^10.0.1",
|
|
24
26
|
"@types/fs-extra": "^11.0.4",
|
|
25
27
|
"@types/node": "^20.0.0",
|
|
26
28
|
"@types/prompts": "^2.4.9",
|
|
27
29
|
"changelogen": "^0.6.2",
|
|
30
|
+
"eslint": "^10.4.1",
|
|
28
31
|
"husky": "^9.0.0",
|
|
29
32
|
"tsup": "^8.0.0",
|
|
30
33
|
"tsx": "^4.22.3",
|
|
31
|
-
"typescript": "^5.4.0"
|
|
34
|
+
"typescript": "^5.4.0",
|
|
35
|
+
"typescript-eslint": "^8.60.0"
|
|
32
36
|
},
|
|
33
37
|
"tsup": {
|
|
34
38
|
"entry": {
|
|
@@ -52,6 +56,8 @@
|
|
|
52
56
|
"scripts": {
|
|
53
57
|
"build": "tsup",
|
|
54
58
|
"dev": "tsup --watch",
|
|
55
|
-
"
|
|
59
|
+
"lint": "eslint",
|
|
60
|
+
"changelog": "changelogen --bump && tsx scripts/changelog.ts",
|
|
61
|
+
"release": "changelogen --bump --output CHANGELOG.md && git add package.json CHANGELOG.md && git commit -m \"chore(release): v$(node -p 'require(\"./package.json\").version')\" && git push -u origin HEAD"
|
|
56
62
|
}
|
|
57
63
|
}
|