---
name: eck:push-to-user-catalog
version: "0.7.4"
description: Push improvements from a deployed project back into the catalog with de-projectification, agent harvesting, and impact analysis.
disable-model-invocation: false
---

# Push To Catalog Workflow

Push improvements, custom components, agents, and fixes from a deployed project back into the catalog (`$ECK_HOME/`). Handles de-projectification (replacing project-specific values with template placeholders), agent harvesting, and updates all impacted template files.

---

## Visual Framework

Follow these conventions for all terminal output displayed to the user via the Bash tool.

### Output Mode Awareness

Before producing output, read the user's preferred output mode:

```bash
cat $ECK_HOME/user-preferences.json 2>/dev/null
```

Extract the `outputMode` field and adapt verbosity:

- **guided** — Add explanatory prose after key steps. Describe what is happening and why.
- **standard** (default) — Balanced. Show steps with brief context for non-obvious decisions.
- **concise** — Steps and summary box only. No prose, no explanations.

If the file is missing or `outputMode` is absent, use **standard**.

### ANSI Colors

Declare these variables at the top of **every** Bash tool call (shell state does not persist between calls):

```bash
source $ECK_HOME/colors.sh
```

Every color sequence **must** end with `${RESET}`. No exceptions.

### Stage Banners

Mark major stage transitions with a banner. One blank line before and after.

```bash
echo ""
printf '%b\n' "${ORANGE}${BOLD}STAGE NAME${RESET}"
echo ""
```

### Status Symbols

| Symbol | Meaning          | Color       |
| ------ | ---------------- | ----------- |
| `[*]`  | Active / running | `${CYAN}`   |
| `[x]`  | Completed        | `${GREEN}`  |
| `[!]`  | Error            | `${RED}`    |
| `[-]`  | Skipped          | `${DIM}`    |
| `[>]`  | In progress      | `${YELLOW}` |
| `[ ]`  | Pending          | `${DIM}`    |

```bash
printf '%b\n' "${GREEN}[x]${RESET} Step completed"
printf '%b\n' "${RED}[!]${RESET} Something failed"
```

No emoji in output. Use only these bracket symbols for status.

### Summary Boxes

Display at checkpoints and at the end of the skill. One blank line before and after.

```bash
echo ""
printf '%b\n' "${ORANGE}${BOLD}Title${RESET}"
echo ""
printf '%b\n' "Label: Value"
echo ""
```

### Error Format

```bash
printf '%b\n' "${RED}[!]${RESET} Error message here"
printf '%b\n' "    ${DIM}Recovery guidance here.${RESET}"
```

### Spacing Rules

- One blank line before and after stage banners and summary boxes.
- No blank lines within step lists.
- Maximum output line width: 70 characters.

### Statusline Stage Updates

At the **start** of each stage, update the statusline:

```bash
bash $ECK_HOME/update-stage.sh "{Statusline text from table}"
```

At **skill completion** (success or error), reset:

```bash
bash $ECK_HOME/update-stage.sh
```

---

## Task Registration

| Stage | Subject                 | Active Form             | Statusline      |
| ----- | ----------------------- | ----------------------- | --------------- |
| 1     | Stage 1: Pre-Flight     | Verifying prerequisites | Preflight (1/7) |
| 2     | Stage 2: Collect        | Collecting components   | Collect (2/7)   |
| 3     | Stage 3: De-Projectify  | De-projectifying        | DeProject (3/7) |
| 4     | Stage 4: Propose        | Proposing changes       | Propose (4/7)   |
| 5     | Stage 5: Apply          | Applying changes        | Apply (5/7)     |
| 6     | Stage 6: Update Catalog | Updating catalog        | Catalog (6/7)   |
| 7     | Stage 7: Summary        | Generating summary      | Summary (7/7)   |

## Usage

```bash
/eck:push-to-user-catalog <project-path>           # Push from specific project to global
```

**Must be run from any project directory.** Templates are read from and written to `$ECK_HOME/`.

---

## Stage 1: Pre-Flight

### Inputs

- `$ARGUMENTS` — target project path
- `<TARGET_PATH>/.claude/project-catalog.yaml`
- `$ECK_HOME/global-catalog.yaml`

### Activities

### Requirements

