@jml6m/skeletor 0.1.0-alpha.2 → 0.1.0-alpha.5
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 +12 -13
- package/package.json +8 -6
- package/src/index.js +68 -39
- package/templates/csharp/Program.cs +1 -1
- package/templates/csharp/ProgramTests.cs +1 -1
- package/templates/csharp/{{{PROJECT_NAME}}.csproj → Project.csproj.tmpl} +17 -17
- package/templates/go/{go.mod → go.mod.tmpl} +1 -1
- package/templates/java/{pom.xml → pom.xml.tmpl} +1 -1
- package/templates/java/src/main/java/{com/example → app}/App.java +2 -2
- package/templates/java/src/test/java/{com/example → app}/AppTest.java +2 -2
- package/templates/javascript/.jscpd.json +13 -0
- package/templates/python/{pyproject.toml → pyproject.toml.tmpl} +1 -1
- package/templates/python/template.json +2 -1
- package/templates/typescript/.jscpd.json +13 -0
- /package/templates/javascript/{package.json → package.json.tmpl} +0 -0
- /package/templates/rust/{Cargo.toml → Cargo.toml.tmpl} +0 -0
- /package/templates/typescript/{package.json → package.json.tmpl} +0 -0
package/README.md
CHANGED
|
@@ -25,20 +25,20 @@ Each template includes a `template.json` manifest that declares `verifyCommands`
|
|
|
25
25
|
|
|
26
26
|
We use `@clack/prompts` for a modern experience:
|
|
27
27
|
- Beautiful template selector with descriptions/hints when you omit `--template`
|
|
28
|
-
- Prompts for owner and description (
|
|
28
|
+
- Prompts for owner and description (interactive mode only)
|
|
29
29
|
- Confirmation step
|
|
30
30
|
- Proper cancel handling and nice spinners/intro/outro
|
|
31
31
|
|
|
32
|
-
`--
|
|
32
|
+
`--auto` stays fully non-interactive for scripts (requires `--template`; uses default owner/description).
|
|
33
33
|
|
|
34
34
|
## Pushing to GitHub
|
|
35
35
|
|
|
36
36
|
**Pushing is not automatic.** After every set of changes (new templates, CLI updates, tests, docs), you **must** explicitly:
|
|
37
37
|
1. `git add -A`
|
|
38
38
|
2. `git commit -m "..."` (good message)
|
|
39
|
-
3. `git push origin
|
|
39
|
+
3. `git push -u origin HEAD`
|
|
40
40
|
|
|
41
|
-
See the root `AGENTS.md` for the standing instruction given to agents working on this repo. This ensures the GitHub repo (`https://github.com/jml6m/skeletor`) stays in sync with every request. There is no special Grok Build setting that auto-pushes; it must be done via
|
|
41
|
+
See the root `AGENTS.md` for the standing instruction given to agents working on this repo. This ensures the GitHub repo (`https://github.com/jml6m/skeletor`) stays in sync with every request. There is no special Grok Build setting that auto-pushes; it must be done via explicit git commands in the agent loop (or documented in project instructions like AGENTS.md).
|
|
42
42
|
|
|
43
43
|
## Usage (alpha)
|
|
44
44
|
|
|
@@ -47,18 +47,16 @@ See the root `AGENTS.md` for the standing instruction given to agents working on
|
|
|
47
47
|
node src/index.js new my-project
|
|
48
48
|
|
|
49
49
|
# Or specify
|
|
50
|
-
node src/index.js new my-api --template typescript
|
|
50
|
+
node src/index.js new my-api --template typescript
|
|
51
51
|
|
|
52
|
-
# Non-interactive (
|
|
53
|
-
npx @jml6m/skeletor new my-service --
|
|
52
|
+
# Non-interactive (scripts)
|
|
53
|
+
npx @jml6m/skeletor new my-service --auto --template go
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
Common flags:
|
|
57
57
|
- `--template <id>` (javascript, typescript, python, go, rust, java, csharp, ...)
|
|
58
|
-
- `--
|
|
59
|
-
- `--
|
|
60
|
-
- `--yes` (skip all prompts)
|
|
61
|
-
- `--no-git`
|
|
58
|
+
- `--auto` (skip all prompts; requires `--template`)
|
|
59
|
+
- `--no-git` (skip git init; git is on by default)
|
|
62
60
|
|
|
63
61
|
After scaffolding, follow the `verifyCommands` from that template's `template.json` (shown in the success message). E.g. for most JS/TS: `npm install && npm run health:full` etc.
|
|
64
62
|
|
|
@@ -84,7 +82,7 @@ Run `skeletor` (or `node src/index.js`) with no args for current help and templa
|
|
|
84
82
|
|
|
85
83
|
```bash
|
|
86
84
|
# From a clone of this repo
|
|
87
|
-
node src/index.js new my-test-project --
|
|
85
|
+
node src/index.js new my-test-project --auto --template javascript
|
|
88
86
|
|
|
89
87
|
cd my-test-project
|
|
90
88
|
npm install
|
|
@@ -124,6 +122,7 @@ The source of truth lives in `templates/<id>/` (each with its own `template.json
|
|
|
124
122
|
|
|
125
123
|
- Edit files inside an existing template → next generation gets the change.
|
|
126
124
|
- Add a whole new `templates/newlang/` with `template.json` + files → it appears in the picker, help, and tests automatically.
|
|
125
|
+
- **Ecosystem manifest files** (e.g. `package.json`, `pyproject.toml`, `Cargo.toml`, `pom.xml`, `*.csproj`, `go.mod`) must be named with a `.tmpl` suffix inside the template (e.g. `package.json.tmpl`). The generator strips the suffix on output, so the generated project sees the conventional filename. This prevents GitHub's dependency graph from treating template dependencies as skeletor's own.
|
|
127
126
|
|
|
128
127
|
Always run `npm test` after template work.
|
|
129
128
|
|
|
@@ -133,7 +132,7 @@ When happy, commit + push (see root `AGENTS.md` for the standing rule).
|
|
|
133
132
|
|
|
134
133
|
Pushing is **explicit** (not automatic). After changes:
|
|
135
134
|
- Run tests
|
|
136
|
-
- `git add -A && git commit -m "..." && git push origin
|
|
135
|
+
- `git add -A && git commit -m "..." && git push -u origin HEAD`
|
|
137
136
|
|
|
138
137
|
See the root `AGENTS.md` (the instruction file for agents) for the exact rule.
|
|
139
138
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jml6m/skeletor",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.5",
|
|
4
4
|
"description": "Efficient scaffolding for custom development. Generates projects wired with personal lint/format/health/release/alias/AGENTS.md conventions.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,12 @@
|
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js",
|
|
12
|
-
"test:watch": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --watch"
|
|
12
|
+
"test:watch": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --watch",
|
|
13
|
+
"prepublishOnly": "npm test"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/jml6m/skeletor.git"
|
|
13
18
|
},
|
|
14
19
|
"files": [
|
|
15
20
|
"src",
|
|
@@ -26,7 +31,7 @@
|
|
|
26
31
|
"knip"
|
|
27
32
|
],
|
|
28
33
|
"dependencies": {
|
|
29
|
-
"@clack/prompts": "^
|
|
34
|
+
"@clack/prompts": "^1.5.1"
|
|
30
35
|
},
|
|
31
36
|
"devDependencies": {
|
|
32
37
|
"jest": "^29.7.0"
|
|
@@ -35,8 +40,5 @@
|
|
|
35
40
|
"license": "ISC",
|
|
36
41
|
"engines": {
|
|
37
42
|
"node": ">=20.19.0"
|
|
38
|
-
},
|
|
39
|
-
"dependencies": {
|
|
40
|
-
"@clack/prompts": "^1.5.1"
|
|
41
43
|
}
|
|
42
44
|
}
|
package/src/index.js
CHANGED
|
@@ -24,17 +24,17 @@ Usage:
|
|
|
24
24
|
skeletor --help
|
|
25
25
|
|
|
26
26
|
Options:
|
|
27
|
-
--template <name>
|
|
28
|
-
Omit
|
|
29
|
-
--
|
|
30
|
-
|
|
31
|
-
--
|
|
32
|
-
--no-git Skip git init
|
|
27
|
+
--template <name> Stack to scaffold (javascript, typescript, python, go, …)
|
|
28
|
+
Omit for an interactive language/stack picker (TTY required)
|
|
29
|
+
--auto Non-interactive; requires --template (uses defaults — edit
|
|
30
|
+
owner/description in generated files afterward)
|
|
31
|
+
--no-git Skip git init (git is initialized by default)
|
|
33
32
|
|
|
34
33
|
Examples:
|
|
34
|
+
skeletor new my-api # interactive: pick stack, owner, description
|
|
35
35
|
skeletor new my-api --template typescript
|
|
36
|
-
skeletor new my-lib --
|
|
37
|
-
skeletor new my-
|
|
36
|
+
skeletor new my-lib --auto --template go
|
|
37
|
+
skeletor new my-lib --auto --template go --no-git
|
|
38
38
|
`;
|
|
39
39
|
|
|
40
40
|
|
|
@@ -47,9 +47,7 @@ function parseArgs(argv) {
|
|
|
47
47
|
command: null,
|
|
48
48
|
name: null,
|
|
49
49
|
template: null, // resolved later, can be interactive
|
|
50
|
-
|
|
51
|
-
description: 'A new project scaffolded with skeletor.',
|
|
52
|
-
yes: false,
|
|
50
|
+
auto: false,
|
|
53
51
|
git: true,
|
|
54
52
|
};
|
|
55
53
|
|
|
@@ -64,9 +62,7 @@ function parseArgs(argv) {
|
|
|
64
62
|
for (let i = 2; i < args.length; i++) {
|
|
65
63
|
const a = args[i];
|
|
66
64
|
if (a === '--template' || a === '-t') result.template = args[++i] || null;
|
|
67
|
-
else if (a === '--
|
|
68
|
-
else if (a === '--description' || a === '-d') result.description = args[++i] || result.description;
|
|
69
|
-
else if (a === '--yes' || a === '-y') result.yes = true;
|
|
65
|
+
else if (a === '--auto') result.auto = true;
|
|
70
66
|
else if (a === '--no-git') result.git = false;
|
|
71
67
|
else if (!a.startsWith('-') && !result.name) result.name = a;
|
|
72
68
|
}
|
|
@@ -86,6 +82,28 @@ function render(content, vars) {
|
|
|
86
82
|
return out;
|
|
87
83
|
}
|
|
88
84
|
|
|
85
|
+
/** Sanitize a segment for use in Java groupId / Go module paths. */
|
|
86
|
+
function sanitizeIdentifierSegment(value) {
|
|
87
|
+
return String(value).replace(/[^a-zA-Z0-9]/g, '').toLowerCase() || 'example';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Build the full token map passed to copyAndRender. */
|
|
91
|
+
function buildRenderVars({ name, owner, description }) {
|
|
92
|
+
const repoOwner = owner || 'jml6m';
|
|
93
|
+
const ownerSegment = sanitizeIdentifierSegment(repoOwner);
|
|
94
|
+
const namespace = String(name).replace(/-/g, '_');
|
|
95
|
+
return {
|
|
96
|
+
PROJECT_NAME: name,
|
|
97
|
+
REPO_OWNER: repoOwner,
|
|
98
|
+
REPO_NAME: name,
|
|
99
|
+
DESCRIPTION: description || 'A new project scaffolded with skeletor.',
|
|
100
|
+
YEAR: new Date().getFullYear(),
|
|
101
|
+
NAMESPACE: namespace,
|
|
102
|
+
GROUP_ID: `io.github.${ownerSegment}`,
|
|
103
|
+
GO_MODULE: `github.com/${repoOwner}/${name}`,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
89
107
|
function ensureDir(dir) {
|
|
90
108
|
if (!fs.existsSync(dir)) {
|
|
91
109
|
fs.mkdirSync(dir, { recursive: true });
|
|
@@ -97,9 +115,11 @@ function copyAndRender(src, dest, vars) {
|
|
|
97
115
|
if (stat.isDirectory()) {
|
|
98
116
|
ensureDir(dest);
|
|
99
117
|
for (const entry of fs.readdirSync(src)) {
|
|
100
|
-
// never copy node_modules
|
|
101
|
-
if (entry === 'node_modules' || entry === '.git') continue;
|
|
102
|
-
|
|
118
|
+
// never copy node_modules, .git, or skeletor manifest inside templates
|
|
119
|
+
if (entry === 'node_modules' || entry === '.git' || entry === 'template.json') continue;
|
|
120
|
+
// strip .tmpl suffix from output filename so e.g. package.json.tmpl → package.json
|
|
121
|
+
const outEntry = entry.endsWith('.tmpl') ? entry.slice(0, -'.tmpl'.length) : entry;
|
|
122
|
+
copyAndRender(path.join(src, entry), path.join(dest, outEntry), vars);
|
|
103
123
|
}
|
|
104
124
|
return;
|
|
105
125
|
}
|
|
@@ -145,13 +165,9 @@ function getTemplatesWithManifests() {
|
|
|
145
165
|
return ids.map((id) => loadTemplateManifest(id));
|
|
146
166
|
}
|
|
147
167
|
|
|
148
|
-
async function chooseTemplateInteractively(templates
|
|
149
|
-
if (
|
|
150
|
-
return
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (!process.stdout.isTTY) {
|
|
154
|
-
return templates[0] ? templates[0].id : null;
|
|
168
|
+
async function chooseTemplateInteractively(templates) {
|
|
169
|
+
if (templates.length === 0) {
|
|
170
|
+
return null;
|
|
155
171
|
}
|
|
156
172
|
|
|
157
173
|
p.intro('💀 Skeletor — pick your scaffolding');
|
|
@@ -163,7 +179,7 @@ async function chooseTemplateInteractively(templates, isYes) {
|
|
|
163
179
|
}));
|
|
164
180
|
|
|
165
181
|
const selected = await p.select({
|
|
166
|
-
message: '
|
|
182
|
+
message: 'What language or stack are you building?',
|
|
167
183
|
options,
|
|
168
184
|
});
|
|
169
185
|
|
|
@@ -175,8 +191,11 @@ async function chooseTemplateInteractively(templates, isYes) {
|
|
|
175
191
|
return selected;
|
|
176
192
|
}
|
|
177
193
|
|
|
194
|
+
const DEFAULT_OWNER = 'jml6m';
|
|
195
|
+
const DEFAULT_DESCRIPTION = 'A new project scaffolded with skeletor.';
|
|
196
|
+
|
|
178
197
|
async function runNew(opts) {
|
|
179
|
-
const { name, git,
|
|
198
|
+
const { name, git, auto } = opts;
|
|
180
199
|
|
|
181
200
|
if (!name || name === '.' || name === '..') {
|
|
182
201
|
logError('❌ Please provide a valid project name (e.g. "my-api").');
|
|
@@ -190,13 +209,28 @@ async function runNew(opts) {
|
|
|
190
209
|
}
|
|
191
210
|
|
|
192
211
|
let chosenTemplateId = opts.template;
|
|
193
|
-
let finalOwner =
|
|
194
|
-
let finalDesc =
|
|
212
|
+
let finalOwner = DEFAULT_OWNER;
|
|
213
|
+
let finalDesc = DEFAULT_DESCRIPTION;
|
|
195
214
|
|
|
196
|
-
const isInteractive = !
|
|
215
|
+
const isInteractive = !auto && process.stdout.isTTY;
|
|
197
216
|
|
|
198
217
|
if (!chosenTemplateId) {
|
|
199
|
-
|
|
218
|
+
if (auto) {
|
|
219
|
+
logError('❌ --auto requires --template <id>. Pick a stack interactively by omitting --auto.');
|
|
220
|
+
logError(` Available: ${allTemplates.map((t) => t.id).join(', ')}`);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
if (!process.stdout.isTTY) {
|
|
224
|
+
logError('❌ No --template provided and this is not an interactive terminal.');
|
|
225
|
+
logError(' Use --auto --template <id> for scripts and piped environments.');
|
|
226
|
+
logError(` Available: ${allTemplates.map((t) => t.id).join(', ')}`);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
chosenTemplateId = await chooseTemplateInteractively(allTemplates);
|
|
230
|
+
} else if (!process.stdout.isTTY && !auto) {
|
|
231
|
+
logError('❌ --template requires --auto when not running in an interactive terminal.');
|
|
232
|
+
logError(` Example: skeletor new ${name} --auto --template ${chosenTemplateId}`);
|
|
233
|
+
process.exit(1);
|
|
200
234
|
}
|
|
201
235
|
|
|
202
236
|
const templateInfo = allTemplates.find((t) => t.id === chosenTemplateId);
|
|
@@ -206,7 +240,7 @@ async function runNew(opts) {
|
|
|
206
240
|
process.exit(1);
|
|
207
241
|
}
|
|
208
242
|
|
|
209
|
-
//
|
|
243
|
+
// Owner, description, and confirm — interactive only (--auto uses defaults above)
|
|
210
244
|
if (isInteractive) {
|
|
211
245
|
const ownerInput = await p.text({
|
|
212
246
|
message: 'GitHub owner / org',
|
|
@@ -240,14 +274,7 @@ async function runNew(opts) {
|
|
|
240
274
|
process.exit(1);
|
|
241
275
|
}
|
|
242
276
|
|
|
243
|
-
const
|
|
244
|
-
const vars = {
|
|
245
|
-
PROJECT_NAME: name,
|
|
246
|
-
REPO_OWNER: finalOwner,
|
|
247
|
-
REPO_NAME: repoName,
|
|
248
|
-
DESCRIPTION: finalDesc,
|
|
249
|
-
YEAR: new Date().getFullYear(),
|
|
250
|
-
};
|
|
277
|
+
const vars = buildRenderVars({ name, owner: finalOwner, description: finalDesc });
|
|
251
278
|
|
|
252
279
|
p.log.info(`Creating "${name}" using ${templateInfo.name}...`);
|
|
253
280
|
|
|
@@ -303,6 +330,8 @@ function main() {
|
|
|
303
330
|
export {
|
|
304
331
|
parseArgs,
|
|
305
332
|
render,
|
|
333
|
+
buildRenderVars,
|
|
334
|
+
sanitizeIdentifierSegment,
|
|
306
335
|
getAvailableTemplates,
|
|
307
336
|
getTemplatesWithManifests,
|
|
308
337
|
loadTemplateManifest,
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
<Project Sdk="Microsoft.NET.Sdk">
|
|
2
|
-
|
|
3
|
-
<PropertyGroup>
|
|
4
|
-
<OutputType>Exe</OutputType>
|
|
5
|
-
<TargetFramework>net8.0</TargetFramework>
|
|
6
|
-
<ImplicitUsings>enable</ImplicitUsings>
|
|
7
|
-
<Nullable>enable</Nullable>
|
|
8
|
-
<RootNamespace>{{
|
|
9
|
-
</PropertyGroup>
|
|
10
|
-
|
|
11
|
-
<ItemGroup>
|
|
12
|
-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
|
13
|
-
<PackageReference Include="xunit" Version="2.6.6" />
|
|
14
|
-
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6" />
|
|
15
|
-
</ItemGroup>
|
|
16
|
-
|
|
17
|
-
</Project>
|
|
1
|
+
<Project Sdk="Microsoft.NET.Sdk">
|
|
2
|
+
|
|
3
|
+
<PropertyGroup>
|
|
4
|
+
<OutputType>Exe</OutputType>
|
|
5
|
+
<TargetFramework>net8.0</TargetFramework>
|
|
6
|
+
<ImplicitUsings>enable</ImplicitUsings>
|
|
7
|
+
<Nullable>enable</Nullable>
|
|
8
|
+
<RootNamespace>{{NAMESPACE}}</RootNamespace>
|
|
9
|
+
</PropertyGroup>
|
|
10
|
+
|
|
11
|
+
<ItemGroup>
|
|
12
|
+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
|
13
|
+
<PackageReference Include="xunit" Version="2.6.6" />
|
|
14
|
+
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6" />
|
|
15
|
+
</ItemGroup>
|
|
16
|
+
|
|
17
|
+
</Project>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
4
4
|
<modelVersion>4.0.0</modelVersion>
|
|
5
5
|
|
|
6
|
-
<groupId>
|
|
6
|
+
<groupId>{{GROUP_ID}}</groupId>
|
|
7
7
|
<artifactId>{{PROJECT_NAME}}</artifactId>
|
|
8
8
|
<version>0.1.0</version>
|
|
9
9
|
<packaging>jar</packaging>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
package
|
|
1
|
+
package app;
|
|
2
2
|
|
|
3
3
|
import org.junit.jupiter.api.Test;
|
|
4
4
|
import static org.junit.jupiter.api.Assertions.*;
|
|
@@ -9,4 +9,4 @@ class AppTest {
|
|
|
9
9
|
void helloReturnsGreeting() {
|
|
10
10
|
assertEquals("Hello, tester from {{PROJECT_NAME}}!", App.hello("tester"));
|
|
11
11
|
}
|
|
12
|
-
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"threshold": 5,
|
|
3
|
+
"reporters": ["html", "console"],
|
|
4
|
+
"ignore": ["**/node_modules/**", "**/dist/**", "**/build/**", "**/*.spec.ts", "**/*.test.ts"],
|
|
5
|
+
"absolute": true,
|
|
6
|
+
"gitignore": true,
|
|
7
|
+
"minLines": 5,
|
|
8
|
+
"minTokens": 50,
|
|
9
|
+
"formatsExts": {
|
|
10
|
+
"javascript": ["js", "jsx"],
|
|
11
|
+
"typescript": ["ts", "tsx"]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"threshold": 5,
|
|
3
|
+
"reporters": ["html", "console"],
|
|
4
|
+
"ignore": ["**/node_modules/**", "**/dist/**", "**/build/**", "**/*.spec.ts", "**/*.test.ts"],
|
|
5
|
+
"absolute": true,
|
|
6
|
+
"gitignore": true,
|
|
7
|
+
"minLines": 5,
|
|
8
|
+
"minTokens": 50,
|
|
9
|
+
"formatsExts": {
|
|
10
|
+
"javascript": ["js", "jsx"],
|
|
11
|
+
"typescript": ["ts", "tsx"]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|