Compare commits

...

2 Commits

Author SHA1 Message Date
Dotta
ab3b9ab19f chore: bump all packages to 0.2.4
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:10:52 -06:00
Dotta
f4a5b00116 fix: bundle skills directory into npm packages for runtime discovery
The claude-local, codex-local adapters and the server all resolve a
skills/ directory using __dirname-relative paths that only work inside
the monorepo.  When installed from npm the paths point outside the
package and cause ENOENT on readdir/readFile.

- Update both adapter execute.ts files to try a published-path
  candidate (../../skills from dist/) before falling back to the
  monorepo dev path (../../../../../skills from src/).
- Update server readSkillMarkdown() to try the published path first.
- Add "skills" to the files array in server, claude-local, and
  codex-local package.json so npm includes them.
- Update release.sh to copy the repo-root skills/ into each package
  before publish, and clean up after.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:06:12 -06:00
21 changed files with 135 additions and 32 deletions

View File

@@ -1,5 +1,19 @@
# paperclipai
## 0.2.4
### Patch Changes
- Version bump (patch)
- Updated dependencies
- @paperclipai/shared@0.2.4
- @paperclipai/adapter-utils@0.2.4
- @paperclipai/db@0.2.4
- @paperclipai/adapter-claude-local@0.2.4
- @paperclipai/adapter-codex-local@0.2.4
- @paperclipai/adapter-openclaw@0.2.4
- @paperclipai/server@0.2.4
## 0.2.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "paperclipai",
"version": "0.2.3",
"version": "0.2.4",
"description": "Paperclip CLI — orchestrate AI agent teams to run a business",
"type": "module",
"bin": {

View File

@@ -23,7 +23,7 @@ const DATA_DIR_OPTION_HELP =
program
.name("paperclipai")
.description("Paperclip CLI — setup, diagnose, and configure your instance")
.version("0.2.3");
.version("0.2.4");
program.hook("preAction", (_thisCommand, actionCommand) => {
const options = actionCommand.optsWithGlobals() as DataDirOptionLike;

View File

@@ -1,5 +1,11 @@
# @paperclipai/adapter-utils
## 0.2.4
### Patch Changes
- Version bump (patch)
## 0.2.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@paperclipai/adapter-utils",
"version": "0.2.3",
"version": "0.2.4",
"type": "module",
"exports": {
".": "./src/index.ts",

View File

@@ -1,5 +1,13 @@
# @paperclipai/adapter-claude-local
## 0.2.4
### Patch Changes
- Version bump (patch)
- Updated dependencies
- @paperclipai/adapter-utils@0.2.4
## 0.2.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@paperclipai/adapter-claude-local",
"version": "0.2.3",
"version": "0.2.4",
"type": "module",
"exports": {
".": "./src/index.ts",
@@ -32,7 +32,8 @@
"types": "./dist/index.d.ts"
},
"files": [
"dist"
"dist",
"skills"
],
"scripts": {
"build": "tsc",

View File

@@ -27,10 +27,19 @@ import {
isClaudeUnknownSessionError,
} from "./parse.js";
const PAPERCLIP_SKILLS_DIR = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"../../../../../skills",
);
const __moduleDir = path.dirname(fileURLToPath(import.meta.url));
const PAPERCLIP_SKILLS_CANDIDATES = [
path.resolve(__moduleDir, "../../skills"), // published: <pkg>/dist/server/ -> <pkg>/skills/
path.resolve(__moduleDir, "../../../../../skills"), // dev: src/server/ -> repo root/skills/
];
async function resolvePaperclipSkillsDir(): Promise<string | null> {
for (const candidate of PAPERCLIP_SKILLS_CANDIDATES) {
const isDir = await fs.stat(candidate).then((s) => s.isDirectory()).catch(() => false);
if (isDir) return candidate;
}
return null;
}
/**
* Create a tmpdir with `.claude/skills/` containing symlinks to skills from
@@ -41,11 +50,13 @@ async function buildSkillsDir(): Promise<string> {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-skills-"));
const target = path.join(tmp, ".claude", "skills");
await fs.mkdir(target, { recursive: true });
const entries = await fs.readdir(PAPERCLIP_SKILLS_DIR, { withFileTypes: true });
const skillsDir = await resolvePaperclipSkillsDir();
if (!skillsDir) return tmp;
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
await fs.symlink(
path.join(PAPERCLIP_SKILLS_DIR, entry.name),
path.join(skillsDir, entry.name),
path.join(target, entry.name),
);
}

View File

@@ -1,5 +1,13 @@
# @paperclipai/adapter-codex-local
## 0.2.4
### Patch Changes
- Version bump (patch)
- Updated dependencies
- @paperclipai/adapter-utils@0.2.4
## 0.2.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@paperclipai/adapter-codex-local",
"version": "0.2.3",
"version": "0.2.4",
"type": "module",
"exports": {
".": "./src/index.ts",
@@ -32,7 +32,8 @@
"types": "./dist/index.d.ts"
},
"files": [
"dist"
"dist",
"skills"
],
"scripts": {
"build": "tsc",

View File

@@ -19,10 +19,11 @@ import {
} from "@paperclipai/adapter-utils/server-utils";
import { parseCodexJsonl, isCodexUnknownSessionError } from "./parse.js";
const PAPERCLIP_SKILLS_DIR = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"../../../../../skills",
);
const __moduleDir = path.dirname(fileURLToPath(import.meta.url));
const PAPERCLIP_SKILLS_CANDIDATES = [
path.resolve(__moduleDir, "../../skills"), // published: <pkg>/dist/server/ -> <pkg>/skills/
path.resolve(__moduleDir, "../../../../../skills"), // dev: src/server/ -> repo root/skills/
];
const CODEX_ROLLOUT_NOISE_RE =
/^\d{4}-\d{2}-\d{2}T[^\s]+\s+ERROR\s+codex_core::rollout::list:\s+state db missing rollout path for thread\s+[a-z0-9-]+$/i;
@@ -66,19 +67,24 @@ function codexHomeDir(): string {
return path.join(os.homedir(), ".codex");
}
async function resolvePaperclipSkillsDir(): Promise<string | null> {
for (const candidate of PAPERCLIP_SKILLS_CANDIDATES) {
const isDir = await fs.stat(candidate).then((s) => s.isDirectory()).catch(() => false);
if (isDir) return candidate;
}
return null;
}
async function ensureCodexSkillsInjected(onLog: AdapterExecutionContext["onLog"]) {
const sourceExists = await fs
.stat(PAPERCLIP_SKILLS_DIR)
.then((stats) => stats.isDirectory())
.catch(() => false);
if (!sourceExists) return;
const skillsDir = await resolvePaperclipSkillsDir();
if (!skillsDir) return;
const skillsHome = path.join(codexHomeDir(), "skills");
await fs.mkdir(skillsHome, { recursive: true });
const entries = await fs.readdir(PAPERCLIP_SKILLS_DIR, { withFileTypes: true });
const entries = await fs.readdir(skillsDir, { withFileTypes: true });
for (const entry of entries) {
if (!entry.isDirectory()) continue;
const source = path.join(PAPERCLIP_SKILLS_DIR, entry.name);
const source = path.join(skillsDir, entry.name);
const target = path.join(skillsHome, entry.name);
const existing = await fs.lstat(target).catch(() => null);
if (existing) continue;

View File

@@ -1,5 +1,13 @@
# @paperclipai/adapter-openclaw
## 0.2.4
### Patch Changes
- Version bump (patch)
- Updated dependencies
- @paperclipai/adapter-utils@0.2.4
## 0.2.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@paperclipai/adapter-openclaw",
"version": "0.2.3",
"version": "0.2.4",
"type": "module",
"exports": {
".": "./src/index.ts",

View File

@@ -1,5 +1,13 @@
# @paperclipai/db
## 0.2.4
### Patch Changes
- Version bump (patch)
- Updated dependencies
- @paperclipai/shared@0.2.4
## 0.2.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@paperclipai/db",
"version": "0.2.3",
"version": "0.2.4",
"type": "module",
"exports": {
".": "./src/index.ts",

View File

@@ -1,5 +1,11 @@
# @paperclipai/shared
## 0.2.4
### Patch Changes
- Version bump (patch)
## 0.2.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@paperclipai/shared",
"version": "0.2.3",
"version": "0.2.4",
"type": "module",
"exports": {
".": "./src/index.ts",

View File

@@ -138,7 +138,13 @@ pnpm --filter @paperclipai/server build
pnpm --filter @paperclipai/ui build
rm -rf "$REPO_ROOT/server/ui-dist"
cp -r "$REPO_ROOT/ui/dist" "$REPO_ROOT/server/ui-dist"
echo " ✓ All packages built (including UI)"
# Bundle skills into packages that need them (adapters + server)
for pkg_dir in server packages/adapters/claude-local packages/adapters/codex-local; do
rm -rf "$REPO_ROOT/$pkg_dir/skills"
cp -r "$REPO_ROOT/skills" "$REPO_ROOT/$pkg_dir/skills"
done
echo " ✓ All packages built (including UI + skills)"
# ── Step 5: Build CLI bundle ─────────────────────────────────────────────────
@@ -191,6 +197,11 @@ fi
# Remove UI dist bundled into server for publishing
rm -rf "$REPO_ROOT/server/ui-dist"
# Remove skills bundled into packages for publishing
for pkg_dir in server packages/adapters/claude-local packages/adapters/codex-local; do
rm -rf "$REPO_ROOT/$pkg_dir/skills"
done
# Stage only release-related files (avoid sweeping unrelated changes with -A)
git add \
.changeset/ \

View File

@@ -1,5 +1,18 @@
# @paperclipai/server
## 0.2.4
### Patch Changes
- Version bump (patch)
- Updated dependencies
- @paperclipai/shared@0.2.4
- @paperclipai/adapter-utils@0.2.4
- @paperclipai/db@0.2.4
- @paperclipai/adapter-claude-local@0.2.4
- @paperclipai/adapter-codex-local@0.2.4
- @paperclipai/adapter-openclaw@0.2.4
## 0.2.3
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "@paperclipai/server",
"version": "0.2.3",
"version": "0.2.4",
"type": "module",
"exports": {
".": "./src/index.ts"
@@ -18,7 +18,8 @@
},
"files": [
"dist",
"ui-dist"
"ui-dist",
"skills"
],
"scripts": {
"dev": "tsx src/index.ts",

View File

@@ -59,8 +59,9 @@ function readSkillMarkdown(skillName: string): string | null {
if (normalized !== "paperclip" && normalized !== "paperclip-create-agent") return null;
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
const candidates = [
path.resolve(process.cwd(), "skills", normalized, "SKILL.md"),
path.resolve(moduleDir, "../../../skills", normalized, "SKILL.md"),
path.resolve(moduleDir, "../../skills", normalized, "SKILL.md"), // published: dist/routes/ -> <pkg>/skills/
path.resolve(process.cwd(), "skills", normalized, "SKILL.md"), // cwd (e.g. monorepo root)
path.resolve(moduleDir, "../../../skills", normalized, "SKILL.md"), // dev: src/routes/ -> repo root/skills/
];
for (const skillPath of candidates) {
try {