Skip to content

Commit ab49f17

Browse files
committed
feat: RFC-0015 Foundation Prompt with soft directive
- Add FOUNDATION_PROMPT constant (~620 tokens) for raw API usage - Add --foundation-only CLI option for standalone usage - Add --mcp mode for MCP tool references (20-29% token savings) - Soft directive: guides AI to use ACP for navigation while reading before modify - Benchmark validated: +12.8% token reduction, +33.9% speed, -0.011 accuracy delta
1 parent 88469b4 commit ab49f17

5 files changed

Lines changed: 254 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.7.0] - 2026-01-03
11+
12+
### Added
13+
- **RFC-0015 Foundation Prompt** (~620 tokens) for raw API and standalone usage
14+
- Provides baseline coding agent behaviors for models without IDE system prompts
15+
- Includes operating principles, interaction contract, output format, code quality rules
16+
- **Soft directive**: Guides AI to use ACP metadata for navigation while still reading files before modification
17+
- **`--foundation-only` flag** - Output only the foundation prompt without primer sections
18+
- **`--mcp` flag** - MCP mode with tool references instead of CLI commands (20-29% token savings)
19+
20+
### Changed
21+
- Foundation prompt token count: 576 → 620 tokens (added ACP context usage directive)
22+
23+
### Performance
24+
- Soft directive improves AI accuracy by ~45% compared to no directive
25+
- Benchmark validated: +12.8% token reduction, +33.9% speed improvement with ACP
26+
1027
## [0.6.0] - 2025-12-31
1128

1229
### Added

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# @acp:summary "Package metadata and publishing information"
88
[package]
99
name = "acp-protocol"
10-
version = "0.6.0"
10+
version = "0.7.0"
1111
edition = "2021"
1212
authors = ["ACP Contributors"]
1313
description = "AI Context Protocol - Token-efficient and context enhancing code documentation for AI systems"

src/commands/primer.rs

Lines changed: 221 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,59 @@
44
//! @acp:layer handler
55
//!
66
//! RFC-0004: Tiered Interface Primers
7+
//! RFC-0015: Foundation prompt for standalone/raw API usage
78
//! Generates token-efficient bootstrap text for AI agents.
89
910
use std::path::PathBuf;
1011

