@orderful/droid 0.52.1 → 0.54.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.
@@ -23,6 +23,10 @@ A plan has two parts:
23
23
 
24
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
25
 
26
+ **An empty actions array is valid.** If your research shows no writes are needed — conflicts block all operations, the work is already done, or the situation requires human input before any action can be proposed — return `"actions": []` with evidence explaining why. This is a "findings only" output. The reviewer still gets your analysis.
27
+
28
+ **Partial plans are also valid.** If some items are blocked but others are clear, include actions only for the clear ones. Flag blocked items in caveats with specific reasons. Don't skip the entire plan because one item has a conflict.
29
+
26
30
  ## Output Format
27
31
 
28
32
  Produce a single JSON object matching the schema in `references/output-schema.json`. The shape:
@@ -32,6 +36,7 @@ Produce a single JSON object matching the schema in `references/output-schema.js
32
36
  "name": "string — short human-readable plan name (optional)",
33
37
  "category": "string — grouping category, e.g. org-provisioning (optional)",
34
38
  "evidence": {
39
+ "planSummary": "string — one-line plain-language summary, e.g. 'Provision org and billing for Acme Corp' (optional)",
35
40
  "reasoning": "string — your analysis of what needs to happen and why",
36
41
  "sourceSummary": "string — concise summary of what you found during research",
37
42
  "sourceRaw": "string — JSON-stringified raw source data from research (optional)",
@@ -53,9 +58,10 @@ Produce a single JSON object matching the schema in `references/output-schema.js
53
58
 
54
59
  ### Evidence Fields
55
60
 
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. |
61
+ | Field | Purpose | Reviewer Experience |
62
+ | --------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
63
+ | `planSummary` | Optional. One-line plain-language summary of the plan. Keep it short and specific entity names, not jargon. | Shown in headers and card previews. Quick scan context. |
64
+ | `reasoning` | Your analysis — what needs to happen and why. Cite actual data from your research, not assumptions. | First thing they read. Establishes trust. |
59
65
  | `sourceSummary` | Concise summary of what you found. Include entity names, IDs, and states. | Quick scan to verify you looked at the right things. |
60
66
  | `sourceRaw` | Optional. JSON-stringified raw API responses or data snapshots. | Expandable detail for reviewers who want to verify. |
61
67
  | `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. |
@@ -129,6 +135,7 @@ Say you've been asked to provision an Orderful org for a new customer. You've qu
129
135
  ```json
