@unified-product-graph/cli 0.6.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/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +247 -0
- package/dist/cli.cjs +141010 -0
- package/package.json +65 -0
- package/skills/README.md +10 -0
- package/skills/upg/SKILL.md +245 -0
- package/skills/upg-analytics/SKILL.md +135 -0
- package/skills/upg-capture/SKILL.md +274 -0
- package/skills/upg-connect/SKILL.md +167 -0
- package/skills/upg-context/SKILL.md +506 -0
- package/skills/upg-context-intelligence/SKILL.md +227 -0
- package/skills/upg-design-system/SKILL.md +265 -0
- package/skills/upg-diff/SKILL.md +150 -0
- package/skills/upg-discover/SKILL.md +290 -0
- package/skills/upg-explore/SKILL-DETAIL.md +481 -0
- package/skills/upg-explore/SKILL.md +297 -0
- package/skills/upg-export/SKILL.md +385 -0
- package/skills/upg-feedback/SKILL.md +141 -0
- package/skills/upg-gaps/SKILL.md +376 -0
- package/skills/upg-hypothesis/SKILL.md +190 -0
- package/skills/upg-impact/SKILL.md +229 -0
- package/skills/upg-import/SKILL.md +189 -0
- package/skills/upg-init/SKILL.md +410 -0
- package/skills/upg-inspect/SKILL.md +167 -0
- package/skills/upg-journey/SKILL.md +207 -0
- package/skills/upg-launch/SKILL-DETAIL.md +392 -0
- package/skills/upg-launch/SKILL.md +141 -0
- package/skills/upg-migrate/SKILL.md +146 -0
- package/skills/upg-okr/SKILL-DETAIL.md +351 -0
- package/skills/upg-okr/SKILL.md +88 -0
- package/skills/upg-persona/SKILL.md +230 -0
- package/skills/upg-prioritise/SKILL.md +195 -0
- package/skills/upg-pull/SKILL-DETAIL.md +398 -0
- package/skills/upg-pull/SKILL.md +57 -0
- package/skills/upg-push/SKILL-DETAIL.md +385 -0
- package/skills/upg-push/SKILL.md +113 -0
- package/skills/upg-reflect/SKILL.md +201 -0
- package/skills/upg-research/SKILL.md +336 -0
- package/skills/upg-rollback/SKILL.md +163 -0
- package/skills/upg-run/SKILL.md +126 -0
- package/skills/upg-schema-changelog/SKILL.md +231 -0
- package/skills/upg-schema-consolidate/SKILL.md +243 -0
- package/skills/upg-schema-edges/SKILL.md +287 -0
- package/skills/upg-schema-evolve/SKILL.md +313 -0
- package/skills/upg-schema-health/SKILL.md +279 -0
- package/skills/upg-schema-update/SKILL.md +206 -0
- package/skills/upg-snapshot/SKILL.md +108 -0
- package/skills/upg-status/SKILL.md +340 -0
- package/skills/upg-strategy/SKILL.md +334 -0
- package/skills/upg-template/SKILL.md +145 -0
- package/skills/upg-trace/SKILL.md +197 -0
- package/skills/upg-tree/SKILL.md +233 -0
- package/skills/upg-verify/SKILL.md +223 -0
- package/skills/upg-workspace/SKILL.md +103 -0
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upg-push-detail
|
|
3
|
+
description: "Detailed push flow, sync file format, ID mapping, edge cases"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# /upg-push — Push Flow Detail
|
|
7
|
+
|
|
8
|
+
## First-Time Push (Step 4)
|
|
9
|
+
|
|
10
|
+
This runs when there is no `.upg-sync` file — the user has never pushed this graph before.
|
|
11
|
+
|
|
12
|
+
### 4a: Match or Create Cloud Product
|
|
13
|
+
|
|
14
|
+
Check if a matching product already exists in the cloud:
|
|
15
|
+
- Search by title match from the product list returned in Step 2
|
|
16
|
+
- If found, confirm: "Found '<title>' in your cloud graph. Push local graph to this product?"
|
|
17
|
+
- If not found, ask: "This product doesn't exist in the cloud yet. Create '<title>' in The Product Creator?"
|
|
18
|
+
|
|
19
|
+
**Create:**
|
|
20
|
+
```
|
|
21
|
+
mcp__upg-cloud__create_product({
|
|
22
|
+
title: "<product title>",
|
|
23
|
+
description: "<product description>"
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Record the `product_id` returned by the cloud.
|
|
28
|
+
|
|
29
|
+
### 4b: Push All Entities
|
|
30
|
+
|
|
31
|
+
For each local node, create in the cloud using batch operations:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
mcp__upg-cloud__batch_create_nodes({
|
|
35
|
+
product_id: "<cloud_product_id>",
|
|
36
|
+
nodes: [
|
|
37
|
+
{
|
|
38
|
+
type: "<type>",
|
|
39
|
+
title: "<title>",
|
|
40
|
+
description: "<description>",
|
|
41
|
+
data: { ...properties },
|
|
42
|
+
parent_ref: "<parent reference for auto-edge>"
|
|
43
|
+
},
|
|
44
|
+
// ... up to 50 per batch
|
|
45
|
+
]
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Important:** Batch create supports `parent_ref` for intra-batch chaining — use this to maintain the hierarchy.
|
|
50
|
+
|
|
51
|
+
After each batch call, collect the returned cloud IDs and build the **node ID map**: `{ "n_local1": "cloud-uuid-1", "n_local2": "cloud-uuid-2", ... }`.
|
|
52
|
+
|
|
53
|
+
### 4c: Push All Edges
|
|
54
|
+
|
|
55
|
+
For edges that weren't auto-created via parent_ref:
|
|
56
|
+
```
|
|
57
|
+
mcp__upg-cloud__create_edge({
|
|
58
|
+
product_id: "<cloud_product_id>",
|
|
59
|
+
source_id: "<cloud_source_id>", // looked up via node_id_map
|
|
60
|
+
target_id: "<cloud_target_id>" // looked up via node_id_map
|
|
61
|
+
})
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Collect cloud edge IDs into the **edge ID map**: `{ "e_local1": "cloud-edge-uuid-1", ... }`.
|
|
65
|
+
|
|
66
|
+
### 4d: Create the .upg-sync File
|
|
67
|
+
|
|
68
|
+
Compute the hash of the current `.upg` file:
|
|
69
|
+
```bash
|
|
70
|
+
shasum -a 256 product.upg | awk '{print $1}'
|
|
71
|
+
```
|
|
72
|
+
(Use the actual `.upg` filename — it may not be `product.upg`.)
|
|
73
|
+
|
|
74
|
+
Write the `.upg-sync` file using Bash:
|
|
75
|
+
```bash
|
|
76
|
+
cat > .upg-sync << 'SYNC_EOF'
|
|
77
|
+
{
|
|
78
|
+
"cloud_endpoint": "https://cloud.unifiedproductgraph.org",
|
|
79
|
+
"product_id": "<cloud_product_id>",
|
|
80
|
+
"last_synced_at": "<current ISO 8601 timestamp>",
|
|
81
|
+
"node_id_map": {
|
|
82
|
+
"n_local1": "cloud-uuid-1",
|
|
83
|
+
"n_local2": "cloud-uuid-2"
|
|
84
|
+
},
|
|
85
|
+
"edge_id_map": {
|
|
86
|
+
"e_local1": "cloud-edge-uuid-1"
|
|
87
|
+
},
|
|
88
|
+
"last_snapshot_hash": "<sha256 hash from above>"
|
|
89
|
+
}
|
|
90
|
+
SYNC_EOF
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
This file enables all future incremental pushes. Go to Step 6.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Incremental Push (Step 5)
|
|
98
|
+
|
|
99
|
+
This runs when `.upg-sync` exists — the user has pushed before and we can sync only changes.
|
|
100
|
+
|
|
101
|
+
### 5a: Quick Hash Check
|
|
102
|
+
|
|
103
|
+
Compute the current `.upg` file hash and compare to `last_snapshot_hash` from `.upg-sync`:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
shasum -a 256 product.upg | awk '{print $1}'
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
If the hash **matches** `last_snapshot_hash`:
|
|
110
|
+
```
|
|
111
|
+
Nothing to push — your graph hasn't changed since last sync.
|
|
112
|
+
|
|
113
|
+
Last synced: <last_synced_at from .upg-sync>
|
|
114
|
+
|
|
115
|
+
Make changes locally, then run /upg-push again.
|
|
116
|
+
```
|
|
117
|
+
Stop here.
|
|
118
|
+
|
|
119
|
+
If the hash is **different**, continue to compute the changeset.
|
|
120
|
+
|
|
121
|
+
### 5b: Verify Cloud Product Still Exists
|
|
122
|
+
|
|
123
|
+
Use the `product_id` from `.upg-sync` to check the cloud:
|
|
124
|
+
```
|
|
125
|
+
mcp__upg-cloud__list_products()
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Look for the product matching the stored `product_id`.
|
|
129
|
+
|
|
130
|
+
If the cloud product is **gone** (deleted, reset, or not found):
|
|
131
|
+
```
|
|
132
|
+
⚠️ Your cloud product (<product_id>) no longer exists.
|
|
133
|
+
|
|
134
|
+
It may have been deleted or reset on the cloud side.
|
|
135
|
+
|
|
136
|
+
1. 🔄 Full re-push — create a new cloud product and push everything
|
|
137
|
+
2. ⏭️ Cancel — keep working locally for now
|
|
138
|
+
|
|
139
|
+
Which would you like?
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
If the user chooses full re-push: delete the `.upg-sync` file and restart from Step 4 (first-time push flow).
|
|
143
|
+
|
|
144
|
+
### 5c: Compute Changeset
|
|
145
|
+
|
|
146
|
+
Read the current graph state via MCP tools:
|
|
147
|
+
```
|
|
148
|
+
list_nodes({ limit: 200 })
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Compare against the ID mappings in `.upg-sync`:
|
|
152
|
+
|
|
153
|
+
**New nodes:** Nodes in the current graph whose IDs are NOT in `node_id_map` keys. These need to be created on the cloud.
|
|
154
|
+
|
|
155
|
+
**Deleted nodes:** IDs that ARE in `node_id_map` but no longer exist in the current graph. These should be deleted from the cloud.
|
|
156
|
+
|
|
157
|
+
**Updated nodes:** Nodes whose IDs are in `node_id_map` AND still exist locally. For each, compare the current node data (title, description, properties, status, tags) against what's in the cloud. If anything changed, it's an update. To detect changes, fetch the cloud state:
|
|
158
|
+
```
|
|
159
|
+
mcp__upg-cloud__get_product_graph({ product_id: "<cloud_product_id>" })
|
|
160
|
+
```
|
|
161
|
+
Then compare each mapped node's current local state against its cloud state.
|
|
162
|
+
|
|
163
|
+
**New edges:** Edges in the current graph whose IDs are NOT in `edge_id_map` keys. Create on cloud.
|
|
164
|
+
|
|
165
|
+
**Deleted edges:** Edge IDs in `edge_id_map` that no longer exist locally. Delete from cloud.
|
|
166
|
+
|
|
167
|
+
### 5d: Show Changeset Summary
|
|
168
|
+
|
|
169
|
+
Present the changeset for confirmation:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
|
|
173
|
+
☁️ INCREMENTAL PUSH
|
|
174
|
+
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
|
|
175
|
+
|
|
176
|
+
Pushing changes to "<Product Name>"
|
|
177
|
+
|
|
178
|
+
➕ <N> new entities
|
|
179
|
+
✏️ <N> updated entities
|
|
180
|
+
🗑️ <N> deleted entities
|
|
181
|
+
🔗 <N> new connections
|
|
182
|
+
✂️ <N> removed connections
|
|
183
|
+
|
|
184
|
+
New:
|
|
185
|
+
📦 <Feature title>
|
|
186
|
+
👤 <Persona title>
|
|
187
|
+
|
|
188
|
+
Updated:
|
|
189
|
+
🎯 <Outcome title> — description changed
|
|
190
|
+
⚗️ <Hypothesis title> — status changed to validated
|
|
191
|
+
|
|
192
|
+
Deleted:
|
|
193
|
+
📝 <Learning title>
|
|
194
|
+
|
|
195
|
+
Push these changes? (yes / no / review details)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Wait for confirmation before proceeding.
|
|
199
|
+
|
|
200
|
+
If the user says "review details", show the full diff for each changed entity (old value → new value).
|
|
201
|
+
|
|
202
|
+
### 5e: Execute Changes
|
|
203
|
+
|
|
204
|
+
Process in this order: creates first, then updates, then deletes.
|
|
205
|
+
|
|
206
|
+
**Create new nodes** (batch):
|
|
207
|
+
```
|
|
208
|
+
mcp__upg-cloud__batch_create_nodes({
|
|
209
|
+
product_id: "<cloud_product_id>",
|
|
210
|
+
nodes: [ ...new nodes... ]
|
|
211
|
+
})
|
|
212
|
+
```
|
|
213
|
+
Add returned cloud IDs to the node ID map.
|
|
214
|
+
|
|
215
|
+
**Update existing nodes:**
|
|
216
|
+
```
|
|
217
|
+
mcp__upg-cloud__update_node({
|
|
218
|
+
product_id: "<cloud_product_id>",
|
|
219
|
+
node_id: "<cloud_id from node_id_map>",
|
|
220
|
+
title: "<updated title>",
|
|
221
|
+
description: "<updated description>",
|
|
222
|
+
data: { ...updated properties }
|
|
223
|
+
})
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Delete removed nodes:**
|
|
227
|
+
```
|
|
228
|
+
mcp__upg-cloud__delete_node({
|
|
229
|
+
product_id: "<cloud_product_id>",
|
|
230
|
+
node_id: "<cloud_id from node_id_map>"
|
|
231
|
+
})
|
|
232
|
+
```
|
|
233
|
+
Remove from the node ID map.
|
|
234
|
+
|
|
235
|
+
**Create new edges:**
|
|
236
|
+
```
|
|
237
|
+
mcp__upg-cloud__create_edge({
|
|
238
|
+
product_id: "<cloud_product_id>",
|
|
239
|
+
source_id: "<cloud_source_id>",
|
|
240
|
+
target_id: "<cloud_target_id>"
|
|
241
|
+
})
|
|
242
|
+
```
|
|
243
|
+
Add to edge ID map.
|
|
244
|
+
|
|
245
|
+
**Delete removed edges:**
|
|
246
|
+
```
|
|
247
|
+
mcp__upg-cloud__delete_edge({
|
|
248
|
+
product_id: "<cloud_product_id>",
|
|
249
|
+
edge_id: "<cloud_id from edge_id_map>"
|
|
250
|
+
})
|
|
251
|
+
```
|
|
252
|
+
Remove from edge ID map.
|
|
253
|
+
|
|
254
|
+
### 5f: Update .upg-sync
|
|
255
|
+
|
|
256
|
+
Recompute the `.upg` file hash and update the sync file:
|
|
257
|
+
```bash
|
|
258
|
+
NEW_HASH=$(shasum -a 256 product.upg | awk '{print $1}')
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Write the updated `.upg-sync` file with:
|
|
262
|
+
- Updated `node_id_map` (new entries added, deleted entries removed)
|
|
263
|
+
- Updated `edge_id_map` (new entries added, deleted entries removed)
|
|
264
|
+
- Updated `last_synced_at` to current timestamp
|
|
265
|
+
- Updated `last_snapshot_hash` to the new hash
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
### Step 6: Report Results
|
|
270
|
+
|
|
271
|
+
**After first-time push:**
|
|
272
|
+
```
|
|
273
|
+
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
|
|
274
|
+
☁️ PUSH COMPLETE
|
|
275
|
+
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
|
|
276
|
+
|
|
277
|
+
Pushed "<Product Name>" to The Product Creator cloud.
|
|
278
|
+
|
|
279
|
+
Entities synced: <N> (<breakdown by type>)
|
|
280
|
+
Connections synced: <N>
|
|
281
|
+
Sync file created: .upg-sync
|
|
282
|
+
|
|
283
|
+
Future pushes will be incremental — only changes get sent.
|
|
284
|
+
|
|
285
|
+
### What You Get in the Cloud
|
|
286
|
+
|
|
287
|
+
- Visual canvas — drag, zoom, explore your graph spatially
|
|
288
|
+
- 47 framework trees — OST, OKR, Strategy Cascade, BMC, and more
|
|
289
|
+
- 43 analytical lenses — filter and slice your graph by any dimension
|
|
290
|
+
- Real-time collaboration — invite your team to build together
|
|
291
|
+
- AI copilot — conversational graph building with full context
|
|
292
|
+
|
|
293
|
+
View your graph: cloud.unifiedproductgraph.org/p/<product_id>
|
|
294
|
+
|
|
295
|
+
### Keep Building Locally
|
|
296
|
+
|
|
297
|
+
Your .upg file is still the source of truth for local work.
|
|
298
|
+
Run /upg-push again anytime — only your changes will be synced.
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
**After incremental push:**
|
|
302
|
+
```
|
|
303
|
+
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
|
|
304
|
+
☁️ PUSH COMPLETE
|
|
305
|
+
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
|
|
306
|
+
|
|
307
|
+
Synced changes to "<Product Name>"
|
|
308
|
+
|
|
309
|
+
➕ Created: <N> entities
|
|
310
|
+
✏️ Updated: <N> entities
|
|
311
|
+
🗑️ Deleted: <N> entities
|
|
312
|
+
🔗 Connections: +<N> / -<N>
|
|
313
|
+
|
|
314
|
+
Cloud graph: <total entities> entities · <total edges> edges
|
|
315
|
+
|
|
316
|
+
View your graph: cloud.unifiedproductgraph.org/p/<product_id>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
**Shared footer (always append):**
|
|
320
|
+
```
|
|
321
|
+
┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
|
|
322
|
+
Your .upg file is yours — open standard, portable, git-friendly.
|
|
323
|
+
unifiedproductgraph.org
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## The .upg-sync File
|
|
327
|
+
|
|
328
|
+
This file is the bridge between local and cloud. It tracks which local entities map to which cloud entities, and what the graph looked like at last sync.
|
|
329
|
+
|
|
330
|
+
**Format:**
|
|
331
|
+
```json
|
|
332
|
+
{
|
|
333
|
+
"cloud_endpoint": "https://cloud.unifiedproductgraph.org",
|
|
334
|
+
"product_id": "<cloud-uuid>",
|
|
335
|
+
"last_synced_at": "2026-03-24T15:00:00Z",
|
|
336
|
+
"node_id_map": {
|
|
337
|
+
"n_local1": "cloud-uuid-1",
|
|
338
|
+
"n_local2": "cloud-uuid-2"
|
|
339
|
+
},
|
|
340
|
+
"edge_id_map": {
|
|
341
|
+
"e_local1": "cloud-edge-uuid-1"
|
|
342
|
+
},
|
|
343
|
+
"last_snapshot_hash": "sha256-of-upg-file-at-last-sync"
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Rules:**
|
|
348
|
+
- The `.upg-sync` file lives in the same directory as the `.upg` file.
|
|
349
|
+
- Read and write it using Bash (`cat`, heredoc write). It's plain JSON.
|
|
350
|
+
- Never expose the sync file contents to the user unless they ask.
|
|
351
|
+
- The `last_snapshot_hash` is a SHA-256 of the `.upg` file at the time of last successful sync.
|
|
352
|
+
- Add `.upg-sync` to `.gitignore` if the user version-controls their `.upg` file — the sync state is machine-local, not portable.
|
|
353
|
+
|
|
354
|
+
## ID Mapping Logic
|
|
355
|
+
|
|
356
|
+
The `node_id_map` and `edge_id_map` are the core of incremental sync:
|
|
357
|
+
|
|
358
|
+
- **Keys** are local IDs (e.g. `n_abc123`, `e_def456`) — these come from the `.upg` file.
|
|
359
|
+
- **Values** are cloud UUIDs — these come from The Product Creator API responses.
|
|
360
|
+
- After creating nodes on cloud, **always** record the mapping.
|
|
361
|
+
- On subsequent pushes, use the mapping to `update` existing cloud nodes instead of creating duplicates.
|
|
362
|
+
- When a local node is deleted, use the mapping to find and delete the cloud node, then remove the entry from the map.
|
|
363
|
+
- When new local nodes appear (not in the map), create them on cloud and add the mapping.
|
|
364
|
+
|
|
365
|
+
## Edge Cases
|
|
366
|
+
|
|
367
|
+
**User renamed the .upg file:**
|
|
368
|
+
The hash won't match any file. Look for `.upg` files in the current directory using Bash:
|
|
369
|
+
```bash
|
|
370
|
+
ls *.upg 2>/dev/null
|
|
371
|
+
```
|
|
372
|
+
If found, use that file. If multiple `.upg` files exist, ask which one.
|
|
373
|
+
|
|
374
|
+
**Very large graph (200+ entities):**
|
|
375
|
+
```
|
|
376
|
+
This is a large graph (<N> entities). Syncing in batches...
|
|
377
|
+
```
|
|
378
|
+
Use pagination on `list_nodes` and batch creates (50 at a time).
|
|
379
|
+
|
|
380
|
+
**Partial push failure:**
|
|
381
|
+
If some batch creates succeed but others fail, report what succeeded and what failed. The `.upg-sync` file should still be updated with the mappings for entities that DID sync — don't throw away progress. Suggest retrying with `/upg-push` for the remaining entities.
|
|
382
|
+
|
|
383
|
+
**Node type mapping:**
|
|
384
|
+
Cloud uses the same entity types as local (`@unified-product-graph/core` shared ontology), so types map directly. Cloud stores type-specific data in a `data` JSONB column — map this from `properties` in the `.upg` format.
|
|
385
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upg-push
|
|
3
|
+
description: "Push your local product graph to The Product Creator cloud"
|
|
4
|
+
user-invocable: true
|
|
5
|
+
argument-hint: "[product-name]"
|
|
6
|
+
category: tooling
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# /upg-push — Push Local Graph to Cloud
|
|
10
|
+
|
|
11
|
+
You are a Unified Product Graph sync engine. Your job is to push the user's local `.upg` graph to The Product Creator cloud, enabling visual canvas, framework trees, team collaboration, and all the features that go beyond what the CLI can offer.
|
|
12
|
+
|
|
13
|
+
This skill supports **incremental sync** — after the first push, only changes are sent. A `.upg-sync` file tracks the mapping between local and cloud IDs, so nothing gets duplicated.
|
|
14
|
+
|
|
15
|
+
**Before producing any output, read the design system:** /upg-context for emoji mappings, score dots, bar styles, and formatting rules.
|
|
16
|
+
|
|
17
|
+
## Tools
|
|
18
|
+
|
|
19
|
+
Use `mcp__unified-product-graph__*` tools to read the local graph state.
|
|
20
|
+
Use `mcp__upg-cloud__*` tools to write to the cloud graph.
|
|
21
|
+
Use Bash to read/write the `.upg-sync` file and compute file hashes.
|
|
22
|
+
|
|
23
|
+
## Push Flow — Decision Tree
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Start
|
|
27
|
+
│
|
|
28
|
+
├─ Read local graph (Step 1)
|
|
29
|
+
│ └─ Empty? → "Nothing to push" → exit
|
|
30
|
+
│
|
|
31
|
+
├─ Check cloud connection (Step 2)
|
|
32
|
+
│ └─ Auth fail? → guide API key setup → exit
|
|
33
|
+
│
|
|
34
|
+
├─ Check for .upg-sync file (Step 3)
|
|
35
|
+
│ │
|
|
36
|
+
│ ├─ NO .upg-sync → First-time push (Step 4)
|
|
37
|
+
│ │ ├─ Match or create cloud product
|
|
38
|
+
│ │ ├─ Full push of all entities + edges
|
|
39
|
+
│ │ └─ CREATE .upg-sync file with ID mappings
|
|
40
|
+
│ │
|
|
41
|
+
│ └─ YES .upg-sync → Incremental push (Step 5)
|
|
42
|
+
│ ├─ Compare hash → unchanged? → "Nothing to push" → exit
|
|
43
|
+
│ ├─ Verify cloud product still exists
|
|
44
|
+
│ │ └─ Gone? → warn, offer full re-push
|
|
45
|
+
│ ├─ Compute changeset (new, updated, deleted)
|
|
46
|
+
│ ├─ Show summary, ask for confirmation
|
|
47
|
+
│ ├─ Execute changes
|
|
48
|
+
│ └─ UPDATE .upg-sync with new mappings + hash
|
|
49
|
+
│
|
|
50
|
+
└─ Report results (Step 6)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
### Step 1: Read Local State
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
get_product_context()
|
|
59
|
+
get_graph_digest()
|
|
60
|
+
list_nodes({ limit: 200 })
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
If the local graph is empty or has no product:
|
|
64
|
+
```
|
|
65
|
+
Your local graph is empty — nothing to push yet.
|
|
66
|
+
Run /upg-init to bootstrap your first product graph.
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Step 2: Check Cloud Connection
|
|
70
|
+
|
|
71
|
+
Try to list existing cloud products:
|
|
72
|
+
```
|
|
73
|
+
mcp__upg-cloud__list_products()
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
If this fails (auth error, no API key):
|
|
77
|
+
```
|
|
78
|
+
To push your graph to The Product Creator, you need an API key.
|
|
79
|
+
|
|
80
|
+
1. Sign up or log in at cloud.unifiedproductgraph.org
|
|
81
|
+
2. Go to Settings → API Keys → Create New Key
|
|
82
|
+
3. Add to your Claude Code MCP config:
|
|
83
|
+
|
|
84
|
+
In .mcp.json, update the upg-cloud server with your API key.
|
|
85
|
+
|
|
86
|
+
Once configured, run /upg-push again.
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Step 3: Check for .upg-sync File
|
|
90
|
+
|
|
91
|
+
Use Bash to check for the sync file:
|
|
92
|
+
```bash
|
|
93
|
+
cat .upg-sync 2>/dev/null
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- **If `.upg-sync` does NOT exist** → this is a first-time push. Go to Step 4.
|
|
97
|
+
- **If `.upg-sync` exists** → this is an incremental push. Go to Step 5.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
**Detailed push flow, sync file handling, ID mapping, and edge cases are in `SKILL-DETAIL.md`.** Read it when executing the push.
|
|
103
|
+
|
|
104
|
+
## Key Principles
|
|
105
|
+
|
|
106
|
+
- **Local is source of truth.** The `.upg` file is the canonical version. Cloud is a sync target.
|
|
107
|
+
- **Incremental by default.** After the first push, only changes travel over the wire. No duplicates.
|
|
108
|
+
- **Don't oversell.** List what the cloud adds factually. The value should be obvious.
|
|
109
|
+
- **Handle auth gracefully.** If no API key, guide them through setup — don't just error.
|
|
110
|
+
- **Follow the design system.** Entity emojis, score dots, filled bars, dashed dividers as defined in /upg-context.
|
|
111
|
+
- **Batch for efficiency.** Use batch_create_nodes (50 at a time) instead of individual creates.
|
|
112
|
+
- **Never lose progress.** On partial failure, save what worked. The user can retry the rest.
|
|
113
|
+
- **Unified Product Graph is the standard.** Reinforce that this is an open format — pushing to The Product Creator is a choice, not a requirement.
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upg-reflect
|
|
3
|
+
description: "Question what you're assuming — guided reflection using Five Whys, Pre-mortem, Red Team, Devil's Advocate, or Second-order Thinking"
|
|
4
|
+
user-invocable: true
|
|
5
|
+
argument-hint: "[entity name / region / scope]"
|
|
6
|
+
category: cognitive
|
|
7
|
+
approaches: [reflect]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# /upg-reflect — Question What You're Assuming
|
|
11
|
+
|
|
12
|
+
You are a Unified Product Graph reflection facilitator. Your job is to help the user **stop building and start questioning**. Pick a target — an entity, a region, or the whole graph — and walk them through one of five canonical reflection frameworks.
|
|
13
|
+
|
|
14
|
+
This is the home of the **Reflect** approach. Where Plan asks "what should I build next?", Reflect asks "what should I be questioning?". Cartographic sense: before approaching the coastline, you check which features of your chart are conjecture rather than verified terrain.
|
|
15
|
+
|
|
16
|
+
**Before producing any output, load the design system:** `/upg-context` (interaction principles, design system, lens rules) and `/upg-context-intelligence` (benchmarks, user personas, product philosophy).
|
|
17
|
+
|
|
18
|
+
## Tools
|
|
19
|
+
|
|
20
|
+
Use the `mcp__unified-product-graph__*` MCP tools:
|
|
21
|
+
- **Read scope:** `search_nodes`, `get_node`, `list_nodes`, `query`, `get_product_context`
|
|
22
|
+
- **Approach + frameworks:** `get_approach({ approach_id: "reflect" })`, `get_framework({ framework_id: "..." })`
|
|
23
|
+
- **Capture findings (optional):** `create_node`, `update_node`, `create_edge`
|
|
24
|
+
|
|
25
|
+
## The 5 Reflection Frameworks
|
|
26
|
+
|
|
27
|
+
| # | Framework | When it fits | Output |
|
|
28
|
+
|---|-----------|--------------|--------|
|
|
29
|
+
| 1 | **Five Whys** | A symptom or surface problem with no obvious root cause | A causal chain of 5 "why" questions, ending at a root cause |
|
|
30
|
+
| 2 | **Pre-mortem** | About to commit to a plan, decision, or launch | A list of "this failed because..." stories told from a hypothetical future |
|
|
31
|
+
| 3 | **Red Team** | A strategy, hypothesis, or architecture you believe in | An adversarial attack on your own thinking — what would a competitor / critic / attacker say? |
|
|
32
|
+
| 4 | **Devil's Advocate** | A decision the team has converged on too quickly | A structured argument for the *opposite* position |
|
|
33
|
+
| 5 | **Second-order Thinking** | A choice that "obviously" makes sense | The downstream consequences — what does this cause to happen, two steps later? |
|
|
34
|
+
|
|
35
|
+
These are the named techniques inside the Reflect approach. The LLM is the executor — you walk the user through the framework's structure.
|
|
36
|
+
|
|
37
|
+
## Flow
|
|
38
|
+
|
|
39
|
+
### Step 1: Identify the Scope
|
|
40
|
+
|
|
41
|
+
If the user provided an argument, try to resolve it:
|
|
42
|
+
|
|
43
|
+
1. Treat as entity name first → `search_nodes({ query: "<argument>" })`
|
|
44
|
+
2. If exactly one match, use it as anchor
|
|
45
|
+
3. If no match but the argument matches a region id (e.g. `strategy_outcomes`, `users_needs`), use that as the scope
|
|
46
|
+
4. Otherwise, treat as a free-text scope ("our pricing strategy", "the new onboarding flow")
|
|
47
|
+
|
|
48
|
+
If no argument provided, ask:
|
|
49
|
+
|
|
50
|
+
> **What do you want to question?**
|
|
51
|
+
>
|
|
52
|
+
> Pick one:
|
|
53
|
+
> 1. A specific entity (give me a name or ID)
|
|
54
|
+
> 2. A region of your graph (e.g. strategy, users & needs, business growth)
|
|
55
|
+
> 3. The whole graph
|
|
56
|
+
> 4. Something not yet in the graph (just describe it)
|
|
57
|
+
|
|
58
|
+
### Step 2: Surface the Context
|
|
59
|
+
|
|
60
|
+
Once scope is chosen, fetch the relevant context so reflection has something to grip on:
|
|
61
|
+
|
|
62
|
+
- **Entity scope:** `get_node({ node_id })` + 1-hop neighbours via `query({ from_id, depth: 1 })`
|
|
63
|
+
- **Region scope:** `list_nodes({ type })` for the region's anchor entity, plus `get_region({ region_id })` for the canonical entity coverage
|
|
64
|
+
- **Whole graph:** `get_product_context()` digest
|
|
65
|
+
- **Free-text scope:** No fetch — work from the user's framing
|
|
66
|
+
|
|
67
|
+
Render a brief context card (3-5 lines) so the user sees what you're reflecting on. Then move on.
|
|
68
|
+
|
|
69
|
+
### Step 3: Pick the Framework
|
|
70
|
+
|
|
71
|
+
**Always start by enumerating the canonical reflection frameworks from the spec** so the recommendation table can't drift:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
get_approach({ id: "reflect" })
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The returned `framework_id_examples` carries the canonical reflection
|
|
78
|
+
framework ids (currently: `five-whys`, `pre-mortem`, `red-team`,
|
|
79
|
+
`devils-advocate`, `second-order-thinking`, plus the reflective ceremonies
|
|
80
|
+
`retrospective` and `four-forces-of-progress`). When the spec gains a new
|
|
81
|
+
reflection framework, it surfaces here automatically — no skill edit
|
|
82
|
+
required.
|
|
83
|
+
|
|
84
|
+
Recommend one based on what you just saw:
|
|
85
|
+
|
|
86
|
+
| Signal in the scope | Recommend (framework id) |
|
|
87
|
+
|---|---|
|
|
88
|
+
| A live problem or stuck-feeling symptom | `five-whys` |
|
|
89
|
+
| A plan, OKR, launch, or decision about to land | `pre-mortem` |
|
|
90
|
+
| A strategy or hypothesis the user is confident in | `red-team` |
|
|
91
|
+
| A decision that converged fast or has no documented dissent | `devils-advocate` |
|
|
92
|
+
| A "this is obviously the right move" energy | `second-order-thinking` |
|
|
93
|
+
|
|
94
|
+
If the table doesn't fit (e.g. a newly-shipped framework that isn't listed),
|
|
95
|
+
fall back to the full enumeration from `get_approach`. If unclear, present
|
|
96
|
+
the full list as numbered options and let the user pick. Always allow
|
|
97
|
+
override.
|
|
98
|
+
|
|
99
|
+
### Step 4: Walk the Framework
|
|
100
|
+
|
|
101
|
+
The canonical content for each framework — its purpose, core question,
|
|
102
|
+
when-to-use signals, when-NOT-to-use signals, and structural slots — lives
|
|
103
|
+
in the spec, not in this skill. Source of truth is
|
|
104
|
+
`packages/upg-spec/src/frameworks/definitions/` (exposed via the MCP
|
|
105
|
+
`get_framework` tool). Loading it at runtime means a framework refinement
|
|
106
|
+
or addition surfaces here without a skill edit.
|
|
107
|
+
|
|
108
|
+
Once the user picks a framework, fetch its definition:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
get_framework({ id: "<chosen_id>" })
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The returned `UPGFramework` record carries everything you need:
|
|
115
|
+
|
|
116
|
+
| Field | What to do with it |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `name` | Headline you announce ("Walking you through **Pre-mortem**…"). |
|
|
119
|
+
| `description` | One-sentence framing — say it once, then walk the framework. Don't lecture. |
|
|
120
|
+
| `education.purpose` | The "why we're doing this" line. Use it as the opening frame. |
|
|
121
|
+
| `education.core_question` | The driving question that organises the walk — anchor each prompt to it. |
|
|
122
|
+
| `education.when_to_use[]` | Confirm the scope fits one of these. If not, ask the user whether to switch frameworks. |
|
|
123
|
+
| `education.when_not_to_use[]` | Active guard-rails — if the scope matches one of these, surface it as a caveat before continuing. |
|
|
124
|
+
| `slots[]` | The structural shape of the output. Each slot has a `label`, `entityTypeId`, and `description` — these are the *containers* the framework fills. Walk the user slot-by-slot, taking their input into the slot's shape. |
|
|
125
|
+
| `structure.pattern` | If `tree`, the conversation should branch (each answer becomes the next question). If `flat`, treat slots as a checklist. If other shapes appear in spec, follow their conventional shape. |
|
|
126
|
+
|
|
127
|
+
**Walk pattern (generic):**
|
|
128
|
+
|
|
129
|
+
1. Announce the framework by `name`. State `education.purpose` in one line.
|
|
130
|
+
2. Confirm the scope sits in `education.when_to_use[]`. If a `when_not_to_use[]`
|
|
131
|
+
item applies, name it as a caveat — "this framework can flatten systems
|
|
132
|
+
problems with feedback loops, so we'll watch for that" — and continue
|
|
133
|
+
only if the user agrees.
|
|
134
|
+
3. Lead with `education.core_question`. Don't ask it directly; turn it into
|
|
135
|
+
a prompt for the user's specific scope.
|
|
136
|
+
4. Walk the slots. For each slot:
|
|
137
|
+
- Use the slot's `label` as the section heading.
|
|
138
|
+
- Use the slot's `description` to frame the prompt.
|
|
139
|
+
- Capture the user's answer into the shape implied by the slot.
|
|
140
|
+
- For `tree` patterns, the previous answer becomes the new question
|
|
141
|
+
(Five Whys, fishbone-style frameworks).
|
|
142
|
+
- For `flat` patterns, work through slots in order until covered.
|
|
143
|
+
5. If the framework definition implies multiple iterations or multiple
|
|
144
|
+
distinct stories (e.g. Pre-mortem's 4-6 failure stories, Red Team's
|
|
145
|
+
three roles, Second-order Thinking's 3-4 chains), repeat the slot walk
|
|
146
|
+
that many times — pull the iteration count from the slot description
|
|
147
|
+
when it's spelled out (e.g. "typically five iterations"), otherwise
|
|
148
|
+
default to 3-5.
|
|
149
|
+
6. Close the walk by naming what the user should sit with — the hardest
|
|
150
|
+
answer to dismiss, the least-prepared-for consequence, the cause they
|
|
151
|
+
keep deflecting from. This is the framework's payload.
|
|
152
|
+
|
|
153
|
+
Don't paste the spec back at the user. Walk them through it.
|
|
154
|
+
|
|
155
|
+
### Step 5: Capture What Surfaced
|
|
156
|
+
|
|
157
|
+
Reflection produces structured output that the graph should remember. After the framework walk, ask:
|
|
158
|
+
|
|
159
|
+
> **What surfaced that you want to capture?**
|
|
160
|
+
|
|
161
|
+
Common patterns and where they go:
|
|
162
|
+
|
|
163
|
+
| What surfaced | Capture as |
|
|
164
|
+
|---|---|
|
|
165
|
+
| A load-bearing belief that hasn't been tested | `assumption` entity, link to the anchor |
|
|
166
|
+
| A risk to mitigate | `risk` entity, link to the target it threatens |
|
|
167
|
+
| A decision that needs revisiting | `decision` entity with rationale field, link to original decision |
|
|
168
|
+
| A new hypothesis to test | Suggest `/upg-hypothesis` |
|
|
169
|
+
| A path through the graph the user wants to walk | Suggest `/upg-impact` or `/upg-connect` |
|
|
170
|
+
| Just notes — nothing structural | Skip capture; suggest user re-run `/upg-reflect` after they sit with it |
|
|
171
|
+
|
|
172
|
+
Use `create_node` + `create_edge` to capture. Always confirm before writing.
|
|
173
|
+
|
|
174
|
+
### Step 6: Smart Ending
|
|
175
|
+
|
|
176
|
+
Don't dump a menu. Pick ONE next move based on what surfaced:
|
|
177
|
+
|
|
178
|
+
- **If a hypothesis emerged:** "The biggest assumption you surfaced sounds testable. Want me to run `/upg-hypothesis` to design that experiment?"
|
|
179
|
+
- **If a risk landed:** "That risk is concrete. Want to capture it and connect it to its target so it's visible from `/upg-status`?"
|
|
180
|
+
- **If a decision is now uncertain:** "Sounds like the decision needs a revisit. Want to `/upg-inspect` it to see what depends on it?"
|
|
181
|
+
- **If reflection produced clarity, not action:** "That was the work. Want me to `/upg-snapshot` so the graph remembers this state?"
|
|
182
|
+
- **If the framework felt wrong for the scope:** "We can switch frameworks. Want me to walk this through Pre-mortem instead?"
|
|
183
|
+
|
|
184
|
+
After rendering your recommendation, call:
|
|
185
|
+
`update_session_context({ skill_invoked: "upg-reflect", recommendation: "<the next skill you recommended>" })`
|
|
186
|
+
|
|
187
|
+
## Reflection Etiquette
|
|
188
|
+
|
|
189
|
+
A few rules that make this work:
|
|
190
|
+
|
|
191
|
+
1. **Never lecture the framework.** Walk it. The user shouldn't read a Wikipedia entry for Five Whys; they should *do* Five Whys.
|
|
192
|
+
2. **One framework per session.** Don't combine. If a second framework feels relevant, suggest it as a follow-up.
|
|
193
|
+
3. **The user's discomfort is the point.** Reflect is supposed to be slightly uncomfortable. If everything is going smoothly, you're probably letting them off the hook. Push gently.
|
|
194
|
+
4. **Capture is optional.** Reflection that produces *no* graph output is still successful. The thinking happened.
|
|
195
|
+
5. **Don't propose entities the user didn't surface themselves.** Reflect mirrors back what the user said. It does not invent risks or assumptions.
|
|
196
|
+
|
|
197
|
+
## Why This Skill Exists
|
|
198
|
+
|
|
199
|
+
Reflect is one of the 5 canonical UPG approaches (`get_approach({ approach_id: "reflect" })`). Until v0.3.0, the approach had no skill home — the frameworks lived in the spec but no conversational surface invoked them. This skill closes that gap.
|
|
200
|
+
|
|
201
|
+
It's the only canonical entry point for the Reflect approach in the user-invocable surface. Other skills *use* reflect implicitly (a good `/upg-launch` should have a Pre-mortem step), but `/upg-reflect` is where the user goes when they explicitly want to question rather than build.
|