rvlib-shipkit 0.8.0 → 0.9.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 +12 -0
- package/package.json +1 -1
- package/src/cli/cmd_Start.ts +28 -0
- package/src/cli.ts +2 -0
- package/src/start-repo.ts +50 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.9.0 — 2026-07-22
|
|
4
|
+
|
|
5
|
+
New:
|
|
6
|
+
|
|
7
|
+
- `shipkit start <name>`: ensure `<root>/<name>` exists (`--root`, default
|
|
8
|
+
`~/dev`) and, with `--include <line>`, that CLAUDE.md starts with that
|
|
9
|
+
include (`--also-ok <line>` variants count as already wired and are never
|
|
10
|
+
double-added; `--skip <name>` repos never get the include). Stdout is
|
|
11
|
+
ONLY the resolved dir: a sourced shell alias can
|
|
12
|
+
`cd "$(shipkit start <name> …)"`, the one thing a child process can't do
|
|
13
|
+
for its parent shell.
|
|
14
|
+
|
|
3
15
|
## 0.8.0 — 2026-07-22
|
|
4
16
|
|
|
5
17
|
New:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rvlib-shipkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "All-in-one repo companion: ci gate (task DAG), auto-discovered health checks, release-contract fulfillness, asks queue, md-wiki tools, asset atelier, live tui",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// shipkit start — ensure <root>/<name> exists (+ a CLAUDE.md include),
|
|
2
|
+
// printing the dir on stdout so a shell alias can cd there (a child process
|
|
3
|
+
// can't cd its parent). Thin clipanion surface; logic in start-repo.ts.
|
|
4
|
+
import { Command, Option } from 'clipanion'
|
|
5
|
+
import { startRepo } from '../start-repo.ts'
|
|
6
|
+
|
|
7
|
+
export class StartCommand extends Command {
|
|
8
|
+
static override paths = [['start']]
|
|
9
|
+
|
|
10
|
+
static override usage = Command.Usage({
|
|
11
|
+
category: 'Setup',
|
|
12
|
+
description: 'ensure <root>/<name> exists, optionally with a CLAUDE.md include line; prints the dir on stdout so a shell alias can cd there',
|
|
13
|
+
examples: [
|
|
14
|
+
['start (or enter) a repo', 'shipkit start pong'],
|
|
15
|
+
['seed CLAUDE.md line 1', "shipkit start pong --include '@~/wiki/index.md'"],
|
|
16
|
+
],
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
name = Option.String()
|
|
20
|
+
root = Option.String('--root', { description: 'parent dir for repos (default ~/dev)' })
|
|
21
|
+
include = Option.String('--include', { description: 'line CLAUDE.md must start with; omitted = only ensure the dir' })
|
|
22
|
+
alsoOk = Option.Array('--also-ok', { description: 'alternative lines that already count as wired — never double-add' })
|
|
23
|
+
skip = Option.Array('--skip', { description: 'repo names that never get the include' })
|
|
24
|
+
|
|
25
|
+
async execute(): Promise<number> {
|
|
26
|
+
return startRepo(this.name, { root: this.root, include: this.include, alsoOk: this.alsoOk, skip: this.skip })
|
|
27
|
+
}
|
|
28
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { ContractsCommand } from './cli/cmd_Contracts.ts'
|
|
|
12
12
|
import { InitCommand } from './cli/cmd_Init.ts'
|
|
13
13
|
import { MvCommand } from './cli/cmd_Mv.ts'
|
|
14
14
|
import { ServeCommand } from './cli/cmd_Serve.ts'
|
|
15
|
+
import { StartCommand } from './cli/cmd_Start.ts'
|
|
15
16
|
import { TuiCommand } from './cli/cmd_Tui.ts'
|
|
16
17
|
import { WikiLintCommand, WikiServeCommand } from './cli/cmd_Wiki.ts'
|
|
17
18
|
|
|
@@ -45,5 +46,6 @@ cli.register(AssetsGenCommand)
|
|
|
45
46
|
cli.register(AssetsIdentityCommand)
|
|
46
47
|
cli.register(AssetsCheckCommand)
|
|
47
48
|
cli.register(InitCommand)
|
|
49
|
+
cli.register(StartCommand)
|
|
48
50
|
|
|
49
51
|
cli.runExit(process.argv.slice(2))
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// `shipkit start` — ensure <root>/<name> exists and, when --include is
|
|
2
|
+
// given, that CLAUDE.md carries that include line. Plain module behind
|
|
3
|
+
// cmd_Start. Status lines go to STDERR — the ONLY stdout output is the
|
|
4
|
+
// resolved dir, so a sourced shell alias can `cd "$(shipkit start <name>)"`
|
|
5
|
+
// (the one thing a child process can't do for its parent shell).
|
|
6
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
|
|
7
|
+
import { homedir } from 'os'
|
|
8
|
+
import { join } from 'path'
|
|
9
|
+
|
|
10
|
+
export type StartOpts = {
|
|
11
|
+
/** parent dir for repos — default ~/dev */
|
|
12
|
+
root?: string
|
|
13
|
+
/** line CLAUDE.md must start with; omitted = only ensure the dir */
|
|
14
|
+
include?: string
|
|
15
|
+
/** alternative lines that already count as wired — never double-add */
|
|
16
|
+
alsoOk?: string[]
|
|
17
|
+
/** repo names that never get the include */
|
|
18
|
+
skip?: string[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const escapeRe = (s: string): string => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
22
|
+
|
|
23
|
+
export function startRepo(name: string, opts: StartOpts = {}): number {
|
|
24
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name)) {
|
|
25
|
+
console.error(`FAIL: bad repo name '${name}' — letters/digits/._- only, no leading dot, no slashes`)
|
|
26
|
+
return 2
|
|
27
|
+
}
|
|
28
|
+
const dir = join(opts.root ?? join(homedir(), 'dev'), name)
|
|
29
|
+
if (!existsSync(dir)) {
|
|
30
|
+
mkdirSync(dir, { recursive: true })
|
|
31
|
+
console.error(`ok: created ${dir}`)
|
|
32
|
+
}
|
|
33
|
+
if (opts.include == null || (opts.skip ?? []).includes(name)) {
|
|
34
|
+
if (opts.include != null) console.error(`skip: ${name} is include-exempt — CLAUDE.md untouched`)
|
|
35
|
+
} else {
|
|
36
|
+
const claudeMd = join(dir, 'CLAUDE.md')
|
|
37
|
+
if (!existsSync(claudeMd)) {
|
|
38
|
+
writeFileSync(claudeMd, `${opts.include}\n`)
|
|
39
|
+
console.error('ok: CLAUDE.md created with the include')
|
|
40
|
+
} else {
|
|
41
|
+
const wiredRe = new RegExp([opts.include, ...(opts.alsoOk ?? [])].map((l) => `^${escapeRe(l)}$`).join('|'), 'm')
|
|
42
|
+
if (!wiredRe.test(readFileSync(claudeMd, 'utf8'))) {
|
|
43
|
+
writeFileSync(claudeMd, `${opts.include}\n\n${readFileSync(claudeMd, 'utf8')}`)
|
|
44
|
+
console.error('ok: include prepended to CLAUDE.md')
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
console.log(dir)
|
|
49
|
+
return 0
|
|
50
|
+
}
|