Commit Graph

19 Commits

Author SHA1 Message Date
typebasedio 7a7b2490c2 feat(v0.5.1): Linux portability baseline
- justfile: add build-linux, build-windows, build-all (output to dist/)
- .gitignore: cover ctask, ctask-*, dist/
- scripts/install.sh + scripts/uninstall.sh: POSIX equivalents of .ps1
- remove WorkspacePath metadata field (no production readers; legacy
  task.yaml files continue to parse silently)

Linux smoke-test on WSL/container pending.
See audit-report.md and v0.5.1-spec.md.
2026-05-07 18:22:41 -04:00
typebasedio a162aec0b2 fix(v0.5.1): use local time for workspace directory prefix and info display
Workspace directory names (YYYY-MM-DD_slug) and the YYYYMMDD-HHMMSS ID
field now use local time so users see their wall-clock date rather
than UTC — the prior behavior caused evening-EST creations to appear
under tomorrow's date for several hours every day. ctask info's
Created/Updated/Archived lines also convert to local for display.

Stored timestamps in task.yaml, session logs, the lease, the manifest,
and the session summary all continue to use UTC. Only user-facing
surfaces change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:33:52 -04:00
typebasedio 47430a1b1e feat(v0.5): include \$CTASK_ROOT/projects/ in SearchRoots by default
When CTASK_PROJECT_ROOT is unset, SearchRoots now appends
\$CTASK_ROOT/projects/ so that projects created under the default
category are discoverable from any shell without per-session env var
setup. Dedupe in scanAllRoots prevents double-counting workspaces that
are reachable both via the depth-2 scan under CTASK_ROOT and via the
new explicit search root. Adds a named regression test asserting no
duplicates appear in either ResolveQuery or ListWorkspaces results.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 19:51:07 -04:00
typebasedio 7cfafdc285 feat(v0.5): scaffold project subdirectory and set launch_dir in task.yaml
ctask new --project now creates an empty subdirectory named after the
final suffixed slug inside the workspace root, and sets meta.LaunchDir
to that slug. Task workspaces are unchanged. Seeds do not target the
subdirectory — the user populates it with their project code and their
own CLAUDE.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 19:47:33 -04:00
typebasedio dcb161022c feat(v0.5): add workspace.ResolveLaunch helper
Resolves a relative launch_dir into the absolute directory the child
process should cd into. Returns an error for absolute paths and paths
that escape the workspace via .. traversal. Returns (wsDir, warning, nil)
on os.IsNotExist or target-is-a-file so the caller prints a warning and
falls back. Non-IsNotExist stat errors (permission, invalid name, I/O)
propagate as real errors rather than being masked as warnings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 19:46:25 -04:00
typebasedio 175fbb0075 feat(v0.5): add launch_dir field to TaskMeta
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 19:44:36 -04:00
typebasedio 075000497f fix(v0.4.1): scan both CTASK_ROOT and CTASK_PROJECT_ROOT in workspace queries
Rewrites scanWorkspaces to handle both category layout
(root/<category>/<workspace>/task.yaml) and flat layout
(root/<workspace>/task.yaml used under CTASK_PROJECT_ROOT).

Adds scanAllRoots to walk multiple roots with absolute-path dedupe.
ResolveQuery, ListWorkspaces, and MostRecentActive now accept []string.
QueryResult gains a Root field so callers can render display paths and
session env vars relative to the originating root.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 17:53:58 -04:00
typebasedio d1bcd1a1ba feat(v0.4): add WriteMetaLocked wrapper backed by workspace write lock 2026-04-21 17:04:01 -04:00
typebasedio 3a4a8d28f2 test(v0.3): add MostRecentActive helper with focused unit coverage
Extracts the cross-type "most recently updated active workspace"
selector that previously lived inline in cmd/last and cmd/delete.
The helper takes only a root path and returns (nil, nil) when
nothing matches, which keeps the call sites trivial.

Eight focused tests cover:
  - tasks-only fixture
  - projects-only fixture
  - project most-recent vs older task
  - task most-recent vs older project
  - legacy v0.2 workspace (no Type field) winning, treated as task
  - newest workspace archived, older active project wins
  - empty root returns (nil, nil)
  - all archived returns (nil, nil)

A new createTestWorkspaceFull helper takes an explicit UpdatedAt
timestamp so the "which is newest" assertions don't depend on
wall-clock ordering.

cmd/last and cmd/delete are migrated onto the helper in the next
commit.
2026-04-10 17:00:05 -04:00
typebasedio bd1cff5b26 refactor(v0.3): replace ListOpts.Projects bool with tri-state Type filter
ListOpts now exposes a Type string field (TypeAny / TypeTask /
TypeProject). TypeAny is the new way to express "both tasks and
projects" in a single ListWorkspaces call -- which the next two
commits will use to consolidate cmd/last and cmd/delete onto a
single helper, and to make 'ctask list' default to showing both
types.