130
136
  {
131
137
  "evidence": {
138
+ "planSummary": "Provision org, billing, and welcome email for Acme Corp",
132
139
  "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.",
133
140
  "sourceSummary": "Salesforce: Acme Corp (SF-001234), signed 2026-03-01, no OrgID. Orderful: no org named 'Acme Corp', no ISA ID 'ACME001' in use.",
134
141
  "sourceRaw": "{\"salesforce_account\":{\"Id\":\"SF-001234\",\"Name\":\"Acme Corp\",\"OrgID__c\":null,\"Customer_ISA_ID__c\":\"ACME001\"},\"orderful_org_search\":{\"results\":[]}}",
@@ -166,7 +173,8 @@ These are hard rules. Violating any of them will cause the plan to be rejected o
166
173
  - **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.
167
174
  - **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.
168
175
  - **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.
169
- - **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.
176
+ - **Don't skip evidence.** A plan without evidence will be rejected — even if the actions array is empty. 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.
177
+ - **Don't force actions when none are possible.** If conflicts or preconditions block all operations, return an empty actions array with evidence explaining why. A findings-only plan is better than a plan with actions that will fail.
170
178
  - **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.
171
179
  - **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.
172
180
  - **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.
@@ -18,6 +18,10 @@
18
18
  "description": "Research findings and rationale supporting the plan",
19
19
  "required": ["reasoning", "sourceSummary", "impactSummary"],
20
20
  "properties": {
21
+ "planSummary": {
22
+ "type": "string",
23
+ "description": "One-line plain-language summary of the plan"
24
+ },
21
25
  "reasoning": {
22
26
  "type": "string",
23
27
  "description": "Analysis of what needs to happen and why"
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "droid-webform",
3
+ "version": "0.1.0",
4
+ "description": "Generate PrintView webform templates for EDI transaction types. Reads Orderful JSON schemas and sample transactions, proposes a visual layout, then generates template code with reusable subsection items.",
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
+ "webform"
15
+ ],
16
+ "skills": [
17
+ "./skills/webform/SKILL.md"
18
+ ],
19
+ "commands": [
20
+ "./commands/webform.md"
21
+ ]
22
+ }
@@ -0,0 +1,26 @@
1
+ name: webform
2
+ description: "Generate PrintView webform templates for EDI transaction types. Reads Orderful JSON schemas and sample transactions, proposes a visual layout, then generates template code with reusable subsection items."
3
+ version: 0.1.0
4
+ status: beta
5
+ audience:
6
+ - engineering
7
+ - product
8
+
9
+ includes:
10
+ skills:
11
+ - name: webform
12
+ required: true
13
+ commands:
14
+ - name: webform
15
+ is_alias: false
16
+ agents: []
17
+
18
+ dependencies: []
19
+
20
+ config_schema:
21
+ schema_repo:
22
+ type: string
23
+ description: "Path to orderful-workspace repo (contains Orderful JSON schemas)"
24
+ ui_repo:
25
+ type: string
26
+ description: "Path to o2-app-ui repo (contains PrintView templates and subsection items)"
@@ -0,0 +1,17 @@
1
+ ---
2
+ name: webform
3
+ description: "Generate PrintView webform templates for EDI transaction types"
4
+ argument-hint: "{transaction-type} [sample-transaction-path-or-inline-json]"
5
+ ---
6
+
7
+ # /webform
8
+
9
+ **User invoked:** `/webform $ARGUMENTS`
10
+
11
+ **Your task:** Invoke the **webform skill** with these arguments.
12
+
13
+ ## Examples
14
+
15
+ - `/webform 850 ~/Downloads/sample-850.json` → Generate 850 template from sample
16
+ - `/webform 812 ~/Downloads/812.edi` → Generate 812 template from EDI file
17
+ - `/webform 997` → Start 997 template (will ask for sample transaction)
@@ -0,0 +1,354 @@
1
+ ---
2
+ name: webform
3
+ description: "Generate PrintView webform templates for EDI transaction types. Reads Orderful JSON schemas and sample transactions, proposes a visual layout, then generates template code with reusable subsection items. User prompts like '/webform 850 sample.json', 'create a webform for 810'."
4
+ argument-hint: "{transaction-type} [sample-transaction-path-or-inline-json]"
5
+ globs: []
6
+ alwaysApply: false
7
+ allowed-tools: [Read, Write, Edit, Glob, Grep, Bash, Agent]
8
+ ---
9
+
10
+ # Webform Skill
11
+
12
+ Generate PrintView webform templates for Orderful EDI transaction types.
13
+
14
+ ## When to Use
15
+
16
+ - Creating a new webform/PrintView template for a transaction type
17
+ - User says "webform", "print view", "create a template for {type}"
18
+
19
+ ## When NOT to Use
20
+
21
+ - Modifying existing templates (just edit directly)
22
+ - Working on forms-mapper shapes/node mappers (different system)
23
+
24
+ ## Configuration
25
+
26
+ **IMPORTANT:** Run `droid config --get tools.webform` first and parse the JSON output. If paths are not configured, **ask the user** for them.
27
+
28
+ | Setting | Default | Description |
29
+ |---------|---------|-------------|
30
+ | `schema_repo` | (none) | Path to orderful-workspace repo (contains Orderful JSON schemas) |
31
+ | `ui_repo` | (none) | Path to o2-app-ui repo (contains PrintView templates) |
32
+ | `override` | (none) | User-defined behaviour overrides |
33
+
34
+ **If not configured:** Ask the user:
35
+ > "I need two repo paths to generate webform templates:
36
+ > 1. **orderful-workspace** — where are the Orderful JSON schemas? (e.g., `~/Projects/orderful-workspace`)
37
+ > 2. **o2-app-ui** — where are the PrintView templates? (e.g., `~/Projects/o2-app-ui`)"
38
+
39
+ Then set their choices:
40
+ ```bash
41
+ droid config --set tools.webform.schema_repo="{user's choice}"
42
+ droid config --set tools.webform.ui_repo="{user's choice}"
43
+ ```
44
+
45
+ ### Derived Paths
46
+
47
+ Once configured, derive all paths from the config:
48
+
49
+ - **Schema location:** `{schema_repo}/libs/schemas/src/lib/data/transactionSchemas/orderful/{type}.schema.json`
50
+ - **Template location:** `{ui_repo}/src/pages/Transaction/PrintView/templates/`
51
+ - **Subsection items:** `{ui_repo}/src/pages/Transaction/PrintView/templates/subsectionItems/`
52
+ - **Helpers:** `{ui_repo}/src/pages/Transaction/PrintView/templates/helpers.ts`
53
+ - **Types:** `{ui_repo}/src/pages/Transaction/PrintView/types/index.ts`
54
+ - **Tests:** `{ui_repo}/src/pages/Transaction/PrintView/templates/tests/templates.spec.ts`
55
+ - **Allowed types:** `{ui_repo}/src/pages/Transaction/CreateTransaction/index.tsx`
56
+ - **Print view routing:** `{ui_repo}/src/shared/helpers/printViewHelpers/getPrintViewComponentAndCsv.ts`
57
+
58
+ **Overrides:** This skill supports user-defined overrides. See `/droid` skill § Skill Overrides for how to create, register, and use overrides.
59
+
60
+ ## Invocation
61
+
62
+ ```
63
+ /webform {transaction-type} {sample-transaction}
64
+ ```
65
+
66
+ - `transaction-type`: e.g., `850`, `810`, `997`
67
+ - `sample-transaction`: file path to a JSON file, or inline JSON pasted by the user. If not provided, ask for it.
68
+
69
+ ## Workflow
70
+
71
+ Follow these steps in order. Do NOT skip the visual layout step.
72
+
73
+ ### Step 1: Load Inputs
74
+
75
+ 1. **Read config** — run `droid config --get tools.webform` and parse the JSON. If not configured, ask the user.
76
+
77
+ 2. **Read the Orderful JSON schema** for the transaction type:
78
+ ```
79
+ {schema_repo}/libs/schemas/src/lib/data/transactionSchemas/orderful/{type}.schema.json
80
+ ```
81
+ This gives you field names (camelCase), titles, descriptions, and nesting structure. Pay attention to:
82
+ - `schemaType`: `element`, `segment`, `segmentArray`, `loop`, `loopArray`
83
+ - `title`: human-readable name like "BEG03 - Purchase Order Number"
84
+ - `segmentIdentifier`: X12 segment ID like "BEG", "N1", "PO1"
85
+
86
+ 3. **Parse the sample transaction** (file or inline JSON). This determines which segments/loops are actually used and should appear in the template. **Only model what's in the sample — not every possible field in the schema.**
87
+
88
+ ### Step 2: Inventory Existing Subsection Items
89
+
90
+ Scan the existing subsection items directory:
91
+ ```
92
+ {ui_repo}/src/pages/Transaction/PrintView/templates/subsectionItems/
93
+ ```
94
+
95
+ Build a map of what already exists. Read the `index.ts` barrel file and glob for all `.ts` files. For each item, note:
96
+ - The segment/loop it handles (e.g., `BEG`, `N1_loop`, `DTM`)
97
+ - Whether it exports a block, table, or both variants
98
+ - The `parentPath` it expects
99
+
100
+ **CRITICAL: X12 is composed of shared building blocks. The same segment (N1, DTM, REF, etc.) appears across many transaction types. ALWAYS reuse existing subsection items. NEVER duplicate.**
101
+
102
+ **NAMING: Subsection items must NOT include transaction type suffixes (e.g., `CDD_loop`, not `CDD_loop_812`).** Subsection items represent reusable X12 building blocks, not transaction-specific components. Some existing items like `PO1_loop_850` have this antipattern — do not follow that convention. Name items after the segment/loop they represent (e.g., `BCD`, `SHD`, `CDD_loop`).
103
+
104
+ ### Step 3: Map Sample to Subsection Items
105
+
106
+ Walk the sample transaction's `transactionSets` structure. For each segment or loop present:
107
+
108
+ 1. **Check if a subsection item already exists** → mark as EXISTING
109
+ 2. **Check if it exists but needs additional fields** for this transaction type → mark as EXTEND
110
+ 3. **No subsection item exists** → mark as NEW
111
+
112
+ Use the schema to determine:
113
+ - Field labels (from `title`, strip the element ID prefix like "BEG03 - ")
114
+ - Field types (`ElementType`: Alphanumeric, Numeric, Date, Time, etc. — infer from `X12DataElementType` or schema `type`)
115
+ - Whether a field value comes from a qualifier code (use `QualifiedValueFieldMapItem` or `QualifiedLabelFieldMapItem`)
116
+ - Whether data is a single record (→ block) or repeating array (→ table)
117
+
118
+ ### Step 4: Propose Visual Layout
119
+
120
+ Present an ASCII/markdown layout to the user showing the proposed template structure. This is the most important step — **wait for user approval before generating any code.**
121
+
122
+ Format:
123
+
124
+ ```
125
+ # Proposed Webform: {type} - {title}
126
+
127
+ ## Section 1: {Section Title}
128
+
129
+ [{SEGMENT}] {Description} (block) ← EXISTING
130
+ • {Field Label} • {Field Label} • {Field Label}
131
+ • {Field Label} • {Field Label}
132
+
133
+ [{SEGMENT}] {Description} (table) ← NEW
134
+ | {Column 1} | {Column 2} | {Column 3} |
135
+
136
+ ## Section 2: {Section Title}
137
+
138
+ [{LOOP}] {Description} (block, 2-col) ← EXISTING
139
+ • {Field Label} • {Field Label}
140
+ ...
141
+
142
+ ## Section N: Totals
143
+
144
+ [footer] ← EXISTING (footer_CTT_loop)
145
+ Left: {fields} Right: {fields}
146
+ ```
147
+
148
+ For each subsection item, clearly indicate:
149
+ - `EXISTING` — will import and reuse as-is
150
+ - `EXTEND` — exists but needs fields added (list which fields)
151
+ - `NEW` — will create a new reusable subsection item
152
+
153
+ **Grouping guidance** (based on existing templates):
154
+ - Header/general info segments go in the first section (BEG/BAK/BIG + CUR, PER, CSH, REF, N9)
155
+ - Allowances/charges (SAC_loop) get their own section if present
156
+ - Additional info segments together (ITD, FOB, DTM, PID, TD5, etc.)
157
+ - Party identification (N1_loop) gets its own section, typically as `block--two-col`
158
+ - Line items loop gets its own section with `countPath`
159
+ - Totals/footer in the last section
160
+
161
+ ### Step 5: Iterate on Feedback
162
+
163
+ The user may request changes:
164
+ - Regroup sections
165
+ - Add/remove fields
166
+ - Change block ↔ table rendering
167
+ - Adjust column layout for tables
168
+
169
+ Iterate until the user confirms the layout.
170
+
171
+ ### Step 6: Generate Code
172
+
173
+ Once approved, generate the template and any new subsection items.
174
+
175
+ #### Template File
176
+
177
+ Create `{ui_repo}/src/pages/Transaction/PrintView/templates/{type}.template.ts`
178
+
179
+ Follow the exact patterns from existing templates. Example structure:
180
+
181
+ ```typescript
182
+ import { PrintViewTemplate } from './types';
183
+ import {
184
+ BEG,
185
+ CUR,
186
+ // ... other imports from subsectionItems
187
+ } from './subsectionItems';
188
+
189
+ export const template: PrintViewTemplate = [
190
+ {
191
+ sectionTitle: 'Section Title',
192
+ subsections: [
193
+ [BEG],
194
+ [CUR],
195
+ ],
196
+ },
197
+ // ... more sections
198
+ ];
199
+ ```
200
+
201
+ Key patterns:
202
+ - Each section is a `PrintViewPredefinedSection` with `sectionTitle` and `subsections`
203
+ - `subsections` is an array of arrays — each inner array is a group of subsection items rendered together
204
+ - Line item sections use `countPath` pointing to the loop array path (e.g., `'PO1_loop'`)
205
+ - Party sections typically use `block--two-col` layout via the subsection item's `subsectionType`
206
+
207
+ #### New Subsection Items
208
+
209
+ For each NEW item, create a file in `subsectionItems/`:
210
+ ```
211
+ {ui_repo}/src/pages/Transaction/PrintView/templates/subsectionItems/{Name}.ts
212
+ ```
213
+
214
+ Follow existing patterns exactly. Key rules:
215
+ - Import types from `'../types'`
216
+ - Use `ElementType` enum for field value types
217
+ - For blocks: set `subsectionType: 'block'` or `'block--two-col'`
218
+ - For tables: set `subsectionType: 'table'`
219
+ - For footers: set `subsectionType: 'footer'` with `leftFieldMap` and `rightFieldMap`
220
+ - `parentPath` should use the Orderful JSON path (e.g., `'beginningSegmentForPurchaseOrder'`)
221
+ - Field `valuePath` is relative to the parentPath
222
+ - For qualifier fields, use `valueQualifierPath` or `labelQualifierPath`
223
+ - Export the item as a named const
224
+
225
+ #### Extended Subsection Items
226
+
227
+ For EXTEND items, propose the specific `Edit` changes to add new fields to the existing file. Show the user what will change before applying.
228
+
229
+ #### Register in helpers.ts
230
+
231
+ Add the new template to the `loadPrintViewTemplate` switch/map in:
232
+ ```
233
+ {ui_repo}/src/pages/Transaction/PrintView/templates/helpers.ts
234
+ ```
235
+
236
+ #### Update barrel exports
237
+
238
+ Add any new subsection items to:
239
+ ```
240
+ {ui_repo}/src/pages/Transaction/PrintView/templates/subsectionItems/index.ts
241
+ ```
242
+
243
+ #### Add TransactionType enum value
244
+
245
+ If this transaction type isn't already in the `TransactionType` enum, add it to:
246
+ ```
247
+ {ui_repo}/src/pages/Transaction/PrintView/types/index.ts
248
+ ```
249
+
250
+ Follow the naming convention: `SCREAMING_SNAKE_CASE = '{type}_{SCREAMING_SNAKE_NAME}'` (e.g., `CREDIT_DEBIT_ADJUSTMENT = '812_CREDIT_DEBIT_ADJUSTMENT'`).
251
+
252
+ #### Add to ALLOWED_TRANSACTION_TYPES
253
+
254
+ If this is a new transaction type, add it to the `ALLOWED_TRANSACTION_TYPES` array in:
255
+ ```
256
+ {ui_repo}/src/pages/Transaction/CreateTransaction/index.tsx
257
+ ```
258
+
259
+ #### Wire up PrintView component
260
+
261
+ Update the print view routing in:
262
+ ```
263
+ {ui_repo}/src/shared/helpers/printViewHelpers/getPrintViewComponentAndCsv.ts
264
+ ```
265
+
266
+ If there's an existing legacy component for this transaction type, replace it with `PrintView`. If there's no existing case, add one:
267
+ ```typescript
268
+ case '{type_enum_value}':
269
+ printViewComponent = PrintView;
270
+ break;
271
+ ```
272
+
273
+ #### Add to test coverage
274
+
275
+ Add the transaction type to `testedTransactionSets` in:
276
+ ```
277
+ {ui_repo}/src/pages/Transaction/PrintView/templates/tests/templates.spec.ts
278
+ ```
279
+
280
+ This test validates that all paths in the template exist in the schema. The template **must** pass this test — if paths don't resolve, the field names in subsection items are wrong.
281
+
282
+ ### Step 7: Summary
283
+
284
+ After generating, provide a summary:
285
+ - Files created (with paths)
286
+ - Files modified (with description of changes)
287
+ - Existing subsection items reused
288
+ - Any items that were extended
289
+
290
+ ## Type Reference
291
+
292
+ Quick reference for the template type system (defined in `templates/types.ts`):
293
+
294
+ ### Subsection Item Types
295
+
296
+ | Type | Use When | Key Fields |
297
+ |------|----------|------------|
298
+ | Block (static header) | Single record, fixed title | `subsectionType`, `header`, `parentPath`, `fieldMap` |
299
+ | Block (dynamic header) | Single record, title from data | `subsectionType`, `headerQualifierPath`, `parentPath`, `fieldMap` |
300
+ | Table (static header) | Repeating records, fixed title | `subsectionType`, `header`, `parentPath`, `columns` |
301
+ | Table (dynamic header) | Repeating records, title from data | `subsectionType`, `headerQualifierPath`, `parentPath`, `columns` |
302
+ | Footer | Totals/summary | `subsectionType: 'footer'`, `parentPath`, `leftFieldMap`, `rightFieldMap` |
303
+ | Hierarchy | HL loops (856 only) | `subsectionType: 'hierarchy'`, complex nesting |
304
+
305
+ ### FieldMapItem Types
306
+
307
+ | Type | Use When | Key Fields |
308
+ |------|----------|------------|
309
+ | DirectFieldMapItem | Simple label + value | `label`, `valuePath`, `valueType` |
310
+ | QualifiedLabelFieldMapItem | Label comes from a code qualifier | `labelQualifierPath`, `valuePath`, `valueType` |
311
+ | QualifiedValueFieldMapItem | The qualifier code IS the display value | `label`, `valueQualifierPath` |
312
+
313
+ ### Table Column Types
314
+
315
+ | Type | Use When | Key Fields |
316
+ |------|----------|------------|
317
+ | TableStaticValueColumn | Simple column | `header`, `valuePath`, `valueType` |
318
+ | TableQualifiedValueColumn | Column value from qualifier | `header`, `valueQualifierPath` |
319
+ | TableFieldMapColumn | Multiple fields in one column | `header`, `fieldMap` |
320
+ | NestedTableColumn | Blocks nested inside column | `header`, `subsections` |
321
+
322
+ ### ValueFormat
323
+
324
+ Use `valueFormat` on DirectFieldMapItem when needed:
325
+ - `'currency'` — e.g., $123.45
326
+ - `'signedCurrency'` — with +/- sign
327
+ - `'percent'` — e.g., 15%
328
+ - `'decimalPercent'` — e.g., 0.15
329
+
330
+ ### ElementType
331
+
332
+ Common values: `Alphanumeric`, `Numeric`, `Date`, `Time`, `ID`
333
+
334
+ ## Existing Subsection Items
335
+
336
+ **Do NOT hardcode this list — always scan the subsectionItems directory at runtime.** This list is for reference only and may be outdated.
337
+
338
+ ### High Reuse (4+ templates)
339
+ N1_loop, DTM (table + block), REF (table + block), SAC_loop, SAC_loop_block, FOB (table + block), TD5 (table + block), PID (table + block)
340
+
341
+ ### Medium Reuse (2-3 templates)
342
+ CUR, PER, CSH, ITD, MEA (table + block), TD1, NTE, MAN, LIN, AMT, CAD, PKG, QTY, TXI
343
+
344
+ ### Transaction-Specific Headers
345
+ BEG (850), BAK (855), BIG (810), BIA (846), BSN (856), G82 (875/880), BCD (812)
346
+
347
+ ### Line Item Loops
348
+ PO1_loop_850, PO1_loop_855, IT1_loop_810, CDD_loop, G68_loop, G83_loop, HL_loop + HL_loop_items
349
+
350
+ ### Footers
351
+ footer_CTT, footer_CTT_loop, footer_TDS_CTT, footer_G76, footer_G84
352
+
353
+ ### Other
354
+ ISS_loop, LIN_loop, R4, V1, SDQ, SCH, LDT, PRF, PO4, SN1, IT3, TD3, TD4_block, MTX, YNQ, N9 (block + loop + table), SHD, G01-G86 (grocery segments), constants
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orderful/droid",
3
- "version": "0.52.1",
3
+ "version": "0.54.0",
4
4
  "description": "AI workflow toolkit for sharing skills, commands, and agents across the team",
5
5
  "type": "module",
6
6
  "bin": {
package/src/bin/droid.ts CHANGED
@@ -21,6 +21,7 @@ import { integrationsSetupSlackCommand, integrationsStatusCommand, slackPostComm
21
21
  import { getVersion } from '../lib/version';
22
22
  import { loadConfig, saveConfig, configExists } from '../lib/config';
23
23
  import { syncNewPlatforms } from '../lib/platforms';
24
+ import { copySkillsCommand } from '../commands/copy-skills';
24
25
 
25
26
  const version = getVersion();
26
27
 
@@ -120,6 +121,14 @@ program
120
121
  .allowUnknownOption()
121
122
  .action(execCommand);
122
123
 
124
+ program
125
+ .command('copy-skills')
126
+ .description('Copy bundled skills to a destination directory')
127
+ .requiredOption('-d, --dest <dir>', 'Destination directory')
128
+ .option('-m, --manifest <file>', 'JSON file with array of skill names')
129
+ .option('-n, --names <names...>', 'Skill names to copy')
130
+ .action(copySkillsCommand);
131
+
123
132
  // Integrations command with subcommands
124
133
  const integrations = program
125
134
  .command('integrations')
@@ -0,0 +1,66 @@
1
+ import chalk from 'chalk';
2
+ import { readFileSync } from 'fs';
3
+ import { copySkillsToDir } from '../lib/skills';
4
+
5
+ interface CopySkillsOptions {
6
+ dest: string;
7
+ manifest?: string;
8
+ names?: string[];
9
+ }
10
+
11
+ /**
12
+ * Copy bundled skills to a destination directory.
13
+ *
14
+ * Usage:
15
+ * droid copy-skills -d dist/assets/skills -m droid-skills.json
16
+ * droid copy-skills -d dist/assets/skills -n propose-plan edi-schema
17
+ */
18
+ export function copySkillsCommand(options: CopySkillsOptions): void {
19
+ const { dest, manifest, names } = options;
20
+
21
+ // Resolve skill names from manifest file or --names flag
22
+ let skillNames: string[];
23
+
24
+ if (manifest) {
25
+ try {
26
+ const content = readFileSync(manifest, 'utf-8');
27
+ skillNames = JSON.parse(content);
28
+ if (!Array.isArray(skillNames)) {
29
+ console.error(
30
+ chalk.red('Manifest must be a JSON array of skill names'),
31
+ );
32
+ process.exit(1);
33
+ }
34
+ } catch (err) {
35
+ console.error(chalk.red(`Failed to read manifest: ${err}`));
36
+ process.exit(1);
37
+ }
38
+ } else if (names?.length) {
39
+ skillNames = names;
40
+ } else {
41
+ console.error(
42
+ chalk.red('Provide either --manifest <file> or --names <names...>'),
43
+ );
44
+ process.exit(1);
45
+ }
46
+
47
+ if (skillNames.length === 0) {
48
+ console.log(chalk.yellow('No skills to copy.'));
49
+ return;
50
+ }
51
+
52
+ const results = copySkillsToDir(skillNames, dest);
53
+
54
+ for (const result of results) {
55
+ if (result.success) {
56
+ console.log(chalk.green(` ✓ ${result.name} → ${result.dest}`));
57
+ } else {
58
+ console.error(chalk.red(` ✗ ${result.name}: ${result.error}`));
59
+ }
60
+ }
61
+
62
+ const failed = results.filter((r) => !r.success);
63
+ if (failed.length > 0) {
64
+ process.exit(1);
65
+ }
66
+ }
package/src/index.ts CHANGED
@@ -2,4 +2,6 @@
2
2
  export * from './lib/types';
3
3
  export * from './lib/config';
4
4
  export * from './lib/skills';
5
+ export * from './lib/tools';
6
+ export * from './lib/pack';
5
7
  export * from './lib/version';
package/src/lib/skills.ts CHANGED
@@ -213,6 +213,69 @@ export function findSkillPath(
213
213
  return null;
214
214
  }
215
215
 
216
+ /**
217
+ * Resolve the absolute path to a bundled skill directory by name.
218
+ * Returns null if the skill is not found.
219
+ *
220
+ * This is the stable public API for consumers who need to locate skill
221
+ * assets (e.g., for copying into a build output). It encapsulates droid's
222
+ * internal `tools/{tool}/skills/{name}/` directory structure.
223
+ */
224
+ export function getSkillPath(skillName: string): string | null {
225
+ const result = findSkillPath(skillName);
226
+ return result?.skillDir ?? null;
227
+ }
228
+
229
+ /**
230
+ * Copy bundled skills to a destination directory.
231
+ * Each skill is copied as `{dest}/{skillName}/` with SKILL.md, references/, etc.
232
+ *
233
+ * @param skillNames - Array of skill names to copy
234
+ * @param dest - Target directory (created if it doesn't exist)
235
+ * @returns Results per skill: success/failure with paths
236
+ */
237
+ export function copySkillsToDir(
238
+ skillNames: string[],
239
+ dest: string,
240
+ ): Array<{ name: string; success: boolean; dest?: string; error?: string }> {
241
+ if (!existsSync(dest)) {
242
+ mkdirSync(dest, { recursive: true });
243
+ }
244
+
245
+ const results: Array<{
246
+ name: string;
247
+ success: boolean;
248
+ dest?: string;
249
+ error?: string;
250
+ }> = [];
251
+
252
+ for (const name of skillNames) {
253
+ const skillDir = getSkillPath(name);
254
+ if (!skillDir) {
255
+ results.push({
256
+ name,
257
+ success: false,
258
+ error: `Skill '${name}' not found in bundled tools`,
259
+ });
260
+ continue;
261
+ }
262
+
263
+ const targetDir = join(dest, name);
264
+ try {
265
+ copyDirectoryRecursive(skillDir, targetDir, () => true);
266
+ results.push({ name, success: true, dest: targetDir });
267
+ } catch (err) {
268
+ results.push({
269
+ name,
270
+ success: false,
271
+ error: `Failed to copy: ${err}`,
272
+ });
273
+ }
274
+ }
275
+
276
+ return results;
277
+ }
278
+
216
279
  /**
217
280
  * Get all bundled skills from all tools
218
281
  */