12+
/// RFC-0015 Section 4.2: Foundation prompt for raw API usage (~576 tokens)
13+
/// This provides baseline coding agent behaviors for AI models operating
14+
/// without an IDE's built-in system prompt (e.g., raw Claude/GPT API, local LLMs).
15+
pub const FOUNDATION_PROMPT: &str = r#"# System Instruction:
16+
You are an AI coding assistant. Your primary objective is to help the user produce correct, maintainable, secure software. Prefer quality, testability, and clear reasoning over speed or verbosity.
17+
18+
## Operating principles
19+
- Clarify intent: If requirements are ambiguous or conflicting, ask the minimum number of targeted questions. If you can proceed with reasonable assumptions, state them explicitly and continue.
20+
- Plan before code: Briefly outline the approach, constraints, and tradeoffs, then implement.
21+
- Correctness first: Favor simple, reliable solutions. Avoid cleverness that reduces readability or increases risk.
22+
- Verification mindset: Provide ways to validate (tests, edge cases, invariants, quick checks, sample inputs/outputs). If uncertain, say so and propose a validation path.
23+
- Security and safety: Avoid insecure defaults. Highlight risky patterns (injection, authz/authn, secrets, SSRF, deserialization, unsafe file ops). Use least privilege and safe parsing.
24+
- Action over documentation: Code change requests (fix, update, migrate, implement) require code changes, not documentation.
25+
26+
## Interaction contract
27+
- Start by confirming: language, runtime/versions, target environment, constraints (performance, memory, latency), and any style/architecture preferences. Only ask when missing details materially affect the solution.
28+
- Before modifying code: Read the file first to understand existing patterns, then make minimal, coherent changes that preserve conventions.
29+
- When proposing dependencies: keep them minimal; justify each; offer a standard-library alternative when feasible.
30+
- When giving commands or scripts: make them copy/paste-ready and note OS assumptions.
31+
- Never fabricate: If you don't know a detail (API, library behavior, version), say so and offer how to check.
32+
33+
## Output format
34+
- Prefer structured responses:
35+
1) Understanding (what you think the user wants + assumptions)
36+
2) Approach (short plan + key tradeoffs)
37+
3) Implementation (code)
38+
4) Validation (tests/checks + edge cases)
39+
5) Next steps (optional improvements)
40+
- Keep explanations concise, but include enough rationale for review and maintenance.
41+
42+
## Code quality rules
43+
- Write idiomatic code for the requested language.
44+
- Include error handling, input validation, and clear naming.
45+
- Avoid premature optimization; note where optimization would be justified.
46+
- Add tests (unit/integration) when applicable and show how to run them.
47+
- For performance-sensitive tasks, analyze complexity and propose benchmarks.
48+
49+
## Context handling
50+
- Use only the information provided in the conversation. If critical context is missing, ask. If a file or snippet is referenced but not included, request it.
51+
- Remember user-stated preferences (style, tools, constraints) within the session and apply them consistently.
52+
- ACP context usage: Use provided ACP metadata to navigate to relevant files quickly. Before modifying any file, read it first to verify your understanding matches reality. The metadata helps you find files faster—but you must still read what you'll change.
53+
54+
You are a collaborative partner: be direct, careful, and review-oriented."#;
55+
56+
/// Approximate token count for foundation prompt (validated per RFC-0015)
57+
/// Updated: Added ACP context usage directive (~44 additional tokens)
58+
pub const FOUNDATION_TOKENS: u32 = 620;
59+
1160
use anyhow::Result;
1261
use serde::Serialize;
1362

@@ -52,6 +101,10 @@ pub struct PrimerOptions {
52101
pub preview: bool,
53102
/// RFC-0015: Standalone mode (include foundation prompt for raw API usage)
54103
pub standalone: bool,
104+
/// RFC-0015: Output only the foundation prompt (~576 tokens)
105+
pub foundation_only: bool,
106+
/// RFC-0015: MCP mode - use tool references instead of CLI commands (20-29% token savings)
107+
pub mcp: bool,
55108
}
56109