Invalid Type values now return an explicit error from
ListWorkspaces (defensive against typos in callers).

cmd/list, cmd/last, and cmd/delete are migrated to the new field.
External behavior is unchanged in this commit; the cleanup of
ctask list semantics happens in a follow-up commit so the diff
stays reviewable.
2026-04-10 16:49:49 -04:00
typebasedio bfe89d830c feat(v0.3): add Projects filter to ListWorkspaces; fix last/delete
ListOpts gains a Projects bool that filters by EffectiveType.
Default behavior (Projects: false) now returns tasks only --
this is a deliberate semantic change that supports the new
'ctask list' (tasks) vs 'ctask list --projects' (projects)
spec.

The change silently regresses two cmd-level callers that scan
for "the most recently updated workspace": cmd/last.go (used by
'ctask last') and cmd/delete.go (used to print the "this was
your most recent workspace" note). Both are fixed by unioning a
tasks-scan with a projects-scan, so 'last' and 'delete' continue
to consider both types.

Test helper createTestWorkspaceTyped allows setting an explicit
type (or "" to simulate a v0.2 workspace with no type field).
2026-04-10 14:43:28 -04:00
typebasedio 6519582de6 feat(v0.3): add EnsureGitignore + RunGitInit helpers
EnsureGitignore writes a minimal .gitignore (.ctask/ +
logs/sessions.log) iff one does not already exist. This is the
file-system half of the v0.3 seed-wins rule for .gitignore: if
either the general or project seed copied a .gitignore into the
workspace, EnsureGitignore must be a no-op.

GitAvailable + RunGitInit wrap exec.LookPath("git") and `git init`
respectively, so the caller in cmd/new.go can decide whether to
print the informational note when git is missing.

Tests cover:
  - missing -> created with the minimal body
  - present -> preserved verbatim
  - integration: general seed .gitignore preserved end-to-end
  - integration: project seed .gitignore preserved end-to-end
  - integration: no seed -> minimal body created
2026-04-10 14:41:14 -04:00
typebasedio 3adfe62410 feat(v0.3): add SkipCategoryDir for CTASK_PROJECT_ROOT semantics
When SkipCategoryDir is true, Create places the workspace directly
under Root and does not append a Category subdirectory. This is the
mechanism that prevents doubled paths like
~/projects/projects/<slug> when the user sets CTASK_PROJECT_ROOT
without an explicit -c flag. The Category value is still recorded
on TaskMeta so list/info/filter still work.
2026-04-10 14:38:01 -04:00
typebasedio e09eac62d1 feat(v0.3): support project mode and layered seed overlay in Create
CreateOpts gains IsProject, SeedDir, and ProjectSeedDir. The new
seeding flow is:

  1. write built-in defaults (task or project CLAUDE.md + notes.md)
  2. apply general seed if SeedDir != "" (overlay)
  3. apply project seed if IsProject && ProjectSeedDir != "" (overlay)
  4. write task.yaml (last, so seeds can never inject metadata)

The obsolete TestCreateDoesNotOverwriteSeedFiles test is removed:
its v0.2 invariant ("don't overwrite existing files") no longer
holds because v0.3 always lays defaults onto a fresh dir and seeds
are expected to overwrite.
2026-04-10 14:37:09 -04:00
typebasedio 72be64cc1a feat(v0.3): add Type field to TaskMeta with backward-compat helper
TaskMeta now records type: task or type: project. EffectiveType
returns 'task' for missing/empty/unknown values and for nil meta,
so v0.2 workspaces continue to read as tasks without any
migration. The field is placed between Category and Mode in the
YAML output. Tests cover the round-trip and the legacy
no-type-field case.
2026-04-10 14:34:53 -04:00
typebasedio 6740c3835e feat: query resolution and workspace listing
5-step query resolution (exact dir, exact slug, substring), archived exclusion. Listing with category filter, limit, reverse-chronological sort.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:32:12 -04:00
typebasedio 17789e4b9f feat: workspace creation with seed files and collision handling
Create function produces full workspace layout (task.yaml, CLAUDE.md, notes.md, context/, output/, logs/). Seed files only written if missing. Collision suffixing tested.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:30:59 -04:00
typebasedio 7b75cb5f3d feat: slug generation and directory collision resolution
Slugify, AutoTitle, DirName, ResolveDir with -2, -3 suffixing. Full test coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:30:06 -04:00
typebasedio 514f2d8233 feat: workspace metadata model with YAML read/write
TaskMeta struct matching task.yaml schema exactly. Tests for roundtrip, field presence, and archive state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 18:29:26 -04:00