@@ -545,6 +545,79 @@ def _maybe_run_mine_after_init(args, cfg) -> None:
545545 sys .exit (1 )
546546
547547
548+ def _mine_via_source_adapter (args , palace_path ):
549+ """Route ``mine --source NAME`` through the RFC 002 source-adapter registry.
550+
551+ Opt-in seam: when an explicit ``--source`` names a registered adapter, mine
552+ resolves it from ``mempalace.sources.registry``, runs its ``ingest()``, and
553+ files each ``DrawerRecord`` through a ``PalaceContext``. When no ``--source``
554+ is given, ``cmd_mine`` keeps its legacy ``--mode`` dispatch — the
555+ filesystem/conversations miners have not moved onto the contract yet, so the
556+ registry path stays opt-in and changes no existing behavior. This wires the
557+ registry the spec reserves.
558+ """
559+ from .knowledge_graph import KnowledgeGraph
560+ from .palace import MineAlreadyRunning , get_collection , mine_palace_lock
561+ from .sources .base import DrawerRecord , SourceItemMetadata , SourceRef
562+ from .sources .context import PalaceContext
563+ from .sources .registry import get_adapter , resolve_adapter_for_source
564+
565+ name = resolve_adapter_for_source (explicit = args .source )
566+ try :
567+ adapter = get_adapter (name )
568+ except KeyError as exc :
569+ print (f"mempalace: { exc } " , file = sys .stderr )
570+ sys .exit (2 )
571+
572+ # Routing precedence (§2.5): explicit --wing flows to the adapter via
573+ # SourceRef.options; the adapter honors it. Secrets never go here (§2.2).
574+ options = {}
575+ if getattr (args , "wing" , None ):
576+ options ["wing" ] = args .wing
577+ ref = SourceRef (local_path = os .path .abspath (os .path .expanduser (args .dir )), options = options )
578+
579+ filed = 0
580+ skipped = 0
581+ try :
582+ with mine_palace_lock (palace_path ):
583+ collection = get_collection (palace_path , create = True )
584+ kg = KnowledgeGraph (db_path = os .path .join (palace_path , "knowledge_graph.sqlite3" ))
585+ ctx = PalaceContext (
586+ drawer_collection = collection ,
587+ knowledge_graph = kg ,
588+ palace_path = palace_path ,
589+ config = MempalaceConfig (),
590+ adapter_name = getattr (adapter , "name" , name ),
591+ adapter_version = getattr (adapter , "adapter_version" , "" ),
592+ )
593+
594+ skip_item = False
595+ for result in adapter .ingest (source = ref , palace = ctx ):
596+ if isinstance (result , SourceItemMetadata ):
597+ skip_item = False
598+ ctx ._skip_requested = False
599+ existing = collection .get (where = {"source_file" : result .source_file }, limit = 1 )
600+ metas = (existing or {}).get ("metadatas" ) or []
601+ if adapter .is_current (item = result , existing_metadata = metas [0 ] if metas else None ):
602+ ctx .skip_current_item ()
603+ skip_item = True
604+ elif isinstance (result , DrawerRecord ):
605+ if skip_item or ctx ._skip_requested :
606+ skipped += 1
607+ continue
608+ if not args .dry_run :
609+ ctx .upsert_drawer (result )
610+ filed += 1
611+ adapter .close ()
612+ except MineAlreadyRunning as exc :
613+ print (f"mempalace: { exc } " , file = sys .stderr )
614+ sys .exit (1 )
615+
616+ verb = "Would file" if args .dry_run else "Drawers filed:"
617+ tail = f" (skipped { skipped } up-to-date)" if skipped else ""
618+ print (f" source={ name } { verb } { filed } { tail } " )
619+
620+
548621def cmd_mine (args ):
549622 palace_path = os .path .expanduser (args .palace ) if args .palace else MempalaceConfig ().palace_path
550623 include_ignored = []
@@ -555,6 +628,10 @@ def cmd_mine(args):
555628 print ("mempalace: --background requires --daemon" , file = sys .stderr )
556629 sys .exit (2 )
557630
631+ if getattr (args , "source" , None ) and getattr (args , "daemon" , False ):
632+ print ("mempalace: --source does not support --daemon yet" , file = sys .stderr )
633+ sys .exit (2 )
634+
558635 if getattr (args , "daemon" , False ):
559636 payload = {
560637 "source" : args .dir ,
@@ -582,6 +659,12 @@ def cmd_mine(args):
582659 llm_provider = None ,
583660 )
584661
662+ # RFC 002 source-adapter seam: an explicit --source routes mine through the
663+ # registry; absent it, the legacy --mode dispatch below runs unchanged.
664+ if getattr (args , "source" , None ):
665+ _mine_via_source_adapter (args , palace_path )
666+ return
667+
585668 from .palace import MineAlreadyRunning , MineValidationError
586669
587670 try :
@@ -1813,6 +1896,16 @@ def main():
18131896 "mempalace[extract])"
18141897 ),
18151898 )
1899+ p_mine .add_argument (
1900+ "--source" ,
1901+ default = None ,
1902+ metavar = "NAME" ,
1903+ help = (
1904+ "RFC 002 source adapter to mine through (e.g. a third-party "
1905+ "mempalace-source-<name> package). Explicit selection only — no "
1906+ "auto-detect. Omit to use the legacy --mode dispatch."
1907+ ),
1908+ )
18161909 p_mine .add_argument ("--wing" , default = None , help = "Wing name (default: directory name)" )
18171910 p_mine .add_argument (
18181911 "--no-gitignore" ,
0 commit comments