Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.72.0 - 2026-06-01

- Add `copilot-missing-applyto` to context health audits so `.github/instructions/**/*.instructions.md` files without `applyTo` frontmatter are no longer treated as fully scoped Copilot guidance.
- Keep scoped Copilot instruction files quiet when they include YAML frontmatter such as `applyTo: "src/**/*.ts"`.
- Refresh README, Copilot customization docs, LLM discovery files, and research notes around path-scoped instruction applicability.

## 0.71.0 - 2026-06-01

- Add `actions-missing-node24-opt-in` to `contextforge actions-audit` so repositories using JavaScript actions can prepare for GitHub's Node 24 hosted-runner default.
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ contextforge pack --task "review auth regression" --budget 20000 --sessions --ou
Or use the GitHub Action before npm publishing is complete:

```yaml
- uses: grnbtqdbyx-create/contextforge@v0.71.0
- uses: grnbtqdbyx-create/contextforge@v0.72.0
with:
min-context-score: 60
min-cache-score: 60
Expand Down Expand Up @@ -498,7 +498,7 @@ contextforge cost-estimate [--demo] [--json] [--summary contextforge-cost-estima
contextforge review-kit [--demo] [--base main] [--output contextforge-review-kit.md]
contextforge artifact-map [--output docs/artifacts.md]
contextforge publish-readiness [--json] [--summary contextforge-publish-readiness.md]
contextforge init [--all] [--github-action] [--pr-comment-workflow] [--agents-md] [--claude-md] [--copilot-instructions] [--project-name "My App"] [--action-ref grnbtqdbyx-create/contextforge@v0.71.0] [--force]
contextforge init [--all] [--github-action] [--pr-comment-workflow] [--agents-md] [--claude-md] [--copilot-instructions] [--project-name "My App"] [--action-ref grnbtqdbyx-create/contextforge@v0.72.0] [--force]
```

Local session scans are bounded by default. Use `--max-session-files` and
Expand Down Expand Up @@ -583,7 +583,7 @@ See [docs/research/adjacent-tools.md](docs/research/adjacent-tools.md).

## Current Status

ContextForge v0.71.0 is a public MVP CLI with:
ContextForge v0.72.0 is a public MVP CLI with:

- Claude Code and Codex JSONL fixture scanners
- bounded local session scanning fallbacks
Expand All @@ -600,6 +600,7 @@ ContextForge v0.71.0 is a public MVP CLI with:
- committed Claude Code settings audits for bypass modes, broad Bash allow rules, remote shell hooks, wildcard HTTP hooks, and missing sensitive-file denies
- Claude Code project subagent and custom slash-command discovery for `.claude/agents/**/*.md` and `.claude/commands/**/*.md`
- GitHub Copilot customization discovery for `.github/copilot-instructions.md`, `.github/instructions/**/*.instructions.md`, `.github/prompts/**/*.prompt.md`, `.github/agents/**/*.md`, `.github/agents/**/*.agent.md`, and project skills under `.github/skills`, `.claude/skills`, and `.agents/skills`
- GitHub Copilot path-scoped instruction checks that flag `.github/instructions/**/*.instructions.md` files missing `applyTo` frontmatter
- GitHub Copilot hook security scanning for `.github/hooks/*.json` and committed `.github/copilot/settings*.json`
- VS Code Copilot workspace settings security scanning for `.vscode/settings.json` and committed `*.code-workspace` files
- adjacent agent rule discovery and security scanning for `.cursor/rules/**/*.mdc`, `.clinerules/**/*.{md,txt}`, `GEMINI.md`, `.windsurfrules`, and `.windsurf/rules/**/*.{md,mdc,txt}` files
Expand Down Expand Up @@ -730,6 +731,7 @@ ContextForge v0.71.0 is a public MVP CLI with:
- **v0.69.0:** GitHub Actions audits catch mutable action refs, pwn-request checkout, missing permissions, and direct script interpolation.
- **v0.70.0:** doctor, proof-pack, and scorecard reports surface Claude settings, agentic workflow, and GitHub Actions hardening evidence in one readiness path.
- **v0.71.0:** GitHub Actions audits catch missing Node 24 JavaScript action runtime opt-ins and document the known runner annotation behavior.
- **v0.72.0:** Context health audits catch path-scoped Copilot instruction files missing `applyTo` frontmatter.
- **Next:** first approved npm publish and external launch outreach.

