dev-wip-package-refer

Pattern for consuming an in-progress (WIP) upstream npm/pnpm package from a sibling git checkout via a `file:../{name}/...` relative dep — without publishing the package. Use when: (1) Setting up a consumer project that needs to depend on a local in-development library or framework checked out next to it, (2) User mentions 'file: dep', 'sibling repo', 'upstream package', 'wip package', 'monorepo-style refer', 'how do we consume the upstream', (3) Deciding between this pattern and a published npm version or a `github:` git-URL dep, (4) Setting up a fresh machine that already has a consumer project but the sibling upstream isn't cloned yet, (5) A consumer's CI is failing because the sibling upstream isn't where the `file:` spec expects it.

6 stars

Best use case

dev-wip-package-refer is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Pattern for consuming an in-progress (WIP) upstream npm/pnpm package from a sibling git checkout via a `file:../{name}/...` relative dep — without publishing the package. Use when: (1) Setting up a consumer project that needs to depend on a local in-development library or framework checked out next to it, (2) User mentions 'file: dep', 'sibling repo', 'upstream package', 'wip package', 'monorepo-style refer', 'how do we consume the upstream', (3) Deciding between this pattern and a published npm version or a `github:` git-URL dep, (4) Setting up a fresh machine that already has a consumer project but the sibling upstream isn't cloned yet, (5) A consumer's CI is failing because the sibling upstream isn't where the `file:` spec expects it.

Teams using dev-wip-package-refer should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/dev-wip-package-refer/SKILL.md --create-dirs "https://raw.githubusercontent.com/Takazudo/claude-resources/main/skills/dev-wip-package-refer/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/dev-wip-package-refer/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How dev-wip-package-refer Compares

Feature / Agentdev-wip-package-referStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Pattern for consuming an in-progress (WIP) upstream npm/pnpm package from a sibling git checkout via a `file:../{name}/...` relative dep — without publishing the package. Use when: (1) Setting up a consumer project that needs to depend on a local in-development library or framework checked out next to it, (2) User mentions 'file: dep', 'sibling repo', 'upstream package', 'wip package', 'monorepo-style refer', 'how do we consume the upstream', (3) Deciding between this pattern and a published npm version or a `github:` git-URL dep, (4) Setting up a fresh machine that already has a consumer project but the sibling upstream isn't cloned yet, (5) A consumer's CI is failing because the sibling upstream isn't where the `file:` spec expects it.

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# dev-wip-package-refer

The "consume a WIP upstream package via a sibling-relative `file:` dep" pattern. Lets a consumer project depend on an in-development library/framework without publishing each iteration to npm.

For the inverse — editing the upstream itself — see [`dev-wip-package-upstream-wt-dev`](../dev-wip-package-upstream-wt-dev/SKILL.md).

## When to reach for this pattern

Use when **the upstream library is itself in active development by the same maintainer (or a tightly coordinated team)** and the iteration loop matters: edit upstream → consumer sees the change immediately on next `pnpm install`, no publish step. Trades multi-machine setup pain for fast iteration.

Don't use it when the upstream is stable and shipped to npm — a normal versioned dep is simpler. Also don't use it for adversarial / arms-length dependencies — `file:` paths assume both repos live on disk under your control.

## How it resolves

The consumer's `package.json` declares the dep with a path-style spec:

```json
{
  "dependencies": {
    "@org/pkg":    "file:../upstream/packages/pkg",
    "@org/pkg-rt": "file:../upstream/packages/pkg-runtime"
  }
}
```

pnpm/npm resolve `file:../upstream/...` against the **consumer project root**, so the upstream sibling is always at `<consumer-root>/../upstream/`. Identical on every machine as long as the sibling layout is preserved — clone the consumer at `$HOME/repos/foo/consumer`, the upstream at `$HOME/repos/foo/upstream`, and `file:../upstream` just works. No env-specific config.

Real example (zudo-doc):

```json
"@takazudo/zfb":                     "file:../zfb/packages/zfb"
"@takazudo/zfb-adapter-cloudflare":  "file:../zfb/packages/zfb-adapter-cloudflare"
"@takazudo/zfb-runtime":             "file:../zfb/packages/zfb-runtime"
"@takazudo/zudo-design-token-panel": "file:../zdtp/packages/zudo-design-token-panel"
```