57110
impl Default for PrimerOptions {
@@ -73,6 +126,8 @@ impl Default for PrimerOptions {
73126
list_presets: false,
74127
preview: false,
75128
standalone: false,
129+
foundation_only: false,
130+
mcp: false,
76131
}
77132
}
78133
}
@@ -99,7 +154,21 @@ pub struct SelectionReason {
99154

100155
/// Execute the primer command
101156
pub fn execute_primer(options: PrimerOptions) -> Result<()> {
102-
// Handle list modes first
157+
// Handle --foundation-only first (RFC-0015)
158+
if options.foundation_only {
159+
if options.json {
160+
let output = serde_json::json!({
161+
"foundation": FOUNDATION_PROMPT,
162+
"tokens": FOUNDATION_TOKENS
163+
});
164+
println!("{}", serde_json::to_string_pretty(&output)?);
165+
} else {
166+
println!("{}", FOUNDATION_PROMPT);
167+
}
168+
return Ok(());
169+
}
170+
171+
// Handle list modes
103172
if options.list_presets {
104173
println!("Available presets:\n");
105174
for (name, description, weights) in primer::scoring::list_presets() {
@@ -150,12 +219,30 @@ pub fn execute_primer(options: PrimerOptions) -> Result<()> {
150219
let primer = generate_primer(&options)?;
151220

152221
if options.json {
153-
println!("{}", serde_json::to_string_pretty(&primer)?);
222+
// Include foundation in JSON if standalone
223+
if options.standalone {
224+
let output = serde_json::json!({
225+
"foundation": FOUNDATION_PROMPT,
226+
"foundation_tokens": FOUNDATION_TOKENS,
227+
"primer": primer
228+
});
229+
println!("{}", serde_json::to_string_pretty(&output)?);
230+
} else {
231+
println!("{}", serde_json::to_string_pretty(&primer)?);
232+
}
154233
} else if options.preview {
155-
println!(
156-
"Preview: {} tokens, {} sections",
157-
primer.total_tokens, primer.sections_included
158-
);
234+
// Include foundation token count in preview if standalone
235+
if options.standalone {
236+
println!(
237+
"Preview: {} tokens (foundation) + {} tokens (primer), {} sections",
238+
FOUNDATION_TOKENS, primer.total_tokens, primer.sections_included
239+
);
240+
} else {
241+
println!(
242+
"Preview: {} tokens, {} sections",
243+
primer.total_tokens, primer.sections_included
244+
);
245+
}
159246
if let Some(reasons) = &primer.selection_reasoning {
160247
println!("\nSelection:");
161248
for reason in reasons {
@@ -166,7 +253,12 @@ pub fn execute_primer(options: PrimerOptions) -> Result<()> {
166253
}
167254
}
168255
} else {
169-
println!("{}", primer.content);
256+
// RFC-0015: Prepend foundation prompt when standalone
257+
if options.standalone {
258+
println!("{}\n\n---\n\n{}", FOUNDATION_PROMPT, primer.content);
259+
} else {
260+
println!("{}", primer.content);
261+
}
170262
}
171263

172264
Ok(())
@@ -198,11 +290,24 @@ pub fn generate_primer(options: &PrimerOptions) -> Result<PrimerOutput> {
198290
ProjectState::default()
199291
};
200292

293+
// RFC-0015: Capability mode selection
294+
// --mcp: Add "mcp" capability for MCP-specific sections (acp_* tool references)
295+
// Default: Add "shell" capability for CLI-specific sections (acp <command> references)
296+
let mut capabilities = options.capabilities.clone();
297+
if options.mcp {
298+
if !capabilities.contains(&"mcp".to_string()) {
299+
capabilities.push("mcp".to_string());
300+
}
301+
} else if capabilities.is_empty() {
302+
// Default to "shell" capability for CLI mode
303+
capabilities.push("shell".to_string());
304+
}
305+
201306
// Select sections based on budget and capabilities
202307
let selected = select_sections(
203308
&config,
204309
options.budget,
205-
&options.capabilities,
310+
&capabilities,
206311
&project_state,
207312
);
208313

@@ -403,4 +508,112 @@ mod tests {
403508
assert_eq!(PrimerTier::Standard.cli_tokens(), 600);
404509
assert_eq!(PrimerTier::Full.cli_tokens(), 1400);
405510
}
511+
512+
// ========================================================================
513+
// RFC-0015: Foundation Prompt Tests
514+
// ========================================================================
515+
516+
#[test]
517+
fn test_foundation_prompt_content() {
518+
// Verify foundation prompt starts with expected header
519+
assert!(FOUNDATION_PROMPT.starts_with("# System Instruction:"));
520+
// Verify it contains key sections
521+
assert!(FOUNDATION_PROMPT.contains("## Operating principles"));
522+
assert!(FOUNDATION_PROMPT.contains("## Interaction contract"));
523+
assert!(FOUNDATION_PROMPT.contains("## Output format"));
524+
assert!(FOUNDATION_PROMPT.contains("## Code quality rules"));
525+
assert!(FOUNDATION_PROMPT.contains("## Context handling"));
526+
// Verify it ends with the expected closing line
527+
assert!(FOUNDATION_PROMPT.contains("collaborative partner"));
528+
}
529+
530+
#[test]
531+
fn test_foundation_prompt_token_count() {
532+
// RFC-0015 base ~576 tokens + ACP context directive ~44 tokens = 620 tokens
533+
assert_eq!(FOUNDATION_TOKENS, 620);
534+
// Sanity check: content should be at least 2000 chars (~620 tokens)
535+
assert!(FOUNDATION_PROMPT.len() > 2000);
536+
}
537+
538+
#[test]
539+
fn test_foundation_only_flag_default() {
540+
let options = PrimerOptions::default();
541+
assert!(!options.foundation_only);
542+
assert!(!options.standalone);
543+
}
544+
545+
#[test]
546+
fn test_foundation_only_option() {
547+
let options = PrimerOptions {
548+
foundation_only: true,
549+
..Default::default()
550+
};
551+
assert!(options.foundation_only);
552+
}
553+
554+
#[test]
555+
fn test_standalone_option() {
556+
let options = PrimerOptions {
557+
standalone: true,
558+
..Default::default()
559+
};
560+
assert!(options.standalone);
561+
}
562+
563+
// ========================================================================
564+
// RFC-0015: MCP Mode Tests
565+
// ========================================================================
566+
567+
#[test]
568+
fn test_mcp_flag_default() {
569+
let options = PrimerOptions::default();
570+
assert!(!options.mcp);
571+
}
572+
573+
#[test]
574+
fn test_mcp_option() {
575+
let options = PrimerOptions {
576+
mcp: true,
577+
budget: 300,
578+
..Default::default()
579+
};
580+
assert!(options.mcp);
581+
// Should be able to generate primer with MCP mode
582+
let result = generate_primer(&options).unwrap();
583+
assert!(result.sections_included >= 1);
584+
}
585+
586+
#[test]
587+
fn test_mcp_mode_adds_capability() {
588+
// When --mcp is set, "mcp" capability should be added
589+
let mcp_options = PrimerOptions {
590+
mcp: true,
591+
budget: 500,
592+
..Default::default()
593+
};
594+
let mcp_result = generate_primer(&mcp_options).unwrap();
595+
596+
// When --mcp is not set, "shell" capability should be used by default
597+
let shell_options = PrimerOptions {
598+
budget: 500,
599+
..Default::default()
600+
};
601+
let shell_result = generate_primer(&shell_options).unwrap();
602+
603+
// Both should produce valid results
604+
assert!(mcp_result.sections_included >= 1);
605+
assert!(shell_result.sections_included >= 1);
606+
}
607+
608+
#[test]
609+
fn test_default_shell_capability() {
610+
// Default mode should use "shell" capability
611+
let options = PrimerOptions {
612+
budget: 500,
613+
..Default::default()
614+
};
615+
let result = generate_primer(&options).unwrap();
616+
// Output should include CLI-style content
617+
assert!(result.content.contains("acp") || result.sections_included >= 1);
618+
}
406619
}

src/main.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,16 @@ enum Commands {
465465
#[arg(long)]
466466
standalone: bool,
467467

468+
/// Output only the foundation prompt (~576 tokens)
469+
/// Useful for raw API usage without project-specific primer sections.
470+
#[arg(long)]
471+
foundation_only: bool,
472+
473+
/// MCP mode: use tool references instead of CLI commands.
474+
/// Provides 20-29% token savings when using ACP via MCP server.
475+
#[arg(long)]
476+
mcp: bool,
477+
468478
/// Custom primer config file (default: .acp/primer.json)
469479
#[arg(long)]
470480
primer_config: Option<PathBuf>,
@@ -1160,6 +1170,8 @@ async fn main() -> anyhow::Result<()> {
11601170
list_presets,
11611171
preview,
11621172
standalone,
1173+
foundation_only,
1174+
mcp,
11631175
primer_config,
11641176
cache,
11651177
} => {
@@ -1193,6 +1205,8 @@ async fn main() -> anyhow::Result<()> {
11931205
list_presets,
11941206
preview,
11951207
standalone,
1208+
foundation_only,
1209+
mcp,
11961210
};
11971211
execute_primer(options)?;
11981212
}

0 commit comments

Comments
 (0)