@stackbilt/cli 0.1.8 → 0.1.10

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
@@ -4,38 +4,103 @@ CLI entry point for Charter Kit -- a local-first governance toolkit for software
4
4
 
5
5
  > **This is the only package most users need.** One install gives you the full Charter Kit toolkit.
6
6
 
7
- ## Install
8
-
9
- ```bash
10
- npm install -g @stackbilt/cli
11
- ```
12
-
13
- This pulls in all Charter Kit packages automatically. You get the `charter` command globally.
14
-
15
- For CI pipelines, install as a dev dependency instead:
7
+ ## Install (Recommended)
8
+
9
+ ```bash
10
+ npm install --save-dev @stackbilt/cli
11
+ ```
12
+
13
+ Use with `npx` in each repository:
14
+
15
+ ```bash
16
+ npx charter
17
+ npx charter setup --ci github
18
+ ```
19
+
20
+ Global install is optional if you want `charter` available system-wide:
21
+
22
+ ```bash
23
+ npm install -g @stackbilt/cli
24
+ ```
25
+
26
+ This pulls in all Charter Kit packages automatically. Use `charter setup` inside each repo to scaffold governance baseline files.
27
+
28
+ For CI pipelines, install as a dev dependency:
16
29
 
17
30
  ```bash
18
31
  npm install --save-dev @stackbilt/cli
19
32
  ```
20
33
 
21
- Requires Node >= 18.
22
-
23
- ## Quick Start
24
-
25
- ```bash
26
- charter setup # bootstrap .charter/ directory
27
- charter doctor # check CLI + config health
28
- charter validate # validate commit governance trailers
29
- charter drift # scan for blessed-stack drift
30
- charter audit # generate governance audit report
31
- charter classify "migrate auth provider"
32
- ```
34
+ Requires Node >= 18.
35
+
36
+ ## Quick Start
37
+
38
+ ```bash
39
+ charter # quick value/risk snapshot + next action
40
+ charter why # why teams adopt Charter and expected payoff
41
+ charter setup # bootstrap .charter/ directory + policy baseline
42
+ charter doctor # check CLI + config health
43
+ charter validate # validate commit governance trailers
44
+ charter drift # scan for blessed-stack drift
45
+ charter audit # generate governance audit report
46
+ charter classify "migrate auth provider"
47
+ ```
48
+
49
+ ## Human Onboarding (Copy/Paste)
50
+
51
+ Run this in the target repository:
52
+
53
+ ```bash
54
+ npm install -g @stackbilt/cli
55
+ charter
56
+ charter setup --ci github
57
+ charter doctor --format json
58
+ charter validate --format text
59
+ charter drift --format text
60
+ charter audit --format text
61
+ ```
62
+
63
+ This ensures people immediately see:
64
+ - what Charter is doing in their repo
65
+ - where baseline files were created (`.charter/*`)
66
+ - how policy checks behave before merge
67
+
68
+ ## LM Agent Onboarding (Deterministic)
69
+
70
+ Use JSON output and explicit CI semantics:
71
+
72
+ ```bash
73
+ charter --format json
74
+ charter setup --ci github --yes --format json
75
+ charter doctor --format json
76
+ charter validate --format json --ci
77
+ charter drift --format json --ci
78
+ charter audit --format json
79
+ ```
80
+
81
+ Agent handling contract:
82
+ - `exit 0`: pass
83
+ - `exit 1`: policy violation (action required)
84
+ - `exit 2`: runtime/usage failure
33
85
 
34
86
  ## Commands
35
87
 
36
- ### `charter setup`
37
-
38
- Bootstrap `.charter/` with config, patterns, and policies. Optionally generates a GitHub Actions workflow.
88
+ ### `charter` (no args)
89
+
90
+ Default first-run experience. Shows:
91
+ - repository governance baseline status
92
+ - commit governance coverage snapshot
93
+ - high-risk unlinked commit count
94
+ - one recommended next action
95
+
96
+ ### `charter why`
97
+
98
+ Explains the adoption case in plain terms (problem, what Charter enforces, expected operational payoff).
99
+
100
+ ### `charter setup`
101
+
102
+ Bootstrap `.charter/` with config, patterns, and policies. Optionally generates a GitHub Actions workflow.
103
+ This is the command that applies Charter governance into a repository.
39
104
 
40
105
  ```bash
41
106
  charter setup --ci github --yes
@@ -69,13 +134,13 @@ charter drift --path ./src --ci
69
134
 
70
135
  Generate a governance audit report covering trailers, risk, and drift.
71
136
 
72
- ### `charter classify`
137
+ ### `charter classify`
73
138
 
74
139
  Classify a change as `SURFACE`, `LOCAL`, or `CROSS_CUTTING`.
75
140
 
76
141
  ```bash
