From 437d758b793ab09487b5503ee24459eb1a088179 Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Wed, 1 Jul 2026 16:57:13 -0700 Subject: [PATCH 1/6] chore: add eslint test script to package.json --- packages/intent/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/intent/package.json b/packages/intent/package.json index d8bb60e..1582f65 100644 --- a/packages/intent/package.json +++ b/packages/intent/package.json @@ -43,6 +43,7 @@ "test:smoke": "pnpm run build && node dist/cli.mjs --help > /dev/null && node dist/cli.mjs load --help > /dev/null", "test:lib": "vitest run --exclude 'tests/integration/**'", "test:integration": "vitest run tests/integration/", - "test:types": "tsc --noEmit" + "test:types": "tsc --noEmit", + "test:eslint": "eslint ." } } From fbe8ec48126bdd651b864be3e25a9672d58584ae Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Wed, 1 Jul 2026 17:03:25 -0700 Subject: [PATCH 2/6] chore: update file paths and imports in various modules for consistency --- eslint.config.js | 2 +- packages/intent/src/commands/list.ts | 6 +++--- packages/intent/src/hooks/install.ts | 2 +- packages/intent/src/staleness/check.ts | 2 +- packages/intent/tests/hooks-install.test.ts | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 8907d2f..0ebda54 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -49,7 +49,7 @@ const config = [ { name: 'intent/static-discovery', files: [ - 'packages/intent/src/scanner.ts', + 'packages/intent/src/discovery/scanner.ts', 'packages/intent/src/lockfile.ts', 'packages/intent/src/manifest.ts', 'packages/intent/src/mcp/**/*.ts', diff --git a/packages/intent/src/commands/list.ts b/packages/intent/src/commands/list.ts index 3f32eb7..0fc45c5 100644 --- a/packages/intent/src/commands/list.ts +++ b/packages/intent/src/commands/list.ts @@ -1,3 +1,6 @@ +import { detectIntentAudience } from '../shared/environment.js' +import { formatIntentCommand } from '../shared/command-runner.js' +import { listIntentSkills } from '../core/index.js' import { coreOptionsFromGlobalFlags, noticeOptionsFromGlobalFlags, @@ -5,9 +8,6 @@ import { printNotices, printWarnings, } from './support.js' -import { detectIntentAudience } from '../shared/environment.js' -import { formatIntentCommand } from '../shared/command-runner.js' -import { listIntentSkills } from '../core/index.js' import type { GlobalScanFlags } from './support.js' import type { IntentPackageSummary, diff --git a/packages/intent/src/hooks/install.ts b/packages/intent/src/hooks/install.ts index fad29e0..2e8f6a5 100644 --- a/packages/intent/src/hooks/install.ts +++ b/packages/intent/src/hooks/install.ts @@ -548,7 +548,7 @@ function isIntentHook(value: unknown): boolean { } function isIntentGateScriptReference(value: string): boolean { - return /(?:^|[\s"'\/])(?:old-)?intent-(claude|codex|copilot)-gate\.mjs(?:$|[?#\s"'])/i.test( + return /(?:^|[\s"'/])(?:old-)?intent-(claude|codex|copilot)-gate\.mjs(?:$|[?#\s"'])/i.test( value, ) } diff --git a/packages/intent/src/staleness/check.ts b/packages/intent/src/staleness/check.ts index 9f9bcd7..ec3191b 100644 --- a/packages/intent/src/staleness/check.ts +++ b/packages/intent/src/staleness/check.ts @@ -1,13 +1,13 @@ import { existsSync, readFileSync } from 'node:fs' import { isAbsolute, join, relative, resolve } from 'node:path' import semver from 'semver' -import { readIntentArtifacts } from './artifact-coverage.js' import { findSkillFiles, parseFrontmatter, readScalarField, toPosixPath, } from '../shared/utils.js' +import { readIntentArtifacts } from './artifact-coverage.js' import type { IntentArtifactSet, IntentArtifactSkill, diff --git a/packages/intent/tests/hooks-install.test.ts b/packages/intent/tests/hooks-install.test.ts index 8048aa0..82e8985 100644 --- a/packages/intent/tests/hooks-install.test.ts +++ b/packages/intent/tests/hooks-install.test.ts @@ -379,7 +379,7 @@ describe('hook installer', () => { }) expect(result.status).toBe(0) - const output = JSON.parse(result.stdout) as any + const output = JSON.parse(result.stdout) const context = agent === 'copilot' ? output.additionalContext From 6ac6c185139e56443daf5a5e908852c1345bea51 Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Wed, 1 Jul 2026 17:13:32 -0700 Subject: [PATCH 3/6] chore: refactor async functions to synchronous in command execution and tests --- packages/intent/src/commands/exclude.ts | 4 +- packages/intent/src/commands/load.ts | 4 +- packages/intent/tests/cli.test.ts | 22 ++-- .../source-policy-surfaces.test.ts | 2 +- packages/intent/tests/stale-command.test.ts | 121 +++++++++--------- 5 files changed, 79 insertions(+), 74 deletions(-) diff --git a/packages/intent/src/commands/exclude.ts b/packages/intent/src/commands/exclude.ts index d525409..503d573 100644 --- a/packages/intent/src/commands/exclude.ts +++ b/packages/intent/src/commands/exclude.ts @@ -126,11 +126,11 @@ function printExcludes(excludes: Array, json?: boolean): void { } } -export async function runExcludeCommand( +export function runExcludeCommand( actionArg: string | undefined, patternArg: string | undefined, options: ExcludeCommandOptions, -): Promise { +): void { const action = normalizeAction(actionArg) const cwd = process.cwd() const pkg = readPackageJson(cwd) diff --git a/packages/intent/src/commands/load.ts b/packages/intent/src/commands/load.ts index 9cf5241..a016041 100644 --- a/packages/intent/src/commands/load.ts +++ b/packages/intent/src/commands/load.ts @@ -32,10 +32,10 @@ function printLoadDebug(loaded: LoadedIntentSkill | ResolvedIntentSkill): void { ]) } -export async function runLoadCommand( +export function runLoadCommand( use: string | undefined, options: LoadCommandOptions, -): Promise { +): void { if (!use) { fail('Missing skill use. Expected: intent load #') } diff --git a/packages/intent/tests/cli.test.ts b/packages/intent/tests/cli.test.ts index c81b62b..5033f71 100644 --- a/packages/intent/tests/cli.test.ts +++ b/packages/intent/tests/cli.test.ts @@ -2728,7 +2728,7 @@ describe('cli commands', () => { const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, - json: async () => ({ version: '1.0.0' }), + json: () => Promise.resolve({ version: '1.0.0' }), } as Response) process.chdir(root) @@ -2781,7 +2781,7 @@ describe('cli commands', () => { const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, - json: async () => ({ version: '1.0.0' }), + json: () => Promise.resolve({ version: '1.0.0' }), } as Response) process.chdir(root) @@ -2849,7 +2849,7 @@ describe('cli commands', () => { const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, - json: async () => ({ version: '1.0.0' }), + json: () => Promise.resolve({ version: '1.0.0' }), } as Response) process.chdir(root) @@ -2916,7 +2916,7 @@ describe('cli commands', () => { const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, - json: async () => ({ version: '1.0.0' }), + json: () => Promise.resolve({ version: '1.0.0' }), } as Response) process.chdir(root) @@ -2986,7 +2986,7 @@ describe('cli commands', () => { const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, - json: async () => ({ version: '1.0.0' }), + json: () => Promise.resolve({ version: '1.0.0' }), } as Response) process.chdir(root) @@ -3044,7 +3044,7 @@ describe('cli commands', () => { const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, - json: async () => ({ version: '1.0.0' }), + json: () => Promise.resolve({ version: '1.0.0' }), } as Response) process.chdir(root) @@ -3133,7 +3133,7 @@ describe('cli commands', () => { const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, - json: async () => ({ version: '1.0.0' }), + json: () => Promise.resolve({ version: '1.0.0' }), } as Response) process.env.INTENT_GLOBAL_NODE_MODULES = globalRoot @@ -3177,7 +3177,7 @@ describe('cli commands', () => { const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, - json: async () => ({ version: '1.0.0' }), + json: () => Promise.resolve({ version: '1.0.0' }), } as Response) process.chdir(root) @@ -3223,7 +3223,7 @@ describe('cli commands', () => { const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, - json: async () => ({ version: '1.0.0' }), + json: () => Promise.resolve({ version: '1.0.0' }), } as Response) process.chdir(root) @@ -3267,7 +3267,7 @@ describe('cli commands', () => { const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, - json: async () => ({ version: '1.0.0' }), + json: () => Promise.resolve({ version: '1.0.0' }), } as Response) process.chdir(join(root, 'packages', 'router')) @@ -3300,7 +3300,7 @@ describe('cli commands', () => { const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, - json: async () => ({ version: '1.0.0' }), + json: () => Promise.resolve({ version: '1.0.0' }), } as Response) const elsewhere = mkdtempSync(join(realTmpdir, 'intent-cli-stale-abs-cwd-')) diff --git a/packages/intent/tests/integration/source-policy-surfaces.test.ts b/packages/intent/tests/integration/source-policy-surfaces.test.ts index 1bedd01..6241736 100644 --- a/packages/intent/tests/integration/source-policy-surfaces.test.ts +++ b/packages/intent/tests/integration/source-policy-surfaces.test.ts @@ -136,7 +136,7 @@ describe('source policy — all four surfaces filter excluded and unlisted', () const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ ok: true, - json: async () => ({ version: '1.0.0' }), + json: () => Promise.resolve({ version: '1.0.0' }), } as Response) process.chdir(root) diff --git a/packages/intent/tests/stale-command.test.ts b/packages/intent/tests/stale-command.test.ts index 29653ba..451bd4e 100644 --- a/packages/intent/tests/stale-command.test.ts +++ b/packages/intent/tests/stale-command.test.ts @@ -26,26 +26,28 @@ describe('runStaleCommand', () => { }) it('prints review signals in non-json output', async () => { - await runStaleCommand(undefined, {}, async () => ({ - reports: [ - { - library: '@tanstack/router', - currentVersion: '1.0.0', - skillVersion: '1.0.0', - versionDrift: null, - skills: [], - signals: [ - { - type: 'missing-package-coverage', - library: '@tanstack/router', - packageName: '@tanstack/react-start-rsc', - reasons: ['package is not represented in skills or artifacts'], - needsReview: true, - }, - ], - }, - ], - })) + await runStaleCommand(undefined, {}, () => + Promise.resolve({ + reports: [ + { + library: '@tanstack/router', + currentVersion: '1.0.0', + skillVersion: '1.0.0', + versionDrift: null, + skills: [], + signals: [ + { + type: 'missing-package-coverage', + library: '@tanstack/router', + packageName: '@tanstack/react-start-rsc', + reasons: ['package is not represented in skills or artifacts'], + needsReview: true, + }, + ], + }, + ], + }), + ) const output = logSpy.mock.calls.map((call) => String(call[0])).join('\n') expect(output).toContain('@tanstack/router') @@ -55,12 +57,14 @@ describe('runStaleCommand', () => { }) it('prints workflow update advisories in non-json output', async () => { - await runStaleCommand(undefined, {}, async () => ({ - reports: [], - workflowAdvisories: [ - 'Intent workflow update available: run `npx @tanstack/intent@latest setup`.', - ], - })) + await runStaleCommand(undefined, {}, () => + Promise.resolve({ + reports: [], + workflowAdvisories: [ + 'Intent workflow update available: run `npx @tanstack/intent@latest setup`.', + ], + }), + ) const output = logSpy.mock.calls.map((call) => String(call[0])).join('\n') expect(output).toContain('Intent workflow update available') @@ -69,12 +73,14 @@ describe('runStaleCommand', () => { }) it('does not print workflow update advisories in json output', async () => { - await runStaleCommand(undefined, { json: true }, async () => ({ - reports: [], - workflowAdvisories: [ - 'Intent workflow update available: run `npx @tanstack/intent@latest setup`.', - ], - })) + await runStaleCommand(undefined, { json: true }, () => + Promise.resolve({ + reports: [], + workflowAdvisories: [ + 'Intent workflow update available: run `npx @tanstack/intent@latest setup`.', + ], + }), + ) const output = logSpy.mock.calls.map((call) => String(call[0])).join('\n') expect(output).toBe('[]') @@ -94,29 +100,30 @@ describe('runStaleCommand', () => { await runStaleCommand( undefined, { githubReview: true, packageLabel: '@tanstack/router' }, - async () => ({ - reports: [ - { - library: '@tanstack/router', - currentVersion: null, - skillVersion: null, - versionDrift: null, - skills: [], - signals: [ - { - type: 'missing-package-coverage', - library: '@tanstack/react-start-rsc', - packageName: '@tanstack/react-start-rsc', - reasons: ['workspace package is not represented'], - needsReview: true, - }, - ], - }, - ], - workflowAdvisories: [ - 'Intent workflow update available: run `npx @tanstack/intent@latest setup`.', - ], - }), + () => + Promise.resolve({ + reports: [ + { + library: '@tanstack/router', + currentVersion: null, + skillVersion: null, + versionDrift: null, + skills: [], + signals: [ + { + type: 'missing-package-coverage', + library: '@tanstack/react-start-rsc', + packageName: '@tanstack/react-start-rsc', + reasons: ['workspace package is not represented'], + needsReview: true, + }, + ], + }, + ], + workflowAdvisories: [ + 'Intent workflow update available: run `npx @tanstack/intent@latest setup`.', + ], + }), ) } finally { process.chdir(originalCwd) @@ -163,9 +170,7 @@ describe('runStaleCommand', () => { await runStaleCommand( undefined, { githubReview: true, packageLabel: '@tanstack/router' }, - async () => { - throw new Error('boom') - }, + () => Promise.reject(new Error('boom')), ) } finally { process.chdir(originalCwd) From 3d39bce9bae122cf3401d1a76677a034e384cfda Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Wed, 1 Jul 2026 17:24:41 -0700 Subject: [PATCH 4/6] chore: add warnings for allow-all mode and update tests accordingly --- packages/intent/src/core/source-policy.ts | 18 +++++++---- packages/intent/tests/cli.test.ts | 33 +++++++++++++++++++++ packages/intent/tests/source-policy.test.ts | 5 ++-- 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/packages/intent/src/core/source-policy.ts b/packages/intent/src/core/source-policy.ts index 5cd24ad..03cbbc2 100644 --- a/packages/intent/src/core/source-policy.ts +++ b/packages/intent/src/core/source-policy.ts @@ -122,6 +122,7 @@ export interface SourcePolicyResult { hiddenSources: Array packages: Array notices: Array + warnings: Array } export function applySourcePolicy( @@ -132,6 +133,7 @@ export function applySourcePolicy( const audience = options.audience ?? 'human' const seen = new Set() const notices: Array = [] + const warnings: Array = [] const emit = (notice: string): void => { if (seen.has(notice)) return @@ -176,7 +178,9 @@ export function applySourcePolicy( } if (config.mode === 'absent') emit(MIGRATION_NOTICE) - else if (config.mode === 'allow-all') emit(ALLOW_ALL_NOTICE) + // Pushed to warnings, not emit()/notices: this risk banner must survive + // --no-notices/INTENT_NO_NOTICES since it's a safety signal, not a tip. + else if (config.mode === 'allow-all') warnings.push(ALLOW_ALL_NOTICE) else if (config.mode === 'empty') emit(EMPTY_NOTE) return { @@ -184,6 +188,7 @@ export function applySourcePolicy( hiddenSources, packages, notices, + warnings, } } @@ -246,10 +251,13 @@ export function scanForPolicedIntents(params: { scan: { ...scanResult, packages: policy.packages, - warnings: scanResult.warnings.filter( - (warning) => - !droppedNames.some((name) => warningMentionsPackage(warning, name)), - ), + warnings: [ + ...policy.warnings, + ...scanResult.warnings.filter( + (warning) => + !droppedNames.some((name) => warningMentionsPackage(warning, name)), + ), + ], notices: policy.notices, conflicts: scanResult.conflicts.filter((conflict) => survivingNames.has(conflict.packageName), diff --git a/packages/intent/tests/cli.test.ts b/packages/intent/tests/cli.test.ts index 5033f71..2d895dc 100644 --- a/packages/intent/tests/cli.test.ts +++ b/packages/intent/tests/cli.test.ts @@ -1108,6 +1108,39 @@ describe('cli commands', () => { expect(stderr).not.toContain('Notices:') }) + it('does not suppress the allow-all risk banner under --no-notices', async () => { + const root = mkdtempSync( + join(realTmpdir, 'intent-cli-list-allow-all-no-notices-'), + ) + const isolatedGlobalRoot = mkdtempSync( + join(realTmpdir, 'intent-cli-list-allow-all-no-notices-empty-global-'), + ) + tempDirs.push(root, isolatedGlobalRoot) + writeJson(join(root, 'package.json'), { + name: 'consumer', + intent: { skills: ['*'] }, + }) + writeInstalledIntentPackage(root, { + name: '@tanstack/query', + version: '5.0.0', + skillName: 'fetching', + description: 'Query data fetching patterns', + }) + + process.env.INTENT_GLOBAL_NODE_MODULES = isolatedGlobalRoot + process.chdir(root) + + const exitCode = await main(['list', '--no-notices']) + const stdout = logSpy.mock.calls.flat().join('\n') + const stderr = errorSpy.mock.calls.flat().join('\n') + + expect(exitCode).toBe(0) + expect(stdout).toContain('@tanstack/query') + expect(stdout).toContain('Warnings:') + expect(stdout).toContain('All skill sources allowed') + expect(stderr).not.toContain('Notices:') + }) + it('suppresses notices when INTENT_NO_NOTICES=1 is set', async () => { const root = mkdtempSync( join(realTmpdir, 'intent-cli-list-env-no-notices-'), diff --git a/packages/intent/tests/source-policy.test.ts b/packages/intent/tests/source-policy.test.ts index 9fdef84..5ba8706 100644 --- a/packages/intent/tests/source-policy.test.ts +++ b/packages/intent/tests/source-policy.test.ts @@ -141,13 +141,14 @@ describe('applySourcePolicy — allowlist matrix', () => { }) describe('applySourcePolicy — permit-all and empty modes', () => { - it('permits every discovered source under allow-all with a loud notice', () => { + it('permits every discovered source under allow-all with a non-suppressible warning', () => { const result = applySourcePolicy( { packages: [pkg('@scope/a', ['x']), pkg('@scope/b', ['y'])] }, { config: config(['*']), excludeMatchers: [] }, ) expect(names(result.packages)).toEqual(['@scope/a', '@scope/b']) - expect(result.notices).toEqual([ALLOW_ALL_NOTICE]) + expect(result.warnings).toEqual([ALLOW_ALL_NOTICE]) + expect(result.notices).toEqual([]) }) it('permits every discovered source under absent config with a migration warning', () => { From bcfa02b1a72d33fa3be4011df3972d7fa08cd243 Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Wed, 1 Jul 2026 17:47:59 -0700 Subject: [PATCH 5/6] chore: update ALLOW_ALL_NOTICE handling to ensure visibility in notices and adjust related tests --- .changeset/forty-heads-fly.md | 7 +++++++ packages/intent/src/core/source-policy.ts | 22 +++++++-------------- packages/intent/src/shared/cli-output.ts | 16 +++++++++++++-- packages/intent/tests/cli.test.ts | 5 ++--- packages/intent/tests/source-policy.test.ts | 5 ++--- 5 files changed, 32 insertions(+), 23 deletions(-) create mode 100644 .changeset/forty-heads-fly.md diff --git a/.changeset/forty-heads-fly.md b/.changeset/forty-heads-fly.md new file mode 100644 index 0000000..29edbfe --- /dev/null +++ b/.changeset/forty-heads-fly.md @@ -0,0 +1,7 @@ +--- +'@tanstack/intent': patch +--- + +Fix `intent list` suppressing the allow-all risk warning. + +When `intent.skills` is set to allow-all (`"*"`), `intent list --no-notices` and `INTENT_NO_NOTICES=1` no longer hide the warning that all skill sources are permitted. This banner is a security-relevant signal, not a migration tip, so it now prints unconditionally alongside other warnings instead of being suppressible. diff --git a/packages/intent/src/core/source-policy.ts b/packages/intent/src/core/source-policy.ts index 03cbbc2..2c1bf6a 100644 --- a/packages/intent/src/core/source-policy.ts +++ b/packages/intent/src/core/source-policy.ts @@ -1,5 +1,6 @@ import { scanForIntents } from '../discovery/scanner.js' import { detectIntentAudience } from '../shared/environment.js' +import { ALLOW_ALL_NOTICE } from '../shared/cli-output.js' import { compileExcludePatterns, getConfigDirs, @@ -22,8 +23,7 @@ import type { IntentHiddenSourceSummary, } from './types.js' -export const ALLOW_ALL_NOTICE = - 'All skill sources allowed (intent.skills: ["*"]) — unvetted skills may be surfaced into agent guidance.' +export { ALLOW_ALL_NOTICE } export const MIGRATION_NOTICE = 'intent.skills is not set — all discovered skill sources are surfaced. A future version will require an explicit intent.skills allowlist; add one to opt in to specific sources.' @@ -122,7 +122,6 @@ export interface SourcePolicyResult { hiddenSources: Array packages: Array notices: Array - warnings: Array } export function applySourcePolicy( @@ -133,7 +132,6 @@ export function applySourcePolicy( const audience = options.audience ?? 'human' const seen = new Set() const notices: Array = [] - const warnings: Array = [] const emit = (notice: string): void => { if (seen.has(notice)) return @@ -178,9 +176,7 @@ export function applySourcePolicy( } if (config.mode === 'absent') emit(MIGRATION_NOTICE) - // Pushed to warnings, not emit()/notices: this risk banner must survive - // --no-notices/INTENT_NO_NOTICES since it's a safety signal, not a tip. - else if (config.mode === 'allow-all') warnings.push(ALLOW_ALL_NOTICE) + else if (config.mode === 'allow-all') emit(ALLOW_ALL_NOTICE) else if (config.mode === 'empty') emit(EMPTY_NOTE) return { @@ -188,7 +184,6 @@ export function applySourcePolicy( hiddenSources, packages, notices, - warnings, } } @@ -251,13 +246,10 @@ export function scanForPolicedIntents(params: { scan: { ...scanResult, packages: policy.packages, - warnings: [ - ...policy.warnings, - ...scanResult.warnings.filter( - (warning) => - !droppedNames.some((name) => warningMentionsPackage(warning, name)), - ), - ], + warnings: scanResult.warnings.filter( + (warning) => + !droppedNames.some((name) => warningMentionsPackage(warning, name)), + ), notices: policy.notices, conflicts: scanResult.conflicts.filter((conflict) => survivingNames.has(conflict.packageName), diff --git a/packages/intent/src/shared/cli-output.ts b/packages/intent/src/shared/cli-output.ts index 58c97b2..f281d88 100644 --- a/packages/intent/src/shared/cli-output.ts +++ b/packages/intent/src/shared/cli-output.ts @@ -1,3 +1,8 @@ +// Lives here (not core/source-policy.ts) so printNotices can enforce +// non-suppressibility by identity without core importing this module. +export const ALLOW_ALL_NOTICE = + 'All skill sources allowed (intent.skills: ["*"]) — unvetted skills may be surfaced into agent guidance.' + export function printWarnings(warnings: Array): void { if (warnings.length === 0) return @@ -27,10 +32,17 @@ export function printNotices( options: NoticeOutputOptions = {}, ): void { if (notices.length === 0) return - if (shouldSuppressNotices(options)) return + + // ALLOW_ALL_NOTICE stays visible even when suppressed: agent hooks read + // warnings/conflicts but never notices, so keeping it here (rather than in + // warnings) also keeps it out of agent-injected context automatically. + const visible = shouldSuppressNotices(options) + ? notices.filter((notice) => notice === ALLOW_ALL_NOTICE) + : notices + if (visible.length === 0) return console.error('Notices:') - for (const notice of notices) { + for (const notice of visible) { console.error(` ℹ ${notice}`) } } diff --git a/packages/intent/tests/cli.test.ts b/packages/intent/tests/cli.test.ts index 2d895dc..f940d1b 100644 --- a/packages/intent/tests/cli.test.ts +++ b/packages/intent/tests/cli.test.ts @@ -1136,9 +1136,8 @@ describe('cli commands', () => { expect(exitCode).toBe(0) expect(stdout).toContain('@tanstack/query') - expect(stdout).toContain('Warnings:') - expect(stdout).toContain('All skill sources allowed') - expect(stderr).not.toContain('Notices:') + expect(stderr).toContain('Notices:') + expect(stderr).toContain('All skill sources allowed') }) it('suppresses notices when INTENT_NO_NOTICES=1 is set', async () => { diff --git a/packages/intent/tests/source-policy.test.ts b/packages/intent/tests/source-policy.test.ts index 5ba8706..9fdef84 100644 --- a/packages/intent/tests/source-policy.test.ts +++ b/packages/intent/tests/source-policy.test.ts @@ -141,14 +141,13 @@ describe('applySourcePolicy — allowlist matrix', () => { }) describe('applySourcePolicy — permit-all and empty modes', () => { - it('permits every discovered source under allow-all with a non-suppressible warning', () => { + it('permits every discovered source under allow-all with a loud notice', () => { const result = applySourcePolicy( { packages: [pkg('@scope/a', ['x']), pkg('@scope/b', ['y'])] }, { config: config(['*']), excludeMatchers: [] }, ) expect(names(result.packages)).toEqual(['@scope/a', '@scope/b']) - expect(result.warnings).toEqual([ALLOW_ALL_NOTICE]) - expect(result.notices).toEqual([]) + expect(result.notices).toEqual([ALLOW_ALL_NOTICE]) }) it('permits every discovered source under absent config with a migration warning', () => { From ba25ea914f61d7b13c034f2c995f7dd7a97a9575 Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Wed, 1 Jul 2026 17:49:20 -0700 Subject: [PATCH 6/6] chore: ensure allow-all risk warning is always displayed in intent list --- .changeset/forty-heads-fly.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/forty-heads-fly.md b/.changeset/forty-heads-fly.md index 29edbfe..b8447e5 100644 --- a/.changeset/forty-heads-fly.md +++ b/.changeset/forty-heads-fly.md @@ -4,4 +4,4 @@ Fix `intent list` suppressing the allow-all risk warning. -When `intent.skills` is set to allow-all (`"*"`), `intent list --no-notices` and `INTENT_NO_NOTICES=1` no longer hide the warning that all skill sources are permitted. This banner is a security-relevant signal, not a migration tip, so it now prints unconditionally alongside other warnings instead of being suppressible. +When `intent.skills` is set to allow-all (`"*"`), `intent list --no-notices` and `INTENT_NO_NOTICES=1` no longer hide the warning that all skill sources are permitted. This banner is a security-relevant signal, not a migration tip, so it's now excluded from suppression while other notices remain suppressible as before.