1414 kb traces [--kind K] [--changeset ID] [--rule ID] [--limit N] query trace/1 records
1515 kb replay --record ID --model PROVIDER:MODEL[:EFFORT] re-ask a recorded call
1616 kb export --out FILE [--role judge|generator] dataset (calibration-safe)
17+ kb init REPO --stage STAGE [--dry-run] [--pin SHA]
18+ bootstrap a repository's knowledge base, one
19+ human-merged stage at a time (skeleton, modules,
20+ deepen, harvest-calibration); never touches kb.db
21+ kb init REPO --suggest-seeds rank existing knowledge pages worth seeding from (no model call)
1722 kb publish (--remote HOST:/STATE_DIR | --local DIR) [--once]
1823 the publisher (GPU box, owner's gh login): perform
1924 the signed outbox items; writes need ALLOW_POST=1
@@ -103,6 +108,36 @@ def _refresh(state_dir: Path, ledger, registry) -> None:
103108 _outbox (state_dir , ledger ).transition (lambda : None )
104109
105110
111+ def _init_command (args , state_dir : Path ) -> int :
112+ from ..config import Settings
113+
114+ if args .suggest_seeds == bool (args .stage ):
115+ print ("kb init: pass exactly one of --stage or --suggest-seeds" , file = sys .stderr )
116+ return 2
117+ if args .suggest_seeds :
118+ from .init_stages import suggest_seeds
119+ from .init_support import InitError , InitRuntime
120+
121+ try :
122+ rt = InitRuntime .from_env (Settings (), state_dir = state_dir )
123+ lifecycle = rt .registry .get (args .repo )
124+ if lifecycle is None :
125+ print (f"no adapter declares knowledge repo { args .repo !r} " , file = sys .stderr )
126+ return 2
127+ for path , score , shared in suggest_seeds (rt , lifecycle ):
128+ print (f"{ score :6.3f} { path } ({ ', ' .join (shared [:8 ])} )" )
129+ except InitError as exc :
130+ print (str (exc ), file = sys .stderr )
131+ return 1
132+ return 0
133+ from .runner import run_playbook
134+
135+ params = {"stage" : args .stage , "dry_run" : "true" if args .dry_run else "false" , "pin" : args .pin or "" }
136+ outcome , run_dir = run_playbook (Settings (), "kb-init" , args .repo , state_dir = state_dir , params = params )
137+ print (f"kb init { args .repo } { args .stage } : { outcome .status } ({ run_dir } )" )
138+ return 0 if outcome .status == "done" else 1
139+
140+
106141def main (argv : list [str ] | None = None ) -> int :
107142 parser = argparse .ArgumentParser (prog = "infermatrix-copilot kb" )
108143 parser .add_argument ("--state-dir" , help = "default: $KB_STATE_DIR or ~/.infermatrix-copilot/kb" )
@@ -142,6 +177,13 @@ def main(argv: list[str] | None = None) -> int:
142177 export = sub .add_parser ("export" )
143178 export .add_argument ("--out" , required = True )
144179 export .add_argument ("--role" , default = "judge" , choices = ("judge" , "generator" ))
180+ init = sub .add_parser ("init" )
181+ init .add_argument ("repo" )
182+ init .add_argument ("--stage" , choices = ("skeleton" , "modules" , "deepen" , "harvest-calibration" ))
183+ init .add_argument ("--dry-run" , action = "store_true" ,
184+ help = "write the tree and PR body under the state directory instead of opening a PR" )
185+ init .add_argument ("--pin" , help = "upstream commit to pin (default: the default branch head)" )
186+ init .add_argument ("--suggest-seeds" , action = "store_true" )
145187 publish = sub .add_parser ("publish" )
146188 where = publish .add_mutually_exclusive_group (required = True )
147189 where .add_argument ("--remote" , help = "host:/absolute/path of the service state directory (over ssh)" )
@@ -163,6 +205,9 @@ def main(argv: list[str] | None = None) -> int:
163205 return _publish (args )
164206 if args .command in {"traces" , "replay" , "export" }:
165207 return _traces_command (args , _state_dir (args .state_dir ))
208+ if args .command == "init" :
209+ # before any ledger is opened: kb init never touches the service's kb.db
210+ return _init_command (args , _state_dir (args .state_dir ))
166211
167212 state_dir = _state_dir (args .state_dir )
168213 ledger = _ledger (state_dir )
0 commit comments