@prmichaelsen/acp-visualizer 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.
Files changed (159) hide show
  1. package/package.json +8 -10
  2. package/src/components/ExtraFieldsBadge.tsx +1 -1
  3. package/src/components/FilterBar.tsx +1 -1
  4. package/src/components/Header.tsx +1 -1
  5. package/src/components/MilestoneTable.tsx +1 -1
  6. package/src/components/MilestoneTree.tsx +2 -2
  7. package/src/components/StatusBadge.tsx +1 -1
  8. package/src/components/StatusDot.tsx +1 -1
  9. package/src/components/TaskList.tsx +1 -1
  10. package/src/routes/__root.tsx +5 -5
  11. package/src/routes/api/watch.ts +1 -1
  12. package/src/routes/index.tsx +2 -2
  13. package/src/routes/milestones.tsx +7 -7
  14. package/src/routes/search.tsx +4 -4
  15. package/src/routes/tasks.tsx +3 -3
  16. package/src/services/progress-database.service.ts +3 -3
  17. package/agent/commands/acp.clarification-address.md +0 -417
  18. package/agent/commands/acp.clarification-capture.md +0 -386
  19. package/agent/commands/acp.clarification-create.md +0 -437
  20. package/agent/commands/acp.clarifications-research.md +0 -326
  21. package/agent/commands/acp.command-create.md +0 -432
  22. package/agent/commands/acp.design-create.md +0 -286
  23. package/agent/commands/acp.design-reference.md +0 -355
  24. package/agent/commands/acp.handoff.md +0 -270
  25. package/agent/commands/acp.index.md +0 -423
  26. package/agent/commands/acp.init.md +0 -546
  27. package/agent/commands/acp.package-create.md +0 -895
  28. package/agent/commands/acp.package-info.md +0 -212
  29. package/agent/commands/acp.package-install.md +0 -539
  30. package/agent/commands/acp.package-list.md +0 -280
  31. package/agent/commands/acp.package-publish.md +0 -541
  32. package/agent/commands/acp.package-remove.md +0 -293
  33. package/agent/commands/acp.package-search.md +0 -307
  34. package/agent/commands/acp.package-update.md +0 -361
  35. package/agent/commands/acp.package-validate.md +0 -540
  36. package/agent/commands/acp.pattern-create.md +0 -386
  37. package/agent/commands/acp.plan.md +0 -587
  38. package/agent/commands/acp.proceed.md +0 -882
  39. package/agent/commands/acp.project-create.md +0 -675
  40. package/agent/commands/acp.project-info.md +0 -312
  41. package/agent/commands/acp.project-list.md +0 -226
  42. package/agent/commands/acp.project-remove.md +0 -379
  43. package/agent/commands/acp.project-set.md +0 -227
  44. package/agent/commands/acp.project-update.md +0 -307
  45. package/agent/commands/acp.projects-restore.md +0 -228
  46. package/agent/commands/acp.projects-sync.md +0 -347
  47. package/agent/commands/acp.report.md +0 -407
  48. package/agent/commands/acp.resume.md +0 -239
  49. package/agent/commands/acp.sessions.md +0 -301
  50. package/agent/commands/acp.status.md +0 -293
  51. package/agent/commands/acp.sync.md +0 -364
  52. package/agent/commands/acp.task-create.md +0 -500
  53. package/agent/commands/acp.update.md +0 -302
  54. package/agent/commands/acp.validate.md +0 -466
  55. package/agent/commands/acp.version-check-for-updates.md +0 -276
  56. package/agent/commands/acp.version-check.md +0 -191
  57. package/agent/commands/acp.version-update.md +0 -289
  58. package/agent/commands/command.template.md +0 -339
  59. package/agent/commands/git.commit.md +0 -526
  60. package/agent/commands/git.init.md +0 -514
  61. package/agent/commands/tanstack-cloudflare.deploy.md +0 -272
  62. package/agent/commands/tanstack-cloudflare.tail.md +0 -275
  63. package/agent/design/.gitkeep +0 -0
  64. package/agent/design/design.template.md +0 -154
  65. package/agent/design/local.dashboard-layout-routing.md +0 -288
  66. package/agent/design/local.data-model-yaml-parsing.md +0 -310
  67. package/agent/design/local.search-filtering.md +0 -331
  68. package/agent/design/local.server-api-auto-refresh.md +0 -235
  69. package/agent/design/local.table-tree-views.md +0 -299
  70. package/agent/design/local.visualizer-requirements.md +0 -349
  71. package/agent/design/requirements.template.md +0 -387
  72. package/agent/index/.gitkeep +0 -0
  73. package/agent/index/acp.core.yaml +0 -137
  74. package/agent/index/local.main.template.yaml +0 -37
  75. package/agent/manifest.template.yaml +0 -13
  76. package/agent/manifest.yaml +0 -302
  77. package/agent/milestones/.gitkeep +0 -0
  78. package/agent/milestones/milestone-1-project-scaffold-data-pipeline.md +0 -67
  79. package/agent/milestones/milestone-1-{title}.template.md +0 -206
  80. package/agent/milestones/milestone-2-dashboard-views-interaction.md +0 -79
  81. package/agent/package.template.yaml +0 -86
  82. package/agent/patterns/.gitkeep +0 -0
  83. package/agent/patterns/bootstrap.template.md +0 -1237
  84. package/agent/patterns/pattern.template.md +0 -382
  85. package/agent/patterns/tanstack-cloudflare.acl-permissions.md +0 -332
  86. package/agent/patterns/tanstack-cloudflare.action-bar-item.md +0 -416
  87. package/agent/patterns/tanstack-cloudflare.api-route-handlers.md +0 -401
  88. package/agent/patterns/tanstack-cloudflare.auth-session-management.md +0 -387
  89. package/agent/patterns/tanstack-cloudflare.card-and-list.md +0 -271
  90. package/agent/patterns/tanstack-cloudflare.chat-engine.md +0 -353
  91. package/agent/patterns/tanstack-cloudflare.confirmation-tokens.md +0 -346
  92. package/agent/patterns/tanstack-cloudflare.durable-objects-websocket.md +0 -516
  93. package/agent/patterns/tanstack-cloudflare.email-service.md +0 -431
  94. package/agent/patterns/tanstack-cloudflare.expander.md +0 -98
  95. package/agent/patterns/tanstack-cloudflare.fcm-push.md +0 -115
  96. package/agent/patterns/tanstack-cloudflare.firebase-anonymous-sessions.md +0 -441
  97. package/agent/patterns/tanstack-cloudflare.firebase-auth.md +0 -348
  98. package/agent/patterns/tanstack-cloudflare.firebase-firestore.md +0 -550
  99. package/agent/patterns/tanstack-cloudflare.firebase-storage.md +0 -369
  100. package/agent/patterns/tanstack-cloudflare.form-controls.md +0 -145
  101. package/agent/patterns/tanstack-cloudflare.global-search-context.md +0 -93
  102. package/agent/patterns/tanstack-cloudflare.image-carousel.md +0 -126
  103. package/agent/patterns/tanstack-cloudflare.library-services.md +0 -553
  104. package/agent/patterns/tanstack-cloudflare.lightbox.md +0 -169
  105. package/agent/patterns/tanstack-cloudflare.markdown-content.md +0 -115
  106. package/agent/patterns/tanstack-cloudflare.mention-suggestions.md +0 -98
  107. package/agent/patterns/tanstack-cloudflare.modal.md +0 -156
  108. package/agent/patterns/tanstack-cloudflare.nextjs-to-tanstack-routing.md +0 -461
  109. package/agent/patterns/tanstack-cloudflare.notifications-engine.md +0 -151
  110. package/agent/patterns/tanstack-cloudflare.oauth-token-refresh.md +0 -90
  111. package/agent/patterns/tanstack-cloudflare.og-metadata.md +0 -296
  112. package/agent/patterns/tanstack-cloudflare.pagination.md +0 -442
  113. package/agent/patterns/tanstack-cloudflare.pill-input.md +0 -220
  114. package/agent/patterns/tanstack-cloudflare.provider-adapter.md +0 -401
  115. package/agent/patterns/tanstack-cloudflare.rate-limiting.md +0 -323
  116. package/agent/patterns/tanstack-cloudflare.scheduled-tasks.md +0 -338
  117. package/agent/patterns/tanstack-cloudflare.searchable-settings.md +0 -375
  118. package/agent/patterns/tanstack-cloudflare.slide-over.md +0 -129
  119. package/agent/patterns/tanstack-cloudflare.ssr-preload.md +0 -571
  120. package/agent/patterns/tanstack-cloudflare.third-party-api-integration.md +0 -508
  121. package/agent/patterns/tanstack-cloudflare.toast-system.md +0 -142
  122. package/agent/patterns/tanstack-cloudflare.unified-header.md +0 -280
  123. package/agent/patterns/tanstack-cloudflare.user-scoped-collections.md +0 -628
  124. package/agent/patterns/tanstack-cloudflare.websocket-manager.md +0 -237
  125. package/agent/patterns/tanstack-cloudflare.wrangler-configuration.md +0 -358
  126. package/agent/patterns/tanstack-cloudflare.zod-schema-validation.md +0 -336
  127. package/agent/progress.template.yaml +0 -161
  128. package/agent/progress.yaml +0 -145
  129. package/agent/schemas/package.schema.yaml +0 -276
  130. package/agent/scripts/acp.common.sh +0 -1781
  131. package/agent/scripts/acp.install.sh +0 -333
  132. package/agent/scripts/acp.package-create.sh +0 -924
  133. package/agent/scripts/acp.package-info.sh +0 -288
  134. package/agent/scripts/acp.package-install.sh +0 -893
  135. package/agent/scripts/acp.package-list.sh +0 -311
  136. package/agent/scripts/acp.package-publish.sh +0 -420
  137. package/agent/scripts/acp.package-remove.sh +0 -348
  138. package/agent/scripts/acp.package-search.sh +0 -156
  139. package/agent/scripts/acp.package-update.sh +0 -517
  140. package/agent/scripts/acp.package-validate.sh +0 -1018
  141. package/agent/scripts/acp.uninstall.sh +0 -85
  142. package/agent/scripts/acp.version-check-for-updates.sh +0 -98
  143. package/agent/scripts/acp.version-check.sh +0 -47
  144. package/agent/scripts/acp.version-update.sh +0 -176
  145. package/agent/scripts/acp.yaml-parser.sh +0 -985
  146. package/agent/scripts/acp.yaml-validate.sh +0 -205
  147. package/agent/tasks/.gitkeep +0 -0
  148. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-1-initialize-tanstack-start-project.md +0 -210
  149. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-2-implement-data-model-yaml-parser.md +0 -294
  150. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-3-build-server-api-data-loading.md +0 -193
  151. package/agent/tasks/milestone-1-project-scaffold-data-pipeline/task-4-add-auto-refresh-sse.md +0 -262
  152. package/agent/tasks/milestone-2-dashboard-views-interaction/task-10-polish-integration-testing.md +0 -156
  153. package/agent/tasks/milestone-2-dashboard-views-interaction/task-5-build-dashboard-layout-routing.md +0 -178
  154. package/agent/tasks/milestone-2-dashboard-views-interaction/task-6-build-overview-page.md +0 -141
  155. package/agent/tasks/milestone-2-dashboard-views-interaction/task-7-implement-milestone-table-view.md +0 -153
  156. package/agent/tasks/milestone-2-dashboard-views-interaction/task-8-implement-milestone-tree-view.md +0 -174
  157. package/agent/tasks/milestone-2-dashboard-views-interaction/task-9-implement-search-filtering.md +0 -233
  158. package/agent/tasks/task-1-{title}.template.md +0 -244
  159. package/vitest.config.ts +0 -27
