Adopt Project Scope logo

Adopt Project Scope

Community
jpicklyk
adopt-project-scope

Migrates an already-populated, unscoped Task Orchestrator database in place to the project-scoping convention — creates a project anchor root, re-parents existing work trees under it, and writes rootId back to config.yaml. Use when a user says: adopt project scope, migrate this database to project scoping, make this DB multi-project, project-scope this workspace, adopt existing database, or set up project scoping for an existing DB.

Overview

Publisherjpicklyk
Repositorytask-orchestrator
Skill nameadopt-project-scope
Stars
204
Forks
22
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by jpicklyk on GitHub. Read the source before you install it.

Installation

Install the Adopt Project Scope AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/jpicklyk/task-orchestrator.git /tmp/task-orchestrator
mkdir -p .claude/skills
cp -r /tmp/task-orchestrator/claude-plugins/task-orchestrator/skills/adopt-project-scope .claude/skills/adopt-project-scope
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Adopt Project Scope in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Adopt Project Scope on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Adopt Project Scope is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Adopt Project Scope — In-Place Migration to Project Scoping

Take an existing, unscoped database (many depth-0 roots, no project anchor) and migrate it in place to the project-scoping convention: one type=project root that owns the workspace's work trees, with its UUID written back to .taskorchestrator/config.yaml under a project: block. Global containers (retrospectives, observations) stay at depth 0; test artifacts are flagged for cleanup, never moved or deleted.

This is a destructive-by-execution skill: the EXECUTE step re-parents real items. It defaults to a dry run and never mutates anything without an explicit confirmation. The only thing it ever deletes is its own throwaway server-probe tree.

Non-goals: cross-DB consolidation (merging items from a second database); merging multiple existing project anchors into one; any server-side enforcement of scope. This skill adopts a single workspace's single database.


Step 0 — Parse Arguments

  • Project name — the first non-flag token(s) in $ARGUMENTS. If absent, do not guess; ask for it in Step 3 (the dry-run plan) via AskUserQuestion before any mutation.
  • --dry-run — if present, the skill stops after Step 3 (the plan) and performs zero mutations regardless of confirmation. The dry run is ALSO always rendered before execution even without the flag; the flag only forces an early exit.

Step 1 — Preflight (abort gates)

Run these checks in order. Each abort prints a clear, user-facing reason and stops.

1a — Already adopted?

Read .taskorchestrator/config.yaml. If it contains a project: block with a non-empty rootId:, the workspace is already scoped:

◆ Workspace already adopted — config.yaml already points at project root <rootId>.
  Nothing to do. Use /work-summary to inspect the existing project tree.

Stop. (If the file is missing entirely, that is fine — a fresh unscoped DB has no config project: block. Continue.)

1b — Existing anchor already in the DB?

query_items(operation="search", type="project", depth=0)

(list mode — query omitted, filtering by type and depth.)

  • No results → continue to 1c.

  • One or more results → do NOT silently create a second anchor. Offer, via AskUserQuestion:

    1. Attach to existing — write the existing anchor's UUID into config.yaml (skip anchor creation in EXECUTE; still re-parent MOVE roots under it) and continue.
    2. Abort — stop and let the user resolve the ambiguity manually.

    If the user picks "attach", record the existing anchor UUID as ANCHOR_ID and note that EXECUTE step (a) is skipped.

1c — Server capability probe (the depth-sweep fix)

Bulk re-parenting is exactly the scenario that a pre-fix server corrupts: on a pre-#219/#220 server, re-parenting a subtree does NOT recompute descendant depths (bug 99a3e642), so this skill would silently leave the moved trees with wrong depths. Verify the fix is deployed with a disposable probe, then delete it.

