@series-inc/rundot-3d-engine 0.4.0 → 0.4.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/package.json CHANGED
@@ -1,12 +1,11 @@
1
1
  {
2
2
  "name": "@series-inc/rundot-3d-engine",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "scripts": {
7
7
  "build": "tsup",
8
8
  "dev": "tsup --watch",
9
- "publish": "npm publish",
10
9
  "lint": "eslint src",
11
10
  "lint:fix": "eslint src --fix",
12
11
  "format": "prettier --write .",
@@ -0,0 +1,49 @@
1
+ import { readdirSync, statSync, writeFileSync } from "fs";
2
+ import { join, relative, resolve, sep } from "path";
3
+
4
+ export function genDocsIndex(
5
+ label = "Docs Index",
6
+ root = "docs",
7
+ srcDir = resolve(import.meta.dirname, "..", "docs"),
8
+ outPath = resolve(import.meta.dirname, "..", "docs-index.txt"),
9
+ ) {
10
+ const src = resolve(srcDir);
11
+ const dest = resolve(outPath);
12
+
13
+ function walk(dir) {
14
+ const entries = [];
15
+ for (const e of readdirSync(dir)) {
16
+ const p = join(dir, e);
17
+ if (statSync(p).isDirectory()) entries.push(...walk(p));
18
+ else if (e.endsWith(".md")) entries.push(relative(src, p).replaceAll(sep, "/"));
19
+ }
20
+ return entries.sort();
21
+ }
22
+
23
+ const files = walk(src);
24
+
25
+ // Build compact tree: group by directory
26
+ const tree = {};
27
+ for (const f of files) {
28
+ const i = f.lastIndexOf("/");
29
+ const dir = i === -1 ? "." : f.slice(0, i);
30
+ const name = f.slice(i + 1);
31
+ (tree[dir] ??= []).push(name);
32
+ }
33
+
34
+ let out = `[${label}]|root:${root}`;
35
+ for (const [dir, names] of Object.entries(tree)) {
36
+ out += `|${dir}:{${names.join(",")}}`;
37
+ }
38
+ out += "\n";
39
+
40
+ writeFileSync(dest, out);
41
+ return files.length;
42
+ }
43
+
44
+ // CLI usage: node gen-docs-index.mjs [label] [root] [srcDir] [outPath]
45
+ if (process.argv[1]?.endsWith("gen-docs-index.mjs")) {
46
+ const args = process.argv.slice(2);
47
+ const count = genDocsIndex(args[0], args[1], args[2], args[3]);
48
+ console.log(`Wrote ${count} files to docs-index.txt`);
49
+ }
@@ -10,19 +10,36 @@ if (!existsSync(docsSource)) {
10
10
  process.exit(0);
11
11
  }
12
12
 
13
- // Walk up from the package root to find the consuming project root.
14
- // If we're inside node_modules, exit out of it. Otherwise (e.g. local dev), use packageRoot.
13
+ // Find the project root where docs and .gitignore should live.
14
+ // 1. INIT_CWD set by npm/yarn/pnpm to where `npm install` was invoked (best for monorepos)
15
+ // 2. Walk up to find .git directory (reliable fallback)
16
+ // 3. Walk up to exit node_modules (legacy fallback)
17
+ // 4. Local dev — packageRoot itself
15
18
  function findProjectRoot() {
19
+ // INIT_CWD is the directory where the user ran `npm install`
20
+ if (process.env.INIT_CWD && existsSync(process.env.INIT_CWD)) {
21
+ return resolve(process.env.INIT_CWD);
22
+ }
23
+
24
+ // Walk up looking for .git (repo root)
16
25
  let dir = packageRoot;
17
26
  while (true) {
27
+ if (existsSync(join(dir, ".git"))) return dir;
18
28
  const parent = dirname(dir);
19
29
  if (parent === dir) break; // filesystem root
30
+ dir = parent;
31
+ }
32
+
33
+ // Legacy: walk up to exit node_modules
34
+ dir = packageRoot;
35
+ while (true) {
36
+ const parent = dirname(dir);
37
+ if (parent === dir) break;
20
38
  const baseName = dir.split(/[\\/]/).pop();
21
- if (baseName === "node_modules") {
22
- return parent;
23
- }
39
+ if (baseName === "node_modules") return parent;
24
40
  dir = parent;
25
41
  }
42
+
26
43
  // Not inside node_modules — running locally in the package itself
27
44
  return packageRoot;
28
45
  }
@@ -61,3 +78,11 @@ if (existsSync(gitignorePath)) {
61
78
  appendFileSync(gitignorePath, (needsNewline ? "\n" : "") + entry + "\n");
62
79
  }
63
80
  }
81
+
82
+ // Generate docs index from the copied docs and splice into the consumer's AGENTS.md/CLAUDE.md
83
+ const { genDocsIndex } = await import("./gen-docs-index.mjs");
84
+ const { updateDocsIndex } = await import("./update-docs-index.mjs");
85
+
86
+ const indexPath = join(projectRoot, "docs-index.txt");
87
+ genDocsIndex("Run.3DEngine Docs Index", ".rundot/3d-engine-docs", docsDest, indexPath);
88
+ updateDocsIndex(projectRoot);
@@ -0,0 +1,44 @@
1
+ import { existsSync, readFileSync, unlinkSync, writeFileSync } from "fs";
2
+ import { resolve } from "path";
3
+
4
+ export function updateDocsIndex(cwd = resolve(import.meta.dirname, "..")) {
5
+ const indexPath = resolve(cwd, "docs-index.txt");
6
+ if (!existsSync(indexPath)) {
7
+ console.warn("docs-index.txt not found, skipping update");
8
+ return;
9
+ }
10
+
11
+ const fresh = readFileSync(indexPath, "utf-8").trimEnd();
12
+ const label = "Docs Index";
13
+ const escaped = label.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14
+ const re = new RegExp(`\\[${escaped}\\][^\\n]*(?:\\n(?![\\[<])[^\\n]*)*`);
15
+
16
+ const targets = ["AGENTS.md", "CLAUDE.md"];
17
+
18
+ for (const file of targets) {
19
+ const filePath = resolve(cwd, file);
20
+ if (!existsSync(filePath)) {
21
+ console.log(`${file} not found, skipping`);
22
+ continue;
23
+ }
24
+
25
+ let content = readFileSync(filePath, "utf-8");
26
+
27
+ if (re.test(content)) {
28
+ content = content.replace(re, fresh);
29
+ } else {
30
+ content = content.trimEnd() + "\n" + fresh + "\n";
31
+ }
32
+
33
+ writeFileSync(filePath, content);
34
+ console.log(`Updated ${file}`);
35
+ }
36
+
37
+ unlinkSync(indexPath);
38
+ console.log("Deleted docs-index.txt");
39
+ }
40
+
41
+ // CLI usage
42
+ if (process.argv[1]?.endsWith("update-docs-index.mjs")) {
43
+ updateDocsIndex();
44
+ }