@gitset-dev/cli 2.4.0 → 2.4.2
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 +32 -0
- package/lib/knowledge/ci.js +17 -1
- package/lib/prompts/defaults.js +8 -4
- package/package.json +1 -1
- package/src/commands/knowledge.js +80 -10
package/README.md
CHANGED
|
@@ -87,6 +87,38 @@ Local tools (no AI):
|
|
|
87
87
|
Common flags: `--provider` `--model` `--yes` `--print` `--json`. Run
|
|
88
88
|
`gitset help <command>` for everything else.
|
|
89
89
|
|
|
90
|
+
## Knowledge Mapper
|
|
91
|
+
|
|
92
|
+
`gitset knowledge` builds and maintains `docs/gitset-knowledge/` — a
|
|
93
|
+
structured, always-current map of your codebase (architecture, module
|
|
94
|
+
boundaries, commands, dependency graph) that both developers and AI coding
|
|
95
|
+
agents can read before touching your code. It's generated from your source
|
|
96
|
+
code, manifests, and CI configuration — never from your existing prose docs,
|
|
97
|
+
so it can't inherit their drift.
|
|
98
|
+
|
|
99
|
+
```sh
|
|
100
|
+
gitset knowledge init # scaffold optional .gitset-knowledge.json (include/exclude globs)
|
|
101
|
+
gitset knowledge scan # zero AI calls — discovers files, prints the plan + exact cost estimate
|
|
102
|
+
gitset knowledge generate # shows the estimate again, asks to confirm, then writes the knowledge base
|
|
103
|
+
gitset knowledge update # incremental — only changed modules (and their direct importers) are re-summarized
|
|
104
|
+
gitset knowledge automate # writes a GitHub Actions workflow that runs `update` in CI and opens a review PR
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
It's a six-stage local pipeline (Discover → Map → Summarize → Plan → Write →
|
|
108
|
+
Validate): Discover and Map walk the repository and build an import graph
|
|
109
|
+
with zero AI calls; only Summarize and Write touch your configured provider
|
|
110
|
+
— there's no separate "default model" for this tool, it uses whatever you
|
|
111
|
+
picked with `gitset config`; and a deterministic Validate pass checks every
|
|
112
|
+
generated link, command, and script before anything is written to disk.
|
|
113
|
+
Secrets are redacted locally before any file content is sent, and prose
|
|
114
|
+
docs / test file bodies are listed structurally but never sent at all.
|
|
115
|
+
|
|
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.
|
|
121
|
+
|
|
90
122
|
## Templates
|
|
91
123
|
|
|
92
124
|
Define your commit style, issue structure, PR format, README skeleton, or
|
package/lib/knowledge/ci.js
CHANGED
|
@@ -53,6 +53,12 @@ function buildKnowledgeWorkflow({ mode, provider, envKey, model, defaultBranch =
|
|
|
53
53
|
'# provider — never to Gitset. Runs whose mapped source files are',
|
|
54
54
|
'# unchanged exit without any AI call (content-hash diff), so harmless',
|
|
55
55
|
'# 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.',
|
|
56
62
|
...buildTrigger(mode, defaultBranch),
|
|
57
63
|
'permissions:',
|
|
58
64
|
' contents: write',
|
|
@@ -87,7 +93,17 @@ function buildKnowledgeWorkflow({ mode, provider, envKey, model, defaultBranch =
|
|
|
87
93
|
' git add docs/gitset-knowledge AGENTS.md',
|
|
88
94
|
' git commit -m "docs: refresh knowledge base"',
|
|
89
95
|
' git push -f origin gitset/knowledge-update',
|
|
90
|
-
'
|
|
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',
|
|
91
107
|
'',
|
|
92
108
|
].join('\n');
|
|
93
109
|
}
|
package/lib/prompts/defaults.js
CHANGED
|
@@ -151,10 +151,14 @@ const knowledgeSummarize = {
|
|
|
151
151
|
'exposes — module.exports, export statements, __all__; an executable or ' +
|
|
152
152
|
'script with no exports gets [], never its internal functions; max 12), ' +
|
|
153
153
|
'"dependencies": string[] (notable internal files or external ' +
|
|
154
|
-
'packages this file relies on, max 10
|
|
155
|
-
'
|
|
156
|
-
'
|
|
157
|
-
'
|
|
154
|
+
'packages this file relies on, max 10 — each entry must be a distinct, ' +
|
|
155
|
+
'real import copied from the file\'s own import/require statements; if ' +
|
|
156
|
+
'you are not certain of one, omit it rather than guessing, and never ' +
|
|
157
|
+
'repeat or extend a name into a longer variant), "notes": string ' +
|
|
158
|
+
'(<= 160 chars — gotchas, side effects, or "" if none)}]. One entry per ' +
|
|
159
|
+
'FILE block in the input, using the exact path given. For a dispatcher/' +
|
|
160
|
+
'entry-point file, note in "notes" the exact command or route names it ' +
|
|
161
|
+
'registers.',
|
|
158
162
|
user: [
|
|
159
163
|
repo && `Repository: ${clip(repo, 200)}`,
|
|
160
164
|
module && `Module: ${clip(module, 200)}`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitset-dev/cli",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.2",
|
|
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": {
|
|
@@ -125,6 +125,12 @@ async function confirmOrAbort(question, argv) {
|
|
|
125
125
|
return answer === 'y' || answer === 'yes';
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
function appendModuleEntries(moduleSummaries, moduleName, entries) {
|
|
129
|
+
const prev = moduleSummaries[moduleName];
|
|
130
|
+
const prevArray = Array.isArray(prev) ? prev : [];
|
|
131
|
+
moduleSummaries[moduleName] = [...prevArray, ...entries];
|
|
132
|
+
}
|
|
133
|
+
|
|
128
134
|
async function summarizeBatches({ batches, repo, argv, cachedSummaries = {}, usage }) {
|
|
129
135
|
const moduleSummaries = { ...cachedSummaries };
|
|
130
136
|
let providerUsed = null;
|
|
@@ -133,7 +139,7 @@ async function summarizeBatches({ batches, repo, argv, cachedSummaries = {}, usa
|
|
|
133
139
|
for (const batch of batches) {
|
|
134
140
|
done += 1;
|
|
135
141
|
log(` [${done}/${batches.length}] Summarizing ${batch.module}${batch.part > 1 ? ` (part ${batch.part})` : ''}…`, 'dim');
|
|
136
|
-
const
|
|
142
|
+
const summarizeBatch = (temperature) => genLocal.generate({
|
|
137
143
|
tool: 'knowledgeSummarize',
|
|
138
144
|
ctx: {
|
|
139
145
|
repo,
|
|
@@ -143,19 +149,37 @@ async function summarizeBatches({ batches, repo, argv, cachedSummaries = {}, usa
|
|
|
143
149
|
provider: flag(argv, '--provider'),
|
|
144
150
|
model: flag(argv, '--model'),
|
|
145
151
|
maxTokens: km.DEFAULTS.summarizeMaxTokens,
|
|
146
|
-
temperature
|
|
152
|
+
temperature,
|
|
147
153
|
interactive: true,
|
|
148
154
|
});
|
|
155
|
+
|
|
156
|
+
let result = await summarizeBatch(0.2);
|
|
157
|
+
trackUsage(usage, result);
|
|
149
158
|
providerUsed = result.provider;
|
|
150
159
|
modelUsed = result.model;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
160
|
+
let entries = km.parseSummarizeResponse(result.raw);
|
|
161
|
+
|
|
162
|
+
if (!entries) {
|
|
163
|
+
log(` Structured parse failed for ${batch.module}${batch.part > 1 ? ` (part ${batch.part})` : ''}; retrying once…`, 'yellow');
|
|
164
|
+
result = await summarizeBatch(0.5);
|
|
165
|
+
trackUsage(usage, result);
|
|
166
|
+
providerUsed = result.provider;
|
|
167
|
+
modelUsed = result.model;
|
|
168
|
+
entries = km.parseSummarizeResponse(result.raw);
|
|
169
|
+
}
|
|
170
|
+
|
|
154
171
|
if (entries) {
|
|
155
|
-
moduleSummaries
|
|
172
|
+
appendModuleEntries(moduleSummaries, batch.module, entries);
|
|
156
173
|
} else {
|
|
157
|
-
log(`
|
|
158
|
-
|
|
174
|
+
log(` Still unparseable after retry; recording ${batch.files.length} file(s) as summary-unavailable (no data lost for the rest of the module).`, 'yellow');
|
|
175
|
+
const placeholders = batch.files.map((f) => ({
|
|
176
|
+
path: f.path,
|
|
177
|
+
purpose: '(AI summary unavailable — the model\'s response for this batch could not be parsed after a retry)',
|
|
178
|
+
exports: [],
|
|
179
|
+
dependencies: [],
|
|
180
|
+
notes: '',
|
|
181
|
+
}));
|
|
182
|
+
appendModuleEntries(moduleSummaries, batch.module, placeholders);
|
|
159
183
|
}
|
|
160
184
|
}
|
|
161
185
|
return { moduleSummaries, providerUsed, modelUsed };
|
|
@@ -468,6 +492,39 @@ async function runUpdate(rootDir, argv) {
|
|
|
468
492
|
}
|
|
469
493
|
}
|
|
470
494
|
|
|
495
|
+
function ghApiJson(args) {
|
|
496
|
+
try {
|
|
497
|
+
const out = execFileSync('gh', ['api', ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
498
|
+
return JSON.parse(out);
|
|
499
|
+
} catch {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
async function ensurePrPermission(repo, argv) {
|
|
505
|
+
if (!repo.includes('/')) return 'unknown';
|
|
506
|
+
const current = ghApiJson([`/repos/${repo}/actions/permissions/workflow`]);
|
|
507
|
+
if (!current) return 'unknown';
|
|
508
|
+
if (current.can_approve_pull_request_reviews) return 'already-enabled';
|
|
509
|
+
|
|
510
|
+
log('\n"Allow GitHub Actions to create and approve pull requests" is currently OFF for this repository. The update step will still run and commit safely without it — only opening the review PR will fail.', 'yellow');
|
|
511
|
+
const proceed = await confirmOrAbort('Enable it now via the GitHub API? (only this one setting changes)', argv);
|
|
512
|
+
if (!proceed) return 'declined';
|
|
513
|
+
|
|
514
|
+
try {
|
|
515
|
+
execFileSync('gh', [
|
|
516
|
+
'api', '--method', 'PUT', `/repos/${repo}/actions/permissions/workflow`,
|
|
517
|
+
'-f', `default_workflow_permissions=${current.default_workflow_permissions}`,
|
|
518
|
+
'-F', 'can_approve_pull_request_reviews=true',
|
|
519
|
+
], { stdio: 'pipe' });
|
|
520
|
+
log('Enabled — CI will be able to open the review PR.', 'green');
|
|
521
|
+
return 'enabled';
|
|
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');
|
|
524
|
+
return 'failed';
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
471
528
|
async function runAutomate(rootDir, argv) {
|
|
472
529
|
let cfg;
|
|
473
530
|
try {
|
|
@@ -534,6 +591,8 @@ async function runAutomate(rootDir, argv) {
|
|
|
534
591
|
log(`\nWrote ${km.WORKFLOW_PATH}.`, 'green');
|
|
535
592
|
|
|
536
593
|
const repo = repoLabel(rootDir);
|
|
594
|
+
const prPermission = await ensurePrPermission(repo, argv);
|
|
595
|
+
|
|
537
596
|
log('\nNext steps:', 'cyan');
|
|
538
597
|
log(` 1. Add the repository secret ${envKey} with your ${cfg.provider} API key:`, 'reset');
|
|
539
598
|
if (repo.includes('/')) {
|
|
@@ -541,8 +600,19 @@ async function runAutomate(rootDir, argv) {
|
|
|
541
600
|
} else {
|
|
542
601
|
log(' GitHub repo > Settings > Secrets and variables > Actions', 'dim');
|
|
543
602
|
}
|
|
544
|
-
|
|
545
|
-
|
|
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');
|
|
605
|
+
} 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');
|
|
609
|
+
} else {
|
|
610
|
+
log(' GitHub repo > Settings > Actions > General > Workflow permissions', 'dim');
|
|
611
|
+
}
|
|
612
|
+
log(' Without this, the update still runs and commits safely — only opening the review PR fails, and the workflow run will show that failure clearly rather than hide it.', 'dim');
|
|
613
|
+
}
|
|
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');
|
|
546
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');
|
|
547
617
|
return 0;
|
|
548
618
|
}
|