diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,8 +1,14 @@
 # Revision history for nix-thunk
 
+## 0.5.0.0
+
+* Fix a critical bug where v6 thunks can not be used to fetch non-GitHub repositories. Please update all your thunks to use the new v7 thunk spec.
+  Updating your thunk can be done by running `nix-thunk unpack $path; nix-thunk pack $path`.
+* Building a functional `nix-thunk` _must_ be done using the included Nix derivation.
+
 ## 0.4.0.0
 
-* The default thunk specification ("v6") now uses a pinned version of nixpkgsk, rather than the magic `<nixpkgs>`, for fetching thunks. This ensures that thunks can be fetched even in an environment where `NIX_PATH` is unset.
+* The default thunk specification ("v6") now uses a pinned version of nixpkgs, rather than the magic `<nixpkgs>`, for fetching thunks. This ensures that thunks can be fetched even in an environment where `NIX_PATH` is unset.
 
 ## 0.3.0.0
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -21,6 +21,8 @@
 nix-env -f https://github.com/obsidiansystems/nix-thunk/archive/master.tar.gz -iA command
 ```
 
+**WARNING**: It is _not_ possible to compile `nix-thunk` without Nix. To ensure that packed thunks are buildable even in environments where diamond paths are unavailable (specifically `<nixpkgs>`), `nix-thunk` _must_ be built with knowledge of a known-good nixpkgs, _and_ for `nix-thunk` to be able to manipulate these thunks, it must _always_ be the same version of nixpkgs.
+
 ## Command Usage
 
 ### Create a dependency
diff --git a/nix-thunk.cabal b/nix-thunk.cabal
--- a/nix-thunk.cabal
+++ b/nix-thunk.cabal
@@ -1,6 +1,6 @@
 cabal-version:      >=1.10
 name:               nix-thunk
-version:            0.4.0.0
+version:            0.5.0.0
 license:            BSD3
 license-file:       LICENSE
 copyright:          Obsidian Systems LLC 2020-2022
@@ -34,8 +34,8 @@
     , aeson-pretty          >=0.8.7    && <0.9
     , base                  >=4.12.0.0 && <4.15
     , bytestring            >=0.10.8.2 && <0.11
-    , cli-extras            >=0.1.0.1  && <0.2
-    , cli-git               >=0.1.0.1  && <0.2
+    , cli-extras            >=0.2.1.0  && <0.3
+    , cli-git               >=0.2.0.0  && <0.3
     , cli-nix               >=0.1.0.1  && <0.2
     , containers            >=0.6.0.1  && <0.7
     , cryptonite            >=0.25     && <0.30
@@ -61,13 +61,15 @@
     , unix                  >=2.7.2.2  && <2.8
     , which                 >=0.2      && <0.3
     , yaml                  >=0.11.1.2 && <0.12
+    , process               >=1.6.0.0  && <1.7
+    , template-haskell      >=2.14.0.0 && <2.19
 
 executable nix-thunk
   main-is:          src-bin/nix-thunk.hs
   default-language: Haskell2010
   build-depends:
       base                  >=4.12.0.0 && <4.15
-    , cli-extras            >=0.1.0.1  && <0.2
+    , cli-extras            >=0.2.1.0  && <0.3
     , nix-thunk
     , optparse-applicative  >=0.14.3.0 && <0.17
     , text                  >=1.2.3.1  && <1.3
diff --git a/src-bin/nix-thunk.hs b/src-bin/nix-thunk.hs
--- a/src-bin/nix-thunk.hs
+++ b/src-bin/nix-thunk.hs
@@ -7,6 +7,8 @@
 import qualified Data.Text.IO as T
 import System.Environment
 import System.Exit
+import System.IO
+import Data.List
 import Data.Void
 
 data Args = Args
@@ -35,13 +37,25 @@
   { prefShowHelpOnEmpty = True
   }
 
-main :: IO Void
-main = do
-  args <- getArgs
-  args' <- handleParseResult $ execParserPure parserPrefs argsInfo args
-  cliConf <- mkDefaultCliConfig args
-  runCli cliConf (runThunkCommand (_args_command args')) >>= \case
-    Right () -> exitWith ExitSuccess
-    Left e -> do
-      T.putStrLn $ prettyNixThunkError e
-      exitWith $ ExitFailure 2
+main :: IO ()
+main =
+  do
+    args <- getArgs
+    args' <- handleParseResult $ execParserPure parserPrefs argsInfo args
+    let logLevel = if _args_verbose args' then Debug else Notice
+    notInteractive <- not <$> isInteractiveTerm args
+    cliConf <- newCliConfig logLevel notInteractive notInteractive (\e -> (prettyNixThunkError e, ExitFailure 1))
+    runCli cliConf (runThunkCommand (_args_command args'))
+  where
+    isInteractiveTerm args = do
+      isTerm <- hIsTerminalDevice stdout
+      -- Running in bash/fish/zsh completion
+      let inShellCompletion = isInfixOf "completion" $ unwords args
+
+      -- Respect the user’s TERM environment variable. Dumb terminals
+      -- like Eshell cannot handle lots of control sequences that the
+      -- spinner uses.
+      termEnv <- lookupEnv "TERM"
+      let isDumb = termEnv == Just "dumb"
+
+      return $ isTerm && not inShellCompletion && not isDumb
diff --git a/src/Nix/Thunk.hs b/src/Nix/Thunk.hs
--- a/src/Nix/Thunk.hs
+++ b/src/Nix/Thunk.hs
@@ -30,6 +30,7 @@
   , ThunkPackConfig (..)
   , ThunkConfig (..)
   , updateThunkToLatest
+  , updateThunk
   , ThunkUpdateConfig (..)
   , unpackThunk
   , ThunkSpec (..)
@@ -102,6 +103,8 @@
 import System.IO.Temp
 import System.Posix.Files
 import qualified Text.URI as URI
+import Language.Haskell.TH (Exp(LitE), Lit(StringL), runIO)
+import qualified System.Process as P
 
 --------------------------------------------------------------------------------
 -- Hacks
@@ -109,7 +112,7 @@
 
 type MonadInfallibleNixThunk m =
   ( CliLog m
-  , HasCliConfig m
+  , HasCliConfig NixThunkError m
   , MonadIO m
   , MonadMask m
   )
@@ -126,7 +129,8 @@
 
 prettyNixThunkError :: NixThunkError -> Text
 prettyNixThunkError = \case
-  NixThunkError_ProcessFailure pf -> prettyProcessFailure pf
+  NixThunkError_ProcessFailure (ProcessFailure p code) ->
+    "Process exited with code " <> T.pack (show code) <> "; " <> reconstructCommand p
   NixThunkError_Unstructured msg -> msg
 
 makePrisms ''NixThunkError
@@ -248,11 +252,6 @@
   , _gitSource_private = _gitHubSource_private s
   }
 
-getThunkGitBranch :: ThunkPtr -> Maybe Text
-getThunkGitBranch (ThunkPtr _ src) = fmap untagName $ case src of
-  ThunkSource_GitHub s -> _gitHubSource_branch s
-  ThunkSource_Git s -> _gitSource_branch s
-
 commitNameToRef :: Name Commit -> Ref SHA1
 commitNameToRef (N c) = refFromHex $ encodeUtf8 c
 
@@ -300,6 +299,17 @@
 attrCacheFileName :: FilePath
 attrCacheFileName = ".attr-cache"
 
+-- | A path from which our known-good nixpkgs can be fetched.
+-- @print-nixpkgs-path@ is a shell script whose only purpose is to print
+-- that path. It is generated and included in the build dependencies of
+-- nix-thunk by our default.nix.
+pinnedNixpkgsPath :: FilePath
+pinnedNixpkgsPath =
+  $(do
+    p <- fmap init . runIO $ P.readCreateProcess (P.shell "print-nixpkgs-path") ""
+    pure $ LitE $ StringL $ p
+  )
+
 -- | Specification for how a file in a thunk version works.
 data ThunkFileSpec
   = ThunkFileSpec_Ptr (LBS.ByteString -> Either String ThunkPtr) -- ^ This file specifies 'ThunkPtr' data
@@ -520,14 +530,6 @@
       liftIO $ createDirectoryIfMissing True $ takeDirectory fullPath
       f fullPath
 
-createThunkWithLatest :: MonadNixThunk m => FilePath -> ThunkSource -> m ()
-createThunkWithLatest target s = do
-  rev <- getLatestRev s
-  createThunk target $ Right $ ThunkPtr
-    { _thunkPtr_source = s
-    , _thunkPtr_rev = rev
-    }
-
 updateThunkToLatest :: MonadNixThunk m => ThunkUpdateConfig -> FilePath -> m ()
 updateThunkToLatest (ThunkUpdateConfig mBranch thunkConfig) target = do
   withSpinner' ("Updating thunk " <> T.pack target <> " to latest") (pure $ const $ "Thunk " <> T.pack target <> " updated to latest") $ do
@@ -562,8 +564,9 @@
 -- This tool will only ever produce the newest one when it writes a thunk.
 gitHubThunkSpecs :: NonEmpty ThunkSpec
 gitHubThunkSpecs =
-  gitHubThunkSpecV6 :|
-  [ gitHubThunkSpecV5
+  gitHubThunkSpecV7 :|
+  [ gitHubThunkSpecV6
+  , gitHubThunkSpecV5
   , gitHubThunkSpecV4
   , gitHubThunkSpecV3
   , gitHubThunkSpecV2
@@ -638,10 +641,11 @@
 in fetch json
 |]
 
--- | Specification for GitHub thunks which use a specific, pinned
--- version of nixpkgs for fetching, rather than using @<nixpkgs>@ from
--- @NIX_PATH@. The "v6" specs ensure that thunks can be fetched even
--- when @NIX_PATH@ is unset.
+-- | See 'gitHubThunkSpecV7'.
+--
+-- __NOTE__: v6 spec thunks are broken! They import the pinned nixpkgs
+-- in an incorrect way. GitHub thunks for public repositories with no
+-- submodules will still work, but update as soon as possible.
 gitHubThunkSpecV6 :: ThunkSpec
 gitHubThunkSpecV6 = mkThunkSpec "github-v6" "github.json" parseGitHubJsonBytes [here|
 # DO NOT HAND-EDIT THIS FILE
@@ -658,14 +662,30 @@
 in fetch json
 |]
 
+-- | Specification for GitHub thunks which use a specific, pinned
+-- version of nixpkgs for fetching, rather than using @<nixpkgs>@ from
+-- @NIX_PATH@. The "v7" specs ensure that thunks can be fetched even
+-- when @NIX_PATH@ is unset.
+gitHubThunkSpecV7 :: ThunkSpec
+gitHubThunkSpecV7 = mkThunkSpec "github-v7" "github.json" parseGitHubJsonBytes [i|# DO NOT HAND-EDIT THIS FILE
+let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }:
+  if !fetchSubmodules && !private then builtins.fetchTarball {
+    url = "https://github.com/\${owner}/\${repo}/archive/\${rev}.tar.gz"; inherit sha256;
+  } else (import ${pinnedNixpkgsPath} {}).fetchFromGitHub {
+    inherit owner repo rev sha256 fetchSubmodules private;
+  };
+  json = builtins.fromJSON (builtins.readFile ./github.json);
+in fetch json|]
+
 parseGitHubJsonBytes :: LBS.ByteString -> Either String ThunkPtr
 parseGitHubJsonBytes = parseJsonObject $ parseThunkPtr $ \v ->
   ThunkSource_GitHub <$> parseGitHubSource v <|> ThunkSource_Git <$> parseGitSource v
 
 gitThunkSpecs :: NonEmpty ThunkSpec
 gitThunkSpecs =
-  gitThunkSpecV6 :|
-  [ gitThunkSpecV5
+  gitThunkSpecV7 :|
+  [ gitThunkSpecV6
+  , gitThunkSpecV5
   , gitThunkSpecV4
   , gitThunkSpecV3
   , gitThunkSpecV2
@@ -751,10 +771,10 @@
 in fetch json
 |]
 
--- | Specification for Git thunks which use a specific, pinned version
--- of nixpkgs for fetching, rather than using @<nixpkgs>@ from
--- @NIX_PATH@. The "v6" specs ensure that thunks can be fetched even
--- when @NIX_PATH@ is unset.
+-- | See 'gitThunkSpecV7'.
+-- __NOTE__: v6 spec thunks are broken! They import the pinned nixpkgs
+-- in an incorrect way. GitHub thunks for public repositories with no
+-- submodules will still work, but update as soon as possible.
 gitThunkSpecV6 :: ThunkSpec
 gitThunkSpecV6 = mkThunkSpec "git-v6" "git.json" parseGitJsonBytes [here|
 # DO NOT HAND-EDIT THIS FILE
@@ -776,6 +796,26 @@
 in fetch json
 |]
 
+-- | Specification for Git thunks which use a specific, pinned version
+-- of nixpkgs for fetching, rather than using @<nixpkgs>@ from
+-- @NIX_PATH@. The "v7" specs ensure that thunks can be fetched even
+-- when @NIX_PATH@ is unset.
+gitThunkSpecV7 :: ThunkSpec
+gitThunkSpecV7 = mkThunkSpec "git-v7" "git.json" parseGitJsonBytes [i|# DO NOT HAND-EDIT THIS FILE
+let fetch = {url, rev, branch ? null, sha256 ? null, fetchSubmodules ? false, private ? false, ...}:
+  let realUrl = let firstChar = builtins.substring 0 1 url; in
+    if firstChar == "/" then /. + url
+    else if firstChar == "." then ./. + url
+    else url;
+  in if !fetchSubmodules && private then builtins.fetchGit {
+    url = realUrl; inherit rev;
+    \${if branch == null then null else "ref"} = branch;
+  } else (import ${pinnedNixpkgsPath} {}).fetchgit {
+    url = realUrl; inherit rev sha256;
+  };
+  json = builtins.fromJSON (builtins.readFile ./git.json);
+in fetch json|]
+
 parseGitJsonBytes :: LBS.ByteString -> Either String ThunkPtr
 parseGitJsonBytes = parseJsonObject $ parseThunkPtr $ fmap ThunkSource_Git . parseGitSource
 
@@ -804,7 +844,7 @@
 nixBuildThunkAttrWithCache
   :: ( MonadIO m
      , MonadLog Output m
-     , HasCliConfig m
+     , HasCliConfig NixThunkError m
      , MonadMask m
      , MonadError NixThunkError m
      , MonadFail m
@@ -856,7 +896,7 @@
 -- | Build a nix attribute, and cache the result if possible
 nixBuildAttrWithCache
   :: ( MonadLog Output m
-     , HasCliConfig m
+     , HasCliConfig NixThunkError m
      , MonadIO m
      , MonadMask m
      , MonadError NixThunkError m
diff --git a/src/Nix/Thunk/Command.hs b/src/Nix/Thunk/Command.hs
--- a/src/Nix/Thunk/Command.hs
+++ b/src/Nix/Thunk/Command.hs
@@ -64,7 +64,7 @@
 
 runThunkCommand
   :: ( MonadLog Output m
-     , HasCliConfig m
+     , HasCliConfig NixThunkError m
      , MonadIO m
      , MonadMask m
      , MonadError NixThunkError m