Release preparation lives in [docs/release-checklist.md](docs/release-checklist.md).
Expand Down
4 changes: 2 additions & 2 deletions contextforge-publish-readiness.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

Status: **warn**

Package: `contextforge@0.71.0`
Package: `contextforge@0.72.0`
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove generated publish-readiness artifact from the commit

The root AGENTS.md says to “Keep generated artifacts out of commits” and explicitly lists contextforge-publish-readiness.md; updating this generated report here violates the repo guidance and forces generated-output churn for version-only changes. Drop this file from the commit and regenerate it locally when needed.

Useful? React with 👍 / 👎.


| Check | Status | Detail |
| --- | --- | --- |
| Package metadata | pass | contextforge@0.71.0 is public-package ready with bin dist/cli.js |
| Package metadata | pass | contextforge@0.72.0 is public-package ready with bin dist/cli.js |
| Package provenance metadata | pass | repository, homepage, and issue tracker point at grnbtqdbyx-create/contextforge for npm provenance readers |
| Trusted publishing workflow | pass | npm Trusted Publishing uses GitHub OIDC, manual dispatch, dry-run default, and environment approval |
| Release artifact attestation | pass | GitHub artifact attestation covers the packed npm tarball before the same tarball is published |
Expand Down
3 changes: 3 additions & 0 deletions docs/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ Why this matters:

- Copilot can automatically add repository-wide and path-specific custom
instructions to requests.
- Path-scoped `.github/instructions/**/*.instructions.md` files need an
`applyTo` frontmatter pattern when maintainers expect automatic application;
ContextForge flags missing scopes as `copilot-missing-applyto`.
- Prompt files, custom agents, and project skills can carry reusable task
guidance that affects how Copilot plans or executes work.
- Copilot hooks can execute shell commands at agent lifecycle points, so they
Expand Down
4 changes: 2 additions & 2 deletions docs/github-action.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ refuses to overwrite existing files by default:

```bash
contextforge init --github-action --force
contextforge init --github-action --action-ref grnbtqdbyx-create/contextforge@v0.71.0
contextforge init --github-action --action-ref grnbtqdbyx-create/contextforge@v0.72.0
```

