@devshop/crew 0.4.1 → 0.5.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 +14 -0
- package/README.md +2 -0
- package/package.json +6 -2
- package/scripts/cli.js +4 -2
- package/scripts/commands/update.js +8 -0
- package/scripts/lib/self-update.js +83 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [0.5.0](https://github.com/devshop-software/crew/compare/v0.4.2...v0.5.0) (2026-04-30)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* crew update auto-bumps the local package via the project's PM ([9e39abd](https://github.com/devshop-software/crew/commit/9e39abdb7c51fba87c38802a07718af9725dccd2))
|
|
7
|
+
|
|
8
|
+
## [0.4.2](https://github.com/devshop-software/crew/compare/v0.4.1...v0.4.2) (2026-04-30)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* publish to npm in prepare phase so failed publish can't orphan tags ([6ee8e5a](https://github.com/devshop-software/crew/commit/6ee8e5aacc4ed698e0e51c17891bd8b699faafb7))
|
|
14
|
+
|
|
1
15
|
# [0.4.0](https://github.com/devshop-software/crew/compare/v0.3.0...v0.4.0) (2026-04-30)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -17,6 +17,8 @@ pnpm add -D @devshop/crew
|
|
|
17
17
|
pnpm exec crew init
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
To pull newer skill content later, just run `pnpm exec crew update` — it auto-detects the package manager (pnpm/npm/yarn/bun) from your lockfile, runs `<pm> update @devshop/crew` to refresh the local install, then re-execs the freshly-installed CLI to apply the new skills. Pass `--no-self-update` to skip the package bump and only re-apply what's already on disk.
|
|
21
|
+
|
|
20
22
|
This copies the skills into `./.claude/skills/`, writes a manifest, and appends a `## Workflow Config` block to `CLAUDE.md` (creating it if absent).
|
|
21
23
|
|
|
22
24
|
## Commands
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devshop/crew",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Project-agnostic Claude Code skills for spec → implement → qa → review → ship",
|
|
5
5
|
"bin": {
|
|
6
6
|
"crew": "scripts/cli.js"
|
|
@@ -37,6 +37,10 @@
|
|
|
37
37
|
},
|
|
38
38
|
"license": "MIT",
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"
|
|
40
|
+
"@devshop/crew": "^0.4.1",
|
|
41
|
+
"semantic-release": "^24.2.0",
|
|
42
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
43
|
+
"@semantic-release/exec": "^7.0.3",
|
|
44
|
+
"@semantic-release/git": "^10.0.1"
|
|
41
45
|
}
|
|
42
46
|
}
|
package/scripts/cli.js
CHANGED
|
@@ -19,12 +19,13 @@ function usage() {
|
|
|
19
19
|
' --force Override prompts and refusals.',
|
|
20
20
|
' --yes Non-interactive (CI-safe defaults).',
|
|
21
21
|
' --dry-run Print actions, write nothing.',
|
|
22
|
-
' --no-claude-md On init only: skip CLAUDE.md append.'
|
|
22
|
+
' --no-claude-md On init only: skip CLAUDE.md append.',
|
|
23
|
+
' --no-self-update On update only: skip pulling a newer package via the local PM.'
|
|
23
24
|
].join('\n');
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
function parseArgs(argv) {
|
|
27
|
-
const flags = { global: false, force: false, yes: false, dryRun: false, noClaudeMd: false };
|
|
28
|
+
const flags = { global: false, force: false, yes: false, dryRun: false, noClaudeMd: false, noSelfUpdate: false };
|
|
28
29
|
let command = null;
|
|
29
30
|
for (const a of argv) {
|
|
30
31
|
if (a.startsWith('--')) {
|
|
@@ -34,6 +35,7 @@ function parseArgs(argv) {
|
|
|
34
35
|
case '--yes': flags.yes = true; break;
|
|
35
36
|
case '--dry-run': flags.dryRun = true; break;
|
|
36
37
|
case '--no-claude-md': flags.noClaudeMd = true; break;
|
|
38
|
+
case '--no-self-update': flags.noSelfUpdate = true; break;
|
|
37
39
|
case '--help': process.stdout.write(usage() + '\n'); process.exit(0);
|
|
38
40
|
default:
|
|
39
41
|
process.stderr.write(`Unknown flag: ${a}\n\n${usage()}\n`);
|
|
@@ -3,6 +3,7 @@ const { resolveTarget } = require('../lib/paths');
|
|
|
3
3
|
const { readManifest, writeManifest, diffSkills, PACKAGE_NAME, SCHEMA_VERSION } = require('../lib/manifest');
|
|
4
4
|
const { copyFolder, backupFolder, backupRoot } = require('../lib/fsx');
|
|
5
5
|
const { chooseEditAction } = require('../lib/prompt');
|
|
6
|
+
const { runSelfUpdate } = require('../lib/self-update');
|
|
6
7
|
const log = require('../lib/log');
|
|
7
8
|
|
|
8
9
|
const PKG_ROOT = path.resolve(__dirname, '..', '..');
|
|
@@ -10,6 +11,13 @@ const PKG_VERSION = require(path.join(PKG_ROOT, 'package.json')).version;
|
|
|
10
11
|
const PKG_SKILLS = path.join(PKG_ROOT, 'skills');
|
|
11
12
|
|
|
12
13
|
module.exports = async function update(flags) {
|
|
14
|
+
// If installed as a local dep, bump the package via the project's package
|
|
15
|
+
// manager first, then re-exec the freshly-installed CLI to do the actual
|
|
16
|
+
// skill update. Skipped for global/dlx invocations (no project lockfile).
|
|
17
|
+
const self = runSelfUpdate(flags, log);
|
|
18
|
+
if (self.error) { log.error(self.message); return self.exitCode; }
|
|
19
|
+
if (self.reExec) return self.exitCode;
|
|
20
|
+
|
|
13
21
|
let target;
|
|
14
22
|
try { target = resolveTarget(flags); }
|
|
15
23
|
catch (e) { log.error(e.message); return e.exitCode || 2; }
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { spawnSync } = require('child_process');
|
|
4
|
+
const { PACKAGE_NAME } = require('./manifest');
|
|
5
|
+
|
|
6
|
+
function findProjectRoot(start = process.cwd()) {
|
|
7
|
+
let dir = path.resolve(start);
|
|
8
|
+
while (true) {
|
|
9
|
+
if (fs.existsSync(path.join(dir, 'package.json'))) return dir;
|
|
10
|
+
const parent = path.dirname(dir);
|
|
11
|
+
if (parent === dir) return null;
|
|
12
|
+
dir = parent;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const LOCKFILES = [
|
|
17
|
+
['pnpm-lock.yaml', 'pnpm'],
|
|
18
|
+
['yarn.lock', 'yarn'],
|
|
19
|
+
['package-lock.json', 'npm'],
|
|
20
|
+
['bun.lock', 'bun'],
|
|
21
|
+
['bun.lockb', 'bun']
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
function detectPm(projectRoot) {
|
|
25
|
+
for (const [file, pm] of LOCKFILES) {
|
|
26
|
+
if (fs.existsSync(path.join(projectRoot, file))) return pm;
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isLocalDep(projectRoot) {
|
|
32
|
+
try {
|
|
33
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'));
|
|
34
|
+
return Boolean(
|
|
35
|
+
(pkg.dependencies && pkg.dependencies[PACKAGE_NAME]) ||
|
|
36
|
+
(pkg.devDependencies && pkg.devDependencies[PACKAGE_NAME])
|
|
37
|
+
);
|
|
38
|
+
} catch { return false; }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const UPDATE_ARGS = {
|
|
42
|
+
pnpm: ['update', PACKAGE_NAME],
|
|
43
|
+
npm: ['update', PACKAGE_NAME],
|
|
44
|
+
yarn: ['upgrade', PACKAGE_NAME],
|
|
45
|
+
bun: ['update', PACKAGE_NAME]
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Returns one of:
|
|
49
|
+
// { skipped: true, reason } — caller should fall through to in-process work
|
|
50
|
+
// { reExec: true, exitCode } — caller should return this exit code
|
|
51
|
+
// { error: true, message, exitCode } — caller should error and return
|
|
52
|
+
function runSelfUpdate(flags, log) {
|
|
53
|
+
if (flags.noSelfUpdate) return { skipped: true, reason: 'flag' };
|
|
54
|
+
if (flags.dryRun) return { skipped: true, reason: 'dry-run' };
|
|
55
|
+
|
|
56
|
+
const projectRoot = findProjectRoot();
|
|
57
|
+
if (!projectRoot) return { skipped: true, reason: 'no project root' };
|
|
58
|
+
if (!isLocalDep(projectRoot)) return { skipped: true, reason: 'not a local dep' };
|
|
59
|
+
const pm = detectPm(projectRoot);
|
|
60
|
+
if (!pm) return { skipped: true, reason: 'no lockfile' };
|
|
61
|
+
|
|
62
|
+
const localCli = path.join(projectRoot, 'node_modules', '@devshop', 'crew', 'scripts', 'cli.js');
|
|
63
|
+
if (!fs.existsSync(localCli)) return { skipped: true, reason: 'local CLI not present' };
|
|
64
|
+
|
|
65
|
+
log.action('self', `${pm} ${UPDATE_ARGS[pm].join(' ')}`);
|
|
66
|
+
const updateResult = spawnSync(pm, UPDATE_ARGS[pm], { cwd: projectRoot, stdio: 'inherit' });
|
|
67
|
+
if (updateResult.status !== 0) {
|
|
68
|
+
return { error: true, message: `${pm} update failed`, exitCode: 3 };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const passthrough = ['update', '--no-self-update'];
|
|
72
|
+
if (flags.global) passthrough.push('--global');
|
|
73
|
+
if (flags.force) passthrough.push('--force');
|
|
74
|
+
if (flags.yes) passthrough.push('--yes');
|
|
75
|
+
|
|
76
|
+
const child = spawnSync(process.execPath, [localCli, ...passthrough], {
|
|
77
|
+
cwd: process.cwd(),
|
|
78
|
+
stdio: 'inherit'
|
|
79
|
+
});
|
|
80
|
+
return { reExec: true, exitCode: child.status || 0 };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = { runSelfUpdate, findProjectRoot, detectPm, isLocalDep };
|