@antoneeo/mkt-agentic-sdlc-skill 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/README.md +4 -0
- package/gemini-extension.json +6 -6
- package/package.json +50 -50
- package/scripts/init.js +22 -11
- package/scripts/lib.js +232 -1
- package/scripts/postinstall.js +34 -2
- package/scripts/preuninstall.js +14 -1
- package/skills/mkt-agentic-sdlc/ENFORCEMENT.md +31 -0
- package/skills/mkt-agentic-sdlc/SKILL.md +240 -240
- package/skills/mkt-agentic-sdlc/review.md +51 -0
- package/skills/mkt-agentic-sdlc/scripts/mkt_check.py +5 -0
- package/skills/mkt-agentic-sdlc/scripts/sdlc_core.py +138 -0
|
@@ -1646,6 +1646,143 @@ def cmd_mark(root, paths):
|
|
|
1646
1646
|
return 0
|
|
1647
1647
|
|
|
1648
1648
|
|
|
1649
|
+
# ------------------------------------------------- orientation-hook wiring
|
|
1650
|
+
# F-041: read-only mirror of the installer-side detection in lib.js
|
|
1651
|
+
# (findOrientHook + orientHookValidator). The JS owns WRITING hooks, with its
|
|
1652
|
+
# injection guards; this side only DETECTS, at `check`, so a project whose
|
|
1653
|
+
# sessions start unoriented hears about it in-project (ENFORCEMENT par.4: the
|
|
1654
|
+
# hook is wired at init only -- pre-F-036 and agent-bootstrapped projects never
|
|
1655
|
+
# got it, and a wired-but-dead hook is "the worst of the three states").
|
|
1656
|
+
# The file list is a SUPERSET of the JS one: .codex/hooks.json is documented in
|
|
1657
|
+
# ENFORCEMENT par.4 but never written by init. Pinned equal on both sides:
|
|
1658
|
+
# the entry-point names, and (F-042) the user-settings path derivation --
|
|
1659
|
+
# CLAUDE_CONFIG_DIR when set, else <home>/.claude -- empty-string env falsy
|
|
1660
|
+
# on both sides.
|
|
1661
|
+
ORIENT_HOOK_ENTRY_POINTS = ("sdlc_check.py", "mkt_check.py")
|
|
1662
|
+
ORIENT_HOOK_CONFIG_FILES = (".claude/settings.json",
|
|
1663
|
+
".claude/settings.local.json",
|
|
1664
|
+
".codex/hooks.json")
|
|
1665
|
+
ORIENT_HOOK_CONFIG_CAP = 1024 * 1024 # settings are tiny; cap pathological files
|
|
1666
|
+
_ORIENT_HOOK_CMD_RE = re.compile(r" orient(\s|$)")
|
|
1667
|
+
_ORIENT_HOOK_TOKEN_RE = re.compile(r'"([^"]*)"|(\S+)')
|
|
1668
|
+
|
|
1669
|
+
|
|
1670
|
+
def _orient_entries_in(p):
|
|
1671
|
+
"""Yield the SessionStart commands invoking an orient entry point from ONE
|
|
1672
|
+
client config file. Fail-open: an unreadable, oversized, malformed or
|
|
1673
|
+
directory-shaped file contributes nothing. Reads are utf-8-sig -- a BOM'd
|
|
1674
|
+
settings.json (PowerShell/Notepad) must not read as unwired (declared
|
|
1675
|
+
divergence from Node's JSON.parse)."""
|
|
1676
|
+
try:
|
|
1677
|
+
if not p.is_file() or p.stat().st_size > ORIENT_HOOK_CONFIG_CAP:
|
|
1678
|
+
return
|
|
1679
|
+
data = json.loads(p.read_text(encoding="utf-8-sig"))
|
|
1680
|
+
except Exception:
|
|
1681
|
+
return
|
|
1682
|
+
if not isinstance(data, dict):
|
|
1683
|
+
return
|
|
1684
|
+
hooks = data.get("hooks")
|
|
1685
|
+
groups = hooks.get("SessionStart") if isinstance(hooks, dict) else None
|
|
1686
|
+
if groups is None:
|
|
1687
|
+
groups = data.get("SessionStart") # the .codex/hooks.json shape
|
|
1688
|
+
if not isinstance(groups, list):
|
|
1689
|
+
return
|
|
1690
|
+
for group in groups:
|
|
1691
|
+
if not isinstance(group, dict):
|
|
1692
|
+
continue
|
|
1693
|
+
inner = group.get("hooks")
|
|
1694
|
+
for h in inner if isinstance(inner, list) else [group]:
|
|
1695
|
+
cmd = h.get("command") if isinstance(h, dict) else None
|
|
1696
|
+
if (isinstance(cmd, str)
|
|
1697
|
+
and any(n in cmd for n in ORIENT_HOOK_ENTRY_POINTS)
|
|
1698
|
+
and _ORIENT_HOOK_CMD_RE.search(cmd)):
|
|
1699
|
+
yield cmd
|
|
1700
|
+
|
|
1701
|
+
|
|
1702
|
+
def _orient_user_settings_path():
|
|
1703
|
+
"""The machine-global settings file (F-042 wires it at install time).
|
|
1704
|
+
|
|
1705
|
+
Read at CALL time -- a DECLARED divergence from AGENTIC_SDLC_KB_ROOT's
|
|
1706
|
+
import-time read: unittest imports this module once before any test runs,
|
|
1707
|
+
so an import-time seam would be frozen before the matrix could set it.
|
|
1708
|
+
AGENTIC_SDLC_USER_SETTINGS is the test-isolation seam (python-side only);
|
|
1709
|
+
the production derivation mirrors lib.js: CLAUDE_CONFIG_DIR || ~/.claude."""
|
|
1710
|
+
seam = os.environ.get("AGENTIC_SDLC_USER_SETTINGS")
|
|
1711
|
+
if seam:
|
|
1712
|
+
return Path(seam)
|
|
1713
|
+
cfg = os.environ.get("CLAUDE_CONFIG_DIR")
|
|
1714
|
+
return (Path(cfg) if cfg else Path.home() / ".claude") / "settings.json"
|
|
1715
|
+
|
|
1716
|
+
|
|
1717
|
+
def _orient_hook_commands(root):
|
|
1718
|
+
"""Yield (command, at_user_level) across every known client config: the
|
|
1719
|
+
three project-relative files, then the machine-global user settings --
|
|
1720
|
+
the file the installer wires (F-042), without which a fixed machine's
|
|
1721
|
+
projects would read as unwired forever."""
|
|
1722
|
+
for rel in ORIENT_HOOK_CONFIG_FILES:
|
|
1723
|
+
for cmd in _orient_entries_in(Path(root) / rel):
|
|
1724
|
+
yield cmd, False
|
|
1725
|
+
for cmd in _orient_entries_in(_orient_user_settings_path()):
|
|
1726
|
+
yield cmd, True
|
|
1727
|
+
|
|
1728
|
+
|
|
1729
|
+
def orient_hook_state(root):
|
|
1730
|
+
"""'wired' | 'dead' | 'unwired'.
|
|
1731
|
+
|
|
1732
|
+
A matching entry whose tokens name no validator is "cannot tell" and counts
|
|
1733
|
+
as wired (the lib.js contract: never report broken on cannot-tell). 'dead'
|
|
1734
|
+
requires every matching entry to name a validator that does not resolve:
|
|
1735
|
+
the client runs ALL SessionStart hooks -- project-level AND user-level
|
|
1736
|
+
merge -- so one live entry at either level still orients the session
|
|
1737
|
+
(any-resolves aggregation across levels). Relative tokens resolve against
|
|
1738
|
+
the project root (the vendored-validator convention); a USER-level relative
|
|
1739
|
+
token has no cwd to resolve against and is treated as cannot-tell."""
|
|
1740
|
+
matched = False
|
|
1741
|
+
for cmd, at_user_level in _orient_hook_commands(root):
|
|
1742
|
+
matched = True
|
|
1743
|
+
token = None
|
|
1744
|
+
for m in _ORIENT_HOOK_TOKEN_RE.finditer(cmd):
|
|
1745
|
+
tok = m.group(1) if m.group(1) is not None else m.group(2)
|
|
1746
|
+
if any(tok.endswith(n) for n in ORIENT_HOOK_ENTRY_POINTS):
|
|
1747
|
+
token = tok
|
|
1748
|
+
break
|
|
1749
|
+
if token is None:
|
|
1750
|
+
return "wired"
|
|
1751
|
+
tp = Path(token)
|
|
1752
|
+
if not tp.is_absolute():
|
|
1753
|
+
if at_user_level:
|
|
1754
|
+
return "wired" # cannot tell: no cwd exists at user level
|
|
1755
|
+
tp = Path(root) / tp
|
|
1756
|
+
if tp.exists():
|
|
1757
|
+
return "wired"
|
|
1758
|
+
return "dead" if matched else "unwired"
|
|
1759
|
+
|
|
1760
|
+
|
|
1761
|
+
def print_orient_hook_note(root):
|
|
1762
|
+
"""Print the FS-A wiring note (unwired / dead), or nothing when wired.
|
|
1763
|
+
|
|
1764
|
+
Informational only, by contract: never an exit code, never a validate
|
|
1765
|
+
warning (--strict CI must not fail on a hook that legitimately lives in
|
|
1766
|
+
git-ignored settings.local.json). Called by the spine's cmd_check AND by
|
|
1767
|
+
an overlay that replaces check with its own pipeline (the marketing lens
|
|
1768
|
+
does): the note is check-layer behaviour, whoever owns the check."""
|
|
1769
|
+
try:
|
|
1770
|
+
state = orient_hook_state(root)
|
|
1771
|
+
if state == "unwired":
|
|
1772
|
+
print("[note] agent sessions start without automatic orientation on this "
|
|
1773
|
+
"machine (no session-orientation hook found at project or user level). "
|
|
1774
|
+
"Fix once: update or reinstall the skill npm package (on Claude Code "
|
|
1775
|
+
"with Python it wires the hook machine-wide), or wire it per "
|
|
1776
|
+
"ENFORCEMENT.md par.4")
|
|
1777
|
+
elif state == "dead":
|
|
1778
|
+
print("[note] a session-orientation hook is configured but points at a "
|
|
1779
|
+
"validator that no longer exists, so sessions start unoriented. Fix: "
|
|
1780
|
+
"update or reinstall the skill npm package, or correct the command. "
|
|
1781
|
+
"Details: ENFORCEMENT.md par.4")
|
|
1782
|
+
except Exception:
|
|
1783
|
+
pass # detection must never break check
|
|
1784
|
+
|
|
1785
|
+
|
|
1649
1786
|
def cmd_check(root, strict=False, hybrid=False):
|
|
1650
1787
|
print("===== validate =====")
|
|
1651
1788
|
rc_v = cmd_validate(root, strict=strict, hybrid=hybrid)
|
|
@@ -1653,6 +1790,7 @@ def cmd_check(root, strict=False, hybrid=False):
|
|
|
1653
1790
|
rc_s = cmd_stale(root, hybrid=hybrid)
|
|
1654
1791
|
print(f"\ncheck: {'CLEAN' if not (rc_v or rc_s) else 'NOT CLEAN'} "
|
|
1655
1792
|
f"(validate rc={rc_v}, stale rc={rc_s})")
|
|
1793
|
+
print_orient_hook_note(root)
|
|
1656
1794
|
return 1 if (rc_v or rc_s) else 0
|
|
1657
1795
|
|
|
1658
1796
|
|