@gitset-dev/cli 2.4.2 → 2.5.0

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.
package/README.md CHANGED
@@ -114,10 +114,21 @@ Secrets are redacted locally before any file content is sent, and prose
114
114
  docs / test file bodies are listed structurally but never sent at all.
115
115
 
116
116
  `gitset knowledge automate` needs "Allow GitHub Actions to create and
117
- approve pull requests" enabled under the repo's **Settings > Actions >
118
- General > Workflow permissions** without it, the scheduled update still
119
- runs and commits safely, but the review-PR step fails, and the workflow run
120
- will show that failure clearly rather than hide it.
117
+ approve pull requests" enabled (**Settings > Actions > General > Workflow
118
+ permissions**) for the update PR to open. It detects this automatically and
119
+ offers to enable it via the GitHub API — asked first, like everything else
120
+ in this command. If you decline, or the API call fails (no admin access, an
121
+ org policy locking the setting), the scheduled update still runs and
122
+ commits safely; only the review-PR step fails, and the workflow run shows
123
+ that failure clearly rather than hide it.
124
+
125
+ Some organizations enforce that setting off with no repo-level override at
126
+ all — the checkbox greys out even on the org's own settings page. For that
127
+ case, add a repository secret named `GITSET_PR_TOKEN` with a personal
128
+ access token (classic: `repo` scope; fine-grained: Pull requests write).
129
+ It's not subject to that organization-wide Actions restriction, and the
130
+ generated workflow picks it up automatically — no need to re-run `automate`
131
+ or touch the workflow file.
121
132
 
122
133
  ## Templates
123
134
 
package/index.js CHANGED
@@ -132,9 +132,11 @@ before anything reaches your AI provider.
132
132
  generate full run (always shows the estimate and asks first)
133
133
  update incremental: re-summarizes only changed modules
134
134
  automate write a GitHub Actions workflow that runs update in CI
135
- and opens a PR when docs change (always asks first)
135
+ (commits directly by default; --sync pr for review PRs,
136
+ with the required access set up for you — always asks first)
136
137
  --since <ref> update selector via git diff instead of content hashes
137
138
  --mode <m> automate trigger: push | releases | weekly
139
+ --sync <s> automate apply style: commit (default) | pr
138
140
  --provider <p> override the default provider for this run
139
141
  --model <m> override the model
