Add AFS Studio CLI helpers and versioning

This commit is contained in:
scawful
2025-12-30 18:09:34 -05:00
parent b28c092b3c
commit 67294096ed
12 changed files with 365 additions and 32 deletions

View File

@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.15)
project(afs_studio LANGUAGES CXX)
project(afs_studio VERSION 0.0.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -131,6 +131,7 @@ target_include_directories(afs_studio PRIVATE
target_compile_definitions(afs_studio PRIVATE
IMGUI_IMPL_OPENGL_LOADER_GLAD=0
AFS_STUDIO_VERSION="${PROJECT_VERSION}"
)
if(TARGET OpenGL::OpenGL)

View File

@@ -5,15 +5,39 @@ Native C++17 visualization and training management application for AFS.
## Build
```bash
# From project root
cmake -B build -S . -DAFS_BUILD_STUDIO=ON
cmake --build build --target afs_studio
# From AFS repo root
cmake -S apps/studio -B build/studio
cmake --build build/studio --target afs_studio
```
## Run
```bash
./build/apps/studio/afs_studio
./build/studio/afs_studio --data ~/src/training
```
## Install (local)
```bash
cmake --install build/studio --prefix ~/.local
# Ensure ~/.local/bin is on PATH
```
## CLI helpers
```bash
python -m afs studio build
python -m afs studio run --build
python -m afs studio install --prefix ~/.local
python -m afs studio alias
```
## Quick aliases
```bash
export AFS_ROOT=~/src/trunk/lab/afs
alias afs-studio='PYTHONPATH="$AFS_ROOT/src" python -m afs studio run --build'
alias afs-studio-build='PYTHONPATH="$AFS_ROOT/src" python -m afs studio build'
```
## Data sources
@@ -23,6 +47,11 @@ cmake --build build --target afs_studio
- Dataset registry: `AFS_DATASET_REGISTRY` or `${AFS_TRAINING_ROOT}/index/dataset_registry.json`.
- Resource index: `AFS_RESOURCE_INDEX` or `${AFS_TRAINING_ROOT}/index/resource_index.json`.
## Flags
- `--data` or `--data-path`: override training root
- `--version`: print version and exit
## Features
- **Dashboard**: Training metrics overview

View File

@@ -1,7 +1,8 @@
#include "app.h"
#include "core/logger.h"
#include "core/context.h"
#include "core/assets.h"
#include "core/context.h"
#include "core/logger.h"
#include "core/version.h"
#include "ui/panels/chat_panel.h"
#include <algorithm>
@@ -41,6 +42,9 @@ App::App(const std::string& data_path)
: data_path_(data_path), loader_(data_path) {
LOG_INFO("AFS Studio initialize with data path: " + data_path);
state_.studio_version = studio::core::StudioVersion();
state_.studio_data_root = data_path_;
std::snprintf(state_.new_agent_role.data(), state_.new_agent_role.size(), "Evaluator");
std::snprintf(state_.new_mission_owner.data(), state_.new_mission_owner.size(), "Ops");
std::snprintf(state_.system_prompt.data(), state_.system_prompt.size(),
@@ -69,7 +73,12 @@ App::App(const std::string& data_path)
SeedDefaultState();
// Create graphics context
context_ = std::make_unique<studio::core::GraphicsContext>("AFS Studio", 1400, 900);
std::string title = "AFS Studio";
if (!state_.studio_version.empty()) {
title += " ";
title += state_.studio_version;
}
context_ = std::make_unique<studio::core::GraphicsContext>(title, 1400, 900);
if (context_->IsValid()) {
fonts_ = studio::core::AssetLoader::LoadFonts();
themes::ApplyHafsTheme();

View File

@@ -0,0 +1,15 @@
#pragma once
namespace afs {
namespace studio {
namespace core {
#ifndef AFS_STUDIO_VERSION
#define AFS_STUDIO_VERSION "0.0.0"
#endif
inline const char* StudioVersion() { return AFS_STUDIO_VERSION; }
} // namespace core
} // namespace studio
} // namespace afs

View File

@@ -41,10 +41,14 @@ std::optional<std::filesystem::path> ResolveHafsScawfulRoot() {
auto trunk_root = ResolveTrunkRoot();
if (trunk_root) {
auto candidate = *trunk_root / "scawful" / "research" / "afs_scawful";
auto candidate = *trunk_root / "lab" / "afs_scawful";
if (studio::core::FileSystem::Exists(candidate)) {
return candidate;
}
auto legacy = *trunk_root / "scawful" / "research" / "afs_scawful";
if (studio::core::FileSystem::Exists(legacy)) {
return legacy;
}
}
return std::nullopt;
@@ -1019,9 +1023,15 @@ void DataLoader::MountDrive(const std::string& name) {
} else {
auto trunk_root = ResolveTrunkRoot();
if (trunk_root) {
script_path = *trunk_root / "scawful" / "research" / "afs_scawful" / "scripts" / "mount_windows.sh";
script_path = *trunk_root / "lab" / "afs_scawful" / "scripts" / "mount_windows.sh";
if (!studio::core::FileSystem::Exists(script_path)) {
script_path = *trunk_root / "scawful" / "research" / "afs_scawful" / "scripts" / "mount_windows.sh";
}
} else {
script_path = studio::core::FileSystem::ResolvePath("~/src/trunk/scawful/research/afs_scawful/scripts/mount_windows.sh");
script_path = studio::core::FileSystem::ResolvePath("~/src/trunk/lab/afs_scawful/scripts/mount_windows.sh");
if (!studio::core::FileSystem::Exists(script_path)) {
script_path = studio::core::FileSystem::ResolvePath("~/src/trunk/scawful/research/afs_scawful/scripts/mount_windows.sh");
}
}
}

View File

@@ -1,11 +1,12 @@
/// AFS Training Data Visualization - Main Entry Point
/// AFS Studio - Main Entry Point
///
/// Usage: afs_viz [data_path]
/// data_path: Path to training data directory (default: ~/src/training if present)
/// Usage: afs_studio [--data PATH]
/// --data PATH: Path to training data directory (default: ~/src/training if present)
/// --version: Print version and exit
///
/// Build:
/// cmake -B build -S src/cc -DAFS_BUILD_VIZ=ON
/// cmake --build build
/// cmake -S apps/studio -B build/studio
/// cmake --build build/studio --target afs_studio
///
/// Keys:
/// F5 - Refresh data
@@ -17,29 +18,62 @@
#include <string>
#include "app.h"
#include "core/logger.h"
#include "core/filesystem.h"
#include "core/logger.h"
#include "core/version.h"
namespace {
void PrintUsage() {
std::cout << "afs_studio [--data PATH]\n"
<< " --data PATH Training data root (default: ~/src/training)\n"
<< " --version Print version and exit\n"
<< " -h, --help Show this help\n";
}
} // namespace
int main(int argc, char* argv[]) {
using afs::studio::core::FileSystem;
// Determine data path
std::string data_path_str;
if (argc > 1) {
data_path_str = argv[1];
} else {
for (int i = 1; i < argc; ++i) {
std::string arg = argv[i];
if (arg == "--help" || arg == "-h") {
PrintUsage();
return 0;
}
if (arg == "--version" || arg == "-v") {
std::cout << "AFS Studio " << afs::studio::core::StudioVersion() << "\n";
return 0;
}
if (arg == "--data" || arg == "--data-path") {
if (i + 1 < argc) {
data_path_str = argv[++i];
continue;
}
std::cerr << "Missing value for --data\n";
return 1;
}
if (data_path_str.empty() && !arg.empty() && arg[0] != '-') {
data_path_str = arg;
}
}
if (data_path_str.empty()) {
const char* env_root = std::getenv("AFS_TRAINING_ROOT");
if (env_root && env_root[0] != '\0') {
data_path_str = env_root;
} else {
auto preferred = FileSystem::ResolvePath("~/src/training");
data_path_str = FileSystem::Exists(preferred) ? preferred.string() : "~/.context/training";
data_path_str = FileSystem::Exists(preferred) ? preferred.string()
: "~/.context/training";
}
}
std::filesystem::path data_path = FileSystem::ResolvePath(data_path_str);
LOG_INFO("AFS Studio Starting...");
LOG_INFO(std::string("AFS Studio ") + afs::studio::core::StudioVersion());
LOG_INFO("Data path: " + data_path.string());
LOG_INFO("Press F5 to refresh data");

View File

@@ -122,6 +122,7 @@ struct AppState {
bool show_quality_trends = false; // Default off, let layout init handle it
bool show_generator_efficiency = false;
bool show_coverage_density = false;
bool show_about_modal = false;
bool enable_viewports = true;
bool enable_docking = true;
bool reset_layout_on_workspace_change = false;
@@ -160,6 +161,8 @@ struct AppState {
bool force_reset_layout = false;
bool lock_layout = false;
double last_refresh_time = 0.0;
std::string studio_version;
std::string studio_data_root;
// Advanced Interaction
std::vector<PlotKind> active_floaters;

View File

@@ -37,16 +37,24 @@ std::filesystem::path ResolveHafsScawfulRoot() {
const char* trunk_env = std::getenv("TRUNK_ROOT");
if (trunk_env && trunk_env[0] != '\0') {
auto trunk_root = studio::core::FileSystem::ResolvePath(trunk_env);
auto candidate = trunk_root / "scawful" / "research" / "afs_scawful";
auto candidate = trunk_root / "lab" / "afs_scawful";
if (studio::core::FileSystem::Exists(candidate)) {
return candidate;
}
auto legacy = trunk_root / "scawful" / "research" / "afs_scawful";
if (studio::core::FileSystem::Exists(legacy)) {
return legacy;
}
}
auto fallback_path = studio::core::FileSystem::ResolvePath("~/src/trunk/scawful/research/afs_scawful");
auto fallback_path = studio::core::FileSystem::ResolvePath("~/src/trunk/lab/afs_scawful");
if (studio::core::FileSystem::Exists(fallback_path)) {
return fallback_path;
}
auto legacy_fallback = studio::core::FileSystem::ResolvePath("~/src/trunk/scawful/research/afs_scawful");
if (studio::core::FileSystem::Exists(legacy_fallback)) {
return legacy_fallback;
}
return {};
}
@@ -1458,11 +1466,33 @@ void RenderMenuBar(AppState& state,
if (show_shortcuts_window) *show_shortcuts_window = true;
}
ImGui::Separator();
if (ImGui::MenuItem("About AFS Viz")) {}
if (ImGui::MenuItem("About AFS Studio")) {
state.show_about_modal = true;
}
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
if (state.show_about_modal) {
ImGui::OpenPopup("About AFS Studio");
}
if (ImGui::BeginPopupModal("About AFS Studio", &state.show_about_modal,
ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text("AFS Studio");
ImGui::Separator();
ImGui::Text("Version: %s", state.studio_version.empty()
? "(unknown)"
: state.studio_version.c_str());
ImGui::Text("Data root: %s", state.studio_data_root.empty()
? "(not set)"
: state.studio_data_root.c_str());
ImGui::Spacing();
if (ImGui::Button("Close")) {
state.show_about_modal = false;
}
ImGui::EndPopup();
}
}
void RenderSidebar(AppState& state, const DataLoader& loader, ImFont* font_ui, ImFont* font_header) {

View File

@@ -108,11 +108,11 @@ bool TrainingStatusWidget::FetchHealthData() {
} else {
const char* trunk_root = std::getenv("TRUNK_ROOT");
if (trunk_root && trunk_root[0] != '\0') {
root = std::string(trunk_root) + "/scawful/research/afs";
root = std::string(trunk_root) + "/lab/afs";
} else {
const char* home = std::getenv("HOME");
if (home && home[0] != '\0') {
root = std::string(home) + "/src/trunk/scawful/research/afs";
root = std::string(home) + "/src/trunk/lab/afs";
}
}
}