`contextforge init --pr-comment-workflow` writes a separate
Expand Down Expand Up @@ -71,7 +71,7 @@ jobs:
- uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5
with:
fetch-depth: 0
- uses: grnbtqdbyx-create/contextforge@v0.71.0
- uses: grnbtqdbyx-create/contextforge@v0.72.0
with:
min-context-score: 60
min-cache-score: 60
Expand Down
1 change: 1 addition & 0 deletions docs/release-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- [x] Cost estimate report uses caller-provided pricing instead of hardcoded provider prices.
- [x] Context pack generation shows a budget ledger and measures final output against the requested token budget.
- [x] GitHub Copilot instruction files are discovered, audited, and scaffolded with the same context hygiene loop as AGENTS.md and CLAUDE.md.
- [x] GitHub Copilot path-scoped instruction files are checked for missing `applyTo` frontmatter.
- [x] GitHub Copilot prompt files, custom agents, and project skills are discovered and audited when present.
- [x] GitHub Copilot hook configs are scanned for unsafe shell commands and context-security risk when present.
- [x] VS Code Copilot workspace settings are scanned for risky committed instruction text when present.
Expand Down
7 changes: 7 additions & 0 deletions docs/research/adjacent-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -582,3 +582,10 @@ agent-edited repositories now see noisy runtime annotations. The audit keeps the
fix deterministic: set `FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true` at workflow
level, then treat any remaining "target Node.js 20 but forced to Node.js 24"
message as a runner metadata annotation rather than a failed hardening state.
ContextForge v0.72.0 tightens Copilot instruction applicability checks:
VS Code applies `*.instructions.md` files automatically from the configured
instruction locations through `applyTo` glob patterns, while files without that
scope may require manual attachment or other selection behavior. Missing scopes
are easy to miss in agent-authored repos, so `contextforge agents-md-audit` now
reports `copilot-missing-applyto` before maintainers assume path-scoped guidance
is deterministically active.
3 changes: 3 additions & 0 deletions llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ separate tools:
`.clinerules/**/*.{md,txt}`, `.clinerules`, `GEMINI.md`, `.windsurfrules`,
`.windsurf/rules/**/*.{md,mdc,txt}`, `SKILL.md`, and
root README files
- Copilot path-scoped instruction checks that flag
`.github/instructions/**/*.instructions.md` files without `applyTo`
frontmatter
- context security checks for prompt injection, secret exfiltration, unsafe
shell instructions, hidden directives, and permission escalation
- Claude Code project subagent and custom command checks for committed
Expand Down
2 changes: 1 addition & 1 deletion llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Codex and Claude can act on.
- [Artifact Map](docs/artifacts.md): Generated catalog of ContextForge artifacts and fast paths.
- [npm Publish](docs/npm-publish.md): Trusted Publishing workflow, provenance metadata, and publish-readiness checks.
- [Agent Context Init](docs/agent-context-init.md): Minimal `AGENTS.md`, `CLAUDE.md`, and Copilot instruction scaffolding.
- [GitHub Copilot Customization](docs/copilot-instructions.md): Copilot instruction, prompt file, custom agent, project skill, hook, and VS Code workspace settings security coverage.
- [GitHub Copilot Customization](docs/copilot-instructions.md): Copilot instruction, `applyTo` scope, prompt file, custom agent, project skill, hook, and VS Code workspace settings security coverage.
- [Agent Action Plan](docs/agent-action-plan.md): `contextforge plan` and audit handoff artifacts for Codex/Claude.
- [Security Audit](docs/security-audit.md): Prompt/context poisoning checks for repo instruction files, Claude Code subagents, custom slash commands, Copilot prompts, hooks, and workspace settings.
- [Security Benchmark](docs/security-benchmark.md): Public malicious-context fixtures and expected findings.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "contextforge",
"version": "0.71.0",
"version": "0.72.0",
"description": "Agent context gate for Codex, Claude Code, GitHub Copilot, MCP, Cursor, Cline, Gemini, and Windsurf repos.",
"type": "module",
"packageManager": "pnpm@11.2.2",
Expand Down
21 changes: 21 additions & 0 deletions src/analyzers/contextHealth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@ export async function auditContextFiles(options: { rootDir?: string } = {}): Pro
const lines = content.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
files.push({ path: name, estimatedTokens: estimateTokens(content), bytes: Buffer.byteLength(content) });

if (isCopilotPathScopedInstruction(name) && !hasApplyToFrontmatter(content)) {
findings.push({
file: name,
type: 'copilot-missing-applyto',
severity: 'medium',
message: `${name} is a path-scoped Copilot instruction file without an applyTo frontmatter scope.`,
suggestion: `Add YAML frontmatter with applyTo, for example applyTo: "src/**/*.ts", so Copilot can apply ${name} intentionally.`
});
}

const seen = new Map<string, number>();
for (const line of lines) {
const count = (seen.get(line.toLowerCase()) ?? 0) + 1;
Expand Down Expand Up @@ -69,6 +79,17 @@ export async function auditContextFiles(options: { rootDir?: string } = {}): Pro
return { files, findings: uniqueFindings, score: Math.max(0, 100 - penalty) };
}

function isCopilotPathScopedInstruction(relativePath: string): boolean {
return /^\.github\/instructions\/.+\.instructions\.md$/i.test(relativePath);
}

function hasApplyToFrontmatter(content: string): boolean {
const normalized = content.replace(/^\uFEFF/, '');
const match = normalized.match(/^---\r?\n([\s\S]*?)\r?\n---/);
if (!match) return false;
return match[1].split(/\r?\n/).some((line) => /^applyTo\s*:\s*.+/i.test(line.trim()));
}

