Skip to content
Closed
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions crates/cli/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ fn get_manifest_dir() -> PathBuf {
// templates list at templates/templates-list.json
// * `get_ai_rules_base` - returns base AI rules for all languages
// * `get_ai_rules_typescript` - returns TypeScript-specific AI rules
// * `get_ai_rules_rust` - returns Rust-specific AI rules
// * `get_ai_rules_csharp` - returns C#-specific AI rules
fn generate_template_files() {
let manifest_dir = get_manifest_dir();
let repo_root = get_repo_root();
Expand Down Expand Up @@ -140,6 +142,34 @@ fn generate_template_files() {
panic!("Could not find \"docs/static/ai-rules/spacetimedb-typescript.mdc\" file.");
}

// Rust-specific rules
let rust_rules_path = ai_rules_dir.join("spacetimedb-rust.mdc");
if rust_rules_path.exists() {
generated_code.push_str("pub fn get_ai_rules_rust() -> &'static str {\n");
generated_code.push_str(&format!(
" include_str!(\"{}\")\n",
rust_rules_path.to_str().unwrap().replace('\\', "\\\\")
));
generated_code.push_str("}\n\n");
println!("cargo:rerun-if-changed={}", rust_rules_path.display());
} else {
panic!("Could not find \"docs/static/ai-rules/spacetimedb-rust.mdc\" file.");
}

// C#-specific rules
let csharp_rules_path = ai_rules_dir.join("spacetimedb-csharp.mdc");
if csharp_rules_path.exists() {
generated_code.push_str("pub fn get_ai_rules_csharp() -> &'static str {\n");
generated_code.push_str(&format!(
" include_str!(\"{}\")\n",
csharp_rules_path.to_str().unwrap().replace('\\', "\\\\")
));
generated_code.push_str("}\n\n");
println!("cargo:rerun-if-changed={}", csharp_rules_path.display());
} else {
panic!("Could not find \"docs/static/ai-rules/spacetimedb-csharp.mdc\" file.");
}

// Expose workspace metadata so `spacetime init` can rewrite template manifests without hardcoding versions.
generated_code.push_str("pub fn get_workspace_edition() -> &'static str {\n");
generated_code.push_str(&format!(" \"{}\"\n", workspace_edition.escape_default()));
Expand Down
43 changes: 35 additions & 8 deletions crates/cli/src/subcommands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1667,15 +1667,22 @@ fn set_dependency_version(item: &mut Item, version: &str, remove_path: bool) {
/// Writes rules to:
/// - .cursor/rules/ (Cursor)
/// - CLAUDE.md (Claude Code)
/// - AGENTS.md (Opencode)
/// - .windsurfrules (Windsurf)
/// - .github/copilot-instructions.md (VS Code Copilot)
fn install_ai_rules(config: &TemplateConfig, project_path: &Path) -> anyhow::Result<()> {
let base_rules = embedded::get_ai_rules_base();
let ts_rules = embedded::get_ai_rules_typescript();
let rust_rules = embedded::get_ai_rules_rust();
let csharp_rules = embedded::get_ai_rules_csharp();

// Check if TypeScript is used in either server or client
// Check which languages are used in server or client
let uses_typescript = config.server_lang == Some(ServerLanguage::TypeScript)
|| config.client_lang == Some(ClientLanguage::TypeScript);
let uses_rust =
config.server_lang == Some(ServerLanguage::Rust) || config.client_lang == Some(ClientLanguage::Rust);
let uses_csharp =
config.server_lang == Some(ServerLanguage::Csharp) || config.client_lang == Some(ClientLanguage::Csharp);

// 1. Cursor: .cursor/rules/ directory with separate files
let cursor_dir = project_path.join(".cursor/rules");
Expand All @@ -1684,24 +1691,44 @@ fn install_ai_rules(config: &TemplateConfig, project_path: &Path) -> anyhow::Res
if uses_typescript {
fs::write(cursor_dir.join("spacetimedb-typescript.mdc"), ts_rules)?;
}
if uses_rust {
fs::write(cursor_dir.join("spacetimedb-rust.mdc"), rust_rules)?;
}
if uses_csharp {
fs::write(cursor_dir.join("spacetimedb-csharp.mdc"), csharp_rules)?;
}

// Build combined content for single-file AI assistants
// Strip the YAML frontmatter from the .mdc files for non-Cursor tools
let base_content = strip_mdc_frontmatter(base_rules);
let combined_content = if uses_typescript {
let mut combined_content = base_content.to_string();

if uses_typescript {
let ts_content = strip_mdc_frontmatter(ts_rules);
format!("{}\n\n{}", base_content, ts_content)
} else {
base_content.to_string()
};
combined_content.push_str("\n\n");
combined_content.push_str(ts_content);
}
if uses_rust {
let rust_content = strip_mdc_frontmatter(rust_rules);
combined_content.push_str("\n\n");
combined_content.push_str(rust_content);
}
if uses_csharp {
let csharp_content = strip_mdc_frontmatter(csharp_rules);
combined_content.push_str("\n\n");
combined_content.push_str(csharp_content);
}

// 2. Claude Code: CLAUDE.md
fs::write(project_path.join("CLAUDE.md"), &combined_content)?;

// 3. Windsurf: .windsurfrules
// 3. Opencode: AGENTS.md
fs::write(project_path.join("AGENTS.md"), &combined_content)?;

// 4. Windsurf: .windsurfrules
fs::write(project_path.join(".windsurfrules"), &combined_content)?;

// 4. VS Code Copilot: .github/copilot-instructions.md
// 5. VS Code Copilot: .github/copilot-instructions.md
let github_dir = project_path.join(".github");
fs::create_dir_all(&github_dir)?;
fs::write(github_dir.join("copilot-instructions.md"), &combined_content)?;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const players = table(
<TabItem value="csharp" label="C#">

```csharp
[SpacetimeDB.Table(Name = "players", Public = true)]
[SpacetimeDB.Table(Name = "Player", Public = true)]
public partial struct Player
{
[SpacetimeDB.PrimaryKey]
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/00100-intro/00200-quickstarts/00100-react.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Get a SpacetimeDB React app running in under 5 minutes.
</StepText>
<StepCode>
```bash
spacetime dev --template basic-react my-spacetime-app
spacetime dev --template basic-react
```
</StepCode>
</Step>
Expand Down
6 changes: 3 additions & 3 deletions docs/docs/00100-intro/00200-quickstarts/00600-c-sharp.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ my-spacetime-app/

<Step title="Understand tables and reducers">
<StepText>
Open `spacetimedb/Lib.cs` to see the module code. The template includes a `person` table and two reducers: `add` to insert a person, and `say_hello` to greet everyone.
Open `spacetimedb/Lib.cs` to see the module code. The template includes a `Person` table and two reducers: `Add` to insert a person, and `SayHello` to greet everyone.

Tables store your data. Reducers are functions that modify data — they're the only way to write to the database.
</StepText>
Expand All @@ -78,7 +78,7 @@ using SpacetimeDB;

public static partial class Module
{
[SpacetimeDB.Table(Name = "person", Public = true)]
[SpacetimeDB.Table(Name = "Person", Public = true)]
public partial struct Person
{
public string Name;
Expand Down Expand Up @@ -114,7 +114,7 @@ public static partial class Module
spacetime call my-spacetime-app Add Alice

# Query the person table
spacetime sql my-spacetime-app "SELECT * FROM person"
spacetime sql my-spacetime-app "SELECT * FROM Person"
name
---------
"Alice"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ using SpacetimeDB;

public static partial class Module
{
[SpacetimeDB.Table(Name = "scheduled_task", Scheduled = nameof(SendReminder))]
[SpacetimeDB.Table(Name = "ScheduledTask", Scheduled = nameof(SendReminder))]
public partial struct ScheduledTask
{
[SpacetimeDB.PrimaryKey]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ public static void Init(ReducerContext ctx)
Log.Info("Database initializing...");

// Set up default data
if (ctx.Db.settings.Count == 0)
if (ctx.Db.Settings.Count == 0)
{
ctx.Db.settings.Insert(new Settings
ctx.Db.Settings.Insert(new Settings
{
Key = "welcome_message",
Value = "Hello, SpacetimeDB!"
Expand Down Expand Up @@ -116,7 +116,7 @@ public static void OnConnect(ReducerContext ctx)
var connId = ctx.ConnectionId!.Value;

// Initialize client session
ctx.Db.sessions.Insert(new Session
ctx.Db.Session.Insert(new Session
{
ConnectionId = connId,
Identity = ctx.Sender,
Expand Down Expand Up @@ -188,7 +188,7 @@ public static void OnDisconnect(ReducerContext ctx)
var connId = ctx.ConnectionId!.Value;

// Clean up client session
ctx.Db.sessions.ConnectionId.Delete(connId);
ctx.Db.Session.ConnectionId.Delete(connId);
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Throw an exception:
[SpacetimeDB.Reducer]
public static void TransferCredits(ReducerContext ctx, ulong toUser, uint amount)
{
var fromUser = ctx.Db.users.Id.Find(ctx.Sender);
var fromUser = ctx.Db.User.Id.Find(ctx.Sender);
if (fromUser == null)
{
throw new InvalidOperationException("User not found");
Expand Down
Loading