bigpowers 2.76.3 → 2.77.1
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/.pi/package.json +2 -2
- package/.pi/prompts/audit-code.md +1 -1
- package/.pi/prompts/build-epic.md +10 -0
- package/.pi/prompts/craft-skill.md +2 -2
- package/.pi/prompts/elaborate-spec.md +1 -1
- package/.pi/prompts/evolve-skill.md +1 -1
- package/.pi/prompts/generate-allure-report.md +284 -0
- package/.pi/prompts/grill-me.md +1 -1
- package/.pi/prompts/guard-git.md +1 -1
- package/.pi/prompts/harden-vps.md +287 -0
- package/.pi/prompts/investigate-bug.md +2 -2
- package/.pi/prompts/model-domain.md +3 -3
- package/.pi/prompts/plan-refactor.md +2 -2
- package/.pi/prompts/plan-release.md +5 -10
- package/.pi/prompts/plan-work.md +14 -1
- package/.pi/prompts/using-bigpowers.md +1 -1
- package/.pi/prompts/write-document.md +2 -2
- package/.pi/skills/audit-code/SKILL.md +1 -1
- package/.pi/skills/build-epic/SKILL.md +10 -0
- package/.pi/skills/change-request/SKILL.md +1 -1
- package/.pi/skills/commit-message/SKILL.md +3 -3
- package/.pi/skills/compose-workflow/SKILL.md +1 -1
- package/.pi/skills/craft-skill/SKILL.md +4 -4
- package/.pi/skills/deepen-architecture/SKILL.md +13 -13
- package/.pi/skills/deploy/SKILL.md +4 -4
- package/.pi/skills/develop-tdd/SKILL.md +5 -5
- package/.pi/skills/elaborate-spec/SKILL.md +1 -1
- package/.pi/skills/evolve-skill/SKILL.md +2 -2
- package/.pi/skills/extract-design/SKILL.md +1 -1
- package/.pi/skills/generate-allure-report/SKILL.md +285 -0
- package/.pi/skills/grill-me/SKILL.md +2 -2
- package/.pi/skills/grill-with-docs/SKILL.md +1 -1
- package/.pi/skills/guard-git/SKILL.md +5 -5
- package/.pi/skills/harden-vps/SKILL.md +288 -0
- package/.pi/skills/investigate-bug/SKILL.md +2 -2
- package/.pi/skills/migrate-spec/SKILL.md +13 -13
- package/.pi/skills/model-domain/SKILL.md +5 -5
- package/.pi/skills/orchestrate-project/SKILL.md +2 -2
- package/.pi/skills/organize-workspace/SKILL.md +1 -1
- package/.pi/skills/plan-refactor/SKILL.md +2 -2
- package/.pi/skills/plan-release/SKILL.md +5 -10
- package/.pi/skills/plan-work/SKILL.md +15 -2
- package/.pi/skills/publish-package/SKILL.md +8 -8
- package/.pi/skills/release-branch/SKILL.md +2 -2
- package/.pi/skills/research-first/SKILL.md +1 -1
- package/.pi/skills/run-evals/SKILL.md +1 -1
- package/.pi/skills/security-review/SKILL.md +3 -3
- package/.pi/skills/seed-conventions/SKILL.md +3 -3
- package/.pi/skills/smoke-test/SKILL.md +5 -5
- package/.pi/skills/stocktake-skills/SKILL.md +1 -1
- package/.pi/skills/using-bigpowers/SKILL.md +1 -1
- package/.pi/skills/validate-contracts/SKILL.md +6 -6
- package/.pi/skills/verify-work/SKILL.md +1 -1
- package/.pi/skills/wire-ci/SKILL.md +8 -8
- package/.pi/skills/write-document/SKILL.md +3 -3
- package/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/SKILL-INDEX.md +1 -1
- package/allure-results/categories.json +23 -0
- package/allure-results/executor.json +6 -0
- package/allure-results/junit-results.xml +2743 -0
- package/package.json +4 -3
- package/scripts/adapters/pi.sh +4 -1
- package/scripts/check-skill-links.py +78 -0
- package/scripts/generate-allure-report.sh +146 -0
- package/scripts/generate-reference-tables.sh +2 -1
- package/scripts/lib/__pycache__/link_utils.cpython-312.pyc +0 -0
- package/scripts/lib/link_utils.py +49 -0
- package/scripts/lib/srp-engine.py +86 -1
- package/scripts/record-cycle-time.sh +28 -0
- package/scripts/sync-status-from-epics.sh +247 -24
- package/scripts/validate-skill-catalog.sh +1 -1
- package/skills/audit-code/SKILL.md +1 -1
- package/skills/build-epic/SKILL.md +10 -0
- package/skills/craft-skill/SKILL.md +2 -2
- package/skills/elaborate-spec/SKILL.md +1 -1
- package/skills/evolve-skill/REFERENCE.md +1 -1
- package/skills/generate-allure-report/REFERENCE.md +243 -0
- package/skills/generate-allure-report/SKILL.md +40 -0
- package/skills/grill-me/REFERENCE.md +1 -1
- package/skills/guard-git/SKILL.md +1 -1
- package/skills/harden-vps/REFERENCE.md +179 -0
- package/skills/harden-vps/SKILL.md +107 -0
- package/skills/investigate-bug/SKILL.md +2 -2
- package/skills/model-domain/CONTEXT-FORMAT.md +3 -3
- package/skills/plan-refactor/SKILL.md +2 -2
- package/skills/plan-release/SKILL.md +5 -10
- package/skills/plan-work/REFERENCE.md +13 -0
- package/skills/plan-work/SKILL.md +1 -1
- package/skills/using-bigpowers/SKILL.md +1 -1
- package/skills/write-document/REFERENCE.md +2 -2
- package/skills-lock.json +20 -10
- package/tests/test_srp_engine.py +148 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""tests/test_srp_engine.py — unit tests for rewrite_links_for_pi and link_utils.
|
|
3
|
+
|
|
4
|
+
Run: python3 -m pytest tests/test_srp_engine.py -v
|
|
5
|
+
or: python3 tests/test_srp_engine.py
|
|
6
|
+
"""
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import unittest
|
|
10
|
+
|
|
11
|
+
# Make scripts/lib importable without installation.
|
|
12
|
+
_REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
13
|
+
_LIB = os.path.join(_REPO_ROOT, "scripts", "lib")
|
|
14
|
+
sys.path.insert(0, _LIB)
|
|
15
|
+
sys.path.insert(0, os.path.join(_REPO_ROOT, "scripts"))
|
|
16
|
+
|
|
17
|
+
# srp-engine.py has a hyphen so we load it via importlib.
|
|
18
|
+
import importlib.util as _ilu
|
|
19
|
+
_spec = _ilu.spec_from_file_location("srp_engine", os.path.join(_LIB, "srp-engine.py"))
|
|
20
|
+
_srp = _ilu.module_from_spec(_spec)
|
|
21
|
+
sys.modules["srp_engine"] = _srp
|
|
22
|
+
_spec.loader.exec_module(_srp)
|
|
23
|
+
|
|
24
|
+
from srp_engine import rewrite_links_for_pi # type: ignore[import]
|
|
25
|
+
from link_utils import strip_code_spans, LINK_RE, MACHINE_PATH_RE
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class TestStripCodeSpans(unittest.TestCase):
|
|
29
|
+
def test_fenced_block_blanked(self):
|
|
30
|
+
md = "Before.\n```\n[fake](fake.md)\n```\nAfter."
|
|
31
|
+
shadow = strip_code_spans(md)
|
|
32
|
+
self.assertNotIn("[fake](fake.md)", shadow)
|
|
33
|
+
self.assertEqual(len(shadow), len(md)) # positions preserved
|
|
34
|
+
|
|
35
|
+
def test_inline_code_blanked(self):
|
|
36
|
+
md = "Use `[x](y.md)` as an example."
|
|
37
|
+
shadow = strip_code_spans(md)
|
|
38
|
+
self.assertNotIn("[x](y.md)", shadow)
|
|
39
|
+
self.assertEqual(len(shadow), len(md))
|
|
40
|
+
|
|
41
|
+
def test_real_link_survives(self):
|
|
42
|
+
md = "See [docs](guide.md) for details."
|
|
43
|
+
shadow = strip_code_spans(md)
|
|
44
|
+
self.assertIn("[docs](guide.md)", shadow)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class TestLinkRE(unittest.TestCase):
|
|
48
|
+
def test_plain_link(self):
|
|
49
|
+
m = LINK_RE.search("[text](target.md)")
|
|
50
|
+
self.assertIsNotNone(m)
|
|
51
|
+
self.assertEqual(m.group(2), "target.md")
|
|
52
|
+
|
|
53
|
+
def test_link_with_title(self):
|
|
54
|
+
m = LINK_RE.search('[text](target.md "My Title")')
|
|
55
|
+
self.assertIsNotNone(m)
|
|
56
|
+
self.assertEqual(m.group(2), "target.md")
|
|
57
|
+
|
|
58
|
+
def test_link_with_fragment(self):
|
|
59
|
+
m = LINK_RE.search("[text](doc.md#section)")
|
|
60
|
+
self.assertIsNotNone(m)
|
|
61
|
+
self.assertEqual(m.group(2), "doc.md#section")
|
|
62
|
+
|
|
63
|
+
def test_no_match_on_image(self):
|
|
64
|
+
# Image links () contain the same syntax — LINK_RE will match
|
|
65
|
+
# the inner part; they should be treated as pass-through by callers.
|
|
66
|
+
m = LINK_RE.search("")
|
|
67
|
+
# The regex matches the inner ](url) portion — callers must handle images.
|
|
68
|
+
self.assertIsNotNone(m)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class TestMachinePathRE(unittest.TestCase):
|
|
72
|
+
def test_file_uri(self):
|
|
73
|
+
self.assertTrue(MACHINE_PATH_RE.search("file:///Users/alice/project"))
|
|
74
|
+
|
|
75
|
+
def test_macos_path(self):
|
|
76
|
+
self.assertTrue(MACHINE_PATH_RE.search("/Users/alice/project"))
|
|
77
|
+
|
|
78
|
+
def test_linux_path(self):
|
|
79
|
+
self.assertTrue(MACHINE_PATH_RE.search("/home/alice/project"))
|
|
80
|
+
|
|
81
|
+
def test_windows_path_single_backslash(self):
|
|
82
|
+
self.assertTrue(MACHINE_PATH_RE.search("C:\\Users\\alice"))
|
|
83
|
+
|
|
84
|
+
def test_github_url_not_flagged(self):
|
|
85
|
+
self.assertFalse(
|
|
86
|
+
MACHINE_PATH_RE.search("https://github.com/danielvm-git/bigpowers")
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class TestRewriteLinksForPi(unittest.TestCase):
|
|
91
|
+
"""Tests run without a real repo tree — path existence is irrelevant here;
|
|
92
|
+
we only verify that the URL construction logic is correct."""
|
|
93
|
+
|
|
94
|
+
NAME = "my-skill"
|
|
95
|
+
|
|
96
|
+
def _rw(self, body):
|
|
97
|
+
return rewrite_links_for_pi(body, self.NAME)
|
|
98
|
+
|
|
99
|
+
def test_external_link_untouched(self):
|
|
100
|
+
body = "[docs](https://example.com)"
|
|
101
|
+
self.assertEqual(self._rw(body), body)
|
|
102
|
+
|
|
103
|
+
def test_anchor_only_untouched(self):
|
|
104
|
+
body = "[section](#heading)"
|
|
105
|
+
self.assertEqual(self._rw(body), body)
|
|
106
|
+
|
|
107
|
+
def test_absolute_path_untouched(self):
|
|
108
|
+
body = "[root](/absolute/path.md)"
|
|
109
|
+
self.assertEqual(self._rw(body), body)
|
|
110
|
+
|
|
111
|
+
def test_relative_link_repointed(self):
|
|
112
|
+
body = "[ref](REFERENCE.md)"
|
|
113
|
+
result = self._rw(body)
|
|
114
|
+
# Must not contain the original bare target
|
|
115
|
+
self.assertNotIn("](REFERENCE.md)", result)
|
|
116
|
+
# Must be a relative path targeting the skills source tree
|
|
117
|
+
self.assertIn("REFERENCE.md", result)
|
|
118
|
+
|
|
119
|
+
def test_fragment_preserved(self):
|
|
120
|
+
body = "[section](REFERENCE.md#anchor)"
|
|
121
|
+
result = self._rw(body)
|
|
122
|
+
self.assertIn("#anchor", result)
|
|
123
|
+
# Fragment must not be mangled into a path component
|
|
124
|
+
self.assertNotIn("anchor" + os.sep, result)
|
|
125
|
+
|
|
126
|
+
def test_link_with_title_attribute(self):
|
|
127
|
+
body = '[ref](REFERENCE.md "My Title")'
|
|
128
|
+
result = self._rw(body)
|
|
129
|
+
# Must not be left unrewritten
|
|
130
|
+
self.assertNotIn("](REFERENCE.md", result)
|
|
131
|
+
|
|
132
|
+
def test_code_block_links_not_rewritten(self):
|
|
133
|
+
body = "Real: [ref](REFERENCE.md)\n\n```\n[fake](nowhere.md)\n```\n"
|
|
134
|
+
result = self._rw(body)
|
|
135
|
+
# The real link should be rewritten
|
|
136
|
+
self.assertNotIn("](REFERENCE.md)", result)
|
|
137
|
+
# The code-block link must be untouched
|
|
138
|
+
self.assertIn("[fake](nowhere.md)", result)
|
|
139
|
+
|
|
140
|
+
def test_inline_code_link_not_rewritten(self):
|
|
141
|
+
body = "Example: `[x](bad.md)` — real [ref](REFERENCE.md)."
|
|
142
|
+
result = self._rw(body)
|
|
143
|
+
self.assertIn("[x](bad.md)", result) # inline code preserved
|
|
144
|
+
self.assertNotIn("](REFERENCE.md)", result) # real link rewritten
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if __name__ == "__main__":
|
|
148
|
+
unittest.main()
|