@@ -34,8 +34,9 @@ func usage() {
3434 fmt .Print (shellArt )
3535 fmt .Println ("Usage:" )
3636 fmt .Println (" nutshell init [--dir <path>] Initialize a new bundle directory" )
37- fmt .Println (" nutshell pack [--dir <path>] [-o <file>] Pack directory into .nut bundle" )
37+ fmt .Println (" nutshell pack [--dir <path>] [-o <file>] Pack directory into .nut bundle (check must pass) " )
3838 fmt .Println (" nutshell pack --encrypt --peer <pubkey> Pack & encrypt for a specific peer" )
39+ fmt .Println (" nutshell pack --force Pack even if check fails" )
3940 fmt .Println (" nutshell unpack <file> [-o <path>] Unpack a .nut bundle (auto-decrypts)" )
4041 fmt .Println (" nutshell inspect <file|-> [--json] Inspect bundle without unpacking" )
4142 fmt .Println (" nutshell validate <file|dir> [--json] Validate bundle against spec" )
@@ -135,17 +136,20 @@ var subHelpText = map[string]string{
135136 Flags:
136137 --dir, -d <path> Target directory (default: .)` ,
137138
138- "pack" : `pack [--dir <path>] [-o <file>] [--encrypt --peer <pubkey>]
139+ "pack" : `pack [--dir <path>] [-o <file>] [--encrypt --peer <pubkey>] [--force]
139140
140141 Pack a directory into a .nut bundle.
142+ Runs a completeness check first — if check fails, pack is blocked.
143+ Use --force to pack despite check errors.
141144 With --encrypt --peer, the bundle is encrypted for the given peer's Ed25519
142145 public key. Only that peer can unpack it.
143146
144147 Flags:
145148 --dir, -d <path> Source directory (default: .)
146149 -o <file> Output filename (default: <slug>.nut)
147150 --encrypt Encrypt the bundle for a specific peer
148- --peer <hex-pubkey> Recipient's Ed25519 public key (64 hex chars)` ,
151+ --peer <hex-pubkey> Recipient's Ed25519 public key (64 hex chars)
152+ --force Pack even if completeness check fails` ,
149153
150154 "unpack" : `unpack <file> [-o <path>] [--identity <keyfile>]
151155
@@ -362,13 +366,33 @@ func cmdPack(args []string) {
362366 }
363367 output , args := getFlag (args , "--output" , "-o" )
364368 encrypt , args := hasFlag (args , "--encrypt" )
369+ force , args := hasFlag (args , "--force" )
365370 peerHex , _ := getFlag (args , "--peer" )
366371
367372 if encrypt && peerHex == "" {
368373 fmt .Fprintf (os .Stderr , "%s✗%s --encrypt requires --peer <hex-pubkey>\n " , red , reset )
369374 os .Exit (1 )
370375 }
371376
377+ // Run completeness check before packing
378+ _ , checkResult , checkErr := nutshell .Check (dir )
379+ if checkErr != nil {
380+ fmt .Fprintf (os .Stderr , "%s✗%s Pre-pack check failed: %s\n " , red , reset , checkErr )
381+ os .Exit (1 )
382+ }
383+ if ! checkResult .IsValid () {
384+ fmt .Fprintf (os .Stderr , "\n %s✗ Pack blocked — completeness check failed:%s\n \n " , red , reset )
385+ for _ , e := range checkResult .Errors {
386+ fmt .Fprintf (os .Stderr , " %s✗%s %s\n " , red , reset , e )
387+ }
388+ fmt .Fprintln (os .Stderr )
389+ if ! force {
390+ fmt .Fprintf (os .Stderr , " Fix the errors above, or use %s--force%s to pack anyway.\n \n " , bold , reset )
391+ os .Exit (1 )
392+ }
393+ fmt .Fprintf (os .Stderr , " %s⚠%s --force specified, packing despite errors.\n \n " , yellow , reset )
394+ }
395+
372396 // Determine output path
373397 if output == "" {
374398 data , err := os .ReadFile (filepath .Join (dir , "nutshell.json" ))
0 commit comments