gitdone-agent 0.8.9 → 0.8.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +49 -7
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -31,7 +31,7 @@ import { randomUUID, createHash } from 'node:crypto'
31
31
  // Reported to the server on every sync so the web UI can flag outdated agents.
32
32
  // Keep in lockstep with packages/agent/package.json. The server's offline
33
33
  // fallback is bumped only after this release has actually reached npm.
34
- const AGENT_VERSION = '0.8.9'
34
+ const AGENT_VERSION = '0.8.11'
35
35
 
36
36
  const AGENT_DIR = join(homedir(), '.gitdone-agent')
37
37
  const CONFIG_PATH = join(AGENT_DIR, 'config.json')
@@ -790,6 +790,32 @@ function gitArgs(args, cwd, { net = false, input } = {}) {
790
790
  }
791
791
  }
792
792
 
793
+ function nulPathList(paths) {
794
+ return Buffer.from(`${paths.join('\0')}\0`, 'utf8')
795
+ }
796
+
797
+ // A snapshot can become stale between rendering and Commit — most notably when
798
+ // the selected change also updates .gitignore. Return the selected paths that
799
+ // match the repo's ignore rules. Normally check-ignore omits tracked files;
800
+ // --no-index includes them so callers can handle the two groups differently.
801
+ function ignoredPaths(paths, cwd, { includeTracked = false } = {}) {
802
+ if (paths.length === 0) return new Set()
803
+ try {
804
+ const args = ['check-ignore', ...(includeTracked ? ['--no-index'] : []), '-z', '--stdin']
805
+ const out = execFileSync('git', args, {
806
+ cwd, encoding: 'buffer', stdio: ['pipe', 'pipe', 'pipe'], input: nulPathList(paths),
807
+ maxBuffer: GIT_MAX_BUFFER, timeout: GIT_TIMEOUT_MS, env: GIT_ENV,
808
+ })
809
+ return new Set(out.toString('utf8').split('\0').filter(Boolean))
810
+ } catch (err) {
811
+ // Exit 1 is check-ignore's documented "none ignored" result, not a failure.
812
+ if (err?.status === 1) return new Set()
813
+ const stderr = err?.stderr?.toString().trim()
814
+ const stdout = err?.stdout?.toString().trim()
815
+ throw new Error(`git check-ignore се провали: ${stderr || stdout || err?.message || 'unknown error'}`)
816
+ }
817
+ }
818
+
793
819
  // Never let a token reach the log file or the server's command-result.
794
820
  function redact(text, token) {
795
821
  if (!text || !token) return text
@@ -806,7 +832,7 @@ function authUrl(auth) {
806
832
  // A push rejected because the remote branch moved ahead of us (someone else
807
833
  // pushed in the meantime). Git prints "non-fast-forward" / "fetch first" /
808
834
  // "tip of your current branch is behind". We recover by pulling+rebasing.
809
- const NON_FAST_FORWARD = /non-fast-forward|fetch first|tip of your current branch is behind|failed to push some refs/i
835
+ const NON_FAST_FORWARD = /non-fast-forward|fetch first|tip of your current branch is behind|updates were rejected because the remote contains work|remote contains work that you do not have locally/i
810
836
 
811
837
  // Point the local remote-tracking ref (e.g. refs/remotes/origin/master) at the
812
838
  // commit we just synced. We push/pull through the ad-hoc token URL, and — unlike
@@ -2666,25 +2692,41 @@ async function executeCommand(cfg, cmd, repoPath) {
2666
2692
  const files = Array.isArray(cmd.payload?.files)
2667
2693
  ? cmd.payload.files.filter((f) => typeof f === 'string' && f.trim())
2668
2694
  : []
2695
+ const ignoredUntracked = ignoredPaths(files, repoPath)
2696
+ const ignoredByRules = ignoredPaths(files, repoPath, { includeTracked: true })
2697
+ const trackedIgnored = new Set([...ignoredByRules].filter((f) => !ignoredUntracked.has(f)))
2698
+ const addFiles = ignoredByRules.size > 0 ? files.filter((f) => !ignoredByRules.has(f)) : files
2699
+ if (ignoredUntracked.size > 0) log(`↷ commit @ ${repoPath}: пропускам ${ignoredUntracked.size} игнорирани untracked файла`)
2669
2700
  // Do not put every selected path in argv: a large change set can exceed
2670
2701
  // Windows' ~32K CreateProcess command-line limit before git even starts
2671
2702
  // (`spawnSync git ENAMETOOLONG`). Git's NUL-delimited stdin pathspec mode
2672
2703
  // has no such argv limit and also preserves spaces/newlines verbatim.
2673
- const add = files.length
2704
+ // `git add <path>` refuses tracked files when a newly added rule ignores
2705
+ // their parent directory. update-index stages those exact tracked paths
2706
+ // without force-adding anything untracked; the normal add below handles
2707
+ // all remaining (including new files and embedded repositories).
2708
+ const updateIgnoredTracked = trackedIgnored.size > 0
2709
+ ? gitArgs(['update-index', '--add', '--remove', '-z', '--stdin'], repoPath, { input: nulPathList([...trackedIgnored]) })
2710
+ : { ok: true, out: '' }
2711
+ if (!updateIgnoredTracked.ok) throw new Error(`git update-index се провали: ${updateIgnoredTracked.out}`)
2712
+ const add = addFiles.length
2674
2713
  ? gitArgs(
2675
2714
  ['--literal-pathspecs', 'add', '--pathspec-from-file=-', '--pathspec-file-nul'],
2676
2715
  repoPath,
2677
- { input: Buffer.from(`${files.join('\0')}\0`, 'utf8') },
2716
+ { input: nulPathList(addFiles) },
2678
2717
  )
2679
- : gitArgs(['add', '-A'], repoPath)
2718
+ : files.length === 0
2719
+ ? gitArgs(['add', '-A'], repoPath)
2720
+ : { ok: true, out: '' }
2680
2721
  if (!add.ok) throw new Error(`git add се провали: ${add.out}`)
2681
2722
  const commit = gitArgs(['commit', '-m', msg], repoPath)
2723
+ const ignoredNote = ignoredUntracked.size > 0 ? `\nпропуснати игнорирани файлове: ${ignoredUntracked.size}` : ''
2682
2724
  if (commit.ok) {
2683
- result = commit.out || 'committed'
2725
+ result = `${commit.out || 'committed'}${ignoredNote}`
2684
2726
  } else if (/nothing to commit|no changes added|working tree clean/i.test(commit.out)) {
2685
2727
  // Benign: the checked files carried no staged change (already committed or
2686
2728
  // whitespace-only). Say so plainly instead of faking success.
2687
- result = 'няма промени за commit'
2729
+ result = `няма промени за commit${ignoredNote}`
2688
2730
  } else {
2689
2731
  // Real failure (hook, identity, lock, corrupt path…) — surface it so the
2690
2732
  // console shows the reason instead of the old silent fake ✓.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitdone-agent",
3
- "version": "0.8.9",
3
+ "version": "0.8.11",
4
4
  "description": "Local gitDone companion for repository and optional World of Warcraft progress sync",
5
5
  "type": "module",
6
6
  "files": [