→ resolves to `../zfb/packages/zfb` and `../zdtp/packages/zudo-design-token-panel`.

## What the consumer needs in place

For `pnpm install` to succeed in the consumer, the upstream sibling must:

1. **Exist on disk** at `../upstream/` (clone before installing).
2. **Be at a known SHA** so the consumer's behavior is reproducible — see "Pinning" below.
3. **Have any required build artifacts present.** pnpm hard-copies `file:` deps at install time. If the dep is "source + a built binary" (e.g. a Rust CLI shipped via the npm package), the binary must be built before the consumer installs. If the dep is "TypeScript + a `dist/`" the dist must be built first.

If any of those are missing, the consumer's `pnpm install` either succeeds with broken/stale state or fails at a postinstall hook.

## Pinning — single source of truth for the SHA

The consumer pins which upstream SHA it depends on. Two places to pin:

1. **In CI workflow env vars** — e.g. `ZFB_PINNED_SHA`, `ZDTP_PINNED_SHA` declared at the workflow `env:` level of every workflow that runs `pnpm install`. CI clones the upstream at that SHA before installing. This is the **source of truth.**
2. **(Optional) A `framework-pins.json`** that both CI workflows and a local bootstrap script read from. Reduces "edit 3 YAML files per bump" to "edit 1 JSON file." Use when you have more than ~2 workflow files.

Bumping the pin = the consumer adopts the upstream change. The change is reviewed and tested in CI's clean clone.

## CI side — clone-then-install

The consumer's CI workflow must clone the upstream at the pinned SHA into `../upstream/` **before** `pnpm install` runs in the consumer. Shape:

```yaml
env:
  UPSTREAM_PINNED_SHA: <full SHA>

jobs:
  build:
    steps:
      - name: Checkout consumer
        uses: actions/checkout@v5

      - name: Clone pinned upstream sibling
        run: |
          git clone https://github.com/<org>/<upstream-repo>.git ../upstream
          git -C ../upstream checkout "$UPSTREAM_PINNED_SHA"

      # OPTIONAL: build upstream artifacts here if the npm package needs them
      # (Rust binary, dist/, etc.). For heavy builds, do it in a separate
      # job and upload the artifact, then download it into ../upstream/ in
      # each consumer job. See zudo-doc's `build-zfb` job for that pattern.

      - name: Setup Node + pnpm
        # ...

      - name: Install consumer deps
        run: pnpm install
```

For expensive upstream builds (Rust toolchain, large bundles), split into a dedicated build job that uploads the binary as an artifact; consumer jobs `cp` it into `../upstream/target/release/` before `pnpm install`. See zudo-doc's `.github/workflows/pr-checks.yml` `build-zfb` job for a reference shape.

## Local side — bootstrap script for multi-machine setup

The brittle part of this pattern is "new machine = clone two repos in the right layout + build their artifacts before `pnpm install`." Solve it with a bootstrap script that does what CI does:

```bash
pnpm setup:upstream     # ensure all WIP-sibling upstreams are present and built
```

The script:

1. Reads the pinned SHA(s) from the single source of truth (CI workflow env vars or `framework-pins.json`).
2. For each upstream:
   - If `../upstream/` doesn't exist → `git clone <url> ../upstream`, then `git checkout <SHA>`.
   - If `../upstream/` exists with a **clean tree** → `git fetch origin && git checkout <SHA>` (matches CI).
   - If `../upstream/` exists with a **dirty tree** → refuse to touch it (you have in-flight upstream edits; resolve first or pass `--force-checkout`).
3. Build upstream artifacts if missing (Rust binary, `dist/`, etc.). Skip if cache is fresh.
4. Run `pnpm install` in the consumer.

The "refuse on dirty tree" rule is what prevents stomping on a parallel upstream-edit session — see [`dev-wip-package-upstream-wt-dev`](../dev-wip-package-upstream-wt-dev/SKILL.md) for why the upstream root is shared.