77
- charter classify "update button color"
78
- ```
142
+ charter classify "update button color"
143
+ ```
79
144
 
80
145
  ## Global Options
81
146
 
@@ -91,6 +91,11 @@ function generateAuditReport(projectName, configPath, patterns) {
91
91
  const patternScore = Math.min(100, activePatterns.length * 20);
92
92
  const policyScore = Math.min(100, policyFiles.length * 33);
93
93
  const overall = Math.round((trailerScore * 0.5) + (patternScore * 0.3) + (policyScore * 0.2));
94
+ const scoreInputs = {
95
+ coveragePercent,
96
+ activePatterns: activePatterns.length,
97
+ policyFiles: policyFiles.length,
98
+ };
94
99
  return {
95
100
  project: projectName,
96
101
  generatedAt: new Date().toISOString(),
@@ -118,6 +123,12 @@ function generateAuditReport(projectName, configPath, patterns) {
118
123
  patternDefinitions: Math.round(patternScore),
119
124
  policyDocumentation: Math.round(policyScore),
120
125
  },
126
+ criteria: {
127
+ trailerCoverage: 'coverage_percent * 1.5 (max 100). 67%+ coverage earns full points.',
128
+ patternDefinitions: 'active_pattern_count * 20 (max 100). 5+ active patterns earns full points.',
129
+ policyDocumentation: 'policy_markdown_files * 33 (max 100). 3+ policy files earns full points.',
130
+ },
131
+ recommendations: getRecommendations(scoreInputs),
121
132
  },
122
133
  };
123
134
  }
@@ -151,6 +162,16 @@ function printReport(report) {
151
162
  console.log(` Pattern definitions: ${report.score.breakdown.patternDefinitions}/100 (30% weight)`);
152
163
  console.log(` Policy documentation: ${report.score.breakdown.policyDocumentation}/100 (20% weight)`);
153
164
  console.log('');
165
+ console.log(' Scoring Criteria');
166
+ console.log(` - Trailer coverage: ${report.score.criteria.trailerCoverage}`);
167
+ console.log(` - Pattern definitions: ${report.score.criteria.patternDefinitions}`);
168
+ console.log(` - Policy documentation: ${report.score.criteria.policyDocumentation}`);
169
+ console.log('');
170
+ console.log(' Actionable Next Steps');
171
+ for (const rec of report.score.recommendations) {
172
+ console.log(` - ${rec}`);
173
+ }
174
+ console.log('');
154
175
  }
