pumuki 6.3.73 → 6.3.76

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.
Files changed (44) hide show
  1. package/VERSION +1 -1
  2. package/docs/README.md +9 -7
  3. package/docs/operations/RELEASE_NOTES.md +0 -18
  4. package/docs/validation/README.md +3 -1
  5. package/integrations/evidence/buildEvidence.ts +14 -0
  6. package/integrations/evidence/repoState.ts +3 -0
  7. package/integrations/evidence/schema.ts +18 -0
  8. package/integrations/evidence/trackingContract.ts +146 -0
  9. package/integrations/evidence/writeEvidence.ts +14 -0
  10. package/integrations/gate/evaluateAiGate.ts +166 -3
  11. package/integrations/gate/governanceActionCatalog.ts +45 -0
  12. package/integrations/gate/remediationCatalog.ts +8 -0
  13. package/integrations/git/GitService.ts +0 -25
  14. package/integrations/git/aiGateRepoPolicyFindings.ts +4 -0
  15. package/integrations/git/runPlatformGateFacts.ts +0 -1
  16. package/integrations/lifecycle/adapter.templates.json +0 -3
  17. package/integrations/lifecycle/adapter.ts +24 -0
  18. package/integrations/lifecycle/bootstrapManifest.ts +248 -0
  19. package/integrations/lifecycle/cli.ts +30 -68
  20. package/integrations/lifecycle/cliSdd.ts +4 -3
  21. package/integrations/lifecycle/doctor.ts +7 -22
  22. package/integrations/lifecycle/governanceObservationSnapshot.ts +29 -2
  23. package/integrations/lifecycle/index.ts +0 -2
  24. package/integrations/lifecycle/install.ts +21 -0
  25. package/integrations/lifecycle/state.ts +8 -1
  26. package/integrations/mcp/aiGateCheck.ts +140 -10
  27. package/integrations/mcp/alignedPlatformGate.ts +232 -0
  28. package/integrations/mcp/autoExecuteAiStart.ts +6 -1
  29. package/integrations/mcp/enterpriseServer.ts +23 -7
  30. package/integrations/mcp/enterpriseStdioServer.cli.ts +32 -5
  31. package/integrations/mcp/preFlightCheck.ts +10 -0
  32. package/integrations/mcp/readMcpPrePushStdin.ts +7 -0
  33. package/integrations/platform/detectPlatforms.ts +0 -37
  34. package/integrations/policy/experimentalFeatures.ts +1 -1
  35. package/package.json +1 -10
  36. package/scripts/consumer-postinstall.cjs +1 -10
  37. package/AGENTS.md +0 -269
  38. package/CHANGELOG.md +0 -686
  39. package/docs/tracking/plan-curso-pumuki-stack-my-architecture.md +0 -62
  40. package/integrations/lifecycle/audit.ts +0 -101
  41. package/scripts/consumer-postinstall-resolve-args.cjs +0 -38
  42. package/scripts/pumuki-full-surface-smoke-lib.ts +0 -37
  43. package/scripts/pumuki-full-surface-smoke.ts +0 -261
  44. package/scripts/pumuki-smoke-installed-wrapper.cjs +0 -31