- Target project must have a deployed catalog (`.claude/project-catalog.yaml`)
- Templates must be installed at `$ECK_HOME/`
- If no catalog exists, instruct user to run `/eck:audit-unmanaged-project --write-catalog` on the target first

### Verification

```bash
# Verify templates are installed
ls $ECK_HOME/global-catalog.yaml

# Verify target has catalog
ls {TARGET_PATH}/.claude/project-catalog.yaml
```

**If templates not installed:**

```bash
printf '%b\n' "${RED}[!]${RESET} Templates not found at $ECK_HOME/"
printf '%b\n' "    ${DIM}Run: npx @evolvconsulting/evolv-coder-kit --global${RESET}"
```

**If target has no catalog:**

```bash
printf '%b\n' "${RED}[!]${RESET} Target project has no deployed catalog"
printf '%b\n' "    ${DIM}Run /eck:audit-unmanaged-project --write-catalog first.${RESET}"
printf '%b\n' "    ${DIM}Then re-run /eck:push-to-user-catalog.${RESET}"
```

### Outputs

- Confirmed target project catalog and global catalog accessible

### Exit Criteria

- Target project catalog found; global catalog accessible; skill may proceed

---

## Stage 2: Collect Pull Candidates

### Inputs

- `<TARGET_PATH>/.claude/project-catalog.yaml`
- `<TARGET_PATH>/.claude/agents/` directory
- `$ECK_HOME/registry.yaml` (for cross-project dedup, if exists)
- `$ECK_HOME/audits/pull-candidates.md` (if exists)

### Activities

### Load Target Catalog

Read `<TARGET_PATH>/.claude/project-catalog.yaml` and collect items that are candidates for pulling upstream:

### Candidate Criteria

| Source         | Criteria                                                         | Priority                                       |
| -------------- | ---------------------------------------------------------------- | ---------------------------------------------- |
| Custom items   | `custom: true` AND `pull_candidate: true`                        | By `pull_confidence` (high > medium > low)     |
| Modified items | `status: modified` AND `modification_type: improvement`          | By `pull_confidence`, then `commit_count` desc |
| Modified items | `status: modified` AND `pull_candidate: true` (manually flagged) | Explicit flag overrides confidence             |

### Agent Candidates

In addition to template files, scan `.claude/agents/` for modified or new agents:

- Compare checksums against `$ECK_HOME/.claude/agents/`
- Modified agents with improvement diffs are push candidates
- New project-created agents may be candidates for catalog inclusion
- Apply the same de-projectification rules as other components

### Exclude from Candidates

- Items with `protected: true` (explicitly marked as project-only)
- Items with `modification_type: project-specific`
- Items with `modification_type: regression`
- Items with `pull_confidence: low` UNLESS `pull_candidate` was explicitly set to `true`

### Candidate Inventory

For each candidate, collect:

1. File path in target project
2. File content
3. Corresponding canonical file (if exists — for modifications)
4. Diff between canonical and project version (for modifications)
5. Catalog metadata (status, modification_type, pull_notes, pull_confidence)
6. Git metadata from catalog (commit_count, last_commit_date, last_commit_message, last_commit_hash)

### Cross-Project Dedup (Registry-Aware)

**Reference:** `ops:registry-update` primitive for the canonical read/write procedure.

**MUST** use `$ECK_HOME/registry.yaml` to perform cross-project dedup before collecting candidates. Do NOT skip dedup when registry exists:

1. **Load registry:** Read `$ECK_HOME/registry.yaml` and collect `pull_candidates` from ALL projects
2. **Flag convergent evolution:** If the same component (by file path or function) appears as a pull candidate in 2+ projects, flag it as convergent evolution — this is the strongest signal for canonicalization
3. **Check pull tracker:** Read `$ECK_HOME/audits/pull-candidates.md` for status of each candidate:
   - If status is `PULLED` — skip (already canonicalized)
   - If status is `IN-PROGRESS` — warn user and show who is working on it
   - If status is `PENDING` — proceed with pull
   - If status is `REJECTED` — skip with note showing rejection reason
4. **Prefer best version:** When the same component exists in multiple projects:
   - Prefer the version with higher `commit_count` and more recent `last_commit_date`
   - If versions differ significantly, flag for manual comparison
   - Note all source projects in the proposal for attribution

**If registry doesn't exist:** Fall back to checking individual audit reports in `$ECK_HOME/audits/` as before.

### After Pulling