140
142
  --yes skip confirmations (CI)`,
@@ -8,6 +8,11 @@ const MODES = {
8
8
  weekly: 'weekly (Mondays 06:00 UTC)',
9
9
  };
10
10
 
11
+ const SYNC_MODES = {
12
+ commit: 'commit updates directly to the default branch (zero setup, works everywhere)',
13
+ pr: 'open a review pull request for each update',
14
+ };
15
+
11
16
  function buildTrigger(mode, defaultBranch) {
12
17
  if (mode === 'push') {
13
18
  return [
@@ -41,9 +46,97 @@ function buildTrigger(mode, defaultBranch) {
41
46
  throw new Error(`Unknown automation mode "${mode}". One of: ${Object.keys(MODES).join(', ')}`);
42
47
  }
43
48
 
44
- function buildKnowledgeWorkflow({ mode, provider, envKey, model, defaultBranch = 'main' }) {
49
+ function commitModeLines(defaultBranch) {
50
+ return {
51
+ header: [
52
+ '#',
53
+ `# Updates are committed directly to ${defaultBranch} using the built-in`,
54
+ '# Actions token — no extra permissions, tokens, or settings needed.',
55
+ "# It cannot re-trigger itself: GitHub never runs workflows for pushes",
56
+ '# made with the built-in token, and the path filters ignore these',
57
+ '# files anyway.',
58
+ ],
59
+ permissions: [
60
+ 'permissions:',
61
+ ' contents: write',
62
+ ],
63
+ checkoutWith: [
64
+ ' with:',
65
+ ` ref: ${defaultBranch}`,
66
+ ],
67
+ finalStep: [
68
+ ' - name: Commit the updated knowledge base',
69
+ ' run: |',
70
+ ' if git diff --quiet -- docs/gitset-knowledge AGENTS.md; then',
71
+ ' echo "Knowledge base already up to date."',
72
+ ' exit 0',
73
+ ' fi',
74
+ ' git config user.name "github-actions[bot]"',
75
+ ' git config user.email "41898282+github-actions[bot]@users.noreply.github.com"',
76
+ ' git add docs/gitset-knowledge AGENTS.md',
77
+ ' git commit -m "docs: refresh knowledge base"',
78
+ ` git push origin ${defaultBranch} || (git pull --rebase origin ${defaultBranch} && git push origin ${defaultBranch})`,
79
+ ` echo "Knowledge base refreshed directly on ${defaultBranch}."`,
80
+ ],
81
+ };
82
+ }
83
+
84
+ function prModeLines() {
85
+ return {
86
+ header: [
87
+ '#',
88
+ '# PR mode needs ONE of the following (gitset knowledge automate sets',
89
+ '# this up for you):',
90
+ '# a. "Allow GitHub Actions to create and approve pull requests"',
91
+ '# enabled under Settings > Actions > General, OR',
92
+ '# b. a repository secret GITSET_PR_TOKEN holding a GitHub token',
93
+ '# that can open pull requests (used automatically when present;',
94
+ '# not subject to organization-level Actions restrictions).',
95
+ '# Without either, the update still commits and pushes its branch',
96
+ '# safely — only opening the review PR fails, and this job reports',
97
+ '# that as a failure so it is never silently missed.',
98
+ ],
99
+ permissions: [
100
+ 'permissions:',
101
+ ' contents: write',
102
+ ' pull-requests: write',
103
+ ],
104
+ checkoutWith: [],
105
+ finalStep: [
106
+ ' - name: Open a PR when the knowledge base changed',
107
+ ' env:',
108
+ ' GH_TOKEN: ${{ secrets.GITSET_PR_TOKEN || github.token }}',
109
+ ' run: |',
110
+ ' if git diff --quiet -- docs/gitset-knowledge AGENTS.md; then',
111
+ ' echo "Knowledge base already up to date."',
112
+ ' exit 0',
113
+ ' fi',
114
+ ' git config user.name "github-actions[bot]"',
115
+ ' git config user.email "41898282+github-actions[bot]@users.noreply.github.com"',
116
+ ' git switch -c gitset/knowledge-update',
117
+ ' git add docs/gitset-knowledge AGENTS.md',
118
+ ' git commit -m "docs: refresh knowledge base"',
119
+ ' git push -f origin gitset/knowledge-update',
120
+ ' echo "Committed and pushed gitset/knowledge-update — this work is now safe on GitHub regardless of the next step."',
121
+ ' if PR_OUTPUT=$(gh pr create --title "docs: refresh knowledge base" --body "Automated incremental update by [Gitset](https://gitset.dev) Knowledge Mapper. Only changed modules were re-analyzed; review like any other docs change." 2>&1); then',
122
+ ' echo "$PR_OUTPUT"',
123
+ ' elif echo "$PR_OUTPUT" | grep -qi "already exists"; then',
124
+ ' echo "A pull request for gitset/knowledge-update already exists — nothing further to do."',
125
+ ' echo "$PR_OUTPUT"',
126
+ ' else',
127
+ ' echo "$PR_OUTPUT"',
128
+ ' echo "::error::Could not open the pull request automatically (the branch and commit ARE safely pushed — no work was lost). Run \'gitset knowledge automate\' again and pick PR mode to set this up automatically, or add a repository secret GITSET_PR_TOKEN with a GitHub token that can open PRs, or open it yourself: https://github.com/${{ github.repository }}/pull/new/gitset/knowledge-update"',
129
+ ' exit 1',
130
+ ' fi',
131
+ ],
132
+ };
133
+ }
134
+
135
+ function buildKnowledgeWorkflow({ mode, syncMode = 'commit', provider, envKey, model, defaultBranch = 'main' }) {
45
136
  if (!provider || !envKey) throw new Error('provider and envKey are required');
137
+ if (!SYNC_MODES[syncMode]) throw new Error(`Unknown sync mode "${syncMode}". One of: ${Object.keys(SYNC_MODES).join(', ')}`);
46
138
  const updateCmd = `gitset knowledge update --yes --provider ${provider}${model ? ` --model ${model}` : ''}`;
139
+ const sync = syncMode === 'commit' ? commitModeLines(defaultBranch) : prModeLines();
47
140
 
48
141
  return [
49
142
  'name: Gitset Knowledge Mapper',
@@ -53,16 +146,9 @@ function buildKnowledgeWorkflow({ mode, provider, envKey, model, defaultBranch =
53
146
  '# provider — never to Gitset. Runs whose mapped source files are',
54
147
  '# unchanged exit without any AI call (content-hash diff), so harmless',
55
148
  '# triggers cost nothing beyond a few CI seconds.',
56
- '#',
57
- '# Also requires "Allow GitHub Actions to create and approve pull',
58
- '# requests" to be enabled: Settings > Actions > General > Workflow',
59
- '# permissions. Without it, the update still commits and pushes',
60
- '# safely, but opening the review PR fails and this job reports it',
61
- '# as a failure so it is never silently missed.',
149
+ ...sync.header,
62
150
  ...buildTrigger(mode, defaultBranch),
63
- 'permissions:',
64
- ' contents: write',
65
- ' pull-requests: write',
151
+ ...sync.permissions,
66
152
  'concurrency:',
67
153
  ' group: gitset-knowledge',
68
154
  ' cancel-in-progress: false',
@@ -71,6 +157,7 @@ function buildKnowledgeWorkflow({ mode, provider, envKey, model, defaultBranch =
71
157
  ' runs-on: ubuntu-latest',
72
158
  ' steps:',
73
159
  ' - uses: actions/checkout@v4',
160
+ ...sync.checkoutWith,
74
161
  ' - uses: actions/setup-node@v4',
75
162
  ' with:',
76
163
  " node-version: '20'",
@@ -79,31 +166,7 @@ function buildKnowledgeWorkflow({ mode, provider, envKey, model, defaultBranch =
79
166
  ' env:',
80
167
  ` ${envKey}: \${{ secrets.${envKey} }}`,
81
168
  ` run: ${updateCmd}`,
82
- ' - name: Open a PR when the knowledge base changed',
83
- ' env:',
84
- ' GH_TOKEN: ${{ github.token }}',
85
- ' run: |',
86
- ' if git diff --quiet -- docs/gitset-knowledge AGENTS.md; then',
87
- ' echo "Knowledge base already up to date."',
88
- ' exit 0',
89
- ' fi',
90
- ' git config user.name "github-actions[bot]"',
91
- ' git config user.email "41898282+github-actions[bot]@users.noreply.github.com"',
92
- ' git switch -c gitset/knowledge-update',
93
- ' git add docs/gitset-knowledge AGENTS.md',
94
- ' git commit -m "docs: refresh knowledge base"',
95
- ' git push -f origin gitset/knowledge-update',
96
- ' echo "Committed and pushed gitset/knowledge-update — this work is now safe on GitHub regardless of the next step."',
97
- ' if PR_OUTPUT=$(gh pr create --title "docs: refresh knowledge base" --body "Automated incremental update by [Gitset](https://gitset.dev) Knowledge Mapper. Only changed modules were re-analyzed; review like any other docs change." 2>&1); then',
98
- ' echo "$PR_OUTPUT"',
99
- ' elif echo "$PR_OUTPUT" | grep -qi "already exists"; then',
100
- ' echo "A pull request for gitset/knowledge-update already exists — nothing further to do."',
101
- ' echo "$PR_OUTPUT"',
102
- ' else',
103
- ' echo "$PR_OUTPUT"',
104
- ' echo "::error::Could not open the pull request automatically (the branch and commit ARE safely pushed — no work was lost). This is usually because \'Allow GitHub Actions to create and approve pull requests\' is disabled for this repository: enable it under Settings > Actions > General > Workflow permissions, then either re-run this workflow or open the PR yourself: https://github.com/${{ github.repository }}/pull/new/gitset/knowledge-update"',
105
- ' exit 1',
106
- ' fi',
169
+ ...sync.finalStep,
107
170
  '',
108
171
  ].join('\n');
109
172
  }
@@ -111,5 +174,6 @@ function buildKnowledgeWorkflow({ mode, provider, envKey, model, defaultBranch =
111
174
  module.exports = {
112
175
  WORKFLOW_PATH,
113
176
  MODES,
177
+ SYNC_MODES,
114
178
  buildKnowledgeWorkflow,
115
179
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitset-dev/cli",
3
- "version": "2.4.2",
3
+ "version": "2.5.0",
4
4
  "description": "Gitset CLI — drafts commits, PRs, issues, READMEs and release notes with your own AI key. Fully local: your code and keys never touch a Gitset server. Draft. Refine. Ship.",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -520,11 +520,66 @@ async function ensurePrPermission(repo, argv) {
520
520
  log('Enabled — CI will be able to open the review PR.', 'green');
521
521
  return 'enabled';
522
522
  } catch {
523
- log('Could not change it automatically (you may not have admin access on this repo, or an organization policy may lock this setting).', 'yellow');
523
+ log('Could not change it automatically you may not have admin access on this repo, or an organization policy locks this setting (if it appears greyed out in the GitHub UI at both the repo and org level, that\'s a hard enforcement you cannot toggle either way).', 'yellow');
524
524
  return 'failed';
525
525
  }
526
526
  }
527
527
 
528
+ async function provisionPrToken(repo, argv) {
529
+ if (!repo.includes('/')) return 'unavailable';
530
+
531
+ const existing = (() => {
532
+ try {
533
+ const out = execFileSync('gh', ['secret', 'list', '--repo', repo], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
534
+ return out.includes('GITSET_PR_TOKEN');
535
+ } catch {
536
+ return false;
537
+ }
538
+ })();
539
+ if (existing) {
540
+ log('The GITSET_PR_TOKEN secret already exists in this repository — PR mode is ready.', 'green');
541
+ return 'already-provisioned';
542
+ }
543
+
544
+ log('\nNo problem — this can be fixed right now, automatically:', 'cyan');
545
+ log(' Your repository (or its organization) blocks the built-in CI token from opening pull requests.', 'reset');
546
+ log(' The fix: store a token from YOUR current GitHub login (the same one the gh CLI already uses) as an encrypted secret named GITSET_PR_TOKEN in this repository.', 'reset');
547
+ log(' · You don\'t create anything manually — no settings pages, no token forms.', 'dim');
548
+ log(' · The token is stored encrypted by GitHub, in this repository only.', 'dim');
549
+ log(' · CI will open pull requests as you, which your organization does allow.', 'dim');
550
+ log(' · Undo anytime: delete the GITSET_PR_TOKEN secret and CI falls back to the built-in token.', 'dim');
551
+
552
+ const proceed = await confirmOrAbort('\nStore it now?', argv);
553
+ if (!proceed) return 'declined';
554
+
555
+ let token = '';
556
+ try {
557
+ token = execFileSync('gh', ['auth', 'token'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
558
+ } catch { }
559
+ if (!token) {
560
+ log('Could not read a token from your gh login. Run "gh auth login" first, then re-run: gitset knowledge automate', 'red');
561
+ return 'failed';
562
+ }
563
+
564
+ try {
565
+ execFileSync('gh', ['secret', 'set', 'GITSET_PR_TOKEN', '--repo', repo], { input: token, stdio: ['pipe', 'pipe', 'pipe'] });
566
+ } catch {
567
+ log('Could not store the secret (you may not have admin access on this repository).', 'red');
568
+ return 'failed';
569
+ }
570
+
571
+ try {
572
+ const out = execFileSync('gh', ['secret', 'list', '--repo', repo], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
573
+ if (!out.includes('GITSET_PR_TOKEN')) throw new Error('not visible');
574
+ } catch {
575
+ log('The secret was sent but could not be verified — check https://github.com/' + repo + '/settings/secrets/actions', 'yellow');
576
+ return 'unverified';
577
+ }
578
+
579
+ log('Done — GITSET_PR_TOKEN is stored. CI can now open pull requests, starting with the very next run.', 'green');
580
+ return 'provisioned';
581
+ }
582
+
528
583
  async function runAutomate(rootDir, argv) {
529
584
  let cfg;
530
585
  try {
@@ -565,8 +620,44 @@ async function runAutomate(rootDir, argv) {
565
620
  return 1;
566
621
  }
567
622
 
623
+ const repo = repoLabel(rootDir);
624
+
625
+ let syncMode = flag(argv, '--sync');
626
+ if (!syncMode) {
627
+ if (!process.stdin.isTTY || argv.includes('--yes') || argv.includes('-y')) {
628
+ syncMode = 'commit';
629
+ } else {
630
+ syncMode = await selectOption('How should CI apply each update?', [
631
+ { label: `Commit directly to ${defaultBranch} (recommended — zero setup, works on every repo and org)`, value: 'commit' },
632
+ { label: 'Open a review pull request for each update (one extra one-time step — automated for you)', value: 'pr' },
633
+ { label: 'Cancel', value: 'cancel' },
634
+ ]);
635
+ if (syncMode === 'cancel') {
636
+ log('Nothing written.', 'yellow');
637
+ return 0;
638
+ }
639
+ }
640
+ }
641
+ if (!km.SYNC_MODES[syncMode]) {
642
+ log(`Unknown sync mode "${syncMode}". One of: ${Object.keys(km.SYNC_MODES).join(', ')}`, 'red');
643
+ return 1;
644
+ }
645
+
646
+ let prReady = null;
647
+ if (syncMode === 'pr') {
648
+ const prPermission = await ensurePrPermission(repo, argv);
649
+ if (prPermission === 'enabled' || prPermission === 'already-enabled') {
650
+ prReady = 'permission';
651
+ } else {
652
+ const patStatus = await provisionPrToken(repo, argv);
653
+ if (patStatus === 'provisioned' || patStatus === 'already-provisioned') prReady = 'token';
654
+ else if (patStatus === 'unverified') prReady = 'token-unverified';
655
+ }
656
+ }
657
+
568
658
  const yaml = km.buildKnowledgeWorkflow({
569
659
  mode,
660
+ syncMode,
570
661
  provider: cfg.provider,
571
662
  envKey,
572
663
  model: cfg.model || null,
@@ -590,9 +681,6 @@ async function runAutomate(rootDir, argv) {
590
681
  fs.writeFileSync(target, yaml);
591
682
  log(`\nWrote ${km.WORKFLOW_PATH}.`, 'green');
592
683
 
593
- const repo = repoLabel(rootDir);
594
- const prPermission = await ensurePrPermission(repo, argv);
595
-
596
684
  log('\nNext steps:', 'cyan');
597
685
  log(` 1. Add the repository secret ${envKey} with your ${cfg.provider} API key:`, 'reset');
598
686
  if (repo.includes('/')) {
@@ -600,20 +688,24 @@ async function runAutomate(rootDir, argv) {
600
688
  } else {
601
689
  log(' GitHub repo > Settings > Secrets and variables > Actions', 'dim');
602
690
  }
603
- if (prPermission === 'enabled' || prPermission === 'already-enabled') {
604
- log(' 2. "Allow GitHub Actions to create and approve pull requests" is already on — the update PR step is ready.', 'reset');
691
+ log(' 2. Make sure the knowledge base itself is committed (gitset knowledge generate, then commit docs/gitset-knowledge/ and AGENTS.md).', 'reset');
692
+ log(' 3. Commit and push the workflow file.', 'reset');
693
+
694
+ if (syncMode === 'commit') {
695
+ log(`\nThat's it — CI will commit refreshed docs straight to ${defaultBranch} whenever mapped source changes. No permissions to touch, no tokens to create, and runs with no mapped changes make zero AI calls.`, 'dim');
696
+ log('Prefer reviewing each update as a pull request instead? Re-run: gitset knowledge automate --sync pr', 'dim');
605
697
  } else {
606
- log(' 2. Enable "Allow GitHub Actions to create and approve pull requests" (required for the update PR):', 'reset');
607
- if (repo.includes('/')) {
608
- log(` https://github.com/${repo}/settings/actions`, 'dim');
698
+ if (prReady === 'permission') {
699
+ log('\nPR mode is ready: GitHub Actions is allowed to open pull requests in this repository.', 'dim');
700
+ } else if (prReady === 'token') {
701
+ log('\nPR mode is ready: CI will open pull requests using the GITSET_PR_TOKEN secret that was just stored.', 'dim');
702
+ } else if (prReady === 'token-unverified') {
703
+ log(`\nPR mode should be ready, but the stored secret could not be verified — double-check https://github.com/${repo}/settings/secrets/actions`, 'yellow');
609
704
  } else {
610
- log(' GitHub repo > Settings > Actions > General > Workflow permissions', 'dim');
705
+ log('\nPR mode is NOT ready yet: CI cannot open pull requests in this repository until one of the fixes above is applied. Updates will still commit and push their branch safely — only the PR step will fail, visibly. Re-run "gitset knowledge automate" anytime to finish the setup, or switch to zero-setup direct commits with: gitset knowledge automate --sync commit', 'yellow');
611
706
  }
612
- log(' Without this, the update still runs and commits safelyonly opening the review PR fails, and the workflow run will show that failure clearly rather than hide it.', 'dim');
707
+ log('When mapped source changes land, CI opens a PR on branch gitset/knowledge-updateyou review it like any docs change. Runs with no mapped changes make zero AI calls.', 'dim');
613
708
  }
614
- log(' 3. Make sure the knowledge base itself is committed (gitset knowledge generate, then commit docs/gitset-knowledge/ and AGENTS.md).', 'reset');
615
- log(' 4. Commit and push the workflow file.', 'reset');
616
- log('\nWhen mapped source changes land, CI opens a PR on branch gitset/knowledge-update — you review it like any docs change. Runs with no mapped changes make zero AI calls.', 'dim');
617
709
  return 0;
618
710
  }
619
711
 
@@ -644,7 +736,8 @@ async function runKnowledgeCommand(argv = []) {
644
736
  log(' generate build docs/gitset-knowledge/ from source code (asks before spending)', 'dim');
645
737
  log(' update incremental refresh: only changed modules are re-summarized', 'dim');
646
738
  log(' automate write a CI workflow that keeps it fresh (always asks first)', 'dim');
647
- log(' flags --provider <p> --model <m> --yes --since <ref> (update) --mode <push|releases|weekly> (automate)', 'dim');
739
+ log(' flags --provider <p> --model <m> --yes --since <ref> (update)', 'dim');
740
+ log(' --mode <push|releases|weekly> --sync <commit|pr> (automate)', 'dim');
648
741
  return verb ? 1 : 0;
649
742
  }
650
743
  }