@@ -1,62 +0,0 @@
1
- # Seguimiento — Curso Pumuki (Stack My Architecture)
2
-
3
- Iniciativa formativa: quinto curso del ecosistema Stack My Architecture. **No** sustituye el espejo operativo de producto en [`plan-activo-de-trabajo.md`](./plan-activo-de-trabajo.md).
4
-
5
- ## Leyenda
6
-
7
- - ✅ Cerrado
8
- - 🚧 En construccion (maximo 1)
9
- - ⏳ Pendiente
10
- - ⛔ Bloqueado
11
-
12
- ## Regla de uso
13
-
14
- - Solo puede existir **una** tarea `🚧` a la vez en este documento.
15
- - Actualizar estados al cerrar fases o al desbloquear dependencias.
16
- - Repos objetivo: `stack-my-architecture-pumuki` (curso), `stack-my-architecture-hub` (publicación), enlaces en `stack-my-architecture-governance` y `stack-my-architecture-SDD`.
17
- - Producto de referencia: repo `ast-intelligence-hooks` (paquete npm `pumuki`). Documentación canónica: `README.md`, `docs/product/USAGE.md`, `docs/product/INSTALLATION.md`.
18
-
19
- ## Contexto
20
-
21
- - **Objetivo:** curso dedicado a Pumuki como capa de enforcement (`Facts → Rules → Gate → evidencia v2.1`) en repos reales, sin re-enseñar OpenSpec ni ADR (remite a cursos SDD y Governance).
22
- - **`courseId` propuesto:** `stack-my-architecture-pumuki` (alinear con hub y progress sync).
23
- - **Versión mínima `pumuki` (MVP):** `>= 6.3.71` (ajustar al publicar el curso si hay release posterior).
24
- - **Lab de práctica:** variante o fork de **GovernanceKit** (Governance) o **HelpdeskSDD** (SDD); documentado en `00-preparacion/02-lab-governancekit-helpdesksdd.md` del repo del curso.
25
-
26
- ## Estado actual
27
-
28
- - **Cierre local y release gate:** `build-hub.sh --mode strict` + smoke runtime en verde.
29
- - **Origen en GitHub:** el repo **`stack-my-architecture-hub`** tiene en **`main`** la carpeta estática **`pumuki/`** y el pipeline `build-hub` que la genera (sincronizado con remoto; despliegue a producción con el workflow cuando toque).
30
- - **Repo Pumuki (ramas):** la documentación del curso en este repositorio está integrada en **`develop`** (**PR #746**, merge `428f10d`). La rama **`main`** del producto va **muy por detrás** de `develop` (del orden de **~600** commits a fecha de integración del plan); promover `develop` → `main` es un **release de producto** aparte, no parte del cierre de este plan formativo.
31
- - **GitHub Actions:** la org **no** dispone de cuota útil para Actions → el **Hub Production Release Gate** en CI **no** se usa como gate; publicación y comprobaciones **en local** con `./scripts/publish-architecture-stack.sh` en el repo del hub (Vercel CLI + mismos scripts).
32
- - **Publicación Vercel:** deploy del proyecto `stack-my-architecture-hub` con verificación de rutas contra el alias **`https://stack-my-architecture-hub.vercel.app`** (incluye `/pumuki/` → 200). El script `publish-architecture-stack.sh` infiere la base desde `Aliased:` (o `SMA_PUBLISH_VERIFY_BASE_URL`) y escribe **`.runtime/publish-verify-base.url`** para alinear `post-deploy-checks.sh` con la URL verificada (localmente o cuando Actions vuelva a estar disponible).
33
- - Si **`https://architecture-stack.vercel.app`** debe exponer el mismo árbol estático que el hub, sincroniza proyecto/CNAME según tu setup (nota emitida por el script al publicar).
34
-
35
- ## Prioridad ordenada (fases y tareas)
36
-
37
- | Fase | Task | Estado | Notas |
38
- |------|------|--------|--------|
39
- | 0 | Crear repo `stack-my-architecture-pumuki` + README + estructura carpetas | ✅ | Repo bajo `stack-my-architecture/stack-my-architecture-pumuki` |
40
- | 0 | Definir `courseId` y versión mínima `pumuki` | ✅ | `stack-my-architecture-pumuki`, `>= 6.3.71` |
41
- | 0 | Elegir proyecto lab (GovernanceKit vs HelpdeskSDD) | ✅ | Documentado en preparación del curso |
42
- | 1 | Redactar roadmap semanal/módulos (`01-roadmap/`) | ✅ | |
43
- | 1 | Módulos 1–7 MVP (lecciones + enlaces USAGE/README Pumuki) | ✅ | `02-modulos/` + referencias npm/docs producto |
44
- | 2 | Integración hub: ruta `/pumuki` + `build-hub.sh` + launcher | ✅ | `stack-hub --course pumuki`, assets desde ios parcheados |
45
- | 2 | Assistant `courseId` + smoke | ✅ | `_hub-platform.js`, `hub-app.js`, README hub + ejemplo curl |
46
- | 3 | Enlaces desde Governance y SDD | ✅ | Semana 8 checkpoint + SDD semana 8 |
47
- | 3 | (Opcional) Anexo iOS/Android Arquitecto/Maestría | ✅ | `00-core-mobile/04-calidad-pr-ready.md` iOS y Android |
48
- | 4 | Scripts validación curso + primera publicación | ✅ | Curso: validate + check-links; hub: publish strict + `/pumuki/` 200 en alias hub |
49
-
50
- ## Criterios de cierre por fase
51
-
52
- - **Fase 0:** repo clonable, README con prerequisitos y convenciones, scripts mínimos ejecutables.
53
- - **Fase 1:** siete módulos enlazados a docs oficiales Pumuki; roadmap con orden de lectura.
54
- - **Fase 2:** `build-hub` genera ruta servible; `stack-hub --course pumuki` documentado.
55
- - **Fase 3:** enlaces mergeados en repos Governance y SDD (y opcional iOS/Android).
56
- - **Fase 4:** checklist publicación + versión `pumuki` fijada en lecciones y CHANGELOG del curso.
57
-
58
- ## Auditoría y release (Fase 4)
59
-
60
- - Ejecutar en el repo del curso: `scripts/validate-course-structure.py`, `scripts/check-links.py`.
61
- - Publicación hub: modo `strict` en `Hub Production Release Gate`; verificación de rutas incluye `/pumuki/` en `publish-architecture-stack.sh`.
62
- - Tras cada release npm relevante de Pumuki, revisar versión mínima en `00-preparacion/` y en este plan.
@@ -1,101 +0,0 @@
1
- import { readEvidence } from '../evidence/readEvidence';
2
- import { GitService } from '../git/GitService';
3
- import { hasAllowedExtension } from '../git/gitDiffUtils';
4
- import { runPlatformGate } from '../git/runPlatformGate';
5
- import { evaluatePlatformGateFindings } from '../git/runPlatformGateEvaluation';
6
- import { DEFAULT_FACT_FILE_EXTENSIONS } from '../git/runPlatformGateFacts';
7
- import { resolvePolicyForStage } from '../gate/stagePolicies';
8
-
9
- export type LifecycleAuditStage = 'PRE_COMMIT' | 'PRE_PUSH' | 'CI';
10
-
11
- export type LifecycleAuditResult = {
12
- command: 'pumuki audit';
13
- repo_root: string;
14
- stage: LifecycleAuditStage;
15
- scope: { kind: 'repo' };
16
- audit_mode: 'gate' | 'engine';
17
- gate_exit_code: number;
18
- files_scanned: number | null;
19
- untracked_matching_extensions_count: number;
20
- snapshot_outcome: string | null;
21
- policy_reconcile_hint: string;
22
- };
23
-
24
- const POLICY_RECONCILE_HINT =
25
- 'If .pumuki/policy-as-code.json signatures drift after a pumuki upgrade, run: pumuki policy reconcile --apply';
26
-
27
- const countUntrackedMatchingExtensions = (
28
- git: GitService,
29
- extensions: ReadonlyArray<string>
30
- ): number => {
31
- const repoRoot = git.resolveRepoRoot();
32
- const raw = git.runGit(['ls-files', '--others', '--exclude-standard'], repoRoot);
33
- return raw
34
- .split('\n')
35
- .map((line) => line.trim())
36
- .filter((line) => line.length > 0)
37
- .filter((path) => hasAllowedExtension(path, extensions)).length;
38
- };
39
-
40
- export const runLifecycleAudit = async (params: {
41
- stage: LifecycleAuditStage;
42
- auditMode: 'gate' | 'engine';
43
- }): Promise<LifecycleAuditResult> => {
44
- const git = new GitService();
45
- const repoRoot = git.resolveRepoRoot();
46
- const resolved = resolvePolicyForStage(params.stage, repoRoot);
47
- const extensions = DEFAULT_FACT_FILE_EXTENSIONS;
48
- const untrackedMatchingExtensionsCount = countUntrackedMatchingExtensions(git, extensions);
49
-
50
- const gateParams =
51
- params.auditMode === 'engine'
52
- ? {
53
- policy: resolved.policy,
54
- policyTrace: resolved.trace,
55
- scope: { kind: 'repo' as const },
56
- silent: true,
57
- auditMode: 'engine' as const,
58
- dependencies: {
59
- evaluatePlatformGateFindings: (
60
- evalParams: Parameters<typeof evaluatePlatformGateFindings>[0]
61
- ) =>
62
- evaluatePlatformGateFindings(evalParams, {
63
- loadHeuristicsConfig: () => ({
64
- astSemanticEnabled: true,
65
- typeScriptScope: 'all',
66
- }),
67
- }),
68
- printGateFindings: () => {},
69
- },
70
- }
71
- : {
72
- policy: resolved.policy,
73
- policyTrace: resolved.trace,
74
- scope: { kind: 'repo' as const },
75
- silent: true,
76
- auditMode: 'gate' as const,
77
- };
78
-
79
- const gateExitCode = await runPlatformGate(gateParams);
80
- const evidence = readEvidence(repoRoot);
81
- const filesScanned =
82
- typeof evidence?.snapshot.files_scanned === 'number' &&
83
- Number.isFinite(evidence.snapshot.files_scanned)
84
- ? evidence.snapshot.files_scanned
85
- : null;
86
- const snapshotOutcome =
87
- typeof evidence?.snapshot.outcome === 'string' ? evidence.snapshot.outcome : null;
88
-
89
- return {
90
- command: 'pumuki audit',
91
- repo_root: repoRoot,
92
- stage: params.stage,
93
- scope: { kind: 'repo' },
94
- audit_mode: params.auditMode,
95
- gate_exit_code: gateExitCode,
96
- files_scanned: filesScanned,
97
- untracked_matching_extensions_count: untrackedMatchingExtensionsCount,
98
- snapshot_outcome: snapshotOutcome,
99
- policy_reconcile_hint: POLICY_RECONCILE_HINT,
100
- };
101
- };
@@ -1,38 +0,0 @@
1
- 'use strict';
2
-
3
- const KNOWN_MCP_AGENTS = new Set(['cursor', 'codex', 'claude', 'repo']);
4
-
5
- const normalizeExplicitAgent = (raw) => {
6
- const trimmed = (raw ?? '').trim();
7
- if (!trimmed) {
8
- return '';
9
- }
10
- const lower = trimmed.toLowerCase();
11
- if (lower === '0' || lower === 'none' || lower === 'false') {
12
- return '';
13
- }
14
- if (!KNOWN_MCP_AGENTS.has(lower)) {
15
- return '';
16
- }
17
- return lower;
18
- };
19
-
20
- const resolveConsumerPostinstallInstallExtras = (_consumerRoot, env = process.env) => {
21
- if (env.PUMUKI_POSTINSTALL_SKIP_MCP === '1') {
22
- return { extras: [], reason: 'skip_mcp' };
23
- }
24
- const explicit = normalizeExplicitAgent(env.PUMUKI_POSTINSTALL_MCP_AGENT);
25
- if (explicit) {
26
- return { extras: ['--with-mcp', `--agent=${explicit}`], reason: 'explicit_agent' };
27
- }
28
- return {
29
- extras: ['--with-mcp', '--agent=repo'],
30
- reason: 'default_repo_adapter',
31
- };
32
- };
33
-
34
- module.exports = {
35
- KNOWN_MCP_AGENTS,
36
- resolveConsumerPostinstallInstallExtras,
37
- normalizeExplicitAgent,
38
- };
@@ -1,37 +0,0 @@
1
- import { dirname, join } from 'node:path';
2
- import { fileURLToPath } from 'node:url';
3
-
4
- export type SmokeBinStrategy = 'source' | 'installed';
5
-
6
- export const resolveBinStrategy = (raw: string | undefined): SmokeBinStrategy => {
7
- const normalized = (raw ?? 'source').trim().toLowerCase();
8
- if (normalized === 'installed' || normalized === 'consumer') {
9
- return 'installed';
10
- }
11
- return 'source';
12
- };
13
-
14
- export type SmokeLayout = {
15
- pumukiPackageRoot: string;
16
- smokeCwd: string;
17
- binStrategy: SmokeBinStrategy;
18
- binRoot: string;
19
- };
20
-
21
- export const resolveSmokeLayout = (params: {
22
- scriptFileUrl: string;
23
- env: NodeJS.ProcessEnv;
24
- }): SmokeLayout => {
25
- const pumukiPackageRoot = join(dirname(fileURLToPath(params.scriptFileUrl)), '..');
26
- const smokeCwd =
27
- (params.env.PUMUKI_SMOKE_REPO_ROOT ?? pumukiPackageRoot).trim() || pumukiPackageRoot;
28
- const binStrategy = resolveBinStrategy(params.env.PUMUKI_SMOKE_BIN_STRATEGY);
29
- const binRoot =
30
- binStrategy === 'installed'
31
- ? join(smokeCwd, 'node_modules', 'pumuki')
32
- : pumukiPackageRoot;
33
- return { pumukiPackageRoot, smokeCwd, binStrategy, binRoot };
34
- };
35
-
36
- export const installedBinMarkerPath = (layout: SmokeLayout): string =>
37
- join(layout.binRoot, 'bin', 'pumuki.js');
@@ -1,261 +0,0 @@
1
- import { spawnSync } from 'node:child_process';
2
- import { existsSync } from 'node:fs';
3
- import { join } from 'node:path';
4
- import {
5
- installedBinMarkerPath,
6
- resolveSmokeLayout,
7
- } from './pumuki-full-surface-smoke-lib';
8
-
9
- type SmokeRow = {
10
- id: string;
11
- bin: string;
12
- args: ReadonlyArray<string>;
13
- timeoutMs: number;
14
- allowNonZero?: boolean;
15
- };
16
-
17
- const layout = resolveSmokeLayout({
18
- scriptFileUrl: import.meta.url,
19
- env: process.env,
20
- });
21
-
22
- const { pumukiPackageRoot, smokeCwd, binStrategy, binRoot } = layout;
23
- const node = process.execPath;
24
- const binPath = (name: string): string => join(binRoot, 'bin', name);
25
-
26
- const run = (row: SmokeRow): { code: number | null; signal: NodeJS.Signals | null; ms: number } => {
27
- const started = Date.now();
28
- const r = spawnSync(node, [binPath(row.bin), ...row.args], {
29
- cwd: smokeCwd,
30
- encoding: 'utf8',
31
- timeout: row.timeoutMs,
32
- maxBuffer: 50 * 1024 * 1024,
33
- env: { ...process.env, FORCE_COLOR: '0' },
34
- });
35
- return { code: r.status, signal: r.signal, ms: Date.now() - started };
36
- };
37
-
38
- const rows: ReadonlyArray<SmokeRow> = [
39
- { id: 'cli.help_implicit', bin: 'pumuki.js', args: [], timeoutMs: 15_000, allowNonZero: true },
40
- { id: 'cli.help_explicit', bin: 'pumuki.js', args: ['--help'], timeoutMs: 15_000 },
41
- {
42
- id: 'cli.unknown_subcommand',
43
- bin: 'pumuki.js',
44
- args: ['__no_such_lifecycle_command__'],
45
- timeoutMs: 15_000,
46
- allowNonZero: true,
47
- },
48
- { id: 'doctor.json', bin: 'pumuki.js', args: ['doctor', '--json'], timeoutMs: 60_000 },
49
- {
50
- id: 'doctor.deep_json',
51
- bin: 'pumuki.js',
52
- args: ['doctor', '--deep', '--json'],
53
- timeoutMs: 180_000,
54
- allowNonZero: true,
55
- },
56
- { id: 'doctor.parity_json', bin: 'pumuki.js', args: ['doctor', '--parity', '--json'], timeoutMs: 120_000 },
57
- { id: 'status.json', bin: 'pumuki.js', args: ['status', '--json'], timeoutMs: 90_000 },
58
- {
59
- id: 'audit.default_json',
60
- bin: 'pumuki.js',
61
- args: ['audit', '--json'],
62
- timeoutMs: 300_000,
63
- allowNonZero: true,
64
- },
65
- {
66
- id: 'audit.pre_push_json',
67
- bin: 'pumuki.js',
68
- args: ['audit', '--stage=PRE_PUSH', '--json'],
69
- timeoutMs: 300_000,
70
- allowNonZero: true,
71
- },
72
- {
73
- id: 'audit.ci_engine_json',
74
- bin: 'pumuki.js',
75
- args: ['audit', '--stage=CI', '--engine', '--json'],
76
- timeoutMs: 300_000,
77
- allowNonZero: true,
78
- },
79
- {
80
- id: 'watch.once_json',
81
- bin: 'pumuki.js',
82
- args: [
83
- 'watch',
84
- '--once',
85
- '--json',
86
- '--stage=PRE_COMMIT',
87
- '--scope=repo',
88
- '--interval-ms=200',
89
- '--no-notify',
90
- ],
91
- timeoutMs: 180_000,
92
- },
93
- {
94
- id: 'watch.once_staged_json',
95
- bin: 'pumuki.js',
96
- args: [
97
- 'watch',
98
- '--once',
99
- '--json',
100
- '--stage=PRE_COMMIT',
101
- '--scope=staged',
102
- '--interval-ms=200',
103
- '--no-notify',
104
- ],
105
- timeoutMs: 180_000,
106
- allowNonZero: true,
107
- },
108
- { id: 'loop.list_json', bin: 'pumuki.js', args: ['loop', 'list', '--json'], timeoutMs: 30_000 },
109
- {
110
- id: 'loop.run_smoke',
111
- bin: 'pumuki.js',
112
- args: ['loop', 'run', '--objective=smoke-surface', '--max-attempts=1', '--json'],
113
- timeoutMs: 300_000,
114
- allowNonZero: true,
115
- },
116
- {
117
- id: 'adapter.install_dry_repo_json',
118
- bin: 'pumuki.js',
119
- args: ['adapter', 'install', '--agent=repo', '--dry-run', '--json'],
120
- timeoutMs: 30_000,
121
- },
122
- {
123
- id: 'adapter.install_dry_codex_json',
124
- bin: 'pumuki.js',
125
- args: ['adapter', 'install', '--agent=codex', '--dry-run', '--json'],
126
- timeoutMs: 30_000,
127
- },
128
- {
129
- id: 'analytics.hotspots_report',
130
- bin: 'pumuki.js',
131
- args: ['analytics', 'hotspots', 'report', '--top=3', '--since-days=30', '--json'],
132
- timeoutMs: 120_000,
133
- allowNonZero: true,
134
- },
135
- {
136
- id: 'analytics.hotspots_diagnose',
137
- bin: 'pumuki.js',
138
- args: ['analytics', 'hotspots', 'diagnose', '--json'],
139
- timeoutMs: 120_000,
140
- allowNonZero: true,
141
- },
142
- {
143
- id: 'policy.reconcile_json',
144
- bin: 'pumuki.js',
145
- args: ['policy', 'reconcile', '--json'],
146
- timeoutMs: 120_000,
147
- allowNonZero: true,
148
- },
149
- {
150
- id: 'policy.reconcile_strict_json',
151
- bin: 'pumuki.js',
152
- args: ['policy', 'reconcile', '--strict', '--json'],
153
- timeoutMs: 120_000,
154
- allowNonZero: true,
155
- },
156
- { id: 'sdd.status_json', bin: 'pumuki.js', args: ['sdd', 'status', '--json'], timeoutMs: 60_000 },
157
- {
158
- id: 'sdd.validate_pre_write_json',
159
- bin: 'pumuki.js',
160
- args: ['sdd', 'validate', '--stage=PRE_WRITE', '--json'],
161
- timeoutMs: 120_000,
162
- allowNonZero: true,
163
- },
164
- {
165
- id: 'sdd.validate_precommit_json',
166
- bin: 'pumuki.js',
167
- args: ['sdd', 'validate', '--stage=PRE_COMMIT', '--json'],
168
- timeoutMs: 120_000,
169
- allowNonZero: true,
170
- },
171
- {
172
- id: 'sdd.validate_prepush_json',
173
- bin: 'pumuki.js',
174
- args: ['sdd', 'validate', '--stage=PRE_PUSH', '--json'],
175
- timeoutMs: 120_000,
176
- allowNonZero: true,
177
- },
178
- {
179
- id: 'sdd.validate_ci_json',
180
- bin: 'pumuki.js',
181
- args: ['sdd', 'validate', '--stage=CI', '--json'],
182
- timeoutMs: 120_000,
183
- allowNonZero: true,
184
- },
185
- { id: 'hook.pre_write', bin: 'pumuki-pre-write.js', args: [], timeoutMs: 300_000, allowNonZero: true },
186
- { id: 'hook.pre_commit', bin: 'pumuki-pre-commit.js', args: [], timeoutMs: 300_000, allowNonZero: true },
187
- { id: 'hook.pre_push', bin: 'pumuki-pre-push.js', args: [], timeoutMs: 300_000, allowNonZero: true },
188
- { id: 'hook.ci', bin: 'pumuki-ci.js', args: [], timeoutMs: 300_000, allowNonZero: true },
189
- ];
190
-
191
- const main = (): number => {
192
- if (binStrategy === 'installed') {
193
- const marker = installedBinMarkerPath(layout);
194
- if (!existsSync(marker)) {
195
- process.stderr.write(
196
- `[pumuki] smoke: PUMUKI_SMOKE_BIN_STRATEGY=installed pero no existe ${marker}. ` +
197
- 'Ejecuta npm install en el consumidor o revisa PUMUKI_SMOKE_REPO_ROOT.\n'
198
- );
199
- return 2;
200
- }
201
- }
202
-
203
- const lines: string[] = [];
204
- lines.push('# Pumuki superficie — smoke');
205
- lines.push('');
206
- lines.push(`- binStrategy: \`${binStrategy}\` (PUMUKI_SMOKE_BIN_STRATEGY=source|installed)`);
207
- lines.push(`- binRoot: \`${binRoot}\``);
208
- lines.push(`- pumukiPackageRoot (script host): \`${pumukiPackageRoot}\``);
209
- lines.push(`- smokeCwd (gate/doctor cwd): \`${smokeCwd}\``);
210
- lines.push(`- node: \`${node}\``);
211
- lines.push(`- filas: **${rows.length}** (incluye CLI, doctor/status, audit×3, watch×2, loop, adapter×2, analytics×2, policy×2, sdd×5, hooks×4)`);
212
- lines.push('');
213
- lines.push('| id | exit | ms | ok |');
214
- lines.push('|----|------|----|----|');
215
- let failed = 0;
216
- for (const row of rows) {
217
- const { code, signal, ms } = run(row);
218
- const exit = signal ? `signal:${signal}` : String(code ?? 'null');
219
- const ok =
220
- signal !== null
221
- ? false
222
- : row.allowNonZero
223
- ? true
224
- : code === 0;
225
- if (!ok) {
226
- failed += 1;
227
- }
228
- lines.push(`| ${row.id} | ${exit} | ${ms} | ${ok ? 'yes' : 'no'} |`);
229
- }
230
- lines.push('');
231
- lines.push('## Interpretación de exit codes');
232
- lines.push('');
233
- lines.push('- `audit` devuelve el **mismo código que el gate** del alcance auditado (p. ej. 1 si hay `BLOCK` en el repo). Eso es correcto, no indica CLI roto.');
234
- lines.push('- `doctor --deep` puede devolver **1** si `doctorHasBlockingIssues` (issues `error` o `deep.blocking`) o mismatch de parity esperado.');
235
- lines.push('- Los **hooks** (`pre-commit`, etc.) reflejan el gate sobre el estado actual del worktree; 1 con cambios locales o rama protegida es esperable.');
236
- lines.push('- `sdd validate` y `policy reconcile --strict` pueden devolver **1** según política del repo; el smoke los marca con `allowNonZero`.');
237
- lines.push('');
238
- lines.push('## No cubierto aquí (manual o destructivo)');
239
- lines.push('');
240
- lines.push('- `pumuki install|uninstall|remove|update|bootstrap` (mutan hooks/artefactos).');
241
- lines.push('- `pumuki framework` / menú interactivo (`pumuki-framework.js`).');
242
- lines.push('- Servidores MCP stdio (`pumuki-mcp-*-stdio.js`): esperan JSON-RPC por stdin; probar con cliente MCP.');
243
- lines.push('- `pumuki sdd session|sync|learn|evidence|state-sync|auto-sync`: requieren ids / evidencia / cambios reales.');
244
- lines.push('- `pumuki doctor|status --remote-checks`: dependen de red / GitHub.');
245
- lines.push('');
246
- lines.push('## Otro repo (consumidor)');
247
- lines.push('');
248
- lines.push(
249
- '- **Bins del tree pumuki (desarrollo):** `PUMUKI_SMOKE_REPO_ROOT=/ruta/al/repo npm run -s smoke:pumuki-surface` desde la raíz de **pumuki** (`PUMUKI_SMOKE_BIN_STRATEGY=source` por defecto).'
250
- );
251
- lines.push(
252
- '- **Bins instalados en el consumidor (`node_modules/pumuki`):** `PUMUKI_SMOKE_REPO_ROOT=/ruta/al/repo PUMUKI_SMOKE_BIN_STRATEGY=installed npm run -s smoke:pumuki-surface` o `npm run -s smoke:pumuki-surface-installed` (exige `PUMUKI_SMOKE_REPO_ROOT`).'
253
- );
254
- lines.push('');
255
- lines.push(`**Resumen:** ${failed} filas con fallo estricto (sin allowNonZero).`);
256
- process.stdout.write(lines.join('\n'));
257
- process.stdout.write('\n');
258
- return failed > 0 ? 1 : 0;
259
- };
260
-
261
- process.exitCode = main();
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env node
2
- 'use strict';
3
-
4
- const { spawnSync } = require('node:child_process');
5
- const { join } = require('node:path');
6
-
7
- if (!process.env.PUMUKI_SMOKE_REPO_ROOT || !String(process.env.PUMUKI_SMOKE_REPO_ROOT).trim()) {
8
- console.error(
9
- '[pumuki] smoke:pumuki-surface-installed requires PUMUKI_SMOKE_REPO_ROOT (absolute path to consumer repo).'
10
- );
11
- process.exit(1);
12
- }
13
-
14
- const root = join(__dirname, '..');
15
- const env = {
16
- ...process.env,
17
- PUMUKI_SMOKE_BIN_STRATEGY: 'installed',
18
- };
19
-
20
- const result = spawnSync(
21
- 'npx',
22
- ['--yes', 'tsx@4.21.0', 'scripts/pumuki-full-surface-smoke.ts'],
23
- {
24
- cwd: root,
25
- env,
26
- stdio: 'inherit',
27
- }
28
- );
29
-
30
- const code = typeof result.status === 'number' ? result.status : 1;
31
- process.exitCode = code;