After a successful pull, update `$ECK_HOME/audits/pull-candidates.md`:

1. Change the candidate's status from `PENDING` to `PULLED (v{version})`
2. Add an entry to the Pull History Log with date, item, version, and operator
3. Update the Summary table counts

### Outputs

- Ranked candidate list with metadata (file path, diff, catalog metadata, git history, attribution)

### Exit Criteria

- Candidate list compiled; cross-project dedup applied; excluded items filtered out

---

## Stage 3: De-Projectification Assessment

### Inputs

- Candidate list from Stage 2
- `<TARGET_PATH>/.claude/project-constants.md` (for project-specific value map)

### Activities

For each pull candidate, assess what's needed to generalize it for all projects.

### Load Project Constants

Parse project constants using the template engine:

```bash
node "$(npm root -g)/@evolvconsulting/evolv-coder-kit/bin/template-cli.js" \
  parse-constants "{TARGET_PATH}/.claude/project-constants.md"
```

### Per-Candidate Assessment

For each candidate, evaluate:

| Assessment           | Question                                             | Output                                  |
| -------------------- | ---------------------------------------------------- | --------------------------------------- |
| **Generalizability** | Does this benefit all projects, or just this one?    | `universal` / `most-projects` / `niche` |
| **Templatization**   | What project values need to become `<PLACEHOLDERS>`? | List of replacements                    |
| **Fit**              | Where does this belong in the template structure?    | Target path in `$ECK_HOME/`             |
| **Impact**           | What other template files need updating?             | List of impacted files                  |
| **Breaking**         | Does this change break existing deployed projects?   | `yes` / `no`                            |

### De-Projectification via Template Engine

Use `template-cli deprojectify` to reverse-resolve project-specific values back to `<PLACEHOLDER>` tokens:

```bash
node "$(npm root -g)/@evolvconsulting/evolv-coder-kit/bin/template-cli.js" \
  deprojectify <candidate-file> \
  --values {TARGET_PATH}/.claude/project-constants.md \
  --format json
```

The deprojectify command:

- Builds a reverse map from project values to `<PLACEHOLDER>` tokens
- Sorts by longest value first to prevent partial matches (e.g., a URL is replaced before its hostname)
- Skips values shorter than 3 characters to avoid false positives
- Returns both the de-projectified content and the list of replacements made

**Review the output.** The template engine handles safe automated replacement, but manual review is still needed for:

- Project-specific logic that can't be templatized (flag for manual review)
- Values that appear in non-placeholder positions (the engine replaces all occurrences — verify the result is correct)

### Outputs

- Each candidate assessed for generalizability, templatization replacements, target path, impact, and breaking-change risk

### Exit Criteria

- All candidates assessed; de-projectification plan ready for each

---

## Stage 4: Present Proposals

### Inputs

- Assessment results from Stage 3 (one proposal per candidate)

### Activities

For each candidate, present a detailed proposal with full impact analysis.

### Proposal Format

Display each proposal with a summary box header and status symbols.

```bash
echo ""
printf '%b\n' "${ORANGE}${BOLD}Pull Proposal {N}: {candidate_name}${RESET}"
echo ""
printf '%b\n' "Source: {TARGET_PATH}/{file_path}"
printf '%b\n' "Type: {custom | modification}"
printf '%b\n' "Generalizability: {universal | most-projects | niche}"
printf '%b\n' "Breaking: {yes | no}"
echo ""
```

**Description:**

```bash
printf '%b\n' "${CYAN}[*]${RESET} {What this component does and why it's valuable}"
```

**Content preview** (first 30 lines of de-projectified content):

```bash
printf '%b\n' "${DIM}--- content preview ---${RESET}"
# Display first 30 lines of content
printf '%b\n' "${DIM}--- end preview ---${RESET}"
```

**Templatization changes:**

```bash
printf '%b\n' "${YELLOW}[>]${RESET} Templatization changes:"
printf '%b\n' "    ${DIM}{project_value} -> {PLACEHOLDER}${RESET}"
```

**Files to update:**

```bash
printf '%b\n' "${CYAN}[*]${RESET} Files to update in evolv-coder-kit:"
printf '%b\n' "    ${DIM}{target_path} — Create/update component${RESET}"
printf '%b\n' "    ${DIM}global-catalog.yaml — Add/update entry${RESET}"
printf '%b\n' "    ${DIM}settings.json.template — Add hook wiring${RESET}"
```

