role-os 1.0.1 → 1.0.2
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
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.0.2
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Fix double-nested `.claude/.claude/` directory created by `roleos init` — `starter-pack/.claude/workflows/full-treatment.md` moved to `starter-pack/workflows/`
|
|
7
|
+
- Read VERSION from `package.json` at runtime instead of hardcoded constant — prevents version drift between CLI and package metadata
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- `roleos init --force` — update canonical scaffolded files while always protecting user-filled `context/` files
|
|
11
|
+
- 4 regression tests: no double-nesting, correct workflow placement, version sync, --force context protection
|
|
12
|
+
|
|
3
13
|
## 1.0.0
|
|
4
14
|
|
|
5
15
|
### Added
|
package/bin/roleos.mjs
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { join, dirname } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
3
6
|
import { initCommand } from "../src/init.mjs";
|
|
4
7
|
import { packetCommand } from "../src/packet.mjs";
|
|
5
8
|
import { routeCommand } from "../src/route.mjs";
|
|
6
9
|
import { reviewCommand } from "../src/review.mjs";
|
|
7
10
|
import { statusCommand } from "../src/status.mjs";
|
|
8
11
|
|
|
9
|
-
const
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const VERSION = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8")).version;
|
|
10
14
|
|
|
11
15
|
function printHelp() {
|
|
12
16
|
console.log(`
|
|
@@ -14,6 +18,7 @@ roleos v${VERSION} — Role OS bootstrap CLI
|
|
|
14
18
|
|
|
15
19
|
Usage:
|
|
16
20
|
roleos init Scaffold Role OS into .claude/
|
|
21
|
+
roleos init --force Update canonical files (protects context/)
|
|
17
22
|
roleos packet new <type> Create a new packet (feature|integration|identity)
|
|
18
23
|
roleos route <packet-file> Recommend the smallest valid chain
|
|
19
24
|
roleos review <packet-file> <verdict> Record a review verdict
|
package/package.json
CHANGED
package/src/fs-utils.mjs
CHANGED
|
@@ -3,11 +3,14 @@ import { join, dirname, relative } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Recursively copy a directory, skipping files that already exist at the target.
|
|
6
|
-
*
|
|
6
|
+
* With force=true, overwrites existing files (except those matching protectedPaths).
|
|
7
|
+
* Returns { created: string[], skipped: string[], updated: string[] } with relative paths.
|
|
7
8
|
*/
|
|
8
|
-
export function copyDirSafe(srcDir, destDir, baseDir =
|
|
9
|
+
export function copyDirSafe(srcDir, destDir, { baseDir, force = false, protectedPaths = [] } = {}) {
|
|
10
|
+
const root = baseDir || destDir;
|
|
9
11
|
const created = [];
|
|
10
12
|
const skipped = [];
|
|
13
|
+
const updated = [];
|
|
11
14
|
|
|
12
15
|
if (!existsSync(srcDir)) {
|
|
13
16
|
throw new Error(`Source directory does not exist: ${srcDir}`);
|
|
@@ -18,15 +21,23 @@ export function copyDirSafe(srcDir, destDir, baseDir = destDir) {
|
|
|
18
21
|
for (const entry of entries) {
|
|
19
22
|
const srcPath = join(srcDir, entry.name);
|
|
20
23
|
const destPath = join(destDir, entry.name);
|
|
21
|
-
const relPath = relative(
|
|
24
|
+
const relPath = relative(root, destPath);
|
|
22
25
|
|
|
23
26
|
if (entry.isDirectory()) {
|
|
24
|
-
const sub = copyDirSafe(srcPath, destPath, baseDir);
|
|
27
|
+
const sub = copyDirSafe(srcPath, destPath, { baseDir: root, force, protectedPaths });
|
|
25
28
|
created.push(...sub.created);
|
|
26
29
|
skipped.push(...sub.skipped);
|
|
30
|
+
updated.push(...sub.updated);
|
|
27
31
|
} else {
|
|
28
32
|
if (existsSync(destPath)) {
|
|
29
|
-
|
|
33
|
+
const normalizedRel = relPath.replace(/\\/g, "/");
|
|
34
|
+
if (force && !protectedPaths.some(p => normalizedRel.startsWith(p))) {
|
|
35
|
+
mkdirSync(dirname(destPath), { recursive: true });
|
|
36
|
+
copyFileSync(srcPath, destPath);
|
|
37
|
+
updated.push(relPath);
|
|
38
|
+
} else {
|
|
39
|
+
skipped.push(relPath);
|
|
40
|
+
}
|
|
30
41
|
} else {
|
|
31
42
|
mkdirSync(dirname(destPath), { recursive: true });
|
|
32
43
|
copyFileSync(srcPath, destPath);
|
|
@@ -35,7 +46,7 @@ export function copyDirSafe(srcDir, destDir, baseDir = destDir) {
|
|
|
35
46
|
}
|
|
36
47
|
}
|
|
37
48
|
|
|
38
|
-
return { created, skipped };
|
|
49
|
+
return { created, skipped, updated };
|
|
39
50
|
}
|
|
40
51
|
|
|
41
52
|
/**
|
package/src/init.mjs
CHANGED
|
@@ -5,13 +5,23 @@ import { copyDirSafe } from "./fs-utils.mjs";
|
|
|
5
5
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
6
|
const STARTER_PACK_DIR = join(__dirname, "..", "starter-pack");
|
|
7
7
|
|
|
8
|
+
// Paths that --force must never overwrite (user-filled content)
|
|
9
|
+
const PROTECTED_PATHS = [
|
|
10
|
+
"context/",
|
|
11
|
+
];
|
|
12
|
+
|
|
8
13
|
export async function initCommand(args) {
|
|
9
|
-
const
|
|
14
|
+
const force = args.includes("--force");
|
|
15
|
+
const positional = args.filter(a => !a.startsWith("--"));
|
|
16
|
+
const targetDir = positional[0] || ".";
|
|
10
17
|
const claudeDir = join(targetDir, ".claude");
|
|
11
18
|
|
|
12
|
-
console.log(
|
|
19
|
+
console.log(`roleos init${force ? " --force" : ""} — scaffolding Role OS into .claude/\n`);
|
|
13
20
|
|
|
14
|
-
const { created, skipped } = copyDirSafe(STARTER_PACK_DIR, claudeDir
|
|
21
|
+
const { created, skipped, updated } = copyDirSafe(STARTER_PACK_DIR, claudeDir, {
|
|
22
|
+
force,
|
|
23
|
+
protectedPaths: PROTECTED_PATHS,
|
|
24
|
+
});
|
|
15
25
|
|
|
16
26
|
if (created.length > 0) {
|
|
17
27
|
console.log(`Created (${created.length}):`);
|
|
@@ -20,6 +30,13 @@ export async function initCommand(args) {
|
|
|
20
30
|
}
|
|
21
31
|
}
|
|
22
32
|
|
|
33
|
+
if (updated.length > 0) {
|
|
34
|
+
console.log(`\nUpdated (${updated.length}):`);
|
|
35
|
+
for (const f of updated) {
|
|
36
|
+
console.log(` ~ ${f}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
23
40
|
if (skipped.length > 0) {
|
|
24
41
|
console.log(`\nSkipped (${skipped.length} — already exist):`);
|
|
25
42
|
for (const f of skipped) {
|
|
@@ -27,8 +44,11 @@ export async function initCommand(args) {
|
|
|
27
44
|
}
|
|
28
45
|
}
|
|
29
46
|
|
|
30
|
-
if (created.length === 0 && skipped.length > 0) {
|
|
47
|
+
if (created.length === 0 && updated.length === 0 && skipped.length > 0) {
|
|
31
48
|
console.log("\nRole OS is already scaffolded. No files were overwritten.");
|
|
49
|
+
if (!force) {
|
|
50
|
+
console.log("Use --force to update canonical files (context/ files are always protected).");
|
|
51
|
+
}
|
|
32
52
|
} else {
|
|
33
53
|
console.log(`\nDone. Fill the context/ files for your project, then run:`);
|
|
34
54
|
console.log(` roleos packet new feature`);
|
|
File without changes
|