function dedupeFindings(findings: Finding[]): Finding[] {
const seen = new Set<string>();
return findings.filter((finding) => {
Expand Down
2 changes: 1 addition & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ Usage:
contextforge surface-inventory [--json] [--output contextforge-agent-surface-inventory.md]
contextforge surface-diff [--base main] [--json] [--output contextforge-agent-surface-diff.md]
contextforge publish-readiness [--json] [--summary contextforge-publish-readiness.md]
contextforge init [--all] [--github-action] [--pr-comment-workflow] [--agents-md] [--claude-md] [--copilot-instructions] [--project-name "My App"] [--action-ref grnbtqdbyx-create/contextforge@v0.71.0] [--force]
contextforge init [--all] [--github-action] [--pr-comment-workflow] [--agents-md] [--claude-md] [--copilot-instructions] [--project-name "My App"] [--action-ref grnbtqdbyx-create/contextforge@v0.72.0] [--force]

Session scan safety:
--max-session-files 50 newest JSONL files to scan per provider
Expand Down
2 changes: 1 addition & 1 deletion src/init/githubAction.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { access, mkdir, writeFile } from 'node:fs/promises';
import path from 'node:path';

export const DEFAULT_GITHUB_ACTION_REF = 'grnbtqdbyx-create/contextforge@v0.71.0';
export const DEFAULT_GITHUB_ACTION_REF = 'grnbtqdbyx-create/contextforge@v0.72.0';

export interface GithubActionScaffoldOptions {
rootDir: string;
Expand Down
2 changes: 1 addition & 1 deletion tests/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe('CLI help command', () => {
it('prints the current default GitHub Action ref in init examples', async () => {
const { stdout } = await execFileAsync('pnpm', ['contextforge', 'help']);

expect(stdout).toContain('--action-ref grnbtqdbyx-create/contextforge@v0.71.0');
expect(stdout).toContain('--action-ref grnbtqdbyx-create/contextforge@v0.72.0');
});
});

Expand Down
23 changes: 23 additions & 0 deletions tests/contextHealth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,29 @@ describe('context health audit', () => {
expect(audit.findings.some((finding) => finding.file === '.github/instructions/frontend.instructions.md' && finding.type === 'vague')).toBe(true);
});

it('flags path-scoped Copilot instructions without applyTo frontmatter', async () => {
const rootDir = await mkdtemp(path.join(os.tmpdir(), 'contextforge-copilot-applyto-'));
await mkdir(path.join(rootDir, '.github/instructions'), { recursive: true });
await writeFile(path.join(rootDir, '.github/instructions/frontend.instructions.md'), 'Use React Query for server state.\n');
await writeFile(
path.join(rootDir, '.github/instructions/api.instructions.md'),
['---', 'applyTo: "src/api/**/*.ts"', '---', 'Validate request bodies with Zod.'].join('\n')
);

const audit = await auditContextFiles({ rootDir });

expect(audit.findings).toContainEqual(
expect.objectContaining({
file: '.github/instructions/frontend.instructions.md',
type: 'copilot-missing-applyto',
severity: 'medium'
})
);
expect(
audit.findings.some((finding) => finding.file === '.github/instructions/api.instructions.md' && finding.type === 'copilot-missing-applyto')
).toBe(false);
});

it('audits Copilot prompts, custom agents, and project skills as repo context', async () => {
const rootDir = await mkdtemp(path.join(os.tmpdir(), 'contextforge-copilot-artifact-health-'));
await mkdir(path.join(rootDir, '.github/prompts'), { recursive: true });
Expand Down
2 changes: 1 addition & 1 deletion tests/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ describe('GitHub Action init scaffold', () => {
const rootDir = await mkdtemp(path.join(os.tmpdir(), 'contextforge-init-default-ref-'));
const result = await scaffoldGithubActionWorkflow({ rootDir });

expect(await readFile(result.path, 'utf8')).toContain('uses: grnbtqdbyx-create/contextforge@v0.71.0');
expect(await readFile(result.path, 'utf8')).toContain('uses: grnbtqdbyx-create/contextforge@v0.72.0');
});

it('is available through the init CLI command', async () => {
Expand Down
Loading