@jenga-ai/agent 3.6.0 → 4.0.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/lib/generate-skill-allow-list.js +42 -5
- package/lib/skill-allow-list.json +1 -1
- package/package.json +1 -2
- package/project/app/api/lib/resolve-project-root.js +1 -1
- package/project/app/package.json +4 -0
- package/project/app/ui/dist/assets/index-BADc5mmH.css +1 -0
- package/project/app/ui/dist/assets/index-C3oiuli_.js +104 -0
- package/project/app/ui/dist/index.html +2 -2
- package/scripts/acquire-concurrency-slot.sh +34 -4
- package/scripts/apply-j-prefix.sh +1 -1
- package/scripts/delete-bare-skill-dirs.sh +1 -2
- package/scripts/idea_manager.sh +16 -1
- package/scripts/release-concurrency-slot.sh +34 -4
- package/scripts/render-ranked-list.sh +270 -0
- package/scripts/repoint-dead-bare-path-prose.py +81 -0
- package/scripts/rewrite-stale-skill-preambles.py +188 -0
- package/scripts/strip-polyfill-frontmatter.py +166 -0
- package/scripts/todo_manager.sh +16 -1
- package/scripts/validate-typed-object.sh +750 -0
- package/skills/j-brainstorm/SKILL.md +3 -4
- package/skills/j-btw/SKILL.md +3 -4
- package/skills/j-clearify/SKILL.md +3 -4
- package/skills/j-close-story/SKILL.md +11 -12
- package/skills/j-close-story/scripts/check-story-closeable.sh +11 -4
- package/skills/j-commit/SKILL.md +3 -4
- package/skills/j-continue/SKILL.md +5 -6
- package/skills/j-deep-dive/SKILL.md +3 -4
- package/skills/j-distribute/SKILL.md +3 -4
- package/skills/j-do/SKILL.md +13 -15
- package/skills/j-doc/SKILL.md +3 -4
- package/skills/j-doc-sync/SKILL.md +3 -4
- package/skills/j-dooo/SKILL.md +6 -15
- package/skills/j-error/SKILL.md +3 -4
- package/skills/j-evaluate/SKILL.md +3 -4
- package/skills/j-examplify/SKILL.md +3 -4
- package/skills/j-help/SKILL.md +3 -4
- package/skills/j-idea/SKILL.md +3 -4
- package/skills/j-improve/SKILL.md +3 -4
- package/skills/j-init/SKILL.md +20 -11
- package/skills/j-init/scripts/apply-scaffold-visibility.sh +8 -6
- package/skills/j-jbp/SKILL.md +3 -4
- package/skills/j-lgtm/SKILL.md +3 -4
- package/skills/j-pi-plan/SKILL.md +3 -4
- package/skills/j-proceed/SKILL.md +3 -4
- package/skills/j-publish/SKILL.md +3 -4
- package/skills/j-publish/scripts/run_gates.sh +1 -1
- package/skills/j-reconcile/SKILL.md +38 -5
- package/skills/j-reconcile/assets/report_format.md +11 -0
- package/skills/j-reconcile/scripts/detect-unlinked-code.sh +2 -2
- package/skills/j-reconcile-origin/SKILL.md +3 -4
- package/skills/j-redo/SKILL.md +3 -4
- package/skills/j-skillify/SKILL.md +3 -4
- package/skills/j-spinoff/SKILL.md +3 -4
- package/skills/j-status/SKILL.md +4 -5
- package/skills/j-todo/SKILL.md +42 -5
- package/skills/j-todo/scripts/argument-is-not-ranked-list.sh +92 -0
- package/skills/j-todo/scripts/argument-is-ranked-list.sh +78 -0
- package/skills/j-uncharted/SKILL.md +251 -12
- package/skills/j-uncharted/scripts/detect-dependencies.sh +79 -21
- package/skills/j-uncharted/scripts/diff-since-baseline.sh +600 -0
- package/skills/j-uncharted/scripts/find-scan-baseline.sh +545 -0
- package/skills/j-uncharted/scripts/run-engine.sh +36 -2
- package/skills/j-uncharted/scripts/write-scan-record.sh +361 -0
- package/skills/j-wtf/SKILL.md +3 -4
- package/skills/jenga/SKILL.md +69 -8
- package/skills/jenga/playbooks/schema.json +4 -4
- package/skills/jenga/scripts/load-nl-catalog.js +5 -2
- package/skills/jenga/scripts/load-playbooks.sh +289 -4
- package/skills/jenga/scripts/match-playbook.sh +4 -4
- package/skills/jenga/scripts/run-playbook-step.sh +267 -1
- package/templates/permission-levels/level-1-locked.json +1 -1
- package/templates/permission-levels/level-2-guarded.json +1 -1
- package/templates/permission-levels/level-3-standard.json +1 -1
- package/templates/permission-levels/level-4-elevated.json +1 -1
- package/templates/permission-levels/level-5-unrestricted.json +1 -1
- package/templates/playbook-types.json +6 -0
- package/project/app/ui/dist/assets/index-BVR_7Owg.css +0 -1
- package/project/app/ui/dist/assets/index-CtU2xLQm.js +0 -104
- package/scripts/audit-twin-divergence.sh +0 -693
|
@@ -181,6 +181,18 @@
|
|
|
181
181
|
# stdout). If that was the last step, emits a completion report instead (see OUTPUT SCHEMA)
|
|
182
182
|
# and removes the state file.
|
|
183
183
|
#
|
|
184
|
+
# RUNTIME TYPE VERIFICATION (E62_S02_T01): when a value IS given, it is checked against the
|
|
185
|
+
# CURRENT step's declared `output_types` (read off that step's own SKILL.md), via
|
|
186
|
+
# `scripts/validate-typed-object.sh` -- normalizing first, then re-checking, before any
|
|
187
|
+
# failure is declared. A `{when, type}` conditional declaration conforms if it satisfies AT
|
|
188
|
+
# LEAST ONE branch (no classifier is ever run, no branch state is tracked). A value that
|
|
189
|
+
# still does not conform after normalize is NOT stored -- this call is silently redirected
|
|
190
|
+
# into the exact same halt behavior as `advance <state_file> failed "<note>"` (see below),
|
|
191
|
+
# with a note naming the step, the declared type(s) checked, and the raw pre-normalization
|
|
192
|
+
# value. A step with no declared `output_types` (including one this call cannot resolve a
|
|
193
|
+
# SKILL.md for) is completely unaffected: no lookup is even attempted beyond determining
|
|
194
|
+
# there is nothing to check.
|
|
195
|
+
#
|
|
184
196
|
# run-playbook-step.sh advance <state_file> skipped
|
|
185
197
|
# Records the CURRENT step as SKIPPED (a new `skipped` list, distinct from `completed`) and
|
|
186
198
|
# advances the pointer exactly like `passed` -- a skipped step never halts the chain and is
|
|
@@ -597,6 +609,30 @@ elif [ "$SUBCOMMAND" = "advance" ]; then
|
|
|
597
609
|
|
|
598
610
|
PROJECT_DIR="$(resolve_project_dir)"
|
|
599
611
|
|
|
612
|
+
# --- E62_S02_T01: resolve the jenga-agent PACKAGE root, needed ONLY to look up a completing
|
|
613
|
+
# step's declared `output_types` off its SKILL.md for runtime type verification. Mirrors
|
|
614
|
+
# load-playbooks.sh's own PKG_ROOT resolution (monorepo checkout vs. installed npm package) --
|
|
615
|
+
# including reusing JENGA_PLAYBOOKS_TEST_ROOT verbatim as the SAME test-injection override, rather
|
|
616
|
+
# than inventing a second env var for the same concept. Deliberately gated on
|
|
617
|
+
# `OUTCOME = passed && EXTRA non-empty`: those are the ONLY two conditions under which
|
|
618
|
+
# verification does anything at all (see the no-op cases in this task's board item), so a
|
|
619
|
+
# `skipped`/`failed` call, or a `passed` call with no typed-output value, never pays for this
|
|
620
|
+
# resolution at all -- "no lookup even attempted beyond determining there is nothing to check."
|
|
621
|
+
PKG_ROOT=""
|
|
622
|
+
if [ "$OUTCOME" = "passed" ] && [ -n "$EXTRA" ]; then
|
|
623
|
+
ADVANCE_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
624
|
+
if [ -n "${JENGA_PLAYBOOKS_TEST_ROOT:-}" ]; then
|
|
625
|
+
PKG_ROOT="$JENGA_PLAYBOOKS_TEST_ROOT"
|
|
626
|
+
elif [ -d "$ADVANCE_SCRIPT_DIR/../../../templates" ]; then
|
|
627
|
+
PKG_ROOT="$(cd "$ADVANCE_SCRIPT_DIR/../../.." && pwd)"
|
|
628
|
+
elif [ -d "$PROJECT_DIR/node_modules/@jenga-ai/agent/templates" ]; then
|
|
629
|
+
PKG_ROOT="$PROJECT_DIR/node_modules/@jenga-ai/agent"
|
|
630
|
+
else
|
|
631
|
+
echo "Error: could not locate the jenga-agent package root (templates/ not found via monorepo checkout or node_modules/@jenga-ai/agent) -- cannot resolve output_types for step verification." >&2
|
|
632
|
+
exit 2
|
|
633
|
+
fi
|
|
634
|
+
fi
|
|
635
|
+
|
|
600
636
|
PY_SCRIPT="$(mktemp -t run-playbook-step-advance-XXXXXX.py)"
|
|
601
637
|
trap 'rm -f "$PY_SCRIPT"' EXIT
|
|
602
638
|
|
|
@@ -604,7 +640,9 @@ elif [ "$SUBCOMMAND" = "advance" ]; then
|
|
|
604
640
|
import json
|
|
605
641
|
import os
|
|
606
642
|
import re
|
|
643
|
+
import subprocess
|
|
607
644
|
import sys
|
|
645
|
+
import tempfile
|
|
608
646
|
from datetime import datetime, timezone
|
|
609
647
|
|
|
610
648
|
state_file_path = sys.argv[1]
|
|
@@ -615,6 +653,221 @@ outcome = sys.argv[2]
|
|
|
615
653
|
extra = sys.argv[3] if len(sys.argv) > 3 and sys.argv[3] != "" else None
|
|
616
654
|
note = extra
|
|
617
655
|
project_dir = sys.argv[4] if len(sys.argv) > 4 and sys.argv[4] != "" else None
|
|
656
|
+
# E62_S02_T01: the jenga-agent package root, resolved (or hard-errored) by the bash wrapper above,
|
|
657
|
+
# ONLY when outcome == "passed" and extra is non-empty. Empty string in every other case -- exactly
|
|
658
|
+
# the cases where verification is a documented no-op, so this is never even looked at.
|
|
659
|
+
pkg_root = sys.argv[5] if len(sys.argv) > 5 and sys.argv[5] != "" else None
|
|
660
|
+
|
|
661
|
+
# ---------------------------------------------------------------------------------------------
|
|
662
|
+
# E62_S02_T01: runtime type verification at the "passed" chokepoint
|
|
663
|
+
# ---------------------------------------------------------------------------------------------
|
|
664
|
+
# `extract_output_types`/`declared_type_entries` below are a DELIBERATE, DOCUMENTED duplication of
|
|
665
|
+
# skills/jenga/scripts/load-playbooks.sh's own `extract_types_field`/`extract_output_types`/
|
|
666
|
+
# `declared_type_entries` (see that script's "TYPE REGISTRY" section, around line 788 as of
|
|
667
|
+
# E62_S01_T04). The task considered factoring this into a shared, importable module, but every
|
|
668
|
+
# python body in both scripts is a throwaway file generated into a fresh `mktemp` path per
|
|
669
|
+
# invocation (see the `cat > "$PY_SCRIPT" <<'PY'` pattern this whole file uses) -- there is no
|
|
670
|
+
# existing precedent in this repo for a shared importable python module fed to that pattern, and
|
|
671
|
+
# building one purely for a ~30-line, rarely-changing frontmatter parser was judged riskier than a
|
|
672
|
+
# small, clearly-labelled duplicate. If this parser ever needs a THIRD copy, that is the signal to
|
|
673
|
+
# extract it for real; until then, a change to one copy's parsing rules should always be checked
|
|
674
|
+
# against the other by anyone touching either.
|
|
675
|
+
_FRONTMATTER_RE = re.compile(r'^---\r?\n(.*?)\r?\n---', re.DOTALL)
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
def extract_output_types(skill_md_path):
|
|
679
|
+
"""Best-effort extraction of the `output_types` frontmatter field from a SKILL.md. Returns
|
|
680
|
+
None if the file/field is missing or unparseable, a `str` for the single-static-type form, or
|
|
681
|
+
a `list[dict]` for the `{when, type}` list form. Mirrors load-playbooks.sh's
|
|
682
|
+
`extract_types_field(path, "output_types")` exactly -- see the module-level note above."""
|
|
683
|
+
try:
|
|
684
|
+
with open(skill_md_path, encoding="utf-8") as fh:
|
|
685
|
+
content = fh.read()
|
|
686
|
+
except OSError:
|
|
687
|
+
return None
|
|
688
|
+
|
|
689
|
+
fm_match = _FRONTMATTER_RE.match(content)
|
|
690
|
+
if not fm_match:
|
|
691
|
+
return None
|
|
692
|
+
|
|
693
|
+
fm_lines = fm_match.group(1).splitlines()
|
|
694
|
+
|
|
695
|
+
for i, line in enumerate(fm_lines):
|
|
696
|
+
key_match = re.match(r'^output_types:\s*(.*)$', line)
|
|
697
|
+
if not key_match:
|
|
698
|
+
continue
|
|
699
|
+
|
|
700
|
+
rest = key_match.group(1).strip()
|
|
701
|
+
if rest:
|
|
702
|
+
return rest.strip('"\'')
|
|
703
|
+
|
|
704
|
+
# Block/list form: gather subsequent, more-indented lines into a list of dicts.
|
|
705
|
+
items = []
|
|
706
|
+
current = {}
|
|
707
|
+
j = i + 1
|
|
708
|
+
while j < len(fm_lines):
|
|
709
|
+
raw_line = fm_lines[j]
|
|
710
|
+
if not raw_line.strip():
|
|
711
|
+
j += 1
|
|
712
|
+
continue
|
|
713
|
+
if not raw_line[0].isspace():
|
|
714
|
+
break # a new top-level frontmatter key ends this block
|
|
715
|
+
|
|
716
|
+
stripped = raw_line.strip()
|
|
717
|
+
item_match = re.match(r'^-\s*(.*)$', stripped)
|
|
718
|
+
if item_match:
|
|
719
|
+
if current:
|
|
720
|
+
items.append(current)
|
|
721
|
+
current = {}
|
|
722
|
+
remainder = item_match.group(1)
|
|
723
|
+
kv = re.match(r'^([a-zA-Z_]+):\s*(.*)$', remainder) if remainder else None
|
|
724
|
+
if kv:
|
|
725
|
+
current[kv.group(1)] = kv.group(2).strip().strip('"\'')
|
|
726
|
+
else:
|
|
727
|
+
kv = re.match(r'^([a-zA-Z_]+):\s*(.*)$', stripped)
|
|
728
|
+
if kv:
|
|
729
|
+
current[kv.group(1)] = kv.group(2).strip().strip('"\'')
|
|
730
|
+
j += 1
|
|
731
|
+
|
|
732
|
+
if current:
|
|
733
|
+
items.append(current)
|
|
734
|
+
return items if items else None
|
|
735
|
+
|
|
736
|
+
return None
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def declared_type_entries(value):
|
|
740
|
+
"""Normalize an `output_types` value into `[(type_or_None, when_or_None), ...]`. Mirrors
|
|
741
|
+
load-playbooks.sh's `declared_type_entries` exactly -- see the module-level note above."""
|
|
742
|
+
if not value:
|
|
743
|
+
return []
|
|
744
|
+
if isinstance(value, str):
|
|
745
|
+
return [(value, None)]
|
|
746
|
+
if isinstance(value, list):
|
|
747
|
+
entries = []
|
|
748
|
+
for item in value:
|
|
749
|
+
if isinstance(item, dict):
|
|
750
|
+
entries.append((item.get("type") or None, item.get("when") or None))
|
|
751
|
+
else:
|
|
752
|
+
entries.append((None, None))
|
|
753
|
+
return entries
|
|
754
|
+
return []
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
def verify_typed_output(step_name, raw_value, pkg_root_dir):
|
|
758
|
+
"""The E62_S02_T01 chokepoint check itself.
|
|
759
|
+
|
|
760
|
+
Returns `(failure_note_or_None, resolved_value)`:
|
|
761
|
+
- `(None, value)` -- conforms (or nothing declared/checkable -- a no-op). `value` is the
|
|
762
|
+
raw value unless a declared branch needed normalization to conform,
|
|
763
|
+
in which case it is that branch's normalized value.
|
|
764
|
+
- `(note, None)` -- every declared branch is non-conforming even after normalize. `note`
|
|
765
|
+
names the step, the declared type(s) checked, and the raw
|
|
766
|
+
pre-normalization value, for the existing `failed` -> `halted` path.
|
|
767
|
+
|
|
768
|
+
The conditional `{when, type}` rule (ratified at epic level, not re-derived here): the value
|
|
769
|
+
conforms if it satisfies AT LEAST ONE declared branch. No classifier script is ever run and no
|
|
770
|
+
branch state is tracked -- every declared type is checked against the SAME captured value.
|
|
771
|
+
"""
|
|
772
|
+
skill_md_path = os.path.join(pkg_root_dir, "skills", step_name, "SKILL.md")
|
|
773
|
+
if not os.path.isfile(skill_md_path):
|
|
774
|
+
# No resolvable skill dir (e.g. a synthetic/test step name, or a stale board reference) --
|
|
775
|
+
# nothing to check against. Not this chokepoint's problem to diagnose further.
|
|
776
|
+
return None, raw_value
|
|
777
|
+
|
|
778
|
+
declared = extract_output_types(skill_md_path)
|
|
779
|
+
entries = [e for e in declared_type_entries(declared) if e[0] is not None]
|
|
780
|
+
if not entries:
|
|
781
|
+
# No declared output_types (or only malformed entries, which is a load-time concern this
|
|
782
|
+
# chokepoint does not re-litigate) -- a documented no-op, exactly as today.
|
|
783
|
+
return None, raw_value
|
|
784
|
+
|
|
785
|
+
validator = os.path.join(pkg_root_dir, "scripts", "validate-typed-object.sh")
|
|
786
|
+
if not os.path.isfile(validator):
|
|
787
|
+
print(
|
|
788
|
+
f"Error: type registry validator not found at {validator} -- cannot verify step "
|
|
789
|
+
f"'{step_name}'s declared output_types.",
|
|
790
|
+
file=sys.stderr,
|
|
791
|
+
)
|
|
792
|
+
sys.exit(2)
|
|
793
|
+
|
|
794
|
+
types_checked = []
|
|
795
|
+
branch_reasons = []
|
|
796
|
+
normalized_fallback = None # first branch that conforms only after normalize
|
|
797
|
+
|
|
798
|
+
for type_name, when_val in entries:
|
|
799
|
+
if type_name not in types_checked:
|
|
800
|
+
types_checked.append(type_name)
|
|
801
|
+
|
|
802
|
+
tmp_path = None
|
|
803
|
+
try:
|
|
804
|
+
with tempfile.NamedTemporaryFile(
|
|
805
|
+
mode="w", encoding="utf-8", delete=False, prefix="run-playbook-step-verify-"
|
|
806
|
+
) as tmp:
|
|
807
|
+
tmp.write(raw_value)
|
|
808
|
+
tmp_path = tmp.name
|
|
809
|
+
proc = subprocess.run(
|
|
810
|
+
[validator, "--value-file", tmp_path, type_name],
|
|
811
|
+
capture_output=True,
|
|
812
|
+
text=True,
|
|
813
|
+
)
|
|
814
|
+
finally:
|
|
815
|
+
if tmp_path is not None:
|
|
816
|
+
try:
|
|
817
|
+
os.unlink(tmp_path)
|
|
818
|
+
except OSError:
|
|
819
|
+
pass
|
|
820
|
+
|
|
821
|
+
if proc.returncode == 0:
|
|
822
|
+
# Conforms as given, under this branch alone -- the strongest possible verdict.
|
|
823
|
+
# "At least one branch accepts it" is satisfied immediately; stop checking further
|
|
824
|
+
# branches and never report this as normalized.
|
|
825
|
+
return None, raw_value
|
|
826
|
+
if proc.returncode == 2:
|
|
827
|
+
if normalized_fallback is None:
|
|
828
|
+
try:
|
|
829
|
+
payload = json.loads(proc.stdout)
|
|
830
|
+
normalized_fallback = payload.get("value")
|
|
831
|
+
except (json.JSONDecodeError, AttributeError):
|
|
832
|
+
normalized_fallback = raw_value
|
|
833
|
+
continue
|
|
834
|
+
if proc.returncode == 3:
|
|
835
|
+
reason = None
|
|
836
|
+
try:
|
|
837
|
+
payload = json.loads(proc.stdout)
|
|
838
|
+
reason = payload.get("reason")
|
|
839
|
+
except (json.JSONDecodeError, AttributeError):
|
|
840
|
+
reason = None
|
|
841
|
+
branch = f" (when='{when_val}')" if when_val else ""
|
|
842
|
+
branch_reasons.append(f"type '{type_name}'{branch}: {reason or 'does not conform'}")
|
|
843
|
+
continue
|
|
844
|
+
# Exit 4 (unknown type), 5 (environment), 6 (registry contract violation) are
|
|
845
|
+
# ENVIRONMENT-level failures, never value judgments -- E62_S01_T02's own validator
|
|
846
|
+
# contract is explicit that these must not be conflated with non-conforming. A properly
|
|
847
|
+
# loaded chain should never reach an unknown/malformed type here (load-playbooks.sh's own
|
|
848
|
+
# load-time check, E62_S01_T04, already rejects that at confirmation time) -- if it
|
|
849
|
+
# happens anyway, this is a broken environment, not a playbook-author's bad value, so it
|
|
850
|
+
# is NOT routed through the failed/halted board mechanism.
|
|
851
|
+
print(
|
|
852
|
+
f"Error: scripts/validate-typed-object.sh reported an environment-level failure "
|
|
853
|
+
f"(exit {proc.returncode}) verifying step '{step_name}'s declared type '{type_name}': "
|
|
854
|
+
f"{proc.stderr.strip() or proc.stdout.strip()}",
|
|
855
|
+
file=sys.stderr,
|
|
856
|
+
)
|
|
857
|
+
sys.exit(2)
|
|
858
|
+
|
|
859
|
+
if normalized_fallback is not None:
|
|
860
|
+
# No branch conformed as given, but at least one conforms after its own normalize.
|
|
861
|
+
return None, normalized_fallback
|
|
862
|
+
|
|
863
|
+
# No declared branch accepts the value, as given or normalized.
|
|
864
|
+
types_list = ", ".join(types_checked)
|
|
865
|
+
detail = "; ".join(branch_reasons) if branch_reasons else "no declared branch accepts it"
|
|
866
|
+
failure_note = (
|
|
867
|
+
f"step '{step_name}' produced a value that does not conform to its declared "
|
|
868
|
+
f"output_types [{types_list}] (raw value: '{raw_value}'); {detail}"
|
|
869
|
+
)
|
|
870
|
+
return failure_note, None
|
|
618
871
|
|
|
619
872
|
# --- E53_S04_T05: artifact persistence + redaction (persisted copy only -- see header) ---
|
|
620
873
|
_ABS_PATH_RE = re.compile(r'(/[^\s"\']+)')
|
|
@@ -693,6 +946,19 @@ if idx >= len(steps):
|
|
|
693
946
|
current_step = steps[idx]
|
|
694
947
|
state.setdefault("skipped", [])
|
|
695
948
|
|
|
949
|
+
# E62_S02_T01: verify a "passed" step's typed-output value against its declared output_types
|
|
950
|
+
# BEFORE the outcome dispatch below, so a non-conforming value can redirect into the EXISTING
|
|
951
|
+
# `outcome == "failed"` branch immediately after -- no second failure mechanism is introduced.
|
|
952
|
+
# Both no-op cases (no declared output_types; no typed-output value at all) leave `outcome` and
|
|
953
|
+
# `extra` untouched, so this is byte-for-byte the pre-existing behavior for them.
|
|
954
|
+
if outcome == "passed" and extra is not None:
|
|
955
|
+
verify_failure_note, resolved_value = verify_typed_output(current_step, extra, pkg_root)
|
|
956
|
+
if verify_failure_note is not None:
|
|
957
|
+
outcome = "failed"
|
|
958
|
+
note = verify_failure_note
|
|
959
|
+
else:
|
|
960
|
+
extra = resolved_value
|
|
961
|
+
|
|
696
962
|
if outcome == "failed":
|
|
697
963
|
state["halted"] = True
|
|
698
964
|
state["failed_step"] = current_step
|
|
@@ -757,7 +1023,7 @@ print(json.dumps({
|
|
|
757
1023
|
print(f"STATE_FILE: {state_file_path}", file=sys.stderr)
|
|
758
1024
|
PY
|
|
759
1025
|
|
|
760
|
-
python3 "$PY_SCRIPT" "$STATE_FILE" "$OUTCOME" "$EXTRA" "$PROJECT_DIR"
|
|
1026
|
+
python3 "$PY_SCRIPT" "$STATE_FILE" "$OUTCOME" "$EXTRA" "$PROJECT_DIR" "$PKG_ROOT"
|
|
761
1027
|
exit $?
|
|
762
1028
|
|
|
763
1029
|
else
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"hooks": [
|
|
42
42
|
{
|
|
43
43
|
"type": "command",
|
|
44
|
-
"command": "NAME=$(jq -r '.name')\n. \"$(git rev-parse --show-toplevel)/lib/resolve-project-dir.sh\"\nDIR=\"$JENGA_PROJECT_DIR/.claude/worktrees/$NAME\"\ngit worktree add \"$DIR\" -b \"$NAME\" 2>&1\necho \"$DIR\"\n"
|
|
44
|
+
"command": "NAME=$(jq -r '.name')\n. \"$(git rev-parse --show-toplevel)/lib/resolve-project-dir.sh\"\nDIR=\"$JENGA_PROJECT_DIR/.claude/worktrees/$NAME\"\ngit worktree add \"$DIR\" -b \"$NAME\" 2>&1\n\"$(git rev-parse --show-toplevel)/scripts/install-worktree-commit-guard.sh\" \"$DIR\" \"$NAME\" 1>&2\necho \"$DIR\"\n"
|
|
45
45
|
}
|
|
46
46
|
]
|
|
47
47
|
}
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"hooks": [
|
|
35
35
|
{
|
|
36
36
|
"type": "command",
|
|
37
|
-
"command": "NAME=$(jq -r '.name')\n. \"$(git rev-parse --show-toplevel)/lib/resolve-project-dir.sh\"\nDIR=\"$JENGA_PROJECT_DIR/.claude/worktrees/$NAME\"\ngit worktree add \"$DIR\" -b \"$NAME\" 2>&1\necho \"$DIR\"\n"
|
|
37
|
+
"command": "NAME=$(jq -r '.name')\n. \"$(git rev-parse --show-toplevel)/lib/resolve-project-dir.sh\"\nDIR=\"$JENGA_PROJECT_DIR/.claude/worktrees/$NAME\"\ngit worktree add \"$DIR\" -b \"$NAME\" 2>&1\n\"$(git rev-parse --show-toplevel)/scripts/install-worktree-commit-guard.sh\" \"$DIR\" \"$NAME\" 1>&2\necho \"$DIR\"\n"
|
|
38
38
|
}
|
|
39
39
|
]
|
|
40
40
|
}
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"hooks": [
|
|
33
33
|
{
|
|
34
34
|
"type": "command",
|
|
35
|
-
"command": "NAME=$(jq -r '.name')\n. \"$(git rev-parse --show-toplevel)/lib/resolve-project-dir.sh\"\nDIR=\"$JENGA_PROJECT_DIR/.claude/worktrees/$NAME\"\ngit worktree add \"$DIR\" -b \"$NAME\" 2>&1\necho \"$DIR\"\n"
|
|
35
|
+
"command": "NAME=$(jq -r '.name')\n. \"$(git rev-parse --show-toplevel)/lib/resolve-project-dir.sh\"\nDIR=\"$JENGA_PROJECT_DIR/.claude/worktrees/$NAME\"\ngit worktree add \"$DIR\" -b \"$NAME\" 2>&1\n\"$(git rev-parse --show-toplevel)/scripts/install-worktree-commit-guard.sh\" \"$DIR\" \"$NAME\" 1>&2\necho \"$DIR\"\n"
|
|
36
36
|
}
|
|
37
37
|
]
|
|
38
38
|
}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"hooks": [
|
|
31
31
|
{
|
|
32
32
|
"type": "command",
|
|
33
|
-
"command": "NAME=$(jq -r '.name')\n. \"$(git rev-parse --show-toplevel)/lib/resolve-project-dir.sh\"\nDIR=\"$JENGA_PROJECT_DIR/.claude/worktrees/$NAME\"\ngit worktree add \"$DIR\" -b \"$NAME\" 2>&1\necho \"$DIR\"\n"
|
|
33
|
+
"command": "NAME=$(jq -r '.name')\n. \"$(git rev-parse --show-toplevel)/lib/resolve-project-dir.sh\"\nDIR=\"$JENGA_PROJECT_DIR/.claude/worktrees/$NAME\"\ngit worktree add \"$DIR\" -b \"$NAME\" 2>&1\n\"$(git rev-parse --show-toplevel)/scripts/install-worktree-commit-guard.sh\" \"$DIR\" \"$NAME\" 1>&2\necho \"$DIR\"\n"
|
|
34
34
|
}
|
|
35
35
|
]
|
|
36
36
|
}
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"hooks": [
|
|
29
29
|
{
|
|
30
30
|
"type": "command",
|
|
31
|
-
"command": "NAME=$(jq -r '.name')\n. \"$(git rev-parse --show-toplevel)/lib/resolve-project-dir.sh\"\nDIR=\"$JENGA_PROJECT_DIR/.claude/worktrees/$NAME\"\ngit worktree add \"$DIR\" -b \"$NAME\" 2>&1\necho \"$DIR\"\n"
|
|
31
|
+
"command": "NAME=$(jq -r '.name')\n. \"$(git rev-parse --show-toplevel)/lib/resolve-project-dir.sh\"\nDIR=\"$JENGA_PROJECT_DIR/.claude/worktrees/$NAME\"\ngit worktree add \"$DIR\" -b \"$NAME\" 2>&1\n\"$(git rev-parse --show-toplevel)/scripts/install-worktree-commit-guard.sh\" \"$DIR\" \"$NAME\" 1>&2\necho \"$DIR\"\n"
|
|
32
32
|
}
|
|
33
33
|
]
|
|
34
34
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
.loading-spinner{padding:2rem;text-align:center;color:#888;font-size:1rem}.error-message{padding:1rem;background:#fef2f2;border:1px solid #fca5a5;border-radius:4px;color:#b91c1c}.history-tab h2{margin-bottom:1rem}.history-empty{color:#888;padding:2rem;text-align:center}.history-list{list-style:none;padding:0;margin:0;border:1px solid #e5e7eb;border-radius:6px;overflow:hidden}.history-entry{display:flex;align-items:center;gap:.75rem;padding:.65rem 1rem;cursor:pointer;border-bottom:1px solid #f3f4f6;transition:background .12s}.history-entry:last-child{border-bottom:none}.history-entry:hover,.history-entry:focus{background:#f0f7ff;outline:none}.history-entry.selected{background:#dbeafe}.history-badge{flex-shrink:0;font-size:.75rem;font-weight:600;padding:.2rem .55rem;border-radius:999px;white-space:nowrap}.badge-commit{background:#dbeafe;color:#1d4ed8}.badge-rapport{background:#dcfce7;color:#15803d}.history-subject{flex:1;font-size:.9rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#1f2937}.history-date{flex-shrink:0;font-size:.78rem;color:#9ca3af}.panel-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000040;z-index:100}.detail-panel{position:fixed;top:0;right:0;bottom:0;width:min(480px,100vw);background:#fff;box-shadow:-4px 0 24px #00000026;z-index:101;overflow-y:auto;padding:1.5rem;animation:slideIn .2s ease-out}@keyframes slideIn{0%{transform:translate(100%)}to{transform:translate(0)}}.panel-close{float:right;background:none;border:none;font-size:1.25rem;cursor:pointer;color:#6b7280;padding:.25rem;line-height:1}.panel-close:hover{color:#111827}.panel-title{margin:0 0 1rem;font-size:1.1rem;color:#1f2937}.panel-meta{display:grid;grid-template-columns:auto 1fr;gap:.3rem 1rem;font-size:.85rem;margin-bottom:1rem}.panel-meta dt{font-weight:600;color:#6b7280;white-space:nowrap}.panel-meta dd{margin:0;color:#1f2937;word-break:break-all}.panel-subject{font-size:.95rem;color:#1f2937}.panel-body{font-size:.82rem;white-space:pre-wrap;background:#f9fafb;border:1px solid #e5e7eb;border-radius:4px;padding:.75rem;max-height:40vh;overflow-y:auto}.panel-empty{color:#9ca3af;font-style:italic}.markdown-body{font-size:.9rem;line-height:1.6;color:#1f2937}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4{margin:1em 0 .4em;line-height:1.3}.markdown-body p{margin:.5em 0}.markdown-body code{background:#f3f4f6;padding:.1em .35em;border-radius:3px;font-size:.85em}.markdown-body pre{background:#f3f4f6;padding:.75rem;border-radius:4px;overflow-x:auto;font-size:.82em}.markdown-body pre code{background:none;padding:0}.markdown-body blockquote{border-left:3px solid #d1d5db;margin:.75em 0;padding:.25em 1em;color:#6b7280}.markdown-body ul,.markdown-body ol{padding-left:1.5em;margin:.5em 0}@media (max-width: 480px){.history-entry{flex-wrap:wrap;row-gap:var(--space-xs)}.history-subject{flex-basis:100%;order:3;white-space:normal}.detail-panel{box-sizing:border-box;padding:var(--space-md)}.panel-close{padding:var(--space-sm);font-size:1.5rem;display:inline-flex;align-items:center;justify-content:center;min-width:44px;min-height:44px}.panel-meta{grid-template-columns:1fr;gap:.15rem 0}.panel-meta dt{margin-top:var(--space-xs)}.markdown-body code{overflow-wrap:break-word;word-break:break-word}.markdown-body table{display:block;max-width:100%;overflow-x:auto}}.board-view{display:flex;flex-direction:column;gap:1.5rem}.epic-card{background:#fff;border:1px solid #e5e7eb;border-radius:6px;padding:1rem}.epic-header{display:flex;align-items:center;gap:.75rem;margin-bottom:.75rem}.epic-title{font-size:1.1rem;font-weight:600;margin:0;cursor:pointer}.epic-id{color:#6b7280;font-size:.85rem}.stories-list{display:flex;flex-direction:column;gap:.75rem;padding-left:1rem}.story-card{background:#f9fafb;border:1px solid #e5e7eb;border-radius:4px;padding:.75rem}.story-header{display:flex;align-items:center;gap:.5rem;margin-bottom:.5rem}.story-title{font-size:1rem;font-weight:500;margin:0;cursor:pointer}.story-id{color:#6b7280;font-size:.8rem}.tasks-list{display:flex;flex-direction:column;gap:.4rem;padding-left:1rem}.task-card{background:#fff;border:1px solid #e5e7eb;border-radius:3px;padding:.5rem .75rem;display:flex;align-items:center;gap:.5rem}.task-title{font-size:.9rem;flex:1;cursor:pointer}.task-id{color:#9ca3af;font-size:.75rem}.task-assigned{color:#6b7280;font-size:.8rem}.status-badge{padding:.2rem .5rem;border-radius:9999px;font-size:.75rem;font-weight:500}.badge-pending{background:#f3f4f6;color:#374151}.badge-running{background:#dbeafe;color:#1d4ed8}.badge-in-progress{background:#ffedd5;color:#c2410c}.badge-passed{background:#dcfce7;color:#15803d}.badge-passed-remarks{background:#fff;color:#15803d;border:1px solid #dcfce7}.badge-done{background:#dbeafe;color:#1d4ed8}.badge-failed{background:#fee2e2;color:#b91c1c}.empty-state{padding:2rem;text-align:center;color:#6b7280}.status-filter{display:flex;flex-wrap:wrap;gap:.75rem;padding:.75rem 1rem;background:#f9fafb;border:1px solid #e5e7eb;border-radius:6px}.status-filter-option{display:flex;align-items:center;gap:.35rem;font-size:.85rem;color:#374151;cursor:pointer}@media (max-width: 480px){.epic-header,.story-header{flex-wrap:wrap;row-gap:var(--space-xs)}.epic-title,.story-title{flex-basis:100%}.task-card{flex-wrap:wrap;row-gap:var(--space-xs)}.task-card .task-title{flex-basis:100%}.stories-list,.tasks-list{padding-left:var(--space-sm)}}.kanban-board{display:flex;align-items:flex-start;gap:.75rem;overflow-x:auto;padding-bottom:.5rem}.kanban-column{flex:0 0 220px;width:220px;background:#f9fafb;border:1px solid #e5e7eb;border-radius:6px;padding:.6rem;display:flex;flex-direction:column;gap:.5rem}.kanban-column-empty{flex:0 0 140px;width:140px;opacity:.6}.kanban-column-header{display:flex;align-items:center;justify-content:space-between;gap:.4rem}.kanban-column-title{font-size:.8rem;font-weight:600;color:#374151;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kanban-column-count{font-size:.7rem;color:#6b7280;background:#e5e7eb;border-radius:9999px;padding:.05rem .4rem}.kanban-column-empty-state{font-size:.75rem;color:#9ca3af;text-align:center;padding:.5rem 0}.kanban-column-items{display:flex;flex-direction:column;gap:.5rem}.kanban-card{background:#fff;border:1px solid #e5e7eb;border-radius:4px;padding:.5rem .6rem;display:flex;flex-direction:column;gap:.3rem}.kanban-card-header{display:flex;align-items:center;justify-content:space-between;gap:.4rem}.kanban-card-type{font-size:.65rem;text-transform:uppercase;letter-spacing:.03em;color:#9ca3af}.kanban-card-id{font-size:.7rem;color:#9ca3af}.kanban-card-title{font-size:.85rem;color:#111827;cursor:pointer}.kanban-card-queued{border-style:dashed;border-color:#c4b5fd;background:#fdfcff}.kanban-card-footer{display:flex;align-items:center;justify-content:space-between;gap:.4rem}.kanban-card-queued-marker{font-size:.65rem;text-transform:uppercase;letter-spacing:.03em;color:#6d28d9;background:#ede9fe;border:1px solid #ddd6fe;border-radius:9999px;padding:.05rem .4rem;white-space:nowrap;cursor:help}@media (max-width: 480px){.kanban-board{position:relative;gap:var(--space-sm);scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch}.kanban-column{flex:0 0 85%;width:85%;scroll-snap-align:start;scroll-snap-stop:always}.kanban-column-empty{flex:0 0 140px;width:140px}.kanban-board:after{content:"";position:sticky;top:0;right:0;flex:0 0 var(--space-xl);align-self:stretch;background:linear-gradient(to right,transparent,rgba(249,250,251,.95));pointer-events:none}}.architecture-tab{overflow-x:hidden}.architecture-tab h2{margin-bottom:1rem}.arch-empty{color:#9ca3af;font-style:italic}.arch-section{margin-bottom:2rem}.arch-section h3{font-size:1rem;font-weight:600;color:#374151;margin-bottom:.75rem;border-bottom:1px solid #e5e7eb;padding-bottom:.35rem}.tech-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:.75rem}.tech-card{background:#fff;border:1px solid #e5e7eb;border-radius:8px;padding:.85rem 1rem;display:flex;flex-direction:column;gap:.3rem;box-shadow:0 1px 3px #0000000d}.tech-name{font-weight:600;font-size:.9rem;color:#1f2937}.tech-description{font-size:.78rem;color:#6b7280}.dep-table{width:100%;border-collapse:collapse;font-size:.875rem}.dep-table th{text-align:left;padding:.5rem .75rem;background:#f9fafb;border:1px solid #e5e7eb;font-weight:600;color:#374151}.dep-table td{padding:.45rem .75rem;border:1px solid #f3f4f6;color:#1f2937}.dep-table tr:hover td{background:#f9fafb}.dep-name{font-family:monospace}.dep-version{color:#6b7280;font-family:monospace}.dep-badge{display:inline-block;font-size:.7rem;font-weight:600;padding:.18rem .55rem;border-radius:999px;white-space:nowrap}.dep-runtime{background:#dbeafe;color:#1d4ed8}.dep-dev{background:#f3f4f6;color:#4b5563}.dep-table-wrap{overflow-x:auto}.sad-map-toolbar{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem;margin-bottom:.6rem}.sad-type-chip{--chip-color: #94a3b8;display:inline-flex;align-items:center;gap:.35rem;font-size:.75rem;font-weight:600;color:#374151;background:#fff;border:1px solid #e5e7eb;border-radius:999px;padding:.25rem .65rem;cursor:pointer;line-height:1.2}.sad-type-chip.is-collapsed{background:#f3f4f6;border-color:var(--chip-color)}.sad-type-chip-dot{width:8px;height:8px;border-radius:50%;background:var(--chip-color);flex-shrink:0}.sad-reset-view-btn{font-size:.75rem;font-weight:600;color:#374151;background:#fff;border:1px solid #e5e7eb;border-radius:6px;padding:.25rem .65rem;cursor:pointer;margin-left:auto}.sad-map-scroll{width:100%;min-height:320px;overflow:hidden;-webkit-overflow-scrolling:touch;border:1px solid var(--border, #e2e8f0);border-radius:8px;background:var(--bg-secondary, #f8fafc)}.sad-map-svg{display:block;width:100%;cursor:grab}.sad-map-svg:active{cursor:grabbing}.sad-empty{color:var(--text-muted, #94a3b8);font-style:italic}.sad-coverage-indicator{font-size:.78rem;color:#6b7280;margin:0 0 .5rem}.sad-coverage-shown{font-weight:600;color:#374151}.sad-coverage-hidden{color:#9ca3af}.sad-coverage-stale{color:#b45309;font-weight:600}.sad-empty-state{display:flex;flex-direction:column;gap:.35rem}.sad-empty-detail{font-size:.8rem;color:#9ca3af;margin:0}.sad-ghost-legend{display:flex;align-items:center;gap:.4rem;font-size:.75rem;color:#9ca3af;margin:0 0 .6rem}.sad-ghost-legend-swatch{display:inline-block;width:12px;height:12px;flex-shrink:0;border-radius:3px;background:#94a3b8;opacity:.4;border:1.5px dashed #1f2937}@media (max-width: 480px){.architecture-tab{padding-bottom:var(--space-sm)}.arch-section{margin-bottom:var(--space-lg)}.arch-section h3{padding-bottom:var(--space-xs)}.tech-grid{gap:var(--space-sm)}.tech-card{padding:var(--space-sm) var(--space-md)}.sad-map-scroll{min-height:180px}}.entry-list-empty{color:#888;padding:2rem;text-align:center}.entry-list{list-style:none;padding:0;margin:0;border:1px solid #e5e7eb;border-radius:6px;overflow:hidden}.entry-list-item{display:flex;align-items:center;gap:.75rem;padding:.65rem 1rem;cursor:pointer;border-bottom:1px solid #f3f4f6;transition:background .12s}.entry-list-item:last-child{border-bottom:none}.entry-list-item:hover,.entry-list-item:focus{background:#f0f7ff;outline:none}.entry-list-item.selected{background:#dbeafe}.entry-badge{flex-shrink:0;font-size:.75rem;font-weight:600;padding:.2rem .55rem;border-radius:999px;white-space:nowrap;text-transform:capitalize;background:#ede9fe;color:#6d28d9}.entry-title{flex:1;font-size:.9rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:#1f2937}.entry-date{flex-shrink:0;font-size:.78rem;color:#9ca3af}@media (max-width: 480px){.entry-list-item{flex-wrap:wrap;row-gap:var(--space-xs)}.entry-title{flex-basis:100%;order:3;white-space:normal}}.pagination-controls{display:flex;align-items:center;justify-content:center;gap:var(--space-md);margin-top:var(--space-md)}.pagination-btn{padding:.4rem .9rem;font-size:.85rem;border:1px solid #e5e7eb;border-radius:6px;background:#fff;color:#1f2937;cursor:pointer}.pagination-btn:hover:not(:disabled){background:#f0f7ff;border-color:#93c5fd}.pagination-btn:disabled{opacity:.45;cursor:not-allowed}.pagination-status{font-size:.82rem;color:#6b7280;white-space:nowrap}@media (max-width: 480px){.pagination-controls{gap:var(--space-sm)}.pagination-btn{padding:.5rem .85rem;font-size:.9rem}.pagination-status{font-size:.78rem}}.rapports-search-input{display:block;width:100%;box-sizing:border-box;padding:.55rem .75rem;margin-bottom:var(--space-md);font-size:.9rem;border:1px solid #e5e7eb;border-radius:6px;color:#1f2937}.rapports-search-input:focus{outline:none;border-color:#93c5fd;box-shadow:0 0 0 2px #3b82f640}@media (max-width: 480px){.rapports-search-input{font-size:1rem;padding:.65rem .75rem}}:root{--space-xs: .25rem;--space-sm: .5rem;--space-md: 1rem;--space-lg: 1.5rem;--space-xl: 2rem;--breakpoint-mobile: 480px;--breakpoint-tablet: 768px}body{font-family:sans-serif;margin:0;background:#f5f5f5}.app{max-width:1200px;margin:0 auto;padding:1rem}.tabs{display:flex;gap:0;border-bottom:1px solid #ddd}.tab-btn{padding:.6rem 1.2rem;border:none;background:transparent;cursor:pointer;font-size:1rem;color:#555;border-bottom:2px solid transparent}.tab-btn.active{color:#2563eb;border-bottom-color:#2563eb;font-weight:600}.tab-content{padding:1rem 0}@media (max-width: 480px){.app{width:100%;box-sizing:border-box;padding:var(--space-sm)}.tabs{flex-wrap:nowrap;overflow-x:auto;-webkit-overflow-scrolling:touch}.tab-btn{flex:0 0 auto;padding:var(--space-sm) var(--space-md)}}
|