155
176
  function getRecentCommits(count) {
156
177
  try {
@@ -193,6 +214,32 @@ function runGit(args) {
193
214
  return (0, node_child_process_1.execFileSync)('git', args, {
194
215
  encoding: 'utf-8',
195
216
  maxBuffer: 10 * 1024 * 1024,
217
+ stdio: ['ignore', 'pipe', 'pipe'],
196
218
  });
197
219
  }
220
+ function getRecommendations(inputs) {
221
+ const recommendations = [];
222
+ const missingCoverage = Math.max(0, 67 - inputs.coveragePercent);
223
+ if (missingCoverage > 0) {
224
+ recommendations.push(`Increase governance trailer coverage by ${missingCoverage}% to reach full trailer score (add Governed-By/Resolves-Request trailers).`);
225
+ }
226
+ else {
227
+ recommendations.push('Trailer coverage is at full-score threshold.');
228
+ }
229
+ const missingPatterns = Math.max(0, 5 - inputs.activePatterns);
230
+ if (missingPatterns > 0) {
231
+ recommendations.push(`Add ${missingPatterns} active pattern(s) in .charter/patterns/*.json to reach full pattern score (target: 5 active patterns).`);
232
+ }
233
+ else {
234
+ recommendations.push('Pattern definitions are at full-score threshold.');
235
+ }
236
+ const missingPolicies = Math.max(0, 3 - inputs.policyFiles);
237
+ if (missingPolicies > 0) {
238
+ recommendations.push(`Add ${missingPolicies} policy markdown file(s) in .charter/policies/ to reach full policy score (target: 3 policy files).`);
239
+ }
240
+ else {
241
+ recommendations.push('Policy documentation is at full-score threshold.');
242
+ }
243
+ return recommendations;
244
+ }
198
245
  //# sourceMappingURL=audit.js.map
@@ -14,8 +14,32 @@ const git_1 = require("@stackbilt/git");
14
14
  const git_2 = require("@stackbilt/git");
15
15
  async function validateCommand(options, args) {
16
16
  const config = (0, config_1.loadConfig)(options.configPath);
17
+ if (!hasCommits()) {
18
+ if (options.format === 'json') {
19
+ console.log(JSON.stringify({ status: 'PASS', summary: 'No commits to validate.' }, null, 2));
20
+ }
21
+ else {
22
+ console.log(' No commits to validate.');
23
+ }
24
+ return index_1.EXIT_CODE.SUCCESS;
25
+ }
17
26
  const range = getCommitRange(args);
18
- const commits = getGitCommits(range);
27
+ const commitLoad = getGitCommits(range);
28
+ if (commitLoad.error) {
29
+ if (options.format === 'json') {
30
+ console.log(JSON.stringify({
31
+ status: 'ERROR',
32
+ summary: 'Failed to read git commits for validation.',
33
+ details: commitLoad.error,
34
+ }, null, 2));
35
+ }
36
+ else {
37
+ console.log(' [fail] Failed to read git commits for validation.');
38
+ console.log(` ${commitLoad.error}`);
39
+ }
40
+ return index_1.EXIT_CODE.RUNTIME_ERROR;
41
+ }
42
+ const commits = commitLoad.commits;
19
43
  if (commits.length === 0) {
20
44
  if (options.format === 'json') {
21
45
  console.log(JSON.stringify({ status: 'PASS', summary: 'No commits to validate.' }, null, 2));
@@ -123,6 +147,7 @@ function getCommitRange(args) {
123
147
  if (rangeIdx !== -1 && rangeIdx + 1 < args.length) {
124
148
  return args[rangeIdx + 1];
125
149
  }
150
+ const recentRange = getRecentCommitRange();
126
151
  try {
127
152
  const currentBranch = runGit(['rev-parse', 'HEAD']).trim();
128
153
  let baseBranch = '';
@@ -133,12 +158,12 @@ function getCommitRange(args) {
133
158
  baseBranch = runGit(['rev-parse', '--verify', 'master']).trim();
134
159
  }
135
160
  if (!baseBranch || baseBranch === currentBranch) {
136
- return 'HEAD~5..HEAD';
161
+ return recentRange;
137
162
  }
138
163
  return `${baseBranch}..HEAD`;
139
164
  }
140
165
  catch {
141
- return 'HEAD~5..HEAD';
166
+ return recentRange;
142
167
  }
143
168
  }
144
169
  function getGitCommits(range) {
@@ -165,16 +190,58 @@ function getGitCommits(range) {
165
190
  }
166
191
  if (current)
167
192
  commits.push(current);
168
- return commits;
193
+ return { commits };
194
+ }
195
+ catch (error) {
196
+ return {
197
+ commits: [],
198
+ error: getGitErrorMessage(error),
199
+ };
200
+ }
201
+ }
202
+ function hasCommits() {
203
+ try {
204
+ runGit(['rev-parse', '--verify', 'HEAD']);
205
+ return true;
169
206
  }
170
207
  catch {
171
- return [];
208
+ return false;
209
+ }
210
+ }
211
+ function getRecentCommitRange() {
212
+ try {
213
+ const count = Number.parseInt(runGit(['rev-list', '--count', 'HEAD']).trim(), 10);
214
+ if (!Number.isFinite(count) || count <= 1) {
215
+ return 'HEAD';
216
+ }
217
+ const span = Math.min(5, count - 1);
218
+ return `HEAD~${span}..HEAD`;
219
+ }
220
+ catch {
221
+ return 'HEAD';
222
+ }
223
+ }
224
+ function getGitErrorMessage(error) {
225
+ const fallback = 'Unknown git error.';
226
+ if (!(error instanceof Error))
227
+ return fallback;
228
+ const execError = error;
229
+ if (execError.stderr) {
230
+ const stderr = execError.stderr.toString().trim();
231
+ if (stderr.length > 0) {
232
+ return stderr;
233
+ }
234
+ }
235
+ if (execError.message) {
236
+ return execError.message.trim();
172
237
  }
238
+ return fallback;
173
239
  }
174
240
  function runGit(args) {
175
241
  return (0, node_child_process_1.execFileSync)('git', args, {
176
242
  encoding: 'utf-8',
177
243
  maxBuffer: 10 * 1024 * 1024,
244
+ stdio: ['ignore', 'pipe', 'pipe'],
178
245
  });
179
246
  }
180
247
  //# sourceMappingURL=validate.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackbilt/cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
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.3",
41
- "@stackbilt/core": "^0.1.3",
42
- "@stackbilt/git": "^0.1.4",
43
- "@stackbilt/classify": "^0.1.4",
44
- "@stackbilt/validate": "^0.1.4",
45
- "@stackbilt/drift": "^0.1.4"
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
  }