@pipefy/pipefy-process-coder 0.1.0 → 0.1.2
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/.agents/skills/ppc-pipefy-flow-authoring/SKILL.md +262 -262
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/01-main-2-danfe-consulta.json +247 -247
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/01-main-2-webhook-retorno-consulta.json +589 -589
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/01-main-4-recebimento-barramento.json +1391 -1391
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/02-subflow-2-danfe-retorno.json +623 -623
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/02-subflow-4-criacao-operacao.json +636 -636
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/03-subflow-4-criacao-titulo.json +3642 -3642
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/04-subflow-4-criacao-cedente.json +863 -863
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/04-subflow-4-criacao-sacado.json +799 -799
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/README.md +42 -42
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/hml-01-subflow-1-titulo-acompanhamento-cobranca.json +581 -581
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/hml-01-subflow-1-titulo-nfe-monitoramento.json +503 -503
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/hml-01-subflow-1-titulo-retorno-bancario.json +562 -562
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/hml-02-subflow-2-retorno-consulta-cedente.json +557 -557
- package/.agents/skills/ppc-pipefy-flow-authoring/examples/hml-02-subflow-2-retorno-consulta-sacado.json +609 -609
- package/.agents/skills/ppc-pipefy-pipe-authoring/SKILL.md +146 -146
- package/.agents/skills/ppc-pipefy-process-design/SKILL.md +127 -127
- package/.agents/skills/ppc-pipefy-workspace/SKILL.md +91 -91
- package/AGENTS.md +476 -456
- package/LICENSE +202 -0
- package/NOTICE +11 -0
- package/README.md +830 -808
- package/bin/pipe.js +25 -4
- package/dist/apply/adopt.js +128 -0
- package/dist/apply/agentops.js +42 -0
- package/dist/apply/compile.js +722 -0
- package/dist/apply/execute.js +277 -0
- package/dist/apply/flowops.js +293 -0
- package/dist/apply/idmap.js +233 -0
- package/dist/apply/mutations.js +704 -0
- package/dist/apply/registry.js +348 -0
- package/dist/apply/types.js +18 -0
- package/dist/cli/args.js +79 -0
- package/dist/cli.js +196 -0
- package/dist/codec/flow.js +164 -0
- package/dist/codec/pack.js +88 -0
- package/dist/codec/roundtrip.js +86 -0
- package/dist/codec/unpack.js +181 -0
- package/dist/commands/agents.js +145 -0
- package/dist/commands/apply.js +982 -0
- package/dist/commands/context.js +79 -0
- package/dist/commands/create.js +63 -0
- package/dist/commands/diff.js +294 -0
- package/dist/commands/flows.js +348 -0
- package/dist/commands/misc.js +562 -0
- package/dist/commands/plan.js +301 -0
- package/dist/commands/pull.js +549 -0
- package/dist/commands/runs.js +74 -0
- package/dist/commands/skills.js +131 -0
- package/dist/commands/verify.js +205 -0
- package/dist/config.js +125 -0
- package/dist/diff/agents.js +80 -0
- package/dist/diff/diff.js +983 -0
- package/dist/diff/flow.js +258 -0
- package/{src/diff/html.ts → dist/diff/html.js} +274 -322
- package/dist/diff/render.js +90 -0
- package/dist/model/payload.js +84 -0
- package/dist/model/tree.js +7 -0
- package/dist/model/volatile.js +54 -0
- package/dist/pipefy/agents.js +131 -0
- package/dist/pipefy/automations.js +155 -0
- package/dist/pipefy/capability.js +84 -0
- package/dist/pipefy/client.js +204 -0
- package/dist/pipefy/discovery.js +148 -0
- package/dist/pipefy/internal.js +406 -0
- package/dist/pipefy/ipaas.js +269 -0
- package/dist/pipefy/reconstruct.js +676 -0
- package/dist/pipefy/reference.js +172 -0
- package/dist/pipefy/snapshot.js +185 -0
- package/dist/pipefy/toolkit.js +174 -0
- package/{src → dist}/pipefy/toolkit_bearer.py +60 -14
- package/dist/report/integrations.js +165 -0
- package/dist/report/run.js +356 -0
- package/dist/util/fsx.js +50 -0
- package/dist/util/git.js +25 -0
- package/dist/util/json.js +56 -0
- package/dist/util/log.js +69 -0
- package/dist/util/pool.js +42 -0
- package/dist/util/slug.js +23 -0
- package/dist/util/tui.js +287 -0
- package/dist/validate/index.js +86 -0
- package/dist/validate/integrity.js +370 -0
- package/dist/validate/reference.js +120 -0
- package/dist/validate/schema.js +278 -0
- package/dist/version.js +18 -0
- package/dist/workspace/agents.js +245 -0
- package/{src/workspace/docs.ts → dist/workspace/docs.js} +367 -407
- package/dist/workspace/flows.js +149 -0
- package/dist/workspace/layout.js +146 -0
- package/dist/workspace/lock.js +83 -0
- package/dist/workspace/read.js +168 -0
- package/dist/workspace/reference.js +24 -0
- package/dist/workspace/stamp.js +234 -0
- package/dist/workspace/write.js +194 -0
- package/package.json +38 -35
- package/src/apply/adopt.ts +0 -150
- package/src/apply/agentops.ts +0 -71
- package/src/apply/compile.ts +0 -875
- package/src/apply/execute.ts +0 -336
- package/src/apply/flowops.ts +0 -399
- package/src/apply/idmap.ts +0 -241
- package/src/apply/mutations.ts +0 -955
- package/src/apply/registry.ts +0 -430
- package/src/apply/types.ts +0 -134
- package/src/cli/args.ts +0 -88
- package/src/cli.ts +0 -211
- package/src/codec/flow.ts +0 -199
- package/src/codec/pack.ts +0 -103
- package/src/codec/roundtrip.ts +0 -94
- package/src/codec/unpack.ts +0 -198
- package/src/commands/agents.ts +0 -184
- package/src/commands/apply.ts +0 -1144
- package/src/commands/context.ts +0 -119
- package/src/commands/create.ts +0 -79
- package/src/commands/diff.ts +0 -314
- package/src/commands/flows.ts +0 -414
- package/src/commands/misc.ts +0 -644
- package/src/commands/plan.ts +0 -331
- package/src/commands/pull.ts +0 -567
- package/src/commands/runs.ts +0 -83
- package/src/commands/skills.ts +0 -137
- package/src/commands/verify.ts +0 -253
- package/src/config.ts +0 -168
- package/src/diff/agents.ts +0 -122
- package/src/diff/diff.ts +0 -1130
- package/src/diff/flow.ts +0 -318
- package/src/diff/render.ts +0 -101
- package/src/model/payload.ts +0 -154
- package/src/model/tree.ts +0 -99
- package/src/model/volatile.ts +0 -55
- package/src/pipefy/agents.ts +0 -165
- package/src/pipefy/automations.ts +0 -219
- package/src/pipefy/capability.ts +0 -119
- package/src/pipefy/client.ts +0 -267
- package/src/pipefy/discovery.ts +0 -209
- package/src/pipefy/internal.ts +0 -380
- package/src/pipefy/ipaas.ts +0 -365
- package/src/pipefy/reconstruct.ts +0 -775
- package/src/pipefy/reference.ts +0 -251
- package/src/pipefy/snapshot.ts +0 -245
- package/src/pipefy/toolkit.ts +0 -200
- package/src/report/integrations.ts +0 -231
- package/src/report/run.ts +0 -475
- package/src/util/fsx.ts +0 -45
- package/src/util/git.ts +0 -32
- package/src/util/json.ts +0 -55
- package/src/util/log.ts +0 -76
- package/src/util/pool.ts +0 -48
- package/src/util/slug.ts +0 -26
- package/src/util/tui.ts +0 -335
- package/src/validate/index.ts +0 -123
- package/src/validate/integrity.ts +0 -387
- package/src/validate/reference.ts +0 -136
- package/src/validate/schema.ts +0 -328
- package/src/workspace/agents.ts +0 -290
- package/src/workspace/flows.ts +0 -191
- package/src/workspace/layout.ts +0 -165
- package/src/workspace/lock.ts +0 -148
- package/src/workspace/read.ts +0 -165
- package/src/workspace/reference.ts +0 -24
- package/src/workspace/stamp.ts +0 -301
- package/src/workspace/write.ts +0 -225
|
@@ -1,407 +1,367 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
2
|
-
import { rm } from 'node:fs/promises';
|
|
3
|
-
import { writeFile, exists } from
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
pipe plan --dry-run
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
\`
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
\`\`\`
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
${
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
|
306
|
-
|
|
|
307
|
-
|
|
|
308
|
-
|
|
|
309
|
-
|
|
|
310
|
-
| \`
|
|
311
|
-
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
##
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
.ppc/
|
|
369
|
-
*.gz
|
|
370
|
-
|
|
371
|
-
# Run folders hold a full copy of the pipe (runs/*/pipe.json) and its integration
|
|
372
|
-
# surface, credentials included. The report beside them is redacted and can be
|
|
373
|
-
# shared by hand; the payloads cannot.
|
|
374
|
-
runs/*/pipe*.json
|
|
375
|
-
runs/*/integrations*.json
|
|
376
|
-
|
|
377
|
-
# Uncomment to keep the structure out of git entirely.
|
|
378
|
-
# pipes/
|
|
379
|
-
# databases/
|
|
380
|
-
# runs/
|
|
381
|
-
`;
|
|
382
|
-
|
|
383
|
-
export const writeDocs = async (
|
|
384
|
-
root: string,
|
|
385
|
-
lock: Lock,
|
|
386
|
-
trees: Tree[],
|
|
387
|
-
caps: Capabilities | null,
|
|
388
|
-
ref: Reference | null = null,
|
|
389
|
-
) => {
|
|
390
|
-
const p = paths(root);
|
|
391
|
-
await writeFile(p.agentsMd, AGENTS_MD(lock, trees));
|
|
392
|
-
await writeFile(p.mappingMd, MAPPING_MD(trees, lock));
|
|
393
|
-
await writeFile(p.capabilitiesMd, CAPABILITIES_MD(caps));
|
|
394
|
-
await writeFile(p.referenceMd, renderReferenceMd(ref));
|
|
395
|
-
await writeFile(p.gitignore, GITIGNORE);
|
|
396
|
-
|
|
397
|
-
/**
|
|
398
|
-
* Migration cleanup: a workspace pulled before this rename has a stale
|
|
399
|
-
* `CLAUDE.md` sitting beside the new `AGENTS.md`, still telling whoever
|
|
400
|
-
* reads it to look here — remove it rather than leave a duplicate that
|
|
401
|
-
* silently goes out of date the next time this file is regenerated.
|
|
402
|
-
*/
|
|
403
|
-
const staleClaudeMd = join(root, 'CLAUDE.md');
|
|
404
|
-
if (await exists(staleClaudeMd)) await rm(staleClaudeMd, { force: true });
|
|
405
|
-
|
|
406
|
-
return [p.agentsMd, p.mappingMd, p.capabilitiesMd, p.referenceMd, p.gitignore];
|
|
407
|
-
};
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { rm } from 'node:fs/promises';
|
|
3
|
+
import { writeFile, exists } from "../util/fsx.js";
|
|
4
|
+
import { registryRows } from "../apply/registry.js";
|
|
5
|
+
import { paths } from "./layout.js";
|
|
6
|
+
import { renderReferenceMd } from "../pipefy/reference.js";
|
|
7
|
+
/**
|
|
8
|
+
* The generated docs that are the whole interface for whoever edits this
|
|
9
|
+
* workspace — PLAN.md §6. No agent baked into the app: the FDE pulls, edits
|
|
10
|
+
* with an agent in a terminal, and publishes. These files are what make that
|
|
11
|
+
* work unattended, regardless of which agent is reading them.
|
|
12
|
+
*/
|
|
13
|
+
const AGENTS_MD = (lock, trees) => {
|
|
14
|
+
const reconstructed = lock.repos.filter((r) => r.source === 'reconstructed');
|
|
15
|
+
const unknownGroups = new Set();
|
|
16
|
+
for (const r of lock.repos) {
|
|
17
|
+
for (const [name, cov] of Object.entries(r.coverage))
|
|
18
|
+
if (cov === 'unknown')
|
|
19
|
+
unknownGroups.add(name);
|
|
20
|
+
}
|
|
21
|
+
return `# Editing this Pipefy workspace
|
|
22
|
+
|
|
23
|
+
This directory is a Pipefy pipe (and everything it connects to) pulled to disk as
|
|
24
|
+
JSON. Edit the JSON, then let \`pipe\` validate, plan and apply it. The rules below
|
|
25
|
+
are not style preferences — breaking one either corrupts a live pipe or makes the
|
|
26
|
+
apply refuse.
|
|
27
|
+
|
|
28
|
+
## The loop
|
|
29
|
+
|
|
30
|
+
\`\`\`bash
|
|
31
|
+
pipe validate # JSON shape + referential integrity. Instant, offline.
|
|
32
|
+
pipe diff # what you changed, against the last pull
|
|
33
|
+
pipe plan --dry-run # is every change actually applicable? refuses if not
|
|
34
|
+
pipe apply # snapshot, apply, verify
|
|
35
|
+
\`\`\`
|
|
36
|
+
|
|
37
|
+
**\`pipe validate && pipe plan --dry-run\` must both be clean before you propose an
|
|
38
|
+
apply.** Iterate against them yourself; they need no network for validate and no
|
|
39
|
+
writes for plan.
|
|
40
|
+
|
|
41
|
+
## Identity rules
|
|
42
|
+
|
|
43
|
+
- \`id\` is **server-owned**. Never invent one, never edit one, never reuse one.
|
|
44
|
+
- A **new** entity is \`"id": null\` plus \`"_new": true\`. For \`phases\`, \`fields\`,
|
|
45
|
+
\`automations\` and \`labels\` — the only four arrays that have one — also add a
|
|
46
|
+
fresh v4 \`uuid\`. Everything else is identified by its owner.
|
|
47
|
+
- To **delete**, remove the object (or the whole file). Do not set a tombstone.
|
|
48
|
+
- \`_\`-prefixed keys are tool metadata. Leave them alone; they are ignored by
|
|
49
|
+
every comparator and never sent to the API.
|
|
50
|
+
|
|
51
|
+
## What cannot be changed at all
|
|
52
|
+
|
|
53
|
+
| Edit | Why it is refused |
|
|
54
|
+
| --- | --- |
|
|
55
|
+
| a field's \`type_id\` | \`updatePhaseField\` has no \`type_id\`. Doing it as delete+create destroys every existing value. |
|
|
56
|
+
| a field's \`phase_id\` | \`updatePhaseField\` has no \`phase_id\`. Same data loss. |
|
|
57
|
+
| a phase's \`index\` (reordering) | \`createPhase\` takes an index, \`updatePhase\` does not. |
|
|
58
|
+
| anything under \`_readonly/\` | no public write path exists. Read it, reason with it, don't edit it. |
|
|
59
|
+
| anything under \`_non-snapshot/\` | not part of the structure payload; never written back. |
|
|
60
|
+
| the start-form phase's existence, \`index\`, or \`name\` | it is a real phase at index 0; deleting or reindexing it is catastrophic, and its name is referenced by other things (the public form, automations, a client's own docs) this tool cannot check for breakage. |
|
|
61
|
+
|
|
62
|
+
If a request needs one of these, say so plainly and stop — do not work around it
|
|
63
|
+
with a delete and a create.
|
|
64
|
+
|
|
65
|
+
## Field references
|
|
66
|
+
|
|
67
|
+
Automations and conditions refer to fields by numeric id inside opaque JSON:
|
|
68
|
+
\`"recipients": "%{<fieldId>}"\`, \`"field_address": "433255493"\`,
|
|
69
|
+
\`"trigger_field_ids": ["433255544"]\`. \`MAPPING.md\` translates every one of those
|
|
70
|
+
numbers into a label and a phase.
|
|
71
|
+
|
|
72
|
+
- **Deleting a field obliges you to fix every reference to it.** \`pipe validate\`
|
|
73
|
+
fails on a dangling reference, by design.
|
|
74
|
+
- Referencing a field you are creating in the same edit is fine: leave the
|
|
75
|
+
placeholder \`%{_new:<uuid>}\` and the applier substitutes the real id after the
|
|
76
|
+
field exists.
|
|
77
|
+
|
|
78
|
+
## Flows reference fields too, and \`pipe validate\` does not check them
|
|
79
|
+
|
|
80
|
+
If this workspace has any \`flows/\` directories, they are iPaaS flows (Advanced
|
|
81
|
+
Automations) — the same entities as the pipe, pulled, diffed, planned and
|
|
82
|
+
applied by the same \`pipe pull/diff/plan/apply\` commands (\`--no-flows\` opts
|
|
83
|
+
out). A flow's datapills address a field by **slug**, inside opaque strings:
|
|
84
|
+
\`{{step_1['output'].data.card.fields.<slug>.value}}\`.
|
|
85
|
+
|
|
86
|
+
- **\`pipe validate\` never looks inside a flow.** Renaming, retyping or deleting
|
|
87
|
+
a field is checked against automations and conditions, never against flow
|
|
88
|
+
datapills — a broken reference there is silent until the flow runs. Before
|
|
89
|
+
editing a field a flow might use, grep \`flows/\` for its slug yourself.
|
|
90
|
+
- A field's slug does **not** change when its label is renamed (measured;
|
|
91
|
+
\`VALIDATION.md\` §3g), so a label-only rename is safe for existing datapills.
|
|
92
|
+
Anything that changes or removes the slug is not.
|
|
93
|
+
- **Adding a node to an existing flow works if the node is reachable from the
|
|
94
|
+
trigger** — some other node's \`_nextAction\`, \`_firstLoopAction\` or a
|
|
95
|
+
branch's \`_children\` must point at it, which is how the diff finds the
|
|
96
|
+
parent to send \`ADD_ACTION\` after. An orphan node nothing points to is
|
|
97
|
+
still refused, by name, rather than guessed at.
|
|
98
|
+
- Enabling a flow arms it against live traffic and is refused unless you pass
|
|
99
|
+
\`--allow-flow-enable\`, the same shape as \`--allow-card-moves\`.
|
|
100
|
+
- Load the \`ppc-pipefy-flow-authoring\` skill before writing or debugging a flow by
|
|
101
|
+
hand — it covers node shapes, datapill syntax and the traps that validate
|
|
102
|
+
clean and resolve nothing at runtime.
|
|
103
|
+
|
|
104
|
+
## Values that come from a closed set
|
|
105
|
+
|
|
106
|
+
\`REFERENCE.md\` in this workspace lists what the API accepts in every position
|
|
107
|
+
where a value must come from a fixed list: each automation event's
|
|
108
|
+
\`event_params\`, each action's \`action_params\`, which events and actions cannot be
|
|
109
|
+
paired, and the field \`type_id\`, \`color\` and \`filter\` sets. It was read from the
|
|
110
|
+
API on the last pull.
|
|
111
|
+
|
|
112
|
+
**Read it before writing an automation.** The schema publishes these lists and
|
|
113
|
+
then types the inputs that consume them as \`ID\` and \`String\`, so a wrong value is
|
|
114
|
+
not caught by GraphQL. It reaches a runtime check and comes back as \`is invalid\`
|
|
115
|
+
or \`All fields must be filled properly.\` — naming no parameter, no event, and no
|
|
116
|
+
accepted value, partway through an apply.
|
|
117
|
+
|
|
118
|
+
The traps are near-misses, not nonsense. \`in_phase_id\` is a real parameter that
|
|
119
|
+
belongs to \`card_inbox_received_email\`; \`card_moved\` takes only \`to_phase_id\`.
|
|
120
|
+
\`card_id\` is real on \`update_card_field\` and refused on \`move_single_card\`.
|
|
121
|
+
|
|
122
|
+
\`pipe validate\` checks all of this offline and names the parameter, so a wrong
|
|
123
|
+
value costs a second rather than a half-applied change. It is a backstop, not a
|
|
124
|
+
substitute for reading the file.
|
|
125
|
+
|
|
126
|
+
## Where things live
|
|
127
|
+
|
|
128
|
+
\`\`\`
|
|
129
|
+
pipes/<id>--<name>/
|
|
130
|
+
pipe.json the repo row
|
|
131
|
+
phases/NN--name.json phase + its fields[] + jump_target_ids[] + team_member_ids[]
|
|
132
|
+
automations/<name>.json automation + condition + field_maps + response_schemas
|
|
133
|
+
field-conditions/<name>.json condition + actions
|
|
134
|
+
labels.json webhooks.json public-form.json preferences.json relations.json
|
|
135
|
+
_readonly/ _non-snapshot/ _meta.json
|
|
136
|
+
databases/<id>--<apiId>--<name>/
|
|
137
|
+
table.json the database row
|
|
138
|
+
phases/00--start-form.json the database's COLUMNS live here, as fields[]
|
|
139
|
+
phases/NN--*.json record statuses — readable, no write path
|
|
140
|
+
README.md what each of those files actually is
|
|
141
|
+
\`\`\`
|
|
142
|
+
|
|
143
|
+
Fields live **inside their phase file** — that is where they make sense and where
|
|
144
|
+
you will edit them most. Ordering comes from the \`index\` value inside the file,
|
|
145
|
+
never from the filename, so renumbering works without renaming anything.
|
|
146
|
+
|
|
147
|
+
${reconstructed.length
|
|
148
|
+
? `## This workspace was read without snapshots
|
|
149
|
+
|
|
150
|
+
${reconstructed.length} of ${lock.repos.length} repo(s) were reconstructed from the
|
|
151
|
+
GraphQL and internal APIs rather than from a snapshot payload. Consequences you
|
|
152
|
+
must respect:
|
|
153
|
+
|
|
154
|
+
${[...unknownGroups].sort().map((g) => `- \`${g}\` is **unknown**, not empty. Do not add to it and do not conclude the pipe has none.`).join('\n')}
|
|
155
|
+
|
|
156
|
+
An \`unknown\` entity group is never diffed and never applied. \`_meta.json\` in each
|
|
157
|
+
repo directory records exactly what was and was not read.
|
|
158
|
+
`
|
|
159
|
+
: `## This workspace was read from snapshots
|
|
160
|
+
|
|
161
|
+
Every entity group came from the snapshot payload, so absence means absence.
|
|
162
|
+
`}
|
|
163
|
+
## Secrets
|
|
164
|
+
|
|
165
|
+
Webhook URLs are bearer-equivalent — possession is authorization — and
|
|
166
|
+
HTTP-request automations can carry live credentials in \`action_params\`. This
|
|
167
|
+
workspace has a \`.gitignore\`, but do not paste these files into anything, and do
|
|
168
|
+
not commit the workspace.
|
|
169
|
+
|
|
170
|
+
## Runs
|
|
171
|
+
|
|
172
|
+
Pass \`--report\` to any of pull, apply, dry run or verify and it writes a folder
|
|
173
|
+
under \`runs/\` recording what happened:
|
|
174
|
+
|
|
175
|
+
\`\`\`
|
|
176
|
+
runs/index.md one line per run, oldest first
|
|
177
|
+
runs/<when>--<kind>/report.md what happened, readable, no credentials
|
|
178
|
+
runs/<when>--<kind>/run.json the same thing structured
|
|
179
|
+
runs/<when>--<kind>/pipe.json the payload as of the end of that run
|
|
180
|
+
runs/<when>--<kind>/integrations.json webhooks, relations, cross-repo automations
|
|
181
|
+
\`\`\`
|
|
182
|
+
|
|
183
|
+
An apply also writes \`pipe.before.json\`, so both sides of the change sit in one
|
|
184
|
+
folder. Read \`report.md\` first — it is the only file in there written for a
|
|
185
|
+
human.
|
|
186
|
+
|
|
187
|
+
It is off by default: a run folder holds a full copy of the pipe, credentials
|
|
188
|
+
included, and one per command would grow without bound. The record an apply
|
|
189
|
+
needs in order to resume is the plan file under \`.ppc/plans/\`, which is always
|
|
190
|
+
written.
|
|
191
|
+
|
|
192
|
+
${trees.length ? `## This workspace\n\n${trees.map((t) => `- ${t.repo['name']} (${t.meta.repoKind} ${t.repo['id']}) — ${t.phases.length} phases, ${t.phases.reduce((n, p) => n + p.fields.length, 0)} fields, ${t.automations.length} automations, read from ${t.meta.source}`).join('\n')}` : ''}
|
|
193
|
+
`;
|
|
194
|
+
};
|
|
195
|
+
const MAPPING_MD = (trees, lock) => {
|
|
196
|
+
const sections = trees.map((t) => {
|
|
197
|
+
const fields = t.phases.flatMap((p) => p.fields.map((f) => ({ f, phase: String(p['name'] ?? '') })));
|
|
198
|
+
const fieldRows = fields
|
|
199
|
+
.map(({ f, phase }) => `| \`${f['id']}\` | ${f['label']} | \`${f['slug'] ?? ''}\` | ${f['type_id']} | ${phase} |`)
|
|
200
|
+
.join('\n');
|
|
201
|
+
const phaseRows = t.phases
|
|
202
|
+
.map((p) => `| ${p['index']} | \`${p['id']}\` | ${p['name']}${Number(p['index']) === 0 ? ' **(start form)**' : ''} | ${p.fields.length} | ${p.jump_target_ids.length ? p.jump_target_ids.map((j) => `\`${j}\``).join(', ') : '—'} |`)
|
|
203
|
+
.join('\n');
|
|
204
|
+
const autoRows = t.automations
|
|
205
|
+
.map((a) => `| \`${a['id']}\` | ${a['name']} | ${a['event_id']} → ${a['action_id']} | ${a['action_repo_id'] !== a['event_repo_id'] ? `cross-repo → \`${a['action_repo_id']}\`` : 'same repo'} | ${a.condition ? 'yes' : '—'} |`)
|
|
206
|
+
.join('\n');
|
|
207
|
+
const connectors = fields
|
|
208
|
+
.filter(({ f }) => f['type_id'] === 'connector')
|
|
209
|
+
.map(({ f, phase }) => `| \`${f['id']}\` | ${f['label']} | ${phase} | \`${f['connected_pipe_id']}\` |`)
|
|
210
|
+
.join('\n');
|
|
211
|
+
return `## ${t.repo['name']} — ${t.meta.repoKind} \`${t.repo['id']}\`
|
|
212
|
+
|
|
213
|
+
### Phases
|
|
214
|
+
|
|
215
|
+
| index | id | name | fields | jumps to |
|
|
216
|
+
| --- | --- | --- | --- | --- |
|
|
217
|
+
${phaseRows}
|
|
218
|
+
|
|
219
|
+
### Fields
|
|
220
|
+
|
|
221
|
+
| id | label | slug | type | phase |
|
|
222
|
+
| --- | --- | --- | --- | --- |
|
|
223
|
+
${fieldRows}
|
|
224
|
+
|
|
225
|
+
### Automations
|
|
226
|
+
|
|
227
|
+
| id | name | event → action | scope | condition |
|
|
228
|
+
| --- | --- | --- | --- | --- |
|
|
229
|
+
${autoRows || '| — | none | | | |'}
|
|
230
|
+
|
|
231
|
+
${connectors ? `### Connector fields\n\n| field id | label | phase | connected repo |\n| --- | --- | --- | --- |\n${connectors}\n` : ''}`;
|
|
232
|
+
});
|
|
233
|
+
/**
|
|
234
|
+
* Edges name what they point at, not just its id.
|
|
235
|
+
*
|
|
236
|
+
* `301908697` on its own tells a reader nothing — and a connector field aimed
|
|
237
|
+
* at a database means something different from one aimed at a pipe: the target
|
|
238
|
+
* has columns and records rather than phases and cards. The repo is looked up
|
|
239
|
+
* in the lock, so a target outside the workspace still shows as unknown rather
|
|
240
|
+
* than being guessed at.
|
|
241
|
+
*/
|
|
242
|
+
const byId = new Map(lock.repos.map((r) => [String(r.id), r]));
|
|
243
|
+
const edgeRows = lock.edges
|
|
244
|
+
.map((e) => {
|
|
245
|
+
const to = byId.get(String(e.to));
|
|
246
|
+
const target = to ? `${to.kind} · ${to.name}` : 'not in this workspace';
|
|
247
|
+
return `| \`${e.from}\` | \`${e.to}\` | ${target} | ${e.kind} | ${e.label ?? ''} |`;
|
|
248
|
+
})
|
|
249
|
+
.join('\n');
|
|
250
|
+
return `# Mapping
|
|
251
|
+
|
|
252
|
+
Generated on pull. This is what makes \`%{<fieldId>}\` mean "Requester email".
|
|
253
|
+
|
|
254
|
+
${sections.join('\n\n')}
|
|
255
|
+
|
|
256
|
+
## Connections
|
|
257
|
+
|
|
258
|
+
Discovered as the union of \`pipe_relations\`, connector fields, and cross-repo
|
|
259
|
+
automations — connector fields alone find edges that neither \`pipe_relations\` nor
|
|
260
|
+
\`pipeMapGraph\` reports (PLAN.md §2.2).
|
|
261
|
+
|
|
262
|
+
| from | to | what it is | via | label |
|
|
263
|
+
| --- | --- | --- | --- | --- |
|
|
264
|
+
${edgeRows || '| — | — | — | — | — |'}
|
|
265
|
+
`;
|
|
266
|
+
};
|
|
267
|
+
const CAPABILITIES_MD = (caps) => {
|
|
268
|
+
const rows = registryRows()
|
|
269
|
+
.sort((a, b) => a.key.localeCompare(b.key))
|
|
270
|
+
.map((r) => `| \`${r.entity}\` | ${r.op} | ${r.status === 'SUPPORTED' ? 'yes' : r.status === 'PARTIAL' ? 'partly' : '**no**'} | ${r.via ? `\`${r.via}\`` : '—'} | ${r.reason ?? ''}${r.ask ? ` _(ask ${r.ask})_` : ''} |`)
|
|
271
|
+
.join('\n');
|
|
272
|
+
const detected = caps
|
|
273
|
+
? `## Detected on this endpoint
|
|
274
|
+
|
|
275
|
+
| Capability | Present | Consequence if absent |
|
|
276
|
+
| --- | --- | --- |
|
|
277
|
+
| snapshots (\`createRepoSnapshot\`) | ${caps.snapshots ? 'yes' : '**no**'} | pulls reconstruct the structure over GraphQL instead |
|
|
278
|
+
| \`restoreRepoSnapshot\` | ${caps.restoreRepoSnapshot ? 'yes' : '**no**'} | no atomic rollback; a failed apply is repaired forward |
|
|
279
|
+
| \`createRepoDraft\` | ${caps.createRepoDraft ? 'yes' : '**no**'} | no free rehearsal environment; plans are checked, not proven |
|
|
280
|
+
| \`importRepoSnapshot\` | ${caps.importRepoSnapshot ? 'yes' : 'no'} | Route B is unavailable; writes go through the mutation compiler |
|
|
281
|
+
| internal API | ${caps.internalApi} | automation detail and phase-jump editing depend on it |
|
|
282
|
+
|
|
283
|
+
${caps.internalApi === 'unavailable' ? '> The internal endpoint did not answer. Automations and phase jumps are reported as **unknown** rather than empty, and any change to them is refused rather than silently skipped.\n' : ''}${caps.internalApi === 'incompatible' ? '> The internal endpoint refused one of this tool\'s own queries as invalid — a bug in `pipe`, not an outage. Automations are reported as **unknown** or **partial** rather than empty, and any change to them is refused. `pipe doctor` names the query.\n' : ''}`
|
|
284
|
+
: '';
|
|
285
|
+
return `# Capabilities
|
|
286
|
+
|
|
287
|
+
Generated from the live registry in \`src/apply/registry.ts\`, so what you read
|
|
288
|
+
here is what the applier will actually attempt. If something says **no**, propose
|
|
289
|
+
a different change rather than a workaround.
|
|
290
|
+
|
|
291
|
+
${detected}
|
|
292
|
+
## Write coverage
|
|
293
|
+
|
|
294
|
+
| Entity | Operation | Writable | Via | Notes |
|
|
295
|
+
| --- | --- | --- | --- | --- |
|
|
296
|
+
${rows}
|
|
297
|
+
|
|
298
|
+
## Flows (iPaaS), separately from the table above
|
|
299
|
+
|
|
300
|
+
Flows compile through \`src/apply/flowops.ts\`, not the registry above — they are
|
|
301
|
+
still applied by the same \`pipe apply\` (skip with \`--no-flows\`), just tracked
|
|
302
|
+
here rather than in the write-coverage table.
|
|
303
|
+
|
|
304
|
+
| Change | Writable | Via | Notes |
|
|
305
|
+
| --- | --- | --- | --- |
|
|
306
|
+
| create a flow | yes | \`createFlow\` (whole tree) | built trigger-first, then each node in execution order |
|
|
307
|
+
| delete a flow | yes | \`deleteFlow\` | destructive |
|
|
308
|
+
| rename a flow | yes | \`CHANGE_NAME\` | |
|
|
309
|
+
| update the trigger | yes | \`UPDATE_TRIGGER\` | whole node, not a sparse patch |
|
|
310
|
+
| update an action | yes | \`UPDATE_ACTION\` | whole node; a router's branches live in \`settings.branches\`, so editing them is an \`UPDATE_ACTION\` on the router — there is no \`UPDATE_BRANCH\` |
|
|
311
|
+
| delete an action | yes | \`DELETE_ACTION\` | destructive |
|
|
312
|
+
| add a node **to an existing flow** | yes, if reachable from the trigger | \`ADD_ACTION\` | the parent is found by scanning the authored tree for whoever's \`_nextAction\` / \`_firstLoopAction\` / \`_children\` points at the new node. An orphan node nothing points to is refused by name |
|
|
313
|
+
| enable a flow | partly | \`CHANGE_STATUS\` | arms it against live traffic — refused unless \`--allow-flow-enable\`, and refused outright while any connection it uses is still a placeholder |
|
|
314
|
+
| disable a flow | yes | \`CHANGE_STATUS\` | |
|
|
315
|
+
| a connection the flow needs but the project lacks | yes, as a placeholder | \`createConnection\` | created disabled with a placeholder credential so the structure can still be built; reconnect it for real before enabling |
|
|
316
|
+
| whole-tree replace | **refused on purpose** | — | \`IMPORT_FLOW\` exists and is never used — it re-runs an old schema migration on an already-live tree and doubles every datapill (\`{{step_1['output'].x}}\` → \`{{step_1['output']['output'].x}}\`), resolving nothing at runtime while every validator still calls it valid |
|
|
317
|
+
|
|
318
|
+
After any flow write, the applier reads the flow back and diffs its datapills
|
|
319
|
+
against what was sent — the only check that catches the \`IMPORT_FLOW\`-shaped
|
|
320
|
+
migration bug, and the reason a flow apply is not reported successful on the
|
|
321
|
+
mutation's response alone.
|
|
322
|
+
|
|
323
|
+
## The three that hurt most
|
|
324
|
+
|
|
325
|
+
1. **Field type changes** — routine to ask for, impossible to do without
|
|
326
|
+
destroying data. Refused.
|
|
327
|
+
2. **Moving a field between phases** — same.
|
|
328
|
+
3. **Reordering phases** — \`createPhase\` takes an index, \`updatePhase\` does not.
|
|
329
|
+
|
|
330
|
+
These are asks 2, 3 and 4 in \`ROUTE-B-V2.md\`. Every refusal pipe emits is a data
|
|
331
|
+
point for them.
|
|
332
|
+
`;
|
|
333
|
+
};
|
|
334
|
+
const GITIGNORE = `# A pulled workspace contains webhook URLs (bearer-equivalent: possession is
|
|
335
|
+
# authorization) and HTTP-request automation credentials. Never commit it.
|
|
336
|
+
.ppc/
|
|
337
|
+
*.gz
|
|
338
|
+
|
|
339
|
+
# Run folders hold a full copy of the pipe (runs/*/pipe.json) and its integration
|
|
340
|
+
# surface, credentials included. The report beside them is redacted and can be
|
|
341
|
+
# shared by hand; the payloads cannot.
|
|
342
|
+
runs/*/pipe*.json
|
|
343
|
+
runs/*/integrations*.json
|
|
344
|
+
|
|
345
|
+
# Uncomment to keep the structure out of git entirely.
|
|
346
|
+
# pipes/
|
|
347
|
+
# databases/
|
|
348
|
+
# runs/
|
|
349
|
+
`;
|
|
350
|
+
export const writeDocs = async (root, lock, trees, caps, ref = null) => {
|
|
351
|
+
const p = paths(root);
|
|
352
|
+
await writeFile(p.agentsMd, AGENTS_MD(lock, trees));
|
|
353
|
+
await writeFile(p.mappingMd, MAPPING_MD(trees, lock));
|
|
354
|
+
await writeFile(p.capabilitiesMd, CAPABILITIES_MD(caps));
|
|
355
|
+
await writeFile(p.referenceMd, renderReferenceMd(ref));
|
|
356
|
+
await writeFile(p.gitignore, GITIGNORE);
|
|
357
|
+
/**
|
|
358
|
+
* Migration cleanup: a workspace pulled before this rename has a stale
|
|
359
|
+
* `CLAUDE.md` sitting beside the new `AGENTS.md`, still telling whoever
|
|
360
|
+
* reads it to look here — remove it rather than leave a duplicate that
|
|
361
|
+
* silently goes out of date the next time this file is regenerated.
|
|
362
|
+
*/
|
|
363
|
+
const staleClaudeMd = join(root, 'CLAUDE.md');
|
|
364
|
+
if (await exists(staleClaudeMd))
|
|
365
|
+
await rm(staleClaudeMd, { force: true });
|
|
366
|
+
return [p.agentsMd, p.mappingMd, p.capabilitiesMd, p.referenceMd, p.gitignore];
|
|
367
|
+
};
|