**Version impact:**

```bash
printf '%b\n' "${DIM}Recommended bump:${RESET} {MINOR | PATCH}"
printf '%b\n' "${DIM}Current version:${RESET}  {version}"
printf '%b\n' "${DIM}Proposed version:${RESET} {new_version}"
```

Prompt: `Accept this pull? (Y/n/edit)`

- **Y** — Accept proposal as-is
- **n** — Skip this candidate
- **edit** — Show full content for manual editing before acceptance

### Outputs

- User-approved list of candidates to apply (with any edit decisions recorded)

### Exit Criteria

- All proposals presented; at least one approved or skill exits if all declined

---

## Stage 5: Apply Approved Pulls

### Inputs

- Approved proposals from Stage 4 (with de-projectified content)
- `$ECK_HOME/` target template directory

### Activities

For each accepted proposal:

### Step 1: Write Component File

Copy the de-projectified content to the canonical template location:

```bash
# For new components:
cp {de-projectified content} $ECK_HOME/{target_path}

# For modifications to existing components:
# Apply the diff to the canonical template file
```

### Step 2: Update User Catalog

If `$ECK_HOME/user-catalog.yaml` does not exist, create it with a header:

```yaml
# user-catalog.yaml — User-contributed catalog entries pushed from projects
version: "1.0.0"
items: []
```

Update `$ECK_HOME/user-catalog.yaml`:

- **New component:** Add entry to appropriate category with:
  - `name`, `file`, `version` (start at "1.0.0" for new, bump for updates)
  - `checksum` (compute from new file)
  - `category`, `required`, `deployment` flags
  - `contains_placeholders` flag
  - `source_project: "<PROJECT_NAME>"` (attribution for where this entry was pushed from)

- **Modified component:** Update:
  - `checksum` to new value
  - `version` bump (PATCH for fixes, MINOR for improvements)
  - `source_project: "<PROJECT_NAME>"` (attribution for the contributing project)

### Step 3: Update Skill Metadata (if skill)

If the pulled item is a skill, update the `skills.skill_metadata` section in `$ECK_HOME/user-catalog.yaml`:

```yaml
{ skill-name }:
  version: "1.0.0"
  owner: "platform-team"
  model_tier: "{tier}"
  description: "{description}"
  invocable: { true|false }
  last_updated: "{DATE}"
  pulled_from: "{PROJECT_NAME}"
```

### Step 4: Update Template settings.json (if hook)

If the pulled item is a hook, update `$ECK_HOME/.claude/settings.json.template`:

- Add hook script reference to appropriate event array
- Determine correct event binding based on hook purpose

### Step 5: Update Impacted Documentation

For each impacted file identified in the proposal:

1. Read the current file
2. Apply the documented change
3. Update version/date metadata

### Step 6: Regenerate Checksums

After all file changes:

```bash
# Recompute checksum for each changed file (CRLF-normalized)
tr -d '\r' < {file} | shasum -a 256 | cut -c1-12
```

Update the canonical catalog with new checksums.

### Outputs

- Component files written to `$ECK_HOME/`; `user-catalog.yaml` updated; checksums regenerated

### Exit Criteria

- All approved components written; checksums verified; catalog updated

---

## Stage 6: Update Target Project Catalog

### Inputs

- Applied items from Stage 5 (now canonical)
- `<TARGET_PATH>/.claude/project-catalog.yaml`

### Activities

Update `<TARGET_PATH>/.claude/project-catalog.yaml`:

