sourcecode 0.48.0__py3-none-any.whl → 1.0.0__py3-none-any.whl

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.
sourcecode/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "0.48.0"
3
+ __version__ = "1.0.0"
sourcecode/cli.py CHANGED
@@ -1445,8 +1445,10 @@ def main(
1445
1445
  data = agent_view(sm)
1446
1446
  # When contract pipeline ran (mode=contract, no legacy flags), include
1447
1447
  # per-file contracts in agent output so agents get structural context.
1448
+ # Remove file_relevance — contracts cover this signal with more detail.
1448
1449
  if _is_contract_mode and sm.file_contracts:
1449
1450
  from sourcecode.serializer import _serialize_contract_minimal
1451
+ data.pop("file_relevance", None)
1450
1452
  data["contracts"] = [_serialize_contract_minimal(c) for c in sm.file_contracts]
1451
1453
  if sm.contract_summary is not None:
1452
1454
  cs = sm.contract_summary
@@ -27,8 +27,8 @@ from sourcecode.schema import EntryPoint, MonorepoPackageInfo
27
27
  # ---------------------------------------------------------------------------
28
28
 
29
29
  _MAX_FILES = 500 # hard cap on files extracted per run
30
- _MAX_CONTRACTS = 25 # default top-N output cap — omit rather than flood
31
- _MIN_CONTRACT_SCORE = 0.10 # drop contracts below this relevance threshold
30
+ _MAX_CONTRACTS = 15 # default top-N output cap — omit rather than flood
31
+ _MIN_CONTRACT_SCORE = 0.15 # drop contracts below this relevance threshold
32
32
  _SRC_EXTENSIONS: frozenset[str] = frozenset(_LANGUAGE_MAP.keys())
33
33
 
34
34
 
sourcecode/serializer.py CHANGED
@@ -350,49 +350,72 @@ def _section_confidence(sm: SourceMap) -> dict[str, str]:
350
350
 
351
351
 
352
352
  def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
353
- """Context package ready for prompt or handoff (~600-800 tokens).
353
+ """Context package ready for prompt or handoff (~300-500 tokens).
354
354
 
355
355
  Answers: what it is, where it enters, what depends on what,
356
356
  what signals matter, and what uncertainty exists.
357
357
 
358
358
  Includes: project_type, project_summary, architecture_summary,
359
- stacks, entry_points, dependency_summary + key_dependencies (when analyzed),
359
+ stacks (minimal), entry_points (path+kind only), key_dependencies (name+version+role),
360
360
  env_summary (when analyzed), code_notes_summary (when analyzed),
361
- confidence_summary, anomalies, analysis_gaps.
361
+ confidence (overall only), analysis_gaps.
362
362
 
363
- Excludes: file_tree, raw dependency lists, docs, module_graph.
364
- Empty sections are explained when relevant.
363
+ Excludes: file_tree, raw dependency lists, docs, module_graph, verbose metadata.
365
364
  """
366
- dep_summary_dict: Any = None
365
+ # Key dependencies — name + version + role only (no ecosystem, source, manifests)
367
366
  key_deps: Any = None
368
367
  if sm.dependency_summary is not None and sm.dependency_summary.requested:
369
- dep_summary_dict = asdict(sm.dependency_summary)
370
- dep_summary_dict.pop("dependencies", None)
371
368
  key_deps = [
372
- asdict(d) for d in sm.key_dependencies
369
+ {
370
+ "name": d.name,
371
+ **({"version": d.declared_version} if d.declared_version else {}),
372
+ **({"role": d.role} if d.role and d.role != "runtime" else {}),
373
+ }
374
+ for d in sm.key_dependencies
373
375
  if (d.role or "unknown") in _PRODUCTION_DEP_ROLES and d.scope not in {"dev"}
374
376
  ][:_KEY_DEPS_CAP]
375
- elif sm.dependency_summary is None or not sm.dependency_summary.requested:
376
- dep_summary_dict = None # "not analyzed" — agent should add --dependencies
377
377
 
378
+ # Dependency summary — requested flag + count + source only
379
+ dep_summary_dict: Any = None
380
+ if sm.dependency_summary is not None and sm.dependency_summary.requested:
381
+ ds = sm.dependency_summary
382
+ dep_summary_dict = {
383
+ "requested": True,
384
+ "total_count": ds.total_count,
385
+ "direct": ds.direct_count,
386
+ **({"sources": ds.sources} if ds.sources else {}),
387
+ }
388
+
389
+ # Env map — key + required + category only (drop type_hint, files list)
378
390
  env_summary_dict: Any = None
379
391
  env_map_items: Any = None
380
392
  if sm.env_summary is not None and sm.env_summary.requested:
381
- env_summary_dict = asdict(sm.env_summary)
393
+ env_summary_dict = {
394
+ "total": sm.env_summary.total,
395
+ "required": sm.env_summary.required_count,
396
+ **({"categories": sm.env_summary.categories} if sm.env_summary.categories else {}),
397
+ }
382
398
  if sm.env_map:
383
399
  _sorted_env = sorted(
384
400
  sm.env_map,
385
401
  key=lambda e: (not getattr(e, "required", False), getattr(e, "key", "")),
386
402
  )
387
403
  env_map_items = [
388
- {k: v for k, v in asdict(e).items() if v is not None and v != "" and v != []}
404
+ {
405
+ "key": getattr(e, "key", ""),
406
+ **({"required": True} if getattr(e, "required", False) else {}),
407
+ **({"category": getattr(e, "category", None)} if getattr(e, "category", None) else {}),
408
+ }
389
409
  for e in _sorted_env[:_ENV_MAP_CAP]
390
410
  ]
391
411
 
412
+ # Code notes — kind + path + line + truncated text only
392
413
  code_notes_summary_dict: Any = None
393
414
  code_notes_items: Any = None
394
415
  if sm.code_notes_summary is not None and sm.code_notes_summary.requested:
395
- code_notes_summary_dict = asdict(sm.code_notes_summary)
416
+ cn = sm.code_notes_summary
417
+ by_kind = {k: v for k, v in cn.by_kind.items() if v > 0}
418
+ code_notes_summary_dict = {"total": cn.total, **({"by_kind": by_kind} if by_kind else {})}
396
419
  if sm.code_notes:
397
420
  _SEVERITY_ORDER = {"BUG": 0, "FIXME": 1, "DEPRECATED": 2, "TODO": 3, "HACK": 4, "WARNING": 5}
398
421
  _sorted_notes = sorted(
@@ -400,43 +423,62 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
400
423
  key=lambda n: (_SEVERITY_ORDER.get(getattr(n, "kind", "").upper(), 9), getattr(n, "path", "")),
401
424
  )
402
425
  code_notes_items = [
403
- {k: v for k, v in asdict(n).items() if v is not None}
426
+ {
427
+ "kind": getattr(n, "kind", ""),
428
+ "path": getattr(n, "path", ""),
429
+ "line": getattr(n, "line", None),
430
+ **({"text": getattr(n, "text", "")[:60]} if getattr(n, "text", "") else {}),
431
+ }
404
432
  for n in _sorted_notes[:_CODE_NOTES_CAP]
405
433
  ]
406
434
 
407
- # Entry points: production runtime only, capped.
408
- # Development entries shown separately; auxiliary omitted from compact view.
435
+ # Entry points path + kind + confidence only
409
436
  ep_groups = _entry_point_groups(sm.entry_points)
410
- entry_points_compact = ep_groups["production"][:_EP_PRODUCTION_CAP]
411
- if not entry_points_compact:
412
- entry_points_compact = [] # truth signal: no production runtime detected
437
+ entry_points_compact = [
438
+ {
439
+ "path": ep["path"],
440
+ **({"kind": ep["kind"]} if ep.get("kind") else {}),
441
+ **({"confidence": ep["confidence"]} if ep.get("confidence") else {}),
442
+ }
443
+ for ep in ep_groups["production"][:_EP_PRODUCTION_CAP]
444
+ ]
413
445
 
414
- # Confidence summary
446
+ # Stacks — name + method + confidence + frameworks (names only)
447
+ stacks_compact = [
448
+ {
449
+ "stack": s.stack,
450
+ "detection_method": s.detection_method,
451
+ "confidence": s.confidence,
452
+ **({"primary": True} if s.primary else {}),
453
+ **({"frameworks": [f.name for f in s.frameworks]} if s.frameworks else {}),
454
+ **({"package_manager": s.package_manager} if s.package_manager else {}),
455
+ }
456
+ for s in sm.stacks
457
+ ]
458
+
459
+ # Confidence — overall only + anomalies
415
460
  conf_dict: Any = None
416
- anomalies: Any = None
417
461
  if sm.confidence_summary is not None:
418
- conf_dict = asdict(sm.confidence_summary)
419
- if sm.confidence_summary.anomalies:
420
- anomalies = sm.confidence_summary.anomalies
462
+ cs = sm.confidence_summary
463
+ conf_dict = {"overall": cs.overall, "stack": cs.stack_confidence, "entry_points": cs.entry_point_confidence}
464
+ if cs.anomalies:
465
+ conf_dict["anomalies"] = cs.anomalies
421
466
 
422
467
  # Analysis gaps
423
468
  gaps_list: Any = None
424
469
  if sm.analysis_gaps:
425
- gaps_list = [asdict(g) for g in sm.analysis_gaps]
426
-
427
- context_summary_dict: Any = None
428
- if sm.context_summary is not None and sm.context_summary.requested:
429
- context_summary_dict = asdict(sm.context_summary)
470
+ gaps_list = [
471
+ {"area": g.area, "reason": g.reason, "impact": g.impact}
472
+ for g in sm.analysis_gaps
473
+ ]
430
474
 
431
475
  result: dict[str, Any] = {
432
476
  "schema_version": sm.metadata.schema_version,
433
477
  "project_type": sm.project_type,
434
478
  "project_summary": sm.project_summary,
435
479
  "architecture_summary": sm.architecture_summary,
436
- "context_summary": context_summary_dict,
437
- "stacks": [asdict(stack) for stack in sm.stacks],
480
+ "stacks": stacks_compact,
438
481
  "entry_points": entry_points_compact,
439
- "development_entry_points": (ep_groups["development"][:_EP_DEV_CAP] or None),
440
482
  "dependency_summary": dep_summary_dict,
441
483
  "key_dependencies": key_deps,
442
484
  "env_summary": env_summary_dict,
@@ -444,14 +486,10 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
444
486
  "code_notes_summary": code_notes_summary_dict,
445
487
  "code_notes": code_notes_items,
446
488
  "confidence_summary": conf_dict,
447
- "anomalies": anomalies,
448
489
  "analysis_gaps": gaps_list,
449
490
  }
450
- # Strip keys that are fully None and not informative
451
- return {k: v for k, v in result.items() if v is not None or k in (
452
- "project_type", "project_summary", "architecture_summary",
453
- "dependency_summary", "confidence_summary",
454
- )}
491
+ _always_include = {"project_type", "project_summary", "architecture_summary", "dependency_summary"}
492
+ return {k: v for k, v in result.items() if v is not None or k in _always_include}
455
493
 
456
494
 
457
495
  def normalize_source_map(sm: SourceMap) -> SourceMap:
@@ -1130,9 +1168,6 @@ def _contract_view_minimal(
1130
1168
  "project": project,
1131
1169
  }
1132
1170
 
1133
- if sm.metadata.traversal_topology:
1134
- result["traversal"] = sm.metadata.traversal_topology
1135
-
1136
1171
  # Per-file contracts
1137
1172
  if contracts:
1138
1173
  serialized: list[dict[str, Any]] = []
@@ -1310,6 +1345,10 @@ def _compress_sig(name: str, sig: str, max_len: int = 100) -> str:
1310
1345
  return full
1311
1346
 
1312
1347
 
1348
+ _MAX_FN_PER_CONTRACT = 5 # max function signatures per contract (token budget)
1349
+ _MAX_SIG_LEN = 60 # max chars per compressed signature
1350
+
1351
+
1313
1352
  def _serialize_contract_minimal(c: Any) -> dict[str, Any]:
1314
1353
  """Serialize one FileContract to minimal format."""
1315
1354
  item: dict[str, Any] = {"path": c.path, "role": c.role}
@@ -1317,38 +1356,47 @@ def _serialize_contract_minimal(c: Any) -> dict[str, Any]:
1317
1356
  if c.is_changed:
1318
1357
  item["changed"] = True
1319
1358
 
1320
- # Exports: flat string for functions/unknown, {name,k} for others
1321
- # When all exports are same non-function kind, group them
1322
- if c.exports:
1323
- exs: list[Any] = []
1324
- kinds = {e.kind for e in c.exports}
1325
- if len(kinds) == 1 and "function" not in kinds and "unknown" not in kinds:
1326
- # All same non-function kind — compact: {"k": "class", "names": [...]}
1327
- only_kind = next(iter(kinds))
1328
- exs = [{"k": only_kind, "names": sorted(e.name for e in c.exports)}]
1329
- else:
1330
- for e in sorted(c.exports, key=lambda e: e.name):
1331
- if e.kind in ("function", "unknown"):
1332
- exs.append(e.name)
1333
- else:
1334
- exs.append({"name": e.name, "k": e.kind})
1335
- item["exports"] = exs
1336
-
1337
- # External deps (non-stdlib already filtered in extractor)
1338
- if c.dependencies:
1339
- item["deps"] = sorted(c.dependencies)
1340
-
1341
- # Exported function signatures — compressed
1359
+ # Exported function signatures compressed, capped
1342
1360
  exported_names = {e.name for e in c.exports}
1361
+ fn_names_in_sigs: set[str] = set()
1343
1362
  if c.functions:
1344
1363
  fns = []
1345
1364
  for f in sorted(c.functions, key=lambda f: f.name):
1346
1365
  if not (f.exported or f.name in exported_names):
1347
1366
  continue
1348
- fns.append(_compress_sig(f.name, f.signature))
1367
+ fns.append(_compress_sig(f.name, f.signature, max_len=_MAX_SIG_LEN))
1368
+ fn_names_in_sigs.add(f.name)
1369
+ if len(fns) >= _MAX_FN_PER_CONTRACT:
1370
+ break
1349
1371
  if fns:
1350
1372
  item["fn"] = fns
1351
1373
 
1374
+ # Exports: omit function names already shown in fn; keep non-function exports
1375
+ if c.exports:
1376
+ exs: list[Any] = []
1377
+ non_fn_exports = [e for e in c.exports if e.kind not in ("function", "unknown")]
1378
+ fn_exports_not_in_sig = [
1379
+ e for e in c.exports
1380
+ if e.kind in ("function", "unknown") and e.name not in fn_names_in_sigs
1381
+ ]
1382
+ remaining = non_fn_exports + fn_exports_not_in_sig
1383
+ if remaining:
1384
+ kinds = {e.kind for e in remaining}
1385
+ if len(kinds) == 1 and "function" not in kinds and "unknown" not in kinds:
1386
+ only_kind = next(iter(kinds))
1387
+ exs = [{"k": only_kind, "names": sorted(e.name for e in remaining)}]
1388
+ else:
1389
+ for e in sorted(remaining, key=lambda e: e.name):
1390
+ if e.kind in ("function", "unknown"):
1391
+ exs.append(e.name)
1392
+ else:
1393
+ exs.append({"name": e.name, "k": e.kind})
1394
+ item["exports"] = exs
1395
+
1396
+ # External deps (non-stdlib already filtered in extractor)
1397
+ if c.dependencies:
1398
+ item["deps"] = sorted(c.dependencies)
1399
+
1352
1400
  # Types: skip if fully covered by exports (avoids duplication in model files)
1353
1401
  if c.types:
1354
1402
  export_names_set = {e.name for e in c.exports}
@@ -1363,12 +1411,6 @@ def _serialize_contract_minimal(c: Any) -> dict[str, Any]:
1363
1411
  if c.hooks_used:
1364
1412
  item["hooks"] = c.hooks_used
1365
1413
 
1366
- # Ranking signals: why this file was ranked here
1367
- if getattr(c, "ranking_reasons", None):
1368
- non_trivial = [r for r in c.ranking_reasons if r not in ("source file", "noise")]
1369
- if non_trivial:
1370
- item["why"] = non_trivial
1371
-
1372
1414
  return item
1373
1415
 
1374
1416
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 0.48.0
3
+ Version: 1.0.0
4
4
  Summary: Deterministic codebase context for AI coding agents
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -1,16 +1,16 @@
1
- sourcecode/__init__.py,sha256=eHhCx5iLdI9jgLRDyF8PiWmNdiWkspKJ_HwyUbcfVP0,103
1
+ sourcecode/__init__.py,sha256=ZYQhk-Q7wjL_jfJS398d9Y2BPlzoHCjDAh6exi9L7JI,102
2
2
  sourcecode/adaptive_scanner.py,sha256=6dh34C2qZXyRbw-8xBhbEwDdXanM6CRFRWayVoYITnA,10190
3
3
  sourcecode/architecture_analyzer.py,sha256=O4AXc7l_WTzIXrcAzstqZy-TGKNaFa6p3MzpgVjaO8g,27749
4
4
  sourcecode/architecture_summary.py,sha256=rSY5MRiaz4N1YdG0pqDTDuFjSN7PO_Zplx-dtNzv2Yo,19985
5
5
  sourcecode/ast_extractor.py,sha256=0OHQwTUBBc9lmqPLryVeB1z8dGIC6NhLlar800CD9oI,41129
6
6
  sourcecode/classifier.py,sha256=GKTMN8qKZX7ponSwDJfN08RrasI4CVpq1_gFBgEopps,7093
7
- sourcecode/cli.py,sha256=BC83AWW3oSIWcQM8IPN0tU-1bSb2NyhkMS_Fyi3bRRc,71887
7
+ sourcecode/cli.py,sha256=YusMOF5OfihL3nBw66LcANRFSiVHugPrXE0vPIycjLQ,72016
8
8
  sourcecode/code_notes_analyzer.py,sha256=rRd8bFYV0krjlxxQV0wenwE9K7pVpUQSR7KvSvUQKw4,9226
9
9
  sourcecode/confidence_analyzer.py,sha256=HxJMPLI5ulqtkncnv98W4iVO6yMbpQo87VuxiuNbDmY,12167
10
10
  sourcecode/context_scorer.py,sha256=QpChSpsmaAYz91rXA4Ue5xzQmNz_ZboZN09YOHScq1U,14679
11
11
  sourcecode/context_summarizer.py,sha256=CiQrfBEzun949bWvmLabWoj2HhPn6Lw62ofqnsy0FlQ,6503
12
12
  sourcecode/contract_model.py,sha256=gCf9-Kj0G7l0lvRTAcRfFAfMgs1Rpizv4mKovQLYUkw,3434
13
- sourcecode/contract_pipeline.py,sha256=Ua48J97RO6xUvpvZSAFJ27W4-Q5K9dJrzIo-dDEugIQ,25686
13
+ sourcecode/contract_pipeline.py,sha256=eann6PZJ8Bg2zavaOhmfN_oy9rMWNSkKPDW68rRZV8E,25686
14
14
  sourcecode/coverage_parser.py,sha256=q0LeZJaX1bnntLu-ImksdBsMlpsVmk_iUfSaB4eaJGo,19702
15
15
  sourcecode/dependency_analyzer.py,sha256=Exq0BfInvfS5iAg9xAr6WI2uPNuotkIudTKcYJcRhB8,52757
16
16
  sourcecode/doc_analyzer.py,sha256=TttdS7mndKQhyJCfJnnAsyGCJrf-TIL7oXxDlTLUFKE,21248
@@ -29,7 +29,7 @@ sourcecode/runtime_classifier.py,sha256=zWX3r3HCKHc-qtIobErOa8aKMmaoPYREtJKvPcBG
29
29
  sourcecode/scanner.py,sha256=aM3h9-DCQ3xKpeHpHYdo2vX6T5P95HA_YwZbkAVNwmo,8288
30
30
  sourcecode/schema.py,sha256=ofEge9hTWHOTjeWt7ceCDQWzP-uhhenrYX2usjW2KVU,22759
31
31
  sourcecode/semantic_analyzer.py,sha256=16EFTgM7ooW0m5gNUKOlTSn7IEMLSzKmzQn-cWaSqjs,82604
32
- sourcecode/serializer.py,sha256=xMUcQjdcUfVJ9U2nWfo_IAw5X3M_cnM5rkJiIzIiZ6s,62626
32
+ sourcecode/serializer.py,sha256=Il-gUEVmVNOX3uA7A9HHf8n64ZoHSC9AqW5qaVTZUBs,64362
33
33
  sourcecode/summarizer.py,sha256=ZuzIdm3t8A-d5MuQL0TSNLrd-L0IQIuguIxeNXMNJf8,16070
34
34
  sourcecode/tree_utils.py,sha256=Fj9OIuUksBvgibNd3feog0sMDjVypJzPexp5lvMoYWI,1424
35
35
  sourcecode/workspace.py,sha256=X_6NmNnitvT3_38V-JDChydo_sR68s249hLFlrQskU0,8271
@@ -60,8 +60,8 @@ sourcecode/telemetry/consent.py,sha256=wLMvGNJeSSyZoNkQXpoUioY6mMv4Qdvuw7S9jAEWn
60
60
  sourcecode/telemetry/events.py,sha256=oEvvulfsv5GIDWG2174gSS6tNB95w38AIYiYeifGKlE,2294
61
61
  sourcecode/telemetry/filters.py,sha256=Asa71oRl7q3Wt_FMwuufIZJFzSYdgRNKS8LHCIyFeYE,4805
62
62
  sourcecode/telemetry/transport.py,sha256=KJeIPCPWMdmbCP3ySGs2iUlia34U6vWne2dZsUezesw,1560
63
- sourcecode-0.48.0.dist-info/METADATA,sha256=CFDL7EnHtt8gWbDzuhQ10QO5z9KIFgM1pOv7O3m48mg,25209
64
- sourcecode-0.48.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
65
- sourcecode-0.48.0.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
66
- sourcecode-0.48.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
67
- sourcecode-0.48.0.dist-info/RECORD,,
63
+ sourcecode-1.0.0.dist-info/METADATA,sha256=E8Jq944wOl_8nMXiZPj0T75q2WKWd1SjXwGQrKnXbhM,25208
64
+ sourcecode-1.0.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
65
+ sourcecode-1.0.0.dist-info/entry_points.txt,sha256=ex3F9rmbXeyDIoFQHtkEqTsKSaJow8F0LrVu8XfIktQ,57
66
+ sourcecode-1.0.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
67
+ sourcecode-1.0.0.dist-info/RECORD,,