@mestreyoda/fabrica 0.1.21 → 0.2.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/README.md +1 -41
- package/dist/index.js +1687 -5491
- package/dist/index.js.map +4 -4
- package/fabrica.manifest.json +0 -1
- package/genesis/scripts/create-task.sh +20 -20
- package/genesis/scripts/genesis-utils.sh +15 -13
- package/genesis/scripts/register-project.sh +20 -20
- package/genesis/scripts/sideband-lib.sh +1 -1
- package/genesis/scripts/triage.sh +19 -19
- package/package.json +1 -3
package/fabrica.manifest.json
CHANGED
|
@@ -4,7 +4,7 @@ set -euo pipefail
|
|
|
4
4
|
# Step 9: Create GitHub issue from session state
|
|
5
5
|
# Input: stdin JSON (complete session state)
|
|
6
6
|
# Output: JSON with issue data to stdout
|
|
7
|
-
# Requires: openclaw CLI +
|
|
7
|
+
# Requires: openclaw CLI + Fabrica plugin (deterministic path),
|
|
8
8
|
# gh CLI authenticated (idempotency checks/fallback),
|
|
9
9
|
# GENESIS_REPO_URL in env or metadata
|
|
10
10
|
|
|
@@ -295,15 +295,15 @@ if [[ -n "$PROJECT_SLUG" ]]; then
|
|
|
295
295
|
echo "WARNING: Requested channel '$REQUESTED_CHANNEL_ID' is not valid for '$PROJECT_SLUG'; using '$PROJECT_CHANNEL_ID' from projects.json." >&2
|
|
296
296
|
fi
|
|
297
297
|
fi
|
|
298
|
-
|
|
299
|
-
if [[ -n "$PROJECT_SLUG" && -n "$PROJECT_CHANNEL_ID" ]] && genesis_openclaw_bin >/dev/null 2>&1 && genesis_openclaw_supports
|
|
300
|
-
|
|
298
|
+
USE_FABRICA_TASKS=false
|
|
299
|
+
if [[ -n "$PROJECT_SLUG" && -n "$PROJECT_CHANNEL_ID" ]] && genesis_openclaw_bin >/dev/null 2>&1 && genesis_openclaw_supports fabrica task; then
|
|
300
|
+
USE_FABRICA_TASKS=true
|
|
301
301
|
fi
|
|
302
302
|
|
|
303
303
|
echo "Target: $OWNER/$REPO" >&2
|
|
304
304
|
|
|
305
305
|
# Lock by repo+session to avoid duplicate issue creation under concurrent runs.
|
|
306
|
-
CREATE_LOCK_DIR="$HOME/.openclaw/workspace/
|
|
306
|
+
CREATE_LOCK_DIR="$HOME/.openclaw/workspace/$FABRICA_DATA_DIR/log"
|
|
307
307
|
LOCK_SAFE_KEY="$(printf '%s_%s_%s' "$OWNER" "$REPO" "$SESSION_ID" | tr -c 'A-Za-z0-9._-' '_')"
|
|
308
308
|
CREATE_LOCK_FILE="$CREATE_LOCK_DIR/create-task-${LOCK_SAFE_KEY}.lock"
|
|
309
309
|
mkdir -p "$CREATE_LOCK_DIR"
|
|
@@ -502,7 +502,7 @@ fi
|
|
|
502
502
|
|
|
503
503
|
BODY="$BODY
|
|
504
504
|
|
|
505
|
-
<!--
|
|
505
|
+
<!-- fabrica-backlog: $BACKLOG_METADATA_JSON -->"
|
|
506
506
|
|
|
507
507
|
# Add risks if present
|
|
508
508
|
RISKS="$(echo "$SPEC" | jq -r '.risks // [] | .[]')"
|
|
@@ -558,8 +558,8 @@ echo "Creating issue: '$TITLE' with labels: $LABELS" >&2
|
|
|
558
558
|
|
|
559
559
|
ISSUE_NUMBER="0"
|
|
560
560
|
ISSUE_URL=""
|
|
561
|
-
if [[ "$
|
|
562
|
-
echo "Creating issue via deterministic
|
|
561
|
+
if [[ "$USE_FABRICA_TASKS" == "true" ]]; then
|
|
562
|
+
echo "Creating issue via deterministic Fabrica task_create..." >&2
|
|
563
563
|
BODY_FILE="$(mktemp)"
|
|
564
564
|
printf '%s\n' "$BODY" > "$BODY_FILE"
|
|
565
565
|
TASK_CREATE_CMD=(create --project "$PROJECT_SLUG" --title "$TITLE" --body-file "$BODY_FILE")
|
|
@@ -570,12 +570,12 @@ if [[ "$USE_DEVCLAW_TASKS" == "true" ]]; then
|
|
|
570
570
|
TASK_CREATE_CMD+=("${FACTORY_CHANGE_FLAG[@]}")
|
|
571
571
|
fi
|
|
572
572
|
TASK_CREATE_JSON="$(
|
|
573
|
-
|
|
573
|
+
genesis_fabrica_task_json \
|
|
574
574
|
"${TASK_CREATE_CMD[@]}" \
|
|
575
575
|
2>>"$GENESIS_LOG"
|
|
576
576
|
)" || {
|
|
577
577
|
rm -f "$BODY_FILE"
|
|
578
|
-
echo "ERROR:
|
|
578
|
+
echo "ERROR: Fabrica task_create failed" >&2
|
|
579
579
|
exit 1
|
|
580
580
|
}
|
|
581
581
|
rm -f "$BODY_FILE"
|
|
@@ -583,7 +583,7 @@ if [[ "$USE_DEVCLAW_TASKS" == "true" ]]; then
|
|
|
583
583
|
ISSUE_NUMBER="$(echo "$TASK_CREATE_JSON" | jq -r '.issue.id // 0' 2>/dev/null || echo 0)"
|
|
584
584
|
ISSUE_URL="$(echo "$TASK_CREATE_JSON" | jq -r '.issue.url // ""' 2>/dev/null || echo "")"
|
|
585
585
|
if [[ "$ISSUE_NUMBER" != "0" && -n "$EXTRA_LABELS" ]]; then
|
|
586
|
-
echo "Applying extra labels via deterministic
|
|
586
|
+
echo "Applying extra labels via deterministic Fabrica task labels: $EXTRA_LABELS" >&2
|
|
587
587
|
TASK_LABELS_CMD=(labels --project "$PROJECT_SLUG" --issue-id "$ISSUE_NUMBER" --add "$EXTRA_LABELS")
|
|
588
588
|
if [[ -n "$PROJECT_CHANNEL_ID" ]]; then
|
|
589
589
|
TASK_LABELS_CMD+=(--channel-id "$PROJECT_CHANNEL_ID")
|
|
@@ -591,15 +591,15 @@ if [[ "$USE_DEVCLAW_TASKS" == "true" ]]; then
|
|
|
591
591
|
if [[ "${#FACTORY_CHANGE_FLAG[@]}" -gt 0 ]]; then
|
|
592
592
|
TASK_LABELS_CMD+=("${FACTORY_CHANGE_FLAG[@]}")
|
|
593
593
|
fi
|
|
594
|
-
|
|
594
|
+
genesis_fabrica_task_json \
|
|
595
595
|
"${TASK_LABELS_CMD[@]}" \
|
|
596
596
|
>/dev/null 2>>"$GENESIS_LOG" || {
|
|
597
|
-
echo "ERROR:
|
|
597
|
+
echo "ERROR: Fabrica task labels failed" >&2
|
|
598
598
|
exit 1
|
|
599
599
|
}
|
|
600
600
|
fi
|
|
601
601
|
else
|
|
602
|
-
echo "
|
|
602
|
+
echo "Fabrica deterministic mode unavailable (missing project slug/channel or openclaw bin); falling back to gh issue create." >&2
|
|
603
603
|
gh label create "$DELIVERY_LABEL" --repo "$OWNER/$REPO" --color 5319E7 --force >/dev/null 2>>"$GENESIS_LOG" || true
|
|
604
604
|
gh label create "$BACKLOG_LABEL" --repo "$OWNER/$REPO" --color BFD4F2 --force >/dev/null 2>>"$GENESIS_LOG" || true
|
|
605
605
|
if [[ "$TYPE" == "research" ]]; then
|
|
@@ -654,15 +654,15 @@ $QA_SCRIPT
|
|
|
654
654
|
**Gates:** $(echo "$QA_CONTRACT" | jq -r '.gates // [] | join(", ")')
|
|
655
655
|
**Coverage threshold:** $(echo "$QA_CONTRACT" | jq -r '.coverage_threshold // 80')%"
|
|
656
656
|
|
|
657
|
-
if [[ "$
|
|
658
|
-
echo "QA comment via
|
|
657
|
+
if [[ "$USE_FABRICA_TASKS" == "true" ]]; then
|
|
658
|
+
echo "QA comment via Fabrica task_comment..." >&2
|
|
659
659
|
QA_COMMENT_FILE="$(mktemp)"
|
|
660
660
|
printf '%s\n' "$QA_COMMENT" > "$QA_COMMENT_FILE"
|
|
661
661
|
TASK_COMMENT_QA_CMD=(comment --project "$PROJECT_SLUG" --issue-id "$ISSUE_NUMBER" --body-file "$QA_COMMENT_FILE")
|
|
662
662
|
if [[ -n "$PROJECT_CHANNEL_ID" ]]; then
|
|
663
663
|
TASK_COMMENT_QA_CMD+=(--channel-id "$PROJECT_CHANNEL_ID")
|
|
664
664
|
fi
|
|
665
|
-
|
|
665
|
+
genesis_fabrica_task_json \
|
|
666
666
|
"${TASK_COMMENT_QA_CMD[@]}" \
|
|
667
667
|
>/dev/null 2>>"$GENESIS_LOG" || echo "WARNING: Failed to attach QA comment via task_comment" >&2
|
|
668
668
|
rm -f "$QA_COMMENT_FILE"
|
|
@@ -731,15 +731,15 @@ $MAP_LINES
|
|
|
731
731
|
$IMPACT_LINES
|
|
732
732
|
$LIST_BLOCKS"
|
|
733
733
|
|
|
734
|
-
if [[ "$
|
|
735
|
-
echo "Map/impact comment via
|
|
734
|
+
if [[ "$USE_FABRICA_TASKS" == "true" ]]; then
|
|
735
|
+
echo "Map/impact comment via Fabrica task_comment..." >&2
|
|
736
736
|
IMPACT_COMMENT_FILE="$(mktemp)"
|
|
737
737
|
printf '%s\n' "$IMPACT_COMMENT" > "$IMPACT_COMMENT_FILE"
|
|
738
738
|
TASK_COMMENT_IMPACT_CMD=(comment --project "$PROJECT_SLUG" --issue-id "$ISSUE_NUMBER" --body-file "$IMPACT_COMMENT_FILE")
|
|
739
739
|
if [[ -n "$PROJECT_CHANNEL_ID" ]]; then
|
|
740
740
|
TASK_COMMENT_IMPACT_CMD+=(--channel-id "$PROJECT_CHANNEL_ID")
|
|
741
741
|
fi
|
|
742
|
-
|
|
742
|
+
genesis_fabrica_task_json \
|
|
743
743
|
"${TASK_COMMENT_IMPACT_CMD[@]}" \
|
|
744
744
|
>/dev/null 2>>"$GENESIS_LOG" || echo "WARNING: Failed to attach impact/map comment via task_comment" >&2
|
|
745
745
|
rm -f "$IMPACT_COMMENT_FILE"
|
|
@@ -16,6 +16,8 @@ _GENESIS_UTILS_LOADED=1
|
|
|
16
16
|
|
|
17
17
|
set -euo pipefail
|
|
18
18
|
|
|
19
|
+
FABRICA_DATA_DIR="fabrica"
|
|
20
|
+
|
|
19
21
|
# === Category A: Stateless Parsing & Validation ===
|
|
20
22
|
|
|
21
23
|
genesis_trim() {
|
|
@@ -135,7 +137,7 @@ genesis_extract_answer_value() {
|
|
|
135
137
|
|
|
136
138
|
genesis_find_project_slug_by_repo() {
|
|
137
139
|
local raw_repo="${1:-}"
|
|
138
|
-
local projects_file="${2:-$HOME/.openclaw/workspace/
|
|
140
|
+
local projects_file="${2:-$HOME/.openclaw/workspace/$FABRICA_DATA_DIR/projects.json}"
|
|
139
141
|
local wanted line slug remote remote_key
|
|
140
142
|
local -a matches=()
|
|
141
143
|
wanted="$(genesis_repo_key "$raw_repo" 2>/dev/null || true)"
|
|
@@ -161,7 +163,7 @@ genesis_find_project_slug_by_repo() {
|
|
|
161
163
|
|
|
162
164
|
genesis_project_primary_channel_id() {
|
|
163
165
|
local slug="${1:-}"
|
|
164
|
-
local projects_file="${2:-$HOME/.openclaw/workspace/
|
|
166
|
+
local projects_file="${2:-$HOME/.openclaw/workspace/$FABRICA_DATA_DIR/projects.json}"
|
|
165
167
|
[[ -n "$slug" ]] || return 1
|
|
166
168
|
[[ -f "$projects_file" ]] || return 1
|
|
167
169
|
jq -r --arg slug "$slug" '.projects[$slug].channels // [] | map(select((.channelId // "") != "")) | .[0].channelId // empty' "$projects_file" 2>/dev/null
|
|
@@ -169,14 +171,14 @@ genesis_project_primary_channel_id() {
|
|
|
169
171
|
|
|
170
172
|
genesis_project_exists() {
|
|
171
173
|
local slug="${1:-}"
|
|
172
|
-
local projects_file="${2:-$HOME/.openclaw/workspace/
|
|
174
|
+
local projects_file="${2:-$HOME/.openclaw/workspace/$FABRICA_DATA_DIR/projects.json}"
|
|
173
175
|
[[ -n "$slug" && -f "$projects_file" ]] || return 1
|
|
174
176
|
jq -e --arg slug "$slug" '.projects[$slug] != null' "$projects_file" >/dev/null 2>&1
|
|
175
177
|
}
|
|
176
178
|
|
|
177
179
|
genesis_project_remote() {
|
|
178
180
|
local slug="${1:-}"
|
|
179
|
-
local projects_file="${2:-$HOME/.openclaw/workspace/
|
|
181
|
+
local projects_file="${2:-$HOME/.openclaw/workspace/$FABRICA_DATA_DIR/projects.json}"
|
|
180
182
|
[[ -n "$slug" && -f "$projects_file" ]] || return 1
|
|
181
183
|
jq -r --arg slug "$slug" '.projects[$slug].repoRemote // .projects[$slug].remote // .projects[$slug].remoteUrl // empty' "$projects_file" 2>/dev/null
|
|
182
184
|
}
|
|
@@ -184,7 +186,7 @@ genesis_project_remote() {
|
|
|
184
186
|
genesis_project_channel_id() {
|
|
185
187
|
local slug="${1:-}"
|
|
186
188
|
local requested_channel_id="${2:-}"
|
|
187
|
-
local projects_file="${3:-$HOME/.openclaw/workspace/
|
|
189
|
+
local projects_file="${3:-$HOME/.openclaw/workspace/$FABRICA_DATA_DIR/projects.json}"
|
|
188
190
|
local requested
|
|
189
191
|
|
|
190
192
|
requested="$(genesis_trim "$requested_channel_id")"
|
|
@@ -212,7 +214,7 @@ genesis_project_channel_id() {
|
|
|
212
214
|
|
|
213
215
|
genesis_project_resolve_ref() {
|
|
214
216
|
local ref="${1:-}"
|
|
215
|
-
local projects_file="${2:-$HOME/.openclaw/workspace/
|
|
217
|
+
local projects_file="${2:-$HOME/.openclaw/workspace/$FABRICA_DATA_DIR/projects.json}"
|
|
216
218
|
local normalized
|
|
217
219
|
|
|
218
220
|
normalized="$(genesis_trim "$ref")"
|
|
@@ -265,14 +267,14 @@ genesis_project_resolve_ref() {
|
|
|
265
267
|
|
|
266
268
|
genesis_project_kind() {
|
|
267
269
|
local slug="${1:-}"
|
|
268
|
-
local projects_file="${2:-$HOME/.openclaw/workspace/
|
|
270
|
+
local projects_file="${2:-$HOME/.openclaw/workspace/$FABRICA_DATA_DIR/projects.json}"
|
|
269
271
|
[[ -n "$slug" && -f "$projects_file" ]] || return 1
|
|
270
272
|
jq -r --arg slug "$slug" '.projects[$slug].projectKind // "implementation"' "$projects_file" 2>/dev/null
|
|
271
273
|
}
|
|
272
274
|
|
|
273
275
|
genesis_project_archived() {
|
|
274
276
|
local slug="${1:-}"
|
|
275
|
-
local projects_file="${2:-$HOME/.openclaw/workspace/
|
|
277
|
+
local projects_file="${2:-$HOME/.openclaw/workspace/$FABRICA_DATA_DIR/projects.json}"
|
|
276
278
|
[[ -n "$slug" && -f "$projects_file" ]] || return 1
|
|
277
279
|
jq -r --arg slug "$slug" '
|
|
278
280
|
(
|
|
@@ -285,7 +287,7 @@ genesis_project_archived() {
|
|
|
285
287
|
|
|
286
288
|
genesis_project_default_notify_channel() {
|
|
287
289
|
local slug="${1:-}"
|
|
288
|
-
local projects_file="${2:-$HOME/.openclaw/workspace/
|
|
290
|
+
local projects_file="${2:-$HOME/.openclaw/workspace/$FABRICA_DATA_DIR/projects.json}"
|
|
289
291
|
[[ -n "$slug" && -f "$projects_file" ]] || return 1
|
|
290
292
|
jq -r --arg slug "$slug" '.projects[$slug].defaultNotifyChannel // empty' "$projects_file" 2>/dev/null
|
|
291
293
|
}
|
|
@@ -333,7 +335,7 @@ genesis_repo_target_candidate() {
|
|
|
333
335
|
|
|
334
336
|
genesis_resolve_canonical_target() {
|
|
335
337
|
local input_json="${1:-}"
|
|
336
|
-
local projects_file="${2:-$HOME/.openclaw/workspace/
|
|
338
|
+
local projects_file="${2:-$HOME/.openclaw/workspace/$FABRICA_DATA_DIR/projects.json}"
|
|
337
339
|
local explicit_repo candidate_slug repo_target_raw repo_target_candidate
|
|
338
340
|
local resolved_repo="" resolved_slug="" resolved_name="" source=""
|
|
339
341
|
local project_ref resolved_remote owner_repo
|
|
@@ -560,21 +562,21 @@ genesis_openclaw_supports() {
|
|
|
560
562
|
return 0
|
|
561
563
|
}
|
|
562
564
|
|
|
563
|
-
|
|
565
|
+
genesis_fabrica_task_json() {
|
|
564
566
|
local attempts delay try status
|
|
565
567
|
attempts="$(genesis_openclaw_retries)"
|
|
566
568
|
delay="$(genesis_openclaw_retry_delay_sec)"
|
|
567
569
|
try=1
|
|
568
570
|
|
|
569
571
|
while true; do
|
|
570
|
-
if genesis_openclaw_exec
|
|
572
|
+
if genesis_openclaw_exec fabrica task "$@" --json; then
|
|
571
573
|
return 0
|
|
572
574
|
fi
|
|
573
575
|
status=$?
|
|
574
576
|
if [[ "$try" -ge "$attempts" ]]; then
|
|
575
577
|
return "$status"
|
|
576
578
|
fi
|
|
577
|
-
echo "WARN:
|
|
579
|
+
echo "WARN: Fabrica task call failed (attempt $try/$attempts, exit=$status). Retrying in ${delay}s..." >&2
|
|
578
580
|
sleep "$delay"
|
|
579
581
|
try=$((try + 1))
|
|
580
582
|
done
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
# Step: Register scaffolded project in
|
|
4
|
+
# Step: Register scaffolded project in Fabrica
|
|
5
5
|
# Input: stdin JSON (from scaffold.stdout)
|
|
6
6
|
# Output: JSON with registration data + sideband file
|
|
7
7
|
# Creates: projects.json entry, workflow.yaml, role prompts, repository labels
|
|
@@ -66,14 +66,14 @@ REPO_REMOTE="https://github.com/$OWNER_REPO.git"
|
|
|
66
66
|
|
|
67
67
|
echo "Registering project: $SLUG (stack=$STACK)" >&2
|
|
68
68
|
|
|
69
|
-
PROJECTS_JSON="$WORKSPACE/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if genesis_openclaw_supports
|
|
73
|
-
|
|
69
|
+
PROJECTS_JSON="$WORKSPACE/fabrica/projects.json"
|
|
70
|
+
USE_FABRICA_PROJECT_REGISTER_CLI=false
|
|
71
|
+
USE_FABRICA_PROJECT_LABELS_CLI=false
|
|
72
|
+
if genesis_openclaw_supports fabrica project register; then
|
|
73
|
+
USE_FABRICA_PROJECT_REGISTER_CLI=true
|
|
74
74
|
fi
|
|
75
|
-
if genesis_openclaw_supports
|
|
76
|
-
|
|
75
|
+
if genesis_openclaw_supports fabrica project ensure-labels; then
|
|
76
|
+
USE_FABRICA_PROJECT_LABELS_CLI=true
|
|
77
77
|
fi
|
|
78
78
|
|
|
79
79
|
# Shared-channel policy guard: avoid silent collisions
|
|
@@ -114,9 +114,9 @@ if jq -e --arg slug "$SLUG" '.projects[$slug]' "$PROJECTS_JSON" &>/dev/null; the
|
|
|
114
114
|
' "$PROJECTS_JSON" 2>/dev/null || echo "false")"
|
|
115
115
|
if [[ "$PROJECT_HAS_CHANNEL" != "true" ]]; then
|
|
116
116
|
CHANNEL_LINKED=false
|
|
117
|
-
if genesis_openclaw_supports
|
|
117
|
+
if genesis_openclaw_supports fabrica channel register; then
|
|
118
118
|
echo "Linking channelId $TELEGRAM_CHAT to existing project $SLUG..." >&2
|
|
119
|
-
if genesis_openclaw_exec
|
|
119
|
+
if genesis_openclaw_exec fabrica channel register \
|
|
120
120
|
--project "$SLUG" \
|
|
121
121
|
--channel-id "$TELEGRAM_CHAT" \
|
|
122
122
|
--type "telegram" >/dev/null 2>>"$GENESIS_LOG"; then
|
|
@@ -154,9 +154,9 @@ else
|
|
|
154
154
|
if [[ -z "$TELEGRAM_CHAT" ]]; then
|
|
155
155
|
echo "No TELEGRAM_CHAT_ID available — skipping registration until a channel is linked." >&2
|
|
156
156
|
PROJECT_REGISTRATION_PENDING="missing_channel"
|
|
157
|
-
elif [[ "$
|
|
158
|
-
echo "Registering project via deterministic
|
|
159
|
-
genesis_openclaw_exec
|
|
157
|
+
elif [[ "$USE_FABRICA_PROJECT_REGISTER_CLI" == "true" ]]; then
|
|
158
|
+
echo "Registering project via deterministic Fabrica project_register..." >&2
|
|
159
|
+
genesis_openclaw_exec fabrica project register \
|
|
160
160
|
--name "$SLUG" \
|
|
161
161
|
--repo "$REPO_LOCAL" \
|
|
162
162
|
--base-branch "main" \
|
|
@@ -168,7 +168,7 @@ else
|
|
|
168
168
|
--json >/dev/null 2>>"$GENESIS_LOG"
|
|
169
169
|
PROJECT_REGISTERED=true
|
|
170
170
|
else
|
|
171
|
-
echo "
|
|
171
|
+
echo "Fabrica project CLI unavailable/incompatible — using local fallback registration." >&2
|
|
172
172
|
echo "Adding $SLUG to projects.json..." >&2
|
|
173
173
|
|
|
174
174
|
# Build the new project entry
|
|
@@ -284,7 +284,7 @@ if [[ "$PROJECT_REGISTERED" != "true" ]]; then
|
|
|
284
284
|
fi
|
|
285
285
|
|
|
286
286
|
# --- Create workflow.yaml ---
|
|
287
|
-
WORKFLOW_DIR="$WORKSPACE/
|
|
287
|
+
WORKFLOW_DIR="$WORKSPACE/fabrica/projects/$SLUG"
|
|
288
288
|
mkdir -p "$WORKFLOW_DIR"
|
|
289
289
|
|
|
290
290
|
if [[ -f "$WORKFLOW_DIR/workflow.yaml" ]]; then
|
|
@@ -310,8 +310,8 @@ fi
|
|
|
310
310
|
# --- Copy role prompts ---
|
|
311
311
|
ROLES_SRC="$WORKSPACE/projects/roles/devclaw-automation"
|
|
312
312
|
ROLES_DEFAULT="$WORKSPACE/projects/roles/default"
|
|
313
|
-
ROLES_WORKSPACE_DEFAULT="$WORKSPACE/
|
|
314
|
-
ROLES_DST="$WORKSPACE/
|
|
313
|
+
ROLES_WORKSPACE_DEFAULT="$WORKSPACE/fabrica/prompts"
|
|
314
|
+
ROLES_DST="$WORKSPACE/fabrica/projects/$SLUG/prompts"
|
|
315
315
|
mkdir -p "$ROLES_DST"
|
|
316
316
|
|
|
317
317
|
for ROLE in developer reviewer tester; do
|
|
@@ -350,14 +350,14 @@ if [[ -f "$LABELS_JSON" ]]; then
|
|
|
350
350
|
fi
|
|
351
351
|
|
|
352
352
|
echo "Ensuring labels in $OWNER_REPO..." >&2
|
|
353
|
-
if [[ "$
|
|
354
|
-
ENSURE_LABELS_CMD=(
|
|
353
|
+
if [[ "$USE_FABRICA_PROJECT_LABELS_CLI" == "true" ]]; then
|
|
354
|
+
ENSURE_LABELS_CMD=(fabrica project ensure-labels --project "$SLUG" --labels-file "$LABELS_JSON")
|
|
355
355
|
if [[ -n "$TELEGRAM_CHAT" ]]; then
|
|
356
356
|
ENSURE_LABELS_CMD+=(--notify-channel-id "$TELEGRAM_CHAT")
|
|
357
357
|
fi
|
|
358
358
|
genesis_openclaw_exec "${ENSURE_LABELS_CMD[@]}" --json >/dev/null 2>>"$GENESIS_LOG"
|
|
359
359
|
else
|
|
360
|
-
echo "
|
|
360
|
+
echo "Fabrica ensure-labels CLI unavailable/incompatible — falling back to gh for custom repo labels." >&2
|
|
361
361
|
LABEL_COUNT=0
|
|
362
362
|
LABEL_TOTAL="$(jq 'length' "$LABELS_JSON")"
|
|
363
363
|
while IFS= read -r label_line; do
|
|
@@ -20,7 +20,7 @@ source "$_SIDEBAND_LIB_DIR/genesis-utils.sh"
|
|
|
20
20
|
# === Category C: Sideband IPC Protocol (Secure Envelope Exchange) ===
|
|
21
21
|
|
|
22
22
|
genesis_sideband_dir() {
|
|
23
|
-
local dir="${GENESIS_SIDEBAND_DIR:-$HOME/.openclaw/workspace/
|
|
23
|
+
local dir="${GENESIS_SIDEBAND_DIR:-$HOME/.openclaw/workspace/fabrica/sideband}"
|
|
24
24
|
|
|
25
25
|
if [[ -L "$dir" ]]; then
|
|
26
26
|
echo "ERROR: Sideband dir must not be a symlink: $dir" >&2
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
3
|
|
|
4
|
-
# Step 10: Triage — prioritize and dispatch issue to
|
|
4
|
+
# Step 10: Triage — prioritize and dispatch issue to Fabrica pipeline
|
|
5
5
|
# Input: stdin JSON (issue data + spec)
|
|
6
6
|
# Output: JSON with triage decision to stdout
|
|
7
|
-
# Applies labels + workflow transitions to dispatch
|
|
7
|
+
# Applies labels + workflow transitions to dispatch Fabrica safely
|
|
8
8
|
|
|
9
9
|
GENESIS_LOG="${GENESIS_LOG:-$HOME/.openclaw/workspace/logs/genesis.log}"
|
|
10
10
|
mkdir -p "$(dirname "$GENESIS_LOG")"
|
|
@@ -173,9 +173,9 @@ if [[ -n "$PROJECT_SLUG" ]]; then
|
|
|
173
173
|
echo "WARNING: Requested channel '$REQUESTED_CHANNEL_ID' is not valid for '$PROJECT_SLUG'; using '$PROJECT_CHANNEL_ID' from projects.json." >&2
|
|
174
174
|
fi
|
|
175
175
|
fi
|
|
176
|
-
|
|
177
|
-
if [[ -n "$PROJECT_SLUG" && -n "$PROJECT_CHANNEL_ID" ]] && genesis_openclaw_bin >/dev/null 2>&1 && genesis_openclaw_supports
|
|
178
|
-
|
|
176
|
+
USE_FABRICA_TASKS=false
|
|
177
|
+
if [[ -n "$PROJECT_SLUG" && -n "$PROJECT_CHANNEL_ID" ]] && genesis_openclaw_bin >/dev/null 2>&1 && genesis_openclaw_supports fabrica task; then
|
|
178
|
+
USE_FABRICA_TASKS=true
|
|
179
179
|
fi
|
|
180
180
|
|
|
181
181
|
if [[ ! -f "$TRIAGE_MATRIX" ]]; then
|
|
@@ -362,7 +362,7 @@ fi
|
|
|
362
362
|
|
|
363
363
|
echo "Applying labels to issue #$ISSUE_NUMBER: $ALL_LABELS" >&2
|
|
364
364
|
|
|
365
|
-
if [[ "$
|
|
365
|
+
if [[ "$USE_FABRICA_TASKS" == "true" ]]; then
|
|
366
366
|
TASK_LABELS_CMD=(labels --project "$PROJECT_SLUG" --issue-id "$ISSUE_NUMBER" --add "$ALL_LABELS")
|
|
367
367
|
if [[ -n "$PROJECT_CHANNEL_ID" ]]; then
|
|
368
368
|
TASK_LABELS_CMD+=(--channel-id "$PROJECT_CHANNEL_ID")
|
|
@@ -370,7 +370,7 @@ if [[ "$USE_DEVCLAW_TASKS" == "true" ]]; then
|
|
|
370
370
|
if [[ "$FACTORY_CHANGE" == "true" ]]; then
|
|
371
371
|
TASK_LABELS_CMD+=(--factory-change)
|
|
372
372
|
fi
|
|
373
|
-
if !
|
|
373
|
+
if ! genesis_fabrica_task_json "${TASK_LABELS_CMD[@]}" >/dev/null 2>>"$GENESIS_LOG"; then
|
|
374
374
|
READY_FOR_DISPATCH=false
|
|
375
375
|
TRIAGE_ERRORS+=("apply_labels_failed")
|
|
376
376
|
fi
|
|
@@ -391,14 +391,14 @@ if [[ "$READY_FOR_DISPATCH" != "true" ]]; then
|
|
|
391
391
|
The issue stayed in **Planning** because required fields are missing:
|
|
392
392
|
$(printf '%s\n' "${TRIAGE_ERRORS[@]}" | sed 's/^/- /')
|
|
393
393
|
"
|
|
394
|
-
if [[ "$
|
|
394
|
+
if [[ "$USE_FABRICA_TASKS" == "true" ]]; then
|
|
395
395
|
DOR_COMMENT_FILE="$(mktemp)"
|
|
396
396
|
printf '%s\n' "$DOR_COMMENT" > "$DOR_COMMENT_FILE"
|
|
397
397
|
TASK_DOR_CMD=(comment --project "$PROJECT_SLUG" --issue-id "$ISSUE_NUMBER" --body-file "$DOR_COMMENT_FILE")
|
|
398
398
|
if [[ -n "$PROJECT_CHANNEL_ID" ]]; then
|
|
399
399
|
TASK_DOR_CMD+=(--channel-id "$PROJECT_CHANNEL_ID")
|
|
400
400
|
fi
|
|
401
|
-
|
|
401
|
+
genesis_fabrica_task_json "${TASK_DOR_CMD[@]}" >/dev/null 2>>"$GENESIS_LOG" || true
|
|
402
402
|
rm -f "$DOR_COMMENT_FILE"
|
|
403
403
|
else
|
|
404
404
|
gh issue comment "$ISSUE_NUMBER" --repo "$OWNER_REPO" --body "$DOR_COMMENT" >/dev/null 2>&1 || true
|
|
@@ -419,8 +419,8 @@ if [[ "$TARGET_QUEUE_LABEL" == "To Research" && "$LEVEL" == "medior" ]]; then
|
|
|
419
419
|
LEVEL="junior"
|
|
420
420
|
fi
|
|
421
421
|
|
|
422
|
-
if [[ "$READY_FOR_DISPATCH" == "true" && "$TARGET_QUEUE_LABEL" == "To Do" && "$
|
|
423
|
-
echo "Dispatching via deterministic
|
|
422
|
+
if [[ "$READY_FOR_DISPATCH" == "true" && "$TARGET_QUEUE_LABEL" == "To Do" && "$USE_FABRICA_TASKS" == "true" ]]; then
|
|
423
|
+
echo "Dispatching via deterministic Fabrica task_start (Planning → To Do, level=$LEVEL)..." >&2
|
|
424
424
|
TASK_START_CMD=(start --project "$PROJECT_SLUG" --issue-id "$ISSUE_NUMBER" --level "$LEVEL")
|
|
425
425
|
if [[ -n "$PROJECT_CHANNEL_ID" ]]; then
|
|
426
426
|
TASK_START_CMD+=(--channel-id "$PROJECT_CHANNEL_ID")
|
|
@@ -428,7 +428,7 @@ if [[ "$READY_FOR_DISPATCH" == "true" && "$TARGET_QUEUE_LABEL" == "To Do" && "$U
|
|
|
428
428
|
if [[ "$FACTORY_CHANGE" == "true" ]]; then
|
|
429
429
|
TASK_START_CMD+=(--factory-change)
|
|
430
430
|
fi
|
|
431
|
-
if !
|
|
431
|
+
if ! genesis_fabrica_task_json "${TASK_START_CMD[@]}" >/dev/null 2>>"$GENESIS_LOG"; then
|
|
432
432
|
READY_FOR_DISPATCH=false
|
|
433
433
|
TRIAGE_ERRORS+=("task_start_failed")
|
|
434
434
|
else
|
|
@@ -436,8 +436,8 @@ if [[ "$READY_FOR_DISPATCH" == "true" && "$TARGET_QUEUE_LABEL" == "To Do" && "$U
|
|
|
436
436
|
LEVEL_LABEL_APPLIED=true
|
|
437
437
|
DISPATCH_STAGE_APPLIED=true
|
|
438
438
|
fi
|
|
439
|
-
elif [[ "$READY_FOR_DISPATCH" == "true" && "$
|
|
440
|
-
echo "Routing deterministically via
|
|
439
|
+
elif [[ "$READY_FOR_DISPATCH" == "true" && "$USE_FABRICA_TASKS" == "true" ]]; then
|
|
440
|
+
echo "Routing deterministically via Fabrica task route (Planning → $TARGET_QUEUE_LABEL, level=$LEVEL)..." >&2
|
|
441
441
|
TASK_ROUTE_CMD=(route --project "$PROJECT_SLUG" --issue-id "$ISSUE_NUMBER" --to-label "$TARGET_QUEUE_LABEL")
|
|
442
442
|
if [[ -n "$PROJECT_CHANNEL_ID" ]]; then
|
|
443
443
|
TASK_ROUTE_CMD+=(--channel-id "$PROJECT_CHANNEL_ID")
|
|
@@ -448,7 +448,7 @@ elif [[ "$READY_FOR_DISPATCH" == "true" && "$USE_DEVCLAW_TASKS" == "true" ]]; th
|
|
|
448
448
|
if [[ "$FACTORY_CHANGE" == "true" ]]; then
|
|
449
449
|
TASK_ROUTE_CMD+=(--factory-change)
|
|
450
450
|
fi
|
|
451
|
-
if !
|
|
451
|
+
if ! genesis_fabrica_task_json "${TASK_ROUTE_CMD[@]}" >/dev/null 2>>"$GENESIS_LOG"; then
|
|
452
452
|
READY_FOR_DISPATCH=false
|
|
453
453
|
TRIAGE_ERRORS+=("task_route_failed")
|
|
454
454
|
else
|
|
@@ -502,7 +502,7 @@ else
|
|
|
502
502
|
fi
|
|
503
503
|
|
|
504
504
|
if [[ "$READY_FOR_DISPATCH" == "true" ]]; then
|
|
505
|
-
if [[ "$
|
|
505
|
+
if [[ "$USE_FABRICA_TASKS" == "true" ]]; then
|
|
506
506
|
TASK_CLEAR_NEEDS_HUMAN_CMD=(labels --project "$PROJECT_SLUG" --issue-id "$ISSUE_NUMBER" --remove "needs-human")
|
|
507
507
|
if [[ -n "$PROJECT_CHANNEL_ID" ]]; then
|
|
508
508
|
TASK_CLEAR_NEEDS_HUMAN_CMD+=(--channel-id "$PROJECT_CHANNEL_ID")
|
|
@@ -510,14 +510,14 @@ if [[ "$READY_FOR_DISPATCH" == "true" ]]; then
|
|
|
510
510
|
if [[ "$FACTORY_CHANGE" == "true" ]]; then
|
|
511
511
|
TASK_CLEAR_NEEDS_HUMAN_CMD+=(--factory-change)
|
|
512
512
|
fi
|
|
513
|
-
|
|
513
|
+
genesis_fabrica_task_json "${TASK_CLEAR_NEEDS_HUMAN_CMD[@]}" >/dev/null 2>>"$GENESIS_LOG" || true
|
|
514
514
|
else
|
|
515
515
|
gh issue edit "$ISSUE_NUMBER" --repo "$OWNER_REPO" --remove-label "needs-human" >/dev/null 2>&1 || true
|
|
516
516
|
fi
|
|
517
517
|
echo "Issue #$ISSUE_NUMBER dispatched to $TARGET_QUEUE_LABEL (level=$LEVEL)" >&2
|
|
518
518
|
else
|
|
519
519
|
echo "Issue #$ISSUE_NUMBER NOT dispatched; triage failed closed: ${TRIAGE_ERRORS[*]}" >&2
|
|
520
|
-
if [[ "$
|
|
520
|
+
if [[ "$USE_FABRICA_TASKS" == "true" ]]; then
|
|
521
521
|
TASK_SET_NEEDS_HUMAN_CMD=(labels --project "$PROJECT_SLUG" --issue-id "$ISSUE_NUMBER" --add "needs-human")
|
|
522
522
|
if [[ -n "$PROJECT_CHANNEL_ID" ]]; then
|
|
523
523
|
TASK_SET_NEEDS_HUMAN_CMD+=(--channel-id "$PROJECT_CHANNEL_ID")
|
|
@@ -525,7 +525,7 @@ else
|
|
|
525
525
|
if [[ "$FACTORY_CHANGE" == "true" ]]; then
|
|
526
526
|
TASK_SET_NEEDS_HUMAN_CMD+=(--factory-change)
|
|
527
527
|
fi
|
|
528
|
-
if !
|
|
528
|
+
if ! genesis_fabrica_task_json "${TASK_SET_NEEDS_HUMAN_CMD[@]}" >/dev/null 2>>"$GENESIS_LOG"; then
|
|
529
529
|
TRIAGE_ERRORS+=("mark_needs_human_failed")
|
|
530
530
|
fi
|
|
531
531
|
elif ! gh issue edit "$ISSUE_NUMBER" --repo "$OWNER_REPO" --add-label "needs-human" >/dev/null 2>&1; then
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mestreyoda/fabrica",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Autonomous software engineering pipeline for OpenClaw. Turns ideas into deployed code via intake, dispatch, review, test, and merge.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,8 +21,6 @@
|
|
|
21
21
|
"LICENSE"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@octokit/app": "^16.1.2",
|
|
25
|
-
"@octokit/webhooks": "^14.2.0",
|
|
26
24
|
"@opentelemetry/api": "^1.9.0",
|
|
27
25
|
"@opentelemetry/auto-instrumentations-node": "^0.71.0",
|
|
28
26
|
"@opentelemetry/exporter-trace-otlp-http": "^0.213.0",
|