From a17b7f5ada7ded83ce641eb7831901e47b1f7b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Villase=C3=B1or=20Montfort?= <195970+montfort@users.noreply.github.com> Date: Tue, 19 May 2026 19:29:09 -0600 Subject: [PATCH 1/2] docs: add StrayMark/AI notice + Copyright section to README; SPDX headers on sources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - README: add "Built with AI · Powered by StrayMark" and "Copyright" sections mirroring the format used in StrangeDaysTech/arborist-metrics, adapted to this CLI's surface (clap, table/JSON/CSV output, self-update, 29-test integration suite). - Add SPDX-License-Identifier + Copyright header to every Rust source under src/ and tests/cli/ (in line with the Rust ecosystem convention and the identical change made in arborist-metrics v0.1.3). - tests/fixtures/*.rs intentionally left untouched — those files are inputs to the analyzer in integration tests; modifying them would change the expected metrics. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 27 +++++++++++++++++++++++++++ src/about.rs | 5 ++++- src/analysis.rs | 5 ++++- src/cli.rs | 5 ++++- src/error.rs | 5 ++++- src/lib.rs | 5 ++++- src/main.rs | 5 ++++- src/output/csv_output.rs | 5 ++++- src/output/json.rs | 5 ++++- src/output/mod.rs | 5 ++++- src/output/table.rs | 5 ++++- src/traversal.rs | 5 ++++- src/update.rs | 5 ++++- tests/cli/directory.rs | 5 ++++- tests/cli/exit_codes.rs | 5 ++++- tests/cli/filtering.rs | 5 ++++- tests/cli/main.rs | 5 ++++- tests/cli/multi_input.rs | 5 ++++- tests/cli/output_formats.rs | 5 ++++- tests/cli/single_file.rs | 5 ++++- tests/cli/stdin.rs | 5 ++++- 21 files changed, 107 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index ffe7ab4..5ad3b9c 100644 --- a/README.md +++ b/README.md @@ -296,6 +296,23 @@ File bugs and feature requests at [GitHub Issues](https://github.com/StrangeDays We welcome patches! Please read [CONTRIBUTING.md](CONTRIBUTING.md) before submitting a PR. All contributors must sign the **Contributor License Agreement (CLA)** — the CLA bot will guide you on your first pull request. +## Built with AI · Powered by StrayMark + +`arborist-cli` is an experiment in **disciplined AI-assisted development**. +The implementation — the `clap`-derived CLI surface, the three output +formats (table / JSON / CSV), the directory traversal layer, self-update +from GitHub Releases, and the 29-test integration suite — was authored +largely by AI agents under human direction. + +To make that velocity sustainable, we use **[StrayMark](https://straymark.dev)**: +a CLI for *cognitive discipline in AI-assisted engineering*. It turned every +architectural choice into an AIDEC record and every implementation block into +an AILOG — all under `.straymark/`, append-only and audit-ready. The +governance artifacts emerged **alongside** the code, not as homework after. + +> StrayMark is built by Strange Days Tech — the same team behind `arborist-cli`. +> It is the tool we made to solve our own problem. + ## License Licensed under either of @@ -306,3 +323,13 @@ Licensed under either of at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +--- + +## Copyright + +**`arborist-cli`** is © 2026 **[Strange Days Tech S.A.S. de C.V.](https://strangedays.tech/)** — original author and intellectual-property holder of the source code. + +The CLI is released under the dual MIT / Apache-2.0 license above; this notice records authorship and does **not** modify those license terms. Each source file carries an SPDX header reflecting the same. + +> Built by **[Strange Days Tech](https://strangedays.tech/)** — México. diff --git a/src/about.rs b/src/about.rs index 4fe0f69..46aafe3 100644 --- a/src/about.rs +++ b/src/about.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + pub fn print() { let version = env!("CARGO_PKG_VERSION"); let description = env!("CARGO_PKG_DESCRIPTION"); @@ -11,4 +14,4 @@ pub fn print() { println!(" Repo: https://github.com/StrangeDaysTech/arborist-cli"); println!(" Web: https://strangedays.tech"); println!(); -} +} \ No newline at end of file diff --git a/src/analysis.rs b/src/analysis.rs index 2cb98f3..0282627 100644 --- a/src/analysis.rs +++ b/src/analysis.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use std::io::Read; use std::path::{Path, PathBuf}; @@ -129,4 +132,4 @@ pub fn sort_and_top( if let Some(n) = top { flat.truncate(n); } -} +} \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs index c85b14e..41d47fb 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use std::path::PathBuf; use clap::{Parser, Subcommand, ValueEnum}; @@ -84,4 +87,4 @@ pub struct AnalyzeArgs { /// Exclude method-level analysis #[arg(long)] pub no_methods: bool, -} +} \ No newline at end of file diff --git a/src/error.rs b/src/error.rs index e35340b..3f28455 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use std::process::ExitCode; #[derive(Debug, thiserror::Error)] @@ -30,4 +33,4 @@ impl ExitReport { ExitCode::SUCCESS } } -} +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index b7f9f31..0cbbdea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + pub mod about; pub mod analysis; pub mod cli; @@ -68,4 +71,4 @@ mod atty { } } } -} +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 532396a..9faa784 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use std::process::ExitCode; use clap::Parser; @@ -21,4 +24,4 @@ fn main() -> ExitCode { } }, } -} +} \ No newline at end of file diff --git a/src/output/csv_output.rs b/src/output/csv_output.rs index 4be77d1..adbe178 100644 --- a/src/output/csv_output.rs +++ b/src/output/csv_output.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use std::io; use arborist::FileReport; @@ -74,4 +77,4 @@ pub fn write_flat(flat: &[FlatFunction]) -> Result<(), ArboristError> { wtr.flush()?; Ok(()) -} +} \ No newline at end of file diff --git a/src/output/json.rs b/src/output/json.rs index ebed1ab..610b67b 100644 --- a/src/output/json.rs +++ b/src/output/json.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use std::io::{self, Write}; use arborist::FileReport; @@ -49,4 +52,4 @@ pub fn write_flat(flat: &[FlatFunction]) -> Result<(), ArboristError> { serde_json::to_string_pretty(&items).map_err(|e| ArboristError::Analysis(e.to_string()))?; writeln!(out, "{json}")?; Ok(()) -} +} \ No newline at end of file diff --git a/src/output/mod.rs b/src/output/mod.rs index bf7b669..98ac2cf 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + pub mod csv_output; pub mod json; pub mod table; @@ -44,4 +47,4 @@ pub fn write_output( OutputFormat::Csv => csv_output::write_reports(reports), } } -} +} \ No newline at end of file diff --git a/src/output/table.rs b/src/output/table.rs index 7c75960..5eb4519 100644 --- a/src/output/table.rs +++ b/src/output/table.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use std::io::{self, IsTerminal, Write}; use arborist::FileReport; @@ -112,4 +115,4 @@ pub fn write_flat(flat: &[FlatFunction], args: &AnalyzeArgs) -> Result<(), Arbor writeln!(out, "{table}")?; Ok(()) -} +} \ No newline at end of file diff --git a/src/traversal.rs b/src/traversal.rs index 39da9cc..3aa8096 100644 --- a/src/traversal.rs +++ b/src/traversal.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use std::path::{Path, PathBuf}; use ignore::WalkBuilder; @@ -94,4 +97,4 @@ fn collect_directory( files.sort(); Ok(()) -} +} \ No newline at end of file diff --git a/src/update.rs b/src/update.rs index 099a9c4..1d82b81 100644 --- a/src/update.rs +++ b/src/update.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use std::process::ExitCode; const CURRENT_VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -102,4 +105,4 @@ fn is_cargo_installed() -> bool { }; let path = exe.to_string_lossy(); path.contains(".cargo/bin") || path.contains(".cargo\\bin") -} +} \ No newline at end of file diff --git a/tests/cli/directory.rs b/tests/cli/directory.rs index d928508..45594fb 100644 --- a/tests/cli/directory.rs +++ b/tests/cli/directory.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use assert_cmd::Command; use predicates::prelude::*; @@ -72,4 +75,4 @@ fn directory_no_recognized_files() { ]) .assert() .success(); -} +} \ No newline at end of file diff --git a/tests/cli/exit_codes.rs b/tests/cli/exit_codes.rs index 2c4d60e..271adf4 100644 --- a/tests/cli/exit_codes.rs +++ b/tests/cli/exit_codes.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use assert_cmd::Command; fn cmd() -> Command { @@ -42,4 +45,4 @@ fn exit_code_error_precedence_over_threshold() { #[test] fn exit_code_2_on_error() { cmd().arg("nonexistent.rs").assert().code(2); -} +} \ No newline at end of file diff --git a/tests/cli/filtering.rs b/tests/cli/filtering.rs index 642ff0c..c5ea4e1 100644 --- a/tests/cli/filtering.rs +++ b/tests/cli/filtering.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use assert_cmd::Command; use predicates::prelude::*; @@ -173,4 +176,4 @@ impl AssertExt for assert_cmd::assert::Assert { fn success_or(self, code: predicates::ord::EqPredicate) -> assert_cmd::assert::Assert { self.code(predicate::eq(0).or(code)) } -} +} \ No newline at end of file diff --git a/tests/cli/main.rs b/tests/cli/main.rs index 2d15060..f86a986 100644 --- a/tests/cli/main.rs +++ b/tests/cli/main.rs @@ -1,7 +1,10 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + mod directory; mod exit_codes; mod filtering; mod multi_input; mod output_formats; mod single_file; -mod stdin; +mod stdin; \ No newline at end of file diff --git a/tests/cli/multi_input.rs b/tests/cli/multi_input.rs index 1204b80..3d85aef 100644 --- a/tests/cli/multi_input.rs +++ b/tests/cli/multi_input.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use assert_cmd::Command; use predicates::prelude::*; @@ -16,4 +19,4 @@ fn multi_file_input() { .stdout(predicate::str::contains("Rust")) .stdout(predicate::str::contains("simple.py")) .stdout(predicate::str::contains("Python")); -} +} \ No newline at end of file diff --git a/tests/cli/output_formats.rs b/tests/cli/output_formats.rs index 3f1d9e3..965809e 100644 --- a/tests/cli/output_formats.rs +++ b/tests/cli/output_formats.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use assert_cmd::Command; use predicates::prelude::*; @@ -92,4 +95,4 @@ fn csv_no_functions_header_only() { let lines: Vec<&str> = stdout.lines().collect(); assert_eq!(lines.len(), 1, "expected header only, got {lines:?}"); assert!(lines[0].starts_with("file,language,function")); -} +} \ No newline at end of file diff --git a/tests/cli/single_file.rs b/tests/cli/single_file.rs index c093ae6..c3c5a7e 100644 --- a/tests/cli/single_file.rs +++ b/tests/cli/single_file.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use assert_cmd::Command; use predicates::prelude::*; @@ -64,4 +67,4 @@ fn syntax_error_partial_parsing() { .assert() .success() .stdout(predicate::str::contains("valid_function")); -} +} \ No newline at end of file diff --git a/tests/cli/stdin.rs b/tests/cli/stdin.rs index 0dd9b46..f2aea31 100644 --- a/tests/cli/stdin.rs +++ b/tests/cli/stdin.rs @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +// Copyright (c) 2026 Strange Days Tech S.A.S. de C.V. + use assert_cmd::Command; use predicates::prelude::*; @@ -30,4 +33,4 @@ fn stdin_no_language_error() { .assert() .code(2) .stderr(predicate::str::contains("--language")); -} +} \ No newline at end of file From c6262af2268f897d80533d3b78b9b17c9e8fe0e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Villase=C3=B1or=20Montfort?= <195970+montfort@users.noreply.github.com> Date: Tue, 19 May 2026 19:34:26 -0600 Subject: [PATCH 2/2] style: restore trailing newlines stripped by SPDX header script The shell loop that prepended the SPDX header used `$(cat ...)` which strips trailing newlines, leaving every patched file without its final `\n`. cargo fmt --check (run in CI) flagged all 20 files. Running cargo fmt restores the trailing newline; no semantic changes. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/about.rs | 2 +- src/analysis.rs | 2 +- src/cli.rs | 2 +- src/error.rs | 2 +- src/lib.rs | 2 +- src/main.rs | 2 +- src/output/csv_output.rs | 2 +- src/output/json.rs | 2 +- src/output/mod.rs | 2 +- src/output/table.rs | 2 +- src/traversal.rs | 2 +- src/update.rs | 2 +- tests/cli/directory.rs | 2 +- tests/cli/exit_codes.rs | 2 +- tests/cli/filtering.rs | 2 +- tests/cli/main.rs | 2 +- tests/cli/multi_input.rs | 2 +- tests/cli/output_formats.rs | 2 +- tests/cli/single_file.rs | 2 +- tests/cli/stdin.rs | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/about.rs b/src/about.rs index 46aafe3..8b1f9ec 100644 --- a/src/about.rs +++ b/src/about.rs @@ -14,4 +14,4 @@ pub fn print() { println!(" Repo: https://github.com/StrangeDaysTech/arborist-cli"); println!(" Web: https://strangedays.tech"); println!(); -} \ No newline at end of file +} diff --git a/src/analysis.rs b/src/analysis.rs index 0282627..a3a3c2a 100644 --- a/src/analysis.rs +++ b/src/analysis.rs @@ -132,4 +132,4 @@ pub fn sort_and_top( if let Some(n) = top { flat.truncate(n); } -} \ No newline at end of file +} diff --git a/src/cli.rs b/src/cli.rs index 41d47fb..6cbbaaa 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -87,4 +87,4 @@ pub struct AnalyzeArgs { /// Exclude method-level analysis #[arg(long)] pub no_methods: bool, -} \ No newline at end of file +} diff --git a/src/error.rs b/src/error.rs index 3f28455..eff5998 100644 --- a/src/error.rs +++ b/src/error.rs @@ -33,4 +33,4 @@ impl ExitReport { ExitCode::SUCCESS } } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 0cbbdea..43196d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,4 +71,4 @@ mod atty { } } } -} \ No newline at end of file +} diff --git a/src/main.rs b/src/main.rs index 9faa784..a76e925 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,4 +24,4 @@ fn main() -> ExitCode { } }, } -} \ No newline at end of file +} diff --git a/src/output/csv_output.rs b/src/output/csv_output.rs index adbe178..99dcba7 100644 --- a/src/output/csv_output.rs +++ b/src/output/csv_output.rs @@ -77,4 +77,4 @@ pub fn write_flat(flat: &[FlatFunction]) -> Result<(), ArboristError> { wtr.flush()?; Ok(()) -} \ No newline at end of file +} diff --git a/src/output/json.rs b/src/output/json.rs index 610b67b..a6df411 100644 --- a/src/output/json.rs +++ b/src/output/json.rs @@ -52,4 +52,4 @@ pub fn write_flat(flat: &[FlatFunction]) -> Result<(), ArboristError> { serde_json::to_string_pretty(&items).map_err(|e| ArboristError::Analysis(e.to_string()))?; writeln!(out, "{json}")?; Ok(()) -} \ No newline at end of file +} diff --git a/src/output/mod.rs b/src/output/mod.rs index 98ac2cf..47f59eb 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -47,4 +47,4 @@ pub fn write_output( OutputFormat::Csv => csv_output::write_reports(reports), } } -} \ No newline at end of file +} diff --git a/src/output/table.rs b/src/output/table.rs index 5eb4519..09ca5ce 100644 --- a/src/output/table.rs +++ b/src/output/table.rs @@ -115,4 +115,4 @@ pub fn write_flat(flat: &[FlatFunction], args: &AnalyzeArgs) -> Result<(), Arbor writeln!(out, "{table}")?; Ok(()) -} \ No newline at end of file +} diff --git a/src/traversal.rs b/src/traversal.rs index 3aa8096..63e623e 100644 --- a/src/traversal.rs +++ b/src/traversal.rs @@ -97,4 +97,4 @@ fn collect_directory( files.sort(); Ok(()) -} \ No newline at end of file +} diff --git a/src/update.rs b/src/update.rs index 1d82b81..82a138a 100644 --- a/src/update.rs +++ b/src/update.rs @@ -105,4 +105,4 @@ fn is_cargo_installed() -> bool { }; let path = exe.to_string_lossy(); path.contains(".cargo/bin") || path.contains(".cargo\\bin") -} \ No newline at end of file +} diff --git a/tests/cli/directory.rs b/tests/cli/directory.rs index 45594fb..ba94a12 100644 --- a/tests/cli/directory.rs +++ b/tests/cli/directory.rs @@ -75,4 +75,4 @@ fn directory_no_recognized_files() { ]) .assert() .success(); -} \ No newline at end of file +} diff --git a/tests/cli/exit_codes.rs b/tests/cli/exit_codes.rs index 271adf4..c5bdf43 100644 --- a/tests/cli/exit_codes.rs +++ b/tests/cli/exit_codes.rs @@ -45,4 +45,4 @@ fn exit_code_error_precedence_over_threshold() { #[test] fn exit_code_2_on_error() { cmd().arg("nonexistent.rs").assert().code(2); -} \ No newline at end of file +} diff --git a/tests/cli/filtering.rs b/tests/cli/filtering.rs index c5ea4e1..ea5c278 100644 --- a/tests/cli/filtering.rs +++ b/tests/cli/filtering.rs @@ -176,4 +176,4 @@ impl AssertExt for assert_cmd::assert::Assert { fn success_or(self, code: predicates::ord::EqPredicate) -> assert_cmd::assert::Assert { self.code(predicate::eq(0).or(code)) } -} \ No newline at end of file +} diff --git a/tests/cli/main.rs b/tests/cli/main.rs index f86a986..7839ebb 100644 --- a/tests/cli/main.rs +++ b/tests/cli/main.rs @@ -7,4 +7,4 @@ mod filtering; mod multi_input; mod output_formats; mod single_file; -mod stdin; \ No newline at end of file +mod stdin; diff --git a/tests/cli/multi_input.rs b/tests/cli/multi_input.rs index 3d85aef..1d266b6 100644 --- a/tests/cli/multi_input.rs +++ b/tests/cli/multi_input.rs @@ -19,4 +19,4 @@ fn multi_file_input() { .stdout(predicate::str::contains("Rust")) .stdout(predicate::str::contains("simple.py")) .stdout(predicate::str::contains("Python")); -} \ No newline at end of file +} diff --git a/tests/cli/output_formats.rs b/tests/cli/output_formats.rs index 965809e..d35a26a 100644 --- a/tests/cli/output_formats.rs +++ b/tests/cli/output_formats.rs @@ -95,4 +95,4 @@ fn csv_no_functions_header_only() { let lines: Vec<&str> = stdout.lines().collect(); assert_eq!(lines.len(), 1, "expected header only, got {lines:?}"); assert!(lines[0].starts_with("file,language,function")); -} \ No newline at end of file +} diff --git a/tests/cli/single_file.rs b/tests/cli/single_file.rs index c3c5a7e..78606e0 100644 --- a/tests/cli/single_file.rs +++ b/tests/cli/single_file.rs @@ -67,4 +67,4 @@ fn syntax_error_partial_parsing() { .assert() .success() .stdout(predicate::str::contains("valid_function")); -} \ No newline at end of file +} diff --git a/tests/cli/stdin.rs b/tests/cli/stdin.rs index f2aea31..19120eb 100644 --- a/tests/cli/stdin.rs +++ b/tests/cli/stdin.rs @@ -33,4 +33,4 @@ fn stdin_no_language_error() { .assert() .code(2) .stderr(predicate::str::contains("--language")); -} \ No newline at end of file +}