@orderful/droid 0.50.0 → 0.52.0
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/.claude-plugin/plugin.json +1 -0
- package/CHANGELOG.md +20 -0
- package/dist/bin/droid.js +72 -4
- package/dist/commands/pack.d.ts +1 -0
- package/dist/commands/pack.d.ts.map +1 -1
- package/dist/lib/pack.d.ts +8 -0
- package/dist/lib/pack.d.ts.map +1 -1
- package/dist/tools/brain/.claude-plugin/plugin.json +1 -1
- package/dist/tools/brain/TOOL.yaml +1 -1
- package/dist/tools/brain/skills/brain/SKILL.md +4 -0
- package/dist/tools/brain/skills/brain/references/workflows.md +21 -7
- package/dist/tools/plan/.claude-plugin/plugin.json +1 -1
- package/dist/tools/plan/TOOL.yaml +1 -1
- package/dist/tools/plan/commands/plan.md +3 -2
- package/dist/tools/plan/skills/plan/SKILL.md +31 -10
- package/dist/tools/plan/skills/plan/references/workflows.md +44 -14
- package/dist/tools/project/.claude-plugin/plugin.json +1 -1
- package/dist/tools/project/TOOL.yaml +5 -1
- package/dist/tools/project/skills/project/SKILL.md +32 -1
- package/dist/tools/project/skills/project/references/loading.md +1 -0
- package/dist/tools/project/skills/project/references/pulling.md +57 -0
- package/dist/tools/project/skills/project/references/pushing.md +79 -0
- package/dist/tools/propose-plan/.claude-plugin/plugin.json +19 -0
- package/dist/tools/propose-plan/TOOL.yaml +17 -0
- package/dist/tools/propose-plan/skills/propose-plan/SKILL.md +190 -0
- package/dist/tools/propose-plan/skills/propose-plan/references/output-schema.json +85 -0
- package/package.json +1 -1
- package/src/bin/droid.ts +2 -1
- package/src/commands/pack.ts +32 -2
- package/src/lib/pack.test.ts +26 -1
- package/src/lib/pack.ts +66 -3
- package/src/tools/brain/.claude-plugin/plugin.json +1 -1
- package/src/tools/brain/TOOL.yaml +1 -1
- package/src/tools/brain/skills/brain/SKILL.md +4 -0
- package/src/tools/brain/skills/brain/references/workflows.md +21 -7
- package/src/tools/plan/.claude-plugin/plugin.json +1 -1
- package/src/tools/plan/TOOL.yaml +1 -1
- package/src/tools/plan/commands/plan.md +3 -2
- package/src/tools/plan/skills/plan/SKILL.md +31 -10
- package/src/tools/plan/skills/plan/references/workflows.md +44 -14
- package/src/tools/project/.claude-plugin/plugin.json +1 -1
- package/src/tools/project/TOOL.yaml +5 -1
- package/src/tools/project/skills/project/SKILL.md +32 -1
- package/src/tools/project/skills/project/references/loading.md +1 -0
- package/src/tools/project/skills/project/references/pulling.md +57 -0
- package/src/tools/project/skills/project/references/pushing.md +79 -0
- package/src/tools/propose-plan/.claude-plugin/plugin.json +19 -0
- package/src/tools/propose-plan/TOOL.yaml +17 -0
- package/src/tools/propose-plan/skills/propose-plan/SKILL.md +190 -0
- package/src/tools/propose-plan/skills/propose-plan/references/output-schema.json +85 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Pulling from Codex
|
|
2
|
+
|
|
3
|
+
**Trigger:** `/project pull [codex:{name}]`
|
|
4
|
+
|
|
5
|
+
Pull shared organizational context into an existing local project.
|
|
6
|
+
|
|
7
|
+
## Procedure
|
|
8
|
+
|
|
9
|
+
1. **Resolve codex project name:**
|
|
10
|
+
- If `codex:{name}` provided → use it, save to PROJECT.md frontmatter as `codex_project: {name}`
|
|
11
|
+
- If not provided → read `codex_project` from PROJECT.md frontmatter
|
|
12
|
+
- If neither → ask user
|
|
13
|
+
|
|
14
|
+
2. **Identify the local project:**
|
|
15
|
+
- If in a loaded project session → use that project
|
|
16
|
+
- If no project loaded → search and load first (same as `/project search`)
|
|
17
|
+
|
|
18
|
+
3. **Load codex entry:**
|
|
19
|
+
- Run `droid config --get tools.codex` and get `codex_repo` from the JSON output
|
|
20
|
+
- Sync: `git -C {codex_repo} pull --rebase --quiet`
|
|
21
|
+
- Locate `{codex_repo}/projects/{name}/`
|
|
22
|
+
- If not found → error with suggestions from available codex projects
|
|
23
|
+
|
|
24
|
+
4. **Read codex documents:**
|
|
25
|
+
- PRD.md → goals, requirements, status
|
|
26
|
+
- TECH-DESIGN.md → architecture, patterns, key paths
|
|
27
|
+
- DECISIONS.md → recent decisions
|
|
28
|
+
- Any other docs in the codex project folder
|
|
29
|
+
|
|
30
|
+
5. **Read current PROJECT.md** and identify:
|
|
31
|
+
- Sections that overlap with codex content
|
|
32
|
+
- Codex content that's newer or more detailed
|
|
33
|
+
- Local content that has no codex equivalent (leave untouched)
|
|
34
|
+
|
|
35
|
+
6. **Propose updates** (never auto-apply):
|
|
36
|
+
- **Be highly selective** — PROJECT.md has context limits. Only propose content that is critical for the AI to have when working on this project.
|
|
37
|
+
- Categorise: "New from codex", "Updated in codex", "No change needed"
|
|
38
|
+
- Preserve local implementation details — codex updates should ADD context, not overwrite working notes
|
|
39
|
+
- Prefer links to codex over copying large sections
|
|
40
|
+
|
|
41
|
+
7. **After approval:**
|
|
42
|
+
- Apply approved changes
|
|
43
|
+
- Update frontmatter: `codex_project: {name}`
|
|
44
|
+
- Add `## Codex Sync` entry or update existing one:
|
|
45
|
+
```
|
|
46
|
+
**Last pulled:** {date} from codex:{name}
|
|
47
|
+
```
|
|
48
|
+
- Determine version bump (patch for context updates, minor if goals/scope changed)
|
|
49
|
+
- Add changelog entry noting codex pull
|
|
50
|
+
|
|
51
|
+
## Key Principles
|
|
52
|
+
|
|
53
|
+
- **Selective, not exhaustive** — only pull what's critical for project context. The project file has size limits.
|
|
54
|
+
- **Additive, not destructive** — codex updates enrich the local project, they don't replace local work
|
|
55
|
+
- **User reviews every change** — always propose, never auto-apply
|
|
56
|
+
- **Provenance tracked** — changelog and sync date show where context came from
|
|
57
|
+
- **Links over copies** — for large sections, link to codex rather than duplicating
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Pushing to Codex
|
|
2
|
+
|
|
3
|
+
**Trigger:** `/project push [codex:{name}]`
|
|
4
|
+
|
|
5
|
+
Push team-relevant content from a local project back to the shared codex.
|
|
6
|
+
|
|
7
|
+
## What Gets Shared
|
|
8
|
+
|
|
9
|
+
Not everything in a project is codex-worthy. Extract only team-relevant content:
|
|
10
|
+
|
|
11
|
+
| Share | Don't Share |
|
|
12
|
+
|-------|-------------|
|
|
13
|
+
| Architecture decisions with rationale | Personal work notes |
|
|
14
|
+
| New patterns discovered during implementation | In-progress tasks |
|
|
15
|
+
| Updated technical constraints | Session logs or worklogs |
|
|
16
|
+
| Status changes (shipped, blocked, pivoted) | Brain docs or research drafts |
|
|
17
|
+
| Key file paths or API changes | Local configuration |
|
|
18
|
+
|
|
19
|
+
## Procedure
|
|
20
|
+
|
|
21
|
+
1. **Resolve codex project name:**
|
|
22
|
+
- If `codex:{name}` provided → use it, save to PROJECT.md frontmatter as `codex_project: {name}`
|
|
23
|
+
- If not provided → read `codex_project` from PROJECT.md frontmatter
|
|
24
|
+
- If neither → ask user which codex project to update, save to frontmatter
|
|
25
|
+
- Codex project must already exist. If it doesn't, suggest `/codex new project {name}` first.
|
|
26
|
+
|
|
27
|
+
2. **Identify the local project:**
|
|
28
|
+
- If in a loaded project session → use that project
|
|
29
|
+
- If no project loaded → search and load first
|
|
30
|
+
|
|
31
|
+
3. **Read PROJECT.md** and companion docs (DECISIONS.md, WORKLOG.md if they exist)
|
|
32
|
+
|
|
33
|
+
4. **Load codex entry:**
|
|
34
|
+
- Run `droid config --get tools.codex` and get `codex_repo` from the JSON output
|
|
35
|
+
- Sync: `git -C {codex_repo} pull --rebase --quiet`
|
|
36
|
+
- Read existing codex project docs
|
|
37
|
+
|
|
38
|
+
5. **Extract and categorise shareable content:**
|
|
39
|
+
- **Decisions:** New entries from DECISIONS.md or inline decisions in PROJECT.md
|
|
40
|
+
- **Architecture:** Changes to technical approach, new patterns, updated diagrams
|
|
41
|
+
- **Status:** Phase changes, milestones hit, scope changes
|
|
42
|
+
- **Constraints:** Newly discovered technical constraints or gotchas
|
|
43
|
+
|
|
44
|
+
6. **Propose codex updates** (never auto-apply):
|
|
45
|
+
- For each item, show:
|
|
46
|
+
- What will be added/changed in codex
|
|
47
|
+
- Which codex file it targets (DECISIONS.md, TECH-DESIGN.md, PRD.md)
|
|
48
|
+
- A diff preview
|
|
49
|
+
- Let user accept, reject, or edit each item
|
|
50
|
+
|
|
51
|
+
7. **After approval:**
|
|
52
|
+
- Use codex git scripts for the write operation:
|
|
53
|
+
```bash
|
|
54
|
+
# Start write
|
|
55
|
+
droid config --get tools.codex | droid exec codex git-start-write --config - \
|
|
56
|
+
--branch codex/sync-{project-name}
|
|
57
|
+
|
|
58
|
+
# Make changes to codex files
|
|
59
|
+
|
|
60
|
+
# Normalize frontmatter
|
|
61
|
+
droid config --get tools.codex | droid exec codex normalize-frontmatter --config -
|
|
62
|
+
|
|
63
|
+
# Finish write (commit + PR)
|
|
64
|
+
droid config --get tools.codex | droid exec codex git-finish-write --config - \
|
|
65
|
+
--message "sync({project}): {summary}" \
|
|
66
|
+
--pr-title "sync({project}): Update from local project" \
|
|
67
|
+
--pr-body "{description of changes}"
|
|
68
|
+
```
|
|
69
|
+
- Update local PROJECT.md sync marker:
|
|
70
|
+
```
|
|
71
|
+
**Last pushed:** {date} to codex:{name}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Key Principles
|
|
75
|
+
|
|
76
|
+
- **Curated, not automated** — user explicitly reviews what gets shared
|
|
77
|
+
- **PR-based** — codex changes go through PR review, not direct commits
|
|
78
|
+
- **Additive** — adds to codex content, doesn't overwrite others' contributions
|
|
79
|
+
- **Idempotent** — running `/project push` twice proposes only new/changed content
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "droid-propose-plan",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Produce structured plans with evidence and actions for human approval in Orderful Agents. Use when an agent must propose write operations that require human-in-the-loop review before execution.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Orderful",
|
|
7
|
+
"url": "https://github.com/orderful"
|
|
8
|
+
},
|
|
9
|
+
"repository": "https://github.com/orderful/droid",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"droid",
|
|
13
|
+
"ai",
|
|
14
|
+
"propose-plan"
|
|
15
|
+
],
|
|
16
|
+
"skills": [
|
|
17
|
+
"./skills/propose-plan/SKILL.md"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: propose-plan
|
|
2
|
+
description: "Produce structured plans with evidence and actions for human approval in Orderful Agents. Use when an agent must propose write operations that require human-in-the-loop review before execution."
|
|
3
|
+
version: 0.1.0
|
|
4
|
+
status: beta
|
|
5
|
+
audience:
|
|
6
|
+
- all
|
|
7
|
+
|
|
8
|
+
includes:
|
|
9
|
+
skills:
|
|
10
|
+
- name: propose-plan
|
|
11
|
+
required: true
|
|
12
|
+
commands: []
|
|
13
|
+
agents: []
|
|
14
|
+
|
|
15
|
+
dependencies: []
|
|
16
|
+
|
|
17
|
+
config_schema: {}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: propose-plan
|
|
3
|
+
description: "Produce a structured plan with evidence and actions for human approval. Use when an agent must propose write operations, research current state before making changes, or produce actions that require human-in-the-loop review before execution."
|
|
4
|
+
globs: []
|
|
5
|
+
alwaysApply: false
|
|
6
|
+
allowed-tools: []
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Propose Plan
|
|
10
|
+
|
|
11
|
+
Structure your findings into a plan for human approval. Use this skill when you've researched the current state, understand what needs to happen, and are ready to propose the specific actions to take.
|
|
12
|
+
|
|
13
|
+
This skill teaches you how to package what you've learned into structured evidence and discrete actions that a reviewer can approve, modify, or reject before anything is executed.
|
|
14
|
+
|
|
15
|
+
Never call write tools directly. Each proposed write is described as an **action** in your plan output. Approved actions are executed separately after human review.
|
|
16
|
+
|
|
17
|
+
## Plan Structure
|
|
18
|
+
|
|
19
|
+
A plan has two parts:
|
|
20
|
+
|
|
21
|
+
1. **Evidence** — your analysis of the gathered context: what it shows, why changes are needed, and what will happen if the plan is approved
|
|
22
|
+
2. **Actions** — an ordered list of discrete operations to execute, each mapped to a specific tool call
|
|
23
|
+
|
|
24
|
+
The human reviewer sees your evidence first (the reasoning and impact), then reviews each action. Clear evidence builds trust; vague evidence gets rejected.
|
|
25
|
+
|
|
26
|
+
## Output Format
|
|
27
|
+
|
|
28
|
+
Produce a single JSON object matching the schema in `references/output-schema.json`. The shape:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"name": "string — short human-readable plan name (optional)",
|
|
33
|
+
"category": "string — grouping category, e.g. org-provisioning (optional)",
|
|
34
|
+
"evidence": {
|
|
35
|
+
"reasoning": "string — your analysis of what needs to happen and why",
|
|
36
|
+
"sourceSummary": "string — concise summary of what you found during research",
|
|
37
|
+
"sourceRaw": "object — raw source data from research (optional)",
|
|
38
|
+
"impactSummary": "string — what will change if this plan is approved",
|
|
39
|
+
"caveats": ["string — anything uncertain, auto-generated, or requiring follow-up"]
|
|
40
|
+
},
|
|
41
|
+
"actions": [
|
|
42
|
+
{
|
|
43
|
+
"type": "mcp_call",
|
|
44
|
+
"config": {
|
|
45
|
+
"tool": "string — the tool name exactly as you see it in your available tools"
|
|
46
|
+
},
|
|
47
|
+
"description": "string — human-readable description of what this action does",
|
|
48
|
+
"payload": "object — arguments to pass to the tool"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Evidence Fields
|
|
55
|
+
|
|
56
|
+
| Field | Purpose | Reviewer Experience |
|
|
57
|
+
| --------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
|
|
58
|
+
| `reasoning` | Your analysis — what needs to happen and why. Cite actual data from your research, not assumptions. | First thing they read. Establishes trust. |
|
|
59
|
+
| `sourceSummary` | Concise summary of what you found. Include entity names, IDs, and states. | Quick scan to verify you looked at the right things. |
|
|
60
|
+
| `sourceRaw` | Optional. Raw API responses or data snapshots as a JSON object. | Expandable detail for reviewers who want to verify. |
|
|
61
|
+
| `impactSummary` | What changes if the plan is approved. Be specific — "creates org X with ISA ID Y" not "creates an org". | The core decision point. Reviewer approves based on this. |
|
|
62
|
+
| `caveats` | Optional. Flag anything uncertain, auto-generated, or needing manual follow-up. | Prevents surprises. Reviewers appreciate honesty about gaps. |
|
|
63
|
+
|
|
64
|
+
### Action Fields
|
|
65
|
+
|
|
66
|
+
| Field | Purpose |
|
|
67
|
+
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
68
|
+
| `type` | Action category. Use `mcp_call` for MCP tool invocations. |
|
|
69
|
+
| `config` | Identifies which tool to call. For `mcp_call`: `{ "tool": "create_org" }`. Use the tool name exactly as it appears in your available tools. |
|
|
70
|
+
| `description` | Human-readable summary. The reviewer sees this before expanding the payload. Write it for someone who won't read the raw payload. |
|
|
71
|
+
| `payload` | Raw arguments passed directly to the tool. No wrapping or nesting — exactly what the tool expects. |
|
|
72
|
+
|
|
73
|
+
## Building Evidence From What You Know
|
|
74
|
+
|
|
75
|
+
By the time you're writing a plan, you've already done the research — queried tools, read documents, collected the data you need. Your job now is to analyse what you found and structure it into a plan.
|
|
76
|
+
|
|
77
|
+
If read tools are still available, use them to **verify and fill gaps** — check for conflicts, confirm preconditions, resolve ambiguities. But the research phase is behind you. Focus on structuring what you know into clear evidence and precise actions.
|
|
78
|
+
|
|
79
|
+
When writing evidence:
|
|
80
|
+
|
|
81
|
+
1. **Cite the gathered data** — reference specific entities, IDs, and values. A reviewer can't verify "the account exists" but they can verify "Salesforce account SF-001234 (Acme Corp) has no OrgID".
|
|
82
|
+
2. **Flag conflicts** — if the gathered data reveals duplicates, collisions, or unexpected state, surface that prominently.
|
|
83
|
+
3. **Note gaps** — if data is missing or ambiguous, say so in caveats rather than guessing. A plan with honest gaps is more useful than one with invented data.
|
|
84
|
+
|
|
85
|
+
## Writing Actions
|
|
86
|
+
|
|
87
|
+
Each action represents one tool call. `config.tool` identifies which tool to call, and `payload` carries the arguments — both must be precise.
|
|
88
|
+
|
|
89
|
+
**Order matters.** List actions in dependency order — if action B depends on the result of action A, action A comes first. The execution step processes them sequentially.
|
|
90
|
+
|
|
91
|
+
**One action per tool call.** Don't batch multiple operations into a single action. If provisioning requires creating an org, then creating a billing customer, those are two separate actions.
|
|
92
|
+
|
|
93
|
+
**Only propose tools from your available list.** You've been given a set of write tools you can propose actions for. If a required operation isn't in that list, note it as a caveat — don't invent actions for tools that don't exist.
|
|
94
|
+
|
|
95
|
+
**Payloads must be complete.** Include every required field the tool expects. Don't leave placeholders or TODOs in payloads. For values that don't exist yet because they come from a prior action, use a template reference (see below). If you can't determine a value at all, flag it in caveats and omit the action.
|
|
96
|
+
|
|
97
|
+
### Action Dependencies (Template References)
|
|
98
|
+
|
|
99
|
+
When an action needs a value produced by a prior action — like an org ID that doesn't exist until the org is created — use a Handlebars template reference in the payload value.
|
|
100
|
+
|
|
101
|
+
**Syntax:** `{{ actions.N.result.path }}` where `N` is the zero-based action index and `path` navigates the result object.
|
|
102
|
+
|
|
103
|
+
At execution time, the system resolves these references using the actual results from completed actions. The human reviewer sees the template expression and understands the data flow — they're approving the intent ("use the org ID from step 1"), not the literal runtime value.
|
|
104
|
+
|
|
105
|
+
**Rules:**
|
|
106
|
+
|
|
107
|
+
- Only reference actions that come _before_ the current action (lower index)
|
|
108
|
+
- Use dot-path navigation to reach nested values: `{{ actions.0.result.org.id }}`
|
|
109
|
+
- Static values and template references can coexist in the same payload
|
|
110
|
+
- Every other payload field should still use concrete values you already know
|
|
111
|
+
|
|
112
|
+
**Example:**
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"type": "mcp_call",
|
|
117
|
+
"config": { "tool": "create_billing_customer" },
|
|
118
|
+
"description": "Create ChargeBee billing customer for Acme Corp using new org ID",
|
|
119
|
+
"payload": {
|
|
120
|
+
"orgId": "{{ actions.0.result.id }}",
|
|
121
|
+
"orgName": "Acme Corp",
|
|
122
|
+
"contactEmail": "jane@acme.com"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Here `orgId` will be resolved at runtime from the result of action 0 (e.g., `create_org`). The reviewer sees that billing depends on the org creation step.
|
|
128
|
+
|
|
129
|
+
## Example
|
|
130
|
+
|
|
131
|
+
Say you've been asked to provision an Orderful org for a new customer. You've queried Salesforce, searched for existing orgs, and gathered the relevant data. Your plan output might look like:
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"evidence": {
|
|
136
|
+
"reasoning": "Salesforce account Acme Corp (SF-001234) was signed on 2026-03-01 and has no Orderful OrgID. No existing Orderful org matches by name or ISA ID. The account's primary contact is jane@acme.com. ISA ID derived from Customer_ISA_ID__c field: ACME001.",
|
|
137
|
+
"sourceSummary": "Salesforce: Acme Corp (SF-001234), signed 2026-03-01, no OrgID. Orderful: no org named 'Acme Corp', no ISA ID 'ACME001' in use.",
|
|
138
|
+
"sourceRaw": {
|
|
139
|
+
"salesforce_account": { "Id": "SF-001234", "Name": "Acme Corp", "OrgID__c": null, "Customer_ISA_ID__c": "ACME001" },
|
|
140
|
+
"orderful_org_search": { "results": [] }
|
|
141
|
+
},
|
|
142
|
+
"impactSummary": "Creates Orderful org 'Acme Corp' with ISA ID 'ACME001', creates billing customer in ChargeBee, and sends welcome email to jane@acme.com.",
|
|
143
|
+
"caveats": ["ARR not found in Salesforce — billing customer will be created without revenue data. Manual update may be needed."]
|
|
144
|
+
},
|
|
145
|
+
"actions": [
|
|
146
|
+
{
|
|
147
|
+
"type": "mcp_call",
|
|
148
|
+
"config": { "tool": "create_org" },
|
|
149
|
+
"description": "Create Orderful org 'Acme Corp' with ISA ID 'ACME001'",
|
|
150
|
+
"payload": {
|
|
151
|
+
"name": "Acme Corp",
|
|
152
|
+
"isaId": "ACME001",
|
|
153
|
+
"contactEmail": "jane@acme.com"
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"type": "mcp_call",
|
|
158
|
+
"config": { "tool": "create_billing_customer" },
|
|
159
|
+
"description": "Create ChargeBee billing customer for Acme Corp using new org ID",
|
|
160
|
+
"payload": {
|
|
161
|
+
"orgId": "{{ actions.0.result.id }}",
|
|
162
|
+
"orgName": "Acme Corp",
|
|
163
|
+
"contactEmail": "jane@acme.com"
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"type": "mcp_call",
|
|
168
|
+
"config": { "tool": "invite_user" },
|
|
169
|
+
"description": "Send welcome email to jane@acme.com for Acme Corp org",
|
|
170
|
+
"payload": {
|
|
171
|
+
"email": "jane@acme.com",
|
|
172
|
+
"orgId": "{{ actions.0.result.id }}"
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## What Not To Do
|
|
180
|
+
|
|
181
|
+
These are hard rules. Violating any of them will cause the plan to be rejected or produce incorrect results.
|
|
182
|
+
|
|
183
|
+
- **Don't call write tools directly.** Your job is to propose, not execute. Every write operation must be described as an action in your plan output. If you call a write tool yourself, the approval flow is bypassed and there is no audit trail.
|
|
184
|
+
- **Don't include read operations as actions.** Actions are for writes only — things that change state and need human approval. Reading data is research, not an action. If you need to look something up, use read tools directly during your research phase.
|
|
185
|
+
- **Don't fabricate data.** If a value wasn't in your research and you can't verify it with read tools, say so in caveats. Never guess IDs, names, emails, or configuration values. A plan with honest gaps gets sent back for more research. A plan with invented data gets approved and breaks things.
|
|
186
|
+
- **Don't produce actions for tools outside your available list.** You can only propose actions for tools you've been given. If an operation requires a tool you don't have, note it as a caveat — don't invent an action for it. The execution step will reject unknown tools.
|
|
187
|
+
- **Don't skip evidence.** A plan without evidence will be rejected. The reviewer needs to see what you found, what it means, and what will change. Evidence is not optional filler — it is how the reviewer decides whether to approve.
|
|
188
|
+
- **Don't combine multiple operations into one action.** Each action is one tool call. "Create org and set up billing" is two actions, not one. The execution step calls each action individually — a combined action will fail.
|
|
189
|
+
- **Don't hardcode values from actions that haven't run yet.** If action 2 needs the org ID from action 1, don't guess or invent a value — use a template reference: `"orgId": "{{ actions.0.result.id }}"`. The execution step resolves these at runtime. Never put a placeholder like `"TBD"` or `"<org-id>"` in a payload.
|
|
190
|
+
- **Don't use vague descriptions.** "Create org" tells the reviewer nothing. "Create Orderful org 'Acme Corp' with ISA ID 'ACME001'" tells them exactly what will happen. The description is what the reviewer reads before deciding whether to expand the payload.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "PlanOutput",
|
|
4
|
+
"description": "Structured plan output produced by an LLM agent for human review and approval",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["evidence", "actions"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"name": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Short human-readable name for the plan"
|
|
11
|
+
},
|
|
12
|
+
"category": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"description": "Grouping category for the plan (e.g. org-provisioning)"
|
|
15
|
+
},
|
|
16
|
+
"evidence": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"description": "Research findings and rationale supporting the plan",
|
|
19
|
+
"required": ["reasoning", "sourceSummary", "impactSummary"],
|
|
20
|
+
"properties": {
|
|
21
|
+
"reasoning": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"description": "Analysis of what needs to happen and why"
|
|
24
|
+
},
|
|
25
|
+
"sourceSummary": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "Concise summary of research findings with entity names and IDs"
|
|
28
|
+
},
|
|
29
|
+
"sourceRaw": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"description": "Raw source data from research",
|
|
32
|
+
"additionalProperties": true
|
|
33
|
+
},
|
|
34
|
+
"impactSummary": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "What changes if the plan is approved"
|
|
37
|
+
},
|
|
38
|
+
"caveats": {
|
|
39
|
+
"type": "array",
|
|
40
|
+
"items": { "type": "string" },
|
|
41
|
+
"description": "Risks or caveats to flag for review"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"additionalProperties": false
|
|
45
|
+
},
|
|
46
|
+
"actions": {
|
|
47
|
+
"type": "array",
|
|
48
|
+
"description": "Ordered list of MCP tool calls to execute",
|
|
49
|
+
"items": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"required": ["type", "config", "description", "payload"],
|
|
52
|
+
"properties": {
|
|
53
|
+
"type": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"const": "mcp_call",
|
|
56
|
+
"description": "Action category — v0 only supports mcp_call"
|
|
57
|
+
},
|
|
58
|
+
"config": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"description": "Configuration specifying which MCP tool to invoke",
|
|
61
|
+
"required": ["tool"],
|
|
62
|
+
"properties": {
|
|
63
|
+
"tool": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"description": "Tool name as it appears in the available tools"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"additionalProperties": false
|
|
69
|
+
},
|
|
70
|
+
"description": {
|
|
71
|
+
"type": "string",
|
|
72
|
+
"description": "Human-readable description of what this action does"
|
|
73
|
+
},
|
|
74
|
+
"payload": {
|
|
75
|
+
"type": "object",
|
|
76
|
+
"description": "Arguments to pass to the MCP tool",
|
|
77
|
+
"additionalProperties": true
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"additionalProperties": false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"additionalProperties": false
|
|
85
|
+
}
|
package/package.json
CHANGED
package/src/bin/droid.ts
CHANGED
|
@@ -106,9 +106,10 @@ program
|
|
|
106
106
|
|
|
107
107
|
program
|
|
108
108
|
.command('pack')
|
|
109
|
-
.description('Create
|
|
109
|
+
.description('Create zip packs for distribution')
|
|
110
110
|
.argument('[audience]', 'Target audience (e.g., engineering, customer-support)')
|
|
111
111
|
.option('-l, --list', 'List available audiences and tool counts')
|
|
112
|
+
.option('-t, --tool <name>', 'Pack a single tool by name')
|
|
112
113
|
.option('-o, --output <dir>', 'Output directory (default: cwd)')
|
|
113
114
|
.action(packCommand);
|
|
114
115
|
|
package/src/commands/pack.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { ToolAudience } from '../lib/types';
|
|
3
|
-
import { getAudienceInfo, buildPack } from '../lib/pack';
|
|
3
|
+
import { getAudienceInfo, buildPack, buildToolPack } from '../lib/pack';
|
|
4
4
|
|
|
5
5
|
function isValidAudience(value: string): value is ToolAudience {
|
|
6
6
|
return Object.values(ToolAudience).includes(value as ToolAudience);
|
|
@@ -8,7 +8,7 @@ function isValidAudience(value: string): value is ToolAudience {
|
|
|
8
8
|
|
|
9
9
|
export async function packCommand(
|
|
10
10
|
audience: string | undefined,
|
|
11
|
-
options: { list?: boolean; output?: string },
|
|
11
|
+
options: { list?: boolean; output?: string; tool?: string },
|
|
12
12
|
): Promise<void> {
|
|
13
13
|
// --list mode: show audiences and tool counts
|
|
14
14
|
if (options.list) {
|
|
@@ -29,10 +29,40 @@ export async function packCommand(
|
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// --tool mode: pack a single tool
|
|
33
|
+
if (options.tool) {
|
|
34
|
+
const outputDir = options.output || process.cwd();
|
|
35
|
+
|
|
36
|
+
console.log(
|
|
37
|
+
chalk.bold(`\nPacking tool ${chalk.cyan(options.tool)}...`),
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const result = await buildToolPack({ toolName: options.tool, outputDir });
|
|
41
|
+
|
|
42
|
+
if (!result.success) {
|
|
43
|
+
console.error(chalk.red(`\n${result.message}`));
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
console.log(chalk.green(`\n${result.message}`));
|
|
48
|
+
console.log(chalk.gray(` Output: ${result.outputPath}`));
|
|
49
|
+
|
|
50
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
51
|
+
console.log(chalk.yellow('\nWarnings:'));
|
|
52
|
+
for (const warning of result.warnings) {
|
|
53
|
+
console.log(chalk.yellow(` - ${warning}`));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log('');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
32
61
|
// Require audience argument
|
|
33
62
|
if (!audience) {
|
|
34
63
|
console.error(chalk.red('\nError: audience argument required'));
|
|
35
64
|
console.log(chalk.gray('Usage: droid pack <audience>'));
|
|
65
|
+
console.log(chalk.gray(' droid pack --tool <name>'));
|
|
36
66
|
console.log(chalk.gray(' droid pack --list'));
|
|
37
67
|
process.exit(1);
|
|
38
68
|
}
|
package/src/lib/pack.test.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { describe, it, expect } from 'bun:test';
|
|
2
|
+
import { existsSync, unlinkSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
2
5
|
import { ToolAudience } from './types';
|
|
3
|
-
import { getToolsForAudience, getAudienceInfo } from './pack';
|
|
6
|
+
import { getToolsForAudience, getAudienceInfo, buildToolPack } from './pack';
|
|
4
7
|
|
|
5
8
|
// Uses real bundled tools for integration-style tests
|
|
6
9
|
|
|
@@ -83,3 +86,25 @@ describe('getAudienceInfo', () => {
|
|
|
83
86
|
expect(engineering!.toolNames).toContain('brain');
|
|
84
87
|
});
|
|
85
88
|
});
|
|
89
|
+
|
|
90
|
+
describe('buildToolPack', () => {
|
|
91
|
+
it('should create a zip for a single tool', async () => {
|
|
92
|
+
const outputDir = tmpdir();
|
|
93
|
+
const result = await buildToolPack({ toolName: 'brain', outputDir });
|
|
94
|
+
|
|
95
|
+
expect(result.success).toBe(true);
|
|
96
|
+
expect(result.toolCount).toBe(1);
|
|
97
|
+
expect(result.outputPath).toContain('droid-brain.zip');
|
|
98
|
+
expect(existsSync(result.outputPath!)).toBe(true);
|
|
99
|
+
|
|
100
|
+
// Cleanup
|
|
101
|
+
unlinkSync(result.outputPath!);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should return error for unknown tool', async () => {
|
|
105
|
+
const result = await buildToolPack({ toolName: 'nonexistent', outputDir: tmpdir() });
|
|
106
|
+
|
|
107
|
+
expect(result.success).toBe(false);
|
|
108
|
+
expect(result.message).toContain('not found');
|
|
109
|
+
});
|
|
110
|
+
});
|
package/src/lib/pack.ts
CHANGED
|
@@ -15,6 +15,11 @@ export interface PackOptions {
|
|
|
15
15
|
outputDir: string;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
export interface ToolPackOptions {
|
|
19
|
+
toolName: string;
|
|
20
|
+
outputDir: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
18
23
|
export interface PackResult {
|
|
19
24
|
success: boolean;
|
|
20
25
|
message: string;
|
|
@@ -100,7 +105,7 @@ function collectToolArtifacts(
|
|
|
100
105
|
// references/
|
|
101
106
|
const refsDir = join(skillDir, 'references');
|
|
102
107
|
if (existsSync(refsDir)) {
|
|
103
|
-
const refFiles = readdirSync(refsDir).filter((f) => f.
|
|
108
|
+
const refFiles = readdirSync(refsDir).filter((f) => !f.startsWith('.'));
|
|
104
109
|
for (const file of refFiles) {
|
|
105
110
|
artifacts.push({
|
|
106
111
|
sourcePath: join(refsDir, file),
|
|
@@ -170,9 +175,9 @@ function generateClaudeMd(tools: ToolManifest[]): string {
|
|
|
170
175
|
/**
|
|
171
176
|
* Generate README.md with install instructions
|
|
172
177
|
*/
|
|
173
|
-
function generateReadme(
|
|
178
|
+
function generateReadme(label: string, tools: ToolManifest[]): string {
|
|
174
179
|
const lines = [
|
|
175
|
-
`# Droid Pack: ${
|
|
180
|
+
`# Droid Pack: ${label}`,
|
|
176
181
|
'',
|
|
177
182
|
'This pack contains AI skills, commands, and agents for Claude Desktop.',
|
|
178
183
|
'',
|
|
@@ -227,6 +232,64 @@ function checkDependencies(tools: ToolManifest[]): string[] {
|
|
|
227
232
|
return warnings;
|
|
228
233
|
}
|
|
229
234
|
|
|
235
|
+
/**
|
|
236
|
+
* Build a zip pack for a single tool
|
|
237
|
+
*/
|
|
238
|
+
export async function buildToolPack(options: ToolPackOptions): Promise<PackResult> {
|
|
239
|
+
const { toolName, outputDir } = options;
|
|
240
|
+
|
|
241
|
+
const allTools = getBundledTools();
|
|
242
|
+
const tool = allTools.find((t) => t.name === toolName);
|
|
243
|
+
|
|
244
|
+
if (!tool) {
|
|
245
|
+
return {
|
|
246
|
+
success: false,
|
|
247
|
+
message: `Tool '${toolName}' not found`,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const tools = [tool];
|
|
252
|
+
const warnings = checkDependencies(tools);
|
|
253
|
+
|
|
254
|
+
const filename = `droid-${toolName}.zip`;
|
|
255
|
+
const outputPath = join(outputDir, filename);
|
|
256
|
+
|
|
257
|
+
return new Promise((resolve) => {
|
|
258
|
+
const output = createWriteStream(outputPath);
|
|
259
|
+
const archive = archiver('zip', { zlib: { level: 9 } });
|
|
260
|
+
|
|
261
|
+
output.on('close', () => {
|
|
262
|
+
resolve({
|
|
263
|
+
success: true,
|
|
264
|
+
message: `Pack created: ${filename}`,
|
|
265
|
+
outputPath,
|
|
266
|
+
toolCount: 1,
|
|
267
|
+
warnings: warnings.length > 0 ? warnings : undefined,
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
archive.on('error', (err: Error) => {
|
|
272
|
+
resolve({
|
|
273
|
+
success: false,
|
|
274
|
+
message: `Failed to create pack: ${err.message}`,
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
archive.pipe(output);
|
|
279
|
+
|
|
280
|
+
const artifacts = collectToolArtifacts(tool);
|
|
281
|
+
for (const artifact of artifacts) {
|
|
282
|
+
const content = readFileSync(artifact.sourcePath, 'utf-8');
|
|
283
|
+
archive.append(content, { name: artifact.zipPath });
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
archive.append(generateClaudeMd(tools), { name: 'CLAUDE.md' });
|
|
287
|
+
archive.append(generateReadme(toolName, tools), { name: 'README.md' });
|
|
288
|
+
|
|
289
|
+
archive.finalize();
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
230
293
|
/**
|
|
231
294
|
* Build a zip pack for the given audience
|
|
232
295
|
*/
|