Build a probe where a grandchild's depth MUST change when a middle node is re-parented deeper:

  1. Create a throwaway root and a two-level branch under it in one call:
    manage_items(operation="create", items=[
      { title: "zzprobe-root",  type: "container", priority: "low" }
    ])
    Capture its UUID as P.
  2. Create the branch (sequential parentage, small tree). manage_items create has no ref/parentRef mechanism (only create_work_tree does), so a sibling's UUID does not exist until its own create call returns — M and G cannot be created in the same batch since G's parentId needs M's real UUID:
    manage_items(operation="create", items=[
      { title: "zzprobe-A",   parentId: P,  priority: "low" }     → capture as A (depth 1)
    ])
    manage_items(operation="create", items=[
      { title: "zzprobe-M",   parentId: P,  priority: "low" }     → capture as M (depth 1, sibling of A)
    ])
    Capture M's returned UUID, then create G referencing it:
    manage_items(operation="create", items=[
      { title: "zzprobe-G",   parentId: "<M's real UUID>",  priority: "low" }     → capture as G (depth 2, child of M)
    ])
  3. Re-parent the middle node M under A (making it deeper):
    manage_items(operation="update", items=[{ itemId: M, parentId: A }])
    Post-fix, M becomes depth 2 and G becomes depth 3.
  4. Read the grandchild's depth and capture it:
    query_items(operation="get", itemId=G)
  5. Delete the probe immediately, before acting on the result — this runs on every path, pass or fail:
    manage_items(operation="delete", itemIds=[P], recursive=true)
  6. Now evaluate the captured depth:
    • depth == 3 → fix is present. Continue.
    • depth == 2 (stale) → abort (probe already deleted):
      ⊘ Server is missing the reparent depth-sweep fix (bug 99a3e642).
        Bulk re-parenting on this server would corrupt subtree depths.
        Upgrade the Task Orchestrator server (PR #219/#220 or later) and retry.

--dry-run skips this probe entirely — no execution will happen, so the capability check is unnecessary and the dry run stays genuinely mutation-free. The probe runs only on the real-execution path, after the user confirms in Step 3.

1d — Config-push capability (tolerant)

manage_project_config may be absent on older servers. Do not probe it destructively here — just remember to attempt the push in EXECUTE step (d) and, if the tool is unavailable, skip it with a note rather than failing the whole adoption. The local config.yaml write (step c) is the source of truth; the server push is a convenience sync.


Step 2 — Inventory & Classify

Full depth-0 census:

query_items(operation="overview", excludeTerminal=false, includeChildren=true, limit=<total>)

If the response reports truncated: true, re-issue with limit set to the reported total so no root is missed.

Classify every depth-0 root into exactly one bucket:

BucketRuleAction
KEEP-GLOBALTitle matches a global container — Session Retrospectives, Improvement Proposals; OR the root itself is tagged/typed agent-observation (these are standalone depth-0 items, each its own root — never children of a container).Stays at depth 0.
MOVEAny work container or work tree (a root with work/queue/review children), and any standalone work item. Everything that is real project work.Re-parented under the new anchor.
CLEANUP-CANDIDATEEvident test artifacts: titles containing probe, smoke, depth-test, zzprobe; or tags matching mtest-*.Flagged only — never moved, never deleted. Recommend /batch-complete.

Ambiguous roots (no clear rule match — e.g., an untagged standalone item that could be work or a stray container): do NOT guess. Collect them and present via AskUserQuestion (multiSelect) asking which should MOVE vs stay KEEP-GLOBAL. Test-artifact-looking items always go to CLEANUP-CANDIDATE without asking.


Step 3 — Dry-Run Plan (always shown; --dry-run stops here)

Render the full plan before any mutation. Require explicit confirmation to proceed.

◆ Adopt Project Scope — Plan   [project: "<name>"]

| Root | ID | Classification | Action |
|------|----|----------------|--------|
| Auth System | `a1b2c3d4` | MOVE | re-parent under new anchor |
| Payments | `e5f6a7b8` | MOVE | re-parent under new anchor |
| Session Retrospectives | `c9d0e1f2` | KEEP-GLOBAL | stays at depth 0 |
| Improvement Proposals | `13243546` | KEEP-GLOBAL | stays at depth 0 |
| zzprobe-smoke-tree | `5768798a` | CLEANUP-CANDIDATE | flag for /batch-complete (not moved) |

Summary: 2 move · 2 stay global · 1 cleanup candidate
Execution will create a "<name>" project anchor and re-parent 2 tree(s) under it.
  • If the project name is still unknown, ask for it now via AskUserQuestion before showing the final counts.
  • --dry-run: stop here. State plainly: "Dry run — no changes made. Re-run without --dry-run to execute."
  • Otherwise, confirm via AskUserQuestion:
    ◆ Proceed with adoption? This re-parents <N> tree(s) under a new project anchor.
      1. Yes — execute
      2. No — abort, make no changes
    Wait for the answer. Only "Yes" proceeds.

Step 4 — Execute

Perform in this order. Record pre-execution counts first (used by Step 5): the depth-0 root count, the MOVE count, and the KEEP-GLOBAL count from Step 2.

(a) Create the project anchor — skip if attaching to an existing anchor (Step 1b):

manage_items(operation="create", items=[{
  title: "<project name>",
  type: "project",
  summary: "Project anchor — subtree scope for <project name>",
  priority: "low"
}])

Capture the new UUID as ANCHOR_ID.

(b) Batch re-parent the MOVE roots — one call, items array (never a sequential call per root):

manage_items(operation="update", items=[
  { itemId: "<move-root-1>", parentId: ANCHOR_ID },
  { itemId: "<move-root-2>", parentId: ANCHOR_ID },
  ...
])

This relies on the depth-sweep fix verified in Step 1c — each moved subtree's descendant depths and root_id are recomputed server-side.

(c) Write the project: block into config.yaml — read-modify-write, preserving ALL existing content:

  • Read the current .taskorchestrator/config.yaml text (create the file with just the block if it does not exist).
  • Insert this top-level block (surgical insert/append — do NOT regenerate or reformat the rest of the file; leave work_item_schemas:, traits:, actor_authentication:, comments, and formatting byte-for-byte intact):
    yaml
    project:
      rootId: "<ANCHOR_ID>"
      name: "<project name>"
  • If a project: key somehow already exists (it should not — Step 1a would have aborted), update its rootId/name in place rather than adding a duplicate key.

(d) Push the config server-side (tolerant — skip if the tool is absent):

manage_project_config(operation="push", rootId=ANCHOR_ID, configYaml="<full config.yaml text>")
  • A warning field about the root's type is only returned when the anchor's type is not project; since step (a) sets type: "project", none is expected. Report it if present but treat it as non-fatal.
  • If manage_project_config is unavailable on this server, skip this step and note: "Config pushed locally only — server does not support manage_project_config; the local config.yaml is authoritative."

Step 5 — Verify & Reconcile

Confirm the migration landed correctly. On ANY mismatch, report loudly and do NOT auto-rollback (say the remedy explicitly).

  1. Anchored child count — the new anchor's direct children must equal the MOVE count:
    query_items(operation="overview", anchorId=ANCHOR_ID)
    The anchor's direct-child count must equal N move.
  2. Global census — remaining depth-0 roots must reconcile:
    query_items(operation="overview", excludeTerminal=false, limit=<total>)
    Expected depth-0 roots = KEEP-GLOBAL count + 1 (the anchor) + any CLEANUP-CANDIDATE roots left in place (cleanup candidates were flagged, not moved). Confirm the moved roots are no longer at depth 0.
  3. Depth spot-check — pick one moved tree that had a grandchild; confirm the grandchild's depth increased by exactly 1 from its pre-move value (the anchor added one level above the old root):
    query_items(operation="get", itemId="<grandchild-of-a-moved-tree>")

Render a before/after table:

✓ Adoption Complete — "<project name>"  (anchor `<8-char-id>`)

|                     | Before | After |
|---------------------|--------|-------|
| Depth-0 roots       | 5      | 4     |
| Under project anchor| 0      | 2     |
| Global containers   | 2      | 2     |
| Cleanup candidates  | 1      | 1 (flagged, not moved) |

↳ Config: project.rootId written to .taskorchestrator/config.yaml
↳ Server push: applied | skipped (tool absent)
↳ Cleanup candidates flagged for /batch-complete: <titles>

On mismatch (e.g., anchored child count ≠ MOVE count):

⚠ Reconciliation mismatch — expected <X> children under the anchor, found <Y>.
  No rollback was performed. To undo: re-parent the affected roots back to depth 0
  with manage_items(operation="update", items=[{ itemId, parentId: null }]) for each,
  then delete the anchor. Investigate before retrying.

Remind the user that schema changes / new config require an MCP reconnect (/mcp) to take effect if they rely on per-root schema resolution immediately.


Safety Summary

  • Default dry run. No mutation without an explicit AskUserQuestion "Yes".
  • Never deletes user data. Cleanup candidates are only flagged; the sole deletion is the throwaway server probe in Step 1c.
  • Never regenerates config.yaml. The project: block is surgically inserted; all other content is preserved verbatim.
  • Abort, don't corrupt. A pre-fix server (missing the depth-sweep) aborts in preflight rather than silently corrupting subtree depths.
  • No auto-rollback. On verify mismatch, the skill reports the manual remedy rather than guessing at an undo.

Verification (before first real run)

Per the specification, exercise the skill against a COPY of a production-shaped DB:

  1. Dry run on the copy must produce zero mutations and a correct classification table.
  2. Execute on the copy and confirm Step 5 count reconciliation passes (depth-0 roots, anchored children, grandchild depth +1).

Only after the copy passes should the skill be run against the real database.

Frequently asked questions

What does the Adopt Project Scope AI skill do?

Migrates an already-populated, unscoped Task Orchestrator database in place to the project-scoping convention — creates a project anchor root, re-parents existing work trees under it, and writes rootId back to config.yaml. Use when a user says: adopt project scope, migrate this database to project scoping, make this DB multi-project, project-scope this workspace, adopt existing database, or set up project scoping for an existing DB.

Why use Adopt Project Scope on TypingMind?

Because you install it once and use it with any model. Adopt Project Scope is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Adopt Project Scope in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/jpicklyk/task-orchestrator/tree/main/claude-plugins/task-orchestrator/skills/adopt-project-scope. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Adopt Project Scope?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Adopt Project Scope?

As many as you like. As long as a model supports skills, you can use Adopt Project Scope with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Adopt Project Scope AI skill free?

Yes. It is published on GitHub by jpicklyk under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