1. Change pulled items from `custom`/`modified` → `synced` (they're now canonical)
2. Remove `pull_candidate: true` flag (already pulled)
3. Add sync_history entry:

```yaml
sync_history:
  - date: "{DATE}"
    action: "pull"
    performed_by: "/eck:push-to-user-catalog"
    target: "evolv-coder-kit"
    claude_code_standards_version: "{new_version}"
    summary: "Pulled {N} items to evolv-coder-kit templates"
    items_pulled:
      - file: "{path}"
        canonical_path: "$ECK_HOME/{path}"
        type: "{custom|modification}"
```

### Outputs

- `project-catalog.yaml` updated: pulled items marked `synced`; `sync_history` entry added

### Exit Criteria

- `project-catalog.yaml` written successfully

---

## Stage 7: Summary

### Inputs

- Pushed item counts and version bumps from Stage 5
- Files modified in `$ECK_HOME/` from Stage 5

### Activities

Display the final summary as a branded summary box with status symbols.

```bash
echo ""
printf '%b\n' "${ORANGE}${BOLD}Push To Catalog Complete${RESET}"
echo ""
printf '%b\n' "Items Pushed: {N}"
printf '%b\n' "Files Modified: {N}"
printf '%b\n' "Version Bump: {MINOR | PATCH}"
printf '%b\n' "Template: v{old} -> v{new}"
printf '%b\n' "Catalog: v{old} -> v{new}"
echo ""
```

**Items pushed:**

```bash
printf '%b\n' "${GREEN}[x]${RESET} {name} — {custom|modification}"
printf '%b\n' "    ${DIM}From: {project_path}${RESET}"
printf '%b\n' "    ${DIM}To:   {template_path} v{version}${RESET}"
```

**Files modified in evolv-coder-kit:**

```bash
printf '%b\n' "${GREEN}[x]${RESET} {template_file} — {Created|Updated}"
printf '%b\n' "${GREEN}[x]${RESET} user-catalog.yaml — {N} added, {N} updated"
printf '%b\n' "${GREEN}[x]${RESET} settings.json.template — {N} hook bindings"
```

**Next steps:**

```bash
printf '%b\n' "${CYAN}[*]${RESET} Review modified files in $ECK_HOME/"
printf '%b\n' "${CYAN}[*]${RESET} Run /eck:audit-unmanaged-project to verify"
printf '%b\n' "${CYAN}[*]${RESET} Run /eck:pull-from-global-catalog to propagate"
```

### Outputs

- Completion summary displayed with item counts, version changes, and next steps

### Exit Criteria

- Summary displayed; statusline reset; skill complete

---

## Safety Rules

1. **Never auto-accept** — every candidate requires explicit user approval
2. **De-projectification is mandatory** — no project-specific values should leak into templates
3. **Impact analysis before acceptance** — user sees all files that will change
4. **Version tracking** — every pull is logged in both catalogs

---

## Error Handling

### Templates Not Installed

```bash
printf '%b\n' "${RED}[!]${RESET} Templates not found at $ECK_HOME/"
printf '%b\n' "    ${DIM}Run: npx @evolvconsulting/evolv-coder-kit --global${RESET}"
```

### No Pull Candidates Found

```bash
printf '%b\n' "${DIM}[-]${RESET} No pull candidates found in {TARGET_PATH}"
printf '%b\n' "    ${DIM}All items are synced, project-specific, or unflagged.${RESET}"
printf '%b\n' "    ${DIM}To flag items:${RESET}"
printf '%b\n' "    ${DIM}1. Run /eck:audit-unmanaged-project --write-catalog${RESET}"
printf '%b\n' "    ${DIM}2. Edit catalog: set pull_candidate: true${RESET}"
printf '%b\n' "    ${DIM}3. Re-run /eck:push-to-user-catalog${RESET}"
```

### De-Projectification Failed

```bash
printf '%b\n' "${YELLOW}[>]${RESET} Could not fully de-projectify {file_name}"
printf '%b\n' "    ${DIM}Remaining project-specific values:${RESET}"
printf '%b\n' "    ${DIM}  {value_1}${RESET}"
printf '%b\n' "    ${DIM}  {value_2}${RESET}"
printf '%b\n' "    ${DIM}Options:${RESET}"
printf '%b\n' "    ${DIM}1. Accept (will need manual templatization)${RESET}"
printf '%b\n' "    ${DIM}2. Skip this candidate${RESET}"
printf '%b\n' "    ${DIM}3. Edit content before accepting${RESET}"
```

---

## Related Documents

- [Global Catalog]($ECK_HOME/global-catalog.yaml) - Canonical source of truth
- [/eck:audit-unmanaged-project skill](../audit-unmanaged-project/SKILL.md) - Read-only audit (prerequisite)
- [/eck:pull-from-global-catalog skill](../pull-from-global-catalog/SKILL.md) - Pull updates into projects
- [/eck:submit-to-repo skill](../submit-to-repo/SKILL.md) - Submit catalog improvements to GitHub
- [/eck:new-project skill](../new-project/SKILL.md) - Bootstrap new projects
