@jrpool/kilotest 59.1.3 → 59.1.4
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/docs/AI-TOOLS.md +5 -5
- package/package.json +8 -2
- package/scripts/postPR.ts +89 -0
- package/scripts/prePR.ts +64 -0
package/docs/AI-TOOLS.md
CHANGED
|
@@ -29,7 +29,7 @@ The internal features that make Kilotest a collection of tools for language mode
|
|
|
29
29
|
- A `server.json` file, conforming to the [MCP server schema](https://modelcontextprotocol.io/), and a GitHub Actions workflow (`publish-mcp.yml`) that authenticates via GitHub OIDC and publishes that file to the official MCP Registry whenever `server.json` or `api/version.ts` changes on the `main` branch.
|
|
30
30
|
- A `capability.md` file with a URL, summarizing what Kilotest tools can do.
|
|
31
31
|
- `mcp`, `mcp-server`, and `modelcontextprotocol` keywords in `package.json`, supporting discovery of the `@jrpool/kilotest` npm package by tools and humans searching the npm registry for MCP servers.
|
|
32
|
-
- An `lhm.plugin.json` file, conforming to the [LobeHub plugin manifest format](https://market.lobehub.com/s/publish-mcp/references/manifest), declaring Kilotest as a plugin in the [LobeHub MCP Market](https://lobehub.com/mcp). Publishing and updating are non-automated (`lhm plugin publish
|
|
32
|
+
- An `lhm.plugin.json` file, conforming to the [LobeHub plugin manifest format](https://market.lobehub.com/s/publish-mcp/references/manifest), declaring Kilotest as a plugin in the [LobeHub MCP Market](https://lobehub.com/mcp). Publishing and updating are non-automated (`lhm plugin publish` for the initial listing; thereafter `npm run publish:lhm`, which wraps `lhm plugin update` and also runs from the `postpublish` hook), because LobeHub requires browser-based OAuth and does not support machine-to-machine publishing.
|
|
33
33
|
- A `sitemap.xml` file.
|
|
34
34
|
- Documentation in the `README.md` file.
|
|
35
35
|
- This `AI-TOOLS.md` file.
|
|
@@ -62,11 +62,11 @@ The external features that support the use of Kilotest as a collection of AI too
|
|
|
62
62
|
|
|
63
63
|
## Required maintenance
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
Releasing a new version is a two-script procedure:
|
|
66
66
|
|
|
67
|
-
-
|
|
68
|
-
-
|
|
69
|
-
- Execute `
|
|
67
|
+
- On a feature branch, commit all substantive changes, then execute `npm run prePR -- <level>` (`major`, `minor`, or `patch`). The script bumps `package.json` and `package-lock.json`, regenerates `openapi.yaml` and the versions of `server.json` and `lhm.plugin.json` via the `version` hook, commits and pushes the result, and records the branch name for the next step.
|
|
68
|
+
- Submit a pull request and merge it to `main` (triggers `publish-mcp.yml` automatically for the MCP Registry).
|
|
69
|
+
- Execute `npm run postPR`. The script checks out and pulls `main`, verifies that the merge landed, publishes to npm (whose `postpublish` hook updates the LobeHub listing), deletes the feature branch, and prunes remote-tracking branches. If the LobeHub update fails, retry it alone with `npm run publish:lhm`.
|
|
70
70
|
|
|
71
71
|
## Use cases
|
|
72
72
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jrpool/kilotest",
|
|
3
|
-
"version": "59.1.
|
|
3
|
+
"version": "59.1.4",
|
|
4
4
|
"description": "Ensemble testing for front-end web quality",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -14,7 +14,13 @@
|
|
|
14
14
|
"test": "c8 node --require ./test/setup.ts --experimental-test-module-mocks --test --test-concurrency=1 --test-randomize '**/*.test.ts'",
|
|
15
15
|
"test:smoke": "node smokeTest.ts",
|
|
16
16
|
"checkinfo": "node checkinfo.ts",
|
|
17
|
-
"generate": "node scripts/generate-openapi.ts && node scripts/update-versions.ts"
|
|
17
|
+
"generate": "node scripts/generate-openapi.ts && node scripts/update-versions.ts",
|
|
18
|
+
"version": "npm run generate",
|
|
19
|
+
"prepublishOnly": "test \"$(git branch --show-current)\" = main",
|
|
20
|
+
"postpublish": "npm run publish:lhm",
|
|
21
|
+
"publish:lhm": "npx -y @lobehub/market-cli plugin update --dir .",
|
|
22
|
+
"prePR": "node scripts/prePR.ts",
|
|
23
|
+
"postPR": "node scripts/postPR.ts"
|
|
18
24
|
},
|
|
19
25
|
"repository": {
|
|
20
26
|
"type": "git",
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/*
|
|
2
|
+
postPR.ts
|
|
3
|
+
Finalizes a release after the prePR pull request has been merged: checks out main, pulls, verifies that the merge landed, publishes to npm (the "postpublish" hook then updates the LobeHub listing), deletes the feature branch, and prunes remote-tracking branches. The feature-branch name is read from the marker file written by prePR.ts, or from an optional command-line argument.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// IMPORTS
|
|
7
|
+
|
|
8
|
+
import {spawnSync} from 'node:child_process';
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
|
|
12
|
+
// CONSTANTS
|
|
13
|
+
|
|
14
|
+
const markerFile = 'kilotest-pr-branch';
|
|
15
|
+
|
|
16
|
+
// FUNCTIONS
|
|
17
|
+
|
|
18
|
+
// Runs a command with inherited stdio and throws on failure.
|
|
19
|
+
const run = (command: string, args: string[]): void => {
|
|
20
|
+
if (spawnSync(command, args, {stdio: 'inherit'}).status !== 0) {
|
|
21
|
+
throw new Error(`${command} ${args.join(' ')} failed`);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
// Runs a command and returns its trimmed standard output.
|
|
25
|
+
const capture = (command: string, args: string[]): string => {
|
|
26
|
+
const result = spawnSync(command, args, {encoding: 'utf8'});
|
|
27
|
+
if (result.status !== 0) {
|
|
28
|
+
throw new Error(`${command} ${args.join(' ')} failed`);
|
|
29
|
+
}
|
|
30
|
+
return result.stdout.trim();
|
|
31
|
+
};
|
|
32
|
+
// Returns the absolute path of a file inside the git directory.
|
|
33
|
+
const gitPath = (name: string): string =>
|
|
34
|
+
path.join(capture('git', ['rev-parse', '--absolute-git-dir']), name);
|
|
35
|
+
// Exits unless the working tree is clean.
|
|
36
|
+
const requireCleanTree = (): void => {
|
|
37
|
+
const dirty = capture('git', ['status', '--porcelain']);
|
|
38
|
+
if (dirty) {
|
|
39
|
+
console.error(`Working tree is not clean. Commit or stash all changes first:\n${dirty}`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
// Returns the version field of package.json, either from the working tree or from a git ref.
|
|
44
|
+
const versionOf = (ref?: string): string => {
|
|
45
|
+
const content = ref
|
|
46
|
+
? capture('git', ['show', `${ref}:package.json`])
|
|
47
|
+
: fs.readFileSync('package.json', 'utf8');
|
|
48
|
+
return JSON.parse(content).version;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// EXECUTION
|
|
52
|
+
|
|
53
|
+
const marker = gitPath(markerFile);
|
|
54
|
+
const argBranch = process.argv[2] ?? '';
|
|
55
|
+
const branch = argBranch ||
|
|
56
|
+
(fs.existsSync(marker) ? fs.readFileSync(marker, 'utf8').trim() : '');
|
|
57
|
+
if (!branch) {
|
|
58
|
+
console.error('No feature branch recorded. Run prePR first, or pass the branch name: npm run postPR -- <branch>');
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
if (branch === 'main') {
|
|
62
|
+
console.error('Refusing to treat main as the feature branch');
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
if (!capture('git', ['branch', '--list', branch])) {
|
|
66
|
+
console.error(`Branch ${branch} does not exist locally; it may already have been deleted`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
requireCleanTree();
|
|
70
|
+
run('git', ['checkout', 'main']);
|
|
71
|
+
run('git', ['pull', '--ff-only']);
|
|
72
|
+
// Aborts if the pull request has not been merged: under squash-merges the feature branch is never an ancestor of main, so version equality is the merge check.
|
|
73
|
+
const branchVersion = versionOf(branch);
|
|
74
|
+
const mainVersion = versionOf();
|
|
75
|
+
if (branchVersion !== mainVersion) {
|
|
76
|
+
console.error(`main is at version ${mainVersion} but ${branch} is at ${branchVersion}; merge the pull request first`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
run('npm', ['publish']);
|
|
80
|
+
// Branch cleanup is best-effort: deletion must precede pruning, because "git branch -d" checks mergedness against the upstream ref, which pruning would remove.
|
|
81
|
+
const deleted = spawnSync('git', ['branch', '-d', branch], {stdio: 'inherit'});
|
|
82
|
+
if (deleted.status !== 0) {
|
|
83
|
+
console.warn(`Could not delete ${branch}; delete it manually after confirming the merge`);
|
|
84
|
+
}
|
|
85
|
+
if (spawnSync('git', ['remote', 'prune', 'origin'], {stdio: 'inherit'}).status !== 0) {
|
|
86
|
+
console.warn('git remote prune origin failed');
|
|
87
|
+
}
|
|
88
|
+
fs.rmSync(marker, {force: true});
|
|
89
|
+
console.log('Release complete');
|
package/scripts/prePR.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/*
|
|
2
|
+
prePR.ts
|
|
3
|
+
Prepares a release pull request on the current feature branch: verifies a clean working tree, bumps the package version (the npm "version" hook regenerates openapi.yaml, server.json, and lhm.plugin.json), commits and pushes the result, records the branch name for postPR.ts, and prints the remaining manual steps.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// IMPORTS
|
|
7
|
+
|
|
8
|
+
import {spawnSync} from 'node:child_process';
|
|
9
|
+
import fs from 'node:fs';
|
|
10
|
+
import path from 'node:path';
|
|
11
|
+
|
|
12
|
+
// CONSTANTS
|
|
13
|
+
|
|
14
|
+
const levels = ['major', 'minor', 'patch'];
|
|
15
|
+
const usage = 'Usage: npm run prePR -- <major|minor|patch>';
|
|
16
|
+
const markerFile = 'kilotest-pr-branch';
|
|
17
|
+
|
|
18
|
+
// FUNCTIONS
|
|
19
|
+
|
|
20
|
+
// Runs a command with inherited stdio and throws on failure.
|
|
21
|
+
const run = (command: string, args: string[]): void => {
|
|
22
|
+
if (spawnSync(command, args, {stdio: 'inherit'}).status !== 0) {
|
|
23
|
+
throw new Error(`${command} ${args.join(' ')} failed`);
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
// Runs a command and returns its trimmed standard output.
|
|
27
|
+
const capture = (command: string, args: string[]): string => {
|
|
28
|
+
const result = spawnSync(command, args, {encoding: 'utf8'});
|
|
29
|
+
if (result.status !== 0) {
|
|
30
|
+
throw new Error(`${command} ${args.join(' ')} failed`);
|
|
31
|
+
}
|
|
32
|
+
return result.stdout.trim();
|
|
33
|
+
};
|
|
34
|
+
// Returns the absolute path of a file inside the git directory.
|
|
35
|
+
const gitPath = (name: string): string =>
|
|
36
|
+
path.join(capture('git', ['rev-parse', '--absolute-git-dir']), name);
|
|
37
|
+
// Exits unless the working tree is clean, so all substantive changes are committed before the version bump.
|
|
38
|
+
const requireCleanTree = (): void => {
|
|
39
|
+
const dirty = capture('git', ['status', '--porcelain']);
|
|
40
|
+
if (dirty) {
|
|
41
|
+
console.error(`Working tree is not clean. Commit or stash all changes first:\n${dirty}`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// EXECUTION
|
|
47
|
+
|
|
48
|
+
const level = process.argv[2] ?? '';
|
|
49
|
+
if (!levels.includes(level)) {
|
|
50
|
+
console.error(usage);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
const branch = capture('git', ['branch', '--show-current']);
|
|
54
|
+
if (!branch || branch === 'main') {
|
|
55
|
+
console.error('prePR must run on a feature branch, not on main');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
requireCleanTree();
|
|
59
|
+
run('npm', ['version', level, '--no-git-tag-version']);
|
|
60
|
+
run('git', ['add', '-A']);
|
|
61
|
+
run('git', ['commit', '-m', 'update version and generated files']);
|
|
62
|
+
run('git', ['push', '-u', 'origin', branch]);
|
|
63
|
+
fs.writeFileSync(gitPath(markerFile), `${branch}\n`);
|
|
64
|
+
console.log('Submit a pull request, merge it to main, then run: npm run postPR');
|