pumuki 6.3.97 → 6.3.99
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/AGENTS.md +269 -0
- package/CHANGELOG.md +697 -0
- package/README.md +4 -2
- package/VERSION +1 -1
- package/docs/README.md +13 -9
- package/docs/operations/RELEASE_NOTES.md +12 -76
- package/docs/product/HOW_IT_WORKS.md +6 -0
- package/docs/product/INSTALLATION.md +1 -1
- package/docs/product/USAGE.md +41 -4
- package/docs/tracking/plan-curso-pumuki-stack-my-architecture.md +118 -0
- package/docs/validation/README.md +6 -3
- package/integrations/config/skillsCustomRules.ts +18 -99
- package/integrations/evidence/buildEvidence.ts +0 -24
- package/integrations/evidence/repoState.ts +0 -3
- package/integrations/evidence/schema.ts +0 -18
- package/integrations/evidence/writeEvidence.ts +0 -24
- package/integrations/gate/evaluateAiGate.ts +15 -232
- package/integrations/gate/remediationCatalog.ts +0 -8
- package/integrations/git/GitService.ts +44 -5
- package/integrations/git/aiGateRepoPolicyFindings.ts +0 -4
- package/integrations/git/runPlatformGate.ts +1 -9
- package/integrations/git/runPlatformGateFacts.ts +19 -1
- package/integrations/git/runPlatformGateOutput.ts +27 -36
- package/integrations/lifecycle/adapter.templates.json +7 -13
- package/integrations/lifecycle/adapter.ts +0 -24
- package/integrations/lifecycle/artifacts.ts +1 -6
- package/integrations/lifecycle/audit.ts +101 -0
- package/integrations/lifecycle/cli.ts +110 -70
- package/integrations/lifecycle/cliSdd.ts +13 -8
- package/integrations/lifecycle/doctor.ts +16 -48
- package/integrations/lifecycle/hookManager.ts +0 -77
- package/integrations/lifecycle/index.ts +2 -0
- package/integrations/lifecycle/install.ts +0 -21
- package/integrations/lifecycle/npmService.ts +3 -155
- package/integrations/lifecycle/policyValidationSnapshot.ts +8 -2
- package/integrations/lifecycle/preWriteAutomation.ts +7 -77
- package/integrations/lifecycle/state.ts +1 -8
- package/integrations/lifecycle/status.ts +2 -29
- package/integrations/mcp/aiGateCheck.ts +26 -206
- package/integrations/mcp/autoExecuteAiStart.ts +87 -94
- package/integrations/mcp/enterpriseServer.ts +7 -23
- package/integrations/mcp/enterpriseStdioServer.cli.ts +4 -31
- package/integrations/mcp/preFlightCheck.ts +5 -51
- package/integrations/platform/detectPlatforms.ts +37 -0
- package/integrations/policy/experimentalFeatures.ts +1 -1
- package/integrations/sdd/evidenceScaffold.ts +2 -109
- package/package.json +10 -2
- package/scripts/check-tracking-single-active.sh +1 -1
- package/scripts/consumer-menu-matrix-baseline-report-lib.ts +13 -38
- package/scripts/consumer-postinstall-resolve-args.cjs +44 -0
- package/scripts/consumer-postinstall.cjs +76 -21
- package/scripts/framework-menu-advanced-view-lib.ts +0 -15
- package/scripts/framework-menu-consumer-actions-lib.ts +28 -4
- package/scripts/framework-menu-consumer-preflight-hints.ts +5 -2
- package/scripts/framework-menu-consumer-preflight-render.ts +0 -10
- package/scripts/framework-menu-consumer-preflight-run.ts +0 -23
- package/scripts/framework-menu-consumer-preflight-types.ts +0 -12
- package/scripts/framework-menu-consumer-runtime-actions.ts +87 -17
- package/scripts/framework-menu-consumer-runtime-audit.ts +36 -2
- package/scripts/framework-menu-consumer-runtime-evidence-classic.ts +140 -0
- package/scripts/framework-menu-consumer-runtime-lib.ts +2 -10
- package/scripts/framework-menu-consumer-runtime-menu.ts +4 -18
- package/scripts/framework-menu-consumer-runtime-types.ts +3 -3
- package/scripts/framework-menu-evidence-summary-lib.ts +1 -0
- package/scripts/framework-menu-evidence-summary-read.ts +57 -5
- package/scripts/framework-menu-evidence-summary-severity.ts +3 -1
- package/scripts/framework-menu-evidence-summary-types.ts +7 -0
- package/scripts/framework-menu-gate-lib.ts +9 -0
- package/scripts/framework-menu-layout-data.ts +5 -0
- package/scripts/framework-menu-matrix-baseline-lib.ts +15 -14
- package/scripts/framework-menu-matrix-canary-lib.ts +22 -1
- package/scripts/framework-menu-matrix-evidence-lib.ts +1 -0
- package/scripts/framework-menu-matrix-evidence-types.ts +13 -1
- package/scripts/framework-menu-matrix-runner-lib.ts +35 -0
- package/scripts/framework-menu-system-notifications-cause.ts +0 -24
- package/scripts/framework-menu-system-notifications-macos-swift-source.ts +24 -204
- package/scripts/framework-menu-system-notifications-macos.ts +4 -0
- package/scripts/framework-menu-system-notifications-payloads-blocked.ts +1 -1
- package/scripts/framework-menu-system-notifications-remediation.ts +13 -24
- package/scripts/framework-menu-system-notifications-text.ts +1 -7
- package/scripts/framework-menu.ts +3 -2
- package/scripts/package-install-smoke-consumer-git-repo-lib.ts +1 -10
- package/scripts/package-install-smoke-consumer-npm-lib.ts +9 -46
- package/scripts/pumuki-full-surface-smoke-lib.ts +37 -0
- package/scripts/pumuki-full-surface-smoke.ts +346 -0
- package/scripts/pumuki-smoke-installed-wrapper.cjs +31 -0
- package/integrations/evidence/trackingContract.ts +0 -150
- package/integrations/gate/governanceActionCatalog.ts +0 -275
- package/integrations/lifecycle/bootstrapManifest.ts +0 -248
- package/integrations/lifecycle/cliGovernanceConsole.ts +0 -69
- package/integrations/lifecycle/governanceNextAction.ts +0 -164
- package/integrations/lifecycle/governanceObservationSnapshot.ts +0 -613
- package/integrations/mcp/alignedPlatformGate.ts +0 -232
- package/integrations/mcp/readMcpPrePushStdin.ts +0 -7
- package/scripts/build-ruralgo-s1-evidence-pack.ts +0 -85
- package/scripts/ruralgo-s1-evidence-pack-lib.ts +0 -200
|
@@ -27,81 +27,6 @@ export type PumukiHooksDirectoryResolution = {
|
|
|
27
27
|
};
|
|
28
28
|
|
|
29
29
|
const HOOK_FILE_MODE = 0o755;
|
|
30
|
-
const PUMUKI_WORKTREE_HOOKS_PATH = '.pumuki/git-hooks';
|
|
31
|
-
|
|
32
|
-
const readLocalGitConfigValue = (repoRoot: string, key: string): string | null => {
|
|
33
|
-
try {
|
|
34
|
-
const value = execFileSync('git', ['config', '--local', '--get', key], {
|
|
35
|
-
cwd: repoRoot,
|
|
36
|
-
encoding: 'utf8',
|
|
37
|
-
stdio: ['ignore', 'pipe', 'ignore'],
|
|
38
|
-
}).trim();
|
|
39
|
-
return value.length > 0 ? value : null;
|
|
40
|
-
} catch {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const writeLocalGitConfigValue = (repoRoot: string, key: string, value: string): void => {
|
|
46
|
-
execFileSync('git', ['config', '--local', key, value], {
|
|
47
|
-
cwd: repoRoot,
|
|
48
|
-
stdio: ['ignore', 'ignore', 'ignore'],
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const unsetLocalGitConfigValue = (repoRoot: string, key: string): void => {
|
|
53
|
-
try {
|
|
54
|
-
execFileSync('git', ['config', '--local', '--unset-all', key], {
|
|
55
|
-
cwd: repoRoot,
|
|
56
|
-
stdio: ['ignore', 'ignore', 'ignore'],
|
|
57
|
-
});
|
|
58
|
-
} catch {
|
|
59
|
-
// noop
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const isWorktreeCheckout = (repoRoot: string): boolean => {
|
|
64
|
-
try {
|
|
65
|
-
const gitPointer = readFileSync(join(repoRoot, '.git'), 'utf8').trim();
|
|
66
|
-
return /^gitdir:\s+/i.test(gitPointer);
|
|
67
|
-
} catch {
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
const isPumukiManagedWorktreeHooksPath = (repoRoot: string, hooksPath: string): boolean => {
|
|
73
|
-
const normalizedHooksPath = hooksPath.trim();
|
|
74
|
-
return (
|
|
75
|
-
normalizedHooksPath === PUMUKI_WORKTREE_HOOKS_PATH ||
|
|
76
|
-
normalizedHooksPath === resolve(repoRoot, PUMUKI_WORKTREE_HOOKS_PATH)
|
|
77
|
-
);
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const ensureWorktreeLocalHooksPath = (repoRoot: string): void => {
|
|
81
|
-
if (!isWorktreeCheckout(repoRoot)) {
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const currentHooksPath = readLocalGitConfigValue(repoRoot, 'core.hooksPath');
|
|
86
|
-
if (currentHooksPath) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
writeLocalGitConfigValue(repoRoot, 'core.hooksPath', PUMUKI_WORKTREE_HOOKS_PATH);
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
const clearWorktreeLocalHooksPath = (repoRoot: string): void => {
|
|
94
|
-
if (!isWorktreeCheckout(repoRoot)) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const currentHooksPath = readLocalGitConfigValue(repoRoot, 'core.hooksPath');
|
|
99
|
-
if (!currentHooksPath || !isPumukiManagedWorktreeHooksPath(repoRoot, currentHooksPath)) {
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
unsetLocalGitConfigValue(repoRoot, 'core.hooksPath');
|
|
104
|
-
};
|
|
105
30
|
|
|
106
31
|
const resolveGitPath = (repoRoot: string, gitPathTarget: string): string | null => {
|
|
107
32
|
try {
|
|
@@ -212,7 +137,6 @@ const ensureHooksDirectory = (repoRoot: string): void => {
|
|
|
212
137
|
};
|
|
213
138
|
|
|
214
139
|
export const installPumukiHooks = (repoRoot: string): HookInstallResult => {
|
|
215
|
-
ensureWorktreeLocalHooksPath(repoRoot);
|
|
216
140
|
ensureHooksDirectory(repoRoot);
|
|
217
141
|
const changedHooks: PumukiManagedHook[] = [];
|
|
218
142
|
|
|
@@ -256,7 +180,6 @@ export const uninstallPumukiHooks = (repoRoot: string): HookUninstallResult => {
|
|
|
256
180
|
changedHooks.push(hook);
|
|
257
181
|
}
|
|
258
182
|
|
|
259
|
-
clearWorktreeLocalHooksPath(repoRoot);
|
|
260
183
|
return { changedHooks };
|
|
261
184
|
};
|
|
262
185
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { runLifecycleDoctor, doctorHasBlockingIssues } from './doctor';
|
|
2
|
+
export { runLifecycleAudit } from './audit';
|
|
3
|
+
export type { LifecycleAuditResult, LifecycleAuditStage } from './audit';
|
|
2
4
|
export { runLifecycleInstall } from './install';
|
|
3
5
|
export { runLifecycleUninstall } from './uninstall';
|
|
4
6
|
export { runLifecycleRemove } from './remove';
|
|
@@ -13,7 +13,6 @@ import { createEmptyEvaluationMetrics } from '../evidence/evaluationMetrics';
|
|
|
13
13
|
import { readOpenSpecManagedArtifacts, writeLifecycleState } from './state';
|
|
14
14
|
import { ensureRuntimeArtifactsIgnored } from './artifacts';
|
|
15
15
|
import { runLifecycleAdapterInstall } from './adapter';
|
|
16
|
-
import { writeLifecycleBootstrapManifest } from './bootstrapManifest';
|
|
17
16
|
|
|
18
17
|
export type LifecycleInstallResult = {
|
|
19
18
|
repoRoot: string;
|
|
@@ -21,10 +20,6 @@ export type LifecycleInstallResult = {
|
|
|
21
20
|
changedHooks: ReadonlyArray<string>;
|
|
22
21
|
openSpecBootstrap?: OpenSpecBootstrapResult;
|
|
23
22
|
degradedDoctorBypass?: boolean;
|
|
24
|
-
bootstrapManifest: {
|
|
25
|
-
path: string;
|
|
26
|
-
changed: boolean;
|
|
27
|
-
};
|
|
28
23
|
};
|
|
29
24
|
|
|
30
25
|
const shouldBootstrapEvidence = (repoRoot: string): boolean =>
|
|
@@ -108,20 +103,12 @@ export const runLifecycleInstall = (params?: {
|
|
|
108
103
|
openSpecManagedArtifacts: priorArtifacts.length > 0 ? priorArtifacts : undefined,
|
|
109
104
|
});
|
|
110
105
|
ensureRepoBaselineAdapter(report.repoRoot);
|
|
111
|
-
const bootstrapManifest = writeLifecycleBootstrapManifest({
|
|
112
|
-
git,
|
|
113
|
-
repoRoot: report.repoRoot,
|
|
114
|
-
});
|
|
115
106
|
return {
|
|
116
107
|
repoRoot: report.repoRoot,
|
|
117
108
|
version,
|
|
118
109
|
changedHooks,
|
|
119
110
|
openSpecBootstrap: undefined,
|
|
120
111
|
degradedDoctorBypass: true,
|
|
121
|
-
bootstrapManifest: {
|
|
122
|
-
path: bootstrapManifest.path,
|
|
123
|
-
changed: bootstrapManifest.changed,
|
|
124
|
-
},
|
|
125
112
|
};
|
|
126
113
|
}
|
|
127
114
|
const renderedIssues = report.issues.map((issue) => `- [${issue.severity}] ${issue.message}`).join('\n');
|
|
@@ -155,19 +142,11 @@ export const runLifecycleInstall = (params?: {
|
|
|
155
142
|
openSpecManagedArtifacts: Array.from(mergedOpenSpecArtifacts),
|
|
156
143
|
});
|
|
157
144
|
ensureRepoBaselineAdapter(report.repoRoot);
|
|
158
|
-
const bootstrapManifest = writeLifecycleBootstrapManifest({
|
|
159
|
-
git,
|
|
160
|
-
repoRoot: report.repoRoot,
|
|
161
|
-
});
|
|
162
145
|
|
|
163
146
|
return {
|
|
164
147
|
repoRoot: report.repoRoot,
|
|
165
148
|
version,
|
|
166
149
|
changedHooks,
|
|
167
150
|
openSpecBootstrap,
|
|
168
|
-
bootstrapManifest: {
|
|
169
|
-
path: bootstrapManifest.path,
|
|
170
|
-
changed: bootstrapManifest.changed,
|
|
171
|
-
},
|
|
172
151
|
};
|
|
173
152
|
};
|
|
@@ -1,173 +1,21 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
1
|
import { spawnSync as runSpawnSync } from 'node:child_process';
|
|
4
2
|
|
|
5
3
|
export interface ILifecycleNpmService {
|
|
6
4
|
runNpm(args: ReadonlyArray<string>, cwd: string): void;
|
|
7
5
|
}
|
|
8
6
|
|
|
9
|
-
type LifecyclePackageManager = 'npm' | 'pnpm' | 'yarn';
|
|
10
|
-
|
|
11
|
-
type PackageManagerManifest = {
|
|
12
|
-
packageManager?: string;
|
|
13
|
-
workspaces?: unknown;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const resolvePackageManagerFromManifest = (cwd: string): LifecyclePackageManager | undefined => {
|
|
17
|
-
const packageJsonPath = join(cwd, 'package.json');
|
|
18
|
-
if (!existsSync(packageJsonPath)) {
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const raw = readFileSync(packageJsonPath, 'utf8');
|
|
24
|
-
const manifest = JSON.parse(raw) as PackageManagerManifest;
|
|
25
|
-
const packageManager = manifest.packageManager?.trim().toLowerCase();
|
|
26
|
-
if (!packageManager) {
|
|
27
|
-
return undefined;
|
|
28
|
-
}
|
|
29
|
-
if (packageManager.startsWith('pnpm@')) {
|
|
30
|
-
return 'pnpm';
|
|
31
|
-
}
|
|
32
|
-
if (packageManager.startsWith('yarn@')) {
|
|
33
|
-
return 'yarn';
|
|
34
|
-
}
|
|
35
|
-
if (packageManager.startsWith('npm@')) {
|
|
36
|
-
return 'npm';
|
|
37
|
-
}
|
|
38
|
-
} catch {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return undefined;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
export const resolveLifecyclePackageManager = (cwd: string): LifecyclePackageManager => {
|
|
46
|
-
const fromManifest = resolvePackageManagerFromManifest(cwd);
|
|
47
|
-
if (fromManifest) {
|
|
48
|
-
return fromManifest;
|
|
49
|
-
}
|
|
50
|
-
if (existsSync(join(cwd, 'pnpm-lock.yaml'))) {
|
|
51
|
-
return 'pnpm';
|
|
52
|
-
}
|
|
53
|
-
if (existsSync(join(cwd, 'yarn.lock'))) {
|
|
54
|
-
return 'yarn';
|
|
55
|
-
}
|
|
56
|
-
return 'npm';
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const isWorkspaceRoot = (cwd: string): boolean => {
|
|
60
|
-
if (existsSync(join(cwd, 'pnpm-workspace.yaml'))) {
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
const packageJsonPath = join(cwd, 'package.json');
|
|
64
|
-
if (!existsSync(packageJsonPath)) {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
try {
|
|
68
|
-
const raw = readFileSync(packageJsonPath, 'utf8');
|
|
69
|
-
const manifest = JSON.parse(raw) as PackageManagerManifest;
|
|
70
|
-
return Array.isArray(manifest.workspaces) || typeof manifest.workspaces === 'object';
|
|
71
|
-
} catch {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
const translateInstallLikeArgs = (
|
|
77
|
-
packageManager: Exclude<LifecyclePackageManager, 'npm'>,
|
|
78
|
-
args: ReadonlyArray<string>,
|
|
79
|
-
cwd: string
|
|
80
|
-
): ReadonlyArray<string> => {
|
|
81
|
-
const packageSpecs = args.filter((arg) => !arg.startsWith('-')).slice(1);
|
|
82
|
-
if (packageSpecs.length === 0) {
|
|
83
|
-
return args;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const useExact = args.includes('--save-exact');
|
|
87
|
-
const useDev = args.includes('--save-dev');
|
|
88
|
-
|
|
89
|
-
if (packageManager === 'pnpm') {
|
|
90
|
-
return [
|
|
91
|
-
'add',
|
|
92
|
-
...(isWorkspaceRoot(cwd) ? ['-w'] : []),
|
|
93
|
-
...(useDev ? ['-D'] : []),
|
|
94
|
-
...(useExact ? ['-E'] : []),
|
|
95
|
-
...packageSpecs,
|
|
96
|
-
];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return [
|
|
100
|
-
'add',
|
|
101
|
-
...(useDev ? ['--dev'] : []),
|
|
102
|
-
...(useExact ? ['--exact'] : []),
|
|
103
|
-
...packageSpecs,
|
|
104
|
-
];
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
const translateUninstallLikeArgs = (
|
|
108
|
-
packageManager: Exclude<LifecyclePackageManager, 'npm'>,
|
|
109
|
-
args: ReadonlyArray<string>,
|
|
110
|
-
cwd: string
|
|
111
|
-
): ReadonlyArray<string> => {
|
|
112
|
-
const packageSpecs = args.filter((arg) => !arg.startsWith('-')).slice(1);
|
|
113
|
-
if (packageSpecs.length === 0) {
|
|
114
|
-
return args;
|
|
115
|
-
}
|
|
116
|
-
if (packageManager === 'pnpm') {
|
|
117
|
-
return ['remove', ...(isWorkspaceRoot(cwd) ? ['-w'] : []), ...packageSpecs];
|
|
118
|
-
}
|
|
119
|
-
return ['remove', ...packageSpecs];
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
export const resolveLifecyclePackageManagerCommand = (
|
|
123
|
-
args: ReadonlyArray<string>,
|
|
124
|
-
cwd: string
|
|
125
|
-
): {
|
|
126
|
-
command: LifecyclePackageManager;
|
|
127
|
-
args: ReadonlyArray<string>;
|
|
128
|
-
} => {
|
|
129
|
-
const packageManager = resolveLifecyclePackageManager(cwd);
|
|
130
|
-
if (packageManager === 'npm') {
|
|
131
|
-
return {
|
|
132
|
-
command: 'npm',
|
|
133
|
-
args,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const primaryCommand = args[0];
|
|
138
|
-
if (primaryCommand === 'install') {
|
|
139
|
-
return {
|
|
140
|
-
command: packageManager,
|
|
141
|
-
args: translateInstallLikeArgs(packageManager, args, cwd),
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
if (primaryCommand === 'uninstall') {
|
|
145
|
-
return {
|
|
146
|
-
command: packageManager,
|
|
147
|
-
args: translateUninstallLikeArgs(packageManager, args, cwd),
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
command: packageManager,
|
|
153
|
-
args,
|
|
154
|
-
};
|
|
155
|
-
};
|
|
156
|
-
|
|
157
7
|
export class LifecycleNpmService implements ILifecycleNpmService {
|
|
158
8
|
runNpm(args: ReadonlyArray<string>, cwd: string): void {
|
|
159
|
-
const
|
|
160
|
-
const renderedCommand = `${execution.command} ${execution.args.join(' ')}`.trim();
|
|
161
|
-
const result = runSpawnSync(execution.command, execution.args, {
|
|
9
|
+
const result = runSpawnSync('npm', args, {
|
|
162
10
|
cwd,
|
|
163
11
|
stdio: 'inherit',
|
|
164
12
|
env: process.env,
|
|
165
13
|
});
|
|
166
14
|
if (result.error) {
|
|
167
|
-
throw new Error(
|
|
15
|
+
throw new Error(`npm ${args.join(' ')} failed: ${result.error.message}`);
|
|
168
16
|
}
|
|
169
17
|
if (typeof result.status !== 'number' || result.status !== 0) {
|
|
170
|
-
throw new Error(
|
|
18
|
+
throw new Error(`npm ${args.join(' ')} failed with exit code ${result.status ?? 'unknown'}`);
|
|
171
19
|
}
|
|
172
20
|
}
|
|
173
21
|
}
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
resolvePolicyForStage,
|
|
4
4
|
type ResolvedStagePolicy,
|
|
5
5
|
} from '../gate/stagePolicies';
|
|
6
|
+
import { resolvePreWriteEnforcement } from '../policy/preWriteEnforcement';
|
|
6
7
|
|
|
7
8
|
export type LifecyclePolicyValidationStageSnapshot = {
|
|
8
9
|
source: ResolvedStagePolicy['trace']['source'];
|
|
@@ -23,8 +24,13 @@ export type LifecyclePolicyValidationSnapshot = {
|
|
|
23
24
|
const POLICY_STAGES: ReadonlyArray<SkillsStage> = ['PRE_WRITE', 'PRE_COMMIT', 'PRE_PUSH', 'CI'];
|
|
24
25
|
|
|
25
26
|
const toStageSnapshot = (
|
|
27
|
+
stage: SkillsStage,
|
|
26
28
|
resolved: ResolvedStagePolicy
|
|
27
29
|
): LifecyclePolicyValidationStageSnapshot => {
|
|
30
|
+
const strictFromPolicy = resolved.trace.validation?.strict ?? false;
|
|
31
|
+
const strict = stage === 'PRE_WRITE'
|
|
32
|
+
? strictFromPolicy || resolvePreWriteEnforcement().blocking
|
|
33
|
+
: strictFromPolicy;
|
|
28
34
|
return {
|
|
29
35
|
source: resolved.trace.source,
|
|
30
36
|
bundle: resolved.trace.bundle,
|
|
@@ -34,7 +40,7 @@ const toStageSnapshot = (
|
|
|
34
40
|
policySource: resolved.trace.policySource ?? null,
|
|
35
41
|
validationStatus: resolved.trace.validation?.status ?? null,
|
|
36
42
|
validationCode: resolved.trace.validation?.code ?? null,
|
|
37
|
-
strict
|
|
43
|
+
strict,
|
|
38
44
|
};
|
|
39
45
|
};
|
|
40
46
|
|
|
@@ -42,7 +48,7 @@ export const readLifecyclePolicyValidationSnapshot = (
|
|
|
42
48
|
repoRoot: string
|
|
43
49
|
): LifecyclePolicyValidationSnapshot => {
|
|
44
50
|
const resolvedByStage = Object.fromEntries(
|
|
45
|
-
POLICY_STAGES.map((stage) => [stage, toStageSnapshot(resolvePolicyForStage(stage, repoRoot))])
|
|
51
|
+
POLICY_STAGES.map((stage) => [stage, toStageSnapshot(stage, resolvePolicyForStage(stage, repoRoot))])
|
|
46
52
|
) as Record<SkillsStage, LifecyclePolicyValidationStageSnapshot>;
|
|
47
53
|
|
|
48
54
|
return {
|
|
@@ -68,24 +68,16 @@ const hasAutoFixableEvidenceViolation = (aiGate: ReturnType<typeof evaluateAiGat
|
|
|
68
68
|
const hasEvidenceGateBlockedViolation = (aiGate: ReturnType<typeof evaluateAiGate>): boolean =>
|
|
69
69
|
aiGate.violations.some((violation) => violation.code === 'EVIDENCE_GATE_BLOCKED');
|
|
70
70
|
|
|
71
|
-
const hasAdapterMissingViolation = (aiGate: ReturnType<typeof evaluateAiGate>): boolean =>
|
|
72
|
-
aiGate.violations.some((violation) => violation.code === 'MCP_ENTERPRISE_ADAPTER_MISSING');
|
|
73
|
-
|
|
74
71
|
const hasAutoFixableMcpReceiptViolation = (aiGate: ReturnType<typeof evaluateAiGate>): boolean =>
|
|
75
|
-
|
|
76
|
-
&& aiGate.violations.some((violation) => PRE_WRITE_AUTOFIXABLE_MCP_RECEIPT_CODES.has(violation.code));
|
|
72
|
+
aiGate.violations.some((violation) => PRE_WRITE_AUTOFIXABLE_MCP_RECEIPT_CODES.has(violation.code));
|
|
77
73
|
|
|
78
74
|
const collectAutoFixableViolationCodes = (aiGate: ReturnType<typeof evaluateAiGate>): string[] =>
|
|
79
75
|
aiGate.violations
|
|
80
|
-
.filter(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
return !hasAdapterMissingViolation(aiGate);
|
|
88
|
-
})
|
|
76
|
+
.filter(
|
|
77
|
+
(violation) =>
|
|
78
|
+
PRE_WRITE_AUTOFIXABLE_EVIDENCE_CODES.has(violation.code)
|
|
79
|
+
|| PRE_WRITE_AUTOFIXABLE_MCP_RECEIPT_CODES.has(violation.code)
|
|
80
|
+
)
|
|
89
81
|
.map((violation) => violation.code)
|
|
90
82
|
.sort((left, right) => left.localeCompare(right));
|
|
91
83
|
|
|
@@ -106,7 +98,7 @@ export const buildPreWriteAutomationTrace = async (params: {
|
|
|
106
98
|
attempted: false,
|
|
107
99
|
actions: [],
|
|
108
100
|
};
|
|
109
|
-
if (params.sdd.stage !== 'PRE_WRITE') {
|
|
101
|
+
if (params.sdd.stage !== 'PRE_WRITE' || params.aiGate.allowed) {
|
|
110
102
|
return {
|
|
111
103
|
aiGate: params.aiGate,
|
|
112
104
|
trace,
|
|
@@ -114,42 +106,6 @@ export const buildPreWriteAutomationTrace = async (params: {
|
|
|
114
106
|
}
|
|
115
107
|
|
|
116
108
|
let aiGate = params.aiGate;
|
|
117
|
-
if (aiGate.allowed) {
|
|
118
|
-
trace.attempted = true;
|
|
119
|
-
try {
|
|
120
|
-
const gateExitCode = await params.runPlatformGate({
|
|
121
|
-
policy: {
|
|
122
|
-
stage: 'PRE_COMMIT',
|
|
123
|
-
blockOnOrAbove: 'ERROR',
|
|
124
|
-
warnOnOrAbove: 'WARN',
|
|
125
|
-
},
|
|
126
|
-
scope: {
|
|
127
|
-
kind: 'workingTree',
|
|
128
|
-
},
|
|
129
|
-
auditMode: 'gate',
|
|
130
|
-
dependencies: {
|
|
131
|
-
printGateFindings: () => {},
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
trace.actions.push({
|
|
135
|
-
action: 'refresh_evidence',
|
|
136
|
-
status: 'OK',
|
|
137
|
-
details: `stage=PRE_COMMIT runPlatformGate exit_code=${gateExitCode} parity_probe=allowed_initial`,
|
|
138
|
-
});
|
|
139
|
-
aiGate = activeDependencies.runEnterpriseAiGateCheck({
|
|
140
|
-
repoRoot: params.repoRoot,
|
|
141
|
-
stage: 'PRE_WRITE',
|
|
142
|
-
requireMcpReceipt: true,
|
|
143
|
-
}).result;
|
|
144
|
-
} catch (error) {
|
|
145
|
-
trace.actions.push({
|
|
146
|
-
action: 'refresh_evidence',
|
|
147
|
-
status: 'FAILED',
|
|
148
|
-
details: error instanceof Error ? error.message : 'Unknown PRE_COMMIT parity refresh error',
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
109
|
if (hasAutoFixableEvidenceViolation(aiGate)) {
|
|
154
110
|
trace.attempted = true;
|
|
155
111
|
try {
|
|
@@ -246,32 +202,6 @@ export const buildPreWriteAutomationTrace = async (params: {
|
|
|
246
202
|
stage: 'PRE_WRITE',
|
|
247
203
|
requireMcpReceipt: true,
|
|
248
204
|
}).result;
|
|
249
|
-
if (aiGate.allowed) {
|
|
250
|
-
const gateExitCode = await params.runPlatformGate({
|
|
251
|
-
policy: {
|
|
252
|
-
stage: 'PRE_COMMIT',
|
|
253
|
-
blockOnOrAbove: 'ERROR',
|
|
254
|
-
warnOnOrAbove: 'WARN',
|
|
255
|
-
},
|
|
256
|
-
scope: {
|
|
257
|
-
kind: 'workingTree',
|
|
258
|
-
},
|
|
259
|
-
auditMode: 'gate',
|
|
260
|
-
dependencies: {
|
|
261
|
-
printGateFindings: () => {},
|
|
262
|
-
},
|
|
263
|
-
});
|
|
264
|
-
trace.actions.push({
|
|
265
|
-
action: 'refresh_evidence',
|
|
266
|
-
status: 'OK',
|
|
267
|
-
details: `stage=PRE_COMMIT runPlatformGate exit_code=${gateExitCode} parity_probe=allowed_after_mcp_receipt_refresh`,
|
|
268
|
-
});
|
|
269
|
-
aiGate = activeDependencies.runEnterpriseAiGateCheck({
|
|
270
|
-
repoRoot: params.repoRoot,
|
|
271
|
-
stage: 'PRE_WRITE',
|
|
272
|
-
requireMcpReceipt: true,
|
|
273
|
-
}).result;
|
|
274
|
-
}
|
|
275
205
|
} catch (error) {
|
|
276
206
|
trace.actions.push({
|
|
277
207
|
action: 'refresh_mcp_receipt',
|
|
@@ -49,17 +49,10 @@ export const writeLifecycleState = (params: {
|
|
|
49
49
|
openSpecManagedArtifacts?: ReadonlyArray<string>;
|
|
50
50
|
}): void => {
|
|
51
51
|
const { git, repoRoot, version } = params;
|
|
52
|
-
const existingInstalledAt = git.localConfig(repoRoot, PUMUKI_CONFIG_KEYS.installedAt);
|
|
53
52
|
git.applyLocalConfig(repoRoot, PUMUKI_CONFIG_KEYS.installed, 'true');
|
|
54
53
|
git.applyLocalConfig(repoRoot, PUMUKI_CONFIG_KEYS.version, version);
|
|
55
54
|
git.applyLocalConfig(repoRoot, PUMUKI_CONFIG_KEYS.hooks, PUMUKI_MANAGED_HOOKS.join(','));
|
|
56
|
-
git.applyLocalConfig(
|
|
57
|
-
repoRoot,
|
|
58
|
-
PUMUKI_CONFIG_KEYS.installedAt,
|
|
59
|
-
typeof existingInstalledAt === 'string' && existingInstalledAt.trim().length > 0
|
|
60
|
-
? existingInstalledAt
|
|
61
|
-
: new Date().toISOString()
|
|
62
|
-
);
|
|
55
|
+
git.applyLocalConfig(repoRoot, PUMUKI_CONFIG_KEYS.installedAt, new Date().toISOString());
|
|
63
56
|
if (params.openSpecManagedArtifacts) {
|
|
64
57
|
const serialized = serializeManagedArtifacts(params.openSpecManagedArtifacts);
|
|
65
58
|
if (serialized) {
|
|
@@ -9,15 +9,6 @@ import {
|
|
|
9
9
|
readLifecyclePolicyValidationSnapshot,
|
|
10
10
|
type LifecyclePolicyValidationSnapshot,
|
|
11
11
|
} from './policyValidationSnapshot';
|
|
12
|
-
import {
|
|
13
|
-
readGovernanceObservationSnapshot,
|
|
14
|
-
type GovernanceObservationSnapshot,
|
|
15
|
-
} from './governanceObservationSnapshot';
|
|
16
|
-
import {
|
|
17
|
-
readGovernanceNextAction,
|
|
18
|
-
type GovernanceNextActionReader,
|
|
19
|
-
type GovernanceNextActionSummary,
|
|
20
|
-
} from './governanceNextAction';
|
|
21
12
|
import { readLifecycleState, type LifecycleState } from './state';
|
|
22
13
|
|
|
23
14
|
export type LifecycleStatus = {
|
|
@@ -31,14 +22,11 @@ export type LifecycleStatus = {
|
|
|
31
22
|
trackedNodeModulesCount: number;
|
|
32
23
|
policyValidation: LifecyclePolicyValidationSnapshot;
|
|
33
24
|
experimentalFeatures: LifecycleExperimentalFeaturesSnapshot;
|
|
34
|
-
governanceObservation: GovernanceObservationSnapshot;
|
|
35
|
-
governanceNextAction: GovernanceNextActionSummary;
|
|
36
25
|
};
|
|
37
26
|
|
|
38
27
|
export const readLifecycleStatus = (params?: {
|
|
39
28
|
cwd?: string;
|
|
40
29
|
git?: ILifecycleGitService;
|
|
41
|
-
governanceNextActionReader?: GovernanceNextActionReader;
|
|
42
30
|
}): LifecycleStatus => {
|
|
43
31
|
const git = params?.git ?? new LifecycleGitService();
|
|
44
32
|
const cwd = params?.cwd ?? process.cwd();
|
|
@@ -50,19 +38,6 @@ export const readLifecycleStatus = (params?: {
|
|
|
50
38
|
repoRoot,
|
|
51
39
|
lifecycleVersion: lifecycleState.version,
|
|
52
40
|
});
|
|
53
|
-
const policyValidation = readLifecyclePolicyValidationSnapshot(repoRoot);
|
|
54
|
-
const experimentalFeatures = readLifecycleExperimentalFeaturesSnapshot();
|
|
55
|
-
const governanceObservation = readGovernanceObservationSnapshot({
|
|
56
|
-
repoRoot,
|
|
57
|
-
experimentalFeatures,
|
|
58
|
-
policyValidation,
|
|
59
|
-
git,
|
|
60
|
-
});
|
|
61
|
-
const governanceNextAction = (params?.governanceNextActionReader ?? readGovernanceNextAction)({
|
|
62
|
-
repoRoot,
|
|
63
|
-
stage: 'PRE_WRITE',
|
|
64
|
-
governanceObservation,
|
|
65
|
-
});
|
|
66
41
|
|
|
67
42
|
return {
|
|
68
43
|
repoRoot,
|
|
@@ -73,9 +48,7 @@ export const readLifecycleStatus = (params?: {
|
|
|
73
48
|
hooksDirectory: hooksDirectory.path,
|
|
74
49
|
hooksDirectoryResolution: hooksDirectory.source,
|
|
75
50
|
trackedNodeModulesCount,
|
|
76
|
-
policyValidation,
|
|
77
|
-
experimentalFeatures,
|
|
78
|
-
governanceNextAction,
|
|
79
|
-
governanceObservation,
|
|
51
|
+
policyValidation: readLifecyclePolicyValidationSnapshot(repoRoot),
|
|
52
|
+
experimentalFeatures: readLifecycleExperimentalFeaturesSnapshot(),
|
|
80
53
|
};
|
|
81
54
|
};
|