@telelabsai/ship 1.0.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/.claude/agents/git-ops.md +41 -0
- package/.claude/hooks/git-safety.cjs +66 -0
- package/.claude/rules/git-conventions.md +17 -0
- package/.claude/settings.json +16 -0
- package/.claude/skills/ship-version/SKILL.md +118 -0
- package/.claude/skills/ship-version/references/commit-standards.md +97 -0
- package/.claude/skills/ship-version/references/safety-protocols.md +41 -0
- package/.claude/skills/ship-version/references/workflow-changelog.md +95 -0
- package/.claude/skills/ship-version/references/workflow-commit.md +77 -0
- package/.claude/skills/ship-version/references/workflow-diff.md +74 -0
- package/.claude/skills/ship-version/references/workflow-pr.md +46 -0
- package/.claude/skills/ship-version/references/workflow-push.md +35 -0
- package/CLAUDE.md +17 -0
- package/README.md +239 -0
- package/cli/__tests__/cli.test.js +155 -0
- package/cli/bin.js +102 -0
- package/cli/commands/init.js +49 -0
- package/cli/commands/new.js +18 -0
- package/cli/commands/update.js +119 -0
- package/package.json +40 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
const { resolve, join, relative } = require('path');
|
|
2
|
+
const { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, statSync, cpSync } = require('fs');
|
|
3
|
+
const { createInterface } = require('readline');
|
|
4
|
+
|
|
5
|
+
const PKG_ROOT = resolve(__dirname, '..', '..');
|
|
6
|
+
const SOURCE_DIR = join(PKG_ROOT, '.claude');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Recursively collects all file paths under a directory.
|
|
10
|
+
* @param {string} dir - Directory to scan
|
|
11
|
+
* @param {string} base - Base path for relative paths
|
|
12
|
+
* @returns {string[]} Array of relative file paths
|
|
13
|
+
*/
|
|
14
|
+
function collectFiles(dir, base) {
|
|
15
|
+
const files = [];
|
|
16
|
+
if (!existsSync(dir)) return files;
|
|
17
|
+
|
|
18
|
+
for (const entry of readdirSync(dir)) {
|
|
19
|
+
const full = join(dir, entry);
|
|
20
|
+
if (statSync(full).isDirectory()) {
|
|
21
|
+
files.push(...collectFiles(full, base));
|
|
22
|
+
} else {
|
|
23
|
+
files.push(relative(base, full));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return files;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Prompts the user with a yes/no question via stdin.
|
|
31
|
+
* @param {string} question - The question to ask
|
|
32
|
+
* @returns {Promise<boolean>}
|
|
33
|
+
*/
|
|
34
|
+
function ask(question) {
|
|
35
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
36
|
+
return new Promise((res) => {
|
|
37
|
+
rl.question(question, (answer) => {
|
|
38
|
+
rl.close();
|
|
39
|
+
res(answer.toLowerCase().startsWith('y'));
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Updates .claude/ in the target directory to match the latest Ship version.
|
|
46
|
+
* - New files: added automatically
|
|
47
|
+
* - Unchanged files: updated automatically
|
|
48
|
+
* - Modified files: asks user before overwriting (unless --force)
|
|
49
|
+
*
|
|
50
|
+
* @param {string} targetDir - Project directory to update
|
|
51
|
+
* @param {boolean} force - Skip confirmation prompts
|
|
52
|
+
*/
|
|
53
|
+
module.exports = async function update(targetDir, force) {
|
|
54
|
+
const targetClaude = join(targetDir, '.claude');
|
|
55
|
+
|
|
56
|
+
if (!existsSync(targetClaude)) {
|
|
57
|
+
console.error(' No .claude/ directory found. Run "ship init" first.');
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const sourceFiles = collectFiles(SOURCE_DIR, SOURCE_DIR);
|
|
62
|
+
const added = [];
|
|
63
|
+
const updated = [];
|
|
64
|
+
const skipped = [];
|
|
65
|
+
|
|
66
|
+
for (const relPath of sourceFiles) {
|
|
67
|
+
const sourcePath = join(SOURCE_DIR, relPath);
|
|
68
|
+
const targetPath = join(targetClaude, relPath);
|
|
69
|
+
const sourceContent = readFileSync(sourcePath);
|
|
70
|
+
|
|
71
|
+
if (!existsSync(targetPath)) {
|
|
72
|
+
// New file — add it
|
|
73
|
+
const dir = join(targetPath, '..');
|
|
74
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
75
|
+
writeFileSync(targetPath, sourceContent);
|
|
76
|
+
added.push(relPath);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const targetContent = readFileSync(targetPath);
|
|
81
|
+
|
|
82
|
+
// Files are identical — skip
|
|
83
|
+
if (sourceContent.equals(targetContent)) continue;
|
|
84
|
+
|
|
85
|
+
if (force) {
|
|
86
|
+
// Force mode — overwrite without asking
|
|
87
|
+
writeFileSync(targetPath, sourceContent);
|
|
88
|
+
updated.push(relPath);
|
|
89
|
+
} else {
|
|
90
|
+
// File was modified locally — ask user
|
|
91
|
+
const yes = await ask(` Overwrite ${relPath}? (y/n) `);
|
|
92
|
+
if (yes) {
|
|
93
|
+
writeFileSync(targetPath, sourceContent);
|
|
94
|
+
updated.push(relPath);
|
|
95
|
+
} else {
|
|
96
|
+
skipped.push(relPath);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Summary
|
|
102
|
+
console.log('');
|
|
103
|
+
if (added.length) {
|
|
104
|
+
console.log(' Added:');
|
|
105
|
+
added.forEach((f) => console.log(` ${f}`));
|
|
106
|
+
}
|
|
107
|
+
if (updated.length) {
|
|
108
|
+
console.log(' Updated:');
|
|
109
|
+
updated.forEach((f) => console.log(` ${f}`));
|
|
110
|
+
}
|
|
111
|
+
if (skipped.length) {
|
|
112
|
+
console.log(' Skipped (locally modified):');
|
|
113
|
+
skipped.forEach((f) => console.log(` ${f}`));
|
|
114
|
+
}
|
|
115
|
+
if (!added.length && !updated.length && !skipped.length) {
|
|
116
|
+
console.log(' Already up to date.');
|
|
117
|
+
}
|
|
118
|
+
console.log('');
|
|
119
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@telelabsai/ship",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI-powered development toolkit. Ship code faster with pre-configured agents, skills, rules, and hooks for Claude Code.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"ship": "cli/bin.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"cli/",
|
|
10
|
+
".claude/",
|
|
11
|
+
"CLAUDE.md",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"claude-code",
|
|
17
|
+
"ai-agents",
|
|
18
|
+
"development-toolkit",
|
|
19
|
+
"cli",
|
|
20
|
+
"devtools",
|
|
21
|
+
"automation",
|
|
22
|
+
"conventional-commits"
|
|
23
|
+
],
|
|
24
|
+
"author": "TeleLabs AI <tele@telelabs.ai> (https://telelabs.ai)",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/telelabs-ai/ship.git"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/telelabs-ai/ship/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://telelabs.ai",
|
|
34
|
+
"scripts": {
|
|
35
|
+
"test": "node --test cli/__tests__/*.test.js"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=18.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|