@@ -1,985 +0,0 @@
1
- #!/bin/bash
2
- # Generic YAML Parser with AST
3
- # Pure POSIX shell implementation
4
- # Version: 1.0.0
5
- # Created: 2026-02-21
6
-
7
- # ============================================================================
8
- # PORTABILITY HELPERS
9
- # ============================================================================
10
-
11
- # Portable in-place sed (works on both GNU and BSD/macOS sed)
12
- # Usage: _yaml_sed_i "expression" "file"
13
- _yaml_sed_i() {
14
- if [ "$(uname)" = "Darwin" ]; then
15
- sed -i '' "$@"
16
- else
17
- sed -i "$@"
18
- fi
19
- }
20
-
21
- # ============================================================================
22
- # GLOBAL STATE
23
- # ============================================================================
24
-
25
- # Prevent variable reset on re-sourcing
26
- if [ -z "${YAML_PARSER_LOADED:-}" ]; then
27
- YAML_PARSER_LOADED=1
28
- AST_FILE=""
29
- AST_ROOT_ID=0
30
- YAML_CURRENT_FILE=""
31
- fi
32
-
33
- # ============================================================================
34
- # UTILITY FUNCTIONS
35
- # ============================================================================
36
-
37
- init_ast() {
38
- AST_FILE=$(mktemp)
39
- echo "0|map||root|-1|" > "$AST_FILE"
40
- AST_ROOT_ID=0
41
- }
42
-
43
- cleanup_ast() {
44
- if [ -n "$AST_FILE" ] && [ -f "$AST_FILE" ]; then
45
- rm -f "$AST_FILE"
46
- fi
47
- }
48
-
49
- get_next_node_id() {
50
- wc -l < "$AST_FILE"
51
- }
52
-
53
- create_node() {
54
- local type="$1"
55
- local key="$2"
56
- local value="$3"
57
- local parent_id="$4"
58
-
59
- key=$(echo "$key" | sed 's/|/\\|/g')
60
- value=$(echo "$value" | sed 's/|/\\|/g')
61
-
62
- local node_id
63
- node_id=$(get_next_node_id)
64
-
65
- echo "$node_id|$type|$key|$value|$parent_id|" >> "$AST_FILE"
66
- echo "$node_id"
67
- }
68
-
69
- get_node() {
70
- local node_id="$1"
71
- sed -n "$((node_id + 1))p" "$AST_FILE"
72
- }
73
-
74
- get_node_field() {
75
- local node_id="$1"
76
- local field_num="$2"
77
- get_node "$node_id" | cut -d'|' -f"$field_num"
78
- }
79
-
80
- add_child() {
81
- local parent_id="$1"
82
- local child_id="$2"
83
-
84
- local node
85
- node=$(get_node "$parent_id")
86
-
87
- local id type key value parent children
88
- id=$(echo "$node" | cut -d'|' -f1)
89
- type=$(echo "$node" | cut -d'|' -f2)
90
- key=$(echo "$node" | cut -d'|' -f3)
91
- value=$(echo "$node" | cut -d'|' -f4)
92
- parent=$(echo "$node" | cut -d'|' -f5)
93
- children=$(echo "$node" | cut -d'|' -f6)
94
-
95
- if [ -z "$children" ]; then
96
- children="$child_id"
97
- else
98
- children="$children,$child_id"
99
- fi
100
-
101
- local updated="$id|$type|$key|$value|$parent|$children"
102
- _yaml_sed_i "$((parent_id + 1))s@.*@$updated@" "$AST_FILE"
103
- }
104
-
105
- update_node_type() {
106
- local node_id="$1"
107
- local new_type="$2"
108
-
109
- local node
110
- node=$(get_node "$node_id")
111
-
112
- local id type key value parent children
113
- id=$(echo "$node" | cut -d'|' -f1)
114
- key=$(echo "$node" | cut -d'|' -f3)
115
- value=$(echo "$node" | cut -d'|' -f4)
116
- parent=$(echo "$node" | cut -d'|' -f5)
117
- children=$(echo "$node" | cut -d'|' -f6)
118
-
119
- local updated="$id|$new_type|$key|$value|$parent|$children"
120
- _yaml_sed_i "$((node_id + 1))s@.*@$updated@" "$AST_FILE"
121
- }
122
-
123
- # ============================================================================
124
- # PARSER
125
- # ============================================================================
126
-
127
- yaml_parse() {
128
- local file="$1"
129
-
130
- if [ ! -f "$file" ]; then
131
- echo "Error: File not found: $file" >&2
132
- return 1
133
- fi
134
-
135
- cleanup_ast
136
- init_ast
137
- YAML_CURRENT_FILE="$file"
138
-
139
- # State tracking
140
- local parent_stack="0"
141
- local indent_stack="-1"
142
- local current_parent=0
143
- local prev_indent=-1
144
- local last_key_node=-1
145
-
146
- while IFS= read -r line || [ -n "$line" ]; do
147
- # Skip empty lines
148
- [ -z "$line" ] && continue
149
-
150
- # Skip comment lines
151
- case "$line" in \#*) continue ;; esac
152
-
153
- # Strip inline comments
154
- line=$(echo "$line" | sed 's/#.*$//')
155
-
156
- # Calculate indentation
157
- local indent=0
158
- local trimmed="$line"
159
- while [ "$trimmed" != "${trimmed# }" ]; do
160
- indent=$((indent + 1))
161
- trimmed="${trimmed# }"
162
- done
163
-
164
- # Skip empty after trim
165
- [ -z "$trimmed" ] && continue
166
-
167
- # Handle dedent - pop stack
168
- while [ "$prev_indent" -ge 0 ] && [ "$indent" -le "$prev_indent" ]; do
169
- # Pop one level
170
- parent_stack=$(echo "$parent_stack" | sed 's/,[^,]*$//')
171
- indent_stack=$(echo "$indent_stack" | sed 's/,[^,]*$//')
172
-
173
- # Get new current parent
174
- current_parent=$(echo "$parent_stack" | awk -F',' '{print $NF}')
175
- prev_indent=$(echo "$indent_stack" | awk -F',' '{print $NF}')
176
-
177
- # Handle empty stack
178
- [ -z "$current_parent" ] && current_parent=0
179
- [ -z "$prev_indent" ] && prev_indent=-1
180
-
181
- last_key_node=-1
182
- done
183
-
184
- # Parse line content
185
- if echo "$trimmed" | grep -q '^-[[:space:]]'; then
186
- # Array item
187
- local item_content
188
- item_content=$(echo "$trimmed" | sed 's/^-[[:space:]]*//')
189
-
190
- # Convert last key node to array if needed
191
- if [ "$last_key_node" -ge 0 ]; then
192
- update_node_type "$last_key_node" "array"
193
- current_parent="$last_key_node"
194
- last_key_node=-1
195
- fi
196
-
197
- # Check if inline object (has colon on same line)
198
- if echo "$item_content" | grep -q ':'; then
199
- # Inline object: - name: value
200
- local obj_node
201
- obj_node=$(create_node "map" "" "" "$current_parent")
202
- add_child "$current_parent" "$obj_node"
203
-
204
- # Parse first field
205
- local key value
206
- key=$(echo "$item_content" | cut -d':' -f1 | sed 's/[[:space:]]*$//')
207
- value=$(echo "$item_content" | cut -d':' -f2- | sed 's/^[[:space:]]*//')
208
-
209
- local field_node
210
- field_node=$(create_node "scalar" "$key" "$value" "$obj_node")
211
- add_child "$obj_node" "$field_node"
212
-
213
- # Push object onto stack for potential additional fields
214
- parent_stack="$parent_stack,$obj_node"
215
- indent_stack="$indent_stack,$indent"
216
- current_parent="$obj_node"
217
- prev_indent="$indent"
218
- else
219
- # Simple array item: - value
220
- local item_node
221
- item_node=$(create_node "scalar" "" "$item_content" "$current_parent")
222
- add_child "$current_parent" "$item_node"
223
- fi
224
- elif echo "$trimmed" | grep -q ':'; then
225
- # Key-value pair
226
- local key value
227
- key=$(echo "$trimmed" | cut -d':' -f1 | sed 's/[[:space:]]*$//')
228
- value=$(echo "$trimmed" | cut -d':' -f2- | sed 's/^[[:space:]]*//')
229
-
230
- if [ -z "$value" ]; then
231
- # Key with no value - map or array follows
232
- local node_id
233
- node_id=$(create_node "map" "$key" "" "$current_parent")
234
- add_child "$current_parent" "$node_id"
235
-
236
- # Push onto stack
237
- parent_stack="$parent_stack,$node_id"
238
- indent_stack="$indent_stack,$indent"
239
- current_parent="$node_id"
240
- prev_indent="$indent"
241
- last_key_node="$node_id"
242
- else
243
- # Check for empty array [] or empty map {}
244
- if [ "$value" = "[]" ]; then
245
- # Empty array
246
- local node_id
247
- node_id=$(create_node "array" "$key" "" "$current_parent")
248
- add_child "$current_parent" "$node_id"
249
- elif [ "$value" = "{}" ]; then
250
- # Empty map
251
- local node_id
252
- node_id=$(create_node "map" "$key" "" "$current_parent")
253
- add_child "$current_parent" "$node_id"
254
- else
255
- # Scalar value
256
- local node_id
257
- node_id=$(create_node "scalar" "$key" "$value" "$current_parent")
258
- add_child "$current_parent" "$node_id"
259
- fi
260
- fi
261
- fi
262
- done < "$file"
263
-
264
- return 0
265
- }
266
-
267
- # ============================================================================
268
- # QUERY ENGINE
269
- # ============================================================================
270
-
271
- find_child_by_key() {
272
- local parent_id="$1"
273
- local key="$2"
274
-
275
- local children
276
- children=$(get_node_field "$parent_id" 6)
277
-
278
- [ -z "$children" ] && return 1
279
-
280
- local IFS=','
281
- for child_id in $children; do
282
- local child_key
283
- child_key=$(get_node_field "$child_id" 3)
284
-
285
- if [ "$child_key" = "$key" ]; then
286
- echo "$child_id"
287
- return 0
288
- fi
289
- done
290
-
291
- return 1
292
- }
293
-
294
- find_child_by_index() {
295
- local parent_id="$1"
296
- local index="$2"
297
-
298
- local children
299
- children=$(get_node_field "$parent_id" 6)
300
-
301
- [ -z "$children" ] && return 1
302
-
303
- local child_id
304
- child_id=$(echo "$children" | tr ',' '\n' | sed -n "$((index + 1))p")
305
-
306
- if [ -n "$child_id" ]; then
307
- echo "$child_id"
308
- return 0
309
- fi
310
-
311
- return 1
312
- }
313
-
314
- yaml_query() {
315
- local path="$1"
316
-
317
- if [ -z "$AST_FILE" ] || [ ! -f "$AST_FILE" ]; then
318
- echo "Error: No AST loaded. Call yaml_parse first." >&2
319
- return 1
320
- fi
321
-
322
- path=$(echo "$path" | sed 's/^\.//')
323
-
324
- local current_node="$AST_ROOT_ID"
325
-
326
- local IFS='.'
327
- for segment in $path; do
328
- if echo "$segment" | grep -q '\['; then
329
- local key index
330
- key=$(echo "$segment" | sed 's/\[.*//')
331
- index=$(echo "$segment" | sed 's/.*\[\([0-9]*\)\].*/\1/')
332
-
333
- current_node=$(find_child_by_key "$current_node" "$key")
334
- [ -z "$current_node" ] && return 1
335
-
336
- current_node=$(find_child_by_index "$current_node" "$index")
337
- [ -z "$current_node" ] && return 1
338
- else
339
- current_node=$(find_child_by_key "$current_node" "$segment")
340
- [ -z "$current_node" ] && return 1
341
- fi
342
- done
343
-
344
- # Check node type
345
- local node_type
346
- node_type=$(get_node_field "$current_node" 2)
347
-
348
- # For map or array nodes, return children keys in YAML format
349
- if [ "$node_type" = "map" ] || [ "$node_type" = "array" ]; then
350
- local children
351
- children=$(get_node_field "$current_node" 6)
352
-
353
- if [ -n "$children" ]; then
354
- # Split by comma and iterate
355
- local IFS=','
356
- for child_id in $children; do
357
- local child_key
358
- child_key=$(get_node_field "$child_id" 3)
359
- echo "${child_key}:"
360
- done
361
- fi
362
- else
363
- # For scalar nodes, return the value
364
- get_node_field "$current_node" 4
365
- fi
366
- }
367
-
368
- # Create a new node in the AST (used by yaml_set for auto-creation)
369
- # Usage: node_id=$(create_node_and_link "type" "key" "value" "parent_id")
370
- # Returns: new node ID
371
- # NOTE: This version adds the node as a child of parent (for yaml_set)
372
- create_node_and_link() {
373
- local type="$1"
374
- local key="$2"
375
- local value="$3"
376
- local parent_id="$4"
377
-
378
- # Get next node ID
379
- local next_id
380
- next_id=$(wc -l < "$AST_FILE")
381
-
382
- # Create node: id|type|key|value|parent|children
383
- echo "${next_id}|${type}|${key}|${value}|${parent_id}|" >> "$AST_FILE"
384
-
385
- # Add this node to parent's children list
386
- if [ "$parent_id" != "-1" ]; then
387
- # Read current parent node
388
- local parent_line
389
- parent_line=$(sed -n "$((parent_id + 1))p" "$AST_FILE")
390
-
391
- # Extract parent fields
392
- local parent_children
393
- parent_children=$(echo "$parent_line" | cut -d'|' -f6)
394
-
395
- # Append new child ID
396
- if [ -z "$parent_children" ]; then
397
- parent_children="$next_id"
398
- else
399
- parent_children="${parent_children},${next_id}"
400
- fi
401
-
402
- # Update parent node with new children list
403
- local parent_prefix
404
- parent_prefix=$(echo "$parent_line" | cut -d'|' -f1-5)
405
- _yaml_sed_i "$((parent_id + 1))s@.*@${parent_prefix}|${parent_children}@" "$AST_FILE"
406
- fi
407
-
408
- echo "$next_id"
409
- }
410
-
411
- # Original create_node for backward compatibility (does NOT link to parent)
412
- create_node() {
413
- local type="$1"
414
- local key="$2"
415
- local value="$3"
416
- local parent_id="$4"
417
-
418
- # Get next node ID
419
- local next_id
420
- next_id=$(wc -l < "$AST_FILE")
421
-
422
- # Create node: id|type|key|value|parent|children
423
- echo "${next_id}|${type}|${key}|${value}|${parent_id}|" >> "$AST_FILE"
424
-
425
- echo "$next_id"
426
- }
427
-
428
- yaml_set() {
429
- local path="$1"
430
- local new_value="$2"
431
-
432
- if [ -z "$AST_FILE" ] || [ ! -f "$AST_FILE" ]; then
433
- echo "Error: No AST loaded. Call yaml_parse first." >&2
434
- return 1
435
- fi
436
-
437
- path=$(echo "$path" | sed 's/^\.//')
438
-
439
- local current_node="$AST_ROOT_ID"
440
- local IFS='.'
441
- local segments=($path)
442
- local last_index=$((${#segments[@]} - 1))
443
-
444
- # Traverse path, creating missing nodes
445
- local i=0
446
- for segment in "${segments[@]}"; do
447
- local is_last=$((i == last_index))
448
-
449
- if echo "$segment" | grep -q '\['; then
450
- local key index
451
- key=$(echo "$segment" | sed 's/\[.*//')
452
- index=$(echo "$segment" | sed 's/.*\[\([0-9]*\)\].*/\1/')
453
-
454
- local child_node
455
- child_node=$(find_child_by_key "$current_node" "$key")
456
- if [ -z "$child_node" ]; then
457
- # Create missing array node
458
- child_node=$(create_node_and_link "array" "$key" "" "$current_node")
459
- fi
460
- current_node="$child_node"
461
-
462
- child_node=$(find_child_by_index "$current_node" "$index")
463
- if [ -z "$child_node" ]; then
464
- echo "Error: Cannot create array index $index (not supported yet)" >&2
465
- return 1
466
- fi
467
- current_node="$child_node"
468
- else
469
- local child_node
470
- child_node=$(find_child_by_key "$current_node" "$segment")
471
-
472
- if [ -z "$child_node" ]; then
473
- # Create missing node
474
- if [ "$is_last" -eq 1 ]; then
475
- # Last segment - check for empty array/map
476
- if [ "$new_value" = "[]" ]; then
477
- # Create empty array
478
- child_node=$(create_node_and_link "array" "$segment" "" "$current_node")
479
- elif [ "$new_value" = "{}" ]; then
480
- # Create empty map
481
- child_node=$(create_node_and_link "map" "$segment" "" "$current_node")
482
- else
483
- # Create scalar with value
484
- child_node=$(create_node_and_link "scalar" "$segment" "$new_value" "$current_node")
485
- fi
486
- return 0
487
- else
488
- # Intermediate segment - create map
489
- child_node=$(create_node_and_link "map" "$segment" "" "$current_node")
490
- fi
491
- fi
492
- current_node="$child_node"
493
- fi
494
-
495
- i=$((i + 1))
496
- done
497
-
498
- # Update existing node value
499
- local node
500
- node=$(get_node "$current_node")
501
-
502
- local id type key value parent children
503
- id=$(echo "$node" | cut -d'|' -f1)
504
- type=$(echo "$node" | cut -d'|' -f2)
505
- key=$(echo "$node" | cut -d'|' -f3)
506
- parent=$(echo "$node" | cut -d'|' -f5)
507
- children=$(echo "$node" | cut -d'|' -f6)
508
-
509
- # Check if converting to empty array
510
- if [ "$new_value" = "[]" ]; then
511
- # Convert node to array type and clear children
512
- local updated="$id|array|$key||$parent|"
513
- _yaml_sed_i "$((current_node + 1))s@.*@$updated@" "$AST_FILE"
514
- else
515
- new_value=$(echo "$new_value" | sed 's/|/\\|/g')
516
- local updated="$id|$type|$key|$new_value|$parent|$children"
517
- _yaml_sed_i "$((current_node + 1))s@.*@$updated@" "$AST_FILE"
518
- fi
519
- }
520
-
521
- yaml_write() {
522
- local output_file="$1"
523
-
524
- if [ -z "$AST_FILE" ] || [ ! -f "$AST_FILE" ]; then
525
- echo "Error: No AST loaded. Call yaml_parse first." >&2
526
- return 1
527
- fi
528
-
529
- serialize_node "$AST_ROOT_ID" 0 > "$output_file"
530
- }
531
-
532
- serialize_node() {
533
- local node_id="$1"
534
- local indent_level="$2"
535
- local parent_type="${3:-}"
536
-
537
- local node
538
- node=$(get_node "$node_id")
539
-
540
- local type key value children parent_id
541
- type=$(echo "$node" | cut -d'|' -f2)
542
- key=$(echo "$node" | cut -d'|' -f3)
543
- value=$(echo "$node" | cut -d'|' -f4)
544
- parent_id=$(echo "$node" | cut -d'|' -f5)
545
- children=$(echo "$node" | cut -d'|' -f6)
546
-
547
- # Determine parent type if not provided
548
- if [ -z "$parent_type" ] && [ "$parent_id" -ge 0 ]; then
549
- parent_type=$(get_node_field "$parent_id" 2)
550
- fi
551
-
552
- local indent=""
553
- local i=0
554
- while [ "$i" -lt "$indent_level" ]; do
555
- indent="$indent "
556
- i=$((i + 1))
557
- done
558
-
559
- case "$type" in
560
- scalar)
561
- if [ -n "$key" ]; then
562
- echo "$indent$key: $value"
563
- else
564
- echo "$indent- $value"
565
- fi
566
- ;;
567
-
568
- map)
569
- # If this map is in an array, first child gets dash prefix
570
- local is_first_child=true
571
-
572
- if [ "$node_id" -ne 0 ] && [ -n "$key" ]; then
573
- echo "$indent$key:"
574
- fi
575
-
576
- if [ -n "$children" ]; then
577
- local IFS=','
578
- local next_indent
579
- # Root node (id=0) doesn't add indentation
580
- if [ "$node_id" -eq 0 ]; then
581
- next_indent="$indent_level"
582
- else
583
- next_indent="$((indent_level + 1))"
584
- fi
585
-
586
- for child_id in $children; do
587
- # If parent is array and this is first child, use dash
588
- if [ "$parent_type" = "array" ] && [ "$is_first_child" = true ]; then
589
- # Serialize first field with dash
590
- local child_node
591
- child_node=$(get_node "$child_id")
592
- local child_type child_key child_value
593
- child_type=$(echo "$child_node" | cut -d'|' -f2)
594
- child_key=$(echo "$child_node" | cut -d'|' -f3)
595
- child_value=$(echo "$child_node" | cut -d'|' -f4)
596
-
597
- if [ "$child_type" = "scalar" ] && [ -n "$child_key" ]; then
598
- echo "$indent- $child_key: $child_value"
599
- fi
600
- is_first_child=false
601
- else
602
- serialize_node "$child_id" "$next_indent" "$type"
603
- fi
604
- done
605
- fi
606
- ;;
607
-
608
- array)
609
- if [ -n "$key" ]; then
610
- echo "$indent$key:"
611
- fi
612
-
613
- if [ -n "$children" ]; then
614
- local IFS=','
615
- # Array children need to be indented
616
- for child_id in $children; do
617
- serialize_node "$child_id" "$((indent_level + 1))" "array"
618
- done
619
- fi
620
- ;;
621
- esac
622
- }
623
-
624
- # ============================================================================
625
- # BACKWARD COMPATIBILITY
626
- # ============================================================================
627
-
628
- yaml_get() {
629
- local file="$1"
630
- local key="$2"
631
-
632
- if [ "$YAML_CURRENT_FILE" != "$file" ]; then
633
- yaml_parse "$file" || return 1
634
- fi
635
-
636
- yaml_query ".$key"
637
- }
638
-
639
- yaml_get_nested() {
640
- local file="$1"
641
- local path="$2"
642
-
643
- if [ "$YAML_CURRENT_FILE" != "$file" ]; then
644
- yaml_parse "$file" || return 1
645
- fi
646
-
647
- yaml_query ".$path"
648
- }
649
-
650
- # Check if key exists (checks if node exists, not if it has a value)
651
- yaml_has_key() {
652
- local file="$1"
653
- local key="$2"
654
-
655
- if [ "$YAML_CURRENT_FILE" != "$file" ]; then
656
- yaml_parse "$file" || return 1
657
- fi
658
-
659
- # Try to find the node (returns empty string on failure, but exit code tells us)
660
- path=$(echo "$key" | sed 's/^\.//')
661
- local current_node="$AST_ROOT_ID"
662
-
663
- local IFS='.'
664
- for segment in $path; do
665
- if echo "$segment" | grep -q '\['; then
666
- local k index
667
- k=$(echo "$segment" | sed 's/\[.*//')
668
- index=$(echo "$segment" | sed 's/.*\[\([0-9]*\)\].*/\1/')
669
-
670
- current_node=$(find_child_by_key "$current_node" "$k" 2>/dev/null)
671
- [ -z "$current_node" ] && return 1
672
-
673
- current_node=$(find_child_by_index "$current_node" "$index" 2>/dev/null)
674
- [ -z "$current_node" ] && return 1
675
- else
676
- current_node=$(find_child_by_key "$current_node" "$segment" 2>/dev/null)
677
- [ -z "$current_node" ] && return 1
678
- fi
679
- done
680
-
681
- # Node exists
682
- return 0
683
- }
684
-
685
- # Get array count (for object arrays)
686
- # Usage: yaml_get_array file.yaml "contents.commands"
687
- # Returns: count of array elements
688
- yaml_get_array() {
689
- local file="$1"
690
- local path="$2"
691
-
692
- if [ "$YAML_CURRENT_FILE" != "$file" ]; then
693
- yaml_parse "$file" || return 1
694
- fi
695
-
696
- # Find the array node
697
- path=$(echo "$path" | sed 's/^\.//')
698
- local current_node="$AST_ROOT_ID"
699
-
700
- local IFS='.'
701
- for segment in $path; do
702
- current_node=$(find_child_by_key "$current_node" "$segment")
703
- [ -z "$current_node" ] && return 1
704
- done
705
-
706
- # Get children count
707
- local children
708
- children=$(get_node_field "$current_node" 6)
709
-
710
- if [ -z "$children" ]; then
711
- echo "0"
712
- else
713
- echo "$children" | tr ',' '\n' | wc -l
714
- fi
715
- }
716
-
717
- # Append scalar item to array
718
- # Usage: yaml_array_append ".path.to.array" "value"
719
- # Returns: node_id of new item
720
- yaml_array_append() {
721
- local path="$1"
722
- local value="$2"
723
-
724
- if [ -z "$AST_FILE" ] || [ ! -f "$AST_FILE" ]; then
725
- echo "Error: No AST loaded. Call yaml_parse first." >&2
726
- return 1
727
- fi
728
-
729
- # Find the array node
730
- path=$(echo "$path" | sed 's/^\.//')
731
- local current_node="$AST_ROOT_ID"
732
-
733
- local IFS='.'
734
- for segment in $path; do
735
- if echo "$segment" | grep -q '\['; then
736
- local key index
737
- key=$(echo "$segment" | sed 's/\[.*//')
738
- index=$(echo "$segment" | sed 's/.*\[\([0-9]*\)\].*/\1/')
739
-
740
- current_node=$(find_child_by_key "$current_node" "$key")
741
- [ -z "$current_node" ] && return 1
742
-
743
- current_node=$(find_child_by_index "$current_node" "$index")
744
- [ -z "$current_node" ] && return 1
745
- else
746
- current_node=$(find_child_by_key "$current_node" "$segment")
747
- [ -z "$current_node" ] && return 1
748
- fi
749
- done
750
-
751
- # Verify it's an array
752
- local node_type
753
- node_type=$(get_node_field "$current_node" 2)
754
-
755
- if [ "$node_type" != "array" ]; then
756
- echo "Error: Path does not point to an array" >&2
757
- return 1
758
- fi
759
-
760
- # Create new scalar node
761
- local new_node
762
- new_node=$(create_node "scalar" "" "$value" "$current_node")
763
-
764
- # Add as child
765
- add_child "$current_node" "$new_node"
766
-
767
- echo "$new_node"
768
- }
769
-
770
- # Delete a node at the specified path
771
- # Usage: yaml_delete ".path.to.node"
772
- # Returns: 0 on success, 1 on failure
773
- yaml_delete() {
774
- local path="$1"
775
-
776
- if [ -z "$path" ]; then
777
- echo "Error: Path is required" >&2
778
- return 1
779
- fi
780
-
781
- # Remove leading dot
782
- path="${path#.}"
783
-
784
- # Navigate to parent and get the key to delete
785
- local parent_path=""
786
- local key_to_delete=""
787
-
788
- # Split path into parent and key
789
- if echo "$path" | grep -q '\.'; then
790
- parent_path=$(echo "$path" | sed 's/\.[^.]*$//')
791
- key_to_delete=$(echo "$path" | sed 's/.*\.//')
792
- else
793
- parent_path=""
794
- key_to_delete="$path"
795
- fi
796
-
797
- # Find parent node
798
- local current_node=0
799
- if [ -n "$parent_path" ]; then
800
- local IFS='.'
801
- for segment in $parent_path; do
802
- if echo "$segment" | grep -q '\['; then
803
- local key index
804
- key=$(echo "$segment" | sed 's/\[.*//')
805
- index=$(echo "$segment" | sed 's/.*\[\([0-9]*\)\].*/\1/')
806
-
807
- current_node=$(find_child_by_key "$current_node" "$key")
808
- [ -z "$current_node" ] && return 1
809
-
810
- current_node=$(find_child_by_index "$current_node" "$index")
811
- [ -z "$current_node" ] && return 1
812
- else
813
- current_node=$(find_child_by_key "$current_node" "$segment")
814
- [ -z "$current_node" ] && return 1
815
- fi
816
- done
817
- fi
818
-
819
- # Find the child node to delete
820
- local node_to_delete
821
- node_to_delete=$(find_child_by_key "$current_node" "$key_to_delete")
822
-
823
- if [ -z "$node_to_delete" ]; then
824
- echo "Error: Node not found: $path" >&2
825
- return 1
826
- fi
827
-
828
- # Remove child from parent's children list
829
- local parent_node
830
- parent_node=$(get_node "$current_node")
831
-
832
- local id type key value parent children
833
- id=$(echo "$parent_node" | cut -d'|' -f1)
834
- type=$(echo "$parent_node" | cut -d'|' -f2)
835
- key=$(echo "$parent_node" | cut -d'|' -f3)
836
- value=$(echo "$parent_node" | cut -d'|' -f4)
837
- parent=$(echo "$parent_node" | cut -d'|' -f5)
838
- children=$(echo "$parent_node" | cut -d'|' -f6)
839
-
840
- # Remove node_to_delete from children list
841
- local new_children=""
842
- local IFS=','
843
- for child_id in $children; do
844
- if [ "$child_id" != "$node_to_delete" ]; then
845
- if [ -z "$new_children" ]; then
846
- new_children="$child_id"
847
- else
848
- new_children="$new_children,$child_id"
849
- fi
850
- fi
851
- done
852
-
853
- # Update parent node
854
- local updated="$id|$type|$key|$value|$parent|$new_children"
855
- _yaml_sed_i "$((current_node + 1))s@.*@$updated@" "$AST_FILE"
856
-
857
- return 0
858
- }
859
-
860
- # Append object to array
861
- # Usage: yaml_array_append_object ".path.to.array"
862
- # Returns: node_id of new object (use yaml_object_set to add fields)
863
- yaml_array_append_object() {
864
- local path="$1"
865
-
866
- if [ -z "$AST_FILE" ] || [ ! -f "$AST_FILE" ]; then
867
- echo "Error: No AST loaded. Call yaml_parse first." >&2
868
- return 1
869
- fi
870
-
871
- # Find the node
872
- path=$(echo "$path" | sed 's/^\.//')
873
- local current_node="$AST_ROOT_ID"
874
-
875
- local IFS='.'
876
- for segment in $path; do
877
- if echo "$segment" | grep -q '\['; then
878
- local key index
879
- key=$(echo "$segment" | sed 's/\[.*//')
880
- index=$(echo "$segment" | sed 's/.*\[\([0-9]*\)\].*/\1/')
881
-
882
- current_node=$(find_child_by_key "$current_node" "$key")
883
- [ -z "$current_node" ] && return 1
884
-
885
- current_node=$(find_child_by_index "$current_node" "$index")
886
- [ -z "$current_node" ] && return 1
887
- else
888
- current_node=$(find_child_by_key "$current_node" "$segment")
889
- [ -z "$current_node" ] && return 1
890
- fi
891
- done
892
-
893
- # Check node type
894
- local node_type
895
- node_type=$(get_node_field "$current_node" 2)
896
-
897
- # If it's a map with no children, convert to array
898
- if [ "$node_type" = "map" ]; then
899
- local children
900
- children=$(get_node_field "$current_node" 6)
901
- if [ -z "$children" ]; then
902
- # Empty map - convert to array
903
- update_node_type "$current_node" "array"
904
- else
905
- echo "Error: Path points to non-empty map, not array" >&2
906
- return 1
907
- fi
908
- elif [ "$node_type" != "array" ]; then
909
- echo "Error: Path does not point to an array" >&2
910
- return 1
911
- fi
912
-
913
- # Create new map node (object)
914
- local new_node
915
- new_node=$(create_node "map" "" "" "$current_node")
916
-
917
- # Add as child
918
- add_child "$current_node" "$new_node"
919
-
920
- echo "$new_node"
921
- }
922
-
923
- # Set field on object (for building objects in arrays)
924
- # Usage: yaml_object_set node_id "field_name" "value"
925
- yaml_object_set() {
926
- local object_node="$1"
927
- local field_name="$2"
928
- local field_value="$3"
929
-
930
- if [ -z "$AST_FILE" ] || [ ! -f "$AST_FILE" ]; then
931
- echo "Error: No AST loaded. Call yaml_parse first." >&2
932
- return 1
933
- fi
934
-
935
- # Create scalar field
936
- local field_node
937
- field_node=$(create_node "scalar" "$field_name" "$field_value" "$object_node")
938
-
939
- # Add as child
940
- add_child "$object_node" "$field_node"
941
-
942
- echo "$field_node"
943
- }
944
-
945
- # ============================================================================
946
- # MAIN
947
- # ============================================================================
948
-
949
- trap cleanup_ast EXIT INT TERM
950
-
951
- # Only run main if script is executed directly (not sourced)
952
- if [ -n "${1:-}" ] && [ "${1:-}" != "-" ] && [ "${BASH_SOURCE[0]}" = "${0}" ]; then
953
- case "$1" in
954
- parse)
955
- yaml_parse "$2"
956
- echo "✓ Parsed $2 ($(get_next_node_id) nodes)"
957
- ;;
958
- query)
959
- yaml_parse "$2"
960
- yaml_query "$3"
961
- ;;
962
- set)
963
- yaml_parse "$2"
964
- yaml_set "$3" "$4"
965
- yaml_write "$2"
966
- echo "✓ Updated $2"
967
- ;;
968
- debug)
969
- yaml_parse "$2"
970
- echo "AST Contents:"
971
- cat "$AST_FILE"
972
- ;;
973
- *)
974
- echo "Usage: $0 {parse|query|set|debug} file.yaml [path] [value]"
975
- echo ""
976
- echo "Examples:"
977
- echo " $0 parse file.yaml"
978
- echo " $0 query file.yaml .name"
979
- echo " $0 query file.yaml .tags[0]"
980
- echo " $0 set file.yaml .version 2.0.0"
981
- echo " $0 debug file.yaml"
982
- exit 1
983
- ;;
984
- esac
985
- fi