wikifier 4.5.9__tar.gz → 4.6.1__tar.gz
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.
- {wikifier-4.5.9/wikifier.egg-info → wikifier-4.6.1}/PKG-INFO +1 -1
- {wikifier-4.5.9 → wikifier-4.6.1}/skills/run.md +19 -15
- wikifier-4.6.1/tests/test_agent_loop.py +226 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/__init__.py +6 -1
- wikifier-4.6.1/wikifier/agent_loop.py +486 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/cli.py +215 -12
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/health.py +93 -1
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/mcp/server.py +67 -19
- {wikifier-4.5.9 → wikifier-4.6.1/wikifier.egg-info}/PKG-INFO +1 -1
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier.egg-info/SOURCES.txt +3 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/CONTRIBUTING.md +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/LICENSE +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/MANIFEST.in +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/README.md +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/diagnostics.html +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/docs/spec.md +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/index.html +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/pyproject.toml +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/setup.cfg +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/tests/test_barrel_invalidation.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/tests/test_gap_closure.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/tests/test_health.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/tests/test_import_cache.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/tests/test_multi_lang_parsers.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/tests/test_parsers.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/tests/test_selftest_wrappers.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/__main__.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/contracts.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/daemon.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/diagnostics.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/import_cache.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/index.html +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/library.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/locking.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/mcp/__init__.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/parsers/__init__.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/parsers/_edge.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/parsers/bree.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/parsers/c_cpp.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/parsers/cdia.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/parsers/csharp.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/parsers/go_lang.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/parsers/java.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/parsers/javascript.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/parsers/python.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/parsers/rust.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/project_root.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/resolution.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/scripts/wikifier.bat +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/scripts/wikifier.ps1 +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/scripts/wikifier.sh +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier/serve.py +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier.egg-info/dependency_links.txt +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier.egg-info/entry_points.txt +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier.egg-info/requires.txt +0 -0
- {wikifier-4.5.9 → wikifier-4.6.1}/wikifier.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: wikifier
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.6.1
|
|
4
4
|
Summary: Zero-dependency agent-to-agent codebase wiki for LLMs and AI agents. Autonomously maintained via record-change and mark-green for token-efficient lookup of files, dependencies, health, and summaries — across tiny scripts to 50k+ monorepos. Optional MCP server with rich tools for agents.
|
|
5
5
|
Author-email: Aron Amos <aron@example.com>
|
|
6
6
|
Maintainer: Aron Amos
|
|
@@ -42,13 +42,16 @@ Do **not** open megamodules (`javascript.py`, `import_cache.py`, `bree.py`) to d
|
|
|
42
42
|
| Need | Use |
|
|
43
43
|
|------|-----|
|
|
44
44
|
| First-run map | `init` → `update-maps` → `health --summary` → `suggest-next` |
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
45
|
+
| **Session start (4.6+)** | `session_bootstrap` (or CLI `session-bootstrap`) — one shot: root, health, attention, `actions[]` |
|
|
46
|
+
| Steady-state | `check-changes` (content-honest) → edit 🔴/actionable 🟡 only → `record-change` → wiki → `mark-green` |
|
|
47
|
+
| **Preflight edit** | `prepare_edit` / CLI `prepare-edit <file>` — wiki + status + deps + dependents |
|
|
48
|
+
| **Why / journal** | `why_file` / `search_journal` — semantic trail, not mtime theater |
|
|
49
|
+
| Lookup Core | `session_bootstrap`, `check_changes`, `prepare_edit`, `suggest_next_actions` (json `actions[]`), `record_change`, `mark_green` |
|
|
50
|
+
| Advanced intel | `get_dependencies`, `get_dependents`, `get_cycles`, barrel/diagnostics — not daily Core |
|
|
48
51
|
| Deletion | `record-deletion` |
|
|
49
52
|
| Long-horizon prep | `autonomous-status` / `readiness`; then `daemon start` with lean monitor |
|
|
50
53
|
| Hygiene | `seed-health`, `prune-pending`, `prune-health`, `validate` |
|
|
51
|
-
| Code entry | `cli.py`
|
|
54
|
+
| Code entry | `cli.py` + `agent_loop.py` + `health.py` + parsers — tests in `tests/` |
|
|
52
55
|
|
|
53
56
|
**Do not open megamodules** (`javascript.py` ~2.6k, `import_cache.py` ~2.3k, `bree.py` ~2k) for workflow decisions — use tools + this table.
|
|
54
57
|
|
|
@@ -61,30 +64,31 @@ Do **not** open megamodules (`javascript.py`, `import_cache.py`, `bree.py`) to d
|
|
|
61
64
|
**Copy this exact block into the system prompt or the very first instruction of every new LLM session that uses Wikifier:**
|
|
62
65
|
|
|
63
66
|
```
|
|
64
|
-
You are now operating inside a Wikifier-managed codebase (Agent Protocol v0.6 — package 4.
|
|
67
|
+
You are now operating inside a Wikifier-managed codebase (Agent Protocol v0.6 — package 4.6.x).
|
|
65
68
|
|
|
66
69
|
This is strictly an agent-to-agent wiki for token saving: map lookup (health + file wikis + deps) instead of full sources; selective wiki updates; not a human docs product or Jira.
|
|
67
70
|
|
|
68
|
-
SELECTIVE WORK (mandatory): Only update/remove/re-wiki 🔴 Red and 🟡 Yellow files. Do not re-summarize 🟢 Green files or the whole tree. First-run builds the structural map; wiki depth is filled as you touch files.
|
|
71
|
+
SELECTIVE WORK (mandatory): Only update/remove/re-wiki 🔴 Red and 🟡 Yellow files. Do not re-summarize 🟢 Green files or the whole tree. First-run builds the structural map; wiki depth is filled as you touch files. check-changes is content-honest (mtime-only thrash does not re-Yellow when source hash matches mark-green baseline).
|
|
69
72
|
|
|
70
73
|
FIRST ACTIONS:
|
|
71
|
-
1. Prefer MCP
|
|
74
|
+
1. Prefer MCP Core: session_bootstrap, check_changes, prepare_edit, suggest_next_actions (use actions[]), record_change, mark_green. Always pass project_root= for external projects.
|
|
72
75
|
2. CLI/library fallback:
|
|
73
|
-
WIKIFIER_PROJECT_ROOT=/path/to/target python -m wikifier
|
|
74
|
-
python -m wikifier
|
|
75
|
-
python -m wikifier suggest-next #
|
|
76
|
-
|
|
76
|
+
WIKIFIER_PROJECT_ROOT=/path/to/target python -m wikifier session-bootstrap
|
|
77
|
+
python -m wikifier check-changes
|
|
78
|
+
python -m wikifier suggest-next --json # actions[] dispatchable
|
|
79
|
+
python -m wikifier prepare-edit path/to/file
|
|
80
|
+
... edit only red/actionable yellow sources ...
|
|
77
81
|
python -m wikifier record-change "path/to/file" "why (semantic). Include subid if agent work."
|
|
78
82
|
... update that file's wiki only ...
|
|
79
83
|
python -m wikifier mark-green "path/to/file" "reason"
|
|
80
84
|
if imports changed: python -m wikifier update-maps [--directory=src/]
|
|
81
85
|
if file removed: python -m wikifier record-deletion "path" "why"
|
|
82
86
|
python -m wikifier health --summary
|
|
83
|
-
3. Optional background: `python -m wikifier monitor` or daemon
|
|
84
|
-
4. Prioritize 🔴 then
|
|
85
|
-
5.
|
|
87
|
+
3. Optional background: `python -m wikifier monitor` or daemon — you still own wiki + mark-green.
|
|
88
|
+
4. Prioritize 🔴 then actionable 🟡. Lookup greens via prepare_edit / get_file_wiki; do not rewrite them.
|
|
89
|
+
5. why_file / search_journal for semantic trail.
|
|
86
90
|
6. Always explicit project_root / WIKIFIER_PROJECT_ROOT for external/multi-project work.
|
|
87
|
-
7. End turn: health + suggest_next_actions.
|
|
91
|
+
7. End turn: health + suggest_next_actions (json).
|
|
88
92
|
|
|
89
93
|
Never skip record-change — semantic audit trail (journal + health + pending).
|
|
90
94
|
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""Agent-first ideal-loop tests: content-dirty, bootstrap, suggest actions, preflight, journal."""
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import os
|
|
5
|
+
import time
|
|
6
|
+
import unittest
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from tests._base import TempProjectTestCase
|
|
10
|
+
|
|
11
|
+
from wikifier import cli
|
|
12
|
+
from wikifier.health import classify_content_dirty, compute_source_content_hash
|
|
13
|
+
from wikifier.agent_loop import (
|
|
14
|
+
session_bootstrap,
|
|
15
|
+
prepare_edit,
|
|
16
|
+
search_journal,
|
|
17
|
+
why_file,
|
|
18
|
+
build_structured_actions,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
health_mod = importlib.import_module("wikifier.health")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TestContentHonestDirty(TempProjectTestCase):
|
|
25
|
+
def test_classify_touch_only_vs_rewrite(self):
|
|
26
|
+
p = self.write("src/a.py", "x = 1\n")
|
|
27
|
+
h1 = compute_source_content_hash(p)
|
|
28
|
+
self.assertIsNotNone(h1)
|
|
29
|
+
v = classify_content_dirty(p, h1)
|
|
30
|
+
self.assertFalse(v["content_dirty"])
|
|
31
|
+
self.assertEqual(v["reason"], "content_unchanged")
|
|
32
|
+
# mtime thrash without content change
|
|
33
|
+
os.utime(p, None)
|
|
34
|
+
v2 = classify_content_dirty(p, h1)
|
|
35
|
+
self.assertFalse(v2["content_dirty"])
|
|
36
|
+
self.assertEqual(v2["reason"], "content_unchanged")
|
|
37
|
+
# rewrite bytes
|
|
38
|
+
p.write_text("x = 2\n", encoding="utf-8")
|
|
39
|
+
v3 = classify_content_dirty(p, h1)
|
|
40
|
+
self.assertTrue(v3["content_dirty"])
|
|
41
|
+
self.assertEqual(v3["reason"], "content_changed")
|
|
42
|
+
|
|
43
|
+
def test_check_changes_skips_mtime_only_after_mark_green(self):
|
|
44
|
+
p = self.write("mod.py", "print(1)\n")
|
|
45
|
+
# Seed health green with source baseline via mark_green
|
|
46
|
+
res_mg = cli.mark_green("mod.py", "baseline", project_root=self.root)
|
|
47
|
+
self.assertTrue(res_mg.get("success"), res_mg)
|
|
48
|
+
data = health_mod.load_health(self.root)
|
|
49
|
+
ent = data["entries"]["mod.py"]
|
|
50
|
+
self.assertIn("source_content_hash", ent)
|
|
51
|
+
baseline = ent["source_content_hash"]
|
|
52
|
+
# Ensure import_cache may list file as dirty via mtime: touch only
|
|
53
|
+
from wikifier import import_cache as ic
|
|
54
|
+
cache = {}
|
|
55
|
+
ic.update_file_data(cache, "mod.py", mtime=1, imports=[], resolved_pairs=[])
|
|
56
|
+
ic.save_cache(self.root, cache)
|
|
57
|
+
time.sleep(0.05)
|
|
58
|
+
os.utime(p, None)
|
|
59
|
+
# Force compute dirty by setting cache mtime older than file
|
|
60
|
+
cache = ic.load_cache(self.root)
|
|
61
|
+
cache["mod.py"]["mtime"] = 1
|
|
62
|
+
ic.save_cache(self.root, cache)
|
|
63
|
+
r = cli.check_changes(project_root=self.root)
|
|
64
|
+
self.assertTrue(r.get("success"), r)
|
|
65
|
+
# Content unchanged → should not Yellow for content
|
|
66
|
+
data2 = health_mod.load_health(self.root)
|
|
67
|
+
st = data2["entries"]["mod.py"]["status"]
|
|
68
|
+
self.assertIn("Green", st)
|
|
69
|
+
self.assertEqual(data2["entries"]["mod.py"].get("source_content_hash"), baseline)
|
|
70
|
+
# Real content edit → Yellow
|
|
71
|
+
p.write_text("print(2)\n", encoding="utf-8")
|
|
72
|
+
cache = ic.load_cache(self.root)
|
|
73
|
+
cache["mod.py"]["mtime"] = 1
|
|
74
|
+
ic.save_cache(self.root, cache)
|
|
75
|
+
r2 = cli.check_changes(project_root=self.root)
|
|
76
|
+
self.assertTrue(r2.get("success"), r2)
|
|
77
|
+
data3 = health_mod.load_health(self.root)
|
|
78
|
+
self.assertIn("Yellow", data3["entries"]["mod.py"]["status"])
|
|
79
|
+
self.assertIn("content", data3["entries"]["mod.py"]["reason"].lower())
|
|
80
|
+
|
|
81
|
+
def test_legacy_green_without_hash_rewrite_yellows(self):
|
|
82
|
+
"""Criterion 2: Green with no source_content_hash + real rewrite must Yellow.
|
|
83
|
+
|
|
84
|
+
Must not seed post-edit hash and stay Green (post-upgrade / legacy path).
|
|
85
|
+
"""
|
|
86
|
+
p = self.write("legacy.py", "print('v1')\n")
|
|
87
|
+
# Legacy Green: upsert only — no source_content_hash baseline
|
|
88
|
+
health_mod.upsert_entry(self.root, "legacy.py", "🟢 Green", "legacy green no hash")
|
|
89
|
+
data = health_mod.load_health(self.root)
|
|
90
|
+
self.assertNotIn("source_content_hash", data["entries"]["legacy.py"] or {})
|
|
91
|
+
from wikifier import import_cache as ic
|
|
92
|
+
cache = {}
|
|
93
|
+
ic.update_file_data(cache, "legacy.py", mtime=1, imports=[], resolved_pairs=[])
|
|
94
|
+
ic.save_cache(self.root, cache)
|
|
95
|
+
# Real content rewrite
|
|
96
|
+
p.write_text("print('v2-rewritten')\n", encoding="utf-8")
|
|
97
|
+
cache = ic.load_cache(self.root)
|
|
98
|
+
cache["legacy.py"]["mtime"] = 1 # force dirty vs disk
|
|
99
|
+
ic.save_cache(self.root, cache)
|
|
100
|
+
r = cli.check_changes(project_root=self.root)
|
|
101
|
+
self.assertTrue(r.get("success"), r)
|
|
102
|
+
self.assertGreaterEqual(int(r.get("changes_detected") or 0), 1)
|
|
103
|
+
data2 = health_mod.load_health(self.root)
|
|
104
|
+
ent = data2["entries"]["legacy.py"]
|
|
105
|
+
self.assertIn("Yellow", ent["status"])
|
|
106
|
+
# Must not have silently stored the post-edit hash as trusted baseline
|
|
107
|
+
self.assertNotEqual(
|
|
108
|
+
ent.get("source_content_hash"),
|
|
109
|
+
compute_source_content_hash(p),
|
|
110
|
+
"must not seed post-edit content hash without mark_green",
|
|
111
|
+
)
|
|
112
|
+
# After mark_green, baseline is set and further touch-only stays green
|
|
113
|
+
cli.mark_green("legacy.py", "re-baselined", project_root=self.root)
|
|
114
|
+
data3 = health_mod.load_health(self.root)
|
|
115
|
+
self.assertIn("source_content_hash", data3["entries"]["legacy.py"])
|
|
116
|
+
baseline = data3["entries"]["legacy.py"]["source_content_hash"]
|
|
117
|
+
cache = ic.load_cache(self.root)
|
|
118
|
+
cache["legacy.py"]["mtime"] = 1
|
|
119
|
+
ic.save_cache(self.root, cache)
|
|
120
|
+
os.utime(p, None)
|
|
121
|
+
r2 = cli.check_changes(project_root=self.root)
|
|
122
|
+
self.assertTrue(r2.get("success"), r2)
|
|
123
|
+
data4 = health_mod.load_health(self.root)
|
|
124
|
+
self.assertIn("Green", data4["entries"]["legacy.py"]["status"])
|
|
125
|
+
self.assertEqual(data4["entries"]["legacy.py"]["source_content_hash"], baseline)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class TestSessionBootstrapAndSuggest(TempProjectTestCase):
|
|
129
|
+
def test_session_bootstrap_shape(self):
|
|
130
|
+
self.write("a.py", "import os\n")
|
|
131
|
+
health_mod.upsert_entry(self.root, "a.py", "🟡 Yellow", "record-change: edit")
|
|
132
|
+
boot = session_bootstrap(project_root=self.root)
|
|
133
|
+
self.assertTrue(boot.get("success"))
|
|
134
|
+
self.assertEqual(Path(boot["project_root"]).resolve(), self.root)
|
|
135
|
+
self.assertIn("health_summary", boot)
|
|
136
|
+
self.assertIn("actions", boot)
|
|
137
|
+
self.assertIsInstance(boot["actions"], list)
|
|
138
|
+
self.assertTrue(any("action" in a for a in boot["actions"]))
|
|
139
|
+
self.assertIn("session_bootstrap", boot.get("core_surface") or [])
|
|
140
|
+
|
|
141
|
+
def test_suggest_json_has_structured_actions(self):
|
|
142
|
+
health_mod.upsert_entry(self.root, "b.py", "🔴 Red", "DELETED — gone")
|
|
143
|
+
health_mod.upsert_entry(self.root, "c.py", "🟡 Yellow", "content changed")
|
|
144
|
+
res = cli.suggest_next_actions(project_root=self.root, format="json")
|
|
145
|
+
self.assertTrue(res.get("success"))
|
|
146
|
+
actions = res.get("actions") or []
|
|
147
|
+
self.assertTrue(actions, "expected dispatchable actions")
|
|
148
|
+
for a in actions:
|
|
149
|
+
self.assertIn("action", a)
|
|
150
|
+
self.assertIn("priority", a)
|
|
151
|
+
self.assertIn("reason", a)
|
|
152
|
+
kinds = {a["action"] for a in actions}
|
|
153
|
+
self.assertTrue(kinds & {"investigate_red", "wiki_refresh", "review_acs", "update_maps_if_structure", "lookup_only", "map_first_ok", "fix_scope"})
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class TestPrepareEditAndJournal(TempProjectTestCase):
|
|
157
|
+
def test_prepare_edit_returns_status_and_lists(self):
|
|
158
|
+
self.write("pkg/x.py", "from . import y\n")
|
|
159
|
+
health_mod.upsert_entry(self.root, "pkg/x.py", "🟢 Green", "ok")
|
|
160
|
+
# minimal cache edge
|
|
161
|
+
from wikifier import import_cache as ic
|
|
162
|
+
cache = {}
|
|
163
|
+
ic.update_file_data(
|
|
164
|
+
cache,
|
|
165
|
+
"pkg/x.py",
|
|
166
|
+
mtime=1,
|
|
167
|
+
imports=["./y"],
|
|
168
|
+
resolved_pairs=[{
|
|
169
|
+
"raw": "./y",
|
|
170
|
+
"resolved": "pkg/y.py",
|
|
171
|
+
"resolved_path": "pkg/y.py",
|
|
172
|
+
"confidence_score": 0.9,
|
|
173
|
+
}],
|
|
174
|
+
)
|
|
175
|
+
cache["_reverse_dependencies"] = {"pkg/x.py": ["pkg/z.py"]}
|
|
176
|
+
ic.save_cache(self.root, cache)
|
|
177
|
+
pe = prepare_edit("pkg/x.py", project_root=self.root)
|
|
178
|
+
self.assertTrue(pe.get("success"))
|
|
179
|
+
self.assertEqual(pe.get("file"), "pkg/x.py")
|
|
180
|
+
self.assertIn("Green", str(pe.get("status") or ""))
|
|
181
|
+
self.assertTrue(isinstance(pe.get("dependencies"), list))
|
|
182
|
+
self.assertIn("pkg/z.py", pe.get("dependents") or [])
|
|
183
|
+
|
|
184
|
+
def test_journal_search_and_why_file(self):
|
|
185
|
+
self.write("tracked.py", "v=1\n")
|
|
186
|
+
r = cli.record_change("tracked.py", "semantic reason alpha-subid", project_root=self.root)
|
|
187
|
+
self.assertTrue(r.get("success"), r)
|
|
188
|
+
sj = search_journal(project_root=self.root, query="alpha-subid")
|
|
189
|
+
self.assertTrue(sj.get("success"))
|
|
190
|
+
self.assertGreaterEqual(len(sj.get("matches") or []), 1)
|
|
191
|
+
self.assertTrue(any("alpha-subid" in (m.get("reason") or "") for m in sj["matches"]))
|
|
192
|
+
sj2 = search_journal(project_root=self.root, file="tracked.py")
|
|
193
|
+
self.assertGreaterEqual(len(sj2.get("matches") or []), 1)
|
|
194
|
+
why = why_file("tracked.py", project_root=self.root)
|
|
195
|
+
self.assertTrue(why.get("success"))
|
|
196
|
+
self.assertTrue(why.get("health_reason") or why.get("journal_matches"))
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class TestBuildActionsUnit(unittest.TestCase):
|
|
200
|
+
def test_build_structured_actions_priorities(self):
|
|
201
|
+
acts = build_structured_actions(
|
|
202
|
+
red_files=["a.py"],
|
|
203
|
+
actionable_yellow_files=["b.py"],
|
|
204
|
+
red=1,
|
|
205
|
+
actionable_yellow=1,
|
|
206
|
+
acs_actionable=3,
|
|
207
|
+
clean=False,
|
|
208
|
+
)
|
|
209
|
+
self.assertTrue(any(a["action"] == "investigate_red" and a["file"] == "a.py" for a in acts))
|
|
210
|
+
self.assertTrue(any(a["action"] == "wiki_refresh" and a["file"] == "b.py" for a in acts))
|
|
211
|
+
self.assertTrue(any(a["action"] == "review_acs" for a in acts))
|
|
212
|
+
# sorted by priority
|
|
213
|
+
pris = [a["priority"] for a in acts]
|
|
214
|
+
self.assertEqual(pris, sorted(pris))
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class TestCliBootstrapEntry(TempProjectTestCase):
|
|
218
|
+
def test_cli_session_bootstrap_function(self):
|
|
219
|
+
self.write("z.py", "pass\n")
|
|
220
|
+
res = cli.session_bootstrap(project_root=self.root)
|
|
221
|
+
self.assertTrue(res.get("success"))
|
|
222
|
+
self.assertIn("actions", res)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
if __name__ == "__main__":
|
|
226
|
+
unittest.main()
|
|
@@ -42,7 +42,12 @@ from .cli import (
|
|
|
42
42
|
suggest_next_actions,
|
|
43
43
|
update_maps,
|
|
44
44
|
health, # flat convenience func (delegates to the health module + adds scoping)
|
|
45
|
+
session_bootstrap,
|
|
46
|
+
prepare_edit,
|
|
47
|
+
search_journal,
|
|
48
|
+
why_file,
|
|
45
49
|
)
|
|
50
|
+
from . import agent_loop
|
|
46
51
|
|
|
47
52
|
# Re-export module under a non-shadowed name (G5). sys.modules['wikifier.health']
|
|
48
53
|
# remains the real module; package attribute `health` is intentionally the function.
|
|
@@ -71,4 +76,4 @@ from .contracts import (
|
|
|
71
76
|
compute_acs_confidence,
|
|
72
77
|
)
|
|
73
78
|
|
|
74
|
-
__version__ = "4.
|
|
79
|
+
__version__ = "4.6.1"
|