pumuki 6.3.63 → 6.3.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/VERSION +1 -1
- package/docs/operations/RELEASE_NOTES.md +10 -0
- package/docs/product/USAGE.md +1 -0
- package/integrations/lifecycle/hookBlock.ts +43 -8
- package/package.json +1 -1
- package/scripts/framework-menu-system-notifications-dispatch.ts +28 -2
- package/scripts/framework-menu-system-notifications-env.ts +7 -0
- package/scripts/framework-menu-system-notifications-gate.ts +1 -12
- package/scripts/framework-menu-system-notifications-lib.ts +1 -1
- package/scripts/framework-menu-system-notifications-payload-types.ts +1 -1
- package/scripts/framework-menu-system-notifications-stdio-fallback.ts +23 -0
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ npx --yes pumuki status
|
|
|
37
37
|
npx --yes pumuki doctor --json
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
Desde **6.3.63**, `npm install` en la raíz de un repo **Git** dispara un `postinstall` que ejecuta `pumuki install` automáticamente (hooks `pre-commit` / `pre-push`). No configura MCP del IDE por sí solo: usa `pumuki install --with-mcp` o el adaptador. Desactivar el postinstall: `PUMUKI_SKIP_POSTINSTALL=1`. En CI suele saltarse solo (`CI=true`).
|
|
40
|
+
Desde **6.3.63**, `npm install` en la raíz de un repo **Git** dispara un `postinstall` que ejecuta `pumuki install` automáticamente (hooks `pre-commit` / `pre-push`). No configura MCP del IDE por sí solo: usa `pumuki install --with-mcp` o el adaptador. Desactivar el postinstall: `PUMUKI_SKIP_POSTINSTALL=1`. En CI suele saltarse solo (`CI=true`). En **6.3.64+**, las notificaciones del sistema en plataformas sin banner nativo se reflejan en **stderr** por defecto (`PUMUKI_DISABLE_STDERR_NOTIFICATIONS=1` para silenciarlas).
|
|
41
41
|
|
|
42
42
|
Fallback (equivalent in pasos separados):
|
|
43
43
|
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
v6.3.
|
|
1
|
+
v6.3.64
|
|
@@ -6,6 +6,16 @@ This file keeps only the operational highlights and rollout notes that matter wh
|
|
|
6
6
|
|
|
7
7
|
## 2026-04 (CLI stability and macOS notifications)
|
|
8
8
|
|
|
9
|
+
### 2026-04-06 (v6.3.65)
|
|
10
|
+
|
|
11
|
+
- **pre-commit.com + `exec`**: Pumuki ya no inserta su bloque gestionado *después* del `exec` del hook generado por pre-commit (código inalcanzable). Tras actualizar a **6.3.65**, ejecutar `pumuki install` en el consumer para reordenar `.git/hooks/pre-commit`.
|
|
12
|
+
- Rollout: repin a `pumuki@6.3.65`; validar que un `git commit` dispara el gate (salida `[pumuki]` / policy) antes de que corra pre-commit.
|
|
13
|
+
|
|
14
|
+
### 2026-04-05 (v6.3.64)
|
|
15
|
+
|
|
16
|
+
- **Notificaciones multiplataforma**: fuera de macOS, avisos críticos van a **stderr** por defecto (terminal visible). `PUMUKI_DISABLE_STDERR_NOTIFICATIONS=1` lo silencia para CI/scripts. En macOS, `PUMUKI_NOTIFICATION_STDERR_MIRROR=1` añade copia en terminal; si `osascript`/banner falla, stderr actúa como respaldo.
|
|
17
|
+
- Rollout: repin a `pumuki@6.3.64`; validar hooks y, si usáis notificaciones, comprobar salida en entorno no-macOS.
|
|
18
|
+
|
|
9
19
|
### 2026-04-05 (v6.3.63)
|
|
10
20
|
|
|
11
21
|
- **Auto-wire on install**: el paquete npm ejecuta `postinstall` → `pumuki install` en el repo del consumidor (`INIT_CWD`) cuando hay `.git`. Saltar con `PUMUKI_SKIP_POSTINSTALL=1` o en CI. OpenSpec bootstrap sigue omitido en ese camino (`PUMUKI_SKIP_OPENSPEC_BOOTSTRAP` por defecto ahí). MCP en Cursor/Codex no se configura solo: usar `pumuki install --with-mcp` / adaptador cuando proceda.
|
package/docs/product/USAGE.md
CHANGED
|
@@ -242,6 +242,7 @@ Stage mapping:
|
|
|
242
242
|
If a scope is empty, the menu prints an explicit operational hint (`Scope vacío`), so `PASS` with zero findings is distinguishable from a clean repository scan.
|
|
243
243
|
|
|
244
244
|
System notifications (macOS) can be enabled from advanced menu option `31` (persisted in `.pumuki/system-notifications.json`).
|
|
245
|
+
On non-macOS platforms, the same payloads are written to **stderr** by default (visible in the terminal) because there is no native banner API. Set `PUMUKI_DISABLE_STDERR_NOTIFICATIONS=1` to silence that path (delivery reports `unsupported-platform` on those OSes). On macOS, set `PUMUKI_NOTIFICATION_STDERR_MIRROR=1` to duplicate the banner text to stderr in addition to the system notification.
|
|
245
246
|
Blocked notifications now use a native Swift floating modal (bottom-right) by default, with AppleScript fallback.
|
|
246
247
|
Override mode with `PUMUKI_MACOS_BLOCKED_DIALOG_MODE=auto|swift-floating|applescript`.
|
|
247
248
|
Custom skills import is available in advanced menu option `33` (writes `/.pumuki/custom-rules.json`).
|
|
@@ -82,23 +82,58 @@ const ensureExecutableHeader = (contents: string): string => {
|
|
|
82
82
|
return contents;
|
|
83
83
|
};
|
|
84
84
|
|
|
85
|
+
const isPreCommitFrameworkWithExecTerminator = (contents: string): boolean => {
|
|
86
|
+
if (!contents.includes('pre-commit.com') && !contents.includes('pre_commit')) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
return /\bexec\b/.test(contents);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const prependManagedBlockAfterShebang = (params: {
|
|
93
|
+
contents: string;
|
|
94
|
+
block: string;
|
|
95
|
+
}): string => {
|
|
96
|
+
const trimmed = trimTrailingWhitespace(params.contents);
|
|
97
|
+
const firstNewline = trimmed.indexOf('\n');
|
|
98
|
+
const shebangLine = firstNewline === -1 ? trimmed : trimmed.slice(0, firstNewline);
|
|
99
|
+
const rest =
|
|
100
|
+
firstNewline === -1 ? '' : trimmed.slice(firstNewline + 1).replace(/^\n+/, '');
|
|
101
|
+
const header = shebangLine.startsWith('#!') ? shebangLine : '#!/usr/bin/env sh';
|
|
102
|
+
const bodyAfterShebang = shebangLine.startsWith('#!') ? rest : trimmed;
|
|
103
|
+
const tail = trimTrailingWhitespace(bodyAfterShebang);
|
|
104
|
+
return tail.length > 0
|
|
105
|
+
? `${header}\n${params.block}\n\n${tail}\n`
|
|
106
|
+
: `${header}\n${params.block}\n`;
|
|
107
|
+
};
|
|
108
|
+
|
|
85
109
|
export const upsertPumukiManagedBlock = (params: {
|
|
86
110
|
contents: string;
|
|
87
111
|
hook: PumukiManagedHook;
|
|
88
112
|
}): string => {
|
|
89
113
|
const block = buildPumukiManagedHookBlock(params.hook);
|
|
90
114
|
const baseline = ensureExecutableHeader(params.contents);
|
|
115
|
+
const hadManagedBlock = hasPumukiManagedBlock(baseline);
|
|
116
|
+
const core = collapseBlankLines(
|
|
117
|
+
trimTrailingWhitespace(
|
|
118
|
+
hadManagedBlock ? baseline.replace(managedBlockPattern, '\n') : baseline
|
|
119
|
+
)
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
if (
|
|
123
|
+
params.hook === 'pre-commit' &&
|
|
124
|
+
core.length > 0 &&
|
|
125
|
+
isPreCommitFrameworkWithExecTerminator(core)
|
|
126
|
+
) {
|
|
127
|
+
const merged = prependManagedBlockAfterShebang({ contents: core, block });
|
|
128
|
+
return `${trimTrailingWhitespace(merged)}\n`;
|
|
129
|
+
}
|
|
91
130
|
|
|
92
|
-
if (
|
|
93
|
-
const
|
|
94
|
-
return `${trimTrailingWhitespace(
|
|
131
|
+
if (hadManagedBlock || core.length > 0) {
|
|
132
|
+
const withSeparator = core.length > 0 ? `${core}\n\n${block}` : block;
|
|
133
|
+
return `${trimTrailingWhitespace(withSeparator)}\n`;
|
|
95
134
|
}
|
|
96
135
|
|
|
97
|
-
|
|
98
|
-
trimTrailingWhitespace(baseline).length > 0
|
|
99
|
-
? `${trimTrailingWhitespace(baseline)}\n\n${block}`
|
|
100
|
-
: block;
|
|
101
|
-
return `${withSeparator}\n`;
|
|
136
|
+
return `${block}\n`;
|
|
102
137
|
};
|
|
103
138
|
|
|
104
139
|
export const removePumukiManagedBlock = (contents: string): {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pumuki",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.65",
|
|
4
4
|
"description": "Enterprise-grade AST Intelligence System with multi-platform support (iOS, Android, Backend, Frontend) and Feature-First + DDD + Clean Architecture enforcement. Includes dynamic violations API for intelligent querying.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,18 +8,33 @@ import type {
|
|
|
8
8
|
} from './framework-menu-system-notifications-types';
|
|
9
9
|
import { applyDialogChoice } from './framework-menu-system-notifications-config';
|
|
10
10
|
import { deliverMacOsNotification } from './framework-menu-system-notifications-macos';
|
|
11
|
+
import { isTruthyEnvValue } from './framework-menu-system-notifications-env';
|
|
12
|
+
import {
|
|
13
|
+
deliverStderrNotificationBanner,
|
|
14
|
+
isStderrNotificationFallbackDisabled,
|
|
15
|
+
} from './framework-menu-system-notifications-stdio-fallback';
|
|
11
16
|
|
|
12
17
|
export const dispatchSystemNotification = (params: {
|
|
13
18
|
event: PumukiCriticalNotificationEvent;
|
|
14
19
|
payload: SystemNotificationPayload;
|
|
20
|
+
platform: NodeJS.Platform;
|
|
15
21
|
repoRoot?: string;
|
|
16
22
|
config: SystemNotificationsConfig;
|
|
17
23
|
env: NodeJS.ProcessEnv;
|
|
18
24
|
nowMs: number;
|
|
19
25
|
runCommand?: SystemNotificationCommandRunner;
|
|
20
26
|
runCommandWithOutput?: SystemNotificationCommandRunnerWithOutput;
|
|
21
|
-
}): SystemNotificationEmitResult =>
|
|
22
|
-
|
|
27
|
+
}): SystemNotificationEmitResult => {
|
|
28
|
+
const stderrOff = isStderrNotificationFallbackDisabled(params.env);
|
|
29
|
+
|
|
30
|
+
if (params.platform !== 'darwin') {
|
|
31
|
+
if (stderrOff) {
|
|
32
|
+
return { delivered: false, reason: 'unsupported-platform' };
|
|
33
|
+
}
|
|
34
|
+
return deliverStderrNotificationBanner({ payload: params.payload });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const macResult = deliverMacOsNotification({
|
|
23
38
|
event: params.event,
|
|
24
39
|
payload: params.payload,
|
|
25
40
|
repoRoot: params.repoRoot,
|
|
@@ -30,3 +45,14 @@ export const dispatchSystemNotification = (params: {
|
|
|
30
45
|
runCommandWithOutput: params.runCommandWithOutput,
|
|
31
46
|
applyDialogChoice,
|
|
32
47
|
});
|
|
48
|
+
|
|
49
|
+
if (macResult.delivered && isTruthyEnvValue(params.env.PUMUKI_NOTIFICATION_STDERR_MIRROR)) {
|
|
50
|
+
deliverStderrNotificationBanner({ payload: params.payload });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!macResult.delivered && macResult.reason === 'command-failed' && !stderrOff) {
|
|
54
|
+
return deliverStderrNotificationBanner({ payload: params.payload });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return macResult;
|
|
58
|
+
};
|
|
@@ -3,19 +3,11 @@ import type {
|
|
|
3
3
|
SystemNotificationEmitResult,
|
|
4
4
|
SystemNotificationsConfig,
|
|
5
5
|
} from './framework-menu-system-notifications-types';
|
|
6
|
-
|
|
7
|
-
const isTruthyEnvValue = (value?: string): boolean => {
|
|
8
|
-
if (!value) {
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
const normalized = value.trim().toLowerCase();
|
|
12
|
-
return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
|
|
13
|
-
};
|
|
6
|
+
import { isTruthyEnvValue } from './framework-menu-system-notifications-env';
|
|
14
7
|
|
|
15
8
|
export const resolveSystemNotificationGate = (params: {
|
|
16
9
|
config: SystemNotificationsConfig;
|
|
17
10
|
nowMs: number;
|
|
18
|
-
platform: NodeJS.Platform;
|
|
19
11
|
env?: NodeJS.ProcessEnv;
|
|
20
12
|
}): SystemNotificationEmitResult | null => {
|
|
21
13
|
if (isTruthyEnvValue(params.env?.PUMUKI_DISABLE_SYSTEM_NOTIFICATIONS)) {
|
|
@@ -27,8 +19,5 @@ export const resolveSystemNotificationGate = (params: {
|
|
|
27
19
|
if (isMutedAt(params.config, params.nowMs)) {
|
|
28
20
|
return { delivered: false, reason: 'muted' };
|
|
29
21
|
}
|
|
30
|
-
if (params.platform !== 'darwin') {
|
|
31
|
-
return { delivered: false, reason: 'unsupported-platform' };
|
|
32
|
-
}
|
|
33
22
|
return null;
|
|
34
23
|
};
|
|
@@ -48,7 +48,6 @@ export const emitSystemNotification = (params: {
|
|
|
48
48
|
const gateResult = resolveSystemNotificationGate({
|
|
49
49
|
config,
|
|
50
50
|
nowMs,
|
|
51
|
-
platform,
|
|
52
51
|
env: params.env ?? process.env,
|
|
53
52
|
});
|
|
54
53
|
if (gateResult) {
|
|
@@ -62,6 +61,7 @@ export const emitSystemNotification = (params: {
|
|
|
62
61
|
return dispatchSystemNotification({
|
|
63
62
|
event: params.event,
|
|
64
63
|
payload,
|
|
64
|
+
platform,
|
|
65
65
|
repoRoot: params.repoRoot,
|
|
66
66
|
config,
|
|
67
67
|
env: params.env ?? process.env,
|
|
@@ -6,5 +6,5 @@ export type SystemNotificationPayload = {
|
|
|
6
6
|
};
|
|
7
7
|
|
|
8
8
|
export type SystemNotificationEmitResult =
|
|
9
|
-
| { delivered: true; reason: 'delivered' }
|
|
9
|
+
| { delivered: true; reason: 'delivered' | 'stderr-fallback' }
|
|
10
10
|
| { delivered: false; reason: 'disabled' | 'muted' | 'unsupported-platform' | 'command-failed' };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SystemNotificationEmitResult,
|
|
3
|
+
SystemNotificationPayload,
|
|
4
|
+
} from './framework-menu-system-notifications-types';
|
|
5
|
+
import { isTruthyEnvValue } from './framework-menu-system-notifications-env';
|
|
6
|
+
|
|
7
|
+
export const isStderrNotificationFallbackDisabled = (env: NodeJS.ProcessEnv): boolean =>
|
|
8
|
+
isTruthyEnvValue(env.PUMUKI_DISABLE_STDERR_NOTIFICATIONS);
|
|
9
|
+
|
|
10
|
+
export const deliverStderrNotificationBanner = (params: {
|
|
11
|
+
payload: SystemNotificationPayload;
|
|
12
|
+
stderr?: NodeJS.WriteStream;
|
|
13
|
+
}): SystemNotificationEmitResult => {
|
|
14
|
+
const stderr = params.stderr ?? process.stderr;
|
|
15
|
+
const lines = [
|
|
16
|
+
'[pumuki]',
|
|
17
|
+
`title: ${params.payload.title}`,
|
|
18
|
+
...(params.payload.subtitle ? [`subtitle: ${params.payload.subtitle}`] : []),
|
|
19
|
+
`message: ${params.payload.message}`,
|
|
20
|
+
];
|
|
21
|
+
stderr.write(`${lines.join('\n')}\n`);
|
|
22
|
+
return { delivered: true, reason: 'stderr-fallback' };
|
|
23
|
+
};
|