wikifier 4.6.0__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.
Files changed (56) hide show
  1. {wikifier-4.6.0/wikifier.egg-info → wikifier-4.6.1}/PKG-INFO +1 -1
  2. {wikifier-4.6.0 → wikifier-4.6.1}/tests/test_agent_loop.py +46 -0
  3. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/__init__.py +1 -1
  4. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/cli.py +5 -19
  5. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/health.py +6 -2
  6. {wikifier-4.6.0 → wikifier-4.6.1/wikifier.egg-info}/PKG-INFO +1 -1
  7. {wikifier-4.6.0 → wikifier-4.6.1}/CONTRIBUTING.md +0 -0
  8. {wikifier-4.6.0 → wikifier-4.6.1}/LICENSE +0 -0
  9. {wikifier-4.6.0 → wikifier-4.6.1}/MANIFEST.in +0 -0
  10. {wikifier-4.6.0 → wikifier-4.6.1}/README.md +0 -0
  11. {wikifier-4.6.0 → wikifier-4.6.1}/diagnostics.html +0 -0
  12. {wikifier-4.6.0 → wikifier-4.6.1}/docs/spec.md +0 -0
  13. {wikifier-4.6.0 → wikifier-4.6.1}/index.html +0 -0
  14. {wikifier-4.6.0 → wikifier-4.6.1}/pyproject.toml +0 -0
  15. {wikifier-4.6.0 → wikifier-4.6.1}/setup.cfg +0 -0
  16. {wikifier-4.6.0 → wikifier-4.6.1}/skills/run.md +0 -0
  17. {wikifier-4.6.0 → wikifier-4.6.1}/tests/test_barrel_invalidation.py +0 -0
  18. {wikifier-4.6.0 → wikifier-4.6.1}/tests/test_gap_closure.py +0 -0
  19. {wikifier-4.6.0 → wikifier-4.6.1}/tests/test_health.py +0 -0
  20. {wikifier-4.6.0 → wikifier-4.6.1}/tests/test_import_cache.py +0 -0
  21. {wikifier-4.6.0 → wikifier-4.6.1}/tests/test_multi_lang_parsers.py +0 -0
  22. {wikifier-4.6.0 → wikifier-4.6.1}/tests/test_parsers.py +0 -0
  23. {wikifier-4.6.0 → wikifier-4.6.1}/tests/test_selftest_wrappers.py +0 -0
  24. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/__main__.py +0 -0
  25. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/agent_loop.py +0 -0
  26. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/contracts.py +0 -0
  27. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/daemon.py +0 -0
  28. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/diagnostics.py +0 -0
  29. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/import_cache.py +0 -0
  30. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/index.html +0 -0
  31. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/library.py +0 -0
  32. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/locking.py +0 -0
  33. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/mcp/__init__.py +0 -0
  34. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/mcp/server.py +0 -0
  35. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/parsers/__init__.py +0 -0
  36. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/parsers/_edge.py +0 -0
  37. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/parsers/bree.py +0 -0
  38. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/parsers/c_cpp.py +0 -0
  39. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/parsers/cdia.py +0 -0
  40. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/parsers/csharp.py +0 -0
  41. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/parsers/go_lang.py +0 -0
  42. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/parsers/java.py +0 -0
  43. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/parsers/javascript.py +0 -0
  44. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/parsers/python.py +0 -0
  45. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/parsers/rust.py +0 -0
  46. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/project_root.py +0 -0
  47. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/resolution.py +0 -0
  48. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/scripts/wikifier.bat +0 -0
  49. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/scripts/wikifier.ps1 +0 -0
  50. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/scripts/wikifier.sh +0 -0
  51. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier/serve.py +0 -0
  52. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier.egg-info/SOURCES.txt +0 -0
  53. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier.egg-info/dependency_links.txt +0 -0
  54. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier.egg-info/entry_points.txt +0 -0
  55. {wikifier-4.6.0 → wikifier-4.6.1}/wikifier.egg-info/requires.txt +0 -0
  56. {wikifier-4.6.0 → 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.6.0
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
@@ -78,6 +78,52 @@ class TestContentHonestDirty(TempProjectTestCase):
78
78
  self.assertIn("Yellow", data3["entries"]["mod.py"]["status"])
79
79
  self.assertIn("content", data3["entries"]["mod.py"]["reason"].lower())
80
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
+
81
127
 
82
128
  class TestSessionBootstrapAndSuggest(TempProjectTestCase):
83
129
  def test_session_bootstrap_shape(self):
@@ -76,4 +76,4 @@ from .contracts import (
76
76
  compute_acs_confidence,
77
77
  )
78
78
 
79
- __version__ = "4.6.0"
79
+ __version__ = "4.6.1"
@@ -1208,48 +1208,34 @@ def check_changes(project_root: Optional[Union[str, Path]] = None) -> Dict[str,
1208
1208
  if stored_hash and live and stored_hash == live:
1209
1209
  verdict = {"content_dirty": False, "reason": "content_unchanged", "seed_baseline": False, "hash": live}
1210
1210
  elif not stored_hash and live:
1211
- verdict = {"content_dirty": False, "reason": "no_baseline", "seed_baseline": True, "hash": live}
1211
+ # no baseline dirty (do not seed post-edit hash)
1212
+ verdict = {"content_dirty": True, "reason": "no_baseline", "seed_baseline": False, "hash": live}
1212
1213
  elif live and stored_hash and stored_hash != live:
1213
1214
  verdict = {"content_dirty": True, "reason": "content_changed", "seed_baseline": False, "hash": live}
1214
1215
  except Exception:
1215
1216
  pass
1216
1217
 
1217
- if verdict.get("seed_baseline") and verdict.get("hash") and isinstance(ent, dict):
1218
- ent["source_content_hash"] = verdict["hash"]
1219
- entries[rel] = ent
1220
- health_dirty = True
1221
- seeded_baselines += 1
1222
- # Do not Yellow solely for first baseline seed on mtime thrash
1223
- continue
1224
1218
  if not verdict.get("content_dirty") and verdict.get("reason") == "content_unchanged":
1225
1219
  skipped_mtime_only += 1
1226
1220
  continue
1227
- # Content changed (or unclassifiable): Yellow with content reason
1221
+ # Content changed, no baseline, or unclassifiable: Yellow.
1222
+ # Never write source_content_hash here — only mark_green sets the
1223
+ # trusted baseline (avoids seeding post-edit bytes and staying Green).
1228
1224
  reason = (
1229
1225
  "content changed since last trusted baseline (check_changes content-honest)"
1230
1226
  if verdict.get("reason") == "content_changed"
1231
1227
  else "content change or no baseline (check_changes content-honest auto-detect)"
1232
1228
  )
1233
1229
  _upsert(root, rel, "🟡 Yellow", reason)
1234
- # refresh local entries view after upsert
1235
1230
  try:
1236
1231
  health_data = _health_mod.load_health(root)
1237
1232
  entries = health_data.setdefault("entries", {})
1238
- if verdict.get("hash") and rel in entries and isinstance(entries[rel], dict):
1239
- # keep prior baseline until mark-green; do not update hash on yellow
1240
- pass
1241
1233
  health_dirty = False # upsert already saved
1242
1234
  except Exception:
1243
1235
  pass
1244
1236
  _add_to_pending(root, rel, "Content change auto-detected — review and run mark-green after wiki update")
1245
1237
  _ensure_journal_entry(root, "auto-detected", rel, reason)
1246
1238
  changed_count += 1
1247
- if health_dirty and health_data is not None:
1248
- try:
1249
- if hasattr(_health_mod, "save_health"):
1250
- _health_mod.save_health(root, health_data)
1251
- except Exception:
1252
- pass
1253
1239
  # Keep pending queue aligned with lean monitored_paths (no flood outside scope)
1254
1240
  if hasattr(_health_mod, "prune_pending_to_monitored"):
1255
1241
  try:
@@ -272,12 +272,16 @@ def classify_content_dirty(
272
272
  "seed_baseline": False,
273
273
  }
274
274
  if not stored_hash:
275
+ # No trusted baseline (legacy Green / never mark-green): when this path is
276
+ # under check_changes dirty observation, treat as needing attention so a
277
+ # post-edit rewrite cannot silently re-seed the *new* bytes and stay Green.
278
+ # Callers that only want to migrate baselines should use an explicit seed helper.
275
279
  return {
276
- "content_dirty": False,
280
+ "content_dirty": True,
277
281
  "missing": False,
278
282
  "hash": live,
279
283
  "reason": "no_baseline",
280
- "seed_baseline": True,
284
+ "seed_baseline": False,
281
285
  }
282
286
  if stored_hash == live:
283
287
  return {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: wikifier
3
- Version: 4.6.0
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
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes