@pareto4/pp-core 1.8.2 → 1.10.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/.claude-plugin/plugin.json +1 -1
- package/README.md +3 -0
- package/agents/pp-code-fixer.md +11 -0
- package/agents/pp-code-reviewer.md +77 -1
- package/agents/pp-executor.md +34 -1
- package/agents/pp-planner.md +24 -103
- package/bin/install.js +9 -1
- package/commands/pp-ns-manage.md +2 -1
- package/commands/pp-vault-promote.md +31 -0
- package/gemini-extension.json +1 -1
- package/package.json +6 -3
- package/pareto-skills/pp-project-setup/SKILL.md +32 -440
- package/pp-core/bin/lib/capability-registry.cjs +111 -2
- package/pp-core/bin/lib/capability-state.cjs +54 -10
- package/pp-core/bin/lib/check-command-router.cjs +473 -3
- package/pp-core/bin/lib/clusters.cjs +3 -0
- package/pp-core/bin/lib/config.cjs +7 -22
- package/pp-core/bin/lib/io.cjs +7 -0
- package/pp-core/bin/lib/loop-resolver.cjs +6 -0
- package/pp-core/bin/lib/practices-review-check.cjs +938 -0
- package/pp-core/bin/lib/profile-output.cjs +4 -2
- package/pp-core/bin/lib/vault-command-router.cjs +108 -2
- package/pp-core/bin/lib/vault-practices.cjs +982 -0
- package/pp-core/bin/lib/vault-write-back.cjs +756 -0
- package/pp-core/bin/lib/vault.cjs +45 -3
- package/pp-core/bin/pp-tools.cjs +26 -0
- package/pp-core/bin/shared/config-schema.manifest.json +4 -0
- package/pp-core/references/planner-checkpoints.md +72 -0
- package/pp-core/references/planner-tdd.md +55 -0
- package/pp-core/references/planner-vault-practices.md +67 -0
- package/pp-core/templates/practices/README.md +122 -0
- package/pp-core/templates/practices/error-handling.md +18 -0
- package/pp-core/templates/practices/minimal-comments.md +14 -0
- package/pp-core/templates/practices/naming-convention.md +18 -0
- package/pp-core/templates/practices/practice-template.md +23 -0
- package/pp-core/workflows/code-review.md +74 -18
- package/pp-core/workflows/discovery-sprint.md +409 -16
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +250 -0
- package/pp-core/workflows/discovery-to-build.md +218 -48
- package/pp-core/workflows/discuss-phase.md +1 -1
- package/pp-core/workflows/execute-phase.md +1 -1
- package/pp-core/workflows/plan-phase.md +1 -1
- package/pp-core/workflows/ship.md +1 -0
- package/pp-core/workflows/vault-write-back.md +213 -0
- package/scripts/changeset/lint.cjs +20 -5
- package/scripts/gen-capability-registry.cjs +68 -8
- package/scripts/lint-allow-test-rule-refs.allowlist.json +8 -1
- package/scripts/lint-descriptions.cjs +3 -1
- package/scripts/lint-docs-required.cjs +10 -3
- package/scripts/lint-legacy-dir-name.cjs +5 -0
- package/scripts/lint-test-file-count.allowlist.json +10 -1
- package/scripts/release-channel.cjs +58 -0
- package/scripts/release-tarball-smoke.cjs +4 -0
- package/scripts/verify-opengsd-sweep.cjs +12 -0
- package/scripts/verify-rename-guard.cjs +333 -0
|
@@ -441,8 +441,10 @@ function generateSkillsSection(cwd) {
|
|
|
441
441
|
for (const entry of entries) {
|
|
442
442
|
if (!entry.isDirectory())
|
|
443
443
|
continue;
|
|
444
|
-
// Skip
|
|
445
|
-
|
|
444
|
+
// Skip pp-core's own installed skills (current 'pp-' naming plus legacy
|
|
445
|
+
// 'gsd-' directories, backward compatibility retained) — only surface
|
|
446
|
+
// project-specific skills.
|
|
447
|
+
if (entry.name.startsWith('pp-') || entry.name.startsWith('gsd-'))
|
|
446
448
|
continue;
|
|
447
449
|
const skillMdPath = node_path_1.default.join(absDir, entry.name, 'SKILL.md');
|
|
448
450
|
if (!node_fs_1.default.existsSync(skillMdPath))
|
|
@@ -29,11 +29,12 @@
|
|
|
29
29
|
*/
|
|
30
30
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
31
31
|
const io = require("./io.cjs");
|
|
32
|
+
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
32
33
|
const { ERROR_REASON } = io;
|
|
33
34
|
// Default CoreModule implementation. _core seam overrides this for test injection.
|
|
34
35
|
const _defaultCore = { output: io.output };
|
|
35
36
|
// ─── Implementation ───────────────────────────────────────────────────────────
|
|
36
|
-
function routeVaultCommand({ args, cwd, raw, error, _vault, _core }) {
|
|
37
|
+
function routeVaultCommand({ args, cwd, raw, error, _vault, _core, _practices, _exec, _writeBack }) {
|
|
37
38
|
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
|
|
38
39
|
const vault = _vault ?? require('./vault.cjs');
|
|
39
40
|
const c = _core ?? _defaultCore;
|
|
@@ -47,8 +48,113 @@ function routeVaultCommand({ args, cwd, raw, error, _vault, _core }) {
|
|
|
47
48
|
else if (subcommand === 'summary') {
|
|
48
49
|
c.output(vault.vaultSummary(cwd), raw);
|
|
49
50
|
}
|
|
51
|
+
else if (subcommand === 'practices') {
|
|
52
|
+
const verb = args[2];
|
|
53
|
+
if (verb === 'match') {
|
|
54
|
+
// T-24-08b: reject any unrecognized `--`-prefixed argument BEFORE the
|
|
55
|
+
// `--changed` git call (so a typo never spawns a subprocess) instead
|
|
56
|
+
// of silently filtering it out of the positional file list. Matched on
|
|
57
|
+
// the WHOLE token so `--files=…` is caught by the same check as
|
|
58
|
+
// `--files value`. DECISION (recorded, not deferred): `--files=` is
|
|
59
|
+
// NOT implemented as a comma-splitting alias here, unlike
|
|
60
|
+
// `/pp-code-review`'s `--files=a,b` (pp-core/workflows/code-review.md).
|
|
61
|
+
// The auditor marked the alias "optional and secondary"; the required
|
|
62
|
+
// behavior is loud failure, and the error message below names the
|
|
63
|
+
// supported forms instead. The host (pp-core/bin/pp-tools.cjs) already
|
|
64
|
+
// splices out its own global flags (--json-errors/--cwd/--ws/--raw/
|
|
65
|
+
// --pick/--default) before dispatch, which is why the recognized set
|
|
66
|
+
// here is only two flags wide.
|
|
67
|
+
const RECOGNIZED_MATCH_FLAGS = new Set(['--changed', '--raw', '--block']);
|
|
68
|
+
const unrecognizedFlag = args.slice(3).find((a) => a.startsWith('--') && !RECOGNIZED_MATCH_FLAGS.has(a));
|
|
69
|
+
if (unrecognizedFlag !== undefined) {
|
|
70
|
+
error(`Unrecognized flag "${unrecognizedFlag}" for vault practices match. Supported: positional file paths, --changed, --raw, or --block.`, ERROR_REASON.SDK_FAIL_FAST);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
|
|
74
|
+
const practices = _practices ?? require('./vault-practices.cjs');
|
|
75
|
+
const runGit = _exec ?? shell_command_projection_cjs_1.execGit;
|
|
76
|
+
let files;
|
|
77
|
+
if (args.includes('--changed')) {
|
|
78
|
+
const gitResult = runGit(['diff', '--name-only', 'HEAD'], { cwd });
|
|
79
|
+
if (gitResult.exitCode !== 0) {
|
|
80
|
+
error('Could not determine changed files: git diff failed (WR-02)', ERROR_REASON.SDK_FAIL_FAST);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
files = gitResult.stdout.split('\n').map((f) => f.trim()).filter((f) => f.length > 0);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
files = args.slice(3).filter((a) => !a.startsWith('--'));
|
|
87
|
+
}
|
|
88
|
+
const result = practices.matchPractices({ cwd, files });
|
|
89
|
+
if (args.includes('--block')) {
|
|
90
|
+
// T-25-08: emit the finished <vault_practices> block so the planner
|
|
91
|
+
// embeds it VERBATIM. The planner never renames a field or applies a
|
|
92
|
+
// fence itself — that transformation used to be prose-only, which is
|
|
93
|
+
// what left the phase's prompt-injection mitigation unenforced.
|
|
94
|
+
c.output(result, true, practices.renderPracticesBlock(result));
|
|
95
|
+
}
|
|
96
|
+
else if (raw) {
|
|
97
|
+
c.output(result, true, JSON.stringify(result));
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
c.output(result, true, practices.renderPracticesReport(result));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else if (verb === 'promote') {
|
|
104
|
+
// T-29-16: reject any unrecognized `--`-prefixed argument BEFORE the
|
|
105
|
+
// lazy require of the write-back module and before any subprocess can
|
|
106
|
+
// spawn — mirrors the `match` branch's T-24-08b guard exactly. Matched
|
|
107
|
+
// on the WHOLE token so `--name=value` is caught by the same check as
|
|
108
|
+
// `--name value`. The recognized set holds exactly three flags — no
|
|
109
|
+
// flag that disables, bypasses, or relaxes any guard exists.
|
|
110
|
+
const RECOGNIZED_PROMOTE_FLAGS = new Set(['--confirmed', '--expect', '--raw']);
|
|
111
|
+
const promoteArgs = args.slice(3);
|
|
112
|
+
const unrecognizedPromoteFlag = promoteArgs.find((a) => a.startsWith('--') && !RECOGNIZED_PROMOTE_FLAGS.has(a));
|
|
113
|
+
if (unrecognizedPromoteFlag !== undefined) {
|
|
114
|
+
error(`Unrecognized flag "${unrecognizedPromoteFlag}" for vault practices promote. Supported: --confirmed, --expect <token>, --raw.`, ERROR_REASON.SDK_FAIL_FAST);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// Positional file path: the first token that is not `--`-prefixed and
|
|
118
|
+
// is not the value immediately following `--expect`.
|
|
119
|
+
const expectFlagIdx = promoteArgs.indexOf('--expect');
|
|
120
|
+
const expectValueIdx = expectFlagIdx !== -1 ? expectFlagIdx + 1 : -1;
|
|
121
|
+
const filePath = promoteArgs.find((a, i) => !a.startsWith('--') && i !== expectValueIdx);
|
|
122
|
+
if (filePath === undefined) {
|
|
123
|
+
error('No practice file was given. Usage: vault practices promote <file> [--confirmed --expect <token>] [--raw].', ERROR_REASON.SDK_MISSING_ARG);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const confirmed = promoteArgs.includes('--confirmed');
|
|
127
|
+
const expect = expectValueIdx !== -1 ? promoteArgs[expectValueIdx] : undefined;
|
|
128
|
+
if (confirmed && (expect === undefined || expect.startsWith('--'))) {
|
|
129
|
+
error('This practice was not pushed: the confirmation is missing the preview token it needs. ' +
|
|
130
|
+
'Run the promotion without confirming first, then pass the token it prints.', ERROR_REASON.VAULT_PROMOTE_CONCURRENT_EDIT);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-assignment
|
|
134
|
+
const writeBack = _writeBack ?? require('./vault-write-back.cjs');
|
|
135
|
+
const verdict = writeBack.promotePractice({ cwd, filePath, confirmed, expect });
|
|
136
|
+
// Emit the structured verdict BEFORE any error call — a refusal still
|
|
137
|
+
// carries its verdict to stdout. This ordering is the whole point of
|
|
138
|
+
// the uniform D-16 refusal contract and is what lets a wrapper
|
|
139
|
+
// workflow render results without scraping prose.
|
|
140
|
+
if (raw) {
|
|
141
|
+
c.output(verdict, true, JSON.stringify(verdict));
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
c.output(verdict, true, writeBack.renderPromoteReport(verdict));
|
|
145
|
+
}
|
|
146
|
+
if (verdict.status === 'refused') {
|
|
147
|
+
// An action command's refusal must be observable in the exit code —
|
|
148
|
+
// do not adopt the ship gate's always-exit-zero convention here.
|
|
149
|
+
error(verdict.message ?? 'This practice was not promoted.', verdict.reason ?? ERROR_REASON.UNKNOWN);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
error('Unknown practices subcommand. Available: match, promote', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
50
156
|
else {
|
|
51
|
-
error('Unknown vault subcommand. Available: fetch, status, summary', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
157
|
+
error('Unknown vault subcommand. Available: fetch, status, summary, practices', ERROR_REASON.SDK_UNKNOWN_COMMAND);
|
|
52
158
|
}
|
|
53
159
|
}
|
|
54
160
|
module.exports = {
|