@stackbilt/cli 0.1.6 → 0.1.9
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 +72 -19
- package/dist/commands/setup.js +14 -9
- package/dist/commands/why.d.ts +3 -0
- package/dist/commands/why.js +168 -0
- package/dist/index.js +15 -7
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -10,7 +10,8 @@ CLI entry point for Charter Kit -- a local-first governance toolkit for software
|
|
|
10
10
|
npm install -g @stackbilt/cli
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
This pulls in all Charter Kit packages automatically. You get the `charter` command globally.
|
|
13
|
+
This pulls in all Charter Kit packages automatically. You get the `charter` command globally.
|
|
14
|
+
Use `charter setup` inside each repo to scaffold governance baseline files.
|
|
14
15
|
|
|
15
16
|
For CI pipelines, install as a dev dependency instead:
|
|
16
17
|
|
|
@@ -18,24 +19,76 @@ For CI pipelines, install as a dev dependency instead:
|
|
|
18
19
|
npm install --save-dev @stackbilt/cli
|
|
19
20
|
```
|
|
20
21
|
|
|
21
|
-
Requires Node >= 18.
|
|
22
|
-
|
|
23
|
-
## Quick Start
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
charter
|
|
27
|
-
charter
|
|
28
|
-
charter
|
|
29
|
-
charter
|
|
30
|
-
charter
|
|
31
|
-
charter
|
|
32
|
-
|
|
22
|
+
Requires Node >= 18.
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
charter # quick value/risk snapshot + next action
|
|
28
|
+
charter why # why teams adopt Charter and expected payoff
|
|
29
|
+
charter setup # bootstrap .charter/ directory + policy baseline
|
|
30
|
+
charter doctor # check CLI + config health
|
|
31
|
+
charter validate # validate commit governance trailers
|
|
32
|
+
charter drift # scan for blessed-stack drift
|
|
33
|
+
charter audit # generate governance audit report
|
|
34
|
+
charter classify "migrate auth provider"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Human Onboarding (Copy/Paste)
|
|
38
|
+
|
|
39
|
+
Run this in the target repository:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install -g @stackbilt/cli
|
|
43
|
+
charter
|
|
44
|
+
charter setup --ci github
|
|
45
|
+
charter doctor --format json
|
|
46
|
+
charter validate --format text
|
|
47
|
+
charter drift --format text
|
|
48
|
+
charter audit --format text
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
This ensures people immediately see:
|
|
52
|
+
- what Charter is doing in their repo
|
|
53
|
+
- where baseline files were created (`.charter/*`)
|
|
54
|
+
- how policy checks behave before merge
|
|
55
|
+
|
|
56
|
+
## LM Agent Onboarding (Deterministic)
|
|
57
|
+
|
|
58
|
+
Use JSON output and explicit CI semantics:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
charter --format json
|
|
62
|
+
charter setup --ci github --yes --format json
|
|
63
|
+
charter doctor --format json
|
|
64
|
+
charter validate --format json --ci
|
|
65
|
+
charter drift --format json --ci
|
|
66
|
+
charter audit --format json
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Agent handling contract:
|
|
70
|
+
- `exit 0`: pass
|
|
71
|
+
- `exit 1`: policy violation (action required)
|
|
72
|
+
- `exit 2`: runtime/usage failure
|
|
33
73
|
|
|
34
74
|
## Commands
|
|
35
75
|
|
|
36
|
-
### `charter
|
|
37
|
-
|
|
38
|
-
|
|
76
|
+
### `charter` (no args)
|
|
77
|
+
|
|
78
|
+
Default first-run experience. Shows:
|
|
79
|
+
- repository governance baseline status
|
|
80
|
+
- commit governance coverage snapshot
|
|
81
|
+
- high-risk unlinked commit count
|
|
82
|
+
- one recommended next action
|
|
83
|
+
|
|
84
|
+
### `charter why`
|
|
85
|
+
|
|
86
|
+
Explains the adoption case in plain terms (problem, what Charter enforces, expected operational payoff).
|
|
87
|
+
|
|
88
|
+
### `charter setup`
|
|
89
|
+
|
|
90
|
+
Bootstrap `.charter/` with config, patterns, and policies. Optionally generates a GitHub Actions workflow.
|
|
91
|
+
This is the command that applies Charter governance into a repository.
|
|
39
92
|
|
|
40
93
|
```bash
|
|
41
94
|
charter setup --ci github --yes
|
|
@@ -69,13 +122,13 @@ charter drift --path ./src --ci
|
|
|
69
122
|
|
|
70
123
|
Generate a governance audit report covering trailers, risk, and drift.
|
|
71
124
|
|
|
72
|
-
### `charter classify`
|
|
125
|
+
### `charter classify`
|
|
73
126
|
|
|
74
127
|
Classify a change as `SURFACE`, `LOCAL`, or `CROSS_CUTTING`.
|
|
75
128
|
|
|
76
129
|
```bash
|
|
77
|
-
charter classify "update button color"
|
|
78
|
-
```
|
|
130
|
+
charter classify "update button color"
|
|
131
|
+
```
|
|
79
132
|
|
|
80
133
|
## Global Options
|
|
81
134
|
|
package/dist/commands/setup.js
CHANGED
|
@@ -107,17 +107,22 @@ async function setupCommand(options, args) {
|
|
|
107
107
|
console.log(JSON.stringify(result, null, 2));
|
|
108
108
|
return index_2.EXIT_CODE.SUCCESS;
|
|
109
109
|
}
|
|
110
|
-
console.log('
|
|
111
|
-
console.log(`
|
|
112
|
-
console.log(`
|
|
110
|
+
console.log(' Governance guardrails are now active for this repo.');
|
|
111
|
+
console.log(` Baseline path: ${result.configPath}`);
|
|
112
|
+
console.log(` Baseline created: ${result.initialized ? 'yes' : 'already present'}`);
|
|
113
113
|
if (result.workflow.mode === 'github') {
|
|
114
|
-
console.log(`
|
|
114
|
+
console.log(` CI policy gate: ${result.workflow.created ? 'enabled' : 'already present'} (${result.workflow.path})`);
|
|
115
115
|
}
|
|
116
116
|
console.log('');
|
|
117
|
-
console.log('
|
|
118
|
-
console.log('
|
|
119
|
-
console.log('
|
|
120
|
-
console.log('
|
|
117
|
+
console.log(' What this gives you immediately:');
|
|
118
|
+
console.log(' - Merge-time checks for risky changes without governance links');
|
|
119
|
+
console.log(' - Drift detection against your blessed stack');
|
|
120
|
+
console.log(' - Audit-ready governance evidence from repo history');
|
|
121
|
+
console.log('');
|
|
122
|
+
console.log(' Run now:');
|
|
123
|
+
console.log(' 1. charter validate --format text');
|
|
124
|
+
console.log(' 2. charter drift --format text');
|
|
125
|
+
console.log(' 3. charter audit --format text');
|
|
121
126
|
return index_2.EXIT_CODE.SUCCESS;
|
|
122
127
|
}
|
|
123
128
|
function writeFileIfMissing(targetPath, content, force) {
|
|
@@ -137,4 +142,4 @@ function getFlag(args, flag) {
|
|
|
137
142
|
}
|
|
138
143
|
return undefined;
|
|
139
144
|
}
|
|
140
|
-
//# sourceMappingURL=setup.js.map
|
|
145
|
+
//# sourceMappingURL=setup.js.map
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.quickstartCommand = quickstartCommand;
|
|
4
|
+
exports.whyCommand = whyCommand;
|
|
5
|
+
const node_child_process_1 = require("node:child_process");
|
|
6
|
+
const fs = require("node:fs");
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
const index_1 = require("../index");
|
|
9
|
+
const git_1 = require("@stackbilt/git");
|
|
10
|
+
async function quickstartCommand(options) {
|
|
11
|
+
const snapshot = getSnapshot(options.configPath);
|
|
12
|
+
if (options.format === 'json') {
|
|
13
|
+
console.log(JSON.stringify(snapshot, null, 2));
|
|
14
|
+
return index_1.EXIT_CODE.SUCCESS;
|
|
15
|
+
}
|
|
16
|
+
console.log('');
|
|
17
|
+
console.log(' Charter Quickstart');
|
|
18
|
+
console.log(' Turns governance from abstract policy into merge-time guardrails.');
|
|
19
|
+
console.log('');
|
|
20
|
+
console.log(' Repo snapshot:');
|
|
21
|
+
console.log(` - Git repo: ${snapshot.inGitRepo ? 'yes' : 'no'}`);
|
|
22
|
+
console.log(` - Governance baseline (.charter): ${snapshot.hasBaseline ? 'installed' : 'missing'}`);
|
|
23
|
+
console.log(` - Recent commits scanned: ${snapshot.commitsScanned}`);
|
|
24
|
+
console.log(` - Governance-linked commit coverage: ${snapshot.coveragePercent}%`);
|
|
25
|
+
console.log(` - High-risk commits without governance links: ${snapshot.highRiskUnlinked}`);
|
|
26
|
+
console.log('');
|
|
27
|
+
console.log(' Why teams use Charter:');
|
|
28
|
+
console.log(' - Catch risky, unreviewed changes before merge');
|
|
29
|
+
console.log(' - Create an auditable trail from code changes to governance decisions');
|
|
30
|
+
console.log(' - Keep policy checks consistent across local dev and CI');
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log(` Next action: ${snapshot.nextAction}`);
|
|
33
|
+
console.log('');
|
|
34
|
+
return index_1.EXIT_CODE.SUCCESS;
|
|
35
|
+
}
|
|
36
|
+
async function whyCommand(options) {
|
|
37
|
+
if (options.format === 'json') {
|
|
38
|
+
console.log(JSON.stringify({
|
|
39
|
+
problem: 'Teams lose context on risky changes and approvals become inconsistent.',
|
|
40
|
+
charterSolves: [
|
|
41
|
+
'Enforces governance trailers for significant changes',
|
|
42
|
+
'Scans for stack drift against blessed patterns',
|
|
43
|
+
'Produces repeatable audit evidence for PRs and reviews',
|
|
44
|
+
],
|
|
45
|
+
value: [
|
|
46
|
+
'Lower probability of breaking changes landing without architectural context',
|
|
47
|
+
'Faster reviews because reviewers see linked decisions in commit metadata',
|
|
48
|
+
'Clear governance posture for leadership and compliance reporting',
|
|
49
|
+
],
|
|
50
|
+
start: 'charter setup --ci github',
|
|
51
|
+
}, null, 2));
|
|
52
|
+
return index_1.EXIT_CODE.SUCCESS;
|
|
53
|
+
}
|
|
54
|
+
console.log('');
|
|
55
|
+
console.log(' Why Charter');
|
|
56
|
+
console.log('');
|
|
57
|
+
console.log(' Problem it solves:');
|
|
58
|
+
console.log(' Governance intent lives in docs, but risky changes merge without clear decision links.');
|
|
59
|
+
console.log('');
|
|
60
|
+
console.log(' What Charter does:');
|
|
61
|
+
console.log(' - Enforces commit-level governance links for significant changes');
|
|
62
|
+
console.log(' - Detects drift from your blessed architecture patterns');
|
|
63
|
+
console.log(' - Generates audit output leadership and reviewers can actually use');
|
|
64
|
+
console.log('');
|
|
65
|
+
console.log(' Expected payoff:');
|
|
66
|
+
console.log(' - Fewer high-impact surprises in production');
|
|
67
|
+
console.log(' - Faster review cycles with clearer architectural accountability');
|
|
68
|
+
console.log(' - Consistent governance behavior across repos');
|
|
69
|
+
console.log('');
|
|
70
|
+
console.log(' Start here: charter setup --ci github');
|
|
71
|
+
console.log('');
|
|
72
|
+
return index_1.EXIT_CODE.SUCCESS;
|
|
73
|
+
}
|
|
74
|
+
function getSnapshot(configPath) {
|
|
75
|
+
const inGitRepo = isGitRepo();
|
|
76
|
+
const hasBaseline = fs.existsSync(path.join(configPath, 'config.json'));
|
|
77
|
+
if (!inGitRepo) {
|
|
78
|
+
return {
|
|
79
|
+
inGitRepo,
|
|
80
|
+
hasBaseline,
|
|
81
|
+
commitsScanned: 0,
|
|
82
|
+
coveragePercent: 0,
|
|
83
|
+
highRiskUnlinked: 0,
|
|
84
|
+
nextAction: 'Run this inside a git repository, then run: charter setup --ci github',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const commits = hasCommits() ? getRecentCommits(20) : [];
|
|
88
|
+
const parsed = (0, git_1.parseAllTrailers)(commits);
|
|
89
|
+
const linked = new Set();
|
|
90
|
+
for (const t of parsed.governedBy)
|
|
91
|
+
linked.add(t.commitSha);
|
|
92
|
+
for (const t of parsed.resolvesRequest)
|
|
93
|
+
linked.add(t.commitSha);
|
|
94
|
+
let highRiskUnlinked = 0;
|
|
95
|
+
for (const commit of commits) {
|
|
96
|
+
if (!linked.has(commit.sha) && (0, git_1.assessCommitRisk)(commit.files_changed, commit.message) === 'HIGH') {
|
|
97
|
+
highRiskUnlinked++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const coveragePercent = commits.length > 0 ? Math.round((linked.size / commits.length) * 100) : 0;
|
|
101
|
+
const nextAction = !hasBaseline
|
|
102
|
+
? 'Run: charter setup --ci github'
|
|
103
|
+
: highRiskUnlinked > 0
|
|
104
|
+
? 'Run: charter validate --format text and add Governed-By trailers to high-risk commits'
|
|
105
|
+
: 'Run: charter audit --format text for a shareable governance posture report';
|
|
106
|
+
return {
|
|
107
|
+
inGitRepo,
|
|
108
|
+
hasBaseline,
|
|
109
|
+
commitsScanned: commits.length,
|
|
110
|
+
coveragePercent,
|
|
111
|
+
highRiskUnlinked,
|
|
112
|
+
nextAction,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function isGitRepo() {
|
|
116
|
+
try {
|
|
117
|
+
(0, node_child_process_1.execFileSync)('git', ['rev-parse', '--is-inside-work-tree'], { stdio: 'ignore' });
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function getRecentCommits(count) {
|
|
125
|
+
try {
|
|
126
|
+
const log = (0, node_child_process_1.execFileSync)('git', ['log', `-${count}`, '--format=%H|%s', '--name-only'], {
|
|
127
|
+
encoding: 'utf-8',
|
|
128
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
129
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
130
|
+
});
|
|
131
|
+
const commits = [];
|
|
132
|
+
let current = null;
|
|
133
|
+
for (const line of log.split('\n')) {
|
|
134
|
+
if (line.includes('|') && line.length > 40) {
|
|
135
|
+
if (current)
|
|
136
|
+
commits.push(current);
|
|
137
|
+
const [sha, ...message] = line.split('|');
|
|
138
|
+
current = {
|
|
139
|
+
sha,
|
|
140
|
+
author: '',
|
|
141
|
+
timestamp: '',
|
|
142
|
+
message: message.join('|'),
|
|
143
|
+
files_changed: [],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
else if (line.trim() && current) {
|
|
147
|
+
current.files_changed.push(line.trim());
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (current)
|
|
151
|
+
commits.push(current);
|
|
152
|
+
return commits;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function hasCommits() {
|
|
159
|
+
try {
|
|
160
|
+
(0, node_child_process_1.execFileSync)('git', ['rev-parse', '--verify', 'HEAD'], {
|
|
161
|
+
stdio: 'ignore',
|
|
162
|
+
});
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -15,18 +15,21 @@ const validate_1 = require("./commands/validate");
|
|
|
15
15
|
const audit_1 = require("./commands/audit");
|
|
16
16
|
const drift_1 = require("./commands/drift");
|
|
17
17
|
const classify_1 = require("./commands/classify");
|
|
18
|
+
const why_1 = require("./commands/why");
|
|
18
19
|
const package_json_1 = require("../package.json");
|
|
19
20
|
const CLI_VERSION = package_json_1.version;
|
|
20
21
|
const HELP = `
|
|
21
22
|
charter - repo-level governance toolkit
|
|
22
23
|
|
|
23
24
|
Usage:
|
|
25
|
+
charter Show immediate governance value + risk snapshot
|
|
24
26
|
charter setup [--ci github] Bootstrap .charter/ and optional CI workflow
|
|
25
27
|
charter init Scaffold .charter/ config directory
|
|
26
28
|
charter validate Validate git commits for governance trailers
|
|
27
29
|
charter audit Generate governance audit report
|
|
28
30
|
charter drift [--path <dir>] Scan files for pattern drift
|
|
29
31
|
charter classify <subject> Classify a change (SURFACE/LOCAL/CROSS_CUTTING)
|
|
32
|
+
charter why Explain why teams adopt Charter and expected ROI
|
|
30
33
|
charter doctor Check CLI + config health
|
|
31
34
|
charter --help Show this help
|
|
32
35
|
charter --version Show version
|
|
@@ -52,7 +55,7 @@ class CLIError extends Error {
|
|
|
52
55
|
}
|
|
53
56
|
exports.CLIError = CLIError;
|
|
54
57
|
async function run(args) {
|
|
55
|
-
if (args.
|
|
58
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
56
59
|
console.log(HELP);
|
|
57
60
|
return exports.EXIT_CODE.SUCCESS;
|
|
58
61
|
}
|
|
@@ -60,18 +63,21 @@ async function run(args) {
|
|
|
60
63
|
console.log(`charter v${CLI_VERSION}`);
|
|
61
64
|
return exports.EXIT_CODE.SUCCESS;
|
|
62
65
|
}
|
|
63
|
-
const
|
|
64
|
-
const restArgs = args.slice(1);
|
|
65
|
-
const format = getFlag(restArgs, '--format') || 'text';
|
|
66
|
+
const format = getFlag(args, '--format') || 'text';
|
|
66
67
|
if (format !== 'text' && format !== 'json') {
|
|
67
68
|
throw new CLIError(`Invalid --format value: ${format}. Use text or json.`);
|
|
68
69
|
}
|
|
69
70
|
const options = {
|
|
70
|
-
configPath: getFlag(
|
|
71
|
+
configPath: getFlag(args, '--config') || '.charter',
|
|
71
72
|
format,
|
|
72
|
-
ciMode:
|
|
73
|
-
yes:
|
|
73
|
+
ciMode: args.includes('--ci'),
|
|
74
|
+
yes: args.includes('--yes'),
|
|
74
75
|
};
|
|
76
|
+
if (args.length === 0 || args[0].startsWith('-')) {
|
|
77
|
+
return (0, why_1.quickstartCommand)(options);
|
|
78
|
+
}
|
|
79
|
+
const command = args[0];
|
|
80
|
+
const restArgs = args.slice(1);
|
|
75
81
|
switch (command) {
|
|
76
82
|
case 'setup':
|
|
77
83
|
return (0, setup_1.setupCommand)(options, restArgs);
|
|
@@ -85,6 +91,8 @@ async function run(args) {
|
|
|
85
91
|
return (0, drift_1.driftCommand)(options, restArgs);
|
|
86
92
|
case 'classify':
|
|
87
93
|
return (0, classify_1.classifyCommand)(options, restArgs);
|
|
94
|
+
case 'why':
|
|
95
|
+
return (0, why_1.whyCommand)(options);
|
|
88
96
|
case 'doctor':
|
|
89
97
|
return (0, doctor_1.doctorCommand)(options);
|
|
90
98
|
default:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackbilt/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Charter CLI — repo-level governance checks",
|
|
5
5
|
"bin": {
|
|
6
6
|
"charter": "./dist/bin.js"
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
"build": "pnpm exec tsc -p tsconfig.json"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@stackbilt/types": "^0.1.
|
|
41
|
-
"@stackbilt/core": "^0.1.
|
|
42
|
-
"@stackbilt/git": "^0.1.
|
|
43
|
-
"@stackbilt/classify": "^0.1.
|
|
44
|
-
"@stackbilt/validate": "^0.1.
|
|
45
|
-
"@stackbilt/drift": "^0.1.
|
|
40
|
+
"@stackbilt/types": "^0.1.9",
|
|
41
|
+
"@stackbilt/core": "^0.1.9",
|
|
42
|
+
"@stackbilt/git": "^0.1.9",
|
|
43
|
+
"@stackbilt/classify": "^0.1.9",
|
|
44
|
+
"@stackbilt/validate": "^0.1.9",
|
|
45
|
+
"@stackbilt/drift": "^0.1.9"
|
|
46
46
|
},
|
|
47
47
|
"license": "Apache-2.0"
|
|
48
48
|
}
|