@codacy/verity-cli 0.31.1-experimental.83a8619 โ 0.31.1-experimental.b9b3862
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/CHANGELOG.md +44 -0
- package/README.md +24 -2
- package/bin/verity.js +305 -60
- package/data/skills/verity-setup/SKILL.md +7 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,50 @@ All notable changes to Verity are documented here. This project follows
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
### ๐ค Joining a project that already has a Standard
|
|
9
|
+
|
|
10
|
+
- **`verity init` now offers the Standard the service already holds** for this
|
|
11
|
+
repository, instead of asking a model to invent a second one. Everyone after the
|
|
12
|
+
first person on a project โ a teammate cloning the repo, the same person on
|
|
13
|
+
another machine, a CI box โ gets the team's actual configuration
|
|
14
|
+
(`standard.yaml`, the analysis config, `.verityignore`, `VERITY.md`) written
|
|
15
|
+
straight to disk, and the model phase is skipped entirely.
|
|
16
|
+
- **This is a correctness fix as much as a speed one.** `verity standard push`
|
|
17
|
+
auto-versions, so a re-synthesis does not produce a private copy โ it produces
|
|
18
|
+
v4, and v4 is what the team is held to from then on.
|
|
19
|
+
- **Nothing about it can block a setup.** No token, offline, a 404, a service that
|
|
20
|
+
errors: every one of those falls through to the regular flow. An unreliable
|
|
21
|
+
answer is never reported as "no Standard stored" โ that would offer a fresh
|
|
22
|
+
synthesis over one the team already agreed.
|
|
23
|
+
- Unattended runs (`--yes`, CI) adopt without asking, for the same reason: a CI
|
|
24
|
+
box quietly authoring a new version of the team's Standard is not an outcome
|
|
25
|
+
anyone would choose on purpose.
|
|
26
|
+
- **`--no-adopt` opts out**, for a script that wants a fresh synthesis on purpose.
|
|
27
|
+
It skips the request as well as the write: asking a question whose answer is
|
|
28
|
+
discarded is a network call the user declined, and in a locked-down CI it is one
|
|
29
|
+
that can fail loudly for no reason.
|
|
30
|
+
- **Declining is remembered per VERSION, never as a mute.** Say no and init stops
|
|
31
|
+
asking about *that* Standard โ and offers the next one, because a new version is
|
|
32
|
+
new information. A boolean "never again" would have been the familiar
|
|
33
|
+
silent-forever failure: decline on Monday meaning to synthesize later, forget,
|
|
34
|
+
and every later run stays quiet while the project has no local config at all.
|
|
35
|
+
|
|
36
|
+
### ๐ง Fixed
|
|
37
|
+
|
|
38
|
+
- **An upgrade no longer removes the gates you chose.** `.verity/setup.json` only
|
|
39
|
+
exists from this release, so every project configured before it had no recorded
|
|
40
|
+
answer โ including everyone who picked commit/push gates through the old
|
|
41
|
+
`/verity-setup`. The first upgraded `verity init` fell back to the shipped
|
|
42
|
+
default and reconciled those gates away, turning Stop back on. What you chose is
|
|
43
|
+
still legible in `.claude/settings.json`, so init reads it.
|
|
44
|
+
- `verity doctor` names the artifact that is actually missing instead of telling
|
|
45
|
+
you to run a phase that will decline: with a Standard adopted and no analysis
|
|
46
|
+
config, `/verity-setup` would have answered "already configured".
|
|
47
|
+
- The setup skill learned the same case โ Standard present, analysis config
|
|
48
|
+
missing โ and writes only the config rather than re-synthesizing the Standard.
|
|
49
|
+
|
|
50
|
+
## [Unreleased]
|
|
51
|
+
|
|
8
52
|
**Setup is one command.** `verity init` and `/verity-setup` were two half-flows
|
|
9
53
|
that overlapped โ and the overlap was not harmless: init wired the Stop hook,
|
|
10
54
|
then the skill reconciled the hooks to a selection it asked for afterwards,
|
package/README.md
CHANGED
|
@@ -12,8 +12,29 @@ verity init
|
|
|
12
12
|
|
|
13
13
|
`verity init` is the whole setup. It asks how deeply to review and when
|
|
14
14
|
(on stop / before commit / before push), wires everything, offers the optional
|
|
15
|
-
GitHub login โ then
|
|
16
|
-
|
|
15
|
+
GitHub login โ then either **adopts the Standard your team already has**, or
|
|
16
|
+
launches Claude Code to synthesize one from your codebase (`/verity-setup`).
|
|
17
|
+
|
|
18
|
+
### Joining a project that already uses Verity
|
|
19
|
+
|
|
20
|
+
A Standard belongs to the project, not to your machine. So once you are signed in,
|
|
21
|
+
init asks the service whether this repository already has one:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
This repository already has a Standard on Verity: version 3, pushed by claude-code
|
|
25
|
+
on 2026-08-12 ยท with analysis config and .verityignore
|
|
26
|
+
Use the existing Standard? [Y/n]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Say yes and it writes `.verity/standard.yaml`, `.codacy/codacy.config.json`,
|
|
30
|
+
`.verityignore` and `VERITY.md` straight from the service โ no model phase, and no
|
|
31
|
+
second version of your team's Standard (`verity standard push` auto-versions, so a
|
|
32
|
+
fresh synthesis would become the version everyone is held to). Say no and it stops asking about
|
|
33
|
+
that version โ and offers the next one, since a new version is new information.
|
|
34
|
+
With no Standard stored yet, the regular flow runs.
|
|
35
|
+
`--no-adopt` opts out of the whole check, including the request. Offline, signed out, or a service
|
|
36
|
+
that cannot answer: also the regular flow โ setup has never needed the network and
|
|
37
|
+
still doesn't.
|
|
17
38
|
|
|
18
39
|
If Claude Code isn't on your PATH, or you're already inside a session, init prints
|
|
19
40
|
the one command to run instead. `verity setup` is an alias for the same command.
|
|
@@ -22,6 +43,7 @@ the one command to run instead. `verity setup` is an alias for the same command.
|
|
|
22
43
|
|------|-----|
|
|
23
44
|
| `--yes` | Take the recommended answer for every question โ no prompts at all (CI, scripts, agents). Includes the GitHub login, whose unattended answer is **skip**: the project stays local-only until you run `verity login`. |
|
|
24
45
|
| `--no-setup` | Stop after the deterministic phase; don't launch Claude Code |
|
|
46
|
+
| `--no-adopt` | Don't offer the Standard the service already holds for this repo โ synthesize a fresh one. Skips the request entirely |
|
|
25
47
|
| `--force` | Overwrite the installed skills even when they differ |
|
|
26
48
|
|
|
27
49
|
Run `verity doctor` any time to see prerequisites, which phase is done, the hook
|
package/bin/verity.js
CHANGED
|
@@ -675,8 +675,8 @@ var require_option = __commonJS({
|
|
|
675
675
|
* @param {(string | string[])} names
|
|
676
676
|
* @return {Option}
|
|
677
677
|
*/
|
|
678
|
-
conflicts(
|
|
679
|
-
this.conflictsWith = this.conflictsWith.concat(
|
|
678
|
+
conflicts(names2) {
|
|
679
|
+
this.conflictsWith = this.conflictsWith.concat(names2);
|
|
680
680
|
return this;
|
|
681
681
|
}
|
|
682
682
|
/**
|
|
@@ -1249,8 +1249,8 @@ var require_command = __commonJS({
|
|
|
1249
1249
|
* @param {string} names
|
|
1250
1250
|
* @return {Command} `this` command for chaining
|
|
1251
1251
|
*/
|
|
1252
|
-
arguments(
|
|
1253
|
-
|
|
1252
|
+
arguments(names2) {
|
|
1253
|
+
names2.trim().split(/ +/).forEach((detail) => {
|
|
1254
1254
|
this.argument(detail);
|
|
1255
1255
|
});
|
|
1256
1256
|
return this;
|
|
@@ -19364,7 +19364,7 @@ function channelSilence(input) {
|
|
|
19364
19364
|
// src/lib/cli-version.ts
|
|
19365
19365
|
function cliVersion() {
|
|
19366
19366
|
try {
|
|
19367
|
-
return true ? "0.31.1-experimental.
|
|
19367
|
+
return true ? "0.31.1-experimental.b9b3862" : "dev";
|
|
19368
19368
|
} catch {
|
|
19369
19369
|
return "dev";
|
|
19370
19370
|
}
|
|
@@ -23288,9 +23288,9 @@ function registerWaiveCommand(program2) {
|
|
|
23288
23288
|
}
|
|
23289
23289
|
|
|
23290
23290
|
// src/commands/init.ts
|
|
23291
|
-
var
|
|
23292
|
-
var
|
|
23293
|
-
var
|
|
23291
|
+
var import_node_fs45 = require("node:fs");
|
|
23292
|
+
var import_promises15 = require("node:fs/promises");
|
|
23293
|
+
var import_node_path30 = require("node:path");
|
|
23294
23294
|
var import_node_child_process14 = require("node:child_process");
|
|
23295
23295
|
|
|
23296
23296
|
// src/lib/banner.ts
|
|
@@ -23701,8 +23701,19 @@ async function buildReport() {
|
|
|
23701
23701
|
if (c.status !== "ok" && c.remedy) next.push(c.remedy);
|
|
23702
23702
|
}
|
|
23703
23703
|
if (!state?.init) next.push('Run "verity init" \u2014 the deterministic setup phase has not completed here.');
|
|
23704
|
-
|
|
23705
|
-
|
|
23704
|
+
const missing = [
|
|
23705
|
+
!artifacts.standard && ".verity/standard.yaml",
|
|
23706
|
+
!artifacts.analysisConfig && ".codacy/codacy.config.json",
|
|
23707
|
+
!artifacts.verityMd && "VERITY.md"
|
|
23708
|
+
].filter((x) => !!x);
|
|
23709
|
+
if (missing.length === 3) {
|
|
23710
|
+
next.push("Run /verity-setup in Claude Code \u2014 it synthesizes the Standard, the analysis config, and VERITY.md.");
|
|
23711
|
+
} else if (missing.length === 1 && !artifacts.analysisConfig) {
|
|
23712
|
+
next.push(
|
|
23713
|
+
'No analysis config, so static analysis runs nothing. Run /verity-setup in Claude Code to write one (it keeps the Standard you already have), or have a teammate run "verity config push".'
|
|
23714
|
+
);
|
|
23715
|
+
} else if (missing.length > 0) {
|
|
23716
|
+
next.push(`Missing ${missing.join(" and ")}. Run "/verity-setup --force" in Claude Code to regenerate.`);
|
|
23706
23717
|
}
|
|
23707
23718
|
const noAnalysisMoment = !hooks.stop && hooks.guardOn.length === 0;
|
|
23708
23719
|
if (noAnalysisMoment) {
|
|
@@ -24152,6 +24163,146 @@ async function promptMultiSelect(question, choices, fallback) {
|
|
|
24152
24163
|
return parsed.ids;
|
|
24153
24164
|
}
|
|
24154
24165
|
|
|
24166
|
+
// src/lib/remote-config.ts
|
|
24167
|
+
var import_node_fs44 = require("node:fs");
|
|
24168
|
+
var import_promises14 = require("node:fs/promises");
|
|
24169
|
+
var import_node_path29 = require("node:path");
|
|
24170
|
+
var import_yaml3 = __toESM(require_dist());
|
|
24171
|
+
var IGNORE_RIDER = "verityignore";
|
|
24172
|
+
async function fetchRemoteSetup(opts) {
|
|
24173
|
+
const standard = await apiRequest({
|
|
24174
|
+
method: "GET",
|
|
24175
|
+
path: "/standards/latest",
|
|
24176
|
+
serviceUrl: opts.serviceUrl,
|
|
24177
|
+
token: opts.token,
|
|
24178
|
+
verbose: opts.verbose
|
|
24179
|
+
});
|
|
24180
|
+
if (!standard.ok) {
|
|
24181
|
+
const missing = /STANDARD_NOT_FOUND|not found|404/i.test(standard.error);
|
|
24182
|
+
return missing ? { status: "none" } : { status: "unavailable", reason: standard.error };
|
|
24183
|
+
}
|
|
24184
|
+
const config = await apiRequest({
|
|
24185
|
+
method: "GET",
|
|
24186
|
+
path: "/analysis-configs",
|
|
24187
|
+
serviceUrl: opts.serviceUrl,
|
|
24188
|
+
token: opts.token,
|
|
24189
|
+
verbose: opts.verbose
|
|
24190
|
+
});
|
|
24191
|
+
return {
|
|
24192
|
+
status: "found",
|
|
24193
|
+
standard: {
|
|
24194
|
+
version: standard.data.version,
|
|
24195
|
+
content: standard.data.content ?? {},
|
|
24196
|
+
createdBy: standard.data.created_by,
|
|
24197
|
+
createdAt: standard.data.created_at
|
|
24198
|
+
},
|
|
24199
|
+
// Absent is normal and not an error: the two are stored independently, and a
|
|
24200
|
+
// project can have a Standard with no analysis config.
|
|
24201
|
+
analysisConfig: config.ok ? config.data.content ?? null : null
|
|
24202
|
+
};
|
|
24203
|
+
}
|
|
24204
|
+
async function adoptRemoteSetup(found, opts) {
|
|
24205
|
+
const written = [];
|
|
24206
|
+
const notes = [];
|
|
24207
|
+
const content = { ...found.standard.content };
|
|
24208
|
+
const rider = typeof content[IGNORE_RIDER] === "string" ? content[IGNORE_RIDER] : null;
|
|
24209
|
+
delete content[IGNORE_RIDER];
|
|
24210
|
+
await writeOut(STANDARD_FILE, (0, import_yaml3.stringify)(content));
|
|
24211
|
+
written.push(STANDARD_FILE);
|
|
24212
|
+
if (rider !== null) {
|
|
24213
|
+
const localIgnore = projectPath(VERITYIGNORE_FILE);
|
|
24214
|
+
if (!(0, import_node_fs44.existsSync)(localIgnore)) {
|
|
24215
|
+
await writeOut(VERITYIGNORE_FILE, rider);
|
|
24216
|
+
written.push(VERITYIGNORE_FILE);
|
|
24217
|
+
} else {
|
|
24218
|
+
const local = await (0, import_promises14.readFile)(localIgnore, "utf-8").catch(() => null);
|
|
24219
|
+
if (local !== null && local !== rider) {
|
|
24220
|
+
notes.push(`${VERITYIGNORE_FILE} already exists here and differs from the pushed copy \u2014 kept yours.`);
|
|
24221
|
+
}
|
|
24222
|
+
}
|
|
24223
|
+
}
|
|
24224
|
+
if (found.analysisConfig) {
|
|
24225
|
+
await writeOut(CODACY_CONFIG_FILE, JSON.stringify(found.analysisConfig, null, 2) + "\n");
|
|
24226
|
+
written.push(CODACY_CONFIG_FILE);
|
|
24227
|
+
} else {
|
|
24228
|
+
notes.push(
|
|
24229
|
+
"No analysis config is stored for this repository, so static analysis has nothing to run. Run /verity-setup in Claude Code to generate one."
|
|
24230
|
+
);
|
|
24231
|
+
}
|
|
24232
|
+
await writeOut(VERITY_MD_FILE, renderVerityMd(found.standard, opts));
|
|
24233
|
+
written.push(VERITY_MD_FILE);
|
|
24234
|
+
return { written, notes };
|
|
24235
|
+
}
|
|
24236
|
+
async function writeOut(relative, body) {
|
|
24237
|
+
const target = projectPath(relative);
|
|
24238
|
+
await (0, import_promises14.mkdir)((0, import_node_path29.dirname)(target), { recursive: true });
|
|
24239
|
+
await (0, import_promises14.writeFile)(target, body);
|
|
24240
|
+
}
|
|
24241
|
+
function names(content, key) {
|
|
24242
|
+
const raw = content[key];
|
|
24243
|
+
if (!Array.isArray(raw)) return [];
|
|
24244
|
+
return raw.map((entry) => {
|
|
24245
|
+
if (typeof entry === "string") return entry;
|
|
24246
|
+
if (entry && typeof entry === "object") {
|
|
24247
|
+
const rec = entry;
|
|
24248
|
+
const label2 = rec.name ?? rec.id ?? rec.dimension ?? rec.pattern;
|
|
24249
|
+
return typeof label2 === "string" ? label2 : null;
|
|
24250
|
+
}
|
|
24251
|
+
return null;
|
|
24252
|
+
}).filter((x) => !!x);
|
|
24253
|
+
}
|
|
24254
|
+
function renderVerityMd(standard, opts) {
|
|
24255
|
+
const dimensions = names(standard.content, "quality_dimensions");
|
|
24256
|
+
const patterns = names(standard.content, "security_patterns");
|
|
24257
|
+
const custom = names(standard.content, "custom_patterns");
|
|
24258
|
+
const list2 = (items, fallback) => (items.length > 0 ? items : fallback).map((i) => `- ${i}`).join("\n");
|
|
24259
|
+
return `# VERITY.md \u2014 Quality Gate
|
|
24260
|
+
|
|
24261
|
+
> This project uses [Verity](https://verity.md) to enforce quality and security standards on AI-generated code.
|
|
24262
|
+
|
|
24263
|
+
**URL:** ${opts.serviceUrl}
|
|
24264
|
+
**Project:** ${opts.projectId ?? "this repository"}
|
|
24265
|
+
**Standard:** v${standard.version} (adopted from the service${standard.createdBy ? `, created by ${standard.createdBy}` : ""})
|
|
24266
|
+
|
|
24267
|
+
## Quality Dimensions
|
|
24268
|
+
${list2(dimensions, ["comprehensibility", "modularity", "type_safety", "test_adequacy"])}
|
|
24269
|
+
|
|
24270
|
+
## Security Patterns
|
|
24271
|
+
${list2(patterns, [
|
|
24272
|
+
"No hardcoded secrets (CWE-798)",
|
|
24273
|
+
"Input sanitization (CWE-20)",
|
|
24274
|
+
"Parameterized queries (CWE-89)",
|
|
24275
|
+
"Dependency verification (CWE-1395)",
|
|
24276
|
+
"No unsafe deserialization (CWE-502)",
|
|
24277
|
+
"Access control checks (CWE-639)",
|
|
24278
|
+
"Config file integrity (CWE-15)"
|
|
24279
|
+
])}
|
|
24280
|
+
${custom.length > 0 ? `
|
|
24281
|
+
## Project Patterns
|
|
24282
|
+
${list2(custom, [])}
|
|
24283
|
+
` : ""}
|
|
24284
|
+
## How It Works
|
|
24285
|
+
Every time the coding agent stops, the Verity hook:
|
|
24286
|
+
1. Runs static analysis via @codacy/analysis-cli
|
|
24287
|
+
2. Sends results + code to the Verity service
|
|
24288
|
+
3. An independent model reviews the code
|
|
24289
|
+
4. Returns PASS / WARN / FAIL with actionable findings
|
|
24290
|
+
|
|
24291
|
+
_This Standard came from the service \u2014 the team's shared version. To change it,
|
|
24292
|
+
edit \`.verity/standard.yaml\` and run \`verity standard push\`, or re-synthesize
|
|
24293
|
+
from the codebase with \`/verity-setup --force\`._
|
|
24294
|
+
`;
|
|
24295
|
+
}
|
|
24296
|
+
function describeRemote(found) {
|
|
24297
|
+
const when = found.standard.createdAt.slice(0, 10);
|
|
24298
|
+
const who = found.standard.createdBy ? ` by ${found.standard.createdBy}` : "";
|
|
24299
|
+
const extras = [];
|
|
24300
|
+
if (found.analysisConfig) extras.push("analysis config");
|
|
24301
|
+
if (typeof found.standard.content[IGNORE_RIDER] === "string") extras.push(".verityignore");
|
|
24302
|
+
const also = extras.length > 0 ? ` \xB7 with ${extras.join(" and ")}` : "";
|
|
24303
|
+
return `version ${found.standard.version}, pushed${who} on ${when}${also}`;
|
|
24304
|
+
}
|
|
24305
|
+
|
|
24155
24306
|
// src/commands/init.ts
|
|
24156
24307
|
async function confirmExistingLogin(serviceUrl, remote, opts) {
|
|
24157
24308
|
const existing = await resolveToken(opts.token);
|
|
@@ -24256,15 +24407,15 @@ async function runOptionalAuth(resolution, opts = {}) {
|
|
|
24256
24407
|
}
|
|
24257
24408
|
function resolveDataDir() {
|
|
24258
24409
|
const candidates = [
|
|
24259
|
-
(0,
|
|
24410
|
+
(0, import_node_path30.join)(__dirname, "..", "data"),
|
|
24260
24411
|
// installed: node_modules/@codacy/verity-cli/data
|
|
24261
|
-
(0,
|
|
24412
|
+
(0, import_node_path30.join)(__dirname, "..", "..", "data"),
|
|
24262
24413
|
// edge case: nested resolution
|
|
24263
|
-
(0,
|
|
24414
|
+
(0, import_node_path30.join)(process.cwd(), "cli", "data")
|
|
24264
24415
|
// local dev: running from repo root
|
|
24265
24416
|
];
|
|
24266
24417
|
for (const candidate of candidates) {
|
|
24267
|
-
if ((0,
|
|
24418
|
+
if ((0, import_node_fs45.existsSync)((0, import_node_path30.join)(candidate, "skills"))) {
|
|
24268
24419
|
return candidate;
|
|
24269
24420
|
}
|
|
24270
24421
|
}
|
|
@@ -24273,16 +24424,16 @@ function resolveDataDir() {
|
|
|
24273
24424
|
);
|
|
24274
24425
|
}
|
|
24275
24426
|
async function copyDir(src, dest) {
|
|
24276
|
-
await (0,
|
|
24277
|
-
await (0,
|
|
24427
|
+
await (0, import_promises15.mkdir)(dest, { recursive: true });
|
|
24428
|
+
await (0, import_promises15.cp)(src, dest, { recursive: true, force: true });
|
|
24278
24429
|
}
|
|
24279
24430
|
async function skillIsCurrent(src, dest) {
|
|
24280
24431
|
const list2 = (dir) => {
|
|
24281
24432
|
const out = [];
|
|
24282
24433
|
const walk = (d, prefix) => {
|
|
24283
|
-
for (const e of (0,
|
|
24434
|
+
for (const e of (0, import_node_fs45.readdirSync)(d, { withFileTypes: true })) {
|
|
24284
24435
|
const rel = prefix ? `${prefix}/${e.name}` : e.name;
|
|
24285
|
-
if (e.isDirectory()) walk((0,
|
|
24436
|
+
if (e.isDirectory()) walk((0, import_node_path30.join)(d, e.name), rel);
|
|
24286
24437
|
else if (e.isFile()) out.push(rel);
|
|
24287
24438
|
}
|
|
24288
24439
|
};
|
|
@@ -24293,8 +24444,8 @@ async function skillIsCurrent(src, dest) {
|
|
|
24293
24444
|
const shipped = list2(src);
|
|
24294
24445
|
if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
|
|
24295
24446
|
for (const rel of shipped) {
|
|
24296
|
-
const a = await (0,
|
|
24297
|
-
const b = await (0,
|
|
24447
|
+
const a = await (0, import_promises15.readFile)((0, import_node_path30.join)(src, rel), "utf-8");
|
|
24448
|
+
const b = await (0, import_promises15.readFile)((0, import_node_path30.join)(dest, rel), "utf-8");
|
|
24298
24449
|
if (a !== b) return false;
|
|
24299
24450
|
}
|
|
24300
24451
|
return true;
|
|
@@ -24323,9 +24474,17 @@ var MOMENT_CHOICES = [
|
|
|
24323
24474
|
{ id: "pre-push", label: "Before push / PR", hint: "reviews the to-be-pushed commits, blocks on FAIL" }
|
|
24324
24475
|
];
|
|
24325
24476
|
var DEFAULT_MOMENTS = ["stop"];
|
|
24477
|
+
async function momentsFromInstalledHooks() {
|
|
24478
|
+
const status = await checkAllVerityHooks();
|
|
24479
|
+
const wired = [];
|
|
24480
|
+
if (status.stop) wired.push("stop");
|
|
24481
|
+
if (status.guardOn.includes("commit")) wired.push("pre-commit");
|
|
24482
|
+
if (status.guardOn.includes("push")) wired.push("pre-push");
|
|
24483
|
+
return wired.length > 0 ? wired : DEFAULT_MOMENTS;
|
|
24484
|
+
}
|
|
24326
24485
|
async function askSetupQuestions(defaultsOnly, previous) {
|
|
24327
24486
|
const intensityDefault = previous?.intensity ?? "balanced";
|
|
24328
|
-
const momentsDefault = previous?.moments?.length ? previous.moments :
|
|
24487
|
+
const momentsDefault = previous?.moments?.length ? previous.moments : await momentsFromInstalledHooks();
|
|
24329
24488
|
if (defaultsOnly) {
|
|
24330
24489
|
return { intensity: intensityDefault, moments: momentsDefault, telemetry: "not-asked" };
|
|
24331
24490
|
}
|
|
@@ -24362,6 +24521,73 @@ function insideClaudeCode() {
|
|
|
24362
24521
|
function resumeDeferredPending(previous) {
|
|
24363
24522
|
return previous?.telemetry === "deferred";
|
|
24364
24523
|
}
|
|
24524
|
+
async function maybeAdoptRemoteSetup(opts) {
|
|
24525
|
+
if (!opts.adopt) {
|
|
24526
|
+
printInfo(" Skipped (--no-adopt) \u2014 the setup below runs from this codebase.");
|
|
24527
|
+
return false;
|
|
24528
|
+
}
|
|
24529
|
+
const token = await resolveToken(opts.globals.token);
|
|
24530
|
+
const url = await resolveServiceUrl(opts.globals.serviceUrl);
|
|
24531
|
+
if (!token.ok || !url.ok) {
|
|
24532
|
+
printInfo(" Not signed in, so there is nothing to fetch \u2014 the setup below runs from your codebase.");
|
|
24533
|
+
return false;
|
|
24534
|
+
}
|
|
24535
|
+
const found = await fetchRemoteSetup({
|
|
24536
|
+
token: token.data.token,
|
|
24537
|
+
serviceUrl: url.data,
|
|
24538
|
+
verbose: opts.globals.verbose
|
|
24539
|
+
});
|
|
24540
|
+
if (found.status === "unavailable") {
|
|
24541
|
+
printWarn(` Could not ask the service (${found.reason}) \u2014 continuing with the regular setup.`);
|
|
24542
|
+
return false;
|
|
24543
|
+
}
|
|
24544
|
+
if (found.status === "none") {
|
|
24545
|
+
printInfo(" No Standard stored for this repository yet \u2014 this setup will create the first one.");
|
|
24546
|
+
return false;
|
|
24547
|
+
}
|
|
24548
|
+
if (opts.previous?.declinedStandardVersion === found.standard.version) {
|
|
24549
|
+
printInfo(` Standard v${found.standard.version} is available; you declined this version before \u2014 not asking again.`);
|
|
24550
|
+
printInfo(" A newer version will be offered. To take it now: verity standard get, or /verity-setup.");
|
|
24551
|
+
return false;
|
|
24552
|
+
}
|
|
24553
|
+
console.log("");
|
|
24554
|
+
console.log(` This repository already has a Standard on Verity: ${describeRemote(found)}`);
|
|
24555
|
+
console.log(" Using it keeps you consistent with everyone else on the project.");
|
|
24556
|
+
console.log(" Synthesizing a new one from this codebase would push a competing version.");
|
|
24557
|
+
console.log("");
|
|
24558
|
+
const use = opts.defaultsOnly ? true : await promptYes(" Use the existing Standard? [Y/n] ", { nonInteractive: true });
|
|
24559
|
+
if (!use) {
|
|
24560
|
+
printInfo(" Keeping the existing version untouched; /verity-setup will synthesize a new one.");
|
|
24561
|
+
await writeSetupState({ declinedStandardVersion: found.standard.version }).catch(() => {
|
|
24562
|
+
});
|
|
24563
|
+
return false;
|
|
24564
|
+
}
|
|
24565
|
+
try {
|
|
24566
|
+
const outcome = await adoptRemoteSetup(found, {
|
|
24567
|
+
serviceUrl: url.data,
|
|
24568
|
+
projectId: await projectLabel(token.data.token, url.data, opts.globals.verbose)
|
|
24569
|
+
});
|
|
24570
|
+
for (const path of outcome.written) printInfo(` ${path} \u2713`);
|
|
24571
|
+
for (const note of outcome.notes) printWarn(` ${note}`);
|
|
24572
|
+
if (opts.previous?.declinedStandardVersion != null) {
|
|
24573
|
+
await writeSetupState({ declinedStandardVersion: void 0 }).catch(() => {
|
|
24574
|
+
});
|
|
24575
|
+
}
|
|
24576
|
+
return true;
|
|
24577
|
+
} catch (err) {
|
|
24578
|
+
printWarn(` Could not write the fetched configuration: ${err.message}`);
|
|
24579
|
+
printInfo(" Falling back to the regular setup.");
|
|
24580
|
+
return false;
|
|
24581
|
+
}
|
|
24582
|
+
}
|
|
24583
|
+
async function projectLabel(token, serviceUrl, verbose) {
|
|
24584
|
+
try {
|
|
24585
|
+
const who = await whoami(token, serviceUrl, verbose);
|
|
24586
|
+
return who.ok ? who.data.project_id : null;
|
|
24587
|
+
} catch {
|
|
24588
|
+
return null;
|
|
24589
|
+
}
|
|
24590
|
+
}
|
|
24365
24591
|
var PHASE_TWO_ARTIFACTS = [
|
|
24366
24592
|
{
|
|
24367
24593
|
path: ".verity/standard.yaml",
|
|
@@ -24438,12 +24664,13 @@ async function handoffToSetup(enabled, claudeInstalled) {
|
|
|
24438
24664
|
await reportPhaseTwo(startedAt);
|
|
24439
24665
|
}
|
|
24440
24666
|
function registerInitCommand(program2) {
|
|
24441
|
-
program2.command("init").alias("setup").description("Set up Verity in the current project (asks the setup questions, then hands off to /verity-setup)").option("--force", "Reinstall the skills even when they are already up to date (hooks are always reconciled)").option("-y, --yes", "Take the recommended answer for every question (no prompts)").option("--no-setup", "Skip the /verity-setup handoff at the end").action(async (opts) => {
|
|
24667
|
+
program2.command("init").alias("setup").description("Set up Verity in the current project (asks the setup questions, then hands off to /verity-setup)").option("--force", "Reinstall the skills even when they are already up to date (hooks are always reconciled)").option("-y, --yes", "Take the recommended answer for every question (no prompts)").option("--no-setup", "Skip the /verity-setup handoff at the end").option("--no-adopt", "Don't offer this repository's existing Standard from the service; synthesize a new one").action(async (opts) => {
|
|
24442
24668
|
const force = opts.force ?? false;
|
|
24443
24669
|
const wantsHandoff = opts.setup !== false;
|
|
24670
|
+
const wantsAdopt = opts.adopt !== false;
|
|
24444
24671
|
const defaultsOnly = (opts.yes ?? false) || !interactive();
|
|
24445
24672
|
const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
|
|
24446
|
-
const isProject = projectMarkers.some((m) => (0,
|
|
24673
|
+
const isProject = projectMarkers.some((m) => (0, import_node_fs45.existsSync)(m));
|
|
24447
24674
|
if (!isProject) {
|
|
24448
24675
|
printError("No project detected in the current directory.");
|
|
24449
24676
|
printInfo('Run "verity init" from your project root.');
|
|
@@ -24458,7 +24685,7 @@ function registerInitCommand(program2) {
|
|
|
24458
24685
|
} else {
|
|
24459
24686
|
printPhase(1, 2, "this machine", "prerequisites \xB7 skills \xB7 hooks \xB7 sign-in");
|
|
24460
24687
|
}
|
|
24461
|
-
const TOTAL_STEPS =
|
|
24688
|
+
const TOTAL_STEPS = 9;
|
|
24462
24689
|
let stepNo = 0;
|
|
24463
24690
|
const step = (label2) => {
|
|
24464
24691
|
stepNo++;
|
|
@@ -24493,17 +24720,17 @@ function registerInitCommand(program2) {
|
|
|
24493
24720
|
console.log("");
|
|
24494
24721
|
step("Installing skills");
|
|
24495
24722
|
const dataDir = resolveDataDir();
|
|
24496
|
-
const skillsSource = (0,
|
|
24723
|
+
const skillsSource = (0, import_node_path30.join)(dataDir, "skills");
|
|
24497
24724
|
const skillsDest = ".claude/skills";
|
|
24498
24725
|
let skillsInstalled = 0;
|
|
24499
24726
|
for (const skill of SKILLS) {
|
|
24500
|
-
const src = (0,
|
|
24501
|
-
const dest = (0,
|
|
24502
|
-
if (!(0,
|
|
24727
|
+
const src = (0, import_node_path30.join)(skillsSource, skill);
|
|
24728
|
+
const dest = (0, import_node_path30.join)(skillsDest, skill);
|
|
24729
|
+
if (!(0, import_node_fs45.existsSync)(src)) {
|
|
24503
24730
|
printWarn(` Skill data not found: ${skill}`);
|
|
24504
24731
|
continue;
|
|
24505
24732
|
}
|
|
24506
|
-
if ((0,
|
|
24733
|
+
if ((0, import_node_fs45.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) {
|
|
24507
24734
|
skillsInstalled++;
|
|
24508
24735
|
continue;
|
|
24509
24736
|
}
|
|
@@ -24519,7 +24746,7 @@ function registerInitCommand(program2) {
|
|
|
24519
24746
|
printInfo(` intensity: ${intensity} \xB7 moments: ${moments.join(", ") || "none"} (no questions asked)`);
|
|
24520
24747
|
}
|
|
24521
24748
|
step("Knowledge base, .gitignore and CLAUDE.md");
|
|
24522
|
-
await (0,
|
|
24749
|
+
await (0, import_promises15.mkdir)(VERITY_DIR, { recursive: true });
|
|
24523
24750
|
await ensureMemoryDir();
|
|
24524
24751
|
const ignoreResult = ensureVerityGitignore();
|
|
24525
24752
|
if (ignoreResult === "failed") {
|
|
@@ -24553,8 +24780,8 @@ function registerInitCommand(program2) {
|
|
|
24553
24780
|
} catch (err) {
|
|
24554
24781
|
printWarn(` Could not update CLAUDE.md: ${err.message}`);
|
|
24555
24782
|
}
|
|
24556
|
-
const globalVerityDir = (0,
|
|
24557
|
-
await (0,
|
|
24783
|
+
const globalVerityDir = (0, import_node_path30.join)(process.env.HOME ?? "", ".verity");
|
|
24784
|
+
await (0, import_promises15.mkdir)(globalVerityDir, { recursive: true });
|
|
24558
24785
|
console.log("");
|
|
24559
24786
|
step("Wiring Claude Code hooks");
|
|
24560
24787
|
await applyMomentSelection(moments);
|
|
@@ -24606,6 +24833,18 @@ function registerInitCommand(program2) {
|
|
|
24606
24833
|
printWarn('Telemetry needs a Verity token \u2014 run "verity login", then "verity telemetry install".');
|
|
24607
24834
|
}
|
|
24608
24835
|
}
|
|
24836
|
+
step("Checking Verity for an existing setup");
|
|
24837
|
+
let adopted = false;
|
|
24838
|
+
if ((0, import_node_fs45.existsSync)(projectPath(STANDARD_FILE))) {
|
|
24839
|
+
printInfo(" This project already has .verity/standard.yaml \u2014 keeping it.");
|
|
24840
|
+
} else {
|
|
24841
|
+
adopted = await maybeAdoptRemoteSetup({
|
|
24842
|
+
globals: program2.opts(),
|
|
24843
|
+
defaultsOnly,
|
|
24844
|
+
previous,
|
|
24845
|
+
adopt: wantsAdopt
|
|
24846
|
+
});
|
|
24847
|
+
}
|
|
24609
24848
|
step("Recording your answers");
|
|
24610
24849
|
try {
|
|
24611
24850
|
await writeSetupState({
|
|
@@ -24614,7 +24853,7 @@ function registerInitCommand(program2) {
|
|
|
24614
24853
|
...telemetryChoice ? { telemetry: telemetryChoice } : {},
|
|
24615
24854
|
init: {
|
|
24616
24855
|
completed_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
24617
|
-
cli_version: true ? "0.31.1-experimental.
|
|
24856
|
+
cli_version: true ? "0.31.1-experimental.b9b3862" : "dev"
|
|
24618
24857
|
}
|
|
24619
24858
|
});
|
|
24620
24859
|
} catch (err) {
|
|
@@ -24632,14 +24871,20 @@ function registerInitCommand(program2) {
|
|
|
24632
24871
|
console.log(" CLAUDE.md memory pointer, waive policy, reflection");
|
|
24633
24872
|
console.log("");
|
|
24634
24873
|
console.log(` Intensity: ${intensity} Moments: ${moments.join(", ") || "none"}`);
|
|
24635
|
-
|
|
24874
|
+
if (adopted) {
|
|
24875
|
+
console.log("");
|
|
24876
|
+
printInfo("Setup complete \u2014 this project uses your team's Standard.");
|
|
24877
|
+
printInfo(" Re-synthesize from this codebase instead: /verity-setup --force");
|
|
24878
|
+
} else {
|
|
24879
|
+
await handoffToSetup(wantsHandoff, claudeInstalled);
|
|
24880
|
+
}
|
|
24636
24881
|
console.log("");
|
|
24637
24882
|
});
|
|
24638
24883
|
}
|
|
24639
24884
|
|
|
24640
24885
|
// src/commands/uninstall.ts
|
|
24641
|
-
var
|
|
24642
|
-
var
|
|
24886
|
+
var import_node_fs46 = require("node:fs");
|
|
24887
|
+
var import_node_path31 = require("node:path");
|
|
24643
24888
|
var SKILL_NAMES = [
|
|
24644
24889
|
"verity-setup",
|
|
24645
24890
|
"verity-analyze",
|
|
@@ -24658,11 +24903,11 @@ function registerUninstallCommand(program2) {
|
|
|
24658
24903
|
const actions = [];
|
|
24659
24904
|
const skillsRoot = projectPath(".claude/skills");
|
|
24660
24905
|
for (const name of SKILL_NAMES) {
|
|
24661
|
-
const dir = (0,
|
|
24662
|
-
if ((0,
|
|
24906
|
+
const dir = (0, import_node_path31.join)(skillsRoot, name);
|
|
24907
|
+
if ((0, import_node_fs46.existsSync)(dir)) {
|
|
24663
24908
|
actions.push({
|
|
24664
24909
|
label: `Remove .claude/skills/${name}/`,
|
|
24665
|
-
apply: () => (0,
|
|
24910
|
+
apply: () => (0, import_node_fs46.rmSync)(dir, { recursive: true, force: true })
|
|
24666
24911
|
});
|
|
24667
24912
|
}
|
|
24668
24913
|
}
|
|
@@ -24676,24 +24921,24 @@ function registerUninstallCommand(program2) {
|
|
|
24676
24921
|
});
|
|
24677
24922
|
}
|
|
24678
24923
|
const verityDir = projectPath(VERITY_DIR);
|
|
24679
|
-
if ((0,
|
|
24924
|
+
if ((0, import_node_fs46.existsSync)(verityDir)) {
|
|
24680
24925
|
actions.push({
|
|
24681
24926
|
label: `Remove ${VERITY_DIR}/`,
|
|
24682
|
-
apply: () => (0,
|
|
24927
|
+
apply: () => (0, import_node_fs46.rmSync)(verityDir, { recursive: true, force: true })
|
|
24683
24928
|
});
|
|
24684
24929
|
}
|
|
24685
24930
|
if (!keepVerityMd) {
|
|
24686
24931
|
const verityMd = projectPath(VERITY_MD_FILE);
|
|
24687
|
-
if ((0,
|
|
24932
|
+
if ((0, import_node_fs46.existsSync)(verityMd)) {
|
|
24688
24933
|
actions.push({
|
|
24689
24934
|
label: `Remove ${VERITY_MD_FILE}`,
|
|
24690
|
-
apply: () => (0,
|
|
24935
|
+
apply: () => (0, import_node_fs46.rmSync)(verityMd, { force: true })
|
|
24691
24936
|
});
|
|
24692
24937
|
}
|
|
24693
24938
|
}
|
|
24694
24939
|
const cleanupEmptyDir = (path) => {
|
|
24695
|
-
if ((0,
|
|
24696
|
-
(0,
|
|
24940
|
+
if ((0, import_node_fs46.existsSync)(path) && (0, import_node_fs46.statSync)(path).isDirectory() && (0, import_node_fs46.readdirSync)(path).length === 0) {
|
|
24941
|
+
(0, import_node_fs46.rmdirSync)(path);
|
|
24697
24942
|
}
|
|
24698
24943
|
};
|
|
24699
24944
|
actions.push({
|
|
@@ -24704,11 +24949,11 @@ function registerUninstallCommand(program2) {
|
|
|
24704
24949
|
}
|
|
24705
24950
|
});
|
|
24706
24951
|
const home = process.env.HOME ?? "";
|
|
24707
|
-
const globalVerityDir = (0,
|
|
24708
|
-
if (purgeGlobal && (0,
|
|
24952
|
+
const globalVerityDir = (0, import_node_path31.join)(home, ".verity");
|
|
24953
|
+
if (purgeGlobal && (0, import_node_fs46.existsSync)(globalVerityDir)) {
|
|
24709
24954
|
actions.push({
|
|
24710
24955
|
label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
|
|
24711
|
-
apply: () => (0,
|
|
24956
|
+
apply: () => (0, import_node_fs46.rmSync)(globalVerityDir, { recursive: true, force: true })
|
|
24712
24957
|
});
|
|
24713
24958
|
}
|
|
24714
24959
|
if (actions.length === 0) {
|
|
@@ -24902,8 +25147,8 @@ function registerTaskCommands(program2) {
|
|
|
24902
25147
|
}
|
|
24903
25148
|
|
|
24904
25149
|
// src/commands/reset.ts
|
|
24905
|
-
var
|
|
24906
|
-
var
|
|
25150
|
+
var import_node_fs47 = require("node:fs");
|
|
25151
|
+
var import_node_path32 = require("node:path");
|
|
24907
25152
|
function registerResetCommand(program2) {
|
|
24908
25153
|
program2.command("reset").description("Close the current task and clear transient state").option("--keep-task", "Only purge caches; leave the current task open").option("--all", "Also purge diagnostic logs (.verity/.logs/)").action(async (opts) => {
|
|
24909
25154
|
const globals = program2.opts();
|
|
@@ -24940,11 +25185,11 @@ function registerResetCommand(program2) {
|
|
|
24940
25185
|
}
|
|
24941
25186
|
const cacheDir = projectPath(CACHE_DIR);
|
|
24942
25187
|
let purged = 0;
|
|
24943
|
-
if ((0,
|
|
24944
|
-
for (const entry of (0,
|
|
25188
|
+
if ((0, import_node_fs47.existsSync)(cacheDir)) {
|
|
25189
|
+
for (const entry of (0, import_node_fs47.readdirSync)(cacheDir)) {
|
|
24945
25190
|
if (entry.startsWith("pending-")) {
|
|
24946
25191
|
try {
|
|
24947
|
-
(0,
|
|
25192
|
+
(0, import_node_fs47.unlinkSync)((0, import_node_path32.join)(cacheDir, entry));
|
|
24948
25193
|
purged++;
|
|
24949
25194
|
} catch {
|
|
24950
25195
|
}
|
|
@@ -24959,19 +25204,19 @@ function registerResetCommand(program2) {
|
|
|
24959
25204
|
projectPath(`${VERITY_DIR}/.last-analysis`)
|
|
24960
25205
|
];
|
|
24961
25206
|
for (const file of filesToClear) {
|
|
24962
|
-
if ((0,
|
|
25207
|
+
if ((0, import_node_fs47.existsSync)(file)) {
|
|
24963
25208
|
try {
|
|
24964
|
-
(0,
|
|
25209
|
+
(0, import_node_fs47.writeFileSync)(file, "");
|
|
24965
25210
|
} catch {
|
|
24966
25211
|
}
|
|
24967
25212
|
}
|
|
24968
25213
|
}
|
|
24969
25214
|
if (opts.all) {
|
|
24970
25215
|
const logsDir = projectPath(`${VERITY_DIR}/.logs`);
|
|
24971
|
-
if ((0,
|
|
24972
|
-
for (const entry of (0,
|
|
25216
|
+
if ((0, import_node_fs47.existsSync)(logsDir)) {
|
|
25217
|
+
for (const entry of (0, import_node_fs47.readdirSync)(logsDir)) {
|
|
24973
25218
|
try {
|
|
24974
|
-
(0,
|
|
25219
|
+
(0, import_node_fs47.unlinkSync)((0, import_node_path32.join)(logsDir, entry));
|
|
24975
25220
|
} catch {
|
|
24976
25221
|
}
|
|
24977
25222
|
}
|
|
@@ -25279,8 +25524,8 @@ function registerTelemetryCommands(program2) {
|
|
|
25279
25524
|
}
|
|
25280
25525
|
|
|
25281
25526
|
// src/cli.ts
|
|
25282
|
-
program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.
|
|
25283
|
-
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.
|
|
25527
|
+
program.name("verity").description("CLI for Verity quality gate service").version("0.31.1-experimental.b9b3862").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
|
|
25528
|
+
installStderrLog(actionCommand.name(), process.argv.slice(2), "0.31.1-experimental.b9b3862");
|
|
25284
25529
|
setUserNamedServiceUrl(program.opts().serviceUrl);
|
|
25285
25530
|
try {
|
|
25286
25531
|
await foldLegacyLocalCredential();
|
|
@@ -46,6 +46,13 @@ answers, the hook wiring, and a `next` list. Branch on it:
|
|
|
46
46
|
to launch a second Claude Code session on top of this one.
|
|
47
47
|
- **`blocked` is true** โ a required prerequisite is missing. Show the
|
|
48
48
|
`prerequisites[].remedy` lines and stop; nothing below can work.
|
|
49
|
+
- **`artifacts.standard` is true but `artifacts.analysisConfig` is false** โ this
|
|
50
|
+
project adopted a Standard rather than synthesizing one (`verity init` can pull
|
|
51
|
+
the team's Standard straight from the service), and the only missing piece is
|
|
52
|
+
the analysis config. **Do Step 2 and Step 5 only**, then `verity config push`,
|
|
53
|
+
then stop. Do NOT re-synthesize the Standard: the team already agreed one, and
|
|
54
|
+
`verity standard push` auto-versions, so writing a new one lands as the version
|
|
55
|
+
everybody is then held to.
|
|
49
56
|
- **`artifacts.verityMd` is true and the user did not pass `--force`** โ already
|
|
50
57
|
configured. Say "Already configured. Run `/verity-setup --force` to
|
|
51
58
|
reconfigure." and stop.
|
package/package.json
CHANGED