From 9923696a9d6d295b5c94f27032e0ed3cf8f87396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Villase=C3=B1or=20Montfort?= <195970+montfort@users.noreply.github.com> Date: Wed, 18 Mar 2026 23:00:33 -0600 Subject: [PATCH 1/2] feat: split framework/CLI versioning with independent release workflows Decouple framework and CLI release cycles so each component can evolve independently. Framework uses `fw-*` tags, CLI uses `cli-*` tags, with separate GitHub Actions workflows for each. - Reset CLI version to 1.0.0, bump framework manifest to 2.1.0 - Add `get_latest_release_by_prefix()` to filter releases by tag prefix - Add `strip_tag_prefix()` helper for fw-/cli-/v- prefixes - Rename `update` command to `update-framework`, create combined `update` - Add `update-framework` subcommand to CLI router - Show both framework and CLI versions in `status` and `about` - Split release.yml into release-framework.yml and release-cli.yml - Update install scripts to fetch `cli-*` releases Co-Authored-By: Claude Opus 4.6 (1M context) --- .../{release.yml => release-cli.yml} | 55 +-- .github/workflows/release-framework.yml | 39 ++ cli/Cargo.lock | 2 +- cli/Cargo.toml | 2 +- cli/src/commands/about.rs | 15 + cli/src/commands/mod.rs | 1 + cli/src/commands/status.rs | 9 +- cli/src/commands/update.rs | 338 +----------------- cli/src/commands/update_framework.rs | 334 +++++++++++++++++ cli/src/download.rs | 79 ++-- cli/src/main.rs | 5 +- cli/src/self_update.rs | 68 +--- cli/tests/update_test.rs | 15 +- dist/dist-manifest.yml | 2 +- install.ps1 | 14 +- install.sh | 22 +- 16 files changed, 518 insertions(+), 482 deletions(-) rename .github/workflows/{release.yml => release-cli.yml} (70%) create mode 100644 .github/workflows/release-framework.yml create mode 100644 cli/src/commands/update_framework.rs diff --git a/.github/workflows/release.yml b/.github/workflows/release-cli.yml similarity index 70% rename from .github/workflows/release.yml rename to .github/workflows/release-cli.yml index ab1e610..7604ffb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release-cli.yml @@ -1,9 +1,9 @@ -name: Release +name: Release CLI on: push: tags: - - 'v*' + - 'cli-*' permissions: contents: write @@ -12,34 +12,6 @@ env: CARGO_TERM_COLOR: always jobs: - # Job 1: Package distributable templates - package-templates: - name: Package Templates - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Read version from Cargo.toml - id: version - run: | - VERSION=$(grep '^version' cli/Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - - - name: Create distribution ZIP - run: | - mkdir -p dist-staging - cp -a dist/. dist-staging/ - cd dist-staging - zip -r "../devtrail-v${{ steps.version.outputs.version }}.zip" . - cd .. - - - name: Upload template artifact - uses: actions/upload-artifact@v4 - with: - name: templates - path: devtrail-v${{ steps.version.outputs.version }}.zip - - # Job 2: Build CLI binaries (cross-platform) build-cli: name: Build CLI (${{ matrix.target }}) runs-on: ${{ matrix.os }} @@ -71,13 +43,23 @@ jobs: if: matrix.os == 'ubuntu-latest' run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config - - name: Read version + - name: Extract version from tag id: version shell: bash run: | - VERSION=$(grep '^version' cli/Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') + VERSION="${GITHUB_REF_NAME#cli-}" echo "version=$VERSION" >> "$GITHUB_OUTPUT" + - name: Verify Cargo.toml version matches tag + shell: bash + run: | + CARGO_VERSION=$(grep '^version' cli/Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') + TAG_VERSION="${GITHUB_REF_NAME#cli-}" + if [ "$CARGO_VERSION" != "$TAG_VERSION" ]; then + echo "ERROR: Cargo.toml version ($CARGO_VERSION) does not match tag version ($TAG_VERSION)" + exit 1 + fi + - name: Build release binary run: cargo build --release --manifest-path cli/Cargo.toml --target ${{ matrix.target }} @@ -106,18 +88,17 @@ jobs: name: cli-${{ matrix.target }} path: ${{ env.ARCHIVE }} - # Job 3: Create GitHub Release create-release: name: Create Release - needs: [package-templates, build-cli] + needs: [build-cli] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Read version + - name: Extract version from tag id: version run: | - VERSION=$(grep '^version' cli/Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/') + VERSION="${GITHUB_REF_NAME#cli-}" echo "version=$VERSION" >> "$GITHUB_OUTPUT" - name: Download all artifacts @@ -136,6 +117,6 @@ jobs: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | gh release create "${{ github.ref_name }}" \ - --title "DevTrail ${{ github.ref_name }}" \ + --title "DevTrail CLI ${{ steps.version.outputs.version }}" \ --generate-notes \ release/* diff --git a/.github/workflows/release-framework.yml b/.github/workflows/release-framework.yml new file mode 100644 index 0000000..8e81897 --- /dev/null +++ b/.github/workflows/release-framework.yml @@ -0,0 +1,39 @@ +name: Release Framework + +on: + push: + tags: + - 'fw-*' + +permissions: + contents: write + +jobs: + package-framework: + name: Package Framework + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Extract version from tag + id: version + run: | + VERSION="${GITHUB_REF_NAME#fw-}" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Create distribution ZIP + run: | + mkdir -p dist-staging + cp -a dist/. dist-staging/ + cd dist-staging + zip -r "../devtrail-fw-${{ steps.version.outputs.version }}.zip" . + cd .. + + - name: Create GitHub Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create "${{ github.ref_name }}" \ + --title "DevTrail Framework ${{ steps.version.outputs.version }}" \ + --generate-notes \ + "devtrail-fw-${{ steps.version.outputs.version }}.zip" diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 3dd22aa..7f2f487 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -401,7 +401,7 @@ dependencies = [ [[package]] name = "devtrail-cli" -version = "2.1.0" +version = "1.0.0" dependencies = [ "anyhow", "assert_cmd", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index ab89d95..d6f457f 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "devtrail-cli" -version = "2.1.0" +version = "1.0.0" edition = "2021" description = "CLI tool for DevTrail - Documentation Governance for AI-Assisted Development" license = "MIT" diff --git a/cli/src/commands/about.rs b/cli/src/commands/about.rs index f4344c4..40b3e2b 100644 --- a/cli/src/commands/about.rs +++ b/cli/src/commands/about.rs @@ -1,6 +1,8 @@ use anyhow::Result; use colored::Colorize; +use crate::manifest::DistManifest; + pub fn run() -> Result<()> { let version = env!("CARGO_PKG_VERSION"); let description = env!("CARGO_PKG_DESCRIPTION"); @@ -15,6 +17,19 @@ pub fn run() -> Result<()> { "DevTrail CLI".bold(), format!("v{version}").dimmed() ); + + // Show framework version if installed + if let Ok(cwd) = std::env::current_dir() { + let manifest_path = cwd.join(".devtrail/dist-manifest.yml"); + if let Ok(manifest) = DistManifest::load(&manifest_path) { + println!( + " {} {}", + "Framework:".dimmed(), + format!("v{}", manifest.version).dimmed() + ); + } + } + println!(" {}", description.dimmed()); println!(); println!(" {} {}", "Author:".cyan(), authors); diff --git a/cli/src/commands/mod.rs b/cli/src/commands/mod.rs index 54b58ce..baf3725 100644 --- a/cli/src/commands/mod.rs +++ b/cli/src/commands/mod.rs @@ -4,3 +4,4 @@ pub mod remove; pub mod status; pub mod update; pub mod update_cli; +pub mod update_framework; diff --git a/cli/src/commands/status.rs b/cli/src/commands/status.rs index 4e543dd..9814699 100644 --- a/cli/src/commands/status.rs +++ b/cli/src/commands/status.rs @@ -53,11 +53,14 @@ pub fn run(path: &str) -> Result<()> { let version = load_version(&target); let language = load_language(&target); + let cli_version = env!("CARGO_PKG_VERSION"); + println!(); println!("{}", "DevTrail Status".bold()); - println!(" {} {}", "Path:".dimmed(), target.display()); - println!(" {} {}", "Version:".dimmed(), version); - println!(" {} {}", "Language:".dimmed(), language); + println!(" {} {}", "Path:".dimmed(), target.display()); + println!(" {} {}", "Framework:".dimmed(), version); + println!(" {} {}", "CLI:".dimmed(), cli_version); + println!(" {} {}", "Language:".dimmed(), language); // Phase 2: Structure check println!(); diff --git a/cli/src/commands/update.rs b/cli/src/commands/update.rs index 12605a1..e3e9d0b 100644 --- a/cli/src/commands/update.rs +++ b/cli/src/commands/update.rs @@ -1,337 +1,27 @@ -use anyhow::{bail, Context, Result}; +use anyhow::Result; use colored::Colorize; -use dialoguer::{Select, theme::ColorfulTheme}; -use std::path::{Path, PathBuf}; -use crate::config::Checksums; -use crate::download; -use crate::inject; -use crate::manifest::DistManifest; -use crate::self_update; use crate::utils; pub fn run() -> Result<()> { - let target = std::env::current_dir().context("Failed to get current directory")?; + let target = std::env::current_dir()?; - // Verify DevTrail is installed - if !target.join(".devtrail").exists() { - bail!( - ".devtrail/ not found. Use {} to initialize first.", - "devtrail init".yellow() - ); - } - - println!("{} DevTrail...", "Updating".cyan().bold()); - - // Load current checksums - let current_checksums = Checksums::load(&target)?; - if !current_checksums.version.is_empty() { - println!( - " {} {}", - "Current version:".dimmed(), - current_checksums.version - ); - } - - // Fetch latest release - utils::info("Checking for updates..."); - let release = download::get_latest_release()?; - println!( - " {} {}", - "Latest version:".dimmed(), - release.tag_name.green() - ); - - // Download ZIP - let temp_dir = tempfile::tempdir().context("Failed to create temp directory")?; - let zip_path = temp_dir.path().join("devtrail.zip"); - - utils::info("Downloading..."); - download::download_zip(&release.zip_url, &zip_path)?; - - // Extract to temp directory for comparison - let extract_dir = temp_dir.path().join("extracted"); - std::fs::create_dir_all(&extract_dir)?; - extract_all(&zip_path, &extract_dir)?; - - // Find source root within extracted content - let source_root = find_source_root(&extract_dir)?; - - // Load manifest from extracted release - let manifest = DistManifest::load(&source_root.join("dist-manifest.yml"))?; - - // Update framework files - utils::info("Updating framework files..."); - let stats = update_files(&target, &source_root, ¤t_checksums)?; - - // Update directive injections - utils::info("Updating AI agent directives..."); - inject_directives(&target, &source_root, &manifest)?; - - // Save manifest locally for future remove operations - save_local_manifest(&target, &manifest)?; - - // Save new checksums - save_checksums(&target, &release.tag_name)?; - - // Print summary - println!(); - utils::success("DevTrail updated successfully!"); - println!(" Files updated: {}", stats.updated); - println!(" Files skipped (user-modified): {}", stats.skipped); - println!(" Files added: {}", stats.added); - - // Notify about CLI updates - self_update::notify_if_newer(&release.tag_name); - - Ok(()) -} - -struct UpdateStats { - updated: usize, - skipped: usize, - added: usize, -} - -/// Update files, respecting user modifications -fn update_files( - target: &Path, - source_root: &Path, - checksums: &Checksums, -) -> Result { - let mut stats = UpdateStats { - updated: 0, - skipped: 0, - added: 0, - }; - - // Walk extracted files - let entries = walkdir(source_root.to_path_buf())?; - - for source_path in entries { - let relative = source_path - .strip_prefix(source_root) - .unwrap_or(&source_path) - .display() - .to_string(); - - // Skip user-generated documents - if utils::is_user_document(&source_path) { - continue; - } - - // Skip checksums file - if relative == ".devtrail/.checksums.json" { - continue; - } - - // Skip dist-manifest.yml (we save it separately) - if relative == ".devtrail/dist-manifest.yml" { - continue; - } - - let target_path = target.join(&relative); - - if !target_path.exists() { - // New file — just copy it - if let Some(parent) = target_path.parent() { - utils::ensure_dir(parent)?; - } - std::fs::copy(&source_path, &target_path)?; - stats.added += 1; - continue; - } - - // File exists — check if user modified it - let current_hash = utils::file_hash(&target_path).unwrap_or_default(); - let original_hash = checksums - .files - .get(&relative) - .cloned() - .unwrap_or_default(); - - if current_hash == original_hash || original_hash.is_empty() { - // User hasn't modified it (or no previous hash) — safe to overwrite - std::fs::copy(&source_path, &target_path)?; - stats.updated += 1; - } else { - // User modified it — prompt for action - let new_hash = utils::file_hash(&source_path).unwrap_or_default(); - if current_hash == new_hash { - // Same content, no action needed - continue; - } - - utils::warn(&format!("User-modified file: {}", relative)); - let selection = Select::with_theme(&ColorfulTheme::default()) - .with_prompt("What would you like to do?") - .items(&["Keep my version", "Use new version", "Backup mine + use new"]) - .default(0) - .interact()?; - - match selection { - 0 => { - stats.skipped += 1; - } - 1 => { - std::fs::copy(&source_path, &target_path)?; - stats.updated += 1; - } - 2 => { - let backup = target_path.with_extension("md.bak"); - std::fs::copy(&target_path, &backup)?; - std::fs::copy(&source_path, &target_path)?; - stats.updated += 1; - utils::info(&format!("Backup saved: {}", backup.display())); - } - _ => { - stats.skipped += 1; - } - } - } - } - - Ok(stats) -} - -/// Inject directives based on manifest and templates from the release -fn inject_directives(target: &Path, source_root: &Path, manifest: &DistManifest) -> Result<()> { - for injection in &manifest.injections { - let target_path = target.join(&injection.target); - - // In update mode, only update targets that already exist - if !target_path.exists() { - continue; - } - - let template_path = source_root.join(&injection.template); - let template_content = match std::fs::read_to_string(&template_path) { - Ok(content) => content, - Err(_) => { - utils::warn(&format!( - "Template not found: {}", - injection.template - )); - continue; - } - }; - - let embed_content = if let Some(embed_file) = &injection.embed { - // Use the embed file from the release, not the local one - let embed_path = source_root.join(embed_file); - if embed_path.exists() { - Some(std::fs::read_to_string(&embed_path).with_context(|| { - format!("Failed to read embed file: {}", embed_path.display()) - })?) - } else { - utils::warn(&format!( - "Embed file not found in release: {} (skipping {})", - embed_file, injection.target - )); - continue; - } - } else { - None - }; - - inject::inject_directive(&target_path, &template_content, embed_content.as_deref())?; - } - - Ok(()) -} - -/// Save the manifest locally for future remove operations -fn save_local_manifest(target: &Path, manifest: &DistManifest) -> Result<()> { - let manifest_path = target.join(".devtrail/dist-manifest.yml"); - let content = manifest.to_yaml()?; - std::fs::write(&manifest_path, content) - .context("Failed to save local dist-manifest.yml")?; - Ok(()) -} - -fn save_checksums(target: &Path, version: &str) -> Result<()> { - let mut checksums = Checksums { - version: version.to_string(), - files: std::collections::HashMap::new(), - }; - - if let Ok(entries) = walkdir(target.join(".devtrail")) { - for entry in entries { - if let Some(hash) = utils::file_hash(&entry) { - let relative = entry - .strip_prefix(target) - .unwrap_or(&entry) - .display() - .to_string(); - checksums.files.insert(relative, hash); - } + // Update framework (skip if not initialized) + if target.join(".devtrail").exists() { + println!("{}", "── Framework ──".bold()); + if let Err(e) = super::update_framework::run() { + utils::warn(&format!("Framework update failed: {}", e)); } + } else { + utils::warn("DevTrail framework not initialized — skipping framework update."); } - let devtrail_path = target.join("DEVTRAIL.md"); - if let Some(hash) = utils::file_hash(&devtrail_path) { - checksums.files.insert("DEVTRAIL.md".to_string(), hash); - } - - checksums.save(target)?; - Ok(()) -} - -fn extract_all(zip_path: &Path, dest: &Path) -> Result<()> { - let file = std::fs::File::open(zip_path)?; - let mut archive = zip::ZipArchive::new(file)?; - - for i in 0..archive.len() { - let mut entry = archive.by_index(i)?; - let path = dest.join(entry.name()); - - if entry.is_dir() { - std::fs::create_dir_all(&path)?; - } else { - if let Some(parent) = path.parent() { - std::fs::create_dir_all(parent)?; - } - let mut outfile = std::fs::File::create(&path)?; - std::io::copy(&mut entry, &mut outfile)?; - } + // Update CLI + println!(); + println!("{}", "── CLI ──".bold()); + if let Err(e) = super::update_cli::run() { + utils::warn(&format!("CLI update failed: {}", e)); } Ok(()) } - -fn find_source_root(extract_dir: &Path) -> Result { - // Check if dist-manifest.yml is directly in extract_dir - if extract_dir.join("dist-manifest.yml").exists() { - return Ok(extract_dir.to_path_buf()); - } - - // Check one level deep (GitHub ZIP archives nest in a directory) - for entry in std::fs::read_dir(extract_dir)? { - let entry = entry?; - let path = entry.path(); - if path.is_dir() && path.join("dist-manifest.yml").exists() { - return Ok(path); - } - } - - bail!("Could not find dist-manifest.yml in extracted archive"); -} - -fn walkdir(dir: PathBuf) -> Result> { - let mut files = Vec::new(); - if !dir.is_dir() { - return Ok(files); - } - - for entry in std::fs::read_dir(&dir)? { - let entry = entry?; - let path = entry.path(); - if path.is_dir() { - files.extend(walkdir(path)?); - } else { - files.push(path); - } - } - - Ok(files) -} diff --git a/cli/src/commands/update_framework.rs b/cli/src/commands/update_framework.rs new file mode 100644 index 0000000..9f14bb1 --- /dev/null +++ b/cli/src/commands/update_framework.rs @@ -0,0 +1,334 @@ +use anyhow::{bail, Context, Result}; +use colored::Colorize; +use dialoguer::{Select, theme::ColorfulTheme}; +use std::path::{Path, PathBuf}; + +use crate::config::Checksums; +use crate::download; +use crate::inject; +use crate::manifest::DistManifest; +use crate::utils; + +pub fn run() -> Result<()> { + let target = std::env::current_dir().context("Failed to get current directory")?; + + // Verify DevTrail is installed + if !target.join(".devtrail").exists() { + bail!( + ".devtrail/ not found. Use {} to initialize first.", + "devtrail init".yellow() + ); + } + + println!("{} DevTrail framework...", "Updating".cyan().bold()); + + // Load current checksums + let current_checksums = Checksums::load(&target)?; + if !current_checksums.version.is_empty() { + println!( + " {} {}", + "Current version:".dimmed(), + current_checksums.version + ); + } + + // Fetch latest release + utils::info("Checking for updates..."); + let release = download::get_latest_release()?; + let display_version = download::strip_tag_prefix(&release.tag_name); + println!( + " {} {}", + "Latest version:".dimmed(), + display_version.green() + ); + + // Download ZIP + let temp_dir = tempfile::tempdir().context("Failed to create temp directory")?; + let zip_path = temp_dir.path().join("devtrail.zip"); + + utils::info("Downloading..."); + download::download_zip(&release.zip_url, &zip_path)?; + + // Extract to temp directory for comparison + let extract_dir = temp_dir.path().join("extracted"); + std::fs::create_dir_all(&extract_dir)?; + extract_all(&zip_path, &extract_dir)?; + + // Find source root within extracted content + let source_root = find_source_root(&extract_dir)?; + + // Load manifest from extracted release + let manifest = DistManifest::load(&source_root.join("dist-manifest.yml"))?; + + // Update framework files + utils::info("Updating framework files..."); + let stats = update_files(&target, &source_root, ¤t_checksums)?; + + // Update directive injections + utils::info("Updating AI agent directives..."); + inject_directives(&target, &source_root, &manifest)?; + + // Save manifest locally for future remove operations + save_local_manifest(&target, &manifest)?; + + // Save new checksums + save_checksums(&target, &release.tag_name)?; + + // Print summary + println!(); + utils::success("DevTrail framework updated successfully!"); + println!(" Files updated: {}", stats.updated); + println!(" Files skipped (user-modified): {}", stats.skipped); + println!(" Files added: {}", stats.added); + + Ok(()) +} + +struct UpdateStats { + updated: usize, + skipped: usize, + added: usize, +} + +/// Update files, respecting user modifications +fn update_files( + target: &Path, + source_root: &Path, + checksums: &Checksums, +) -> Result { + let mut stats = UpdateStats { + updated: 0, + skipped: 0, + added: 0, + }; + + // Walk extracted files + let entries = walkdir(source_root.to_path_buf())?; + + for source_path in entries { + let relative = source_path + .strip_prefix(source_root) + .unwrap_or(&source_path) + .display() + .to_string(); + + // Skip user-generated documents + if utils::is_user_document(&source_path) { + continue; + } + + // Skip checksums file + if relative == ".devtrail/.checksums.json" { + continue; + } + + // Skip dist-manifest.yml (we save it separately) + if relative == ".devtrail/dist-manifest.yml" { + continue; + } + + let target_path = target.join(&relative); + + if !target_path.exists() { + // New file — just copy it + if let Some(parent) = target_path.parent() { + utils::ensure_dir(parent)?; + } + std::fs::copy(&source_path, &target_path)?; + stats.added += 1; + continue; + } + + // File exists — check if user modified it + let current_hash = utils::file_hash(&target_path).unwrap_or_default(); + let original_hash = checksums + .files + .get(&relative) + .cloned() + .unwrap_or_default(); + + if current_hash == original_hash || original_hash.is_empty() { + // User hasn't modified it (or no previous hash) — safe to overwrite + std::fs::copy(&source_path, &target_path)?; + stats.updated += 1; + } else { + // User modified it — prompt for action + let new_hash = utils::file_hash(&source_path).unwrap_or_default(); + if current_hash == new_hash { + // Same content, no action needed + continue; + } + + utils::warn(&format!("User-modified file: {}", relative)); + let selection = Select::with_theme(&ColorfulTheme::default()) + .with_prompt("What would you like to do?") + .items(&["Keep my version", "Use new version", "Backup mine + use new"]) + .default(0) + .interact()?; + + match selection { + 0 => { + stats.skipped += 1; + } + 1 => { + std::fs::copy(&source_path, &target_path)?; + stats.updated += 1; + } + 2 => { + let backup = target_path.with_extension("md.bak"); + std::fs::copy(&target_path, &backup)?; + std::fs::copy(&source_path, &target_path)?; + stats.updated += 1; + utils::info(&format!("Backup saved: {}", backup.display())); + } + _ => { + stats.skipped += 1; + } + } + } + } + + Ok(stats) +} + +/// Inject directives based on manifest and templates from the release +fn inject_directives(target: &Path, source_root: &Path, manifest: &DistManifest) -> Result<()> { + for injection in &manifest.injections { + let target_path = target.join(&injection.target); + + // In update mode, only update targets that already exist + if !target_path.exists() { + continue; + } + + let template_path = source_root.join(&injection.template); + let template_content = match std::fs::read_to_string(&template_path) { + Ok(content) => content, + Err(_) => { + utils::warn(&format!( + "Template not found: {}", + injection.template + )); + continue; + } + }; + + let embed_content = if let Some(embed_file) = &injection.embed { + // Use the embed file from the release, not the local one + let embed_path = source_root.join(embed_file); + if embed_path.exists() { + Some(std::fs::read_to_string(&embed_path).with_context(|| { + format!("Failed to read embed file: {}", embed_path.display()) + })?) + } else { + utils::warn(&format!( + "Embed file not found in release: {} (skipping {})", + embed_file, injection.target + )); + continue; + } + } else { + None + }; + + inject::inject_directive(&target_path, &template_content, embed_content.as_deref())?; + } + + Ok(()) +} + +/// Save the manifest locally for future remove operations +fn save_local_manifest(target: &Path, manifest: &DistManifest) -> Result<()> { + let manifest_path = target.join(".devtrail/dist-manifest.yml"); + let content = manifest.to_yaml()?; + std::fs::write(&manifest_path, content) + .context("Failed to save local dist-manifest.yml")?; + Ok(()) +} + +fn save_checksums(target: &Path, version: &str) -> Result<()> { + let mut checksums = Checksums { + version: version.to_string(), + files: std::collections::HashMap::new(), + }; + + if let Ok(entries) = walkdir(target.join(".devtrail")) { + for entry in entries { + if let Some(hash) = utils::file_hash(&entry) { + let relative = entry + .strip_prefix(target) + .unwrap_or(&entry) + .display() + .to_string(); + checksums.files.insert(relative, hash); + } + } + } + + let devtrail_path = target.join("DEVTRAIL.md"); + if let Some(hash) = utils::file_hash(&devtrail_path) { + checksums.files.insert("DEVTRAIL.md".to_string(), hash); + } + + checksums.save(target)?; + Ok(()) +} + +fn extract_all(zip_path: &Path, dest: &Path) -> Result<()> { + let file = std::fs::File::open(zip_path)?; + let mut archive = zip::ZipArchive::new(file)?; + + for i in 0..archive.len() { + let mut entry = archive.by_index(i)?; + let path = dest.join(entry.name()); + + if entry.is_dir() { + std::fs::create_dir_all(&path)?; + } else { + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent)?; + } + let mut outfile = std::fs::File::create(&path)?; + std::io::copy(&mut entry, &mut outfile)?; + } + } + + Ok(()) +} + +fn find_source_root(extract_dir: &Path) -> Result { + // Check if dist-manifest.yml is directly in extract_dir + if extract_dir.join("dist-manifest.yml").exists() { + return Ok(extract_dir.to_path_buf()); + } + + // Check one level deep (GitHub ZIP archives nest in a directory) + for entry in std::fs::read_dir(extract_dir)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() && path.join("dist-manifest.yml").exists() { + return Ok(path); + } + } + + bail!("Could not find dist-manifest.yml in extracted archive"); +} + +fn walkdir(dir: PathBuf) -> Result> { + let mut files = Vec::new(); + if !dir.is_dir() { + return Ok(files); + } + + for entry in std::fs::read_dir(&dir)? { + let entry = entry?; + let path = entry.path(); + if path.is_dir() { + files.extend(walkdir(path)?); + } else { + files.push(path); + } + } + + Ok(files) +} diff --git a/cli/src/download.rs b/cli/src/download.rs index d1c3670..7b77876 100644 --- a/cli/src/download.rs +++ b/cli/src/download.rs @@ -43,16 +43,24 @@ fn build_client() -> Result { builder.build().context("Failed to create HTTP client") } -/// Get full release info including all assets -pub fn get_latest_release_full() -> Result { - let url = format!("{}/repos/{}/releases/latest", GITHUB_API_BASE, GITHUB_REPO); +/// Strip known tag prefixes (fw-, cli-, v) and return the version string +pub fn strip_tag_prefix(tag: &str) -> &str { + tag.strip_prefix("fw-") + .or_else(|| tag.strip_prefix("cli-")) + .or_else(|| tag.strip_prefix("v")) + .unwrap_or(tag) +} + +/// Fetch the latest release whose tag starts with the given prefix +pub fn get_latest_release_by_prefix(prefix: &str) -> Result { + let url = format!("{}/repos/{}/releases", GITHUB_API_BASE, GITHUB_REPO); let client = build_client()?; let response = client .get(&url) .send() - .context("Failed to fetch latest release from GitHub")?; + .context("Failed to fetch releases from GitHub")?; if response.status() == reqwest::StatusCode::FORBIDDEN { bail!( @@ -68,40 +76,57 @@ pub fn get_latest_release_full() -> Result { bail!("GitHub API error: {}", response.status()); } - let body: serde_json::Value = response.json().context("Failed to parse release JSON")?; - - let tag_name = body["tag_name"] - .as_str() - .context("Missing tag_name in release")? - .to_string(); - - let mut assets = Vec::new(); - if let Some(asset_array) = body["assets"].as_array() { - for asset in asset_array { - if let (Some(name), Some(url)) = ( - asset["name"].as_str(), - asset["browser_download_url"].as_str(), - ) { - assets.push(ReleaseAsset { - name: name.to_string(), - download_url: url.to_string(), - }); + let body: serde_json::Value = response.json().context("Failed to parse releases JSON")?; + + let releases = body.as_array().context("Expected JSON array of releases")?; + + for release in releases { + let tag_name = match release["tag_name"].as_str() { + Some(t) => t, + None => continue, + }; + + if !tag_name.starts_with(prefix) { + continue; + } + + let tag_name = tag_name.to_string(); + let mut assets = Vec::new(); + + if let Some(asset_array) = release["assets"].as_array() { + for asset in asset_array { + if let (Some(name), Some(url)) = ( + asset["name"].as_str(), + asset["browser_download_url"].as_str(), + ) { + assets.push(ReleaseAsset { + name: name.to_string(), + download_url: url.to_string(), + }); + } } } + + return Ok(FullReleaseInfo { tag_name, assets }); } - Ok(FullReleaseInfo { tag_name, assets }) + bail!("No release found with tag prefix '{}'", prefix) +} + +/// Get full release info for CLI releases (cli-* tags) +pub fn get_latest_release_full() -> Result { + get_latest_release_by_prefix("cli-") } -/// Get the latest release info from GitHub +/// Get the latest framework release info (fw-* tags) pub fn get_latest_release() -> Result { - let full = get_latest_release_full()?; + let full = get_latest_release_by_prefix("fw-")?; - // Look for the devtrail distribution ZIP (not CLI binary ZIPs) + // Look for the devtrail distribution ZIP (devtrail-fw-*.zip) let zip_url = full .assets .iter() - .find(|a| a.name.starts_with("devtrail-v") && a.name.ends_with(".zip")) + .find(|a| a.name.starts_with("devtrail-fw-") && a.name.ends_with(".zip")) .map(|a| a.download_url.clone()); // Fallback to zipball if no distribution asset found diff --git a/cli/src/main.rs b/cli/src/main.rs index b025975..f429603 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -27,8 +27,10 @@ enum Commands { #[arg(default_value = ".")] path: String, }, - /// Update DevTrail to the latest version + /// Update both framework and CLI to the latest version Update, + /// Update the DevTrail framework to the latest version + UpdateFramework, /// Update the CLI binary to the latest version UpdateCli, /// Remove DevTrail from the project @@ -56,6 +58,7 @@ fn main() { let result = match cli.command { Commands::Init { path } => commands::init::run(&path), Commands::Update => commands::update::run(), + Commands::UpdateFramework => commands::update_framework::run(), Commands::UpdateCli => commands::update_cli::run(), Commands::Remove { full } => commands::remove::run(full), Commands::Status { path } => commands::status::run(&path), diff --git a/cli/src/self_update.rs b/cli/src/self_update.rs index bbaabbc..a4a303a 100644 --- a/cli/src/self_update.rs +++ b/cli/src/self_update.rs @@ -6,69 +6,6 @@ use crate::download; use crate::platform; use crate::utils; -/// Information about an available CLI update -pub struct UpdateAvailable { - pub current: String, - pub latest: String, -} - -/// Check if a CLI update is available by comparing versions -pub fn check_for_cli_update(release_tag: &str) -> Option { - let current_str = env!("CARGO_PKG_VERSION"); - let tag_version = release_tag.strip_prefix('v').unwrap_or(release_tag); - - let current = semver::Version::parse(current_str).ok()?; - let latest = semver::Version::parse(tag_version).ok()?; - - if latest > current { - Some(UpdateAvailable { - current: current_str.to_string(), - latest: tag_version.to_string(), - }) - } else { - None - } -} - -/// Print a notification box if a CLI update is available -pub fn notify_if_newer(release_tag: &str) { - if let Some(update) = check_for_cli_update(release_tag) { - let line1 = format!( - " CLI update available: v{} -> v{} ", - update.current, update.latest - ); - let line2 = " Run `devtrail update-cli` to update the binary "; - let width = line1.len().max(line2.len()); - let border = "\u{2500}".repeat(width); - - println!(); - println!( - " {}{}{}", - "\u{256d}".yellow(), - border.yellow(), - "\u{256e}".yellow() - ); - println!( - " {}{: Result<()> { let current_version = env!("CARGO_PKG_VERSION"); @@ -77,10 +14,7 @@ pub fn perform_update() -> Result<()> { // Fetch latest release utils::info("Checking for updates..."); let release = download::get_latest_release_full()?; - let tag_version = release - .tag_name - .strip_prefix('v') - .unwrap_or(&release.tag_name); + let tag_version = download::strip_tag_prefix(&release.tag_name); println!( " {} v{}", diff --git a/cli/tests/update_test.rs b/cli/tests/update_test.rs index 48ca14e..dc42260 100644 --- a/cli/tests/update_test.rs +++ b/cli/tests/update_test.rs @@ -2,17 +2,28 @@ use assert_cmd::Command; use predicates::prelude::*; #[test] -fn test_update_shows_error_when_not_initialized() { +fn test_update_framework_shows_error_when_not_initialized() { let dir = tempfile::TempDir::new().unwrap(); let mut cmd = Command::cargo_bin("devtrail").unwrap(); cmd.current_dir(dir.path()) - .arg("update") + .arg("update-framework") .assert() .failure() .stderr(predicate::str::contains("not found")); } +#[test] +fn test_update_in_empty_dir_does_not_fail() { + let dir = tempfile::TempDir::new().unwrap(); + + let mut cmd = Command::cargo_bin("devtrail").unwrap(); + cmd.current_dir(dir.path()) + .arg("update") + .assert() + .success(); +} + #[test] fn test_remove_shows_error_when_not_initialized() { let dir = tempfile::TempDir::new().unwrap(); diff --git a/dist/dist-manifest.yml b/dist/dist-manifest.yml index 8aed148..c1f59fa 100644 --- a/dist/dist-manifest.yml +++ b/dist/dist-manifest.yml @@ -1,4 +1,4 @@ -version: "2.0.0" +version: "2.1.0" description: "DevTrail distribution manifest" repository: "https://github.com/StrangeDaysTech/devtrail" diff --git a/install.ps1 b/install.ps1 index 76cbbbe..d327a0b 100644 --- a/install.ps1 +++ b/install.ps1 @@ -4,7 +4,7 @@ # irm https://raw.githubusercontent.com/StrangeDaysTech/devtrail/main/install.ps1 | iex # # # Or with parameters: -# & ([scriptblock]::Create((irm https://raw.githubusercontent.com/StrangeDaysTech/devtrail/main/install.ps1))) -Tag v2.0.0 +# & ([scriptblock]::Create((irm https://raw.githubusercontent.com/StrangeDaysTech/devtrail/main/install.ps1))) -Tag cli-1.0.0 # # Compatible with PowerShell 5.1+ and PowerShell Core (pwsh). @@ -61,13 +61,13 @@ function Get-GitHubHeaders { # ── Get latest tag ─────────────────────────────────────────────────────── function Get-LatestTag { - $apiUrl = "https://api.github.com/repos/$Repo/releases/latest" + $apiUrl = "https://api.github.com/repos/$Repo/releases" $headers = Get-GitHubHeaders - Write-Status "fetching latest release info..." + Write-Status "fetching latest CLI release info..." try { - $release = Invoke-RestMethod -Uri $apiUrl -Headers $headers -UseBasicParsing + $releases = Invoke-RestMethod -Uri $apiUrl -Headers $headers -UseBasicParsing } catch { Write-Err "Failed to fetch release info from GitHub API." @@ -78,9 +78,9 @@ function Get-LatestTag { exit 1 } - $tagName = $release.tag_name + $tagName = ($releases | Where-Object { $_.tag_name -like "cli-*" } | Select-Object -First 1).tag_name if (-not $tagName) { - Write-Err "Could not parse latest release tag from GitHub API response." + Write-Err "Could not find a CLI release (cli-* tag) from GitHub API response." exit 1 } @@ -102,7 +102,7 @@ try { } # Build asset name and URL - $versionNum = $Tag -replace "^v", "" + $versionNum = $Tag -replace "^cli-", "" $asset = "devtrail-cli-v${versionNum}-${Target}.zip" $url = "https://github.com/$Repo/releases/download/$Tag/$asset" diff --git a/install.sh b/install.sh index b0b76b6..9dc8103 100644 --- a/install.sh +++ b/install.sh @@ -3,7 +3,7 @@ # # Usage: # curl -fsSL https://raw.githubusercontent.com/StrangeDaysTech/devtrail/main/install.sh | sh -# curl -fsSL ... | sh -s -- --tag v2.0.0 --to ~/.local/bin +# curl -fsSL ... | sh -s -- --tag cli-1.0.0 --to ~/.local/bin # # Compatible with bash, zsh, dash, and POSIX sh. @@ -63,7 +63,7 @@ USAGE: install.sh [OPTIONS] OPTIONS: - --tag Install a specific version (e.g. v2.0.0). Default: latest + --tag Install a specific version (e.g. cli-1.0.0). Default: latest --to Installation directory. Default: ~/.local/bin (or /usr/local/bin with sudo) --help Show this help message @@ -72,7 +72,7 @@ ENVIRONMENT: EXAMPLES: curl -fsSL https://raw.githubusercontent.com/StrangeDaysTech/devtrail/main/install.sh | sh - curl -fsSL ... | sh -s -- --tag v2.0.0 + curl -fsSL ... | sh -s -- --tag cli-1.0.0 curl -fsSL ... | sh -s -- --to /usr/local/bin EOF } @@ -150,10 +150,10 @@ resolve_install_dir() { # ── Get latest tag ─────────────────────────────────────────────────────── get_latest_tag() { - _api_url="https://api.github.com/repos/${REPO}/releases/latest" - _tmp_json="${TMPDIR_CLEANUP}/release.json" + _api_url="https://api.github.com/repos/${REPO}/releases" + _tmp_json="${TMPDIR_CLEANUP}/releases.json" - say "fetching latest release info..." + say "fetching latest CLI release info..." if ! download "$_api_url" "$_tmp_json" 2>/dev/null; then echo "" echo " Failed to fetch release info from GitHub API." >&2 @@ -163,11 +163,11 @@ get_latest_tag() { err "could not determine latest version" fi - # Extract tag_name from JSON without jq (POSIX-compatible) - TAG=$(sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$_tmp_json" | head -1) + # Extract the first tag_name starting with "cli-" (POSIX-compatible) + TAG=$(sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\(cli-[^"]*\)".*/\1/p' "$_tmp_json" | head -1) if [ -z "$TAG" ]; then - err "could not parse latest release tag from GitHub API response" + err "could not find a CLI release (cli-* tag) from GitHub API response" fi say "latest version: ${TAG}" @@ -196,8 +196,8 @@ main() { say "using specified version: ${TAG}" fi - # Strip leading 'v' for the version in asset name (asset uses v-prefix) - VERSION_NUM="${TAG#v}" + # Strip leading 'cli-' prefix for the version in asset name + VERSION_NUM="${TAG#cli-}" # Build asset name and URL ASSET="devtrail-cli-v${VERSION_NUM}-${TARGET}.tar.gz" From 8c0d1af55fa69ab48aa9a8cd398a9001850a1dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Villase=C3=B1or=20Montfort?= <195970+montfort@users.noreply.github.com> Date: Wed, 18 Mar 2026 23:30:33 -0600 Subject: [PATCH 2/2] docs: restructure documentation with CLI reference, workflows, and version fixes Add CLI-REFERENCE.md and WORKFLOWS.md for adopters (EN + ES), update all docs to reflect independent fw-*/cli-* versioning, fix broken cookbook.md link, remove hardcoded v2.0.0 from dist files, and update releases/latest references to point to fw-* releases. Co-Authored-By: Claude Opus 4.6 (1M context) --- CONTRIBUTING.md | 32 ++- README.md | 47 ++-- dist/.devtrail/QUICK-REFERENCE.md | 4 +- dist/DEVTRAIL.md | 2 +- dist/dist-templates/directives/CLAUDE.md | 2 +- dist/dist-templates/directives/GEMINI.md | 2 +- .../directives/copilot-instructions.md | 2 +- docs/README.md | 18 +- docs/adopters/ADOPTION-GUIDE.md | 10 +- docs/adopters/CLI-REFERENCE.md | 260 ++++++++++++++++++ docs/adopters/WORKFLOWS.md | 192 +++++++++++++ docs/contributors/TRANSLATION-GUIDE.md | 2 +- docs/i18n/es/CONTRIBUTING.md | 32 ++- docs/i18n/es/README.md | 47 ++-- docs/i18n/es/adopters/ADOPTION-GUIDE.md | 10 +- docs/i18n/es/adopters/CLI-REFERENCE.md | 260 ++++++++++++++++++ docs/i18n/es/adopters/WORKFLOWS.md | 192 +++++++++++++ 17 files changed, 1035 insertions(+), 79 deletions(-) create mode 100644 docs/adopters/CLI-REFERENCE.md create mode 100644 docs/adopters/WORKFLOWS.md create mode 100644 docs/i18n/es/adopters/CLI-REFERENCE.md create mode 100644 docs/i18n/es/adopters/WORKFLOWS.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fd6039b..4d6aa8c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -314,23 +314,27 @@ The release binary is optimized with LTO and stripped for minimal size. ``` cli/src/ -├── main.rs # Entry point + clap CLI definition +├── main.rs # Entry point + clap CLI definition ├── commands/ -│ ├── mod.rs # Subcommand routing -│ ├── init.rs # devtrail init [path] -│ ├── update.rs # devtrail update -│ ├── remove.rs # devtrail remove [--full] -│ ├── update_cli.rs # devtrail update-cli -│ └── about.rs # devtrail about -├── config.rs # Config and checksums management -├── download.rs # GitHub Releases download -├── inject.rs # Directive file injection (markers) -├── manifest.rs # dist-manifest.yml parsing -├── platform.rs # OS/arch detection for binary downloads -├── self_update.rs # CLI binary self-update logic -└── utils.rs # Helpers (hashing, colors, paths) +│ ├── mod.rs # Subcommand routing +│ ├── init.rs # devtrail init [path] +│ ├── update.rs # devtrail update (combined) +│ ├── update_framework.rs # devtrail update-framework +│ ├── update_cli.rs # devtrail update-cli +│ ├── remove.rs # devtrail remove [--full] +│ ├── status.rs # devtrail status [path] +│ └── about.rs # devtrail about +├── config.rs # Config and checksums management +├── download.rs # GitHub Releases API (prefix-filtered) +├── inject.rs # Directive file injection (markers) +├── manifest.rs # dist-manifest.yml parsing +├── platform.rs # OS/arch detection for binary downloads +├── self_update.rs # CLI binary self-update logic +└── utils.rs # Helpers (hashing, colors, paths) ``` +> **Note**: Framework and CLI use independent versioning (`fw-*` and `cli-*` tags). See [CLI Reference](docs/adopters/CLI-REFERENCE.md#versioning) for details. + --- ## Questions? diff --git a/README.md b/README.md index 515c1da..0e62053 100644 --- a/README.md +++ b/README.md @@ -123,30 +123,41 @@ devtrail init . The CLI downloads the latest DevTrail release, sets up the framework, and configures your AI agent directive files automatically. -```bash -# Update DevTrail documents to latest version -devtrail update +### Versioning -# Update the CLI binary itself -devtrail update-cli +DevTrail uses independent version tags for each component: -# Show authorship and license info -devtrail about +| Component | Tag prefix | Example | Includes | +|-----------|-----------|---------|----------| +| Framework | `fw-` | `fw-2.1.0` | Templates, governance, directives, scripts | +| CLI | `cli-` | `cli-1.0.0` | The `devtrail` binary | -# Remove DevTrail -devtrail remove -``` +Check installed versions with `devtrail status` or `devtrail about`. + +### CLI Commands + +| Command | Description | +|---------|-------------| +| `devtrail init [path]` | Initialize DevTrail in a project | +| `devtrail update` | Update both framework and CLI | +| `devtrail update-framework` | Update only the framework | +| `devtrail update-cli` | Update the CLI binary | +| `devtrail remove [--full]` | Remove DevTrail from project | +| `devtrail status [path]` | Show installation health and doc stats | +| `devtrail about` | Show version and license info | + +See [CLI Reference](docs/adopters/CLI-REFERENCE.md) for detailed usage. ### Option 2: Manual Setup ```bash -# Download the latest release ZIP from GitHub -# https://github.com/StrangeDaysTech/devtrail/releases/latest +# Download the latest framework release ZIP from GitHub +# Go to https://github.com/StrangeDaysTech/devtrail/releases +# and download the latest fw-* release (e.g., fw-2.1.0) # Extract and copy to your project -cp -r .devtrail your-project/ -cp DEVTRAIL.md your-project/ -cp -r scripts your-project/ +unzip devtrail-fw-*.zip -d your-project/ +cd your-project # Commit git add .devtrail/ DEVTRAIL.md scripts/ @@ -166,7 +177,7 @@ DevTrail documentation is organized by audience: | [**Adopters**](docs/adopters/) | Teams adopting DevTrail in their projects | [ADOPTION-GUIDE.md](docs/adopters/ADOPTION-GUIDE.md) | | [**Contributors**](docs/contributors/) | Developers contributing to DevTrail | [TRANSLATION-GUIDE.md](docs/contributors/TRANSLATION-GUIDE.md) | -**Adopters**: Follow the [Adoption Guide](docs/adopters/ADOPTION-GUIDE.md) for step-by-step instructions, migration strategies for existing projects, and team rollout plans. +**Adopters**: Follow the [Adoption Guide](docs/adopters/ADOPTION-GUIDE.md) for step-by-step instructions, the [CLI Reference](docs/adopters/CLI-REFERENCE.md) for command details, and the [Workflows Guide](docs/adopters/WORKFLOWS.md) for daily usage patterns. **Contributors**: See [CONTRIBUTING.md](CONTRIBUTING.md) for development guidelines, and the [Translation Guide](docs/contributors/TRANSLATION-GUIDE.md) for adding new languages. @@ -174,9 +185,11 @@ DevTrail documentation is organized by audience: | Document | Description | |----------|-------------| -| [**📘 Quick Reference**](dist/.devtrail/QUICK-REFERENCE.md) | One-page overview of document types and naming | +| [**Quick Reference**](dist/.devtrail/QUICK-REFERENCE.md) | One-page overview of document types and naming | | [DEVTRAIL.md](dist/DEVTRAIL.md) | Unified governance rules (source of truth) | | [ADOPTION-GUIDE.md](docs/adopters/ADOPTION-GUIDE.md) | Adoption guide for new/existing projects | +| [CLI-REFERENCE.md](docs/adopters/CLI-REFERENCE.md) | Complete CLI command reference | +| [WORKFLOWS.md](docs/adopters/WORKFLOWS.md) | Recommended daily workflows and team patterns | ### Internal Structure diff --git a/dist/.devtrail/QUICK-REFERENCE.md b/dist/.devtrail/QUICK-REFERENCE.md index aff3db4..b63efb5 100644 --- a/dist/.devtrail/QUICK-REFERENCE.md +++ b/dist/.devtrail/QUICK-REFERENCE.md @@ -163,8 +163,6 @@ Mark `review_required: true` when: | Found tech debt | TDE | | Handled PII data | AILOG + ETH | -📖 **See [docs/cookbook.md](../docs/cookbook.md) for detailed examples.** - --- -*DevTrail v2.0.0 | [GitHub](https://github.com/StrangeDaysTech/devtrail) | [Strange Days Tech](https://strangedays.tech)* +*DevTrail | [GitHub](https://github.com/StrangeDaysTech/devtrail) | [Strange Days Tech](https://strangedays.tech)* diff --git a/dist/DEVTRAIL.md b/dist/DEVTRAIL.md index 91a9949..d34888e 100644 --- a/dist/DEVTRAIL.md +++ b/dist/DEVTRAIL.md @@ -274,5 +274,5 @@ risk_level: low | medium | high | critical --- -*DevTrail v2.0.0 | [GitHub](https://github.com/StrangeDaysTech/devtrail)* +*DevTrail | [GitHub](https://github.com/StrangeDaysTech/devtrail)* *[Strange Days Tech](https://strangedays.tech) — Because every change tells a story.* diff --git a/dist/dist-templates/directives/CLAUDE.md b/dist/dist-templates/directives/CLAUDE.md index 2ccbaa2..c684aac 100644 --- a/dist/dist-templates/directives/CLAUDE.md +++ b/dist/dist-templates/directives/CLAUDE.md @@ -7,4 +7,4 @@ --- -*DevTrail v2.0.0 | [Strange Days Tech](https://strangedays.tech) — Because every change tells a story.* +*DevTrail | [Strange Days Tech](https://strangedays.tech) — Because every change tells a story.* diff --git a/dist/dist-templates/directives/GEMINI.md b/dist/dist-templates/directives/GEMINI.md index 4d0fa84..a58d023 100644 --- a/dist/dist-templates/directives/GEMINI.md +++ b/dist/dist-templates/directives/GEMINI.md @@ -7,4 +7,4 @@ --- -*DevTrail v2.0.0 | [Strange Days Tech](https://strangedays.tech) — Because every change tells a story.* +*DevTrail | [Strange Days Tech](https://strangedays.tech) — Because every change tells a story.* diff --git a/dist/dist-templates/directives/copilot-instructions.md b/dist/dist-templates/directives/copilot-instructions.md index 316112a..066098b 100644 --- a/dist/dist-templates/directives/copilot-instructions.md +++ b/dist/dist-templates/directives/copilot-instructions.md @@ -7,4 +7,4 @@ --- -*DevTrail v2.0.0 | [Strange Days Tech](https://strangedays.tech) — Because every change tells a story.* +*DevTrail | [Strange Days Tech](https://strangedays.tech) — Because every change tells a story.* diff --git a/docs/README.md b/docs/README.md index db70f30..9f9f730 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,7 +7,9 @@ This directory contains DevTrail's user-facing documentation, organized by audie ``` docs/ ├── adopters/ # For teams adopting DevTrail -│ └── ADOPTION-GUIDE.md +│ ├── ADOPTION-GUIDE.md +│ ├── CLI-REFERENCE.md +│ └── WORKFLOWS.md ├── contributors/ # For developers contributing to DevTrail │ └── TRANSLATION-GUIDE.md └── i18n/ # Translations @@ -19,18 +21,32 @@ docs/ └── ADOPTION-GUIDE.md ``` +## Versioning + +DevTrail uses independent version tags: `fw-*` for framework releases and `cli-*` for CLI releases. See the [CLI Reference](adopters/CLI-REFERENCE.md#versioning) for details. + ## For Adopters | Document | Description | |----------|-------------| | [ADOPTION-GUIDE.md](adopters/ADOPTION-GUIDE.md) | Step-by-step guide for adopting DevTrail in new or existing projects | +| [CLI-REFERENCE.md](adopters/CLI-REFERENCE.md) | Complete CLI command reference with flags, arguments, and examples | +| [WORKFLOWS.md](adopters/WORKFLOWS.md) | Recommended daily workflows, update cadences, and team patterns | ## For Contributors | Document | Description | |----------|-------------| +| [CONTRIBUTING.md](../CONTRIBUTING.md) | Development setup, PR process, style guidelines | | [TRANSLATION-GUIDE.md](contributors/TRANSLATION-GUIDE.md) | Rules and guidelines for translating DevTrail documentation | +## Key References + +| Document | Description | +|----------|-------------| +| [QUICK-REFERENCE.md](../dist/.devtrail/QUICK-REFERENCE.md) | One-page cheat sheet: document types, naming, metadata | +| [DEVTRAIL.md](../dist/DEVTRAIL.md) | Unified governance rules (source of truth) | + ## Translations | Language | Documents | diff --git a/docs/adopters/ADOPTION-GUIDE.md b/docs/adopters/ADOPTION-GUIDE.md index d9b341d..41ebfc8 100644 --- a/docs/adopters/ADOPTION-GUIDE.md +++ b/docs/adopters/ADOPTION-GUIDE.md @@ -196,11 +196,11 @@ The CLI automatically: 1. **Download the latest release** - Go to [GitHub Releases](https://github.com/StrangeDaysTech/devtrail/releases/latest) and download the distribution ZIP. + Go to [GitHub Releases](https://github.com/StrangeDaysTech/devtrail/releases) and download the latest `fw-*` release ZIP (e.g., `fw-2.1.0`). 2. **Extract to your project** ```bash - unzip devtrail-v*.zip -d your-project/ + unzip devtrail-fw-*.zip -d your-project/ ``` 3. **Commit the structure** @@ -239,8 +239,8 @@ The CLI automatically: # Using CLI (recommended) devtrail init . - # Or manually: download from GitHub Releases - # https://github.com/StrangeDaysTech/devtrail/releases/latest + # Or manually: download the latest fw-* release from GitHub Releases + # https://github.com/StrangeDaysTech/devtrail/releases ``` 2. **Resolve conflicts with existing `docs/`** @@ -501,6 +501,8 @@ A: DevTrail rules are instructions, not enforcement. If an AI assistant creates ## Getting Help +- **CLI Reference**: [CLI-REFERENCE.md](CLI-REFERENCE.md) — detailed command reference +- **Workflows**: [WORKFLOWS.md](WORKFLOWS.md) — recommended daily usage patterns - **Issues**: [GitHub Issues](https://github.com/StrangeDaysTech/devtrail/issues) - **Discussions**: [GitHub Discussions](https://github.com/StrangeDaysTech/devtrail/discussions) - **Contributing**: See [CONTRIBUTING.md](../../CONTRIBUTING.md) diff --git a/docs/adopters/CLI-REFERENCE.md b/docs/adopters/CLI-REFERENCE.md new file mode 100644 index 0000000..e4f7758 --- /dev/null +++ b/docs/adopters/CLI-REFERENCE.md @@ -0,0 +1,260 @@ +# DevTrail CLI Reference + +**Complete reference for the `devtrail` command-line tool.** + +[![Strange Days Tech](https://img.shields.io/badge/by-Strange_Days_Tech-purple.svg)](https://strangedays.tech) + +**Languages**: English + +--- + +## Table of Contents + +1. [Installation](#installation) +2. [Versioning](#versioning) +3. [Commands](#commands) +4. [Environment Variables](#environment-variables) +5. [Exit Codes](#exit-codes) + +--- + +## Installation + +Install the DevTrail CLI using one of the methods below. For full setup instructions, see the [README](../../README.md#getting-started). + +**Quick install (prebuilt binary):** + +```bash +# Linux / macOS +curl -fsSL https://raw.githubusercontent.com/StrangeDaysTech/devtrail/main/install.sh | sh +``` + +```powershell +# Windows (PowerShell) +irm https://raw.githubusercontent.com/StrangeDaysTech/devtrail/main/install.ps1 | iex +``` + +**From source:** + +```bash +cargo install devtrail-cli +``` + +--- + +## Versioning + +DevTrail uses **independent version tags** for each component: + +| Component | Tag prefix | Example | What it includes | +|-----------|-----------|---------|------------------| +| Framework | `fw-` | `fw-2.1.0` | Templates, governance docs, directives, scripts | +| CLI | `cli-` | `cli-1.0.0` | The `devtrail` binary | + +Framework and CLI are released independently. A framework update does not require a CLI update, and vice versa. + +**Check installed versions:** + +```bash +devtrail about # Shows CLI version + framework version (if installed) +devtrail status # Shows full installation health including versions +``` + +--- + +## Commands + +### `devtrail init [path]` + +Initialize DevTrail in a project directory. + +**Arguments:** + +| Argument | Default | Description | +|----------|---------|-------------| +| `path` | `.` (current directory) | Target project directory | + +**What it does:** + +1. Downloads the latest framework release (`fw-*`) from GitHub +2. Creates the `.devtrail/` directory structure +3. Creates `DEVTRAIL.md` with governance rules +4. Configures AI agent directive files (`CLAUDE.md`, `GEMINI.md`, `.cursorrules`, etc.) +5. Copies validation scripts and CI/CD workflows + +**Example:** + +```bash +$ devtrail init . +✔ Downloaded DevTrail fw-2.1.0 +✔ Created .devtrail/ directory structure +✔ Created DEVTRAIL.md +✔ Configured AI agent directives +✔ Copied validation scripts + +DevTrail initialized successfully! +Next: git add .devtrail/ DEVTRAIL.md scripts/ && git commit -m "chore: adopt DevTrail" +``` + +--- + +### `devtrail update` + +Update **both** framework and CLI to their latest versions. Equivalent to running `update-framework` followed by `update-cli`. + +If `.devtrail/` does not exist in the current directory, the framework update is skipped with a warning. + +**Example:** + +```bash +$ devtrail update +Updating framework... +✔ Framework updated to fw-2.1.0 +Updating CLI... +✔ CLI updated to cli-1.0.0 +``` + +--- + +### `devtrail update-framework` + +Update only the framework files. Looks for the latest `fw-*` release on GitHub. + +**Conflict handling:** If you have modified framework files (e.g., governance docs or templates), the update preserves your changes and reports conflicts for manual resolution. + +**Example:** + +```bash +$ devtrail update-framework +✔ Framework updated to fw-2.1.0 +``` + +--- + +### `devtrail update-cli` + +Auto-update the `devtrail` binary itself. Looks for the latest `cli-*` release on GitHub and replaces the current binary. + +**Example:** + +```bash +$ devtrail update-cli +✔ CLI updated to cli-1.0.0 +``` + +--- + +### `devtrail remove [--full]` + +Remove DevTrail from the current project. + +**Flags:** + +| Flag | Description | +|------|-------------| +| `--full` | Remove everything, including user-created documents in `.devtrail/`. Asks for confirmation. | + +**Default behavior** (without `--full`): removes the framework structure but preserves documents you created inside `.devtrail/`. + +**Example:** + +```bash +$ devtrail remove +✔ DevTrail framework removed. User documents preserved in .devtrail/. + +$ devtrail remove --full +⚠ This will delete all DevTrail files including your documents. +Continue? [y/N]: y +✔ DevTrail completely removed. +``` + +--- + +### `devtrail status [path]` + +Show installation health and documentation statistics. + +**Arguments:** + +| Argument | Default | Description | +|----------|---------|-------------| +| `path` | `.` (current directory) | Target project directory | + +**Output includes:** + +- Project path +- Framework version +- CLI version +- Configured language +- Directory structure integrity +- Document statistics (count by type) + +**Example:** + +```bash +$ devtrail status +DevTrail Status +─────────────── +Path: /home/user/my-project +Framework version: fw-2.1.0 +CLI version: cli-1.0.0 +Language: en +Structure: ✔ Complete + +Documents: + AILOG: 12 + AIDEC: 4 + ADR: 7 + REQ: 3 + TES: 2 + TDE: 1 + INC: 0 + ETH: 1 + Total: 30 +``` + +--- + +### `devtrail about` + +Show version, authorship, and license information. + +**Example:** + +```bash +$ devtrail about +DevTrail CLI + CLI version: cli-1.0.0 + Framework version: fw-2.1.0 + Author: Strange Days Tech, S.A.S. + License: MIT + Repository: https://github.com/StrangeDaysTech/devtrail + Website: https://strangedays.tech +``` + +--- + +## Environment Variables + +| Variable | Description | +|----------|-------------| +| `GITHUB_TOKEN` | GitHub personal access token for authenticated API requests. Useful to avoid rate limits when downloading releases. | + +--- + +## Exit Codes + +| Code | Meaning | +|------|---------| +| `0` | Success | +| `1` | Error (details printed to stderr) | + +--- + +
+ +**DevTrail** — Because every change tells a story. + +[Back to docs](../README.md) • [README](../../README.md) • [Strange Days Tech](https://strangedays.tech) + +
diff --git a/docs/adopters/WORKFLOWS.md b/docs/adopters/WORKFLOWS.md new file mode 100644 index 0000000..ec916e0 --- /dev/null +++ b/docs/adopters/WORKFLOWS.md @@ -0,0 +1,192 @@ +# DevTrail - Recommended Workflows + +**Patterns and cadences for using DevTrail day to day.** + +[![Strange Days Tech](https://img.shields.io/badge/by-Strange_Days_Tech-purple.svg)](https://strangedays.tech) + +**Languages**: English + +--- + +## Table of Contents + +1. [After Initial Setup](#after-initial-setup) +2. [Daily Development](#daily-development) +3. [Keeping DevTrail Updated](#keeping-devtrail-updated) +4. [Checking Project Health](#checking-project-health) +5. [Using Skills (Active Documentation)](#using-skills-active-documentation) +6. [Team Patterns](#team-patterns) +7. [Understanding Versions](#understanding-versions) + +--- + +## After Initial Setup + +You ran `devtrail init .` and committed the result. Now what? + +1. **Open your project** with your AI coding assistant (Claude Code, Cursor, Gemini CLI, etc.) +2. The assistant will **automatically read** the DevTrail directives (`CLAUDE.md`, `GEMINI.md`, etc.) +3. From this point on, the assistant **creates documentation** in `.devtrail/` as part of its normal workflow +4. **No extra configuration needed** — DevTrail works passively through the directive files + +--- + +## Daily Development + +### The Passive Loop + +1. Work normally with your AI assistant — write features, fix bugs, refactor +2. The AI creates documents in `.devtrail/` according to the governance rules: + - **AILOG** for significant implementations (>10 lines changed) + - **AIDEC** when choosing between alternatives + - **ADR** for architectural decisions + - **ETH** when ethical concerns arise +3. Review documents flagged with `review_required: true` +4. Commit documentation together with the corresponding code changes + +### When to Create Documents Manually + +Use the active system (skills) when: + +- The AI missed documenting a significant change +- You (a human) made a decision that should be recorded +- You want to create a REQ, TES, TDE, or INC document +- You want to check documentation compliance + +--- + +## Keeping DevTrail Updated + +### Recommended Cadence + +- **Monthly** or when you see a new release on GitHub +- Check the [releases page](https://github.com/StrangeDaysTech/devtrail/releases) for changelogs + +### Update Commands + +| Goal | Command | +|------|---------| +| Update both framework and CLI | `devtrail update` | +| Update only templates and governance files | `devtrail update-framework` | +| Update only the CLI binary | `devtrail update-cli` | + +Framework and CLI have **independent versions** — you can update one without the other. See [Understanding Versions](#understanding-versions). + +### After Updating + +1. Review changes to directive files and governance docs +2. Commit the updated files: `git add .devtrail/ && git commit -m "chore: update DevTrail framework"` +3. If you customized any framework files, check for conflicts + +--- + +## Checking Project Health + +### CLI Status + +```bash +devtrail status +``` + +Shows: framework version, CLI version, directory structure integrity, and document statistics by type. Use it to verify that the installation is healthy. + +### Documentation Compliance (Skill) + +```bash +/devtrail-status +``` + +The `/devtrail-status` skill (available in Claude Code and Gemini CLI) analyzes: + +- Which recent code changes lack corresponding documentation +- Document compliance against governance rules +- Overall documentation health + +--- + +## Using Skills (Active Documentation) + +DevTrail has two documentation systems: + +| System | How it works | When to use | +|--------|-------------|-------------| +| **Passive** | AI auto-documents via directive files | Default — happens automatically | +| **Active** | User invokes skills to create docs | When passive missed something, or for human decisions | + +### Available Skills + +| Skill | Purpose | +|-------|---------| +| `/devtrail-status` | Check documentation compliance | +| `/devtrail-new` | Create any document type (suggests best fit) | +| `/devtrail-ailog` | Quick AILOG creation | +| `/devtrail-aidec` | Quick AIDEC creation | +| `/devtrail-adr` | Quick ADR creation | + +For full skill details, see the [README](../../README.md#skills). + +--- + +## Team Patterns + +### PR Reviews + +- Check that significant code changes include corresponding `.devtrail/` documents +- Review any documents with `review_required: true` +- Verify that AILOGs accurately describe what the AI did + +### Onboarding New Team Members + +1. Point them to `.devtrail/QUICK-REFERENCE.md` for a quick overview +2. Have them read recent ADRs to understand architectural context +3. Show them AILOGs from recent features to see how documentation works in practice + +### Sprint Retrospectives + +- Review AILOGs and AIDECs from the sprint to understand AI contribution patterns +- Identify undocumented decisions that should have been recorded +- Check TDE documents for accumulating technical debt + +### Shared AI Assistant Usage + +When multiple team members use AI assistants on the same project: + +- Each assistant session produces its own documents +- The `agent` field in metadata identifies which assistant created each document +- Review overlapping or contradictory AIDECs during PR review + +--- + +## Understanding Versions + +DevTrail uses **independent versioning** for its two components: + +| Component | Tag prefix | Contains | Updated via | +|-----------|-----------|----------|-------------| +| **Framework** | `fw-` | Templates, governance docs, directives, scripts | `devtrail update-framework` | +| **CLI** | `cli-` | The `devtrail` binary | `devtrail update-cli` | + +### Why Independent Versions? + +- Framework changes (new templates, updated rules) are more frequent +- CLI changes (new commands, bug fixes) follow a different cadence +- You can update governance docs without needing a new CLI binary + +### Checking Your Versions + +```bash +devtrail about # Quick version check +devtrail status # Full health report including versions +``` + +For detailed CLI information, see the [CLI Reference](CLI-REFERENCE.md#versioning). + +--- + +
+ +**DevTrail** — Because every change tells a story. + +[Back to docs](../README.md) • [README](../../README.md) • [Strange Days Tech](https://strangedays.tech) + +
diff --git a/docs/contributors/TRANSLATION-GUIDE.md b/docs/contributors/TRANSLATION-GUIDE.md index 357127b..c49aab6 100644 --- a/docs/contributors/TRANSLATION-GUIDE.md +++ b/docs/contributors/TRANSLATION-GUIDE.md @@ -60,7 +60,7 @@ The AI agent will then use Spanish templates when creating documentation. | Category | Files | Location | |----------|-------|----------| | **Main Documentation** | README.md, CONTRIBUTING.md, CODE_OF_CONDUCT.md | `docs/i18n/{lang}/` | -| **Adopter Documentation** | ADOPTION-GUIDE.md | `docs/i18n/{lang}/adopters/` | +| **Adopter Documentation** | ADOPTION-GUIDE.md, CLI-REFERENCE.md, WORKFLOWS.md | `docs/i18n/{lang}/adopters/` | | **Templates** | TEMPLATE-*.md (8 files) | `.devtrail/templates/i18n/{lang}/` | | **Governance** | PRINCIPLES.md, DOCUMENTATION-POLICY.md, AGENT-RULES.md, GIT-BRANCHING-STRATEGY.md, QUICK-REFERENCE.md | `.devtrail/00-governance/i18n/{lang}/` | diff --git a/docs/i18n/es/CONTRIBUTING.md b/docs/i18n/es/CONTRIBUTING.md index 6ecdd83..c86a22a 100644 --- a/docs/i18n/es/CONTRIBUTING.md +++ b/docs/i18n/es/CONTRIBUTING.md @@ -314,23 +314,27 @@ El binario de release está optimizado con LTO y stripped para tamaño mínimo. ``` cli/src/ -├── main.rs # Punto de entrada + definición CLI con clap +├── main.rs # Punto de entrada + definición CLI con clap ├── commands/ -│ ├── mod.rs # Enrutamiento de subcomandos -│ ├── init.rs # devtrail init [path] -│ ├── update.rs # devtrail update -│ ├── remove.rs # devtrail remove [--full] -│ ├── update_cli.rs # devtrail update-cli -│ └── about.rs # devtrail about -├── config.rs # Gestión de configuración y checksums -├── download.rs # Descarga desde GitHub Releases -├── inject.rs # Inyección de archivos de directiva (markers) -├── manifest.rs # Parsing de dist-manifest.yml -├── platform.rs # Detección de SO/arquitectura para descarga de binarios -├── self_update.rs # Lógica de auto-actualización del binario CLI -└── utils.rs # Helpers (hashing, colores, paths) +│ ├── mod.rs # Enrutamiento de subcomandos +│ ├── init.rs # devtrail init [path] +│ ├── update.rs # devtrail update (combinado) +│ ├── update_framework.rs # devtrail update-framework +│ ├── update_cli.rs # devtrail update-cli +│ ├── remove.rs # devtrail remove [--full] +│ ├── status.rs # devtrail status [path] +│ └── about.rs # devtrail about +├── config.rs # Gestión de configuración y checksums +├── download.rs # API de GitHub Releases (filtrado por prefijo) +├── inject.rs # Inyección de archivos de directiva (markers) +├── manifest.rs # Parsing de dist-manifest.yml +├── platform.rs # Detección de SO/arquitectura para descarga de binarios +├── self_update.rs # Lógica de auto-actualización del binario CLI +└── utils.rs # Helpers (hashing, colores, paths) ``` +> **Nota**: Framework y CLI usan versionado independiente (tags `fw-*` y `cli-*`). Ver [Referencia CLI](docs/adopters/CLI-REFERENCE.md#versioning) para detalles. + --- ## ¿Preguntas? diff --git a/docs/i18n/es/README.md b/docs/i18n/es/README.md index c819698..d0d1abc 100644 --- a/docs/i18n/es/README.md +++ b/docs/i18n/es/README.md @@ -123,30 +123,41 @@ devtrail init . El CLI descarga la última versión de DevTrail, configura el framework y los archivos de directivas de agentes IA automáticamente. -```bash -# Actualizar documentos DevTrail a la última versión -devtrail update +### Versionado -# Actualizar el binario del CLI -devtrail update-cli +DevTrail usa tags de versión independientes para cada componente: -# Mostrar información de autoría y licencia -devtrail about +| Componente | Prefijo de tag | Ejemplo | Incluye | +|------------|---------------|---------|---------| +| Framework | `fw-` | `fw-2.1.0` | Plantillas, gobernanza, directivas, scripts | +| CLI | `cli-` | `cli-1.0.0` | El binario `devtrail` | -# Eliminar DevTrail -devtrail remove -``` +Verifica las versiones instaladas con `devtrail status` o `devtrail about`. + +### Comandos CLI + +| Comando | Descripción | +|---------|-------------| +| `devtrail init [path]` | Inicializar DevTrail en un proyecto | +| `devtrail update` | Actualizar framework y CLI | +| `devtrail update-framework` | Actualizar solo el framework | +| `devtrail update-cli` | Actualizar el binario del CLI | +| `devtrail remove [--full]` | Eliminar DevTrail del proyecto | +| `devtrail status [path]` | Mostrar estado de la instalación y estadísticas | +| `devtrail about` | Mostrar información de versión y licencia | + +Ver [Referencia CLI](adopters/CLI-REFERENCE.md) para uso detallado. ### Opción 2: Configuración Manual ```bash -# Descargar el último release ZIP de GitHub -# https://github.com/StrangeDaysTech/devtrail/releases/latest +# Descargar el último release ZIP del framework desde GitHub +# Ve a https://github.com/StrangeDaysTech/devtrail/releases +# y descarga el último release fw-* (ej. fw-2.1.0) # Extraer y copiar a tu proyecto -cp -r .devtrail tu-proyecto/ -cp DEVTRAIL.md tu-proyecto/ -cp -r scripts tu-proyecto/ +unzip devtrail-fw-*.zip -d tu-proyecto/ +cd tu-proyecto # Commit git add .devtrail/ DEVTRAIL.md scripts/ @@ -166,7 +177,7 @@ La documentación de DevTrail está organizada por audiencia: | [**Adoptantes**](adopters/) | Equipos que adoptan DevTrail en sus proyectos | [ADOPTION-GUIDE.md](adopters/ADOPTION-GUIDE.md) | | [**Contribuidores**](../../../docs/contributors/) | Desarrolladores que contribuyen a DevTrail | [TRANSLATION-GUIDE.md](../../../docs/contributors/TRANSLATION-GUIDE.md) | -**Adoptantes**: Sigue la [Guía de Adopción](adopters/ADOPTION-GUIDE.md) para instrucciones paso a paso, estrategias de migración para proyectos existentes y planes de implementación en equipos. +**Adoptantes**: Sigue la [Guía de Adopción](adopters/ADOPTION-GUIDE.md) para instrucciones paso a paso, la [Referencia CLI](adopters/CLI-REFERENCE.md) para detalles de comandos, y la [Guía de Flujos de Trabajo](adopters/WORKFLOWS.md) para patrones de uso diario. **Contribuidores**: Consulta [CONTRIBUTING.md](CONTRIBUTING.md) para guías de desarrollo, y la [Guía de Traducción](../../../docs/contributors/TRANSLATION-GUIDE.md) para agregar nuevos idiomas. @@ -174,9 +185,11 @@ La documentación de DevTrail está organizada por audiencia: | Documento | Descripción | |-----------|-------------| -| [**📘 Referencia Rápida**](../../../dist/.devtrail/QUICK-REFERENCE.md) | Resumen de tipos de documentos y nomenclatura | +| [**Referencia Rápida**](../../../dist/.devtrail/QUICK-REFERENCE.md) | Resumen de tipos de documentos y nomenclatura | | [DEVTRAIL.md](../../../dist/DEVTRAIL.md) | Reglas de gobernanza unificadas (fuente de verdad) | | [ADOPTION-GUIDE.md](adopters/ADOPTION-GUIDE.md) | Guía de adopción para proyectos nuevos/existentes | +| [CLI-REFERENCE.md](adopters/CLI-REFERENCE.md) | Referencia completa de comandos CLI | +| [WORKFLOWS.md](adopters/WORKFLOWS.md) | Flujos de trabajo diarios y patrones de equipo | ### Estructura Interna diff --git a/docs/i18n/es/adopters/ADOPTION-GUIDE.md b/docs/i18n/es/adopters/ADOPTION-GUIDE.md index c986715..ab38fe4 100644 --- a/docs/i18n/es/adopters/ADOPTION-GUIDE.md +++ b/docs/i18n/es/adopters/ADOPTION-GUIDE.md @@ -196,11 +196,11 @@ El CLI automáticamente: 1. **Descargar el último release** - Ve a [GitHub Releases](https://github.com/StrangeDaysTech/devtrail/releases/latest) y descarga el ZIP de distribución. + Ve a [GitHub Releases](https://github.com/StrangeDaysTech/devtrail/releases) y descarga el último release `fw-*` (ej. `fw-2.1.0`). 2. **Extraer en tu proyecto** ```bash - unzip devtrail-v*.zip -d tu-proyecto/ + unzip devtrail-fw-*.zip -d tu-proyecto/ ``` 3. **Commit de la estructura** @@ -239,8 +239,8 @@ El CLI automáticamente: # Usando CLI (recomendado) devtrail init . - # O manualmente: descargar desde GitHub Releases - # https://github.com/StrangeDaysTech/devtrail/releases/latest + # O manualmente: descargar el último release fw-* desde GitHub Releases + # https://github.com/StrangeDaysTech/devtrail/releases ``` 2. **Resolver conflictos con `docs/` existente** @@ -501,6 +501,8 @@ R: Las reglas de DevTrail son instrucciones, no cumplimiento forzado. Si un asis ## Obtener Ayuda +- **Referencia CLI**: [CLI-REFERENCE.md](CLI-REFERENCE.md) — referencia detallada de comandos +- **Flujos de Trabajo**: [WORKFLOWS.md](WORKFLOWS.md) — patrones de uso diario recomendados - **Issues**: [GitHub Issues](https://github.com/StrangeDaysTech/devtrail/issues) - **Discusiones**: [GitHub Discussions](https://github.com/StrangeDaysTech/devtrail/discussions) - **Contribuir**: Ver [CONTRIBUTING.md](../CONTRIBUTING.md) diff --git a/docs/i18n/es/adopters/CLI-REFERENCE.md b/docs/i18n/es/adopters/CLI-REFERENCE.md new file mode 100644 index 0000000..29ea840 --- /dev/null +++ b/docs/i18n/es/adopters/CLI-REFERENCE.md @@ -0,0 +1,260 @@ +# DevTrail - Referencia CLI + +**Referencia completa de la herramienta de línea de comandos `devtrail`.** + +[![Strange Days Tech](https://img.shields.io/badge/by-Strange_Days_Tech-purple.svg)](https://strangedays.tech) + +**Idiomas**: [English](../../../adopters/CLI-REFERENCE.md) | Español + +--- + +## Tabla de Contenidos + +1. [Instalación](#instalación) +2. [Versionado](#versionado) +3. [Comandos](#comandos) +4. [Variables de Entorno](#variables-de-entorno) +5. [Códigos de Salida](#códigos-de-salida) + +--- + +## Instalación + +Instala el CLI de DevTrail usando uno de los métodos a continuación. Para instrucciones completas de configuración, consulta el [README](../README.md#inicio-rápido). + +**Instalación rápida (binario precompilado):** + +```bash +# Linux / macOS +curl -fsSL https://raw.githubusercontent.com/StrangeDaysTech/devtrail/main/install.sh | sh +``` + +```powershell +# Windows (PowerShell) +irm https://raw.githubusercontent.com/StrangeDaysTech/devtrail/main/install.ps1 | iex +``` + +**Desde el código fuente:** + +```bash +cargo install devtrail-cli +``` + +--- + +## Versionado + +DevTrail usa **tags de versión independientes** para cada componente: + +| Componente | Prefijo de tag | Ejemplo | Qué incluye | +|------------|---------------|---------|-------------| +| Framework | `fw-` | `fw-2.1.0` | Plantillas, docs de gobernanza, directivas, scripts | +| CLI | `cli-` | `cli-1.0.0` | El binario `devtrail` | + +Framework y CLI se publican de forma independiente. Una actualización del framework no requiere actualización del CLI, y viceversa. + +**Verificar versiones instaladas:** + +```bash +devtrail about # Muestra versión CLI + versión framework (si está instalado) +devtrail status # Muestra estado completo de la instalación incluyendo versiones +``` + +--- + +## Comandos + +### `devtrail init [path]` + +Inicializa DevTrail en un directorio de proyecto. + +**Argumentos:** + +| Argumento | Por defecto | Descripción | +|-----------|-------------|-------------| +| `path` | `.` (directorio actual) | Directorio del proyecto destino | + +**Qué hace:** + +1. Descarga el último release del framework (`fw-*`) desde GitHub +2. Crea la estructura de directorios `.devtrail/` +3. Crea `DEVTRAIL.md` con las reglas de gobernanza +4. Configura archivos de directivas de agentes IA (`CLAUDE.md`, `GEMINI.md`, `.cursorrules`, etc.) +5. Copia scripts de validación y workflows de CI/CD + +**Ejemplo:** + +```bash +$ devtrail init . +✔ Downloaded DevTrail fw-2.1.0 +✔ Created .devtrail/ directory structure +✔ Created DEVTRAIL.md +✔ Configured AI agent directives +✔ Copied validation scripts + +DevTrail initialized successfully! +Next: git add .devtrail/ DEVTRAIL.md scripts/ && git commit -m "chore: adopt DevTrail" +``` + +--- + +### `devtrail update` + +Actualiza **ambos** framework y CLI a sus últimas versiones. Equivale a ejecutar `update-framework` seguido de `update-cli`. + +Si `.devtrail/` no existe en el directorio actual, la actualización del framework se omite con una advertencia. + +**Ejemplo:** + +```bash +$ devtrail update +Updating framework... +✔ Framework updated to fw-2.1.0 +Updating CLI... +✔ CLI updated to cli-1.0.0 +``` + +--- + +### `devtrail update-framework` + +Actualiza solo los archivos del framework. Busca el último release `fw-*` en GitHub. + +**Manejo de conflictos:** Si has modificado archivos del framework (ej. docs de gobernanza o plantillas), la actualización preserva tus cambios y reporta conflictos para resolución manual. + +**Ejemplo:** + +```bash +$ devtrail update-framework +✔ Framework updated to fw-2.1.0 +``` + +--- + +### `devtrail update-cli` + +Auto-actualiza el binario `devtrail`. Busca el último release `cli-*` en GitHub y reemplaza el binario actual. + +**Ejemplo:** + +```bash +$ devtrail update-cli +✔ CLI updated to cli-1.0.0 +``` + +--- + +### `devtrail remove [--full]` + +Elimina DevTrail del proyecto actual. + +**Flags:** + +| Flag | Descripción | +|------|-------------| +| `--full` | Elimina todo, incluyendo documentos creados por el usuario en `.devtrail/`. Pide confirmación. | + +**Comportamiento por defecto** (sin `--full`): elimina la estructura del framework pero preserva los documentos que creaste dentro de `.devtrail/`. + +**Ejemplo:** + +```bash +$ devtrail remove +✔ DevTrail framework removed. User documents preserved in .devtrail/. + +$ devtrail remove --full +⚠ This will delete all DevTrail files including your documents. +Continue? [y/N]: y +✔ DevTrail completely removed. +``` + +--- + +### `devtrail status [path]` + +Muestra el estado de la instalación y estadísticas de documentación. + +**Argumentos:** + +| Argumento | Por defecto | Descripción | +|-----------|-------------|-------------| +| `path` | `.` (directorio actual) | Directorio del proyecto destino | + +**La salida incluye:** + +- Ruta del proyecto +- Versión del framework +- Versión del CLI +- Idioma configurado +- Integridad de la estructura de directorios +- Estadísticas de documentos (conteo por tipo) + +**Ejemplo:** + +```bash +$ devtrail status +DevTrail Status +─────────────── +Path: /home/user/my-project +Framework version: fw-2.1.0 +CLI version: cli-1.0.0 +Language: en +Structure: ✔ Complete + +Documents: + AILOG: 12 + AIDEC: 4 + ADR: 7 + REQ: 3 + TES: 2 + TDE: 1 + INC: 0 + ETH: 1 + Total: 30 +``` + +--- + +### `devtrail about` + +Muestra información de versión, autoría y licencia. + +**Ejemplo:** + +```bash +$ devtrail about +DevTrail CLI + CLI version: cli-1.0.0 + Framework version: fw-2.1.0 + Author: Strange Days Tech, S.A.S. + License: MIT + Repository: https://github.com/StrangeDaysTech/devtrail + Website: https://strangedays.tech +``` + +--- + +## Variables de Entorno + +| Variable | Descripción | +|----------|-------------| +| `GITHUB_TOKEN` | Token de acceso personal de GitHub para solicitudes autenticadas a la API. Útil para evitar límites de tasa al descargar releases. | + +--- + +## Códigos de Salida + +| Código | Significado | +|--------|-------------| +| `0` | Éxito | +| `1` | Error (detalles impresos en stderr) | + +--- + +
+ +**DevTrail** — Porque cada cambio cuenta una historia. + +[Volver a docs](../../README.md) • [README](../README.md) • [Strange Days Tech](https://strangedays.tech) + +
diff --git a/docs/i18n/es/adopters/WORKFLOWS.md b/docs/i18n/es/adopters/WORKFLOWS.md new file mode 100644 index 0000000..800d8d1 --- /dev/null +++ b/docs/i18n/es/adopters/WORKFLOWS.md @@ -0,0 +1,192 @@ +# DevTrail - Flujos de Trabajo Recomendados + +**Patrones y cadencias para usar DevTrail en el día a día.** + +[![Strange Days Tech](https://img.shields.io/badge/by-Strange_Days_Tech-purple.svg)](https://strangedays.tech) + +**Idiomas**: [English](../../../adopters/WORKFLOWS.md) | Español + +--- + +## Tabla de Contenidos + +1. [Después de la Configuración Inicial](#después-de-la-configuración-inicial) +2. [Desarrollo Diario](#desarrollo-diario) +3. [Mantener DevTrail Actualizado](#mantener-devtrail-actualizado) +4. [Verificar el Estado del Proyecto](#verificar-el-estado-del-proyecto) +5. [Usar Skills (Documentación Activa)](#usar-skills-documentación-activa) +6. [Patrones de Equipo](#patrones-de-equipo) +7. [Entender las Versiones](#entender-las-versiones) + +--- + +## Después de la Configuración Inicial + +Ejecutaste `devtrail init .` e hiciste commit del resultado. ¿Ahora qué? + +1. **Abre tu proyecto** con tu asistente de codificación IA (Claude Code, Cursor, Gemini CLI, etc.) +2. El asistente **leerá automáticamente** las directivas de DevTrail (`CLAUDE.md`, `GEMINI.md`, etc.) +3. A partir de este punto, el asistente **crea documentación** en `.devtrail/` como parte de su flujo de trabajo normal +4. **No se necesita configuración adicional** — DevTrail funciona de forma pasiva a través de los archivos de directivas + +--- + +## Desarrollo Diario + +### El Ciclo Pasivo + +1. Trabaja normalmente con tu asistente IA — escribe features, corrige bugs, refactoriza +2. La IA crea documentos en `.devtrail/` según las reglas de gobernanza: + - **AILOG** para implementaciones significativas (>10 líneas cambiadas) + - **AIDEC** al elegir entre alternativas + - **ADR** para decisiones arquitectónicas + - **ETH** cuando surgen preocupaciones éticas +3. Revisa los documentos marcados con `review_required: true` +4. Haz commit de la documentación junto con los cambios de código correspondientes + +### Cuándo Crear Documentos Manualmente + +Usa el sistema activo (skills) cuando: + +- La IA omitió documentar un cambio significativo +- Tú (un humano) tomaste una decisión que debería registrarse +- Quieres crear un documento REQ, TES, TDE o INC +- Quieres verificar el cumplimiento de documentación + +--- + +## Mantener DevTrail Actualizado + +### Cadencia Recomendada + +- **Mensualmente** o cuando veas un nuevo release en GitHub +- Consulta la [página de releases](https://github.com/StrangeDaysTech/devtrail/releases) para changelogs + +### Comandos de Actualización + +| Objetivo | Comando | +|----------|---------| +| Actualizar framework y CLI | `devtrail update` | +| Actualizar solo plantillas y docs de gobernanza | `devtrail update-framework` | +| Actualizar solo el binario CLI | `devtrail update-cli` | + +Framework y CLI tienen **versiones independientes** — puedes actualizar uno sin el otro. Ver [Entender las Versiones](#entender-las-versiones). + +### Después de Actualizar + +1. Revisa los cambios en archivos de directivas y docs de gobernanza +2. Haz commit de los archivos actualizados: `git add .devtrail/ && git commit -m "chore: update DevTrail framework"` +3. Si personalizaste archivos del framework, verifica si hay conflictos + +--- + +## Verificar el Estado del Proyecto + +### Estado via CLI + +```bash +devtrail status +``` + +Muestra: versión del framework, versión del CLI, integridad de la estructura de directorios y estadísticas de documentos por tipo. Úsalo para verificar que la instalación está saludable. + +### Cumplimiento de Documentación (Skill) + +```bash +/devtrail-status +``` + +El skill `/devtrail-status` (disponible en Claude Code y Gemini CLI) analiza: + +- Qué cambios de código recientes carecen de documentación correspondiente +- Cumplimiento de documentos contra las reglas de gobernanza +- Estado general de documentación + +--- + +## Usar Skills (Documentación Activa) + +DevTrail tiene dos sistemas de documentación: + +| Sistema | Cómo funciona | Cuándo usar | +|---------|---------------|-------------| +| **Pasivo** | La IA auto-documenta via archivos de directivas | Por defecto — sucede automáticamente | +| **Activo** | El usuario invoca skills para crear docs | Cuando el pasivo omitió algo, o para decisiones humanas | + +### Skills Disponibles + +| Skill | Propósito | +|-------|-----------| +| `/devtrail-status` | Verificar cumplimiento de documentación | +| `/devtrail-new` | Crear cualquier tipo de documento (sugiere el más adecuado) | +| `/devtrail-ailog` | Creación rápida de AILOG | +| `/devtrail-aidec` | Creación rápida de AIDEC | +| `/devtrail-adr` | Creación rápida de ADR | + +Para detalles completos de skills, consulta el [README](../README.md#skills). + +--- + +## Patrones de Equipo + +### Revisión de PRs + +- Verifica que los cambios de código significativos incluyan documentos correspondientes en `.devtrail/` +- Revisa cualquier documento con `review_required: true` +- Verifica que los AILOGs describan con precisión lo que hizo la IA + +### Onboarding de Nuevos Miembros + +1. Apúntalos a `.devtrail/QUICK-REFERENCE.md` para una vista rápida +2. Pídeles que lean los ADRs recientes para entender el contexto arquitectónico +3. Muéstrales AILOGs de features recientes para ver cómo funciona la documentación en la práctica + +### Retrospectivas de Sprint + +- Revisa AILOGs y AIDECs del sprint para entender patrones de contribución de la IA +- Identifica decisiones no documentadas que deberían haberse registrado +- Revisa documentos TDE para deuda técnica acumulada + +### Uso Compartido de Asistentes IA + +Cuando múltiples miembros del equipo usan asistentes IA en el mismo proyecto: + +- Cada sesión de asistente produce sus propios documentos +- El campo `agent` en los metadatos identifica qué asistente creó cada documento +- Revisa AIDECs superpuestos o contradictorios durante la revisión de PRs + +--- + +## Entender las Versiones + +DevTrail usa **versionado independiente** para sus dos componentes: + +| Componente | Prefijo de tag | Contiene | Se actualiza con | +|------------|---------------|----------|-----------------| +| **Framework** | `fw-` | Plantillas, docs de gobernanza, directivas, scripts | `devtrail update-framework` | +| **CLI** | `cli-` | El binario `devtrail` | `devtrail update-cli` | + +### ¿Por Qué Versiones Independientes? + +- Los cambios de framework (nuevas plantillas, reglas actualizadas) son más frecuentes +- Los cambios de CLI (nuevos comandos, corrección de bugs) siguen una cadencia diferente +- Puedes actualizar docs de gobernanza sin necesitar un nuevo binario del CLI + +### Verificar Tus Versiones + +```bash +devtrail about # Verificación rápida de versiones +devtrail status # Reporte completo de salud incluyendo versiones +``` + +Para información detallada del CLI, consulta la [Referencia CLI](CLI-REFERENCE.md#versionado). + +--- + +
+ +**DevTrail** — Porque cada cambio cuenta una historia. + +[Volver a docs](../../README.md) • [README](../README.md) • [Strange Days Tech](https://strangedays.tech) + +