A starter Node.js skeleton (adjust to the consumer's needs):

```js
// scripts/setup-upstream.mjs
import { execSync } from "node:child_process";
import { existsSync, readFileSync } from "node:fs";
import { resolve } from "node:path";

const projectRoot = process.cwd();
const pkg = JSON.parse(readFileSync(resolve(projectRoot, "package.json"), "utf8"));

// Discover sibling-relative file: deps and group by upstream root.
const upstreams = new Map(); // siblingDir -> { siblingPath }
for (const [name, spec] of Object.entries(pkg.dependencies ?? {})) {
  if (typeof spec !== "string" || !spec.startsWith("file:..")) continue;
  const siblingDir = spec.slice("file:".length).split("/")[0]; // "../upstream"
  const siblingPath = resolve(projectRoot, siblingDir);
  if (!upstreams.has(siblingDir)) upstreams.set(siblingDir, { siblingPath });
}

// For each upstream, load (sha, gitUrl, buildSteps) from `framework-pins.json`
// (or grep the workflow YAML) and ensure-clone-build.
// ...left to the consumer to fill in based on its specific upstreams.
```

Wire the script into `package.json` as a regular script (`"setup:upstream": "node scripts/setup-upstream.mjs"`). Don't run it from `postinstall` — that would loop. Document it as the **first thing to run on a new machine.**

## Comparison with alternatives

| Approach | Multi-machine | Lets you edit upstream live | Effort |
|---|---|---|---|
| `file:../sibling` + bootstrap (this pattern) | One command per new machine | ✅ | Small |
| `github:org/repo#SHA` git URL | Just works on any machine; lockfile pins SHA | ⚠️ slower iteration; needs an upstream prebuilt-binary release flow if there's a non-JS artifact | Medium |
| Published npm package | Cleanest | ❌ requires `npm link` for upstream-edit flow | Large; an upstream-side project of its own |

The `file:` pattern wins when **iteration speed on upstream matters and the team can absorb the bootstrap step.** The published-package pattern wins when the upstream stabilizes.

## Bumping the pin

Edit the SHA in the single source of truth (CI workflow env var or `framework-pins.json`). Commit + push the consumer-side change. CI re-clones the upstream at the new SHA in its clean checkout and validates. Locally re-run `pnpm setup:upstream` to mirror the new pin if you want to test before pushing — otherwise trust CI.

For the **how** of preparing the new upstream SHA itself (an upstream PR, merge, watch CI green), see [`dev-wip-package-upstream-wt-dev`](../dev-wip-package-upstream-wt-dev/SKILL.md).

## Anti-patterns

- **Don't `git checkout` random branches on `../upstream/`.** That checkout is shared with every consumer using `file:../upstream/...` and with any concurrent Claude session. Use a worktree — see [`dev-wip-package-upstream-wt-dev`](../dev-wip-package-upstream-wt-dev/SKILL.md).
- **Don't run the bootstrap from `postinstall`.** Postinstall already runs on every `pnpm install` — the bootstrap script itself runs `pnpm install`, so wiring it as postinstall loops.
- **Don't commit absolute paths to `package.json`.** `file:../upstream/...` is portable; `file:/home/you/repos/upstream/...` is not.
- **Don't pin the SHA only in `pnpm-lock.yaml`.** `file:` deps point at on-disk paths, not SHAs — the lockfile is stable across pin bumps and won't help reproducibility. The SHA must live in CI env vars (or `framework-pins.json`).

Related Skills

refer-another-project

6
from Takazudo/claude-resources

Refer another project while protecting sensitive information. Use when: (1) User says 'refer project', 'copy from project', or 'look at another repo', (2) User wants to reference patterns or setup from another codebase, (3) User needs to learn from another project's structure without leaking private data.

logrefer

6
from Takazudo/claude-resources

Browse and read recent logs and artifacts from $HOME/cclogs/{slug}/. Use when: (1) User wants to see recent agent logs, (2) User says 'logrefer', 'show logs', 'recent logs', (3) User wants to read a specific log file from a previous session.

dev-wip-package-upstream-wt-dev

6
from Takazudo/claude-resources

Workflow for editing a WIP upstream package (consumed by this project via a sibling `file:../upstream/...` dep) when a fix or feature requires changing the upstream's code. ALWAYS work in a git worktree of the upstream — never on the shared `../upstream/` root checkout — because every consumer using that sibling shares the same on-disk HEAD. Use when: (1) The fix lives in the upstream package's source, not the consumer's, (2) User says 'edit upstream', 'fix upstream', 'patch upstream', 'upstream PR', 'I need to change the upstream framework', 'fix zfb / zdtp upstream', (3) Triaging a consumer-side issue and the root cause is in the upstream library, (4) Bumping the consumer's pin requires landing an upstream PR first.

dev-tweak-serve-package-json

6
from Takazudo/claude-resources

Tweak serve/dev commands in package.json. Use when: (1) User says 'tweak serve', 'dev tweak serve', or 'tweak-serve', (2) User wants to add port-kill before dev/serve (--kill), (3) User wants :net LAN-accessible variants of dev/serve (--net). Flags: --kill adds predev port cleanup, --net adds 0.0.0.0 host variants.

dev-package-json

6
from Takazudo/claude-resources

Organize and maintain package.json and npm config (.npmrc) for readability and security. Use when: (1) Reorganizing scripts section or adding separators, (2) Extracting multi-process commands into shell scripts, (3) Setting up multi-environment dev commands (local/preview/prod), (4) Handling pnpm "Ignored build scripts" warnings, (5) Configuring .npmrc security (strictDepBuilds, allowBuilds, ignoredBuilds), (6) Managing pnpm via corepack and packageManager field, (7) Adding predev port cleanup. Keywords: package.json, npm scripts, .npmrc, pnpm, build scripts, supply chain, corepack, packageManager, predev, kill port, port in use.

dev-npm-package

6
from Takazudo/claude-resources

Develop npm packages with Node.js and TypeScript following modern best practices. Use when: (1) Creating a new npm package, (2) Setting up package.json exports (dual ESM/CJS or ESM-only), (3) Configuring TypeScript for library authoring (Bundler or Node16 moduleResolution), (4) Building/publishing with tsup or tsc, (5) Creating CLI tools with bin field, (6) Testing with vitest, (7) CI/CD for npm publishing, (8) ESM/CJS interop issues. Keywords: npm package, publish to npm, library development.

zudoesa-articlify

6
from Takazudo/claude-resources

Convert conversation context into an esa article via the zudoesa-writer subagent. ONLY invoke when the user explicitly asks — NEVER proactively propose. Triggers: 'write esa article', 'esa記事', 'esaに書いて', 'articlify for esa', or /zudoesa-articlify. Gathers context, creates a writing brief, delegates to the writer subagent.

zudoesa-apply-voice

6
from Takazudo/claude-resources

Apply Takazudo's esa writing voice and vocabulary rules to text. Use when: (1) User wants to write/rewrite text in Takazudo's esa style, (2) User says 'apply voice', 'esa voice', 'esa文体で', 'esa風に書いて', '文体を適用', (3) User provides text to transform to esa style. Reads writing-style.md and vocabulary-rule.md from takazudo-esa-writing repo and applies the rules.

zudocg-articlify

6
from Takazudo/claude-resources

Convert conversation context into a CodeGrid article via the zudocg-writer subagent. ONLY invoke when the user explicitly asks — NEVER proactively propose. Triggers: 'write codegrid article', 'CodeGrid記事', 'codegridに書いて', 'articlify for codegrid', or /zudocg-articlify. Gathers context, creates a writing brief, delegates to the writer subagent.

zudocg-apply-voice

6
from Takazudo/claude-resources

Apply Takazudo's CodeGrid writing voice and vocabulary rules to text. Use when: (1) User wants to write/rewrite text in Takazudo's CodeGrid style, (2) User says 'apply voice', 'codegrid voice', 'codegrid文体で', 'codegrid風に書いて', '文体を適用', (3) User provides text to transform to CodeGrid style. Reads writing-style.md and vocabulary-rule.md from takazudo-codegrid-writing repo and applies the rules.

zpaper-articlify

6
from Takazudo/claude-resources

Convert conversation context into a zpaper blog article via the zpaper-writer subagent. ONLY invoke when the user explicitly asks — NEVER proactively propose. Triggers: 'write zpaper article', 'zpaper記事', 'zpaperに書いて', 'articlify for zpaper', or /zpaper-articlify. Gathers context, creates a writing brief, delegates to the writer subagent.

zpaper-apply-voice

6
from Takazudo/claude-resources

Apply Takazudo's zpaper blog writing voice and vocabulary rules to text. Use when: (1) User wants to write/rewrite text in Takazudo's zpaper style, (2) User says 'apply voice', 'zpaper voice', 'zpaper文体で', 'zpaper風に書いて', 'ブログ文体を適用', (3) User provides text to transform to zpaper style. Reads writing-style.md and vocabulary-rule.md from the zpaper repo and applies the rules.