jpkg: honor project ignore files when packing

ober

12351dd9b5cad1b6a5de8a3da056b8c374dc5cb6

diff --git a/lib/std/pkg/artifact.ss b/lib/std/pkg/artifact.ss
index 1437882..6a6019d 100644
--- a/lib/std/pkg/artifact.ss
+++ b/lib/std/pkg/artifact.ss
@@ -65,6 +65,37 @@
                   segs)
           (excluded-file? rel))))
 
+  ;; Projects may keep local build outputs outside the standard directory
+  ;; names (for example, a platform-named executable at the repository root).
+  ;; Honor a small, deterministic .jpkgignore format: one exact path or
+  ;; directory prefix (ending in /) per line; blank lines and # comments are
+  ;; ignored. The ignore file itself remains excluded as a dotfile.
+  (def (jpkgignore-patterns project-dir)
+    (let ([path (path-concat project-dir ".jpkgignore")])
+      (if (file-exists? path)
+          (let loop ([lines (string-split-char
+                             (utf8->string (read-file-bytevector path)) #\newline)]
+                     [acc '()])
+            (if (null? lines)
+                (reverse acc)
+                (let ([line (car lines)])
+                  (loop (cdr lines)
+                        (if (or (= (string-length line) 0)
+                                (char=? (string-ref line 0) #\#))
+                            acc
+                            (cons (if (char=? (string-ref line 0) #\/)
+                                      (substring line 1 (string-length line))
+                                      line)
+                                  acc))))))
+          '())))
+
+  (def (jpkgignore-path? rel patterns)
+    (exists (lambda (pattern)
+              (if (string-suffix-of? "/" pattern)
+                  (string-prefix-of? pattern rel)
+                  (string=? pattern rel)))
+            patterns))
+
   (def (top-level-keeper? rel)
     ;; top-level docs always included
     (and (not (let loop ([i 0])
@@ -76,13 +107,16 @@
 
   (def (collect-project-files project-dir root)
     ;; returns sorted relative paths to include (excluding jpkg.sexp)
-    (let ([acc '()])
+    (let ([acc '()]
+          [ignore-patterns (jpkgignore-patterns project-dir)])
       (walk-files project-dir
         (lambda (rel)
           (let ([in-root?
                  (or (string=? root ".")
                      (string-prefix-of? (string-append root "/") rel))])
-            (when (or (and in-root? (not (excluded-path? rel)))
+            (when (or (and in-root?
+                           (not (excluded-path? rel))
+                           (not (jpkgignore-path? rel ignore-patterns)))
                       (top-level-keeper? rel))
               (set! acc (cons rel acc))))))
       (list-sort string<? acc)))